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 @@
+
+
+
+
{{ title }}
+
+
{{ cancelBtnText }}
+
{{ confirmBtnText }}
+
+
+
+
+
+
+
\ 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 @@
-
+
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 @@
-
+
-
+
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')