From 2162f3cffdb1715294c699cee5e15adfeae32028 Mon Sep 17 00:00:00 2001 From: dayou <853094838@qq.com> Date: Fri, 23 Aug 2024 17:43:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20pinia=E6=94=B9=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/surveyHistory.controller.ts | 2 - .../survey/services/surveyDownload.service.ts | 2 +- .../modules/contentModule/PublishPanel.vue | 61 +--- .../edit/modules/contentModule/SavePanel.vue | 2 +- .../pages/edit/setterConfig/baseConfig.js | 2 +- .../pages/edit/setterConfig/baseFormConfig.js | 16 +- web/src/render/components/QuestionWrapper.vue | 17 + web/src/render/hooks/useOptionsQuota.js | 7 +- web/src/render/pages/RenderPage.vue | 2 +- web/src/render/store/actions.js | 2 +- web/src/render/stores/question.js | 294 ++++++++++++------ web/src/render/stores/survey.js | 142 ++++++++- 12 files changed, 383 insertions(+), 166 deletions(-) diff --git a/server/src/modules/survey/controllers/surveyHistory.controller.ts b/server/src/modules/survey/controllers/surveyHistory.controller.ts index d48c7cb2..38b8733b 100644 --- a/server/src/modules/survey/controllers/surveyHistory.controller.ts +++ b/server/src/modules/survey/controllers/surveyHistory.controller.ts @@ -18,8 +18,6 @@ import { SURVEY_PERMISSION } from 'src/enums/surveyPermission'; import { Logger } from 'src/logger'; import { HttpException } from 'src/exceptions/httpException'; import { EXCEPTION_CODE } from 'src/enums/exceptionCode'; -import { val } from 'cheerio/lib/api/attributes'; - @ApiTags('survey') @Controller('/api/surveyHisotry') export class SurveyHistoryController { diff --git a/server/src/modules/survey/services/surveyDownload.service.ts b/server/src/modules/survey/services/surveyDownload.service.ts index c5b4d76b..9e0ccc3c 100644 --- a/server/src/modules/survey/services/surveyDownload.service.ts +++ b/server/src/modules/survey/services/surveyDownload.service.ts @@ -189,7 +189,7 @@ export class SurveyDownloadService implements OnModuleInit { } return { ...data, - difTime: (submitedData.difTime / 1000).toFixed(2), + diffTime: (submitedData.diffTime / 1000).toFixed(2), createDate: moment(submitedData.createDate).format( 'YYYY-MM-DD HH:mm:ss', ), diff --git a/web/src/management/pages/edit/modules/contentModule/PublishPanel.vue b/web/src/management/pages/edit/modules/contentModule/PublishPanel.vue index 161586ee..fd0ee0c2 100644 --- a/web/src/management/pages/edit/modules/contentModule/PublishPanel.vue +++ b/web/src/management/pages/edit/modules/contentModule/PublishPanel.vue @@ -6,6 +6,7 @@ diff --git a/web/src/render/hooks/useOptionsQuota.js b/web/src/render/hooks/useOptionsQuota.js index 1df8070b..c9d8a688 100644 --- a/web/src/render/hooks/useOptionsQuota.js +++ b/web/src/render/hooks/useOptionsQuota.js @@ -1,9 +1,10 @@ -import store from '../store/index' +import { useQuestionStore } from '../stores/question' export const useOptionsQuota = (questionKey) => { - const options = store.state.questionData[questionKey].options.map((option) => { + const questionStore = useQuestionStore() + const options = questionStore.questionData[questionKey].options.map((option) => { if(option.quota){ const optionHash = option.hash - const selectCount = store.state.quotaMap?.[questionKey]?.[optionHash] || 0 + const selectCount = questionStore.quotaMap?.[questionKey]?.[optionHash] || 0 const release = Number(option.quota) - Number(selectCount) return { ...option, diff --git a/web/src/render/pages/RenderPage.vue b/web/src/render/pages/RenderPage.vue index b0697042..9920c921 100644 --- a/web/src/render/pages/RenderPage.vue +++ b/web/src/render/pages/RenderPage.vue @@ -92,7 +92,7 @@ const normalizationRequestBody = () => { localStorage.removeItem(surveyPath.value + "_questionData") localStorage.removeItem("isSubmit") //数据加密 - var formData = Object.assign({}, store.state.formValues) + var formData = Object.assign({}, surveyStore.formValues) for(const key in formData){ formData[key] = encodeURIComponent(formData[key]) } diff --git a/web/src/render/store/actions.js b/web/src/render/store/actions.js index cd6dba48..9c767fc8 100644 --- a/web/src/render/store/actions.js +++ b/web/src/render/store/actions.js @@ -360,5 +360,5 @@ export default { }) // 获取已投票数据 dispatch('initVoteData') - dispatch('initQuotaMap') + // dispatch('initQuotaMap') } diff --git a/web/src/render/stores/question.js b/web/src/render/stores/question.js index 0f810353..1298c465 100644 --- a/web/src/render/stores/question.js +++ b/web/src/render/stores/question.js @@ -3,11 +3,201 @@ import { defineStore } from 'pinia' import { set } from 'lodash-es' import { useSurveyStore } from '@/render/stores/survey' import { queryVote } from '@/render/api/survey' +import { QUESTION_TYPE, NORMAL_CHOICES } from '@/common/typeEnum' const VOTE_INFO_KEY = 'voteinfo' +const QUOTA_INFO_KEY = 'limitinfo' + + +import useCommandComponent from '../hooks/useCommandComponent' +import BackAnswerDialog from '../components/BackAnswerDialog.vue' + +const confirm = useCommandComponent(BackAnswerDialog) + +// 投票进度逻辑聚合 +const usevVoteMap = (questionData) => { + const voteMap = ref({}) + //初始化投票题的数据 + const initVoteData = async () => { + const surveyStore = useSurveyStore() + const surveyPath = surveyStore.surveyPath + + const fieldList = [] + + for (const field in questionData.value) { + const { type } = questionData.value[field] + if (type.includes(QUESTION_TYPE.VOTE)) { + fieldList.push(field) + } + } + + if (fieldList.length <= 0) { + return + } + try { + localStorage.removeItem(VOTE_INFO_KEY) + const voteRes = await queryVote({ + surveyPath, + fieldList: fieldList.join(',') + }) + + if (voteRes.code === 200) { + localStorage.setItem( + VOTE_INFO_KEY, + JSON.stringify({ + ...voteRes.data + }) + ) + setVoteMap(voteRes.data) + } + } catch (error) { + console.log(error) + } + } + const updateVoteMapByKey = (data) => { + const { questionKey, voteKey, voteValue } = data + // 兼容为空的情况 + if (!voteMap.value[questionKey]) { + voteMap.value[questionKey] = {} + } + voteMap.value[questionKey][voteKey] = voteValue + } + const setVoteMap = (data) => { + voteMap.value = data + } + const updateVoteData = (data) => { + const { key: questionKey, value: questionVal } = data + // 更新前获取接口缓存在localStorage中的数据 + const localData = localStorage.getItem(VOTE_INFO_KEY) + const voteinfo = JSON.parse(localData) + const currentQuestion = questionData.value[questionKey] + const options = currentQuestion.options + const voteTotal = voteinfo?.[questionKey]?.total || 0 + let totalPayload = { + questionKey, + voteKey: 'total', + voteValue: voteTotal + } + options.forEach((option) => { + const optionhash = option.hash + const voteCount = voteinfo?.[questionKey]?.[optionhash] || 0 + // 如果选中值包含该选项,对应voteCount 和 voteTotal + 1 + if ( + Array.isArray(questionVal) ? questionVal.includes(optionhash) : questionVal === optionhash + ) { + const countPayload = { + questionKey, + voteKey: optionhash, + voteValue: voteCount + 1 + } + totalPayload.voteValue += 1 + updateVoteMapByKey(countPayload) + } else { + const countPayload = { + questionKey, + voteKey: optionhash, + voteValue: voteCount + } + updateVoteMapByKey(countPayload) + } + updateVoteMapByKey(totalPayload) + }) + } + return { + voteMap, + initVoteData, + updateVoteData + } +} + +// 选项配额逻辑聚合 +const useQuotaMap = (questionData) => { + const quotaMap = ref({}) + const updateQuotaMapByKey = ({ questionKey, optionKey, data }) =>{ + // 兼容为空的情况 + if (!quotaMap.value[questionKey]) { + quotaMap.value[questionKey] = {} + } + quotaMap.value[questionKey][optionKey] = data + } + const initQuotaMap = async () => { + const surveyStore = useSurveyStore() + const surveyPath = surveyStore.surveyPath + const fieldList = Object.keys(questionData.value).filter(field => { + if (NORMAL_CHOICES.includes(questionData.value[field].type)) { + return questionData.value[field].options.some(option => option.quota > 0) + } + }) + + // 如果不存在则不请求选项上限接口 + if (fieldList.length <= 0) { + return + } + + try { + localStorage.removeItem(QUOTA_INFO_KEY) + const quotaRes = await queryVote({ + surveyPath, + fieldList: fieldList.join(',') + }) + + if (quotaRes.code === 200) { + localStorage.setItem( + QUOTA_INFO_KEY, + JSON.stringify({ + ...quotaRes.data + }) + ) + Object.keys(quotaRes.data).forEach(field => { + Object.keys(quotaRes.data[field]).forEach((optionHash) => { + updateQuotaMapByKey({ questionKey: field, optionKey: optionHash, data: quotaRes.data[field][optionHash] }) + }) + }) + } + } catch (error) { + console.log(error) + } + } + const updateQuotaData = (data) => { + const { key: questionKey, value: questionVal } = data + // 更新前获取接口缓存在localStorage中的数据 + const localData = localStorage.getItem(QUOTA_INFO_KEY) + const quotaMap = JSON.parse(localData) + // const quotaMap = state.quotaMap + const currentQuestion = questionData.value[questionKey] + const options = currentQuestion.options + options.forEach((option) => { + const optionhash = option.hash + const selectCount = quotaMap?.[questionKey]?.[optionhash].selectCount || 0 + // 如果选中值包含该选项,对应 voteCount 和 voteTotal + 1 + if ( + Array.isArray(questionVal) ? questionVal.includes(optionhash) : questionVal === optionhash + ) { + const countPayload = { + questionKey, + optionKey: optionhash, + selectCount: selectCount + 1 + } + updateQuotaMapByKey(countPayload) + } else { + const countPayload = { + questionKey, + optionKey: optionhash, + selectCount: selectCount + } + updateQuotaMapByKey(countPayload) + } + }) + } + return { + quotaMap, + initQuotaMap, + updateQuotaMapByKey, + updateQuotaData + } +} export const useQuestionStore = defineStore('question', () => { - const voteMap = ref({}) const questionData = ref(null) const questionSeq = ref([]) // 题目的顺序,因为可能会有分页的情况,所以是一个二维数组[[qid1, qid2], [qid3,qid4]] const pageIndex = ref(1) // 当前分页的索引 @@ -82,6 +272,8 @@ export const useQuestionStore = defineStore('question', () => { const setQuestionData = (data) => { questionData.value = data } + const { voteMap, setVoteMap, initVoteData, updateVoteData } = usevVoteMap(questionData) + const { quotaMap, initQuotaMap, updateQuotaData } = useQuotaMap(questionData) const changeSelectMoreData = (data) => { const { key, value, field } = data @@ -91,96 +283,7 @@ export const useQuestionStore = defineStore('question', () => { const setQuestionSeq = (data) => { questionSeq.value = data } - - const setVoteMap = (data) => { - voteMap.value = data - } - - const updateVoteMapByKey = (data) => { - const { questionKey, voteKey, voteValue } = data - // 兼容为空的情况 - if (!voteMap.value[questionKey]) { - voteMap.value[questionKey] = {} - } - voteMap.value[questionKey][voteKey] = voteValue - } - - //初始化投票题的数据 - const initVoteData = async () => { - const surveyStore = useSurveyStore() - const surveyPath = surveyStore.surveyPath - - const fieldList = [] - - for (const field in questionData.value) { - const { type } = questionData.value[field] - if (/vote/.test(type)) { - fieldList.push(field) - } - } - - if (fieldList.length <= 0) { - return - } - try { - localStorage.removeItem(VOTE_INFO_KEY) - const voteRes = await queryVote({ - surveyPath, - fieldList: fieldList.join(',') - }) - - if (voteRes.code === 200) { - localStorage.setItem( - VOTE_INFO_KEY, - JSON.stringify({ - ...voteRes.data - }) - ) - setVoteMap(voteRes.data) - } - } catch (error) { - console.log(error) - } - } - - const updateVoteData = (data) => { - const { key: questionKey, value: questionVal } = data - // 更新前获取接口缓存在localStorage中的数据 - const localData = localStorage.getItem(VOTE_INFO_KEY) - const voteinfo = JSON.parse(localData) - const currentQuestion = questionData.value[questionKey] - const options = currentQuestion.options - const voteTotal = voteinfo?.[questionKey]?.total || 0 - let totalPayload = { - questionKey, - voteKey: 'total', - voteValue: voteTotal - } - options.forEach((option) => { - const optionhash = option.hash - const voteCount = voteinfo?.[questionKey]?.[optionhash] || 0 - // 如果选中值包含该选项,对应voteCount 和 voteTotal + 1 - if ( - Array.isArray(questionVal) ? questionVal.includes(optionhash) : questionVal === optionhash - ) { - const countPayload = { - questionKey, - voteKey: optionhash, - voteValue: voteCount + 1 - } - totalPayload.voteValue += 1 - updateVoteMapByKey(countPayload) - } else { - const countPayload = { - questionKey, - voteKey: optionhash, - voteValue: voteCount - } - updateVoteMapByKey(countPayload) - } - updateVoteMapByKey(totalPayload) - }) - } + const setChangeField = (field) => { changeField.value = field @@ -199,7 +302,7 @@ export const useQuestionStore = defineStore('question', () => { needHideFields.value = needHideFields.value.filter(field => !fields.includes(field)) } return { - voteMap, + questionData, questionSeq, renderData, @@ -209,8 +312,8 @@ export const useQuestionStore = defineStore('question', () => { setQuestionData, changeSelectMoreData, setQuestionSeq, + voteMap, setVoteMap, - updateVoteMapByKey, initVoteData, updateVoteData, changeField, @@ -219,6 +322,9 @@ export const useQuestionStore = defineStore('question', () => { needHideFields, addNeedHideFields, removeNeedHideFields, - getQuestionIndexByField + getQuestionIndexByField, + quotaMap, + initQuotaMap, + updateQuotaData } }) diff --git a/web/src/render/stores/survey.js b/web/src/render/stores/survey.js index 9a188d3a..5e36bc73 100644 --- a/web/src/render/stores/survey.js +++ b/web/src/render/stores/survey.js @@ -26,6 +26,8 @@ const CODE_MAP = { ERROR: 500, NO_AUTH: 403 } + + export const useSurveyStore = defineStore('survey', () => { const surveyPath = ref('') const isMobile = ref(isInMobile()) @@ -110,15 +112,11 @@ export const useSurveyStore = defineStore('survey', () => { return isSuccess } - const initSurvey = (option) => { - setEnterTime() - if (!canFillQuestionnaire(option.baseConf, option.submitConf)) { - return - } - + // 加载空白页面 + function clearFormData(option) { // 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段 const { questionData, @@ -137,6 +135,7 @@ export const useSurveyStore = defineStore('survey', () => { 'pageConf' ]) ) + // todo: 建议通过questionStore提供setqueationdata方法修改属性,否则不好跟踪变化 questionStore.questionData = questionData questionStore.questionSeq = questionSeq @@ -151,8 +150,139 @@ export const useSurveyStore = defineStore('survey', () => { formValues.value = _formValues whiteData.value = option.whiteData pageConf.value = option.pageConf + // 获取已投票数据 questionStore.initVoteData() + questionStore.initQuotaMap() + + } + + // 加载上次填写过的数据到问卷页 + function loadFormData({bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }, formData) { + // 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段 + const { questionData, questionSeq, rules, formValues } = adapter.generateData({ + bannerConf, + baseConf, + bottomConf, + dataConf, + skinConf, + submitConf + }) + + for(const key in formData){ + formValues[key] = formData[key] + } + + // 将数据设置到state上 + commit('assignState', { + questionData, + questionSeq, + rules, + bannerConf, + baseConf, + bottomConf, + dataConf, + skinConf, + submitConf, + formValues + }) + // 获取已投票数据 + dispatch('initVoteData') + // 获取选项上线选中数据 + dispatch('initQuotaMap') + + // todo: 建议通过questionStore提供setqueationdata方法修改属性,否则不好跟踪变化 + questionStore.questionData = questionData + questionStore.questionSeq = questionSeq + + // 将数据设置到state上 + rules.value = rules + bannerConf.value = option.bannerConf + baseConf.value = option.baseConf + bottomConf.value = option.bottomConf + dataConf.value = option.dataConf + skinConf.value = option.skinConf + submitConf.value = option.submitConf + formValues.value = _formValues + + whiteData.value = option.whiteData + pageConf.value = option.pageConf + + // 获取已投票数据 + questionStore.initVoteData() + questionStore.initQuotaMap() + + } + const initSurvey = (option) => { + setEnterTime() + + if (!canFillQuestionnaire(option.baseConf, option.submitConf)) { + return + } + + const localData = JSON.parse(localStorage.getItem(surveyPath.value + "_questionData")) + for(const key in localData){ + localData[key] = decodeURIComponent(localData[key]) + } + + const isSubmit = JSON.parse(localStorage.getItem('isSubmit')) + if(localData) { + if(isSubmit){ + if(!option.baseConf.backAnswer) { + clearFormData(option) + } else { + confirm({ + title: "您之前已提交过问卷,是否要回填?", + onConfirm: async () => { + try { + loadFormData(option, localData) + } catch (error) { + console.log(error) + } finally { + confirm.close() + } + }, + onCancel: async() => { + try { + clearFormData({ commit, dispatch }, { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }) + } catch (error) { + console.log(error) + } finally { + confirm.close() + } + } + }) + } + } else { + if(!option.baseConf.breakAnswer) { + clearFormData(option) + } else { + confirm({ + title: "您之前已填写部分内容, 是否要继续填写?", + onConfirm: async () => { + try { + loadFormData(option, localData) + } catch (error) { + console.log(error) + } finally { + confirm.close() + } + }, + onCancel: async() => { + try { + clearFormData(option) + } catch (error) { + console.log(error) + } finally { + confirm.close() + } + } + }) + } + } + } else { + clearFormData(option) + } } // 用户输入或者选择后,更新表单数据