feat: 修改验收问题 (#421)
This commit is contained in:
parent
6cbfe20be1
commit
b484b786ea
@ -1,5 +1,5 @@
|
||||
XIAOJU_SURVEY_MONGO_DB_NAME=xiaojuSurvey
|
||||
XIAOJU_SURVEY_MONGO_URL= # mongodb://localhost:27017 # 建议设置强密码
|
||||
XIAOJU_SURVEY_MONGO_URL= # mongodb://127.0.0.1:27017 # 建议设置强密码
|
||||
XIAOJU_SURVEY_MONGO_AUTH_SOURCE= # admin
|
||||
|
||||
XIAOJU_SURVEY_REDIS_HOST=
|
||||
|
2
server/.gitignore
vendored
2
server/.gitignore
vendored
@ -39,3 +39,5 @@ yarn.lock
|
||||
!.vscode/extensions.json
|
||||
|
||||
tmp
|
||||
exportfile
|
||||
userUpload
|
@ -12,4 +12,7 @@ export class Session extends BaseEntity {
|
||||
|
||||
@Column()
|
||||
surveyId: string;
|
||||
|
||||
@Column()
|
||||
userId: string;
|
||||
}
|
||||
|
@ -79,15 +79,16 @@ export class DownloadTaskController {
|
||||
async downloadList(
|
||||
@Query()
|
||||
queryInfo: GetDownloadTaskListDto,
|
||||
@Request() req,
|
||||
) {
|
||||
const { value, error } = GetDownloadTaskListDto.validate(queryInfo);
|
||||
if (error) {
|
||||
this.logger.error(error.message);
|
||||
throw new HttpException('参数有误', EXCEPTION_CODE.PARAMETER_ERROR);
|
||||
}
|
||||
const { ownerId, pageIndex, pageSize } = value;
|
||||
const { pageIndex, pageSize } = value;
|
||||
const { total, list } = await this.downloadTaskService.getDownloadTaskList({
|
||||
ownerId,
|
||||
ownerId: req.user._id.toString(),
|
||||
pageIndex,
|
||||
pageSize,
|
||||
});
|
||||
|
@ -39,6 +39,8 @@ export class SessionController {
|
||||
reqBody: {
|
||||
surveyId: string;
|
||||
},
|
||||
@Request()
|
||||
req,
|
||||
) {
|
||||
const { value, error } = Joi.object({
|
||||
surveyId: Joi.string().required(),
|
||||
@ -50,7 +52,10 @@ export class SessionController {
|
||||
}
|
||||
|
||||
const surveyId = value.surveyId;
|
||||
const session = await this.sessionService.create({ surveyId });
|
||||
const session = await this.sessionService.create({
|
||||
surveyId,
|
||||
userId: req.user._id.toString(),
|
||||
});
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
|
@ -34,6 +34,7 @@ import { WorkspaceGuard } from 'src/guards/workspace.guard';
|
||||
import { PERMISSION as WORKSPACE_PERMISSION } from 'src/enums/workspace';
|
||||
import { SessionService } from '../services/session.service';
|
||||
import { MemberType, WhitelistType } from 'src/interfaces/survey';
|
||||
import { UserService } from 'src/modules/auth/services/user.service';
|
||||
|
||||
@ApiTags('survey')
|
||||
@Controller('/api/survey')
|
||||
@ -47,6 +48,7 @@ export class SurveyController {
|
||||
private readonly logger: XiaojuSurveyLogger,
|
||||
private readonly counterService: CounterService,
|
||||
private readonly sessionService: SessionService,
|
||||
private readonly userService: UserService,
|
||||
) {}
|
||||
|
||||
@Get('/getBannerData')
|
||||
@ -146,12 +148,22 @@ export class SurveyController {
|
||||
if (latestEditingOne && latestEditingOne._id.toString() !== sessionId) {
|
||||
const curSession = await this.sessionService.findOne(sessionId);
|
||||
if (curSession.createDate <= latestEditingOne.updateDate) {
|
||||
// 在当前用户打开之后,有人保存过了
|
||||
throw new HttpException(
|
||||
'当前问卷已在其它页面开启编辑',
|
||||
EXCEPTION_CODE.SURVEY_SAVE_CONFLICT,
|
||||
// 在当前用户打开之后,被其他页面保存过了
|
||||
const isSameOperator =
|
||||
latestEditingOne.userId === req.user._id.toString();
|
||||
let preOperator;
|
||||
if (!isSameOperator) {
|
||||
preOperator = await this.userService.getUserById(
|
||||
latestEditingOne.userId,
|
||||
);
|
||||
}
|
||||
return {
|
||||
code: EXCEPTION_CODE.SURVEY_SAVE_CONFLICT,
|
||||
errmsg: isSameOperator
|
||||
? '当前问卷已在其它页面开启编辑,刷新以获取最新内容'
|
||||
: `当前问卷已由 ${preOperator.username} 编辑,刷新以获取最新内容`,
|
||||
};
|
||||
}
|
||||
}
|
||||
await this.sessionService.updateSessionToEditing({ sessionId, surveyId });
|
||||
|
||||
@ -331,11 +343,6 @@ export class SurveyController {
|
||||
pageId: surveyId,
|
||||
});
|
||||
|
||||
await this.counterService.createCounters({
|
||||
surveyPath: surveyMeta.surveyPath,
|
||||
dataList: surveyConf.code.dataConf.dataList,
|
||||
});
|
||||
|
||||
await this.surveyHistoryService.addHistory({
|
||||
surveyId,
|
||||
schema: surveyConf.code,
|
||||
|
@ -13,6 +13,7 @@ import { load } from 'cheerio';
|
||||
import { get } from 'lodash';
|
||||
import { FileService } from 'src/modules/file/services/file.service';
|
||||
import { XiaojuSurveyLogger } from 'src/logger';
|
||||
import moment from 'moment';
|
||||
|
||||
@Injectable()
|
||||
export class DownloadTaskService {
|
||||
@ -41,6 +42,7 @@ export class DownloadTaskService {
|
||||
operatorId: string;
|
||||
params: any;
|
||||
}) {
|
||||
const filename = `${responseSchema.title}-${params.isDesensitive ? '脱敏' : '原'}回收数据-${moment().format('YYYYMMDDHHmmss')}.xlsx`;
|
||||
const downloadTask = this.downloadTaskRepository.create({
|
||||
surveyId,
|
||||
surveyPath: responseSchema.surveyPath,
|
||||
@ -50,6 +52,7 @@ export class DownloadTaskService {
|
||||
...params,
|
||||
title: responseSchema.title,
|
||||
},
|
||||
filename,
|
||||
});
|
||||
await this.downloadTaskRepository.save(downloadTask);
|
||||
return downloadTask._id.toString();
|
||||
@ -65,7 +68,7 @@ export class DownloadTaskService {
|
||||
pageSize: number;
|
||||
}) {
|
||||
const where = {
|
||||
onwer: ownerId,
|
||||
ownerId,
|
||||
'curStatus.status': {
|
||||
$ne: RECORD_STATUS.REMOVED,
|
||||
},
|
||||
@ -209,16 +212,12 @@ export class DownloadTaskService {
|
||||
{ name: 'sheet1', data: xlsxData, options: {} },
|
||||
]);
|
||||
|
||||
const isDesensitive = taskInfo.params?.isDesensitive;
|
||||
|
||||
const originalname = `${taskInfo.params.title}-${isDesensitive ? '脱敏' : '原'}回收数据.xlsx`;
|
||||
|
||||
const file: Express.Multer.File = {
|
||||
fieldname: 'file',
|
||||
originalname: originalname,
|
||||
originalname: taskInfo.filename,
|
||||
encoding: '7bit',
|
||||
mimetype: 'application/octet-stream',
|
||||
filename: originalname,
|
||||
filename: taskInfo.filename,
|
||||
size: buffer.length,
|
||||
buffer: buffer,
|
||||
stream: null,
|
||||
@ -246,7 +245,6 @@ export class DownloadTaskService {
|
||||
$set: {
|
||||
curStatus,
|
||||
url,
|
||||
filename: originalname,
|
||||
fileKey: key,
|
||||
fileSize: buffer.length,
|
||||
},
|
||||
|
@ -12,9 +12,10 @@ export class SessionService {
|
||||
private readonly sessionRepository: MongoRepository<Session>,
|
||||
) {}
|
||||
|
||||
create({ surveyId }) {
|
||||
create({ surveyId, userId }) {
|
||||
const session = this.sessionRepository.create({
|
||||
surveyId,
|
||||
userId,
|
||||
});
|
||||
return this.sessionRepository.save(session);
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import { Controller, Post, Body, HttpCode } from '@nestjs/common';
|
||||
import { HttpException } from 'src/exceptions/httpException';
|
||||
import { SurveyNotFoundException } from 'src/exceptions/surveyNotFoundException';
|
||||
import { checkSign } from 'src/utils/checkSign';
|
||||
import { cleanRichTextWithMediaTag } from 'src/utils/xss'
|
||||
import { ENCRYPT_TYPE } from 'src/enums/encrypt';
|
||||
import { EXCEPTION_CODE } from 'src/enums/exceptionCode';
|
||||
import { getPushingData } from 'src/utils/messagePushing';
|
||||
@ -23,6 +22,14 @@ import { XiaojuSurveyLogger } from 'src/logger';
|
||||
import { WhitelistType } from 'src/interfaces/survey';
|
||||
import { UserService } from 'src/modules/auth/services/user.service';
|
||||
import { WorkspaceMemberService } from 'src/modules/workspace/services/workspaceMember.service';
|
||||
import { QUESTION_TYPE } from 'src/enums/question';
|
||||
|
||||
const optionQuestionType: Array<string> = [
|
||||
QUESTION_TYPE.RADIO,
|
||||
QUESTION_TYPE.CHECKBOX,
|
||||
QUESTION_TYPE.BINARY_CHOICE,
|
||||
QUESTION_TYPE.VOTE,
|
||||
];
|
||||
|
||||
@ApiTags('surveyResponse')
|
||||
@Controller('/api/surveyResponse')
|
||||
@ -207,6 +214,7 @@ export class SurveyResponseController {
|
||||
const optionTextAndId = dataList
|
||||
.filter((questionItem) => {
|
||||
return (
|
||||
optionQuestionType.includes(questionItem.type) &&
|
||||
Array.isArray(questionItem.options) &&
|
||||
questionItem.options.length > 0 &&
|
||||
decryptedData[questionItem.field]
|
||||
@ -222,20 +230,23 @@ export class SurveyResponseController {
|
||||
return pre;
|
||||
}, {});
|
||||
|
||||
// 使用redis作为锁,校验选项配额
|
||||
const surveyId = responseSchema.pageId;
|
||||
const lockKey = `locks:optionSelectedCount:${surveyId}`;
|
||||
const lock = await this.redisService.lockResource(lockKey, 1000);
|
||||
this.logger.info(`lockKey: ${lockKey}`);
|
||||
try {
|
||||
const successParams = [];
|
||||
for (const field in decryptedData) {
|
||||
const value = decryptedData[field];
|
||||
const values = Array.isArray(value) ? value : [value];
|
||||
if (field in optionTextAndId) {
|
||||
const optionCountData = await this.counterService.get({
|
||||
const optionCountData =
|
||||
(await this.counterService.get({
|
||||
key: field,
|
||||
surveyPath,
|
||||
type: 'option',
|
||||
});
|
||||
})) || {};
|
||||
|
||||
//遍历选项hash值
|
||||
for (const val of values) {
|
||||
@ -243,38 +254,41 @@ export class SurveyResponseController {
|
||||
(opt) => opt['hash'] === val,
|
||||
);
|
||||
const quota = parseInt(option['quota']);
|
||||
if (quota !== 0 && quota <= optionCountData[val]) {
|
||||
const item = dataList.find((item) => item['field'] === field);
|
||||
throw new HttpException(
|
||||
`【${cleanRichTextWithMediaTag(item['title'])}】中的【${cleanRichTextWithMediaTag(option['text'])}】所选人数已达到上限,请重新选择`,
|
||||
EXCEPTION_CODE.RESPONSE_OVER_LIMIT,
|
||||
);
|
||||
if (
|
||||
quota &&
|
||||
optionCountData?.[val] &&
|
||||
quota <= optionCountData[val]
|
||||
) {
|
||||
return {
|
||||
code: EXCEPTION_CODE.RESPONSE_OVER_LIMIT,
|
||||
data: {
|
||||
field,
|
||||
optionHash: option.hash,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (!optionCountData[val]) {
|
||||
optionCountData[val] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const field in decryptedData) {
|
||||
const value = decryptedData[field];
|
||||
const values = Array.isArray(value) ? value : [value];
|
||||
if (field in optionTextAndId) {
|
||||
const optionCountData = await this.counterService.get({
|
||||
key: field,
|
||||
surveyPath,
|
||||
type: 'option',
|
||||
});
|
||||
for (const val of values) {
|
||||
optionCountData[val]++;
|
||||
this.counterService.set({
|
||||
}
|
||||
if (!optionCountData['total']) {
|
||||
optionCountData['total'] = 1;
|
||||
} else {
|
||||
optionCountData['total']++;
|
||||
}
|
||||
successParams.push({
|
||||
key: field,
|
||||
surveyPath,
|
||||
type: 'option',
|
||||
data: optionCountData,
|
||||
});
|
||||
}
|
||||
optionCountData['total']++;
|
||||
}
|
||||
}
|
||||
// 校验通过后统一更新
|
||||
await Promise.all(
|
||||
successParams.map((item) => this.counterService.set(item)),
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error(error.message);
|
||||
throw error;
|
||||
|
@ -65,25 +65,4 @@ export class CounterService {
|
||||
return pre;
|
||||
}, {});
|
||||
}
|
||||
|
||||
async createCounters({ surveyPath, dataList }) {
|
||||
const optionList = dataList.filter((questionItem) => {
|
||||
return (
|
||||
Array.isArray(questionItem.options) && questionItem.options.length > 0
|
||||
);
|
||||
});
|
||||
optionList.forEach((option) => {
|
||||
const data = {};
|
||||
option.options.forEach((option) => {
|
||||
data[option.hash] = 0;
|
||||
});
|
||||
data['total'] = 0;
|
||||
this.set({
|
||||
surveyPath,
|
||||
key: option.field,
|
||||
type: 'option',
|
||||
data: data,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,53 +1,53 @@
|
||||
import xss from 'xss'
|
||||
import xss from 'xss';
|
||||
|
||||
const myxss = new (xss as any).FilterXSS({
|
||||
onIgnoreTagAttr(tag, name, value) {
|
||||
if (name === 'style' || name === 'class') {
|
||||
return `${name}="${value}"`
|
||||
return `${name}="${value}"`;
|
||||
}
|
||||
return undefined
|
||||
return undefined;
|
||||
},
|
||||
onIgnoreTag(tag, html) {
|
||||
// <xxx>过滤为空,否则不过滤为空
|
||||
var re1 = new RegExp('<.+?>', 'g')
|
||||
const re1 = new RegExp('<.+?>', 'g');
|
||||
if (re1.test(html)) {
|
||||
return ''
|
||||
return '';
|
||||
} else {
|
||||
return html
|
||||
return html;
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
export const cleanRichTextWithMediaTag = (text) => {
|
||||
if (!text) {
|
||||
return text === 0 ? 0 : ''
|
||||
return text === 0 ? 0 : '';
|
||||
}
|
||||
const html = transformHtmlTag(text)
|
||||
.replace(/<img([\w\W]+?)\/>/g, '[图片]')
|
||||
.replace(/<video.*\/video>/g, '[视频]')
|
||||
const content = html.replace(/<[^<>]+>/g, '').replace(/ /g, '')
|
||||
.replace(/<video.*\/video>/g, '[视频]');
|
||||
const content = html.replace(/<[^<>]+>/g, '').replace(/ /g, '');
|
||||
|
||||
return content
|
||||
}
|
||||
return content;
|
||||
};
|
||||
|
||||
export function escapeHtml(html) {
|
||||
return html.replace(/</g, '<').replace(/>/g, '>')
|
||||
return html.replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
export const transformHtmlTag = (html) => {
|
||||
if (!html) return ''
|
||||
if (typeof html !== 'string') return 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(/\\\n/g, '\\n');
|
||||
//.replace(/ /g, "")
|
||||
}
|
||||
};
|
||||
|
||||
const filterXSSClone = myxss.process.bind(myxss)
|
||||
const filterXSSClone = myxss.process.bind(myxss);
|
||||
|
||||
export const filterXSS = (html) => filterXSSClone(transformHtmlTag(html))
|
||||
export const filterXSS = (html) => filterXSSClone(transformHtmlTag(html));
|
||||
|
||||
export const escapeFilterXSS = (html) => escapeHtml(filterXSS(html))
|
||||
export const escapeFilterXSS = (html) => escapeHtml(filterXSS(html));
|
||||
|
@ -60,6 +60,7 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import ImagePreview from './ImagePreview.vue'
|
||||
import { cleanRichTextWithMediaTag } from '@/common/xss'
|
||||
|
||||
const props = defineProps({
|
||||
tableData: {
|
||||
@ -78,8 +79,16 @@ const popoverVirtualRef = ref()
|
||||
const popoverContent = ref('')
|
||||
|
||||
const getContent = (content) => {
|
||||
// const content = cleanRichText(value)
|
||||
return content === 0 ? 0 : content || '未知'
|
||||
if (Array.isArray(content)) {
|
||||
return content.map(item => getContent(item)).join(',');
|
||||
}
|
||||
if (content === null || content === undefined) {
|
||||
return ''
|
||||
}
|
||||
if (typeof content !== 'string') {
|
||||
content = content + ''
|
||||
}
|
||||
return cleanRichTextWithMediaTag(content) || '未知'
|
||||
}
|
||||
const setPopoverContent = (content) => {
|
||||
popoverContent.value = content
|
||||
|
@ -32,14 +32,22 @@
|
||||
v-model="downloadDialogVisible"
|
||||
title="导出确认"
|
||||
width="500"
|
||||
style="padding: 40px;"
|
||||
>
|
||||
<el-form :model="downloadForm">
|
||||
<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>
|
||||
<el-radio value="Venue">原回收数据</el-radio>
|
||||
<el-radio :value="false">原回收数据</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<div class="download-tips">
|
||||
<div>注:</div>
|
||||
<div>
|
||||
<p>推荐优先下载脱敏数据,如手机号:1***3。</p>
|
||||
<p>原回收数据可能存在敏感信息,请谨慎下载。</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
@ -163,9 +171,9 @@ const confirmDownload = async () => {
|
||||
const createRes = await createDownloadSurveyResponseTask({ surveyId: route.params.id, isDesensitive: downloadForm.isDesensitive })
|
||||
dataTableState.downloadDialogVisible = false
|
||||
if (createRes.code === 200) {
|
||||
ElMessage.success(`下载文件计算中,可前往“下载中心”查看`)
|
||||
try {
|
||||
const taskInfo = await checkIsTaskFinished(createRes.data.taskId)
|
||||
console.log(taskInfo)
|
||||
if (taskInfo.url) {
|
||||
window.open(taskInfo.url)
|
||||
ElMessage.success("导出成功")
|
||||
@ -217,6 +225,11 @@ const checkIsTaskFinished = (taskId) => {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.download-tips {
|
||||
display: flex;
|
||||
color: #ec4e29;
|
||||
}
|
||||
|
||||
.menus {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ const handleLogout = () => {
|
||||
const activeIndex = ref('2')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style lang="scss" scoped>
|
||||
.question-list-root {
|
||||
height: 100%;
|
||||
background-color: #f6f7f9;
|
||||
@ -94,7 +94,7 @@ const activeIndex = ref('2')
|
||||
}
|
||||
}
|
||||
.table-container {
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%; /* 确保容器宽度为100% */
|
||||
|
@ -19,14 +19,14 @@
|
||||
:prop="field.key"
|
||||
:label="field.title"
|
||||
:width="field.width"
|
||||
class-name="link"
|
||||
:class-name="[field.key]"
|
||||
:formatter="field.formatter"
|
||||
>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200">
|
||||
<template v-slot="{ row }">
|
||||
<el-button size="small" @click="handleDownload(row)"> 下载 </el-button>
|
||||
<el-button type="primary" size="small" @click="openDeleteDialog(row)"> 删除 </el-button>
|
||||
<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>
|
||||
</el-table>
|
||||
@ -35,31 +35,21 @@
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:total="total"
|
||||
:size="pageSize"
|
||||
small
|
||||
:page-size="pageSize"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
<el-dialog v-model="centerDialogVisible" title="" width="500" align-center>
|
||||
<span>确认删除下载记录吗?</span>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="centerDialogVisible = false"> 取消 </el-button>
|
||||
<el-button type="primary" @click="confirmDelete"> 确认 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { get, map } from 'lodash-es'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { deleteDownloadTask, getDownloadTaskList } from '@/management/api/downloadTask'
|
||||
import { CODE_MAP } from '@/management/api/base'
|
||||
import 'element-plus/theme-chalk/src/message.scss'
|
||||
import 'element-plus/theme-chalk/src/message-box.scss'
|
||||
|
||||
import moment from 'moment'
|
||||
// 引入中文
|
||||
@ -68,9 +58,9 @@ import 'moment/locale/zh-cn'
|
||||
moment.locale('zh-cn')
|
||||
|
||||
const loading = ref(false)
|
||||
const pageSize = ref(15)
|
||||
const pageSize = ref(10)
|
||||
const total = ref(0)
|
||||
const dataList = reactive([])
|
||||
const dataList: Array<any> = reactive([])
|
||||
|
||||
onMounted(() => {
|
||||
getList({ pageIndex: 1 })
|
||||
@ -87,7 +77,8 @@ const getList = async ({ pageIndex }: { pageIndex: number }) => {
|
||||
const res: Record<string, any> = await getDownloadTaskList(params)
|
||||
if (res.code === CODE_MAP.SUCCESS) {
|
||||
total.value = res.data.total
|
||||
dataList.values = res.data.list
|
||||
const list = res.data.list as any
|
||||
dataList.splice(0, dataList.length, ...list);
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
@ -99,7 +90,6 @@ const statusTextMap: Record<string, string> = {
|
||||
removed: '已删除',
|
||||
};
|
||||
|
||||
const centerDialogVisible = ref(false)
|
||||
let currentDelRow: Record<string, any> = {}
|
||||
// 下载文件
|
||||
const handleDownload = async (row: any) => {
|
||||
@ -112,20 +102,33 @@ const handleDownload = async (row: any) => {
|
||||
}
|
||||
}
|
||||
// 删除文件
|
||||
const openDeleteDialog = (row: any) => {
|
||||
centerDialogVisible.value = true
|
||||
const openDeleteDialog = async (row: any) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('是否确认删除?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
currentDelRow = row
|
||||
confirmDelete()
|
||||
} catch (error) {
|
||||
console.log('取消删除')
|
||||
}
|
||||
}
|
||||
|
||||
// 确认删除文件
|
||||
const confirmDelete = async () => {
|
||||
try {
|
||||
await deleteDownloadTask(currentDelRow.taskId)
|
||||
const res: Record<string, any> = await deleteDownloadTask(currentDelRow.taskId)
|
||||
if (res.code !== CODE_MAP.SUCCESS) {
|
||||
ElMessage.error(res.errmsg)
|
||||
} else {
|
||||
ElMessage.success('删除成功');
|
||||
await getList({ pageIndex: 1 })
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error("删除失败,请刷新重试")
|
||||
}
|
||||
centerDialogVisible.value = false
|
||||
}
|
||||
|
||||
const fields = ['filename', 'fileSize', 'createDate', 'curStatus']
|
||||
@ -177,13 +180,30 @@ const handleCurrentChange = (val: number) => {
|
||||
background-color: #f6f7f9;
|
||||
|
||||
.list-wrapper {
|
||||
width: 90%;
|
||||
min-width: 1080px;
|
||||
padding: 10px 20px;
|
||||
background: #fff;
|
||||
margin: 0 auto;
|
||||
|
||||
.list-table {
|
||||
.cell {
|
||||
text-align: center;
|
||||
}
|
||||
.text-btn {
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-left: 20px;
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
.download-btn {
|
||||
color: $primary-color;
|
||||
}
|
||||
.delete-btn {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
.small-text {
|
||||
color: red;
|
||||
|
@ -7,11 +7,12 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { useEditStore } from '@/management/stores/edit'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage, } from 'element-plus'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import 'element-plus/theme-chalk/src/message.scss'
|
||||
import { publishSurvey, saveSurvey } from '@/management/api/survey'
|
||||
import { publishSurvey, saveSurvey, seizeSession } from '@/management/api/survey'
|
||||
import buildData from './buildData'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { CODE_MAP } from '@/management/api/base'
|
||||
|
||||
interface Props {
|
||||
updateLogicConf: any
|
||||
@ -27,6 +28,16 @@ const { schema, sessionId } = storeToRefs(editStore)
|
||||
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,8 +72,33 @@ const onSave = async () => {
|
||||
return null
|
||||
}
|
||||
|
||||
const res: Record<string, any> = await saveSurvey(saveData.value)
|
||||
try {
|
||||
const res: any = await saveSurvey(saveData.value)
|
||||
if(!res) {
|
||||
return null
|
||||
}
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('保存成功')
|
||||
return res
|
||||
} else if (res.code === 3006) {
|
||||
ElMessageBox.alert(res.errmsg, '提示', {
|
||||
confirmButtonText: '刷新同步',
|
||||
callback: (action: string) => {
|
||||
if (action === 'confirm') {
|
||||
seize();
|
||||
}
|
||||
}
|
||||
});
|
||||
return null
|
||||
} else {
|
||||
ElMessage.error(res.errmsg)
|
||||
return null
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('保存问卷失败')
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
const handlePublish = async () => {
|
||||
if (isPublishing.value) {
|
||||
@ -81,11 +117,7 @@ const handlePublish = async () => {
|
||||
|
||||
try {
|
||||
const saveRes: any = await onSave()
|
||||
if (!saveRes) {
|
||||
return
|
||||
}
|
||||
if(saveRes && saveRes?.code !== 200) {
|
||||
ElMessage.error(`保存失败 ${saveRes.errmsg}`)
|
||||
if (!saveRes || saveRes.code !== CODE_MAP.SUCCESS) {
|
||||
return
|
||||
}
|
||||
const publishRes: any = await publishSurvey({ surveyId: saveData.value.surveyId })
|
||||
|
@ -154,11 +154,13 @@ const handleSave = async () => {
|
||||
ElMessage.success('保存成功')
|
||||
return res
|
||||
} else if (res.code === 3006) {
|
||||
ElMessageBox.alert('当前问卷已在其它页面开启编辑,点击“抢占”以获取保存权限。', '提示', {
|
||||
confirmButtonText: '抢占',
|
||||
callback: () => {
|
||||
ElMessageBox.alert(res.errmsg, '提示', {
|
||||
confirmButtonText: '刷新同步',
|
||||
callback: (action: string) => {
|
||||
if (action === 'confirm') {
|
||||
seize();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ElMessage.error(res.errmsg)
|
||||
|
@ -187,7 +187,7 @@ const handleLogout = () => {
|
||||
}
|
||||
// 下载页面
|
||||
const handleDownload = () => {
|
||||
router.push('/survey/downloadTask/')
|
||||
router.push({ name: 'download' })
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -9,6 +9,9 @@ import { SurveyPermissions } from '@/management/utils/types/workSpace'
|
||||
import { analysisTypeMap } from '@/management/config/analysisConfig'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import 'element-plus/theme-chalk/src/message.scss'
|
||||
import 'element-plus/theme-chalk/src/message-box.scss'
|
||||
import 'element-plus/theme-chalk/src/button.scss'
|
||||
import 'element-plus/theme-chalk/src/overlay.scss'
|
||||
import { useUserStore } from '@/management/stores/user'
|
||||
import { useEditStore } from '@/management/stores/edit'
|
||||
|
||||
@ -27,7 +30,7 @@ const routes: RouteRecordRaw[] = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/survey/downloadTask/',
|
||||
path: '/download',
|
||||
name: 'download',
|
||||
component: () => import('../pages/downloadTask/TaskList.vue'),
|
||||
meta: {
|
||||
|
Loading…
Reference in New Issue
Block a user