feat: c端路由改造 (#296)

This commit is contained in:
dayou 2024-06-14 19:20:36 +08:00 committed by sudoooooo
parent 32b1c4888d
commit 053d9751c3
11 changed files with 296 additions and 280 deletions

View File

@ -1,8 +1,8 @@
<template> <template>
<div <div
class="condition-wrapper" class="condition-wrapper"
:class="{ 'is-last': isLastCondition }" :class="{ 'is-last': isLastCondition }"
:data-content-before="!isLastCondition ? '且' : ''" :data-content-before="!isLastCondition ? '且' : ''"
> >
<span class="desc">如果</span> <span class="desc">如果</span>
<el-form-item <el-form-item
@ -15,16 +15,9 @@
placeholder="请选择题目" placeholder="请选择题目"
@change="(val: any) => handleChange(conditionNode, 'field', val)" @change="(val: any) => handleChange(conditionNode, 'field', val)"
> >
<el-option <el-option v-for="{ label, value } in fieldList" :key="value" :label="label" :value="value">
v-for="{ label, value } in fieldList"
:key="value"
:label="label"
:value="value"
>
</el-option> </el-option>
<template #empty> <template #empty> 无数据 </template>
无数据
</template>
</el-select> </el-select>
</el-form-item> </el-form-item>
<span class="desc">选择了</span> <span class="desc">选择了</span>
@ -45,9 +38,7 @@
:label="label" :label="label"
:value="value" :value="value"
></el-option> ></el-option>
<template #empty> <template #empty> 无数据 </template>
无数据
</template>
</el-select> </el-select>
</el-form-item> </el-form-item>
<span class="desc">中的任一选项 </span> <span class="desc">中的任一选项 </span>

View File

@ -36,9 +36,7 @@
:value="value" :value="value"
> >
</el-option> </el-option>
<template #empty> <template #empty> 无数据 </template>
无数据
</template>
</el-select> </el-select>
</el-form-item> </el-form-item>
</div> </div>

View File

@ -1,49 +1,15 @@
<template> <template>
<div id="app"> <div id="app">
<Component <router-view></router-view>
v-if="store.state.router"
:is="
components[
upperFirst(store.state.router) as 'IndexPage' | 'EmptyPage' | 'ErrorPage' | 'SuccessPage'
]
"
>
</Component>
<LogoIcon
v-if="!['successPage', 'indexPage'].includes(store.state.router)"
:logo-conf="logoConf"
:readonly="true"
/>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, watch, onMounted } from 'vue' import { computed, watch } from 'vue'
import { useStore } from 'vuex' import { useStore } from 'vuex'
import { get as _get } from 'lodash-es'
import { getPublishedSurveyInfo, getPreviewSchema } from './api/survey'
import useCommandComponent from './hooks/useCommandComponent'
import EmptyPage from './pages/EmptyPage.vue'
import IndexPage from './pages/IndexPage.vue'
import ErrorPage from './pages/ErrorPage.vue'
import SuccessPage from './pages/SuccessPage.vue'
import AlertDialog from './components/AlertDialog.vue'
// @ts-ignore
import communalLoader from '@materials/communals/communalLoader.js'
import { get as _get, upperFirst } from 'lodash-es'
import { initRuleEngine } from '@/render/hooks/useRuleEngine.js'
const LogoIcon = communalLoader.loadComponent('LogoIcon')
const store = useStore() const store = useStore()
const logoConf = computed(() => store.state?.bottomConf || {})
const skinConf = computed(() => _get(store, 'state.skinConf', {})) const skinConf = computed(() => _get(store, 'state.skinConf', {}))
const components = {
EmptyPage,
IndexPage,
ErrorPage,
SuccessPage
}
const updateSkinConfig = (value: any) => { const updateSkinConfig = (value: any) => {
const root = document.documentElement const root = document.documentElement
@ -65,66 +31,9 @@ const updateSkinConfig = (value: any) => {
} }
} }
const loadData = (res: any, surveyPath: string) => {
if (res.code === 200) {
const data = res.data
const {
bannerConf,
baseConf,
bottomConf,
dataConf,
skinConf,
submitConf,
logicConf
} = data.code
const questionData = {
bannerConf,
baseConf,
bottomConf,
dataConf,
skinConf,
submitConf
}
document.title = data.title
updateSkinConfig(skinConf)
store.commit('setSurveyPath', surveyPath)
store.dispatch('init', questionData)
initRuleEngine(logicConf?.showLogicConf)
} else {
throw new Error(res.errmsg)
}
}
watch(skinConf, (value) => { watch(skinConf, (value) => {
updateSkinConfig(value) updateSkinConfig(value)
}) })
onMounted(async () => {
const surveyPath = location.pathname.split('/').pop()
if (!surveyPath) {
store.commit('setRouter', 'EmptyPage')
return
}
const alert = useCommandComponent(AlertDialog)
try {
if (surveyPath.length > 8) {
const res: any = await getPreviewSchema({ surveyPath })
loadData(res, surveyPath)
} else {
const res: any = await getPublishedSurveyInfo({ surveyPath })
loadData(res, surveyPath)
store.dispatch('getEncryptInfo')
}
} catch (error: any) {
console.log(error)
alert({ title: error.message || '获取问卷失败' })
}
})
</script> </script>
<style lang="scss"> <style lang="scss">
@import url('./styles/icon.scss'); @import url('./styles/icon.scss');

