diff --git a/web/components.d.ts b/web/components.d.ts new file mode 100644 index 00000000..a7ad42ea --- /dev/null +++ b/web/components.d.ts @@ -0,0 +1,52 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// Generated by unplugin-vue-components +// Read more: https://github.com/vuejs/core/pull/3399 +export {} + +declare module 'vue' { + export interface GlobalComponents { + ElButton: typeof import('element-plus/es')['ElButton'] + ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] + ElDialog: typeof import('element-plus/es')['ElDialog'] + ElForm: typeof import('element-plus/es')['ElForm'] + ElFormItem: typeof import('element-plus/es')['ElFormItem'] + ElInput: typeof import('element-plus/es')['ElInput'] + ElMenu: typeof import('element-plus/es')['ElMenu'] + ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] + ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup'] + ElOption: typeof import('element-plus/es')['ElOption'] + ElPagination: typeof import('element-plus/es')['ElPagination'] + ElPopover: typeof import('element-plus/es')['ElPopover'] + ElSelect: typeof import('element-plus/es')['ElSelect'] + ElSelectV2: typeof import('element-plus/es')['ElSelectV2'] + ElTable: typeof import('element-plus/es')['ElTable'] + ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] + ElTag: typeof import('element-plus/es')['ElTag'] + ElTooltip: typeof import('element-plus/es')['ElTooltip'] + IEpBottom: typeof import('~icons/ep/bottom')['default'] + IEpCheck: typeof import('~icons/ep/check')['default'] + IEpCirclePlus: typeof import('~icons/ep/circle-plus')['default'] + IEpClose: typeof import('~icons/ep/close')['default'] + IEpCopyDocument: typeof import('~icons/ep/copy-document')['default'] + IEpDelete: typeof import('~icons/ep/delete')['default'] + IEpIphone: typeof import('~icons/ep/iphone')['default'] + IEpLoading: typeof import('~icons/ep/loading')['default'] + IEpMinus: typeof import('~icons/ep/minus')['default'] + IEpMonitor: typeof import('~icons/ep/monitor')['default'] + IEpMore: typeof import('~icons/ep/more')['default'] + IEpSearch: typeof import('~icons/ep/search')['default'] + IEpSort: typeof import('~icons/ep/sort')['default'] + IEpSortDown: typeof import('~icons/ep/sort-down')['default'] + IEpSortUp: typeof import('~icons/ep/sort-up')['default'] + IEpTop: typeof import('~icons/ep/top')['default'] + IEpView: typeof import('~icons/ep/view')['default'] + IEpWarningFilled: typeof import('~icons/ep/warning-filled')['default'] + RouterLink: typeof import('vue-router')['RouterLink'] + RouterView: typeof import('vue-router')['RouterView'] + } + export interface ComponentCustomProperties { + vLoading: typeof import('element-plus/es')['ElLoadingDirective'] + } +} diff --git a/web/src/management/pages/edit/modules/settingModule/SettingPanel.vue b/web/src/management/pages/edit/modules/settingModule/SettingPanel.vue index 176dbaee..629b713e 100644 --- a/web/src/management/pages/edit/modules/settingModule/SettingPanel.vue +++ b/web/src/management/pages/edit/modules/settingModule/SettingPanel.vue @@ -67,13 +67,13 @@ const setterList = computed(() => { } } else { formValue = _get(store.state.edit.schema, formKey, formItem.value) + console.log("formVaue:", formValue) dataConfig[formKey] = formValue } formItem.value = formValue } form.dataConfig = dataConfig - return form }) }) diff --git a/web/src/management/pages/edit/modules/settingModule/config/baseConfig.js b/web/src/management/pages/edit/modules/settingModule/config/baseConfig.js index bdeab24a..6af2a296 100644 --- a/web/src/management/pages/edit/modules/settingModule/config/baseConfig.js +++ b/web/src/management/pages/edit/modules/settingModule/config/baseConfig.js @@ -7,6 +7,6 @@ export default [ { title: '提交限制', key: 'limitConfig', - formList: ['limit_tLimit'] + formList: ['limit_tLimit', 'limit_breakAnswer', 'limit_backAnswer'] } ] diff --git a/web/src/management/pages/edit/modules/settingModule/config/baseFormConfig.js b/web/src/management/pages/edit/modules/settingModule/config/baseFormConfig.js index 59fed0a1..e1556f87 100644 --- a/web/src/management/pages/edit/modules/settingModule/config/baseFormConfig.js +++ b/web/src/management/pages/edit/modules/settingModule/config/baseFormConfig.js @@ -21,5 +21,19 @@ export default { tip: '问卷仅在指定时间段内可填写', type: 'QuestionTimeHour', placement: 'top' + }, + limit_breakAnswer: { + key: 'baseConf.breakAnswer', + label: '允许断点续答', + tip: '回填前一次作答中的内容(注:更换设备/浏览器/清除缓存/更改内容重新发布则此功能失效)', + type: 'ELSwitch', + value: false + }, + limit_backAnswer: { + key: 'baseConf.backAnswer', + label: '自动填充上次填写内容', + tip: '回填前一次提交的内容(注:更换设备/浏览器/清除缓存/更改内容重新发布则此功能失效)', + type: 'ELSwitch', + value: false } } diff --git a/web/src/management/store/edit/state.js b/web/src/management/store/edit/state.js index 7fcab2e6..a14554e4 100644 --- a/web/src/management/store/edit/state.js +++ b/web/src/management/store/edit/state.js @@ -42,7 +42,9 @@ export default { tLimit: 0, answerBegTime: '', answerEndTime: '', - answerLimitTime: 0 + answerLimitTime: 0, + breakAnswer: false, + backAnswer: false }, submitConf: { submitTitle: '', diff --git a/web/src/materials/setters/widgets/ELSwitch.vue b/web/src/materials/setters/widgets/ELSwitch.vue new file mode 100644 index 00000000..10347939 --- /dev/null +++ b/web/src/materials/setters/widgets/ELSwitch.vue @@ -0,0 +1,38 @@ + + + + + \ No newline at end of file diff --git a/web/src/render/components/BackAnswerDialog.vue b/web/src/render/components/BackAnswerDialog.vue new file mode 100644 index 00000000..eeafa7ee --- /dev/null +++ b/web/src/render/components/BackAnswerDialog.vue @@ -0,0 +1,79 @@ + + + + \ No newline at end of file diff --git a/web/src/render/pages/RenderPage.vue b/web/src/render/pages/RenderPage.vue index a43e0715..04b7bcdb 100644 --- a/web/src/render/pages/RenderPage.vue +++ b/web/src/render/pages/RenderPage.vue @@ -81,6 +81,17 @@ const normalizationRequestBody = () => { clientTime: Date.now() } + //浏览器缓存数据 + localStorage.removeItem(surveyPath + "_questionData") + localStorage.removeItem("isSubmit") + //数据加密 + var formData = Object.assign({}, store.state.formValues) + for(const key in formData){ + formData[key] = encodeURIComponent(formData[key]) + } + localStorage.setItem(surveyPath + "_questionData", JSON.stringify(formData)) + localStorage.setItem('isSubmit', JSON.stringify(true)) + if (encryptInfo?.encryptType) { result.encryptType = encryptInfo?.encryptType result.data = encrypt[result.encryptType as 'rsa']({ diff --git a/web/src/render/store/actions.js b/web/src/render/store/actions.js index 05f6c169..6ab52462 100644 --- a/web/src/render/store/actions.js +++ b/web/src/render/store/actions.js @@ -6,6 +6,9 @@ moment.locale('zh-cn') import adapter from '../adapter' import { queryVote, getEncryptInfo } from '@/render/api/survey' import { RuleMatch } from '@/common/logicEngine/RulesMatch' +import state from './state' +import useCommandComponent from '../hooks/useCommandComponent' +import BackAnswerDialog from '../components/BackAnswerDialog.vue' /** * CODE_MAP不从management引入,在dev阶段,会导致B端 router被加载,进而导致C端路由被添加 baseUrl: /management */ @@ -16,6 +19,9 @@ const CODE_MAP = { } const VOTE_INFO_KEY = 'voteinfo' import router from '../router' + +const confirm = useCommandComponent(BackAnswerDialog) + export default { // 初始化 init( @@ -23,7 +29,7 @@ export default { { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf } ) { commit('setEnterTime') - const { begTime, endTime, answerBegTime, answerEndTime } = baseConf + const { begTime, endTime, answerBegTime, answerEndTime, breakAnswer, backAnswer} = baseConf const { msgContent } = submitConf const now = Date.now() if (now < new Date(begTime).getTime()) { @@ -57,31 +63,72 @@ export default { } } - // 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段 - const { questionData, questionSeq, rules, formValues } = adapter.generateData({ - bannerConf, - baseConf, - bottomConf, - dataConf, - skinConf, - submitConf - }) + //回填,断点续填 + const localData = JSON.parse(localStorage.getItem(state.surveyPath + "_questionData")) - // 将数据设置到state上 - commit('assignState', { - questionData, - questionSeq, - rules, - bannerConf, - baseConf, - bottomConf, - dataConf, - skinConf, - submitConf, - formValues - }) - // 获取已投票数据 - dispatch('initVoteData') + //数据解密 + for(const key in localData){ + localData[key] = decodeURIComponent(localData[key]) + } + + const isSubmit = JSON.parse(localStorage.getItem('isSubmit')) + if(localData) { + if(isSubmit){ + if(!backAnswer) { + clearFormData({ commit, dispatch }, { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }) + } else { + confirm({ + title: "您之前已提交过问卷,是否要回填?", + onConfirm: async () => { + try { + loadFormData({ commit, dispatch }, {bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }, 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(!breakAnswer) { + clearFormData({ commit, dispatch }, { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }) + } else { + confirm({ + title: "您之前已填写部分内容, 是否要继续填写?", + onConfirm: async () => { + try { + loadFormData({ commit, dispatch }, {bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }, 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 { + clearFormData({ commit, dispatch }, { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }) + } }, // 用户输入或者选择后,更新表单数据 changeData({ commit }, data) { @@ -177,3 +224,71 @@ export default { commit('setRuleEgine', ruleEngine) } } + + // 加载上次填写过的数据到问卷页 + function loadFormData({ commit, dispatch }, {bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }, formData) { + commit('setRouter', 'indexPage') + + // 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段 + const { questionData, questionSeq, rules, formValues } = adapter.generateData({ + bannerConf, + baseConf, + bottomConf, + dataConf, + skinConf, + submitConf + }) + console.log("formdata", formData) + + for(const key in formData){ + formValues[key] = formData[key] + console.log("formValues",formValues) + } + + // 将数据设置到state上 + commit('assignState', { + questionData, + questionSeq, + rules, + bannerConf, + baseConf, + bottomConf, + dataConf, + skinConf, + submitConf, + formValues + }) + // 获取已投票数据 + dispatch('initVoteData') + } + + // 加载空白页面 + function clearFormData({ commit, dispatch }, { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }) { + commit('setRouter', 'indexPage') + + // 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段 + const { questionData, questionSeq, rules, formValues } = adapter.generateData({ + bannerConf, + baseConf, + bottomConf, + dataConf, + skinConf, + submitConf + }) + + // 将数据设置到state上 + commit('assignState', { + questionData, + questionSeq, + rules, + bannerConf, + baseConf, + bottomConf, + dataConf, + skinConf, + submitConf, + formValues + }) + // 获取已投票数据 + dispatch('initVoteData') + } diff --git a/web/src/render/store/mutations.js b/web/src/render/store/mutations.js index 4ada3d86..4a5c22ca 100644 --- a/web/src/render/store/mutations.js +++ b/web/src/render/store/mutations.js @@ -17,8 +17,19 @@ export default { }, changeFormData(state, data) { let { key, value } = data - // console.log('formValues', key, value) set(state, `formValues.${key}`, value) + + //数据加密 + var formData = Object.assign({}, state.formValues); + for(const key in formData){ + formData[key] = encodeURIComponent(formData[key]) + } + + //浏览器存储 + localStorage.removeItem(state.surveyPath + "_questionData") + localStorage.setItem(state.surveyPath + "_questionData", JSON.stringify(formData)) + localStorage.setItem('isSubmit', JSON.stringify(false)) + }, changeSelectMoreData(state, data) { const { key, value, field } = data