diff --git a/.eslintignore b/.eslintignore index 6e162d36..b1714d94 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,5 @@ /src/views/Creative/3d /src/font +/src/plugins/tinymce-placeholder.js +/src/plugins/tinymce-quickbars.js +/public diff --git a/src/views/DataAnalyse/particulars/components/js/questions.js b/src/views/DataAnalyse/particulars/components/js/questions.js deleted file mode 100644 index 16d157f1..00000000 --- a/src/views/DataAnalyse/particulars/components/js/questions.js +++ /dev/null @@ -1,425 +0,0 @@ -import AnswerApi from "../../api"; -import { computed, getCurrentInstance, defineComponent, inject, ref } from "vue"; -import { message } from "ant-design-vue"; -import PfePagination from "@/components/PfePagination.vue"; -import Question from "../../questions/Question.vue"; -import QRadio from '@/views/Answer/questions/QRadio' -import QCheckbox from "../../questions/QCheckbox.vue"; -import QCascader from "../../questions/QCascader.vue"; -import QInput from "../../questions/QInput.vue"; -import QRate from "../../questions/QRate.vue"; -import QImgText from "../../questions/QImgText.vue"; -import QDate from "../../questions/QDate.vue"; -import MatrixInput from "../../questions/QMatrix/MatrixInput.vue"; -import MatrixRadio from "../../questions/QMatrix/MatrixRadio.vue"; -import MatrixCheck from "../../questions/QMatrix/MatrixCheck.vue"; -import MatrixRate from "../../questions/QMatrix/MatrixRate.vue"; -import QImgShow from "../../questions/QImgShow.vue"; -import QImgRadio from "../../questions/QImgRadio.vue"; -import QImgCheck from "../../questions/QImgCheck.vue"; -import QClass from "../../questions/QClass.vue"; -import QSort from "../../questions/QSort.vue"; -import QSum from "../../questions/QSum.vue"; -import QUpload from "../../questions/QUpload.vue"; -import QMap from "../../questions/QMap.vue"; -import QPhone from "../../questions/QPhone.vue"; -import QPassword from "../../questions/QPassword.vue"; -import QSign from "../../questions/QSign.vue"; -import QConsent from "../../questions/QConsent.vue"; -import QPSM from "../../questions/high/QPSM.vue"; -import QKANO from "../../questions/high/QKANO.vue"; -import QBPTO from "../../questions/high/QBPTO.vue"; -import QMXD from "../../questions/high/QMXD.vue"; - -export default defineComponent({ - components: { - PfePagination, - Question, - QFirst, - QLast, - QRadio, - QCheckbox, - QInput, - QCascader, - QRate, - QImgText, - QDate, - MatrixInput, - MatrixRadio, - MatrixCheck, - MatrixRate, - QImgShow, - QImgRadio, - QImgCheck, - QClass, - QSort, - QSum, - QUpload, - QMap, - QPhone, - QPassword, - QSign, - QConsent, - QPSM, - QKANO, - QBPTO, - QMXD, - }, - props: { - // 是否答题模式 - isAnswer: { - type: Boolean, - default: false, - }, - }, - setup(props) { - const page = ref(0); // 当前页数 - const questionsData = inject("questionsData"); // 问卷数据 - const { proxy } = getCurrentInstance(); - const loading = ref(false); - - // 主题样式 - const styleInfo = computed(() => questionsData.value.survey?.style || {}); - // 分页 - const pages = computed(() => questionsData.value.answer?.pages || []); - // 当前页问卷 - const questions = computed(() => { - const currentPages = pages.value[page.value - 1] || []; - return (questionsData.value.questions || []).filter((quetion) => - currentPages.find((index) => quetion.question_index === index) - ); - }); - // 是否显示分页器 - const showPage = computed(() => { - let show = true; - if (questions.value[0]?.question_type === 104 || questions.value[0]?.question_type === 105) { - show = false; - } - return show; - }); - - // 答题 - async function answer(callback) { - if ((questions.value.length && props.isAnswer) || !questionsData.value.questions.length) { - // 表单验证(当前页) - const errors = questions.value.filter((question) => { - const { config, answer, question_type: questionType } = question; - let isError = false; - if (config.is_required && !answer) { - isError = true; - if (questionType === 10) { - // 矩阵多选题 - question.error = `每行最少选${config.min_select}个`; - } else if (questionType === 15) { - // 分类题 - question.error = "部分选项未归类"; - } else if (!question.error) { - question.error = "这是一道必答题"; - } - } else if ( - answer && - questionType === 1 && - Object.keys(answer).findIndex((value) => !answer[value]) !== -1 - ) { - // 单选题 - isError = true; - question.error = `请输入`; - } else if (answer && questionType === 2) { - // 多选题 - if (Object.keys(answer).length < config.min_select) { - let options = []; - question.list.forEach((list) => { - options = [...options, ...list.options]; - }); - const index = options.findIndex( - (option) => option.option_key === Object.keys(answer)[0] && option.is_remove_other - ); - if (index === -1) { - isError = true; - question.error = `最少选择${config.min_select}个`; - } else { - question.error = ""; - } - } else if (Object.keys(answer).findIndex((value) => !answer[value]) !== -1) { - isError = true; - question.error = `请输入`; - } else { - question.error = ""; - } - } else if ( - answer && - questionType === 14 && - Object.keys(answer).length < config.min_select - ) { - // 图片多选题 - isError = true; - question.error = `最少选择${config.min_select}个`; - } else if (answer && questionType === 17) { - // 恒定总和题 - let sum = 0; - Object.keys(answer).forEach((key) => { - sum += answer[key] * 1; - }); - if (sum === config.total) { - question.error = ""; - } else { - isError = true; - question.error = `请让总和等于${config.total}`; - } - } else if (answer && questionType === 18 && answer.length < config.min_number) { - // 文件上传题 - isError = true; - question.error = `最少上传${config.min_number}个文件`; - } else if (answer && questionType === 4) { - question.error = ""; - // 填空题 - const { value } = answer; - switch (config.text_type) { - case 3: // 字母 - isError = !/^[a-zA-Z]+$/.test(value); - question.error = isError ? "请输入字母" : ""; - break; - case 4: // 中文 - isError = - !/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/.test( - value - ); - question.error = isError ? "请输入中文" : ""; - break; - case 5: // 邮箱 - isError = !/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test( - value - ); - question.error = isError ? "请输入正确的email" : ""; - break; - case 6: // 手机号 - isError = !/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(value); - question.error = isError ? "请输入正确的手机号" : ""; - break; - case 7: // 身份证号 - isError = - !/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test( - value - ); - question.error = isError ? "请输入正确的身份证号" : ""; - break; - default: - break; - } - if (!isError && value.length < config.min) { - isError = true; - question.error = `请至少输入${config.min}个字`; - } - } else if (answer && questionType === 8) { - // 矩阵填空题 - question.error = ""; - Object.keys(answer).forEach((key) => { - const value = answer[key]; - switch (config.text_type) { - case 3: // 字母 - if (!/^[a-zA-Z]+$/.test(value)) question.error = "请输入字母"; - break; - case 4: // 中文 - if ( - !/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/.test( - value - ) - ) - question.error = "请输入中文"; - break; - case 5: // 邮箱 - if (!/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(value)) - question.error = "请输入正确的email"; - break; - case 6: // 手机号 - if (!/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(value)) - question.error = "请输入正确的手机号"; - break; - case 7: // 身份证号 - if ( - !/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test( - value - ) - ) - question.error = "请输入正确的身份证号"; - break; - default: - break; - } - if (!question.error && value.length < config.min) { - question.error = `请至少输入${config.min}个字`; - } - }); - if (question.error) isError = true; - } else { - question.error = ""; - } - return isError; - }); - if (!errors.length) { - // 表单验证通过,开始答题 - const questionsAnswer = questions.value.map((question) => ({ - question_index: question.question_index, - answer: question.answer || {}, - })); - loading.value = true; - try { - const { data } = await AnswerApi.answer({ - id: proxy.$route.query.sn, - data: { - answer: JSON.stringify(questionsAnswer), - }, - }); - // 更新答案 - updateAnswer(data.answer_info_autofill); - // 更新分页数组 - questionsData.value.answer.pages = data.pages; - // 更新问卷状态 - if (data.action.code === 20014) { - const timer = setTimeout(() => { - let url = data.action.msg; - if (url.indexOf("http://") === -1 && url.indexOf("https://") === -1) { - url = `http://${url}`; - } - open(url); - clearTimeout(timer); - }); - } - questionsData.value.action = data.action; - callback(); - } catch (error) { - console.log(error); - } - loading.value = false; - } else { - const { question_index, error } = errors[0]; - const index = questions.value.findIndex( - (question) => question.question_index === question_index - ); - message.error(`第${index + 1}题:${error}`); - } - } else { - callback(); - } - } - - // 上一页 - function previous() { - page.value -= 1; - } - - // 下一页 - async function next() { - answer(() => { - page.value += 1; - }); - } - - // 关联引用 - function onRelation( - { options, value, list }, - { question_type, question_index, related, answer } - ) { - // 关联 - related.forEach((relationItem) => { - let relationOptions = []; - if (question_type === 9 || question_type === 10) { - // 矩阵选择 - list.forEach((item) => { - if (item.type === relationItem.cite_type) { - relationOptions = [...relationOptions, ...item.options]; - } - if (relationItem.relation_type === 1) { - relationOptions = relationOptions.filter((option) => - question_type === 9 ? option.value : option.value?.length - ); - } else if (relationItem.relation_type === 2) { - relationOptions = relationOptions.filter((option) => - question_type === 9 ? !option.value : !option.value?.length - ); - } - }); - } else if (question_type === 11) { - // 矩阵打分 - list.forEach((item) => { - if (item.type === relationItem.cite_type) { - relationOptions = [...relationOptions, ...item.options]; - } - }); - } else if (relationItem.relation_type === 0) { - // 全部选项 - relationOptions = options; - } else { - // 过滤选中/未选中选项 - relationOptions = options.filter((option) => { - if (question_type === 1) { - // 单选 - if (relationItem.relation_type === 1) return value === option.option_key; - return value !== option.option_key; - } else if (question_type === 2) { - // 多选 - if (relationItem.relation_type === 1) return value.includes(option.option_key); - return !value.includes(option.option_key); - } - return true; - }); - } - // 找到关联题 - const question = questionsData.value.questions.find( - (question) => question.question_index === relationItem.relation_question_index - ); - // 深拷贝关联选项 - const copyRelationOptions = JSON.parse(JSON.stringify(relationOptions)); - // 更新关联选项key - copyRelationOptions.forEach((option) => { - if (option.option_key[0] !== "Q") { - let letter = "A"; - // 矩阵题行、列 - if (question_type >= 9 && question_type <= 11) { - letter = relationItem.cite_type === 1 ? "R" : "C"; - } - option.option_key = `Q${question_index}${letter}${option.option_key}`; - } - // 其他项特殊处理 - if (option.is_other) { - // option.is_other = 0 - option.oldOption = option.option; - option.option = option.value || option.option; - } - delete option.value; - }); - // 更新关联题列表 - if (answer) { - question.list[0].options = copyRelationOptions; - } else { - question.list[0].options = []; - } - }); - } - - function updateAnswer(auto) { - if (auto) { - auto.forEach((autoItem) => { - const question = questionsData.value.questions.find( - (question) => question.question_index === autoItem.question_index - ); - question.answer = JSON.parse(autoItem.answer); - }); - } - } - - return { - page, - pages, - loading, - showPage, - styleInfo, - questions, - questionsData, - answer, - previous, - next, - onRelation, - }; - }, -});