View File

@ -1,7 +1,7 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import EventBus from './utils/eventbus' import EventBus from './utils/eventbus'
import router from './router'
import store from './store' import store from './store'
const app = createApp(App) const app = createApp(App)
@ -10,7 +10,7 @@ const $bus = new EventBus()
app.provide('$bus', $bus) app.provide('$bus', $bus)
// 挂载到this上 // 挂载到this上
app.config.globalProperties.$bus = $bus app.config.globalProperties.$bus = $bus
app.use(router)
app.use(store) app.use(store)
app.mount('#app') app.mount('#app')

View File

@ -1,16 +1,21 @@
<template> <template>
<div class="result-page-wrap"> <div class="result-page-wrap">
<div class="result-page"> <div class="result-page">
<div class="error-wrapper"> <div class="page-content">
<img class="error-img" :src="errorImageUrl" /> <img class="img" :src="errorImageUrl" />
<div class="bottom-word" v-html="errorMsg"></div> <div class="msg" v-html="errorMsg"></div>
</div> </div>
<LogoIcon :logo-conf="logoConf" :readonly="true" />
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { useStore } from 'vuex' import { useStore } from 'vuex'
// @ts-ignore
import communalLoader from '@materials/communals/communalLoader.js'
const LogoIcon = communalLoader.loadComponent('LogoIcon')
const store = useStore() const store = useStore()
@ -24,7 +29,8 @@ const errorImageUrl = computed(() => {
return imageMap[errorType as 'overTime'] || imageMap.default return imageMap[errorType as 'overTime'] || imageMap.default
}) })
const errorMsg = computed(() => store.state?.errorInfo?.errorMsg) const errorMsg = computed(() => store.state?.errorInfo?.errorMsg || '提交失败')
const logoConf = computed(() => store.state?.bottomConf || {})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.result-page-wrap { .result-page-wrap {
@ -42,25 +48,27 @@ const errorMsg = computed(() => store.state?.errorInfo?.errorMsg)
height: 100%; height: 100%;
} }
} }
.error-wrapper { .page-content {
text-align: center; position: relative;
font-size: 14px; max-width: 920px;
margin: 0 auto;
height: 100%;
width: 100%;
position: relative;
padding-top: 2rem;
flex: 1; flex: 1;
p { .img {
text-align: center;
}
.error-img {
margin-top: 2rem;
margin-bottom: 30px;
width: 2rem; width: 2rem;
height: auto;
} }
.bottom-word { .msg {
color: #999; font-size: 0.32rem;
margin-top: 10px; color: #4a4c5b;
letter-spacing: 0;
text-align: center;
font-weight: 500;
margin-top: 0.15rem;
} }
} }
</style> </style>

View File

