From 377d3d740947c59af73cb2890022aec9ea0de98c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E4=BE=9D=E5=A9=B7?= <1322922489@qq.com> Date: Mon, 10 Jun 2024 10:56:22 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E6=96=AD=E7=82=B9?= =?UTF-8?q?=E7=BB=AD=E7=AD=94=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/components.d.ts | 4 - .../modules/settingModule/SettingPanel.vue | 2 +- .../settingModule/config/baseConfig.js | 2 +- .../settingModule/config/baseFormConfig.js | 14 ++ web/src/management/store/edit/state.js | 4 +- .../materials/setters/widgets/ELSwitch.vue | 38 ++++ .../render/components/BackAnswerDialog.vue | 78 ++++++++ web/src/render/pages/IndexPage.vue | 13 ++ web/src/render/store/actions.js | 166 +++++++++++++++--- web/src/render/store/mutations.js | 13 +- 10 files changed, 299 insertions(+), 35 deletions(-) create mode 100644 web/src/materials/setters/widgets/ELSwitch.vue create mode 100644 web/src/render/components/BackAnswerDialog.vue diff --git a/web/components.d.ts b/web/components.d.ts index 8824b169..1163e3dd 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -11,7 +11,6 @@ declare module 'vue' { ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCollapse: typeof import('element-plus/es')['ElCollapse'] ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] - ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] ElDialog: typeof import('element-plus/es')['ElDialog'] @@ -25,13 +24,10 @@ declare module 'vue' { ElOption: typeof import('element-plus/es')['ElOption'] ElPagination: typeof import('element-plus/es')['ElPagination'] ElPopover: typeof import('element-plus/es')['ElPopover'] - ElRadio: typeof import('element-plus/es')['ElRadio'] ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] - ElRow: typeof import('element-plus/es')['ElRow'] ElSelect: typeof import('element-plus/es')['ElSelect'] ElSelectV2: typeof import('element-plus/es')['ElSelectV2'] - ElSlider: typeof import('element-plus/es')['ElSlider'] ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTable: typeof import('element-plus/es')['ElTable'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] 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 1927e24a..42730268 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..630308c2 --- /dev/null +++ b/web/src/render/components/BackAnswerDialog.vue @@ -0,0 +1,78 @@ + + + + \ No newline at end of file diff --git a/web/src/render/pages/IndexPage.vue b/web/src/render/pages/IndexPage.vue index 7f02b5cd..669d852c 100644 --- a/web/src/render/pages/IndexPage.vue +++ b/web/src/render/pages/IndexPage.vue @@ -79,6 +79,18 @@ 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']({ @@ -100,6 +112,7 @@ const submitSurver = async () => { const params = normalizationRequestBody() console.log(params) const res: any = await submitForm(params) + if (res.code === 200) { store.commit('setRouter', 'successPage') } else { diff --git a/web/src/render/store/actions.js b/web/src/render/store/actions.js index 3438ebea..34958c1e 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 */ @@ -15,12 +18,13 @@ const CODE_MAP = { NO_AUTH: 403 } const VOTE_INFO_KEY = 'voteinfo' +const confirm = useCommandComponent(BackAnswerDialog) export default { // 初始化 - init({ commit, dispatch }, { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }) { + init({ commit, dispatch },{ 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()) { @@ -53,33 +57,73 @@ export default { return } } - commit('setRouter', 'indexPage') - // 根据初始的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) { @@ -175,3 +219,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 33bc69f0..7ba6ab41 100644 --- a/web/src/render/store/mutations.js +++ b/web/src/render/store/mutations.js @@ -20,8 +20,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 From f5d343d420d5d41dc1dabf699ec777ff753bb512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E4=BE=9D=E5=A9=B7?= <1322922489@qq.com> Date: Tue, 18 Jun 2024 10:05:19 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E6=96=AD=E7=82=B9?= =?UTF-8?q?=E7=BB=AD=E7=AD=94=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/components.d.ts | 24 ------------------- .../render/components/BackAnswerDialog.vue | 1 + 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/web/components.d.ts b/web/components.d.ts index 1163e3dd..c054149b 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -9,51 +9,27 @@ declare module 'vue' { export interface GlobalComponents { ElButton: typeof import('element-plus/es')['ElButton'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] - ElCollapse: typeof import('element-plus/es')['ElCollapse'] - ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] - ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] - ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] 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'] - ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] 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'] - ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] - ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] ElSelect: typeof import('element-plus/es')['ElSelect'] ElSelectV2: typeof import('element-plus/es')['ElSelectV2'] - ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTable: typeof import('element-plus/es')['ElTable'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] - ElTabPane: typeof import('element-plus/es')['ElTabPane'] - ElTabs: typeof import('element-plus/es')['ElTabs'] ElTag: typeof import('element-plus/es')['ElTag'] - ElTimePicker: typeof import('element-plus/es')['ElTimePicker'] 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'] - IEpLoading: typeof import('~icons/ep/loading')['default'] - IEpMinus: typeof import('~icons/ep/minus')['default'] IEpMore: typeof import('~icons/ep/more')['default'] - IEpPlus: typeof import('~icons/ep/plus')['default'] - IEpQuestionFilled: typeof import('~icons/ep/question-filled')['default'] - IEpRank: typeof import('~icons/ep/rank')['default'] - IEpRemove: typeof import('~icons/ep/remove')['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'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] } diff --git a/web/src/render/components/BackAnswerDialog.vue b/web/src/render/components/BackAnswerDialog.vue index 630308c2..eeafa7ee 100644 --- a/web/src/render/components/BackAnswerDialog.vue +++ b/web/src/render/components/BackAnswerDialog.vue @@ -19,6 +19,7 @@ interface Emit { (ev: 'confirm', callback: () => void): void + (ev: 'cancel', callback: () => void): void (ev: 'close'): void } From aa0c4b8afc7a7a683b03bfecb155a5fb09dfc53c Mon Sep 17 00:00:00 2001 From: dayou <853094838@qq.com> Date: Fri, 21 Jun 2024 11:41:50 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=E5=90=8C=E6=AD=A5=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=B9=B6=E4=B8=94=E8=A7=A3=E5=86=B3=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- README_EN.md | 4 +- nginx/nginx.conf | 5 + server/package.json | 4 + .../survey/controllers/survey.controller.ts | 32 +++ web/components.d.ts | 12 ++ web/public/imgs/preview-phone.png | Bin 0 -> 7320 bytes web/src/common/typeEnum.ts | 50 +++++ web/src/management/api/space.ts | 5 +- web/src/management/components/LeftMenu.vue | 14 +- .../pages/edit/components/ModuleNavbar.vue | 2 + .../modules/contentModule/PreviewPanel.vue | 177 ++++++++++++++++ .../edit/modules/generalModule/BackPanel.vue | 8 +- .../logicModule/components/ConditionView.vue | 25 +-- .../logicModule/components/RuleNodeView.vue | 4 +- .../questionModule/components/TypeList.vue | 6 +- .../pages/list/components/BaseList.vue | 2 +- .../pages/list/components/SpaceList.vue | 5 +- .../pages/list/components/SpaceModify.vue | 1 - .../management/pages/publish/PublishPage.vue | 2 +- web/src/management/router/index.ts | 97 ++++----- web/src/management/store/actions.js | 1 - .../utils/{constant.js => constant.ts} | 9 +- web/src/management/utils/index.js | 3 +- .../questions/common/config/tagList.js | 15 -- .../EditOptions/Options/OptionEditBar.vue | 4 +- .../TitleModules/TitleContent/index.jsx | 4 +- .../questions/widgets/VoteModule/index.jsx | 4 +- .../materials/setters/widgets/InputNumber.vue | 4 +- web/src/render/App.vue | 83 +------- web/src/render/adapter/formValue.js | 3 +- web/src/render/adapter/question.js | 4 +- web/src/render/adapter/rules.js | 11 +- web/src/render/api/survey.js | 8 + web/src/render/components/MaterialGroup.vue | 3 - web/src/render/components/QuestionWrapper.vue | 8 +- web/src/render/constant/index.js | 14 -- web/src/render/main.js | 4 +- web/src/render/pages/EmptyPage.vue | 6 +- web/src/render/pages/ErrorPage.vue | 44 ++-- web/src/render/pages/IndexPage.vue | 196 +++++------------- web/src/render/pages/RenderPage.vue | 173 ++++++++++++++++ web/src/render/router/index.ts | 38 ++++ web/src/render/store/actions.js | 12 +- web/src/render/store/getters.js | 1 - web/src/render/store/mutations.js | 3 - web/src/render/store/state.js | 1 - web/vite.config.ts | 4 + 48 files changed, 706 insertions(+), 411 deletions(-) create mode 100644 web/public/imgs/preview-phone.png create mode 100644 web/src/common/typeEnum.ts create mode 100644 web/src/management/pages/edit/modules/contentModule/PreviewPanel.vue rename web/src/management/utils/{constant.js => constant.ts} (52%) delete mode 100644 web/src/materials/questions/common/config/tagList.js delete mode 100644 web/src/render/constant/index.js create mode 100644 web/src/render/pages/RenderPage.vue create mode 100644 web/src/render/router/index.ts diff --git a/README.md b/README.md index 0973f75d..ddd9e1ab 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ docs - docs + docs diff --git a/README_EN.md b/README_EN.md index b69c0da0..b827ecf7 100644 --- a/README_EN.md +++ b/README_EN.md @@ -19,10 +19,10 @@ pr - docs + docs - docs + docs diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 88c6265d..34854cdf 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -39,6 +39,11 @@ http { try_files $uri $uri/ /management.html; } + location /management/preview/ { + try_files $uri $uri/ /render.html; + } + + location /render/ { try_files $uri $uri/ /render.html; } diff --git a/server/package.json b/server/package.json index 44164d74..f6aade83 100644 --- a/server/package.json +++ b/server/package.json @@ -97,5 +97,9 @@ "moduleNameMapper": { "^src/(.*)$": "/$1" } + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.6.0" } } diff --git a/server/src/modules/survey/controllers/survey.controller.ts b/server/src/modules/survey/controllers/survey.controller.ts index c8b59fb2..df1a3d62 100644 --- a/server/src/modules/survey/controllers/survey.controller.ts +++ b/server/src/modules/survey/controllers/survey.controller.ts @@ -223,6 +223,38 @@ export class SurveyController { }; } + @Get('/getPreviewSchema') + @HttpCode(200) + async getPreviewSchema( + @Query() + queryInfo: { + surveyPath: string; + }, + @Request() + req, + ) { + const { value, error } = Joi.object({ + surveyId: Joi.string().required(), + }).validate({ surveyId: queryInfo.surveyPath }); + + if (error) { + this.logger.error(error.message, { req }); + throw new HttpException('参数有误', EXCEPTION_CODE.PARAMETER_ERROR); + } + const surveyId = value.surveyId; + const surveyConf = + await this.surveyConfService.getSurveyConfBySurveyId(surveyId); + const surveyMeta = await this.surveyMetaService.getSurveyById({ surveyId }); + return { + code: 200, + data: { + ...surveyConf, + title: surveyMeta?.title, + surveyPath: surveyMeta?.surveyPath, + }, + }; + } + @Post('/publishSurvey') @HttpCode(200) @UseGuards(SurveyGuard) diff --git a/web/components.d.ts b/web/components.d.ts index c054149b..259d398d 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -25,11 +25,23 @@ declare module 'vue' { 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'] + 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'] } diff --git a/web/public/imgs/preview-phone.png b/web/public/imgs/preview-phone.png new file mode 100644 index 0000000000000000000000000000000000000000..375bf1b52f21b1bc8908a667f238e53e4ebe4665 GIT binary patch literal 7320 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⪚IPmv0-cc7BIQ>1hHk?2-r9yooS@+X)LL+I)_-+ zZ*%`u<0)3OM&;}c0eM%tzMXUSO4UAE^h(#-?;}lZ`~}dOsN-B+BbV^u3aCdum7_bG zIwwWWeV}WRE3Uq{09@SKX=;=)cf5tkCTSQ2rLXZAOlNzH3$YdoG-tpfYQl;JOQl4Rhf{aM%>F=R{$6Mz`y33Yy^jz(7QY-AZYm4=#4CcK)E{74;+XjBco13q=G{#HZ|irbnyGSJ}}O9IhCA zqRm@RS7aJ8XHr{%p_6JZ4JS40&$QQANtZC2-68(IGLn2Y zoN&zdOzeBHCTD$mOFta);2Yn-K!}6~YbfVq2uGVi{OIjz1n*TN8$x?fJ%t$Oo{Jl) zcfPM-So55;yUX5>UA8SA3PNiDtEX#D41G1Yp=`NOkI|xipjJ_f%RHlFQKvXoqjo7flru`<816Z zyccEr9!;qF#RWx&l?f%6%L~UfZh(A5Op%>Gzik44t^(8Ns)9+H>%sh1@AM#K*+cNX zC2LDGKd2LG=BdCGThd@vsCk)wC&N~ODWHg28+6WQV)Ct6Q$FAh#AE8|s3E)gu3hsh zkc+M@5VDdyH8c*|TT1OWOZ@_J>EpZ45?e(Pa)*<{0GexJ2jZ+m0!KdEsb6mcg;l8n z?qtSxkY0CWELUSRYq=6bX|Hg=wvW)WTs1yOu97CpCW$zLAEem#0jCktGR)^{=X|&0 zmQ#oEiMHyPbfz$0geRuLpu-~nDi8hVWDDI^oo+jTmqOL}mI9v~J} n+Nc^RMkBAkEyrv=LGzqaYNvTW_BogQ@3ufyNef;AvkdqjZ+&Uy literal 0 HcmV?d00001 diff --git a/web/src/common/typeEnum.ts b/web/src/common/typeEnum.ts new file mode 100644 index 00000000..755e6f24 --- /dev/null +++ b/web/src/common/typeEnum.ts @@ -0,0 +1,50 @@ +// 题型枚举 +export enum QUESTION_TYPE { + TEXT = 'text', + TEXTAREA = 'textarea', + RADIO = 'radio', + CHECKBOX = 'checkbox', + BINARY_CHOICE = 'binary-choice', + RADIO_STAR = 'radio-star', + RADIO_NPS = 'radio-nps', + VOTE = 'vote', +} + +// 题目类型标签映射对象 +export const typeTagLabels: Record = { + [QUESTION_TYPE.TEXT]: '单行输入框', + [QUESTION_TYPE.TEXTAREA]: '多行输入框', + [QUESTION_TYPE.RADIO]: '单选', + [QUESTION_TYPE.CHECKBOX]: '多选', + [QUESTION_TYPE.BINARY_CHOICE]: '判断', + [QUESTION_TYPE.RADIO_STAR]: '评分', + [QUESTION_TYPE.RADIO_NPS]: 'NPS评分', + [QUESTION_TYPE.VOTE]: '投票' +} + +// 输入类题型 +export const INPUT = [ + QUESTION_TYPE.TEXT, + QUESTION_TYPE.TEXTAREA +] + +// 选择类题型分类 +export const NORMAL_CHOICES = [ + QUESTION_TYPE.RADIO, + QUESTION_TYPE.CHECKBOX +] + +// 选择类题型分类 +export const CHOICES = [ + QUESTION_TYPE.RADIO, + QUESTION_TYPE.CHECKBOX, + QUESTION_TYPE.BINARY_CHOICE, + QUESTION_TYPE.VOTE +] + +// 评分题题型分类 +export const RATES = [ + QUESTION_TYPE.RADIO_STAR, + QUESTION_TYPE.RADIO_NPS +] + diff --git a/web/src/management/api/space.ts b/web/src/management/api/space.ts index 24de4466..b0aadda9 100644 --- a/web/src/management/api/space.ts +++ b/web/src/management/api/space.ts @@ -1,5 +1,4 @@ import axios from './base' - // 空间 export const createSpace = ({ name, description, members }: any) => { return axios.post('/workspace', { name, description, members }) @@ -29,7 +28,7 @@ export const getUserList = (username: string) => { }) } -// 协作权限列表 +// 获取协作权限下拉框枚举 export const getPermissionList = () => { return axios.get('collaborator/getPermissionList') } @@ -72,4 +71,4 @@ export const getCollaboratorPermissions = (surveyId: string) => { surveyId } }) -} +} \ No newline at end of file diff --git a/web/src/management/components/LeftMenu.vue b/web/src/management/components/LeftMenu.vue index 7f631fe6..e9dc05ab 100644 --- a/web/src/management/components/LeftMenu.vue +++ b/web/src/management/components/LeftMenu.vue @@ -26,7 +26,7 @@ diff --git a/web/src/management/pages/edit/modules/generalModule/BackPanel.vue b/web/src/management/pages/edit/modules/generalModule/BackPanel.vue index 60a595dd..ff728371 100644 --- a/web/src/management/pages/edit/modules/generalModule/BackPanel.vue +++ b/web/src/management/pages/edit/modules/generalModule/BackPanel.vue @@ -5,7 +5,13 @@ diff --git a/web/src/render/pages/IndexPage.vue b/web/src/render/pages/IndexPage.vue index 669d852c..338f03a5 100644 --- a/web/src/render/pages/IndexPage.vue +++ b/web/src/render/pages/IndexPage.vue @@ -1,169 +1,71 @@ - diff --git a/web/src/render/pages/RenderPage.vue b/web/src/render/pages/RenderPage.vue new file mode 100644 index 00000000..04b7bcdb --- /dev/null +++ b/web/src/render/pages/RenderPage.vue @@ -0,0 +1,173 @@ + + + diff --git a/web/src/render/router/index.ts b/web/src/render/router/index.ts new file mode 100644 index 00000000..01d8f095 --- /dev/null +++ b/web/src/render/router/index.ts @@ -0,0 +1,38 @@ +import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router' + +const routes: RouteRecordRaw[] = [ + { + path: '/:surveyId', + component: () => import('../pages/IndexPage.vue'), + children: [ + { + path: '', + name: 'renderPage', + component: () => import('../pages/RenderPage.vue') + }, + { + path: 'success', + name: 'successPage', + component: () => import('../pages/SuccessPage.vue') + }, + { + path: 'error', + name: 'errorPage', + component: () => import('../pages/ErrorPage.vue') + } + ] + }, + { + path: '/:catchAll(.*)', + name: 'emptyPage', + component: () => import('../pages/EmptyPage.vue') + } +] +// 兼容预览模式 +const base = window.location.pathname.includes('preview') ? 'management/preview' : 'render' +const router = createRouter({ + history: createWebHistory(base), + routes +}) + +export default router diff --git a/web/src/render/store/actions.js b/web/src/render/store/actions.js index 34958c1e..8f14cf8a 100644 --- a/web/src/render/store/actions.js +++ b/web/src/render/store/actions.js @@ -18,17 +18,21 @@ const CODE_MAP = { NO_AUTH: 403 } const VOTE_INFO_KEY = 'voteinfo' +import router from '../router' const confirm = useCommandComponent(BackAnswerDialog) export default { // 初始化 - init({ commit, dispatch },{ bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf}) { + init( + { commit, dispatch }, + { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf } + ) { commit('setEnterTime') const { begTime, endTime, answerBegTime, answerEndTime, breakAnswer, backAnswer} = baseConf const { msgContent } = submitConf const now = Date.now() if (now < new Date(begTime).getTime()) { - commit('setRouter', 'errorPage') + router.push({ name: 'errorPage' }) commit('setErrorInfo', { errorType: 'overTime', errorMsg: `

