feat:新增预览功能 (#257)

* feat:问卷预览功能
* feat:修复样式问题
This commit is contained in:
chaorenluo 2024-06-11 16:04:00 +08:00 committed by sudoooooo
parent cffe037269
commit 48fdb3138a
9 changed files with 281 additions and 28 deletions

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -8,6 +8,7 @@
<NavPanel></NavPanel>
</div>
<div class="right-group">
<PreviewPanel></PreviewPanel>
<HistoryPanel></HistoryPanel>
<SavePanel></SavePanel>
<PublishPanel></PublishPanel>
@ -23,6 +24,7 @@ import BackPanel from '../modules/generalModule/BackPanel.vue'
import TitlePanel from '../modules/generalModule/TitlePanel.vue'
import NavPanel from '../modules/generalModule/NavPanel.vue'
import HistoryPanel from '../modules/contentModule/HistoryPanel.vue'
import PreviewPanel from '../modules/contentModule/PreviewPanel.vue'
import SavePanel from '../modules/contentModule/SavePanel.vue'
import PublishPanel from '../modules/contentModule/PublishPanel.vue'

View File

@ -0,0 +1,184 @@
<template>
<div class="preview-panel">
<div class="preview-btn" @click="dialogTableVisible = true">
<i-ep-view class="view-icon" :size="20" />
<span class="btn-txt">预览</span>
</div>
<el-dialog
:z-index="99999"
top="50px"
class="preview-config-wrapper"
:destroy-on-close="true"
:show-close="false"
@open="openDialog"
@closed="closedDialog"
v-model="dialogTableVisible"
:width="`${previewTab == 1 ? '398' : '1290'}`"
>
<div class="ml75">
<div class="preview-tab">
<div :class="`preview-tab-item ${previewTab == 1 ? 'active' : ''}`" @click="previewTab = 1">
<i-ep-iphone />
</div>
<div :class="`preview-tab-item ${previewTab == 2 ? 'active' : ''}`" @click="previewTab = 2">
<i-ep-monitor />
</div>
</div>
<div :class="`preview-panel ${previewTab == 1 ? 'phone' : 'pc'}`">
<div class="wrapper">
<div class="tips-wrapper">
<i-ep-WarningFilled /> <span>用户预览模式数据不保存</span>
</div>
<div v-loading="loading" element-loading-text="加载中..." style="height: 100%">
<iframe
v-loading="loading"
id="iframe-preview"
:src="`/management/preview/${surveyId}`"
frameborder="0"
width="100%"
height="100%"
></iframe>
</div>
</div>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const dialogTableVisible = ref(false)
const previewTab = ref(1)
const surveyId = route.params.id
const loading = ref(true)
const openDialog = () => {
const iframePreview = document.getElementById('iframe-preview')
if (!iframePreview) return
iframePreview.onload = function () {
loading.value = false
}
}
const closedDialog = () => {
loading.value = true
}
</script>
<style lang="scss" scoped>
.preview-panel {
:deep(.preview-config-wrapper) {
background-color: transparent;
box-shadow: none;
padding: 0;
}
.ml75{
margin-left: 75px;
}
.preview-btn {
width: 50px;
display: flex;
flex-direction: column;
align-items: center;
color: #4a4c5b;
cursor: pointer;
i {
font-size: 20px;
}
.btn-txt {
font-size: 12px;
}
}
.view-icon {
font-size: 18px;
margin-bottom: 4px;
}
.preview-tab {
display: flex;
align-items: center;
justify-content: center;
.border-right-none {
border-right: none;
}
.active {
border-color: $primary-color;
color: $primary-color;
}
.border-left-none {
border-left: none;
}
&-item {
width: 80px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
background: #ffffff;
border: 1px solid rgba(227, 228, 232, 1);
cursor: pointer;
&:hover {
border-color: $primary-color;
color: $primary-color;
}
}
}
.preview-panel {
margin-top: 16px;
&.pc {
display: flex;
justify-content: center;
background: #ffffff;
box-shadow: 0px 2px 10px -2px rgba(82, 82, 102, 0.2);
height: 726px;
.wrapper {
width: 636px;
height: 704px;
}
}
&.phone {
display: flex;
align-items: center;
justify-content: center;
.wrapper {
background: url('/imgs/preview-phone.png') no-repeat;
width: 328px;
height: 678px;
background-size: 100% 100%;
padding: 0 14px;
padding-top: 58px;
padding-bottom: 14px;
display: flex;
flex-direction: column;
}
iframe {
border-radius: 0px 0px 20px 20px;
}
}
}
.tips-wrapper {
display: flex;
align-items: center;
background: $primary-bg-color;
color: $primary-color;
font-size: 12px;
padding: 2px 0;
padding-left: 9px;
span {
margin-left: 5px;
}
}
}
</style>

View File

@ -20,7 +20,7 @@
import { computed, watch, onMounted } from 'vue'
import { useStore } from 'vuex'
import { getPublishedSurveyInfo } from './api/survey'
import { getPublishedSurveyInfo, getPreviewSchema } from './api/survey'
import useCommandComponent from './hooks/useCommandComponent'
import EmptyPage from './pages/EmptyPage.vue'
@ -65,6 +65,39 @@ 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)
})
@ -78,33 +111,14 @@ onMounted(async () => {
}
const alert = useCommandComponent(AlertDialog)
try {
const res: any = await getPublishedSurveyInfo({ surveyPath })
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)
store.dispatch('getEncryptInfo')
initRuleEngine(logicConf?.showLogicConf)
if (surveyPath.length > 8) {
const res: any = await getPreviewSchema({ surveyPath })
loadData(res, surveyPath)
} else {
throw new Error(res.errmsg)
const res: any = await getPublishedSurveyInfo({ surveyPath })
loadData(res, surveyPath)
store.dispatch('getEncryptInfo')
}
} catch (error: any) {
console.log(error)

View File

@ -8,6 +8,14 @@ export const getPublishedSurveyInfo = ({ surveyPath }) => {
})
}
export const getPreviewSchema = ({ surveyPath }) => {
return axios.get('/survey/getPreviewSchema', {
params: {
surveyPath
}
})
}
export const submitForm = (data) => {
return axios.post('/surveyResponse/createResponse', data)
}

View File

@ -60,6 +60,7 @@ 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
@ -70,10 +71,9 @@ const normalizationRequestBody = () => {
const enterTime = store.state.enterTime
const encryptInfo = store.state.encryptInfo
const formValues = store.state.formValues
const surveyPath = store.state.surveyPath
const result: any = {
surveyPath,
surveyPath: surveyPath.value,
data: JSON.stringify(formValues),
difTime: Date.now() - enterTime,
clientTime: Date.now()
@ -96,6 +96,10 @@ const normalizationRequestBody = () => {
}
const submitSurver = async () => {
if (surveyPath.value.length > 8) {
store.commit('setRouter', 'successPage')
return
}
try {
const params = normalizationRequestBody()
console.log(params)

View File

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