@ -1,159 +1,71 @@
<template> <template>
<div class="index"> <router-view></router-view>
<ProgressBar />
<div class="wrapper" ref="boxRef">
<HeaderContent :bannerConf="bannerConf" :readonly="true" />
<div class="content">
<MainTitle :bannerConf="bannerConf" :readonly="true"></MainTitle>
<MainRenderer ref="mainRef"></MainRenderer>
<SubmitButton
:validate="validate"
:submitConf="submitConf"
:readonly="true"
:renderData="renderData"
@submit="handleSubmit"
></SubmitButton>
<LogoIcon :logo-conf="logoConf" :readonly="true" />
</div>
</div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { onMounted } from 'vue'
import { useStore } from 'vuex' import { useStore } from 'vuex'
// @ts-ignore import { useRoute } from 'vue-router'
import communalLoader from '@materials/communals/communalLoader.js'
import MainRenderer from '../components/MainRenderer.vue'
import AlertDialog from '../components/AlertDialog.vue'
import ConfirmDialog from '../components/ConfirmDialog.vue'
import ProgressBar from '../components/ProgressBar.vue'
import { submitForm } from '../api/survey'
import encrypt from '../utils/encrypt'
import { getPublishedSurveyInfo, getPreviewSchema } from '../api/survey'
import useCommandComponent from '../hooks/useCommandComponent' import useCommandComponent from '../hooks/useCommandComponent'
interface Props { import AlertDialog from '../components/AlertDialog.vue'
questionInfo?: any import { initRuleEngine } from '@/render/hooks/useRuleEngine.js'
isMobile?: boolean
}
withDefaults(defineProps<Props>(), {
questionInfo: {},
isMobile: false
})
const HeaderContent = communalLoader.loadComponent('HeaderContent')
const MainTitle = communalLoader.loadComponent('MainTitle')
const SubmitButton = communalLoader.loadComponent('SubmitButton')
const LogoIcon = communalLoader.loadComponent('LogoIcon')
const mainRef = ref<any>()
const boxRef = ref<HTMLElement>()
const alert = useCommandComponent(AlertDialog)
const confirm = useCommandComponent(ConfirmDialog)
const store = useStore() const store = useStore()
const route = useRoute()
const bannerConf = computed(() => store.state?.bannerConf || {}) const loadData = (res: any, surveyPath: string) => {
const renderData = computed(() => store.getters.renderData) if (res.code === 200) {
const submitConf = computed(() => store.state?.submitConf || {}) const data = res.data
const logoConf = computed(() => store.state?.bottomConf || {}) const {
const surveyPath = computed(() => store.state?.surveyPath || '') bannerConf,
baseConf,
const validate = (cbk: (v: boolean) => void) => { bottomConf,
const index = 0 dataConf,
mainRef.value.$refs.formGroup[index].validate(cbk) skinConf,
} submitConf,
logicConf
const normalizationRequestBody = () => { } = data.code
const enterTime = store.state.enterTime const questionData = {
const encryptInfo = store.state.encryptInfo bannerConf,
const formValues = store.state.formValues baseConf,
bottomConf,
const result: any = { dataConf,
surveyPath: surveyPath.value, skinConf,
data: JSON.stringify(formValues), submitConf
difTime: Date.now() - enterTime,
clientTime: Date.now()
}
if (encryptInfo?.encryptType) {
result.encryptType = encryptInfo?.encryptType
result.data = encrypt[result.encryptType as 'rsa']({
data: result.data,
secretKey: encryptInfo?.data?.secretKey
})
if (encryptInfo?.data?.sessionId) {
result.sessionId = encryptInfo.data.sessionId
} }
document.title = data.title
store.commit('setSurveyPath', surveyPath)
store.dispatch('init', questionData)
initRuleEngine(logicConf?.showLogicConf)
} else { } else {
result.data = JSON.stringify(result.data) throw new Error(res.errmsg)
} }
return result
} }
onMounted(() => {
const surveyId = route.params.surveyId
console.log({ surveyId })
store.commit('setSurveyPath', surveyId)
getDetail(surveyId as string)
})
const getDetail = async (surveyPath: string) => {
const alert = useCommandComponent(AlertDialog)
const submitSurver = async () => {
if (surveyPath.value.length > 8) {
store.commit('setRouter', 'successPage')
return
}
try { try {
const params = normalizationRequestBody() if (surveyPath.length > 8) {
const res: any = await submitForm(params) const res: any = await getPreviewSchema({ surveyPath })
if (res.code === 200) { loadData(res, surveyPath)
store.commit('setRouter', 'successPage')
} else { } else {
alert({ const res: any = await getPublishedSurveyInfo({ surveyPath })
title: res.errmsg || '提交失败' loadData(res, surveyPath)
}) store.dispatch('getEncryptInfo')
} }
} catch (error) { } catch (error: any) {
console.log(error) console.log(error)
} alert({ title: error.message || '获取问卷失败' })
}
const handleSubmit = () => {
const confirmAgain = store.state.submitConf.confirmAgain
const { again_text, is_again } = confirmAgain
if (is_again) {
confirm({
title: again_text,
onConfirm: async () => {
try {
submitSurver()
} catch (error) {
console.log(error)
} finally {
confirm.close()
}
}
})
} else {
submitSurver()
} }
} }
</script> </script>
<style scoped lang="scss">
.index {
min-height: 100%;
.wrapper {
min-height: 100%;
background-color: var(--primary-background-color);
display: flex;
flex-direction: column;
.content {
flex: 1;
margin: 0 0.3rem;
background: rgba(255, 255, 255, var(--opacity));
border-radius: 8px 8px 0 0;
height: 100%;
}
}
}
</style>

