|
|
@@ -0,0 +1,271 @@
|
|
|
+import type { TestCenter } from "@/types";
|
|
|
+import type { InjectionKey } from "vue";
|
|
|
+import { reactive, computed, toRefs } from "vue";
|
|
|
+import { hollStepsQuestions, hollSteps, mbtiSteps, mbtiStepsQuestions } from "@/api/modules/test-center";
|
|
|
+
|
|
|
+export const useTest = (type: 'holland' | 'mbti') => {
|
|
|
+ // 使用 reactive 创建响应式状态对象
|
|
|
+ const steps = ref<TestCenter.TestStepVO[]>([])
|
|
|
+ const stepIndex = ref(0)
|
|
|
+ const questionIndex = ref(0)
|
|
|
+ const isReady = ref(false)
|
|
|
+ const swiperDuration = ref(300)
|
|
|
+
|
|
|
+ // 计算属性
|
|
|
+ const currentStep = computed(() => {
|
|
|
+ return steps.value?.[stepIndex.value];
|
|
|
+ });
|
|
|
+
|
|
|
+ const questionList = computed(() => {
|
|
|
+ return currentStep.value?.questions || [];
|
|
|
+ });
|
|
|
+
|
|
|
+ const currentQuestion = computed(() => {
|
|
|
+ return steps.value?.[stepIndex.value]?.questions?.[questionIndex.value];
|
|
|
+ });
|
|
|
+
|
|
|
+ const canPrev = computed(() => {
|
|
|
+ return stepIndex.value > 0 || (stepIndex.value === 0 && questionIndex.value > 0);
|
|
|
+ });
|
|
|
+
|
|
|
+ const canNext = computed(() => {
|
|
|
+ return questionList.value.length > 0 && (stepIndex.value < steps.value.length - 1 || (stepIndex.value === steps.value.length - 1 && questionIndex.value < questionList.value.length - 1));
|
|
|
+ });
|
|
|
+
|
|
|
+ const unAnseredQuestionList = computed(() => {
|
|
|
+ return questionList.value.filter(item => {
|
|
|
+ return item.answer === undefined;
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 方法
|
|
|
+ const prevQuestionQuickly = () => {
|
|
|
+ if (!canPrev.value) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ swiperDuration.value = 0;
|
|
|
+ setTimeout(() => {
|
|
|
+ prevQuestion();
|
|
|
+ setTimeout(() => {
|
|
|
+ swiperDuration.value = 300;
|
|
|
+ }, 0);
|
|
|
+ }, 0);
|
|
|
+ }
|
|
|
+ const prevQuestion = () => {
|
|
|
+ if (!canPrev.value) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (questionIndex.value > 0) {
|
|
|
+ questionIndex.value--;
|
|
|
+ } else {
|
|
|
+ const index = stepIndex.value - 1;
|
|
|
+ stepIndex.value = index;
|
|
|
+ questionIndex.value = steps.value[index].questions.length - 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const nextQuestionQuickly = () => {
|
|
|
+ if (!canNext.value) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ swiperDuration.value = 0;
|
|
|
+ setTimeout(() => {
|
|
|
+ nextQuestion();
|
|
|
+ setTimeout(() => {
|
|
|
+ swiperDuration.value = 300;
|
|
|
+ }, 0);
|
|
|
+ }, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ const nextQuestion = () => {
|
|
|
+ if (!canNext.value) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (questionList.value.length > 0 && questionIndex.value < questionList.value.length - 1) {
|
|
|
+ questionIndex.value++;
|
|
|
+ } else {
|
|
|
+ stepIndex.value++;
|
|
|
+ questionIndex.value = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const getSteps = async () => {
|
|
|
+ let data: TestCenter.TestStepVO[] = [];
|
|
|
+ if (type === 'holland') {
|
|
|
+ const res = await hollSteps();
|
|
|
+ data = res.data.map(item => {
|
|
|
+ return {
|
|
|
+ id: item.id,
|
|
|
+ no: item.no,
|
|
|
+ title: item.title,
|
|
|
+ questions: [] // 初始化为空数组
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ const res = await mbtiSteps();
|
|
|
+ data = res.data.map(item => {
|
|
|
+ return {
|
|
|
+ id: item.id,
|
|
|
+ no: item.no,
|
|
|
+ title: item.title,
|
|
|
+ questions: [] // 初始化为空数组
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ steps.value = data;
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+ const genScoreList = () => {
|
|
|
+ return new Array(7).fill(null).map((item, index) => {
|
|
|
+ return {
|
|
|
+ no: (index + 1).toString(),
|
|
|
+ title: `${index + 1}分`,
|
|
|
+ answer: index + 1
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ const getQuestions = async (step: TestCenter.TestStepVO, index: number) => {
|
|
|
+ if (step.id) {
|
|
|
+ const stepId = step.id;
|
|
|
+ if (type === 'holland') {
|
|
|
+ const res = await hollStepsQuestions(stepId);
|
|
|
+ const questions = res.rows.map(item => {
|
|
|
+ if (index === 3) {
|
|
|
+ return {
|
|
|
+ id: item.id,
|
|
|
+ no: item.no.toString(),
|
|
|
+ title: item.title,
|
|
|
+ options: genScoreList(),
|
|
|
+ answer: undefined,
|
|
|
+ multiple: false
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return {
|
|
|
+ id: item.id,
|
|
|
+ no: item.no.toString(),
|
|
|
+ title: item.title,
|
|
|
+ options: item.options.map(option => {
|
|
|
+ return {
|
|
|
+ no: option.no.toString(),
|
|
|
+ title: option.label,
|
|
|
+ answer: option.id
|
|
|
+ }
|
|
|
+ }),
|
|
|
+ answer: undefined,
|
|
|
+ multiple: true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 关键修复:找到 steps 数组中对应的 step,然后替换整个对象
|
|
|
+ const stepIndexInArray = steps.value.findIndex(s => s.id === step.id);
|
|
|
+ if (stepIndexInArray !== -1) {
|
|
|
+ steps.value[stepIndexInArray] = {
|
|
|
+ ...steps.value[stepIndexInArray],
|
|
|
+ questions
|
|
|
+ };
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const res = await mbtiStepsQuestions(stepId);
|
|
|
+ const questions = res.rows.map(item => {
|
|
|
+ return {
|
|
|
+ id: item.id,
|
|
|
+ no: item.no.toString(),
|
|
|
+ title: item.title,
|
|
|
+ options: [
|
|
|
+ {
|
|
|
+ no: 'A',
|
|
|
+ title: item.optionA,
|
|
|
+ answer: item.valueA
|
|
|
+ },
|
|
|
+ {
|
|
|
+ no: 'B',
|
|
|
+ title: item.optionB,
|
|
|
+ answer: item.valueB
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ answer: undefined,
|
|
|
+ multiple: false
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 关键修复:找到 steps 数组中对应的 step,然后替换整个对象
|
|
|
+ const stepIndexInArray = steps.value.findIndex(s => s.id === step.id);
|
|
|
+ if (stepIndexInArray !== -1) {
|
|
|
+ steps.value[stepIndexInArray] = {
|
|
|
+ ...steps.value[stepIndexInArray],
|
|
|
+ questions
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const setQuestionIndex = (index: number, widthAnimation = true) => {
|
|
|
+ if (widthAnimation) {
|
|
|
+ swiperDuration.value = 300;
|
|
|
+ setTimeout(() => {
|
|
|
+ questionIndex.value = index;
|
|
|
+ }, 0);
|
|
|
+ } else {
|
|
|
+ swiperDuration.value = 0;
|
|
|
+ setTimeout(() => {
|
|
|
+ questionIndex.value = index;
|
|
|
+ }, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const getResult = () => {
|
|
|
+ if (type === 'holland') {
|
|
|
+ return steps.value.map(item => item.questions.map(qs => {
|
|
|
+ return {
|
|
|
+ questionId: qs.id,
|
|
|
+ answer: qs.multiple ? JSON.stringify(qs.answer) : JSON.stringify([qs.answer])
|
|
|
+ }
|
|
|
+ })).flat();
|
|
|
+ } else {
|
|
|
+ return steps.value.map(item => item.questions.map(qs => {
|
|
|
+ return {
|
|
|
+ questionId: qs.id,
|
|
|
+ answer: qs.answer
|
|
|
+ }
|
|
|
+ })).flat();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const init = async () => {
|
|
|
+ uni.$ie.showLoading();
|
|
|
+ try {
|
|
|
+ const data = await getSteps();
|
|
|
+ const promiseList = data.map((item, index) => {
|
|
|
+ return getQuestions(item, index);
|
|
|
+ });
|
|
|
+ await Promise.all(promiseList);
|
|
|
+ isReady.value = true;
|
|
|
+ } catch (error) { } finally {
|
|
|
+ uni.$ie.hideLoading();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ init();
|
|
|
+
|
|
|
+ return {
|
|
|
+ steps,
|
|
|
+ stepIndex,
|
|
|
+ questionIndex,
|
|
|
+ isReady,
|
|
|
+ swiperDuration,
|
|
|
+ currentStep,
|
|
|
+ questionList,
|
|
|
+ currentQuestion,
|
|
|
+ prevQuestion,
|
|
|
+ prevQuestionQuickly,
|
|
|
+ nextQuestion,
|
|
|
+ nextQuestionQuickly,
|
|
|
+ setQuestionIndex,
|
|
|
+ canPrev,
|
|
|
+ canNext,
|
|
|
+ unAnseredQuestionList,
|
|
|
+ getResult
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+export const SYMBOL_TEST_DATA = Symbol('TEST_DATA') as InjectionKey<ReturnType<typeof useTest>>;
|