问卷未到开始填写时间,暂时无法进行填写

@@ -36,7 +40,7 @@ export default { }) return } else if (now > new Date(endTime).getTime()) { - commit('setRouter', 'errorPage') + router.push({ name: 'errorPage' }) commit('setErrorInfo', { errorType: 'overTime', errorMsg: msgContent.msg_9001 || '您来晚了,感谢支持问卷~' @@ -48,7 +52,7 @@ export default { const momentStartTime = moment(`${todayStr} ${answerBegTime}`) const momentEndTime = moment(`${todayStr} ${answerEndTime}`) if (momentNow.isBefore(momentStartTime) || momentNow.isAfter(momentEndTime)) { - commit('setRouter', 'errorPage') + router.push({ name: 'errorPage' }) commit('setErrorInfo', { errorType: 'overTime', errorMsg: `

不在答题时间范围内,暂时无法进行填写

diff --git a/web/src/render/store/getters.js b/web/src/render/store/getters.js index 0dd0a32f..79d8a23f 100644 --- a/web/src/render/store/getters.js +++ b/web/src/render/store/getters.js @@ -10,7 +10,6 @@ export default { const questionArr = [] item.forEach((questionKey) => { - console.log('题目重新计算') const question = { ...questionData[questionKey] } // 开启显示序号 if (question.showIndex) { diff --git a/web/src/render/store/mutations.js b/web/src/render/store/mutations.js index 7ba6ab41..4a5c22ca 100644 --- a/web/src/render/store/mutations.js +++ b/web/src/render/store/mutations.js @@ -9,9 +9,6 @@ export default { setQuestionData(state, data) { state.questionData = data }, - setRouter(state, data) { - state.router = data - }, setErrorInfo(state, { errorType, errorMsg }) { state.errorInfo = { errorType, diff --git a/web/src/render/store/state.js b/web/src/render/store/state.js index 233de0d3..2a63c735 100644 --- a/web/src/render/store/state.js +++ b/web/src/render/store/state.js @@ -3,7 +3,6 @@ import { isMobile } from '../utils/index' export default { surveyPath: '', questionData: null, - router: '', isMobile: isMobile(), errorInfo: { errorType: '', diff --git a/web/vite.config.ts b/web/vite.config.ts index d7e5a8d8..1d36bdcf 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -34,6 +34,10 @@ const mpaPlugin = createMpaPlugin({ from: /render/, to: () => normalizePath('/src/render/index.html') }, + { + from: /management\/preview/, + to: () => normalizePath('/src/render/index.html') + }, { from: /\/|\/management\/.?/, to: () => normalizePath('/src/management/index.html')