feat: 抽离答题校验逻辑
- 纠正 Preview/index 错误的逻辑,开始检测 error 字段 - validateAnswer 类型纠正,修复 answerItem 验证逻辑 - 答题校验抽离
This commit is contained in:
4
src/types/question.d.ts
vendored
4
src/types/question.d.ts
vendored
@@ -34,7 +34,7 @@ declare interface IQuestionOption {
|
|||||||
relation_last_scope: number;
|
relation_last_scope: number;
|
||||||
relation_first_scope: number;
|
relation_first_scope: number;
|
||||||
relation_question_index: number;
|
relation_question_index: number;
|
||||||
options?: IMatrixCheckboxOption[];
|
options: IMatrixCheckboxOption[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 答案 config 类型
|
// 答案 config 类型
|
||||||
@@ -93,7 +93,7 @@ export declare interface IQuestion<QuestionConfig = IBaseConfig> {
|
|||||||
question_index?: number;
|
question_index?: number;
|
||||||
question_type?: number;
|
question_type?: number;
|
||||||
// 如果没有自定义类型,那么就直接用基础 config 类型
|
// 如果没有自定义类型,那么就直接用基础 config 类型
|
||||||
config?: QuestionConfig;
|
config: QuestionConfig;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
created_user_id?: number;
|
created_user_id?: number;
|
||||||
updated_user_id?: number | null;
|
updated_user_id?: number | null;
|
||||||
|
|||||||
@@ -640,10 +640,11 @@ async function answer(callback, callbackBeforePage) {
|
|||||||
if ((questions.value.length || !questionsData.value.questions.length) && !props.isTemplate) {
|
if ((questions.value.length || !questionsData.value.questions.length) && !props.isTemplate) {
|
||||||
// 表单验证(当前页)
|
// 表单验证(当前页)
|
||||||
const errors = questions.value.filter((question) => {
|
const errors = questions.value.filter((question) => {
|
||||||
const { config, answer, question_type: questionType } = question;
|
const { config, answer, question_type: questionType, error } = question;
|
||||||
let isError = false;
|
let isError = false;
|
||||||
// 如果问题没有答案还有是必须填空的,就往下处理
|
// 如果问题没有答案还有是必须填空的,就往下处理
|
||||||
if (config.is_required && !answer) {
|
// 2025/4/1新增 : 如果有 error 内容, 同样视为有错误
|
||||||
|
if (!answer || error.length) {
|
||||||
isError = true;
|
isError = true;
|
||||||
// 各个问题单独处理
|
// 各个问题单独处理
|
||||||
if (questionType === 10) {
|
if (questionType === 10) {
|
||||||
|
|||||||
@@ -12,9 +12,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, onUnmounted, ref, watch } from 'vue';
|
||||||
import MatrixQuestion from '@/views/Design/components/Questions/MatrixQuestion.vue';
|
import MatrixQuestion from '@/views/Design/components/Questions/MatrixQuestion.vue';
|
||||||
import type { IQuestion } from '@/types/question';
|
import type { IQuestion, IQuestionOption } from '@/types/question';
|
||||||
|
import { validateAnswerNum } from '@/views/Survey/views/Preview/components/questions/validate/validateAnswer';
|
||||||
|
import type { IMatrixCheckboxConfig } from '@/types/questions/matrixCheckbox';
|
||||||
|
import { validateList } from '@/views/Survey/views/Preview/hooks/useValidateQuestion';
|
||||||
// const questionType = defineModel<number>('questionType', { required: false });
|
// const questionType = defineModel<number>('questionType', { required: false });
|
||||||
|
|
||||||
// 矩阵单选的答案类型
|
// 矩阵单选的答案类型
|
||||||
@@ -25,8 +28,10 @@ type answerType = {
|
|||||||
// preview props
|
// preview props
|
||||||
// const stem = defineModel('stem');
|
// const stem = defineModel('stem');
|
||||||
// const list = defineModel<questionsList[]>('list', { required: false });
|
// const list = defineModel<questionsList[]>('list', { required: false });
|
||||||
// const config = defineModel<OptionConfigType>('config', { required: false });
|
const config = defineModel<IMatrixCheckboxConfig>('config', { required: false });
|
||||||
const question = defineModel<IQuestion>('question', { default: () => {} });
|
const question = defineModel<IQuestion>('question', {
|
||||||
|
default: () => {}
|
||||||
|
});
|
||||||
|
|
||||||
// console.log(`question`, question.value);
|
// console.log(`question`, question.value);
|
||||||
|
|
||||||
@@ -73,7 +78,20 @@ function parseAnswer(answer: answerType) {
|
|||||||
* 行的内容在 question.list[0].options
|
* 行的内容在 question.list[0].options
|
||||||
* 列的内容在 question.list[1].options
|
* 列的内容在 question.list[1].options
|
||||||
*/
|
*/
|
||||||
const [rows, cols] = question.value?.list;
|
const [rows, cols] = <IQuestionOption[]>question.value?.list;
|
||||||
|
|
||||||
|
// 将校验函数 push 给 validateList,以便与点击下一页进行校验。
|
||||||
|
// 备选方法,此外还可以兼容 pc 答题行为
|
||||||
|
const validateFun = () => {
|
||||||
|
// 答案长度校验
|
||||||
|
question.value.error = validateAnswerNum(rowRecord.value, config.value!, rows.options);
|
||||||
|
};
|
||||||
|
|
||||||
|
validateList.value.push(validateFun);
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 在页面卸载的时候,将校验函数从 validateList 中移除
|
||||||
|
validateList.value = validateList.value.filter((func) => func !== validateFun);
|
||||||
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
rowRecord,
|
rowRecord,
|
||||||
@@ -86,7 +104,15 @@ watch(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 如果行记录答案不够,那么就不进行传递答案
|
// 如果行记录答案不够,那么就不进行传递答案
|
||||||
if (rowRecord.value.length !== rows.options.length) return;
|
// if (rowRecord.value.length !== rows.options.length) return;
|
||||||
|
const errMsg = validateAnswerNum(rowRecord.value, config.value!, rows.options);
|
||||||
|
question.value.error = errMsg;
|
||||||
|
|
||||||
|
// 如果答案有错误,那么就不进行传递答案,并重置之前的答案
|
||||||
|
if (errMsg.length > 0) {
|
||||||
|
answer.value = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
answer.value = newAnswer;
|
answer.value = newAnswer;
|
||||||
emit('changeAnswer', newAnswer);
|
emit('changeAnswer', newAnswer);
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
import { getLanguage } from '@/views/Survey/views/Preview/js/language';
|
import { getLanguage } from '@/views/Survey/views/Preview/js/language';
|
||||||
import type { IRateConfig } from '@/types/questions/rateConfig';
|
|
||||||
import type { IQuestionOption } from '@/types/question';
|
import type { IQuestionOption } from '@/types/question';
|
||||||
|
import type { IBaseConfig } from '@/types/baseConfig';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检验答案是否数量是否符合题目要求
|
* 检验答案是否数量是否符合题目要求
|
||||||
* @param answer 答案
|
* @param answer 答案
|
||||||
* @param config 配置
|
* @param config 配置
|
||||||
|
* @param options 选项
|
||||||
* @param translatedText 错误提示
|
* @param translatedText 错误提示
|
||||||
*/
|
*/
|
||||||
function validateAnswerNum(
|
function validateAnswerNum<T extends IBaseConfig>(
|
||||||
answer: number[],
|
answer: number[],
|
||||||
config: IRateConfig,
|
config: T,
|
||||||
options: IQuestionOption[],
|
options: IQuestionOption[],
|
||||||
translatedText = getLanguage(['zh'])
|
translatedText = getLanguage(['zh'])
|
||||||
) {
|
) {
|
||||||
const { is_required, max, min } = config;
|
const { is_required } = config;
|
||||||
|
const min = config.min || 0;
|
||||||
|
const max = config.max || 0;
|
||||||
let errorMessage: string = '';
|
let errorMessage: string = '';
|
||||||
|
|
||||||
// 如果不是必答题,跳过检验
|
// 如果不是必答题,跳过检验
|
||||||
@@ -22,7 +25,8 @@ function validateAnswerNum(
|
|||||||
|
|
||||||
// 检测答案数量是否在范围内
|
// 检测答案数量是否在范围内
|
||||||
answer.forEach((answerItem) => {
|
answer.forEach((answerItem) => {
|
||||||
if (answerItem < min || answerItem > max) {
|
// 如果不在范围内,提示错误, answerItem < min 或者 max 存在,并且 answerItem > max
|
||||||
|
if (answerItem < min || (answerItem > max && max)) {
|
||||||
errorMessage = translatedText.PleaseSelectAValueThatIsNoLessThanOneAndNoMoreThanOne(min, max);
|
errorMessage = translatedText.PleaseSelectAValueThatIsNoLessThanOneAndNoMoreThanOne(min, max);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user