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

This commit is contained in:
dayou 2024-06-14 19:20:36 +08:00
parent c93b29f397
commit 6d0a772686
11 changed files with 296 additions and 280 deletions

View File

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

View File

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

View File

@ -1,49 +1,15 @@
<template>
<div id="app">
<Component
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"
/>
<router-view></router-view>
</div>
</template>
<script setup lang="ts">
import { computed, watch, onMounted } from 'vue'
import { computed, watch } from 'vue'
import { useStore } from 'vuex'
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')
import { get as _get } from 'lodash-es'
const store = useStore()
const logoConf = computed(() => store.state?.bottomConf || {})
const skinConf = computed(() => _get(store, 'state.skinConf', {}))
const components = {
EmptyPage,
IndexPage,
ErrorPage,
SuccessPage
}
const updateSkinConfig = (value: any) => {
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) => {
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>
<style lang="scss">
@import url('./styles/icon.scss');

View File

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

View File

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

View File

@ -1,159 +1,71 @@
<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>
<router-view></router-view>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { onMounted } from 'vue'
import { useStore } from 'vuex'
// @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 { useRoute } from 'vue-router'
import { getPublishedSurveyInfo, getPreviewSchema } from '../api/survey'
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)
import AlertDialog from '../components/AlertDialog.vue'
import { initRuleEngine } from '@/render/hooks/useRuleEngine.js'
const store = useStore()
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
const route = useRoute()
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
store.commit('setSurveyPath', surveyPath)
store.dispatch('init', questionData)
initRuleEngine(logicConf?.showLogicConf)
} 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 {
const params = normalizationRequestBody()
const res: any = await submitForm(params)
if (res.code === 200) {
store.commit('setRouter', 'successPage')
if (surveyPath.length > 8) {
const res: any = await getPreviewSchema({ surveyPath })
loadData(res, surveyPath)
} else {
alert({
title: res.errmsg || '提交失败'
})
const res: any = await getPublishedSurveyInfo({ surveyPath })
loadData(res, surveyPath)
store.dispatch('getEncryptInfo')
}
} catch (error) {
} catch (error: any) {
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()
alert({ title: error.message || '获取问卷失败' })
}
}
</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
}
const VOTE_INFO_KEY = 'voteinfo'
import router from '../router'
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 { msgContent } = submitConf
const now = Date.now()
if (now < new Date(begTime).getTime()) {
commit('setRouter', 'errorPage')
router.push({ name: 'errorPage' })
commit('setErrorInfo', {
errorType: 'overTime',
errorMsg: `<p>问卷未到开始填写时间,暂时无法进行填写<p/>
@ -32,7 +35,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 || '您来晚了,感谢支持问卷~'
@ -44,7 +47,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: `<p>不在答题时间范围内,暂时无法进行填写<p/>
@ -53,7 +56,6 @@ export default {
return
}
}
commit('setRouter', 'indexPage')
// 根据初始的schema生成questionData, questionSeq, rules, formValues, 这四个字段
const { questionData, questionSeq, rules, formValues } = adapter.generateData({

View File

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

View File

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