View File

@ -0,0 +1,162 @@
<template>
<div class="index">
<ProgressBar />
<div class="wrapper" ref="boxRef">
<HeaderContent :bannerConf="bannerConf" :readonly="true" />
<div class="content">
<MainTitle :bannerConf="bannerConf" :readonly="true"></MainTitle>
<MainRenderer ref="mainRef"></MainRenderer>
<SubmitButton
:validate="validate"
:submitConf="submitConf"
:readonly="true"
:renderData="renderData"
@submit="handleSubmit"
></SubmitButton>
<LogoIcon :logo-conf="logoConf" :readonly="true" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useStore } from 'vuex'
import { useRouter } from 'vue-router'
// @ts-ignore
import communalLoader from '@materials/communals/communalLoader.js'
import MainRenderer from '../components/MainRenderer.vue'
import AlertDialog from '../components/AlertDialog.vue'
import ConfirmDialog from '../components/ConfirmDialog.vue'
import ProgressBar from '../components/ProgressBar.vue'
import { submitForm } from '../api/survey'
import encrypt from '../utils/encrypt'
import useCommandComponent from '../hooks/useCommandComponent'
interface Props {
questionInfo?: any
isMobile?: boolean
}
withDefaults(defineProps<Props>(), {
questionInfo: {},
isMobile: false
})
const HeaderContent = communalLoader.loadComponent('HeaderContent')
const MainTitle = communalLoader.loadComponent('MainTitle')
const SubmitButton = communalLoader.loadComponent('SubmitButton')
const LogoIcon = communalLoader.loadComponent('LogoIcon')
const mainRef = ref<any>()
const boxRef = ref<HTMLElement>()
const alert = useCommandComponent(AlertDialog)
const confirm = useCommandComponent(ConfirmDialog)
const store = useStore()
const router = useRouter()
const bannerConf = computed(() => store.state?.bannerConf || {})
const renderData = computed(() => store.getters.renderData)
const submitConf = computed(() => store.state?.submitConf || {})
const logoConf = computed(() => store.state?.bottomConf || {})
const surveyPath = computed(() => store.state?.surveyPath || '')
const validate = (cbk: (v: boolean) => void) => {
const index = 0
mainRef.value.$refs.formGroup[index].validate(cbk)
}
const normalizationRequestBody = () => {
const enterTime = store.state.enterTime
const encryptInfo = store.state.encryptInfo
const formValues = store.state.formValues
const result: any = {
surveyPath: surveyPath.value,
data: JSON.stringify(formValues),
difTime: Date.now() - enterTime,
clientTime: Date.now()
}
if (encryptInfo?.encryptType) {
result.encryptType = encryptInfo?.encryptType
result.data = encrypt[result.encryptType as 'rsa']({
data: result.data,
secretKey: encryptInfo?.data?.secretKey
})
if (encryptInfo?.data?.sessionId) {
result.sessionId = encryptInfo.data.sessionId
}
} else {
result.data = JSON.stringify(result.data)
}
return result
}
const submitSurver = async () => {
if (surveyPath.value.length > 8) {
router.push({ name: 'successPage' })
return
}
try {
const params = normalizationRequestBody()
console.log(params)
const res: any = await submitForm(params)
if (res.code === 200) {
router.push({ name: 'successPage' })
} else {
alert({
title: res.errmsg || '提交失败'
})
}
} catch (error) {
console.log(error)
}
}
const handleSubmit = () => {
const confirmAgain = store.state.submitConf.confirmAgain
const { again_text, is_again } = confirmAgain
if (is_again) {
confirm({
title: again_text,
onConfirm: async () => {
try {
submitSurver()
} catch (error) {
console.log(error)
} finally {
confirm.close()
}
}
})
} else {
submitSurver()
}
}
</script>
<style scoped lang="scss">
.index {
min-height: 100%;
.wrapper {
min-height: 100%;
background-color: var(--primary-background-color);
display: flex;
flex-direction: column;
.content {
flex: 1;
margin: 0 0.3rem;
background: rgba(255, 255, 255, var(--opacity));
border-radius: 8px 8px 0 0;
height: 100%;
}
}
}
</style>

