| src/views/Satisfaction/configurationmyd/batch.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/Satisfaction/configurationmyd/components/DetailsAnomaly.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/Satisfaction/configurationmyd/dispose.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/Satisfaction/configurationmyd/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/views/Satisfaction/configurationmyd/batch.vue
@@ -59,7 +59,7 @@ <el-option label="已处理" :value="'1'" /> </el-select> </el-form-item> <el-form-item label="模板类型"> <el-form-item label="满意度类型"> <el-select v-model="filterParams.templateType" placeholder="请选择模板类型" @@ -139,23 +139,23 @@ <div class="patient-row"> <div class="patient-item"> <span class="label">姓名:</span> <span class="value">{{ row.patientName }}</span> <span class="value">{{ row.patdescJson.sendname }}</span> </div> <div class="patient-item"> <span class="label">性别:</span> <span class="value">{{ row.gender === 1 ? "男" : "女" row.patdescJson.sex }}</span> </div> <div class="patient-item"> <span class="label">年龄:</span> <span class="value">{{ row.age }}岁</span> <span class="value">{{ row.patdescJson.age }}岁</span> </div> </div> <div class="patient-row"> <div class="patient-item full-width"> <span class="label">电话:</span> <span class="value">{{ row.phone }}</span> <span class="value">{{ row.patdescJson.phone }}</span> </div> </div> </div> @@ -484,6 +484,7 @@ <script> import DetailsAnomaly from "./components/DetailsAnomaly.vue"; import { tracelist, traceedit } from "@/api/AiCentre/index"; import dayjs from "dayjs"; import { deptTreeSelect } from "@/api/system/user"; export default { @@ -512,8 +513,8 @@ filterParams: { todeptcode: "", handleFlag: "", templateType: "", scriptid: null, templateType: null, scriptids: null, pageNum: 1, pageSize: 10, }, @@ -598,13 +599,18 @@ created() { // 从路由参数获取问题ID if (this.$route.query.questionId) { this.filterParams.scriptid = this.$route.query.questionId || null; this.filterParams.scriptids = null; } else if (this.$route.query.questionIds) { this.filterParams.scriptids = this.$route.query.questionIds; this.filterParams.scriptid=null; } this.filterParams.scriptids = this.$route.query.questionId || this.$route.query.questionIds||null; // if (this.$route.query.questionId) { // } else if (this.$route.query.questionIds) { // console.log( // this.$route.query.questionIds, // "this.$route.query.questionIds" // ); this.filterParams.templateType = Number(this.$route.query.type)||null; // this.filterParams.scriptid = null; // } this.hasQualityPermission = this.checkQualityPermission(); }, @@ -745,8 +751,11 @@ params.templateType = this.filterParams.templateType; } if (this.filterParams.scriptid) { params.scriptid = this.filterParams.scriptid; // if (this.filterParams.scriptid) { // params.scriptid = this.filterParams.scriptid; // } if (this.filterParams.scriptids) { params.scriptids = this.filterParams.scriptids.split(","); } return params; @@ -789,7 +798,7 @@ todeptcode: "", handleFlag: "", templateType: "", scriptid: null, // 保留问题ID scriptids: null, // 保留问题ID pageNum: 1, pageSize: 10, }; @@ -833,7 +842,7 @@ // 生成弹框标题 let title = "异常反馈详情"; if (row.patdesc) { const patientName = row.patdesc.split("|")[0]; const patientName = row.patdescJson.sendname; if (patientName) { title = `${patientName} - ${title}`; } @@ -888,6 +897,8 @@ handleresult: this.processForm.handleresult, handledesc: this.processForm.handledesc, finaloption: this.processForm.finaloption, handleBy: this.$store.state.user.nickName, handleTime: dayjs().format("YYYY-MM-DD HH:mm:ss"), // 将数组转换为逗号分隔的字符串 ccdepts: Array.isArray(this.processForm.ccdepts) ? this.processForm.ccdepts.join(",") @@ -1141,7 +1152,7 @@ .patient-item { flex: 1; display: flex; justify-content: space-between; justify-content: flex-start; align-items: center; padding: 0 5px; @@ -1175,7 +1186,7 @@ .info-item { display: flex; justify-content: space-between; justify-content: flex-start; align-items: center; margin-bottom: 5px; padding: 2px 0; @@ -1188,7 +1199,7 @@ .value { color: #333; font-weight: 500; text-align: right; // text-align: right; flex: 1; &.time { src/views/Satisfaction/configurationmyd/components/DetailsAnomaly.vue
@@ -73,12 +73,6 @@ <span class="value">{{ currentRecord.handleBy || '未处理' }}</span> </div> </el-col> <el-col :span="24" v-if="currentRecord.patdesc"> <div class="info-item"> <span class="label">患者信息:</span> <span class="value">{{ currentRecord.patdesc }}</span> </div> </el-col> <el-col :span="8" v-if="currentRecord.handleresult"> <div class="info-item"> <span class="label">处理结果:</span> src/views/Satisfaction/configurationmyd/dispose.vue
@@ -21,7 +21,7 @@ > <el-form-item label="满意度类型" prop="templateid"> <el-select v-model="queryParams.templateid" v-model="queryParams.templateType" placeholder="请选择模板" clearable style="width: 200px" @@ -48,7 +48,7 @@ <el-option v-for="dept in deptList" :key="dept.deptCode" :label="dept.deptName" :label="dept.label" :value="dept.deptCode" /> </el-select> @@ -96,11 +96,7 @@ > 批量处理 ({{ selectedIds.length }}) </el-button> <el-button type="info" icon="el-icon-download" @click="handleExport" > <el-button type="info" icon="el-icon-download" @click="handleExport"> 导出异常数据 </el-button> <el-button @@ -120,12 +116,14 @@ <el-col :span="8"> <el-card shadow="never" class="stat-card"> <div class="stat-content"> <div class="stat-icon" style="background: #f0f9ff;"> <i class="el-icon-s-claim" style="color: #5788FE;"></i> <div class="stat-icon" style="background: #f0f9ff"> <i class="el-icon-s-claim" style="color: #5788fe"></i> </div> <div class="stat-info"> <div class="stat-title">总异常数量</div> <div class="stat-value">{{ overviewData.totalExceptionCount }}</div> <div class="stat-value"> {{ overviewData.totalExceptionCount }} </div> </div> </div> </el-card> @@ -133,8 +131,8 @@ <el-col :span="8"> <el-card shadow="never" class="stat-card"> <div class="stat-content"> <div class="stat-icon" style="background: #f0f9ff;"> <i class="el-icon-s-flag" style="color: #E6A23C;"></i> <div class="stat-icon" style="background: #f0f9ff"> <i class="el-icon-s-flag" style="color: #e6a23c"></i> </div> <div class="stat-info"> <div class="stat-title">待处理异常</div> @@ -146,8 +144,8 @@ <el-col :span="8"> <el-card shadow="never" class="stat-card"> <div class="stat-content"> <div class="stat-icon" style="background: #f0f9ff;"> <i class="el-icon-check" style="color: #67C23A;"></i> <div class="stat-icon" style="background: #f0f9ff"> <i class="el-icon-check" style="color: #67c23a"></i> </div> <div class="stat-info"> <div class="stat-title">已处理异常</div> @@ -170,11 +168,7 @@ @selection-change="handleSelectionChange" class="exception-table" > <el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" /> <el-table-column label="序号" @@ -197,7 +191,7 @@ size="mini" :type="getTemplateTypeTag(row.templateType)" > {{ row.templateType === 1 ? '语音模板' : '问卷模板' }} {{ row.templateType === 1 ? "语音模板" : "问卷模板" }} </el-tag> </div> </div> @@ -225,43 +219,45 @@ </template> </el-table-column> <el-table-column label="填写情况" width="200" align="center" > <el-table-column label="填写情况" width="200" align="center"> <template slot-scope="{ row }"> <div class="fill-statistics"> <div class="stat-item"> <span class="stat-label">有效填写:</span> <span class="stat-value">{{ row.fillSituation.effectiveFillNum }}</span> <span class="stat-value">{{ row.fillSituation.effectiveFillNum }}</span> </div> <div class="stat-item"> <span class="stat-label">异常填写:</span> <span class="stat-value exception-count">{{ row.fillSituation.exceptionFillNum }}</span> <span class="stat-value exception-count">{{ row.fillSituation.exceptionFillNum }}</span> </div> </div> </template> </el-table-column> <el-table-column label="异常任务" width="280" align="center" > <el-table-column label="异常任务" width="280" align="center"> <template slot-scope="{ row }"> <div class="exception-tasks"> <div class="task-category"> <div class="task-title">已处理</div> <div class="task-count processed">{{ row.exceptionQuesNum.yesDeal }}</div> <div class="task-count processed"> {{ row.exceptionQuesNum.yesDeal }} </div> </div> <div class="task-category"> <div class="task-title">待处理</div> <div class="task-count pending">{{ row.exceptionQuesNum.noDeal }}</div> <div class="task-count pending"> {{ row.exceptionQuesNum.noDeal }} </div> </div> <div class="task-category"> <div class="task-title">异常总数</div> <div class="task-count total">{{ row.exceptionQuesNum.all }}</div> <div class="task-count total"> {{ row.exceptionQuesNum.all }} </div> </div> </div> </template> @@ -275,8 +271,10 @@ > <template slot-scope="{ row }"> <div v-if="row.handleTime" class="last-process"> <div class="process-time">{{ formatDateTime(row.handleTime) }}</div> <div class="process-user">{{ row.handleBy || '系统处理' }}</div> <div class="process-time"> {{ formatDateTime(row.handleTime) }} </div> <div class="process-user">{{ row.handleBy || "系统处理" }}</div> </div> <span v-else class="no-process">暂无处理记录</span> </template> @@ -321,21 +319,22 @@ <script> import { tracedeallist } from "@/api/AiCentre/index"; import { deptTreeSelect } from "@/api/system/user"; export default { name: 'ExceptionList', name: "ExceptionList", data() { return { // 查询参数 queryParams: { todeptcode: [], // 处理科室编号数组 todeptname: '', // 处理科室名称 templateid: '', // 任务模板ID handleStartTime: '', // 处理开始时间 handleEndTime: '', // 处理结束时间 todeptname: "", // 处理科室名称 templateType: 2, // 任务模板ID handleStartTime: "", // 处理开始时间 handleEndTime: "", // 处理结束时间 handleTimeRange: [], // 时间范围,用于界面展示 pageNum: 1, pageSize: 10 pageSize: 10, }, // 加载状态 @@ -346,27 +345,13 @@ // 模板列表 templateList: [ { id: 1, name: '语音模板' }, { id: 2, name: '问卷模板' } { id: 1, name: "语音满意度" }, { id: 2, name: "问卷满意度" }, // 你可以根据实际情况从接口获取模板列表 ], // 科室列表 deptList: [ // 你可以从接口获取科室列表,这里先用静态数据 { deptCode: '001', deptName: '心血管内科' }, { deptCode: '002', deptName: '神经内科' }, { deptCode: '003', deptName: '普外科' }, { deptCode: '004', deptName: '骨科' }, { deptCode: '005', deptName: '妇产科' }, { deptCode: '006', deptName: '儿科' }, { deptCode: '007', deptName: '急诊科' }, { deptCode: '008', deptName: '呼吸内科' }, { deptCode: '009', deptName: '消化内科' }, { deptCode: '010', deptName: '内分泌科' }, { deptCode: '011', deptName: '肾内科' }, { deptCode: '012', deptName: '肿瘤科' } ], deptList: [], // 异常列表数据 exceptionList: [], @@ -377,100 +362,134 @@ totalExceptionCount: 0, pendingCount: 0, processedCount: 0, todayProcessedCount: 0 todayProcessedCount: 0, }, // 日期选择器选项 pickerOptions: { shortcuts: [ { text: '最近一周', text: "最近一周", onClick(picker) { const end = new Date(); const start = new Date(); start.setTime(start.getTime() - 3600 * 1000 * 24 * 7); picker.$emit('pick', [start, end]); } picker.$emit("pick", [start, end]); }, }, { text: '最近一个月', text: "最近一个月", onClick(picker) { const end = new Date(); const start = new Date(); start.setTime(start.getTime() - 3600 * 1000 * 24 * 30); picker.$emit('pick', [start, end]); } picker.$emit("pick", [start, end]); }, }, { text: '最近三个月', text: "最近三个月", onClick(picker) { const end = new Date(); const start = new Date(); start.setTime(start.getTime() - 3600 * 1000 * 24 * 90); picker.$emit('pick', [start, end]); } } picker.$emit("pick", [start, end]); }, }, ], disabledDate(time) { return time.getTime() > Date.now(); } } }, }, }; }, mounted() { this.loadData(); this.getDeptOptions(); }, methods: { // 格式化日期时间 formatDateTime(dateTime) { if (!dateTime) return ''; if (!dateTime) return ""; const date = new Date(dateTime); return date.toLocaleDateString().replace(/\//g, '-') + ' ' + date.toTimeString().split(' ')[0]; return ( date.toLocaleDateString().replace(/\//g, "-") + " " + date.toTimeString().split(" ")[0] ); }, // 获取模板类型标签样式 getTemplateTypeTag(type) { return type === 1 ? 'primary' : 'success'; return type === 1 ? "primary" : "success"; }, // 构建查询参数 buildQueryParams() { const params = { pageNum: this.queryParams.pageNum, pageSize: this.queryParams.pageSize pageSize: this.queryParams.pageSize, }; // 处理科室编号 if (this.queryParams.todeptcode && this.queryParams.todeptcode.length > 0) { if ( this.queryParams.todeptcode && this.queryParams.todeptcode.length > 0 ) { // 接口可能需要字符串格式的科室编号,根据实际情况调整 params.todeptcode = this.queryParams.todeptcode.join(','); params.todeptcode = this.queryParams.todeptcode.join(","); } // 模板ID if (this.queryParams.templateid) { params.templateid = this.queryParams.templateid; if (this.queryParams.templateType) { params.templateType = this.queryParams.templateType; } // 处理时间范围 if (this.queryParams.handleTimeRange && this.queryParams.handleTimeRange.length === 2) { if ( this.queryParams.handleTimeRange && this.queryParams.handleTimeRange.length === 2 ) { params.handleStartTime = this.queryParams.handleTimeRange[0]; params.handleEndTime = this.queryParams.handleTimeRange[1]; } return params; }, /** 查询科室列表 */ getDeptOptions() { deptTreeSelect() .then((res) => { if (res.code == 200) { this.deptList = this.flattenArray(res.data) || []; } }) .catch((error) => { console.error("获取科室列表失败:", error); this.$message.error("获取科室列表失败"); }); }, flattenArray(multiArray) { let result = []; function flatten(element) { if (element.children && element.children.length > 0) { element.children.forEach((child) => flatten(child)); } else { let item = JSON.parse(JSON.stringify(element)); result.push(item); } } multiArray.forEach((element) => flatten(element)); return result; }, // 加载数据 async loadData() { this.loading = true; try { await Promise.all([ this.loadExceptionList(), this.loadOverviewData() ]); await Promise.all([this.loadExceptionList(), this.loadOverviewData()]); } finally { this.loading = false; } @@ -482,19 +501,19 @@ const params = this.buildQueryParams(); const response = await tracedeallist(params); if (response.code==200) { if (response.code == 200) { this.exceptionList = response.rows.detailTraceDealDTOList || []; this.overviewData.totalExceptionCount=response.rows.totalException this.overviewData.pendingCount=response.rows.noDealException this.overviewData.processedCount=response.rows.yesDealException this.overviewData.totalExceptionCount = response.rows.totalException; this.overviewData.pendingCount = response.rows.noDealException; this.overviewData.processedCount = response.rows.yesDealException; this.total = response.total || 0; } else { this.exceptionList = []; this.total = 0; } } catch (error) { console.error('加载异常列表失败:', error); this.$message.error('加载异常列表失败,请稍后重试'); console.error("加载异常列表失败:", error); this.$message.error("加载异常列表失败,请稍后重试"); this.exceptionList = []; this.total = 0; } @@ -504,18 +523,26 @@ async loadOverviewData() { try { // 从接口数据计算统计数据 const totalExceptionCount = this.exceptionList.reduce((sum, item) => sum + (item.exceptionQuesNum?.all || 0), 0); const pendingCount = this.exceptionList.reduce((sum, item) => sum + (item.exceptionQuesNum?.noDeal || 0), 0); const processedCount = this.exceptionList.reduce((sum, item) => sum + (item.exceptionQuesNum?.yesDeal || 0), 0); const totalExceptionCount = this.exceptionList.reduce( (sum, item) => sum + (item.exceptionQuesNum?.all || 0), 0 ); const pendingCount = this.exceptionList.reduce( (sum, item) => sum + (item.exceptionQuesNum?.noDeal || 0), 0 ); const processedCount = this.exceptionList.reduce( (sum, item) => sum + (item.exceptionQuesNum?.yesDeal || 0), 0 ); // 计算今日处理数(这里可以根据实际需求调整逻辑) const today = new Date().toISOString().split('T')[0]; const todayProcessedCount = this.exceptionList.filter(item => { const today = new Date().toISOString().split("T")[0]; const todayProcessedCount = this.exceptionList.filter((item) => { if (!item.handleTime) return false; const handleDate = new Date(item.handleTime).toISOString().split('T')[0]; const handleDate = new Date(item.handleTime) .toISOString() .split("T")[0]; return handleDate === today; }).length; @@ -523,15 +550,15 @@ totalExceptionCount, pendingCount, processedCount, todayProcessedCount todayProcessedCount, }; } catch (error) { console.error('加载概览数据失败:', error); console.error("加载概览数据失败:", error); this.overviewData = { totalExceptionCount: 0, pendingCount: 0, processedCount: 0, todayProcessedCount: 0 todayProcessedCount: 0, }; } }, @@ -554,43 +581,44 @@ // 处理批量处理 handleBatchProcess() { if (this.selectedIds.length === 0) { this.$message.warning('请先选择要处理的异常题目'); this.$message.warning("请先选择要处理的异常题目"); return; } // 跳转到批量处理页面 this.$router.push({ path: '/Intelligentcenter/batch', path: "/Intelligentcenter/batch", query: { questionIds: this.selectedIds.join(',') } questionIds: this.selectedIds.join(","), type: this.queryParams.templateType, }, }); }, // 处理导出 handleExport() { this.$message.success('导出功能开发中...'); this.$message.success("导出功能开发中..."); }, // 刷新数据 refreshData() { this.loadData(); this.$message.success('数据已刷新'); this.$message.success("数据已刷新"); }, // 处理选择变化 handleSelectionChange(selection) { this.selectedIds = selection.map(item => item.scriptid); this.selectedIds = selection.map((item) => item.scriptid); }, // 处理单个题目批量处理 handleBatchQuestion(row) { this.$router.push({ path: '/Intelligentcenter/batch', path: "/Intelligentcenter/batch", query: { questionId: row.scriptid, questionText: encodeURIComponent(row.questiontext) } type: this.queryParams.templateType, }, }); }, @@ -605,8 +633,8 @@ handlePageChange(page) { this.queryParams.pageNum = page; this.loadExceptionList(); } } }, }, }; </script> @@ -619,7 +647,7 @@ .page-header { margin-bottom: 20px; padding: 20px; background: linear-gradient(135deg, #5788FE 0%, #66b1ff 100%); background: linear-gradient(135deg, #5788fe 0%, #66b1ff 100%); border-radius: 8px; color: white; @@ -803,7 +831,7 @@ } &.total { color: #5788FE; color: #5788fe; } } } @@ -818,7 +846,7 @@ .process-user { font-size: 13px; color: #5788FE; color: #5788fe; font-weight: 500; } } src/views/Satisfaction/configurationmyd/index.vue
@@ -481,89 +481,95 @@ </div> </div> </div> <!-- 选项配置对话框 --> <el-dialog title="选项异常状态配置" :visible.sync="optionDialogVisible" width="700px" center :close-on-click-modal="false" > <div v-if="editingQuestion" class="option-config-wrapper"> <div class="dialog-header"> <h4>{{ editingQuestion.scriptTopic || '无主题' }}</h4> <p class="dialog-subtitle">{{ editingQuestion.scriptContent }}</p> </div> <!-- 选项配置对话框 --> <el-dialog title="选项异常状态配置" :visible.sync="optionDialogVisible" width="700px" center :close-on-click-modal="false" > <div v-if="editingQuestion" class="option-config-wrapper"> <div class="dialog-header"> <h4>{{ editingQuestion.scriptTopic || "无主题" }}</h4> <p class="dialog-subtitle">{{ editingQuestion.scriptContent }}</p> </div> <div class="option-list"> <el-alert v-if="!currentOptions.some(opt => opt.isabnormal === 1)" title="请至少设置一个异常选项(标记为异常)" type="warning" :closable="false" show-icon style="margin-bottom: 20px;" /> <div class="option-list"> <el-alert v-if="!currentOptions.some((opt) => opt.isabnormal === 1)" title="请至少设置一个异常选项(标记为异常)" type="warning" :closable="false" show-icon style="margin-bottom: 20px" /> <div v-for="(option, index) in currentOptions" :key="index" class="option-item"> <el-form :model="option" :rules="optionRules" ref="optionForm" size="small" class="option-form" > <el-row :gutter="12" align="middle"> <el-col :span="2"> <div class="option-index">#{{ index + 1 }}</div> </el-col> <div v-for="(option, index) in currentOptions" :key="index" class="option-item" > <el-form :model="option" :rules="optionRules" ref="optionForm" size="small" class="option-form" > <el-row :gutter="12" align="middle"> <el-col :span="2"> <div class="option-index">#{{ index + 1 }}</div> </el-col> <el-col :span="12"> <el-form-item prop="targetvalue"> <el-input v-model="option.targetvalue" placeholder="请输入选项内容" clearable maxlength="200" show-word-limit /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item prop="targetvalue"> <el-input v-model="option.targetvalue" placeholder="请输入选项内容" clearable maxlength="200" show-word-limit /> </el-form-item> </el-col> <el-col :span="6"> <el-form-item prop="isabnormal"> <el-select v-model="option.isabnormal" placeholder="选择状态" style="width: 100%" > <el-option v-for="status in abnormalOptions" :key="status.value" :label="status.label" :value="status.value" > <el-tag :type="status.type" size="small">{{ status.label }}</el-tag> </el-option> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item prop="isabnormal"> <el-select v-model="option.isabnormal" placeholder="选择状态" style="width: 100%" > <el-option v-for="status in abnormalOptions" :key="status.value" :label="status.label" :value="status.value" > <el-tag :type="status.type" size="small">{{ status.label }}</el-tag> </el-option> </el-select> </el-form-item> </el-col> <el-col :span="4"> <el-button type="danger" icon="el-icon-delete" @click="removeOption(index)" size="small" circle plain /> </el-col> </el-row> </el-form> </div> <el-col :span="4"> <el-button type="danger" icon="el-icon-delete" @click="removeOption(index)" size="small" circle plain /> </el-col> </el-row> </el-form> </div> <!-- <el-button <!-- <el-button type="primary" icon="el-icon-plus" @click="addNewOption" @@ -573,16 +579,16 @@ > 添加选项 </el-button> --> </div> </div> </div> </div> <span slot="footer" class="dialog-footer"> <el-button @click="optionDialogVisible = false">取消</el-button> <el-button type="primary" @click="saveOptions" :loading="savingOptions"> 保存配置 </el-button> </span> </el-dialog> <span slot="footer" class="dialog-footer"> <el-button @click="optionDialogVisible = false">取消</el-button> <el-button type="primary" @click="saveOptions" :loading="savingOptions"> 保存配置 </el-button> </span> </el-dialog> <!-- 题目预览对话框 --> <el-dialog title="题目预览" @@ -848,30 +854,12 @@ this.voiceCategories.includes(q.scriptAssortid) ).length; } return 0; }, // 检查题目是否有异常选项 hasAbnormalOption(question) { return (question) => { if (!question) return false; // 问卷模板 if (this.templateForm.templateType === 1) { const options = question.svyLibTemplateTargetoptions || []; return options.some((opt) => opt.isabnormal === 1); } // 语音模板 else if (this.templateForm.templateType === 2) { const options = question.ivrLibaScriptTargetoptionList || []; return options.some((opt) => opt.isabnormal === 1); } return false; }; }, // 筛选后的题目列表 filteredQuestionList() { let filtered = this.questionList; console.log(this.questionnaireCategorys); // 筛选满意度题目 if (this.templateForm.templateType === 1) { @@ -888,7 +876,7 @@ if (this.queryParams.scriptTopic) { const keyword = this.queryParams.scriptTopic.toLowerCase(); filtered = filtered.filter( (q) => q.scriptTopic && q.scriptTopic.toLowerCase().includes(keyword) (q) => q.scriptTopic && q.criptTopic.toLowerCase().includes(keyword) ); } @@ -1212,9 +1200,9 @@ /** 配置变更处理 */ handleConfigChange(question) { this.$nextTick(() => { const index = this.filteredQuestionList.findIndex( (q) => q.id === question.id ); const index = this.filteredQuestionList.findIndex((q) => q.id === question.id); console.log(index,'index'); if (index !== -1) { const formRef = this.$refs.configForm && this.$refs.configForm[index]; if (formRef) { @@ -1261,24 +1249,61 @@ const changedItems = this.questionList.filter((q) => q.hasChanges); this.changedCount = changedItems.length; this.hasChanges = changedItems.length > 0; // 强制更新视图 this.$forceUpdate(); }, /** 检查题目是否有异常选项 */ checkHasAbnormalOptions(question) { if (this.templateForm.templateType === 1) { return (question.svyLibTemplateTargetoptions || []).some( (opt) => opt.isabnormal === 1 ); } else if (this.templateForm.templateType === 2) { return (question.ivrLibaScriptTargetoptionList || []).some( (opt) => opt.isabnormal === 1 ); } return false; }, /** 保存单个题目配置 */ async saveSingleConfig(question) { if (!question.hasChanges) return; async saveSingleConfig(question, skipAbnormalCheck = false) { // 检查是否有变更 if (!question.hasChanges && !skipAbnormalCheck) { this.$message.info("当前配置无变化"); return; } const index = this.filteredQuestionList.findIndex( (q) => q.id === question.id ); console.log(index, "filteredQuestionList"); // 检查是否有异常选项 if (!skipAbnormalCheck && !this.checkHasAbnormalOptions(question)) { this.$confirm( "该题目没有设置异常选项,必须先配置异常选项才能保存。是否立即配置?", "提示", { confirmButtonText: "去配置", cancelButtonText: "取消", type: "warning", } ) .then(() => { this.openOptionDialog(question); }) .catch(() => {}); return; } const index = this.questionList.findIndex((q) => q.id === question.id); if (index === -1) return; const formRef = this.$refs.configForm && this.$refs.configForm[index]; if (!formRef) return; const valid = await formRef.validate(); if (!valid) { // 验证表单 try { await formRef.validate(); } catch (error) { this.$message.warning("请先完成必填项"); return; } @@ -1339,6 +1364,18 @@ reportDeptName: reportDeptNames.join(","), }; // 如果需要,也更新选项数据 if (question.hasChanges && this.templateForm.templateType === 1) { questions[questionIndex].svyLibTemplateTargetoptions = question.svyLibTemplateTargetoptions || []; } else if ( question.hasChanges && this.templateForm.templateType === 2 ) { questions[questionIndex].ivrLibaScriptTargetoptionList = question.ivrLibaScriptTargetoptionList || []; } // 更新模板 updatedTemplateDetail[questionsField] = questions; @@ -1375,7 +1412,6 @@ } }, /** 处理保存成功 */ /** 处理保存成功 */ handleSaveSuccess(question) { // 同时更新题目顶层字段 @@ -1450,6 +1486,36 @@ async handleBatchSave() { if (!this.hasChanges || this.batchSaving) return; // 获取有变更的题目 const changedQuestions = this.questionList.filter((q) => q.hasChanges); if (changedQuestions.length === 0) { this.$message.info("没有需要保存的配置变更"); return; } // 检查是否有题目缺少异常选项 const questionsWithoutAbnormal = changedQuestions.filter( (q) => !this.checkHasAbnormalOptions(q) ); if (questionsWithoutAbnormal.length > 0) { this.$confirm( `有 ${questionsWithoutAbnormal.length} 个题目没有设置异常选项,必须配置异常选项后才能保存。是否先去配置?`, "提示", { confirmButtonText: "去配置", cancelButtonText: "取消", type: "warning", } ) .then(() => { // 打开第一个没有异常选项的题目的配置对话框 this.openOptionDialog(questionsWithoutAbnormal[0]); }) .catch(() => {}); return; } this.$confirm("确定要保存所有修改过的配置吗?", "批量保存", { confirmButtonText: "确定", cancelButtonText: "取消", @@ -1458,24 +1524,20 @@ .then(async () => { this.batchSaving = true; const changedQuestions = this.questionList.filter( (q) => q.hasChanges ); const results = []; for (const question of changedQuestions) { try { await this.saveSingleConfig(question); // 跳过异常检查,因为在上面已经检查过了 await this.saveSingleConfig(question, true); results.push({ id: question.id, success: !question.hasChanges && question.saveStatus?.type === "success", success: !question.hasChanges, }); } catch (error) { results.push({ id: question.id, success: false, error: error.message, }); } } @@ -1492,6 +1554,11 @@ this.$message.warning( `成功保存 ${successCount} 个,失败 ${failCount} 个` ); // 可以显示具体哪些失败了 const failedQuestions = results .filter((r) => !r.success) .map((r) => r.id); console.error("保存失败的题目ID:", failedQuestions); } }) .catch(() => { @@ -1505,39 +1572,38 @@ this.previewAnswer = ""; this.previewVisible = true; }, /** 检查题目是否有异常选项 */ checkHasAbnormalOptions(question) { if (this.templateForm.templateType === 1) { return (question.svyLibTemplateTargetoptions || []).some( (opt) => opt.isabnormal === 1 ); } else if (this.templateForm.templateType === 2) { return (question.ivrLibaScriptTargetoptionList || []).some( (opt) => opt.isabnormal === 1 ); } return false; }, /** 打开选项管理对话框 */ /** 修改选项管理对话框的打开方法,保存原始选项 */ openOptionDialog(question) { this.editingQuestion = question; // 保存原始选项的快照 if (this.templateForm.templateType === 1) { this.editingQuestion.originalOptions = JSON.parse( JSON.stringify(question.svyLibTemplateTargetoptions || []) ); } else if (this.templateForm.templateType === 2) { this.editingQuestion.originalOptions = JSON.parse( JSON.stringify(question.ivrLibaScriptTargetoptionList || []) ); } // 复制选项数据 if (this.templateForm.templateType === 1) { this.currentOptions = JSON.parse( JSON.stringify(question.svyLibTemplateTargetoptions || []) ).map((opt) => ({ ).map((opt, index) => ({ ...opt, id: opt.id, id: opt.id || `temp_${Date.now()}_${index}`, targetvalue: opt.optioncontent || "", isabnormal: opt.isabnormal || 0, })); } else if (this.templateForm.templateType === 2) { this.currentOptions = JSON.parse( JSON.stringify(question.ivrLibaScriptTargetoptionList || []) ).map((opt) => ({ ).map((opt, index) => ({ ...opt, id: opt.id || `temp_${Date.now()}_${index}`, targetvalue: opt.targetvalue || "", isabnormal: opt.isabnormal || 0, })); @@ -1549,7 +1615,7 @@ /** 添加新选项 */ addNewOption() { this.currentOptions.push({ id: Date.now(), // 临时ID id: `temp_${Date.now()}_${this.currentOptions.length}`, targetvalue: "", isabnormal: 0, isNew: true, @@ -1558,16 +1624,25 @@ /** 删除选项 */ removeOption(index) { this.currentOptions.splice(index, 1); this.$confirm("确定要删除这个选项吗?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }) .then(() => { this.currentOptions.splice(index, 1); }) .catch(() => {}); }, /** 保存选项配置 */ async saveOptions() { try { // 验证必填项 for (const option of this.currentOptions) { for (let i = 0; i < this.currentOptions.length; i++) { const option = this.currentOptions[i]; if (!option.targetvalue || option.targetvalue.trim() === "") { this.$message.warning("请填写所有选项内容"); this.$message.warning(`第 ${i + 1} 个选项内容不能为空`); return; } } @@ -1582,6 +1657,27 @@ return; } // 判断选项是否发生变化 let isOptionsChanged = false; if (this.templateForm.templateType === 1) { const originalOptions = this.editingQuestion.svyLibTemplateTargetoptions || []; isOptionsChanged = this.checkOptionsChanged( originalOptions, this.currentOptions, "questionnaire" ); } else if (this.templateForm.templateType === 2) { const originalOptions = this.editingQuestion.ivrLibaScriptTargetoptionList || []; isOptionsChanged = this.checkOptionsChanged( originalOptions, this.currentOptions, "voice" ); } // 保存逻辑 - 更新题目对象的选项数据 if (this.templateForm.templateType === 1) { this.editingQuestion.svyLibTemplateTargetoptions = @@ -1589,14 +1685,24 @@ ...opt, optioncontent: opt.targetvalue, isabnormal: opt.isabnormal, // 清除临时字段 targetvalue: undefined, isNew: undefined, })); } else if (this.templateForm.templateType === 2) { this.editingQuestion.ivrLibaScriptTargetoptionList = this.currentOptions; this.currentOptions.map((opt) => ({ ...opt, // 清除临时字段 isNew: undefined, })); } // 触发配置变更检查 this.handleConfigChange(this.editingQuestion); // 如果选项有变化,则设置题目为有变更状态 if (isOptionsChanged) { this.editingQuestion.hasChanges = true; this.updateChangedStatus(); } this.$message.success("选项配置保存成功"); this.optionDialogVisible = false; @@ -1605,126 +1711,39 @@ this.$message.error("保存选项失败"); } }, /** 修改保存单个题目配置方法,添加异常选项检查 */ async saveSingleConfig(question) { // 检查是否有异常选项 if (!this.checkHasAbnormalOptions(question)) { this.$confirm("该题目没有设置异常选项,是否先配置选项?", "提示", { confirmButtonText: "去配置", cancelButtonText: "取消", type: "warning", }) .then(() => { this.openOptionDialog(question); }) .catch(() => {}); return; /** 检查选项是否发生变化 */ checkOptionsChanged(originalOptions, newOptions, templateType) { // 如果数量不同,则一定变化了 if (originalOptions.length !== newOptions.length) { return true; } // 原有的保存逻辑... if (!question.hasChanges) return; // 比较每个选项的内容和异常状态 for (let i = 0; i < originalOptions.length; i++) { const original = originalOptions[i]; const current = newOptions[i]; const index = this.filteredQuestionList.findIndex( (q) => q.id === question.id ); if (index === -1) return; const formRef = this.$refs.configForm && this.$refs.configForm[index]; if (!formRef) return; const valid = await formRef.validate(); if (!valid) { this.$message.warning("请先完成必填项"); return; } // 继续原有的保存逻辑... question.saving = true; question.saveStatus = null; try { // ... 原有的保存逻辑不变 } catch (error) { // ... 错误处理不变 } finally { question.saving = false; } }, /** 批量保存时也要检查 */ async handleBatchSave() { if (!this.hasChanges || this.batchSaving) return; // 检查所有有变更的题目是否都有异常选项 const changedQuestions = this.questionList.filter((q) => q.hasChanges); const questionsWithoutAbnormal = changedQuestions.filter( (q) => !this.checkHasAbnormalOptions(q) ); if (questionsWithoutAbnormal.length > 0) { this.$confirm( `有 ${questionsWithoutAbnormal.length} 个题目没有设置异常选项,请先配置选项。是否继续?`, "提示", { confirmButtonText: "继续", cancelButtonText: "去配置", type: "warning", if (templateType === "questionnaire") { // 问卷模板比较 if ( original.optioncontent !== current.targetvalue || original.isabnormal !== current.isabnormal ) { return true; } ) .then(() => { // 继续执行批量保存 this.executeBatchSave(changedQuestions); }) .catch(() => { // 可以在这里跳转到第一个没有异常选项的题目 if (questionsWithoutAbnormal.length > 0) { this.openOptionDialog(questionsWithoutAbnormal[0]); } }); } else { this.executeBatchSave(changedQuestions); } }, /** 执行批量保存 */ async executeBatchSave(changedQuestions) { this.$confirm("确定要保存所有修改过的配置吗?", "批量保存", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }) .then(async () => { this.batchSaving = true; const results = []; for (const question of changedQuestions) { try { // 这里调用修改后的saveSingleConfig方法 await this.saveSingleConfig(question); results.push({ id: question.id, success: !question.hasChanges && question.saveStatus?.type === "success", }); } catch (error) { results.push({ id: question.id, success: false, }); } } else if (templateType === "voice") { // 语音模板比较 if ( original.targetvalue !== current.targetvalue || original.isabnormal !== current.isabnormal ) { return true; } } } this.batchSaving = false; // ... 后续处理不变 }) .catch(() => { this.batchSaving = false; }); return false; }, /** 获取异常选项统计 */ getAbnormalStats(question) { if (this.templateForm.templateType === 1) { @@ -1746,6 +1765,7 @@ } return { total: 0, abnormal: 0, warning: 0, normal: 0 }; }, /** 搜索 */ handleQuery() { // 仅筛选显示,不需要重新加载