fix: 断点续答and编辑检测代码cr优化 (#428)
* fix: 优化代码以及去掉无用字段 * fix: 断点续答验收问题优化 * fix: 优化字段 * fix: lint * fix: 编辑检测相关问题优化 * fix: lint * fix: 第二批cr优化 * fix: 去掉无用代码 * fix: 调整session守卫位置 * fix: 文件大小写
This commit is contained in:
parent
628872f27c
commit
bf5db3f47b
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,7 +3,6 @@ node_modules
|
||||
dist
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
|
@ -48,8 +48,7 @@
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.8.1",
|
||||
"svg-captcha": "^1.4.0",
|
||||
"typeorm": "^0.3.19",
|
||||
"xss": "^1.0.15"
|
||||
"typeorm": "^0.3.19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^10.0.0",
|
||||
|
@ -6,7 +6,7 @@ export enum RECORD_STATUS {
|
||||
PUBLISHED = 'published', // 发布
|
||||
REMOVED = 'removed', // 删除
|
||||
FORCE_REMOVED = 'forceRemoved', // 从回收站删除
|
||||
COMOPUTETING = 'computing', // 计算中
|
||||
COMPUTING = 'computing', // 计算中
|
||||
FINISHED = 'finished', // 已完成
|
||||
ERROR = 'error', // 错误
|
||||
}
|
||||
|
@ -2,25 +2,16 @@ import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { get } from 'lodash';
|
||||
import { NoPermissionException } from 'src/exceptions/noPermissionException';
|
||||
import { SurveyNotFoundException } from 'src/exceptions/surveyNotFoundException';
|
||||
import { SessionService } from 'src/modules/survey/services/session.service';
|
||||
import { SurveyMetaService } from 'src/modules/survey/services/surveyMeta.service';
|
||||
import { WorkspaceMemberService } from 'src/modules/workspace/services/workspaceMember.service';
|
||||
import { CollaboratorService } from 'src/modules/survey/services/collaborator.service';
|
||||
|
||||
@Injectable()
|
||||
export class SessionGuard implements CanActivate {
|
||||
constructor(
|
||||
private reflector: Reflector,
|
||||
private readonly sessionService: SessionService,
|
||||
private readonly surveyMetaService: SurveyMetaService,
|
||||
private readonly workspaceMemberService: WorkspaceMemberService,
|
||||
private readonly collaboratorService: CollaboratorService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const user = request.user;
|
||||
const sessionIdKey = this.reflector.get<string>(
|
||||
'sessionId',
|
||||
context.getHandler(),
|
||||
@ -31,64 +22,8 @@ export class SessionGuard implements CanActivate {
|
||||
if (!sessionId) {
|
||||
throw new NoPermissionException('没有权限');
|
||||
}
|
||||
|
||||
const saveSession = await this.sessionService.findOne(sessionId);
|
||||
|
||||
request.saveSession = saveSession;
|
||||
|
||||
const surveyId = saveSession.surveyId;
|
||||
|
||||
const surveyMeta = await this.surveyMetaService.getSurveyById({ surveyId });
|
||||
|
||||
if (!surveyMeta) {
|
||||
throw new SurveyNotFoundException('问卷不存在');
|
||||
}
|
||||
|
||||
request.surveyMeta = surveyMeta;
|
||||
|
||||
// 兼容老的问卷没有ownerId
|
||||
if (
|
||||
surveyMeta.ownerId === user._id.toString() ||
|
||||
surveyMeta.owner === user.username
|
||||
) {
|
||||
// 问卷的owner,可以访问和操作问卷
|
||||
return true;
|
||||
}
|
||||
|
||||
if (surveyMeta.workspaceId) {
|
||||
const memberInfo = await this.workspaceMemberService.findOne({
|
||||
workspaceId: surveyMeta.workspaceId,
|
||||
userId: user._id.toString(),
|
||||
});
|
||||
if (!memberInfo) {
|
||||
throw new NoPermissionException('没有权限');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const permissions = this.reflector.get<string[]>(
|
||||
'surveyPermission',
|
||||
context.getHandler(),
|
||||
);
|
||||
|
||||
if (!Array.isArray(permissions) || permissions.length === 0) {
|
||||
throw new NoPermissionException('没有权限');
|
||||
}
|
||||
|
||||
const info = await this.collaboratorService.getCollaborator({
|
||||
surveyId,
|
||||
userId: user._id.toString(),
|
||||
});
|
||||
|
||||
if (!info) {
|
||||
throw new NoPermissionException('没有权限');
|
||||
}
|
||||
request.collaborator = info;
|
||||
if (
|
||||
permissions.some((permission) => info.permissions.includes(permission))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
throw new NoPermissionException('没有权限');
|
||||
const sessionInfo = await this.sessionService.findOne(sessionId);
|
||||
request.sessionInfo = sessionInfo;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,4 @@ export class SurveyHistory extends BaseEntity {
|
||||
username: string;
|
||||
_id: string;
|
||||
};
|
||||
|
||||
@Column('string')
|
||||
sessionId: string;
|
||||
}
|
||||
|
@ -35,13 +35,4 @@ export class AuthService {
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
async expiredCheck(token: string) {
|
||||
try {
|
||||
verify(token, this.configService.get<string>('XIAOJU_SURVEY_JWT_SECRET'));
|
||||
} catch (err) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,6 @@ describe('SurveyHistoryService', () => {
|
||||
schema,
|
||||
type,
|
||||
user,
|
||||
sessionId: '',
|
||||
});
|
||||
|
||||
expect(spyCreate).toHaveBeenCalledWith({
|
||||
|
@ -67,7 +67,9 @@ export class SessionController {
|
||||
|
||||
@Post('/seize')
|
||||
@HttpCode(200)
|
||||
@UseGuards(SurveyGuard)
|
||||
@UseGuards(SessionGuard)
|
||||
|
||||
@SetMetadata('sessionId', 'body.sessionId')
|
||||
@SetMetadata('surveyPermission', [SURVEY_PERMISSION.SURVEY_CONF_MANAGE])
|
||||
@UseGuards(Authentication)
|
||||
@ -75,11 +77,11 @@ export class SessionController {
|
||||
@Request()
|
||||
req,
|
||||
) {
|
||||
const saveSession = req.saveSession;
|
||||
const sessionInfo = req.sessionInfo;
|
||||
|
||||
await this.sessionService.updateSessionToEditing({
|
||||
sessionId: saveSession._id.toString(),
|
||||
surveyId: saveSession.surveyId,
|
||||
sessionId: sessionInfo._id.toString(),
|
||||
surveyId: sessionInfo.surveyId,
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -156,7 +156,7 @@ export class DownloadTaskService {
|
||||
{
|
||||
$set: {
|
||||
curStatus: {
|
||||
status: RECORD_STATUS.COMOPUTETING,
|
||||
status: RECORD_STATUS.COMPUTING,
|
||||
date: Date.now(),
|
||||
},
|
||||
},
|
||||
|
@ -43,7 +43,6 @@
|
||||
"options": [
|
||||
{
|
||||
"text": "选项1",
|
||||
"imageUrl": "",
|
||||
"others": false,
|
||||
"mustOthers": false,
|
||||
"othersKey": "",
|
||||
@ -52,7 +51,6 @@
|
||||
},
|
||||
{
|
||||
"text": "选项2",
|
||||
"imageUrl": "",
|
||||
"others": false,
|
||||
"mustOthers": false,
|
||||
"othersKey": "",
|
||||
|
@ -47,7 +47,6 @@
|
||||
{
|
||||
"text": "课程1",
|
||||
"hash": "115019",
|
||||
"imageUrl": "",
|
||||
"others": false,
|
||||
"mustOthers": false,
|
||||
"othersKey": "",
|
||||
@ -56,7 +55,6 @@
|
||||
{
|
||||
"text": "课程2",
|
||||
"hash": "115020",
|
||||
"imageUrl": "",
|
||||
"others": false,
|
||||
"mustOthers": false,
|
||||
"othersKey": "",
|
||||
@ -65,7 +63,6 @@
|
||||
{
|
||||
"text": "课程3",
|
||||
"hash": "115021",
|
||||
"imageUrl": "",
|
||||
"others": false,
|
||||
"mustOthers": false,
|
||||
"othersKey": "",
|
||||
@ -74,7 +71,6 @@
|
||||
{
|
||||
"text": "课程4",
|
||||
"hash": "115022",
|
||||
"imageUrl": "",
|
||||
"others": false,
|
||||
"mustOthers": false,
|
||||
"othersKey": "",
|
||||
|
@ -46,7 +46,6 @@
|
||||
"options": [
|
||||
{
|
||||
"text": "选项1",
|
||||
"imageUrl": "",
|
||||
"others": false,
|
||||
"mustOthers": false,
|
||||
"othersKey": "",
|
||||
@ -55,7 +54,6 @@
|
||||
},
|
||||
{
|
||||
"text": "选项2",
|
||||
"imageUrl": "",
|
||||
"others": false,
|
||||
"mustOthers": false,
|
||||
"othersKey": "",
|
||||
|
@ -51,6 +51,7 @@
|
||||
},
|
||||
"pageConf": [],
|
||||
"logicConf": {
|
||||
"showLogicConf": []
|
||||
"showLogicConf": [],
|
||||
"jumpLogicConf": []
|
||||
}
|
||||
}
|
||||
|
@ -1,53 +0,0 @@
|
||||
import xss from 'xss';
|
||||
|
||||
const myxss = new (xss as any).FilterXSS({
|
||||
onIgnoreTagAttr(tag, name, value) {
|
||||
if (name === 'style' || name === 'class') {
|
||||
return `${name}="${value}"`;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
onIgnoreTag(tag, html) {
|
||||
// <xxx>过滤为空,否则不过滤为空
|
||||
const re1 = new RegExp('<.+?>', 'g');
|
||||
if (re1.test(html)) {
|
||||
return '';
|
||||
} else {
|
||||
return html;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const cleanRichTextWithMediaTag = (text) => {
|
||||
if (!text) {
|
||||
return text === 0 ? 0 : '';
|
||||
}
|
||||
const html = transformHtmlTag(text)
|
||||
.replace(/<img([\w\W]+?)\/>/g, '[图片]')
|
||||
.replace(/<video.*\/video>/g, '[视频]');
|
||||
const content = html.replace(/<[^<>]+>/g, '').replace(/ /g, '');
|
||||
|
||||
return content;
|
||||
};
|
||||
|
||||
export function escapeHtml(html) {
|
||||
return html.replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
export const transformHtmlTag = (html) => {
|
||||
if (!html) return '';
|
||||
if (typeof html !== 'string') return html + '';
|
||||
return html
|
||||
.replace(html ? /&(?!#?\w+;)/g : /&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/\\\n/g, '\\n');
|
||||
//.replace(/ /g, "")
|
||||
};
|
||||
|
||||
const filterXSSClone = myxss.process.bind(myxss);
|
||||
|
||||
export const filterXSS = (html) => filterXSSClone(transformHtmlTag(html));
|
||||
|
||||
export const escapeFilterXSS = (html) => escapeHtml(filterXSS(html));
|
58
web/src/common/localstorage.ts
Normal file
58
web/src/common/localstorage.ts
Normal file
@ -0,0 +1,58 @@
|
||||
interface ExtendedItem {
|
||||
value: any
|
||||
expires?: number // 可选属性
|
||||
}
|
||||
|
||||
const localstorage = {
|
||||
// 检查是否支持localStorage
|
||||
isSupported(): boolean {
|
||||
return typeof window !== 'undefined' && 'localStorage' in window
|
||||
},
|
||||
|
||||
// 设置值
|
||||
setItem(key: string, value: any, expires?: number): void {
|
||||
if (!this.isSupported()) return
|
||||
|
||||
let item: ExtendedItem = { value }
|
||||
|
||||
if (expires !== undefined) {
|
||||
item = { ...item, expires: Date.now() + expires * 1000 }
|
||||
}
|
||||
|
||||
const serializedValue = JSON.stringify(item)
|
||||
|
||||
localStorage.setItem(key, serializedValue)
|
||||
},
|
||||
|
||||
// 获取值
|
||||
getItem<T>(key: string): T | null {
|
||||
if (!this.isSupported()) return null
|
||||
|
||||
const serializedValue = localStorage.getItem(key) as string
|
||||
if (!serializedValue) return null
|
||||
|
||||
let item: any
|
||||
try {
|
||||
item = JSON.parse(serializedValue)
|
||||
} catch (e) {
|
||||
console.error('Error parsing JSON from localStorage')
|
||||
return null
|
||||
}
|
||||
|
||||
if (item.expires && item.expires < Date.now()) {
|
||||
this.removeItem(key)
|
||||
return null
|
||||
}
|
||||
|
||||
return item.value as T
|
||||
},
|
||||
|
||||
// 移除值
|
||||
removeItem(key: string): void {
|
||||
if (!this.isSupported()) return
|
||||
|
||||
localStorage.removeItem(key)
|
||||
}
|
||||
}
|
||||
|
||||
export default localstorage
|
@ -9,7 +9,7 @@ const myxss = new xss.FilterXSS({
|
||||
},
|
||||
onIgnoreTag(tag, html) {
|
||||
// <xxx>过滤为空,否则不过滤为空
|
||||
var re1 = new RegExp('<.+?>', 'g')
|
||||
const re1 = new RegExp('<.+?>', 'g')
|
||||
if (re1.test(html)) {
|
||||
return ''
|
||||
} else {
|
||||
|
@ -3,7 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch } from 'vue'
|
||||
import { watch, onBeforeUnmount } from 'vue'
|
||||
import { get as _get } from 'lodash-es'
|
||||
import { useUserStore } from '@/management/stores/user'
|
||||
import { useRouter } from 'vue-router'
|
||||
@ -23,11 +23,11 @@ const showConfirmBox = () => {
|
||||
showClose: false,
|
||||
callback: (action: Action) => {
|
||||
if (action === 'confirm') {
|
||||
userStore.logout();
|
||||
router.replace({ name: 'login' });
|
||||
userStore.logout()
|
||||
router.replace({ name: 'login' })
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
const checkAuth = async () => {
|
||||
@ -41,32 +41,40 @@ const checkAuth = async () => {
|
||||
}
|
||||
})
|
||||
if (res.data.code !== 200) {
|
||||
showConfirmBox();
|
||||
showConfirmBox()
|
||||
} else {
|
||||
timer = setTimeout(() => {
|
||||
checkAuth()
|
||||
}, 30 * 60 * 1000);
|
||||
timer = setTimeout(
|
||||
() => {
|
||||
checkAuth()
|
||||
},
|
||||
30 * 60 * 1000
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
const e = error as any
|
||||
ElMessage.error(e.message)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
watch(() => userStore.hasLogined, (hasLogined) => {
|
||||
if (hasLogined) {
|
||||
timer = setTimeout(() => {
|
||||
checkAuth()
|
||||
}, 30 * 60 * 1000);
|
||||
} else {
|
||||
clearTimeout(timer);
|
||||
watch(
|
||||
() => userStore.hasLogined,
|
||||
(hasLogined) => {
|
||||
if (hasLogined) {
|
||||
timer = setTimeout(
|
||||
() => {
|
||||
checkAuth()
|
||||
},
|
||||
30 * 60 * 1000
|
||||
)
|
||||
} else {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearTimeout(timer)
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -16,4 +16,3 @@ export const getStatisticList = (data) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,10 @@ export const createDownloadSurveyResponseTask = ({ surveyId, isDesensitive }) =>
|
||||
})
|
||||
}
|
||||
|
||||
export const getDownloadTask = taskId => {
|
||||
export const getDownloadTask = (taskId) => {
|
||||
return axios.get('/downloadTask/getDownloadTask', { params: { taskId } })
|
||||
}
|
||||
|
||||
|
||||
export const getDownloadTaskList = ({ pageIndex, pageSize }) => {
|
||||
return axios.get('/downloadTask/getDownloadTaskList', {
|
||||
params: {
|
||||
@ -24,6 +23,6 @@ export const getDownloadTaskList = ({ pageIndex, pageSize }) => {
|
||||
//问卷删除
|
||||
export const deleteDownloadTask = (taskId) => {
|
||||
return axios.post('/downloadTask/deleteDownloadTask', {
|
||||
taskId,
|
||||
taskId
|
||||
})
|
||||
}
|
||||
|
@ -59,4 +59,4 @@ export const getSessionId = ({ surveyId }) => {
|
||||
|
||||
export const seizeSession = ({ sessionId }) => {
|
||||
return axios.post('/session/seize', { sessionId })
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ const popoverContent = ref('')
|
||||
|
||||
const getContent = (content) => {
|
||||
if (Array.isArray(content)) {
|
||||
return content.map(item => getContent(item)).join(',');
|
||||
return content.map((item) => getContent(item)).join(',')
|
||||
}
|
||||
if (content === null || content === undefined) {
|
||||
return ''
|
||||
|
@ -28,13 +28,8 @@
|
||||
<EmptyIndex :data="noDataConfig" />
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
v-model="downloadDialogVisible"
|
||||
title="导出确认"
|
||||
width="500"
|
||||
style="padding: 40px;"
|
||||
>
|
||||
<el-form :model="downloadForm" label-width="100px" label-position="left" >
|
||||
<el-dialog v-model="downloadDialogVisible" title="导出确认" width="500" style="padding: 40px">
|
||||
<el-form :model="downloadForm" label-width="100px" label-position="left">
|
||||
<el-form-item label="导出内容">
|
||||
<el-radio-group v-model="downloadForm.isDesensitive">
|
||||
<el-radio :value="true">脱敏数据</el-radio>
|
||||
@ -52,9 +47,7 @@
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="downloadDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmDownload()">
|
||||
确认
|
||||
</el-button>
|
||||
<el-button type="primary" @click="confirmDownload()"> 确认 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@ -85,11 +78,12 @@ const dataTableState = reactive({
|
||||
isDownloading: false,
|
||||
downloadDialogVisible: false,
|
||||
downloadForm: {
|
||||
isDesensitive: true,
|
||||
},
|
||||
isDesensitive: true
|
||||
}
|
||||
})
|
||||
|
||||
const { mainTableLoading, tableData, isShowOriginData, downloadDialogVisible, isDownloading } = toRefs(dataTableState)
|
||||
const { mainTableLoading, tableData, isShowOriginData, downloadDialogVisible, isDownloading } =
|
||||
toRefs(dataTableState)
|
||||
const downloadForm = dataTableState.downloadForm
|
||||
|
||||
const route = useRoute()
|
||||
@ -168,7 +162,10 @@ const confirmDownload = async () => {
|
||||
}
|
||||
try {
|
||||
isDownloading.value = true
|
||||
const createRes = await createDownloadSurveyResponseTask({ surveyId: route.params.id, isDesensitive: downloadForm.isDesensitive })
|
||||
const createRes = await createDownloadSurveyResponseTask({
|
||||
surveyId: route.params.id,
|
||||
isDesensitive: downloadForm.isDesensitive
|
||||
})
|
||||
dataTableState.downloadDialogVisible = false
|
||||
if (createRes.code === 200) {
|
||||
ElMessage.success(`下载文件计算中,可前往“下载中心”查看`)
|
||||
@ -176,12 +173,11 @@ const confirmDownload = async () => {
|
||||
const taskInfo = await checkIsTaskFinished(createRes.data.taskId)
|
||||
if (taskInfo.url) {
|
||||
window.open(taskInfo.url)
|
||||
ElMessage.success("导出成功")
|
||||
ElMessage.success('导出成功')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('导出失败,请重试')
|
||||
}
|
||||
|
||||
} else {
|
||||
ElMessage.error('导出失败,请重试')
|
||||
}
|
||||
@ -190,13 +186,12 @@ const confirmDownload = async () => {
|
||||
} finally {
|
||||
isDownloading.value = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const checkIsTaskFinished = (taskId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const run = () => {
|
||||
getDownloadTask(taskId).then(res => {
|
||||
getDownloadTask(taskId).then((res) => {
|
||||
if (res.code === 200 && res.data) {
|
||||
const status = res.data.curStatus.status
|
||||
if (status === 'new' || status === 'computing') {
|
||||
@ -207,15 +202,13 @@ const checkIsTaskFinished = (taskId) => {
|
||||
resolve(res.data)
|
||||
}
|
||||
} else {
|
||||
reject("导出失败");
|
||||
reject('导出失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
run()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -25,7 +25,13 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200">
|
||||
<template v-slot="{ row }">
|
||||
<span v-if="row.curStatus?.status === 'finished'" class="text-btn download-btn" @click="handleDownload(row)"> 下载 </span>
|
||||
<span
|
||||
v-if="row.curStatus?.status === 'finished'"
|
||||
class="text-btn download-btn"
|
||||
@click="handleDownload(row)"
|
||||
>
|
||||
下载
|
||||
</span>
|
||||
<span class="text-btn delete-btn" @click="openDeleteDialog(row)"> 删除 </span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -71,14 +77,14 @@ const getList = async ({ pageIndex }: { pageIndex: number }) => {
|
||||
}
|
||||
const params = {
|
||||
pageSize: pageSize.value,
|
||||
pageIndex,
|
||||
pageIndex
|
||||
}
|
||||
|
||||
const res: Record<string, any> = await getDownloadTaskList(params)
|
||||
if (res.code === CODE_MAP.SUCCESS) {
|
||||
total.value = res.data.total
|
||||
const list = res.data.list as any
|
||||
dataList.splice(0, dataList.length, ...list);
|
||||
dataList.splice(0, dataList.length, ...list)
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
@ -87,8 +93,8 @@ const statusTextMap: Record<string, string> = {
|
||||
new: '排队中',
|
||||
computing: '计算中',
|
||||
finished: '已完成',
|
||||
removed: '已删除',
|
||||
};
|
||||
removed: '已删除'
|
||||
}
|
||||
|
||||
let currentDelRow: Record<string, any> = {}
|
||||
// 下载文件
|
||||
@ -123,11 +129,11 @@ const confirmDelete = async () => {
|
||||
if (res.code !== CODE_MAP.SUCCESS) {
|
||||
ElMessage.error(res.errmsg)
|
||||
} else {
|
||||
ElMessage.success('删除成功');
|
||||
ElMessage.success('删除成功')
|
||||
await getList({ pageIndex: 1 })
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error("删除失败,请刷新重试")
|
||||
ElMessage.error('删除失败,请刷新重试')
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,7 +168,7 @@ const downloadListConfig = {
|
||||
formatter(row: Record<string, any>, column: Record<string, any>) {
|
||||
console.log({
|
||||
row,
|
||||
column,
|
||||
column
|
||||
})
|
||||
return statusTextMap[get(row, column.rawColumnKey)]
|
||||
}
|
||||
|
@ -18,10 +18,15 @@
|
||||
</CooperationPanel>
|
||||
<PreviewPanel></PreviewPanel>
|
||||
<HistoryPanel></HistoryPanel>
|
||||
<SavePanel :updateLogicConf="updateLogicConf" :updateWhiteConf="updateWhiteConf"></SavePanel>
|
||||
<SavePanel
|
||||
:updateLogicConf="updateLogicConf"
|
||||
:updateWhiteConf="updateWhiteConf"
|
||||
:seize="seize"
|
||||
></SavePanel>
|
||||
<PublishPanel
|
||||
:updateLogicConf="updateLogicConf"
|
||||
:updateWhiteConf="updateWhiteConf"
|
||||
:seize="seize"
|
||||
></PublishPanel>
|
||||
</div>
|
||||
</div>
|
||||
@ -30,6 +35,8 @@
|
||||
import { computed } from 'vue'
|
||||
import { useEditStore } from '@/management/stores/edit'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import 'element-plus/theme-chalk/src/message.scss'
|
||||
|
||||
import BackPanel from '../modules/generalModule/BackPanel.vue'
|
||||
import TitlePanel from '../modules/generalModule/TitlePanel.vue'
|
||||
@ -39,6 +46,7 @@ import PreviewPanel from '../modules/contentModule/PreviewPanel.vue'
|
||||
import SavePanel from '../modules/contentModule/SavePanel.vue'
|
||||
import PublishPanel from '../modules/contentModule/PublishPanel.vue'
|
||||
import CooperationPanel from '../modules/contentModule/CooperationPanel.vue'
|
||||
import { seizeSession } from '@/management/api/survey'
|
||||
|
||||
const editStore = useEditStore()
|
||||
const { schema, changeSchema } = editStore
|
||||
@ -68,15 +76,15 @@ const updateLogicConf = () => {
|
||||
}
|
||||
|
||||
const showLogicConf = showLogicEngine.value.toJson()
|
||||
if(JSON.stringify(schema.logicConf.showLogicConf) !== JSON.stringify(showLogicConf)) {
|
||||
if (JSON.stringify(schema.logicConf.showLogicConf) !== JSON.stringify(showLogicConf)) {
|
||||
// 更新逻辑配置
|
||||
changeSchema({ key: 'logicConf', value: { showLogicConf } })
|
||||
}
|
||||
|
||||
|
||||
return res
|
||||
}
|
||||
const jumpLogicConf = jumpLogicEngine.value.toJson()
|
||||
if(JSON.stringify(schema.logicConf.jumpLogicConf) !== JSON.stringify(jumpLogicConf)){
|
||||
if (JSON.stringify(schema.logicConf.jumpLogicConf) !== JSON.stringify(jumpLogicConf)) {
|
||||
changeSchema({ key: 'logicConf', value: { jumpLogicConf } })
|
||||
}
|
||||
|
||||
@ -106,6 +114,16 @@ const updateWhiteConf = () => {
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// 重新获取sessionid
|
||||
const seize = async (sessionId: string) => {
|
||||
const seizeRes: Record<string, any> = await seizeSession({ sessionId })
|
||||
if (seizeRes.code === 200) {
|
||||
location.reload()
|
||||
} else {
|
||||
ElMessage.error('获取权限失败,请重试')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import url('@/management/styles/edit-btn.scss');
|
||||
|
@ -24,7 +24,6 @@ import LeftMenu from '@/management/components/LeftMenu.vue'
|
||||
import CommonTemplate from './components/CommonTemplate.vue'
|
||||
import Navbar from './components/ModuleNavbar.vue'
|
||||
|
||||
|
||||
const editStore = useEditStore()
|
||||
const { init, setSurveyId } = editStore
|
||||
|
||||
|
@ -9,7 +9,7 @@ import { useEditStore } from '@/management/stores/edit'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import 'element-plus/theme-chalk/src/message.scss'
|
||||
import { publishSurvey, saveSurvey, seizeSession } from '@/management/api/survey'
|
||||
import { publishSurvey, saveSurvey } from '@/management/api/survey'
|
||||
import buildData from './buildData'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { CODE_MAP } from '@/management/api/base'
|
||||
@ -17,6 +17,7 @@ import { CODE_MAP } from '@/management/api/base'
|
||||
interface Props {
|
||||
updateLogicConf: any
|
||||
updateWhiteConf: any
|
||||
seize: any
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
@ -29,15 +30,6 @@ const saveData = computed(() => {
|
||||
return buildData(schema.value, sessionId.value)
|
||||
})
|
||||
|
||||
const seize = async () => {
|
||||
const seizeRes: Record<string, any> = await seizeSession({ sessionId: sessionId.value })
|
||||
if (seizeRes.code === 200) {
|
||||
location.reload();
|
||||
} else {
|
||||
ElMessage.error('获取权限失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const validate = () => {
|
||||
@ -61,12 +53,11 @@ const validate = () => {
|
||||
}
|
||||
|
||||
const onSave = async () => {
|
||||
|
||||
if (!saveData.value.sessionId) {
|
||||
ElMessage.error('未获取到sessionId')
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
if (!saveData.value.surveyId) {
|
||||
ElMessage.error('未获取到问卷id')
|
||||
return null
|
||||
@ -74,7 +65,7 @@ const onSave = async () => {
|
||||
|
||||
try {
|
||||
const res: any = await saveSurvey(saveData.value)
|
||||
if(!res) {
|
||||
if (!res) {
|
||||
return null
|
||||
}
|
||||
if (res.code === 200) {
|
||||
@ -85,10 +76,10 @@ const onSave = async () => {
|
||||
confirmButtonText: '刷新同步',
|
||||
callback: (action: string) => {
|
||||
if (action === 'confirm') {
|
||||
seize();
|
||||
props.seize(sessionId.value)
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
return null
|
||||
} else {
|
||||
ElMessage.error(res.errmsg)
|
||||
@ -98,7 +89,6 @@ const onSave = async () => {
|
||||
ElMessage.error('保存问卷失败')
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
const handlePublish = async () => {
|
||||
if (isPublishing.value) {
|
||||
|
@ -21,12 +21,13 @@ import { useEditStore } from '@/management/stores/edit'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import 'element-plus/theme-chalk/src/message.scss'
|
||||
|
||||
import { saveSurvey, seizeSession } from '@/management/api/survey'
|
||||
import { saveSurvey } from '@/management/api/survey'
|
||||
import buildData from './buildData'
|
||||
|
||||
interface Props {
|
||||
updateLogicConf: any
|
||||
updateWhiteConf: any
|
||||
seize: any
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
@ -46,7 +47,6 @@ const saveText = computed(
|
||||
const editStore = useEditStore()
|
||||
const { schemaUpdateTime, schema, sessionId } = storeToRefs(editStore)
|
||||
|
||||
|
||||
const validate = () => {
|
||||
let checked = true
|
||||
let msg = ''
|
||||
@ -69,7 +69,7 @@ const validate = () => {
|
||||
}
|
||||
|
||||
const onSave = async () => {
|
||||
const saveData = buildData(schema.value, sessionId.value);
|
||||
const saveData = buildData(schema.value, sessionId.value)
|
||||
if (!saveData.sessionId) {
|
||||
ElMessage.error('sessionId有误')
|
||||
return null
|
||||
@ -81,17 +81,8 @@ const onSave = async () => {
|
||||
}
|
||||
|
||||
const res: Record<string, any> = await saveSurvey(saveData)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
const seize = async () => {
|
||||
const seizeRes: Record<string, any> = await seizeSession({ sessionId: sessionId.value })
|
||||
if (seizeRes.code === 200) {
|
||||
location.reload();
|
||||
} else {
|
||||
ElMessage.error('获取权限失败,请重试')
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
const timerHandle = ref<NodeJS.Timeout | number | null>(null)
|
||||
@ -147,7 +138,7 @@ const handleSave = async () => {
|
||||
|
||||
try {
|
||||
const res: any = await onSave()
|
||||
if(!res) {
|
||||
if (!res) {
|
||||
return
|
||||
}
|
||||
if (res.code === 200) {
|
||||
@ -158,10 +149,10 @@ const handleSave = async () => {
|
||||
confirmButtonText: '刷新同步',
|
||||
callback: (action: string) => {
|
||||
if (action === 'confirm') {
|
||||
seize();
|
||||
props.seize(sessionId.value)
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
} else {
|
||||
ElMessage.error(res.errmsg)
|
||||
}
|
||||
|
@ -141,20 +141,13 @@ export default {
|
||||
this.initCurOption()
|
||||
},
|
||||
addOption(text = '选项', others = false, index = -1, fieldId) {
|
||||
let addOne
|
||||
if (this.curOptions[0]) {
|
||||
addOne = _cloneDeep(this.curOptions[0])
|
||||
} else {
|
||||
addOne = {
|
||||
text: '',
|
||||
hash: '',
|
||||
others: false,
|
||||
mustOthers: false,
|
||||
othersKey: '',
|
||||
placeholderDesc: '',
|
||||
score: 0,
|
||||
limit: ''
|
||||
}
|
||||
let addOne = {
|
||||
text: '',
|
||||
hash: '',
|
||||
others: false,
|
||||
mustOthers: false,
|
||||
othersKey: '',
|
||||
placeholderDesc: ''
|
||||
}
|
||||
for (const i in addOne) {
|
||||
if (i === 'others') {
|
||||
|
@ -7,7 +7,7 @@ export default [
|
||||
{
|
||||
title: '提交限制',
|
||||
key: 'limitConfig',
|
||||
formList: ['limit_tLimit', 'limit_breakAnswer', 'limit_backAnswer']
|
||||
formList: ['limit_tLimit', 'limit_fillAnswer', 'limit_fillSubmitAnswer']
|
||||
},
|
||||
{
|
||||
title: '作答限制',
|
||||
|
@ -22,21 +22,21 @@ export default {
|
||||
type: 'QuestionTimeHour',
|
||||
placement: 'top'
|
||||
},
|
||||
limit_breakAnswer: {
|
||||
key: 'breakAnswer',
|
||||
limit_fillAnswer: {
|
||||
key: 'fillAnswer',
|
||||
label: '允许断点续答',
|
||||
tip: '回填前一次作答中的内容(注:更换设备/浏览器/清除缓存/更改内容重新发布则此功能失效)',
|
||||
placement: 'top',
|
||||
type: 'CustomedSwitch',
|
||||
value: false,
|
||||
value: false
|
||||
},
|
||||
limit_backAnswer: {
|
||||
key: 'backAnswer',
|
||||
limit_fillSubmitAnswer: {
|
||||
key: 'fillSubmitAnswer',
|
||||
label: '自动填充上次提交内容',
|
||||
tip: '回填前一次提交的内容(注:更换设备/浏览器/清除缓存/更改内容重新发布则此功能失效)',
|
||||
placement: 'top',
|
||||
type: 'CustomedSwitch',
|
||||
value: false,
|
||||
value: false
|
||||
},
|
||||
interview_pwd_switch: {
|
||||
key: 'passwordSwitch',
|
||||
@ -112,5 +112,5 @@ export default {
|
||||
relyFunc: (data) => {
|
||||
return data.whitelistType === 'MEMBER'
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@
|
||||
<el-form-item label="验证码" prop="captcha">
|
||||
<div class="captcha-wrapper">
|
||||
<el-input style="width: 280px" v-model="formData.captcha" size="large"></el-input>
|
||||
<div class="captcha-img" click="refreshCaptcha" v-html="captchaImgData"></div>
|
||||
<div class="captcha-img" @click="refreshCaptcha" v-html="captchaImgData"></div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
set as _set,
|
||||
isNumber as _isNumber
|
||||
} from 'lodash-es'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { QUESTION_TYPE } from '@/common/typeEnum'
|
||||
import { getQuestionByType } from '@/management/utils/index'
|
||||
import { filterQuestionPreviewData } from '@/management/utils/index'
|
||||
@ -24,7 +25,6 @@ import { CODE_MAP } from '../api/base'
|
||||
import { RuleBuild } from '@/common/logicEngine/RuleBuild'
|
||||
import { useShowLogicInfo } from '@/management/hooks/useShowLogicInfo'
|
||||
import { useJumpLogicInfo } from '@/management/hooks/useJumpLogicInfo'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
|
||||
const innerMetaConfig = {
|
||||
submit: {
|
||||
@ -69,7 +69,6 @@ function useInitializeSchema(surveyId: Ref<string>, initializeSchemaCallBack: ()
|
||||
begTime: '',
|
||||
endTime: '',
|
||||
language: 'chinese',
|
||||
showVoteProcess: 'allow',
|
||||
tLimit: 0,
|
||||
answerBegTime: '',
|
||||
answerEndTime: '',
|
||||
@ -93,7 +92,7 @@ function useInitializeSchema(surveyId: Ref<string>, initializeSchemaCallBack: ()
|
||||
})
|
||||
const { showLogicEngine, initShowLogicEngine, jumpLogicEngine, initJumpLogicEngine } =
|
||||
useLogicEngine(schema)
|
||||
|
||||
|
||||
function initSchema({ metaData, codeData }: { metaData: any; codeData: any }) {
|
||||
schema.metaData = metaData
|
||||
schema.bannerConf = _merge({}, schema.bannerConf, codeData.bannerConf)
|
||||
@ -107,6 +106,21 @@ function useInitializeSchema(surveyId: Ref<string>, initializeSchemaCallBack: ()
|
||||
schema.pageConf = codeData.pageConf
|
||||
}
|
||||
|
||||
const sessionId = ref('')
|
||||
async function initSessionId() {
|
||||
const sessionIdKey = `${surveyId.value}_sessionId`
|
||||
const localSessionId = sessionStorage.getItem(sessionIdKey)
|
||||
if (localSessionId) {
|
||||
sessionId.value = localSessionId
|
||||
} else {
|
||||
const res: Record<string, any> = await getSessionId({ surveyId: surveyId.value })
|
||||
if (res.code === 200) {
|
||||
sessionId.value = res.data.sessionId
|
||||
sessionStorage.setItem(sessionIdKey, sessionId.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getSchemaFromRemote() {
|
||||
const res: any = await getSurveyById(surveyId.value)
|
||||
if (res.code === 200) {
|
||||
@ -149,10 +163,11 @@ function useInitializeSchema(surveyId: Ref<string>, initializeSchemaCallBack: ()
|
||||
|
||||
return {
|
||||
schema,
|
||||
initSchema,
|
||||
getSchemaFromRemote,
|
||||
showLogicEngine,
|
||||
jumpLogicEngine,
|
||||
sessionId,
|
||||
initSessionId
|
||||
}
|
||||
}
|
||||
|
||||
@ -479,7 +494,6 @@ function useLogicEngine(schema: any) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type IBannerItem = {
|
||||
name: string
|
||||
key: string
|
||||
@ -487,37 +501,54 @@ type IBannerItem = {
|
||||
}
|
||||
type IBannerList = Record<string, IBannerItem>
|
||||
|
||||
|
||||
|
||||
export const useEditStore = defineStore('edit', () => {
|
||||
const surveyId = ref('')
|
||||
const bannerList: Ref<IBannerList> = ref({})
|
||||
const fetchBannerData = async () => {
|
||||
const res: any = await getBannerData()
|
||||
if (res.code === CODE_MAP.SUCCESS) {
|
||||
bannerList.value = res.data
|
||||
}
|
||||
}
|
||||
|
||||
const cooperPermissions = ref(Object.values(SurveyPermissions))
|
||||
const fetchCooperPermissions = async (id: string) => {
|
||||
const res: any = await getCollaboratorPermissions(id)
|
||||
if (res.code === CODE_MAP.SUCCESS) {
|
||||
cooperPermissions.value = res.data.permissions
|
||||
}
|
||||
}
|
||||
|
||||
const schemaUpdateTime = ref(Date.now())
|
||||
|
||||
function updateTime() {
|
||||
schemaUpdateTime.value = Date.now()
|
||||
}
|
||||
|
||||
const surveyId = ref('')
|
||||
function setSurveyId(id: string) {
|
||||
surveyId.value = id
|
||||
}
|
||||
|
||||
const { schema, initSchema, getSchemaFromRemote, showLogicEngine, jumpLogicEngine } =
|
||||
useInitializeSchema(surveyId, () => {
|
||||
editGlobalBaseConf.initCounts()
|
||||
// 初始化schem相关
|
||||
const {
|
||||
schema,
|
||||
sessionId,
|
||||
initSessionId,
|
||||
getSchemaFromRemote,
|
||||
showLogicEngine,
|
||||
jumpLogicEngine
|
||||
} = useInitializeSchema(surveyId, () => {
|
||||
editGlobalBaseConf.initCounts()
|
||||
})
|
||||
|
||||
function changeSchema({ key, value }: { key: string; value: any }) {
|
||||
_set(schema, key, value)
|
||||
updateTime()
|
||||
}
|
||||
|
||||
function changeThemePreset(presets: any) {
|
||||
Object.keys(presets).forEach((key) => {
|
||||
_set(schema, key, presets[key])
|
||||
})
|
||||
|
||||
const sessionId = ref('')
|
||||
|
||||
async function initSessionId() {
|
||||
const sessionIdKey = `${surveyId.value}_sessionId`;
|
||||
const localSessionId = sessionStorage.getItem(sessionIdKey)
|
||||
if (localSessionId) {
|
||||
sessionId.value = localSessionId
|
||||
} else {
|
||||
const res: Record<string, any> = await getSessionId({ surveyId: surveyId.value })
|
||||
if (res.code === 200) {
|
||||
sessionId.value = res.data.sessionId
|
||||
sessionStorage.setItem(sessionIdKey, sessionId.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const questionDataList = toRef(schema, 'questionDataList')
|
||||
@ -527,86 +558,14 @@ export const useEditStore = defineStore('edit', () => {
|
||||
schema.questionDataList = data
|
||||
}
|
||||
|
||||
|
||||
const fetchBannerData = async () => {
|
||||
const res: any = await getBannerData()
|
||||
if (res.code === CODE_MAP.SUCCESS) {
|
||||
bannerList.value = res.data
|
||||
const createNewQuestion = ({ type }: { type: QUESTION_TYPE }) => {
|
||||
const fields = questionDataList.value.map((item: any) => item.field)
|
||||
const newQuestion = getQuestionByType(type, fields)
|
||||
newQuestion.title = newQuestion.title = `标题${newQuestionIndex.value + 1}`
|
||||
if (type === QUESTION_TYPE.VOTE) {
|
||||
newQuestion.innerType = QUESTION_TYPE.RADIO
|
||||
}
|
||||
}
|
||||
|
||||
const fetchCooperPermissions = async (id: string) => {
|
||||
const res: any = await getCollaboratorPermissions(id)
|
||||
if (res.code === CODE_MAP.SUCCESS) {
|
||||
cooperPermissions.value = res.data.permissions
|
||||
}
|
||||
}
|
||||
// const { showLogicEngine, initShowLogicEngine, jumpLogicEngine, initJumpLogicEngine } = useLogicEngine(schema)
|
||||
const {
|
||||
currentEditOne,
|
||||
currentEditKey,
|
||||
currentEditStatus,
|
||||
moduleConfig,
|
||||
formConfigList,
|
||||
currentEditMeta,
|
||||
setCurrentEditOne,
|
||||
changeCurrentEditStatus
|
||||
} = useCurrentEdit({ schema, questionDataList })
|
||||
|
||||
async function init() {
|
||||
const { metaData } = schema
|
||||
if (!metaData || (metaData as any)?._id !== surveyId.value) {
|
||||
await getSchemaFromRemote()
|
||||
await initSessionId()
|
||||
}
|
||||
currentEditOne.value = null
|
||||
currentEditStatus.value = 'Success'
|
||||
}
|
||||
|
||||
function updateTime() {
|
||||
schemaUpdateTime.value = Date.now()
|
||||
}
|
||||
|
||||
const {
|
||||
pageEditOne,
|
||||
pageConf,
|
||||
isFinallyPage,
|
||||
pageCount,
|
||||
pageQuestionData,
|
||||
getSorter,
|
||||
updatePageEditOne,
|
||||
deletePage,
|
||||
pageOperations,
|
||||
addPage,
|
||||
getPageQuestionData,
|
||||
copyPage,
|
||||
swapArrayRanges,
|
||||
setPage
|
||||
} = usePageEdit({ schema, questionDataList }, updateTime)
|
||||
|
||||
const { copyQuestion, addQuestion, deleteQuestion, moveQuestion } = useQuestionDataListOperations(
|
||||
{
|
||||
questionDataList,
|
||||
updateTime,
|
||||
pageOperations,
|
||||
updateCounts: editGlobalBaseConf.updateCounts
|
||||
}
|
||||
)
|
||||
|
||||
function moveQuestionDataList(data: any) {
|
||||
const { startIndex, endIndex } = getSorter()
|
||||
const newData = [
|
||||
...questionDataList.value.slice(0, startIndex),
|
||||
...data,
|
||||
...questionDataList.value.slice(endIndex)
|
||||
]
|
||||
const countTotal: number = (schema.pageConf as Array<number>).reduce(
|
||||
(v: number, i: number) => v + i
|
||||
)
|
||||
if (countTotal != newData.length) {
|
||||
schema.pageConf[pageEditOne.value - 1] = (schema.pageConf[pageEditOne.value - 1] + 1) as never
|
||||
}
|
||||
setQuestionDataList(newData)
|
||||
return newQuestion
|
||||
}
|
||||
|
||||
const compareQuestionSeq = (val: Array<any>) => {
|
||||
@ -644,25 +603,70 @@ export const useEditStore = defineStore('edit', () => {
|
||||
}
|
||||
})
|
||||
|
||||
const createNewQuestion = ({ type }: { type: QUESTION_TYPE }) => {
|
||||
const fields = questionDataList.value.map((item: any) => item.field)
|
||||
const newQuestion = getQuestionByType(type, fields)
|
||||
newQuestion.title = newQuestion.title = `标题${newQuestionIndex.value + 1}`
|
||||
if (type === QUESTION_TYPE.VOTE) {
|
||||
newQuestion.innerType = QUESTION_TYPE.RADIO
|
||||
// 当前编辑题目
|
||||
const {
|
||||
currentEditOne,
|
||||
currentEditKey,
|
||||
currentEditStatus,
|
||||
moduleConfig,
|
||||
formConfigList,
|
||||
currentEditMeta,
|
||||
setCurrentEditOne,
|
||||
changeCurrentEditStatus
|
||||
} = useCurrentEdit({ schema, questionDataList })
|
||||
|
||||
// 初始化问卷
|
||||
async function init() {
|
||||
const { metaData } = schema
|
||||
if (!metaData || (metaData as any)?._id !== surveyId.value) {
|
||||
await Promise.all([getSchemaFromRemote(), initSessionId()])
|
||||
}
|
||||
return newQuestion
|
||||
currentEditOne.value = null
|
||||
currentEditStatus.value = 'Success'
|
||||
}
|
||||
|
||||
function changeSchema({ key, value }: { key: string; value: any }) {
|
||||
_set(schema, key, value)
|
||||
updateTime()
|
||||
}
|
||||
// 分页相关
|
||||
const {
|
||||
pageEditOne,
|
||||
pageConf,
|
||||
isFinallyPage,
|
||||
pageCount,
|
||||
pageQuestionData,
|
||||
getSorter,
|
||||
updatePageEditOne,
|
||||
deletePage,
|
||||
pageOperations,
|
||||
addPage,
|
||||
getPageQuestionData,
|
||||
copyPage,
|
||||
swapArrayRanges,
|
||||
setPage
|
||||
} = usePageEdit({ schema, questionDataList }, updateTime)
|
||||
|
||||
function changeThemePreset(presets: any) {
|
||||
Object.keys(presets).forEach((key) => {
|
||||
_set(schema, key, presets[key])
|
||||
})
|
||||
// 问卷列表相关操作
|
||||
const { copyQuestion, addQuestion, deleteQuestion, moveQuestion } = useQuestionDataListOperations(
|
||||
{
|
||||
questionDataList,
|
||||
updateTime,
|
||||
pageOperations,
|
||||
updateCounts: editGlobalBaseConf.updateCounts
|
||||
}
|
||||
)
|
||||
|
||||
function moveQuestionDataList(data: any) {
|
||||
const { startIndex, endIndex } = getSorter()
|
||||
const newData = [
|
||||
...questionDataList.value.slice(0, startIndex),
|
||||
...data,
|
||||
...questionDataList.value.slice(endIndex)
|
||||
]
|
||||
const countTotal: number = (schema.pageConf as Array<number>).reduce(
|
||||
(v: number, i: number) => v + i
|
||||
)
|
||||
if (countTotal != newData.length) {
|
||||
schema.pageConf[pageEditOne.value - 1] = (schema.pageConf[pageEditOne.value - 1] + 1) as never
|
||||
}
|
||||
setQuestionDataList(newData)
|
||||
}
|
||||
|
||||
return {
|
||||
@ -670,7 +674,6 @@ export const useEditStore = defineStore('edit', () => {
|
||||
surveyId,
|
||||
sessionId,
|
||||
setSurveyId,
|
||||
initSessionId,
|
||||
bannerList,
|
||||
fetchBannerData,
|
||||
cooperPermissions,
|
||||
@ -703,7 +706,6 @@ export const useEditStore = defineStore('edit', () => {
|
||||
setQuestionDataList,
|
||||
moveQuestionDataList,
|
||||
init,
|
||||
initSchema,
|
||||
getSchemaFromRemote,
|
||||
copyQuestion,
|
||||
addQuestion,
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import localstorage from '@/common/localstorage'
|
||||
|
||||
type IUserInfo = {
|
||||
username: string
|
||||
token: string
|
||||
@ -17,12 +19,12 @@ export const useUserStore = defineStore('user', () => {
|
||||
const initialized = ref(false)
|
||||
|
||||
const init = () => {
|
||||
const localData = localStorage.getItem(USER_INFO_KEY)
|
||||
const localData = localstorage.getItem(USER_INFO_KEY)
|
||||
if (localData) {
|
||||
try {
|
||||
const { userInfo: info, loginTime: time } = JSON.parse(localData)
|
||||
const { userInfo: info, loginTime: time } = localData as any
|
||||
if (Date.now() - time > 7 * 3600000) {
|
||||
localStorage.removeItem(USER_INFO_KEY)
|
||||
localstorage.removeItem(USER_INFO_KEY)
|
||||
} else {
|
||||
login(info)
|
||||
}
|
||||
@ -36,18 +38,15 @@ export const useUserStore = defineStore('user', () => {
|
||||
userInfo.value = data
|
||||
hasLogined.value = true
|
||||
loginTime.value = Date.now()
|
||||
localStorage.setItem(
|
||||
USER_INFO_KEY,
|
||||
JSON.stringify({
|
||||
userInfo: data,
|
||||
loginTime: loginTime
|
||||
})
|
||||
)
|
||||
localstorage.setItem(USER_INFO_KEY, {
|
||||
userInfo: data,
|
||||
loginTime: loginTime
|
||||
})
|
||||
}
|
||||
const logout = () => {
|
||||
userInfo.value = null
|
||||
hasLogined.value = false
|
||||
localStorage.removeItem(USER_INFO_KEY)
|
||||
localstorage.removeItem(USER_INFO_KEY)
|
||||
}
|
||||
|
||||
return { userInfo, hasLogined, loginTime, initialized, init, login, logout }
|
||||
|
@ -19,7 +19,6 @@ export function getRandom(len) {
|
||||
|
||||
const optionListItem = [
|
||||
'text',
|
||||
'imageUrl',
|
||||
'others',
|
||||
'mustOthers',
|
||||
'limit',
|
||||
|
@ -25,14 +25,12 @@
|
||||
padding: 0 0.2rem !important;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
|
||||
// .choice-content, .choice-item {
|
||||
// width: 100%;
|
||||
// }
|
||||
|
||||
&.vertical {
|
||||
display: flex;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.choice-outer {
|
||||
|
@ -54,7 +54,6 @@ const meta = {
|
||||
defaultValue: [
|
||||
{
|
||||
text: '对',
|
||||
imageUrl: '',
|
||||
others: false,
|
||||
mustOthers: false,
|
||||
othersKey: '',
|
||||
@ -63,7 +62,6 @@ const meta = {
|
||||
},
|
||||
{
|
||||
text: '错',
|
||||
imageUrl: '',
|
||||
others: false,
|
||||
mustOthers: false,
|
||||
othersKey: '',
|
||||
@ -79,29 +77,32 @@ const meta = {
|
||||
defaultValue: 'vertical'
|
||||
}
|
||||
],
|
||||
formConfig: [basicConfig, {
|
||||
name: 'optionConfig',
|
||||
title: '选项配置',
|
||||
type: 'Customed',
|
||||
content: [
|
||||
{
|
||||
label: '排列方式',
|
||||
type: 'RadioGroup',
|
||||
key: 'layout',
|
||||
value: 'vertical',
|
||||
options: [
|
||||
{
|
||||
label: '竖排',
|
||||
value: 'vertical'
|
||||
},
|
||||
{
|
||||
label: '横排',
|
||||
value: 'horizontal'
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}],
|
||||
formConfig: [
|
||||
basicConfig,
|
||||
{
|
||||
name: 'optionConfig',
|
||||
title: '选项配置',
|
||||
type: 'Customed',
|
||||
content: [
|
||||
{
|
||||
label: '排列方式',
|
||||
type: 'RadioGroup',
|
||||
key: 'layout',
|
||||
value: 'vertical',
|
||||
options: [
|
||||
{
|
||||
label: '竖排',
|
||||
value: 'vertical'
|
||||
},
|
||||
{
|
||||
label: '横排',
|
||||
value: 'horizontal'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
editConfigure: {
|
||||
optionEdit: {
|
||||
show: false
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { computed, defineComponent, shallowRef, defineAsyncComponent, watch } from 'vue'
|
||||
import { computed, defineComponent, shallowRef, defineAsyncComponent } from 'vue'
|
||||
import { includes } from 'lodash-es'
|
||||
|
||||
import BaseChoice from '../BaseChoice'
|
||||
|
@ -53,7 +53,6 @@ const meta = {
|
||||
defaultValue: [
|
||||
{
|
||||
text: '选项1',
|
||||
imageUrl: '',
|
||||
others: false,
|
||||
mustOthers: false,
|
||||
othersKey: '',
|
||||
@ -62,7 +61,6 @@ const meta = {
|
||||
},
|
||||
{
|
||||
text: '选项2',
|
||||
imageUrl: '',
|
||||
others: false,
|
||||
mustOthers: false,
|
||||
othersKey: '',
|
||||
|
@ -12,7 +12,7 @@ function useOptionBase(options) {
|
||||
others: false,
|
||||
mustOthers: false,
|
||||
othersKey: '',
|
||||
placeholderDesc: '',
|
||||
placeholderDesc: ''
|
||||
}
|
||||
if (typeof text !== 'string') {
|
||||
text = '选项'
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { defineComponent, shallowRef, watch, defineAsyncComponent } from 'vue'
|
||||
import { defineComponent, shallowRef, defineAsyncComponent } from 'vue'
|
||||
import BaseChoice from '../BaseChoice'
|
||||
|
||||
/**
|
||||
|
@ -54,7 +54,6 @@ const meta = {
|
||||
defaultValue: [
|
||||
{
|
||||
text: '选项1',
|
||||
imageUrl: '',
|
||||
others: false,
|
||||
mustOthers: false,
|
||||
othersKey: '',
|
||||
@ -63,7 +62,6 @@ const meta = {
|
||||
},
|
||||
{
|
||||
text: '选项2',
|
||||
imageUrl: '',
|
||||
others: false,
|
||||
mustOthers: false,
|
||||
othersKey: '',
|
||||
|
@ -48,9 +48,9 @@ export default defineComponent({
|
||||
|
||||
watch(status, (v) => {
|
||||
if (v === 'edit') {
|
||||
document.addEventListener('click', handleDocumentClick, {capture: true})
|
||||
document.addEventListener('click', handleDocumentClick, { capture: true })
|
||||
} else {
|
||||
document.removeEventListener('click', handleDocumentClick, {capture: true})
|
||||
document.removeEventListener('click', handleDocumentClick, { capture: true })
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -54,7 +54,6 @@ const meta = {
|
||||
defaultValue: [
|
||||
{
|
||||
text: '选项1',
|
||||
imageUrl: '',
|
||||
others: false,
|
||||
mustOthers: false,
|
||||
othersKey: '',
|
||||
@ -63,7 +62,6 @@ const meta = {
|
||||
},
|
||||
{
|
||||
text: '选项2',
|
||||
imageUrl: '',
|
||||
others: false,
|
||||
mustOthers: false,
|
||||
othersKey: '',
|
||||
@ -120,7 +118,9 @@ const meta = {
|
||||
key: 'minNum',
|
||||
value: '',
|
||||
min: 0,
|
||||
max: moduleConfig => { return moduleConfig?.maxNum || 0 },
|
||||
max: (moduleConfig) => {
|
||||
return moduleConfig?.maxNum || 0
|
||||
},
|
||||
contentClass: 'input-number-config'
|
||||
},
|
||||
{
|
||||
@ -128,8 +128,12 @@ const meta = {
|
||||
type: 'InputNumber',
|
||||
key: 'maxNum',
|
||||
value: '',
|
||||
min: moduleConfig => { return moduleConfig?.minNum || 0 },
|
||||
max: moduleConfig => { return moduleConfig?.options?.length || 0 },
|
||||
min: (moduleConfig) => {
|
||||
return moduleConfig?.minNum || 0
|
||||
},
|
||||
max: (moduleConfig) => {
|
||||
return moduleConfig?.options?.length || 0
|
||||
},
|
||||
contentClass: 'input-number-config'
|
||||
}
|
||||
]
|
||||
|
@ -13,7 +13,6 @@ import { ElMessage } from 'element-plus'
|
||||
import 'element-plus/theme-chalk/src/message.scss'
|
||||
import { FORM_CHANGE_EVENT_KEY } from '@/materials/setters/constant'
|
||||
|
||||
|
||||
interface Props {
|
||||
formConfig: any
|
||||
moduleConfig: any
|
||||
@ -66,9 +65,12 @@ const handleInputChange = (value: number) => {
|
||||
|
||||
emit(FORM_CHANGE_EVENT_KEY, { key, value })
|
||||
}
|
||||
watch(() => props.moduleConfig, (newVal) => {
|
||||
myModuleConfig.value = newVal
|
||||
})
|
||||
watch(
|
||||
() => props.moduleConfig,
|
||||
(newVal) => {
|
||||
myModuleConfig.value = newVal
|
||||
}
|
||||
)
|
||||
watch(
|
||||
() => props.formConfig.value,
|
||||
(newVal) => {
|
||||
|
@ -1,79 +0,0 @@
|
||||
<template>
|
||||
<div class="mask" v-show="visible">
|
||||
<div class="box">
|
||||
<div class="title">{{ title }}</div>
|
||||
<div class="btn-box">
|
||||
<div class="btn cancel" @click="handleCancel">{{ cancelBtnText }}</div>
|
||||
<div class="btn confirm" @click="handleConfirm">{{ confirmBtnText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
visible?: boolean
|
||||
cancelBtnText?: string
|
||||
confirmBtnText?: string
|
||||
title?: string
|
||||
}
|
||||
|
||||
interface Emit {
|
||||
(ev: 'confirm', callback: () => void): void
|
||||
(ev: 'cancel', callback: () => void): void
|
||||
(ev: 'close'): void
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emit>()
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
visible: false,
|
||||
cancelBtnText: '取消',
|
||||
confirmBtnText: '确定',
|
||||
title: ''
|
||||
})
|
||||
|
||||
const handleConfirm = () => {
|
||||
emit('confirm', () => {
|
||||
emit('close')
|
||||
})
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('cancel', () => {
|
||||
emit('close')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import url('../styles/dialog.scss');
|
||||
|
||||
.btn-box {
|
||||
padding: 20px 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.btn {
|
||||
width: 48%;
|
||||
font-size: 0.28rem;
|
||||
border-radius: 0.04rem;
|
||||
text-align: center;
|
||||
padding: 0.16rem 0;
|
||||
line-height: 0.4rem;
|
||||
cursor: pointer;
|
||||
|
||||
&.cancel {
|
||||
background: #fff;
|
||||
color: #92949d;
|
||||
border: 1px solid #e3e4e8;
|
||||
}
|
||||
|
||||
&.confirm {
|
||||
background-color: #4a4c5b;
|
||||
border: 1px solid #4a4c5b;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -9,17 +9,18 @@
|
||||
></QuestionRuleContainer>
|
||||
</template>
|
||||
<script setup>
|
||||
import { unref, computed, watch } from 'vue'
|
||||
import { unref, computed, watch, ref } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import QuestionRuleContainer from '../../materials/questions/QuestionRuleContainer'
|
||||
import { useVoteMap } from '@/render/hooks/useVoteMap'
|
||||
import { useShowOthers } from '@/render/hooks/useShowOthers'
|
||||
import { useShowInput } from '@/render/hooks/useShowInput'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { debounce, cloneDeep } from 'lodash-es'
|
||||
import { useQuestionStore } from '../stores/question'
|
||||
import { useSurveyStore } from '../stores/survey'
|
||||
|
||||
import { FORMDATA_SUFFIX, SUBMIT_FLAG } from '../utils/constant'
|
||||
import { NORMAL_CHOICES, RATES, QUESTION_TYPE } from '@/common/typeEnum.ts'
|
||||
import localstorage from '@/common/localstorage'
|
||||
|
||||
const props = defineProps({
|
||||
indexNumber: {
|
||||
@ -46,7 +47,7 @@ const { changeField, changeIndex, needHideFields } = storeToRefs(questionStore)
|
||||
const questionConfig = computed(() => {
|
||||
let moduleConfig = props.moduleConfig
|
||||
const { type, field, options = [], ...rest } = cloneDeep(moduleConfig)
|
||||
// console.log(field,'这里依赖的formValue,所以change时会触发重新计算')
|
||||
|
||||
let alloptions = options
|
||||
|
||||
if (type === QUESTION_TYPE.VOTE) {
|
||||
@ -126,14 +127,28 @@ const handleChange = (data) => {
|
||||
if (props.moduleConfig.type === QUESTION_TYPE.VOTE) {
|
||||
questionStore.updateVoteData(data)
|
||||
}
|
||||
// 断点续答的的数据缓存
|
||||
localStorageBack()
|
||||
processJumpSkip()
|
||||
valueTemp.value = data.value
|
||||
debounceStorageSave()
|
||||
}
|
||||
const valueTemp = ref()
|
||||
const handleInput = (e) => {
|
||||
valueTemp.value = e.target.value
|
||||
debounceStorageSave()
|
||||
}
|
||||
|
||||
const handleInput = () => {
|
||||
localStorageBack()
|
||||
}
|
||||
const debounceStorageSave = debounce(() => {
|
||||
let data = {
|
||||
key: props.moduleConfig.field,
|
||||
value: valueTemp.value
|
||||
}
|
||||
const formData = cloneDeep(formValues.value)
|
||||
let { key, value } = data
|
||||
if (key in formData) {
|
||||
formData[key] = value
|
||||
}
|
||||
localStorageSave(formData)
|
||||
}, 500)
|
||||
|
||||
const processJumpSkip = () => {
|
||||
const targetResult = surveyStore.jumpLogicEngine
|
||||
@ -175,12 +190,9 @@ const processJumpSkip = () => {
|
||||
.map((item) => item.field)
|
||||
questionStore.addNeedHideFields(skipKey)
|
||||
}
|
||||
const localStorageBack = () => {
|
||||
var formData = Object.assign({}, surveyStore.formValues)
|
||||
|
||||
//浏览器存储
|
||||
localStorage.removeItem(surveyStore.surveyPath + '_questionData')
|
||||
localStorage.setItem(surveyStore.surveyPath + '_questionData', JSON.stringify(formData))
|
||||
localStorage.setItem('isSubmit', JSON.stringify(false))
|
||||
const localStorageSave = (formData) => {
|
||||
localstorage.removeItem(surveyStore.surveyPath + FORMDATA_SUFFIX)
|
||||
localstorage.setItem(surveyStore.surveyPath + FORMDATA_SUFFIX, formData)
|
||||
localstorage.setItem(SUBMIT_FLAG, false)
|
||||
}
|
||||
</script>
|
||||
|
@ -4,15 +4,17 @@ export const useQuestionInfo = (field: string) => {
|
||||
const questionstore = useQuestionStore()
|
||||
|
||||
const questionTitle = cleanRichText(questionstore.questionData[field]?.title)
|
||||
const getOptionTitle = (value:any) => {
|
||||
const options = questionstore.questionData[field]?.options || []
|
||||
if (value instanceof Array) {
|
||||
return options
|
||||
.filter((item:any) => value.includes(item.hash))
|
||||
.map((item:any) => cleanRichText(item.text))
|
||||
} else {
|
||||
return options.filter((item:any) => item.hash === value).map((item:any) => cleanRichText(item.text))
|
||||
}
|
||||
const getOptionTitle = (value: any) => {
|
||||
const options = questionstore.questionData[field]?.options || []
|
||||
if (value instanceof Array) {
|
||||
return options
|
||||
.filter((item: any) => value.includes(item.hash))
|
||||
.map((item: any) => cleanRichText(item.text))
|
||||
} else {
|
||||
return options
|
||||
.filter((item: any) => item.hash === value)
|
||||
.map((item: any) => cleanRichText(item.text))
|
||||
}
|
||||
}
|
||||
return { questionTitle, getOptionTitle }
|
||||
}
|
||||
|
@ -9,9 +9,9 @@
|
||||
<script>
|
||||
;(function () {
|
||||
function resetRemUnit() {
|
||||
var PC_W = 750
|
||||
var docEl = window.document.documentElement
|
||||
var width = docEl.getBoundingClientRect().width || 375
|
||||
const PC_W = 750
|
||||
let docEl = window.document.documentElement
|
||||
let width = docEl.getBoundingClientRect().width || 375
|
||||
|
||||
if (!/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)) {
|
||||
width = width < PC_W ? width : PC_W
|
||||
@ -20,15 +20,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
var f = Math.min(width / 7.5, 50)
|
||||
const f = Math.min(width / 7.5, 50)
|
||||
docEl.style.fontSize = f + 'px'
|
||||
|
||||
var d = window.document.createElement('div')
|
||||
let d = window.document.createElement('div')
|
||||
d.style.width = '1rem'
|
||||
d.style.display = 'none'
|
||||
var head = window.document.getElementsByTagName('head')[0]
|
||||
let head = window.document.getElementsByTagName('head')[0]
|
||||
head.appendChild(d)
|
||||
var realf = parseFloat(window.getComputedStyle(d, null).getPropertyValue('width'))
|
||||
const realf = parseFloat(window.getComputedStyle(d, null).getPropertyValue('width'))
|
||||
|
||||
if (f !== realf) {
|
||||
docEl.style.fontSize = f * (f / realf) + 'px'
|
||||
|
@ -38,7 +38,8 @@ import encrypt from '../utils/encrypt'
|
||||
|
||||
import useCommandComponent from '../hooks/useCommandComponent'
|
||||
import { getPublishedSurveyInfo, getPreviewSchema } from '../api/survey'
|
||||
import { useQuestionInfo } from '../hooks/useQuestionInfo'
|
||||
import { FORMDATA_SUFFIX, SUBMIT_FLAG } from '@/render/utils/constant'
|
||||
import localstorage from '@/common/localstorage'
|
||||
|
||||
interface Props {
|
||||
questionInfo?: any
|
||||
@ -151,13 +152,13 @@ const normalizationRequestBody = () => {
|
||||
}
|
||||
|
||||
//浏览器缓存数据
|
||||
localStorage.removeItem(surveyPath.value + '_questionData')
|
||||
localStorage.removeItem('isSubmit')
|
||||
localstorage.removeItem(surveyPath.value + FORMDATA_SUFFIX)
|
||||
localstorage.removeItem(SUBMIT_FLAG)
|
||||
//数据加密
|
||||
var formData: Record<string, any> = Object.assign({}, surveyStore.formValues)
|
||||
let formData: Record<string, any> = Object.assign({}, surveyStore.formValues)
|
||||
|
||||
localStorage.setItem(surveyPath.value + '_questionData', JSON.stringify(formData))
|
||||
localStorage.setItem('isSubmit', JSON.stringify(true))
|
||||
localstorage.setItem(surveyPath.value + FORMDATA_SUFFIX, formData)
|
||||
localstorage.setItem(SUBMIT_FLAG, true)
|
||||
|
||||
if (encryptInfo?.encryptType) {
|
||||
result.encryptType = encryptInfo.encryptType
|
||||
|
@ -3,9 +3,9 @@ import { defineStore } from 'pinia'
|
||||
import { set } from 'lodash-es'
|
||||
import { useSurveyStore } from '@/render/stores/survey'
|
||||
import { queryVote } from '@/render/api/survey'
|
||||
import { QUESTION_TYPE, NORMAL_CHOICES } from '@/common/typeEnum'
|
||||
|
||||
const VOTE_INFO_KEY = 'voteinfo'
|
||||
import { QUESTION_TYPE } from '@/common/typeEnum'
|
||||
import { VOTE_INFO_KEY } from '@/render/utils/constant'
|
||||
import localstorage from '@/common/localstorage'
|
||||
|
||||
// 投票进度逻辑聚合
|
||||
const usevVoteMap = (questionData) => {
|
||||
@ -28,19 +28,16 @@ const usevVoteMap = (questionData) => {
|
||||
return
|
||||
}
|
||||
try {
|
||||
localStorage.removeItem(VOTE_INFO_KEY)
|
||||
localstorage.removeItem(VOTE_INFO_KEY)
|
||||
const voteRes = await queryVote({
|
||||
surveyPath,
|
||||
fieldList: fieldList.join(',')
|
||||
})
|
||||
|
||||
if (voteRes.code === 200) {
|
||||
localStorage.setItem(
|
||||
VOTE_INFO_KEY,
|
||||
JSON.stringify({
|
||||
...voteRes.data
|
||||
})
|
||||
)
|
||||
localstorage.setItem(VOTE_INFO_KEY, {
|
||||
...voteRes.data
|
||||
})
|
||||
setVoteMap(voteRes.data)
|
||||
}
|
||||
} catch (error) {
|
||||
@ -60,9 +57,8 @@ const usevVoteMap = (questionData) => {
|
||||
}
|
||||
const updateVoteData = (data) => {
|
||||
const { key: questionKey, value: questionVal } = data
|
||||
// 更新前获取接口缓存在localStorage中的数据
|
||||
const localData = localStorage.getItem(VOTE_INFO_KEY)
|
||||
const voteinfo = JSON.parse(localData)
|
||||
// 更新前获取接口缓存在localstorage中的数据
|
||||
const voteinfo = localstorage.getItem(VOTE_INFO_KEY)
|
||||
const currentQuestion = questionData.value[questionKey]
|
||||
const options = currentQuestion.options
|
||||
const voteTotal = voteinfo?.[questionKey]?.total || 0
|
||||
|
@ -2,23 +2,24 @@ import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { defineStore } from 'pinia'
|
||||
import { cloneDeep, pick } from 'lodash-es'
|
||||
|
||||
import { isMobile as isInMobile } from '@/render/utils/index'
|
||||
import { getEncryptInfo as getEncryptInfoApi } from '@/render/api/survey'
|
||||
import { useQuestionStore } from '@/render/stores/question'
|
||||
import { useErrorInfo } from '@/render/stores/errorInfo'
|
||||
|
||||
import moment from 'moment'
|
||||
// 引入中文
|
||||
import 'moment/locale/zh-cn'
|
||||
// 设置中文
|
||||
|
||||
import { isMobile as isInMobile } from '@/render/utils/index'
|
||||
import { getEncryptInfo as getEncryptInfoApi } from '@/render/api/survey'
|
||||
import { useQuestionStore } from '@/render/stores/question'
|
||||
import { useErrorInfo } from '@/render/stores/errorInfo'
|
||||
import { FORMDATA_SUFFIX, SUBMIT_FLAG } from '@/render/utils/constant'
|
||||
|
||||
import adapter from '../adapter'
|
||||
import { RuleMatch } from '@/common/logicEngine/RulesMatch'
|
||||
import useCommandComponent from '../hooks/useCommandComponent'
|
||||
import BackAnswerDialog from '../components/BackAnswerDialog.vue'
|
||||
import ConfirmDialog from '../components/ConfirmDialog.vue'
|
||||
import localstorage from '@/common/localstorage'
|
||||
|
||||
const confirm = useCommandComponent(BackAnswerDialog)
|
||||
const confirm = useCommandComponent(ConfirmDialog)
|
||||
|
||||
moment.locale('zh-cn')
|
||||
/**
|
||||
@ -134,7 +135,7 @@ export const useSurveyStore = defineStore('survey', () => {
|
||||
'pageConf'
|
||||
])
|
||||
)
|
||||
// todo: 建议通过questionStore提供setqueationdata方法修改属性,否则不好跟踪变化
|
||||
|
||||
questionStore.questionData = questionData
|
||||
questionStore.questionSeq = questionSeq
|
||||
|
||||
@ -168,52 +169,29 @@ export const useSurveyStore = defineStore('survey', () => {
|
||||
// 加载空白问卷
|
||||
clearFormData(option)
|
||||
|
||||
const { breakAnswer, backAnswer } = option.baseConf
|
||||
const localData = JSON.parse(localStorage.getItem(surveyPath.value + '_questionData'))
|
||||
const { fillAnswer, fillSubmitAnswer } = option.baseConf
|
||||
const localData = localstorage.getItem(surveyPath.value + FORMDATA_SUFFIX)
|
||||
|
||||
const isSubmit = JSON.parse(localStorage.getItem('isSubmit'))
|
||||
|
||||
if (localData) {
|
||||
// 断点续答
|
||||
if (breakAnswer) {
|
||||
confirm({
|
||||
title: '是否继续上次填写的内容?',
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
// 回填答题内容
|
||||
fillFormData(localData)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
} finally {
|
||||
confirm.close()
|
||||
}
|
||||
},
|
||||
onCancel: async () => {
|
||||
const isSubmit = localstorage.getItem(SUBMIT_FLAG)
|
||||
// 开启了断点续答 or 回填上一次提交内容
|
||||
if ((fillAnswer || (fillSubmitAnswer && isSubmit)) && localData) {
|
||||
const title = fillAnswer ? '是否继续上次填写的内容?' : '是否继续上次提交的内容?'
|
||||
confirm({
|
||||
title: title,
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
// 回填答题内容
|
||||
fillFormData(localData)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} finally {
|
||||
confirm.close()
|
||||
}
|
||||
})
|
||||
} else if (backAnswer) {
|
||||
if (isSubmit) {
|
||||
confirm({
|
||||
title: '是否继续上次提交的内容?',
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
// 回填答题内容
|
||||
fillFormData(localData)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
} finally {
|
||||
confirm.close()
|
||||
}
|
||||
},
|
||||
onCancel: async () => {
|
||||
confirm.close()
|
||||
}
|
||||
})
|
||||
},
|
||||
onClose: async () => {
|
||||
confirm.close()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
clearFormData(option)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
5
web/src/render/utils/constant.ts
Normal file
5
web/src/render/utils/constant.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export const VOTE_INFO_KEY = 'voteinfo'
|
||||
export const QUOTA_INFO_KEY = 'limitinfo'
|
||||
|
||||
export const SUBMIT_FLAG = 'isSubmit'
|
||||
export const FORMDATA_SUFFIX = '_questionData'
|
@ -16,7 +16,7 @@ export default class EventBus {
|
||||
|
||||
off(eventName, fn) {
|
||||
if (this.events[eventName]) {
|
||||
for (var i = 0; i < this.events[eventName].length; i++) {
|
||||
for (let i = 0; i < this.events[eventName].length; i++) {
|
||||
if (this.events[eventName][i] === fn) {
|
||||
this.events[eventName].splice(i, 1)
|
||||
break
|
||||
|
Loading…
Reference in New Issue
Block a user