View File

@ -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

View File

@ -15,16 +15,19 @@ const CODE_MAP = {
NO_AUTH: 403 NO_AUTH: 403
} }
const VOTE_INFO_KEY = 'voteinfo' const VOTE_INFO_KEY = 'voteinfo'
import router from '../router'
export default { export default {
// 初始化 // 初始化
init({ commit, dispatch }, { bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }) { init(
{ commit, dispatch },
{ bannerConf, baseConf, bottomConf, dataConf, skinConf, submitConf }
) {
commit('setEnterTime') commit('setEnterTime')
const { begTime, endTime, answerBegTime, answerEndTime } = baseConf const { begTime, endTime, answerBegTime, answerEndTime } = baseConf
const { msgContent } = submitConf const { msgContent } = submitConf
const now = Date.now() const now = Date.now()
if (now < new Date(begTime).getTime()) { if (now < new Date(begTime).getTime()) {
commit('setRouter', 'errorPage') router.push({ name: 'errorPage' })
commit('setErrorInfo', { commit('setErrorInfo', {
errorType: 'overTime', errorType: 'overTime',
errorMsg: `<p>问卷未到开始填写时间,暂时无法进行填写<p/> errorMsg: `<p>问卷未到开始填写时间,暂时无法进行填写<p/>
@ -32,7 +35,7 @@ export default {
}) })
return return
} else if (now > new Date(endTime).getTime()) { } else if (now > new Date(endTime).getTime()) {
commit('setRouter', 'errorPage') router.push({ name: 'errorPage' })
commit('setErrorInfo', { commit('setErrorInfo', {
errorType: 'overTime', errorType: 'overTime',
errorMsg: msgContent.msg_9001 || '您来晚了,感谢支持问卷~' errorMsg: msgContent.msg_9001 || '您来晚了,感谢支持问卷~'
@ -44,7 +47,7 @@ export default {
const momentStartTime = moment(`${todayStr} ${answerBegTime}`) const momentStartTime = moment(`${todayStr} ${answerBegTime}`)
const momentEndTime = moment(`${todayStr} ${answerEndTime}`) const momentEndTime = moment(`${todayStr} ${answerEndTime}`)
if (momentNow.isBefore(momentStartTime) || momentNow.isAfter(momentEndTime)) { if (momentNow.isBefore(momentStartTime) || momentNow.isAfter(momentEndTime)) {
commit('setRouter', 'errorPage') router.push({ name: 'errorPage' })
commit('setErrorInfo', { commit('setErrorInfo', {
errorType: 'overTime', errorType: 'overTime',
errorMsg: `<p>不在答题时间范围内,暂时无法进行填写<p/> errorMsg: `<p>不在答题时间范围内,暂时无法进行填写<p/>
@ -53,7 +56,6 @@ export default {
return return
} }
} }
commit('setRouter', 'indexPage')
// 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段 // 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段
const { questionData, questionSeq, rules, formValues } = adapter.generateData({ const { questionData, questionSeq, rules, formValues } = adapter.generateData({

View File

@ -9,9 +9,6 @@ export default {
setQuestionData(state, data) { setQuestionData(state, data) {
state.questionData = data state.questionData = data
}, },
setRouter(state, data) {
state.router = data
},
setErrorInfo(state, { errorType, errorMsg }) { setErrorInfo(state, { errorType, errorMsg }) {
state.errorInfo = { state.errorInfo = {
errorType, errorType,

View File

@ -3,7 +3,6 @@ import { isMobile } from '../utils/index'
export default { export default {
surveyPath: '', surveyPath: '',
questionData: null, questionData: null,
router: '',
isMobile: isMobile(), isMobile: isMobile(),
errorInfo: { errorInfo: {
errorType: '', errorType: '',