<template>
|
<div class="batch-process">
|
<!-- 页面标题 -->
|
<div class="page-header">
|
<div class="header-content">
|
<h2 class="page-title">异常批量处理</h2>
|
<p class="page-description">批量处理选中的异常反馈</p>
|
<div class="header-actions">
|
<el-button
|
type="primary"
|
icon="el-icon-check"
|
@click="handleBatchSubmit"
|
:loading="batchProcessing"
|
:disabled="selectedExceptionIds.length === 0"
|
>
|
批量提交处理 ({{ selectedExceptionIds.length }})
|
</el-button>
|
<el-button type="warning" icon="el-icon-back" @click="handleGoBack">
|
返回异常列表
|
</el-button>
|
</div>
|
</div>
|
</div>
|
|
<!-- 异常列表 -->
|
<div class="list-section">
|
<el-card shadow="never">
|
<div class="filter-section">
|
<el-form
|
:model="filterParams"
|
:inline="true"
|
size="medium"
|
class="filter-form"
|
>
|
<el-form-item label="负责科室">
|
<el-select
|
v-model="filterParams.todeptcode"
|
placeholder="请选择科室"
|
clearable
|
filterable
|
style="width: 200px"
|
>
|
<el-option
|
v-for="dept in deptList"
|
:key="dept.deptCode"
|
:label="dept.label"
|
:value="dept.deptCode"
|
/>
|
</el-select>
|
</el-form-item>
|
<el-form-item label="处理状态">
|
<el-select
|
v-model="filterParams.handleFlag"
|
placeholder="请选择状态"
|
clearable
|
style="width: 200px"
|
>
|
<el-option label="未处理" :value="'0'" />
|
<el-option label="已处理" :value="'1'" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="满意度类型">
|
<el-select
|
v-model="filterParams.templateType"
|
placeholder="请选择模板类型"
|
clearable
|
style="width: 200px"
|
>
|
<el-option label="语音模板" :value="1" />
|
<el-option label="问卷模板" :value="2" />
|
</el-select>
|
</el-form-item>
|
<el-form-item>
|
<el-button
|
type="primary"
|
icon="el-icon-search"
|
@click="handleFilter"
|
>
|
筛选
|
</el-button>
|
<el-button icon="el-icon-refresh" @click="handleResetFilter">
|
重置
|
</el-button>
|
</el-form-item>
|
</el-form>
|
</div>
|
|
<el-table
|
v-loading="loading"
|
:data="exceptionList"
|
:border="true"
|
style="width: 100%"
|
@selection-change="handleSelectionChange"
|
row-key="id"
|
class="exception-table"
|
>
|
<el-table-column type="selection" width="55" align="center" />
|
|
<el-table-column
|
label="序号"
|
type="index"
|
width="60"
|
align="center"
|
/>
|
|
<el-table-column
|
label="负责科室"
|
prop="todeptname"
|
width="200"
|
align="center"
|
>
|
<template slot-scope="{ row }">
|
<el-tag type="primary" v-if="row.todeptname">{{
|
row.todeptname
|
}}</el-tag>
|
<span v-else class="no-data">未分配</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="不满意详情" min-width="250" align="center">
|
<template slot-scope="{ row }">
|
<div class="detail-content">
|
<div class="question-text">
|
<strong>问题:</strong>{{ row.questiontext }}
|
</div>
|
<div class="answer-text">
|
<strong>回答:</strong>{{ row.asrtext || "无回答" }}
|
</div>
|
<div class="matched-text" v-if="row.matchedtext">
|
<strong>解析值:</strong>{{ row.matchedtext }}
|
</div>
|
</div>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="患者信息" width="300" align="center">
|
<template slot-scope="{ row }">
|
<div class="patient-info">
|
<div class="patient-row">
|
<div class="patient-item">
|
<span class="label">姓名:</span>
|
<span class="value">{{ row.patdescJson.sendname }}</span>
|
</div>
|
<div class="patient-item">
|
<span class="label">性别:</span>
|
<span class="value">{{
|
row.patdescJson.sex
|
}}</span>
|
</div>
|
<div class="patient-item">
|
<span class="label">年龄:</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.patdescJson.phone }}</span>
|
</div>
|
</div>
|
</div>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="填写信息" width="180" align="center">
|
<template slot-scope="{ row }">
|
<div class="fill-info">
|
<div class="info-item">
|
<span class="label">填报时间:</span>
|
<span class="value time">{{
|
formatDateTime(row.createTime)
|
}}</span>
|
</div>
|
<div v-if="row.recordurl" class="info-item">
|
<el-button
|
type="text"
|
size="small"
|
@click="handlePlayAudio(row.recordurl)"
|
icon="el-icon-headset"
|
>
|
播放录音
|
</el-button>
|
</div>
|
</div>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
label="处理状态"
|
prop="handleFlag"
|
width="100"
|
align="center"
|
>
|
<template slot-scope="{ row }">
|
<el-tag :type="getStatusTagType(row.handleFlag)" effect="dark">
|
{{ getStatusText(row.handleFlag) }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
|
<el-table-column label="最新处理信息" width="180" align="center">
|
<template slot-scope="{ row }">
|
<div v-if="row.handleTime" class="handle-info">
|
<div class="info-item">
|
<span class="label">处理人:</span>
|
<span class="value">{{ row.handleBy || "系统" }}</span>
|
</div>
|
<div class="info-item">
|
<span class="label">处理时间:</span>
|
<span class="value time">{{
|
formatDateTime(row.handleTime)
|
}}</span>
|
</div>
|
<div class="info-item">
|
<span class="label">处理说明:</span>
|
<span class="value">{{ row.handledesc }}</span>
|
</div>
|
</div>
|
<span v-else class="no-data">未处理</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
label="操作"
|
width="210"
|
align="center"
|
fixed="right"
|
>
|
<template slot-scope="{ row }">
|
<el-button
|
type="primary"
|
size="small"
|
icon="el-icon-view"
|
@click="handleViewDetail(row)"
|
>
|
查看详情
|
</el-button>
|
<el-button
|
type="warning"
|
size="small"
|
icon="el-icon-edit"
|
@click="handleProcess(row)"
|
:disabled="row.handleFlag === '1'"
|
>
|
处理
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<!-- 分页 -->
|
<div class="pagination-section">
|
<el-pagination
|
background
|
layout="total, sizes, prev, pager, next, jumper"
|
:current-page="filterParams.pageNum"
|
:page-size="filterParams.pageSize"
|
:page-sizes="[10, 20, 30, 50]"
|
:total="total"
|
@size-change="handleSizeChange"
|
@current-change="handlePageChange"
|
/>
|
</div>
|
</el-card>
|
</div>
|
|
<!-- 处理对话框 -->
|
<el-dialog
|
title="处理异常反馈"
|
:visible.sync="processDialogVisible"
|
width="600px"
|
center
|
>
|
<el-form
|
:model="processForm"
|
:rules="processRules"
|
ref="processForm"
|
label-width="100px"
|
size="medium"
|
>
|
<el-form-item label="处理状态" prop="handleFlag">
|
<el-select
|
v-model="processForm.handleFlag"
|
placeholder="请选择处理状态"
|
style="width: 100%"
|
>
|
<el-option label="已处理" :value="'1'" />
|
<el-option label="取消处理" :value="'0'" />
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="报备科室" prop="ccdepts">
|
<el-select
|
v-model="processForm.ccdepts"
|
placeholder="请选择报备科室"
|
multiple
|
filterable
|
collapse-tags
|
style="width: 100%"
|
:disabled="processForm.handleFlag !== '1'"
|
>
|
<el-option
|
v-for="dept in deptList"
|
:key="dept.deptCode"
|
:label="dept.label"
|
:value="dept.deptCode"
|
/>
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="处理结果" prop="handleresult">
|
<el-select
|
v-model="processForm.handleresult"
|
placeholder="请选择处理结果"
|
style="width: 100%"
|
:disabled="processForm.handleFlag !== '1'"
|
>
|
<el-option label="已解决" value="resolved" />
|
<el-option label="已解释" value="explained" />
|
<el-option label="已转交" value="transferred" />
|
<el-option label="需改进" value="improvement" />
|
<el-option label="已驳回" value="rejected" />
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="处理说明" prop="handledesc">
|
<el-input
|
v-model="processForm.handledesc"
|
type="textarea"
|
:rows="4"
|
placeholder="请输入处理说明(最多500字)"
|
maxlength="500"
|
show-word-limit
|
:disabled="processForm.handleFlag !== '1'"
|
/>
|
</el-form-item>
|
|
<el-form-item
|
label="最终意见"
|
prop="finaloption"
|
v-if="hasQualityPermission"
|
>
|
<el-input
|
v-model="processForm.finaloption"
|
type="textarea"
|
:rows="3"
|
placeholder="请输入最终处理意见(最多300字)"
|
maxlength="300"
|
show-word-limit
|
/>
|
</el-form-item>
|
</el-form>
|
<span slot="footer" class="dialog-footer">
|
<el-button @click="processDialogVisible = false">取消</el-button>
|
<el-button type="primary" @click="submitProcess" :loading="processing">
|
提交处理
|
</el-button>
|
</span>
|
</el-dialog>
|
|
<!-- 批量处理对话框 -->
|
<el-dialog
|
title="批量处理异常反馈"
|
:visible.sync="batchDialogVisible"
|
width="600px"
|
center
|
>
|
<el-form
|
:model="batchProcessForm"
|
:rules="processRules"
|
ref="batchProcessForm"
|
label-width="100px"
|
size="medium"
|
>
|
<el-form-item label="处理状态" prop="handleFlag">
|
<el-select
|
v-model="batchProcessForm.handleFlag"
|
placeholder="请选择处理状态"
|
style="width: 100%"
|
>
|
<el-option label="已处理" :value="'1'" />
|
<el-option label="取消处理" :value="'0'" />
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="报备科室" prop="ccdepts">
|
<el-select
|
v-model="batchProcessForm.ccdepts"
|
placeholder="请选择报备科室"
|
multiple
|
filterable
|
collapse-tags
|
style="width: 100%"
|
:disabled="batchProcessForm.handleFlag !== '1'"
|
>
|
<el-option
|
v-for="dept in deptList"
|
:key="dept.deptCode"
|
:label="dept.label"
|
:value="dept.deptCode"
|
/>
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="处理结果" prop="handleresult">
|
<el-select
|
v-model="batchProcessForm.handleresult"
|
placeholder="请选择处理结果"
|
style="width: 100%"
|
:disabled="batchProcessForm.handleFlag !== '1'"
|
>
|
<el-option label="已解决" value="resolved" />
|
<el-option label="已解释" value="explained" />
|
<el-option label="已转交" value="transferred" />
|
<el-option label="需改进" value="improvement" />
|
<el-option label="已驳回" value="rejected" />
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="处理说明" prop="handledesc">
|
<el-input
|
v-model="batchProcessForm.handledesc"
|
type="textarea"
|
:rows="4"
|
placeholder="请输入处理说明(最多500字)"
|
maxlength="500"
|
show-word-limit
|
:disabled="batchProcessForm.handleFlag !== '1'"
|
/>
|
</el-form-item>
|
</el-form>
|
<span slot="footer" class="dialog-footer">
|
<el-button @click="batchDialogVisible = false">取消</el-button>
|
<el-button
|
type="primary"
|
@click="submitBatchProcess"
|
:loading="batchProcessing"
|
>
|
批量提交 ({{ selectedExceptionIds.length }})
|
</el-button>
|
</span>
|
</el-dialog>
|
<!-- 进度对话框 -->
|
<el-dialog
|
title="批量处理进度"
|
:visible.sync="batchProgress.visible"
|
width="400px"
|
:close-on-click-modal="false"
|
:show-close="false"
|
:close-on-press-escape="false"
|
>
|
<div class="progress-content">
|
<el-progress
|
:percentage="batchProgress.percentage"
|
:status="batchProgress.percentage === 100 ? 'success' : ''"
|
/>
|
<div class="progress-info">
|
已处理 {{ batchProgress.processed }}/{{ batchProgress.total }} 条记录
|
</div>
|
</div>
|
</el-dialog>
|
<!-- 异常详情弹框 -->
|
<Details-anomaly
|
:visible="detailDialogVisible"
|
:record-id="selectedRecordId"
|
:title="detailDialogTitle"
|
:record-data="selectedRecordData"
|
@update:visible="handleDetailDialogClose"
|
@processed="handleProcessed"
|
@close="handleDetailDialogClose"
|
/>
|
|
<!-- 录音播放器 -->
|
<audio
|
v-if="audioUrl"
|
:src="audioUrl"
|
ref="audioPlayer"
|
controls
|
style="display: none"
|
/>
|
</div>
|
</template>
|
|
<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 {
|
name: "BatchProcess",
|
components: {
|
DetailsAnomaly,
|
},
|
data() {
|
return {
|
// 详情弹框相关
|
detailDialogVisible: false,
|
selectedRecordId: null,
|
selectedRecordData: null,
|
detailDialogTitle: "异常反馈详情",
|
|
// 音频播放
|
audioUrl: "",
|
|
// 当前处理的异常ID
|
currentExceptionId: null,
|
|
// 批量选中的异常ID
|
selectedExceptionIds: [],
|
|
// 过滤参数
|
filterParams: {
|
todeptcode: "",
|
handleFlag: "",
|
templateType: null,
|
scriptids: null,
|
pageNum: 1,
|
pageSize: 10,
|
},
|
|
// 加载状态
|
loading: false,
|
processing: false,
|
batchProcessing: false,
|
|
// 权限控制
|
hasQualityPermission: false, // 是否具有质管权限
|
|
// 科室列表
|
deptList: [],
|
|
// 异常列表数据
|
exceptionList: [],
|
total: 0,
|
|
// 处理对话框
|
processDialogVisible: false,
|
processForm: {
|
handleFlag: "",
|
ccdepts: [],
|
handleresult: "",
|
handledesc: "",
|
finaloption: "",
|
},
|
batchProgress: {
|
visible: false,
|
percentage: 0,
|
processed: 0,
|
total: 0,
|
},
|
processRules: {
|
handleFlag: [
|
{ required: true, message: "请选择处理状态", trigger: "change" },
|
],
|
handleresult: [
|
{
|
required: true,
|
message: "请选择处理结果",
|
trigger: "change",
|
validator: (rule, value, callback) => {
|
if (this.processForm.handleFlag === "1" && !value) {
|
callback(new Error("请选择处理结果"));
|
} else {
|
callback();
|
}
|
},
|
},
|
],
|
handledesc: [
|
{
|
required: true,
|
message: "请输入处理说明",
|
trigger: "blur",
|
validator: (rule, value, callback) => {
|
if (
|
this.processForm.handleFlag === "1" &&
|
(!value || value.trim().length < 3)
|
) {
|
callback(new Error("处理说明至少3个字符"));
|
} else {
|
callback();
|
}
|
},
|
},
|
],
|
},
|
|
// 批量处理对话框
|
batchDialogVisible: false,
|
batchProcessForm: {
|
handleFlag: "",
|
ccdepts: [],
|
handleresult: "",
|
handledesc: "",
|
},
|
};
|
},
|
|
created() {
|
// 从路由参数获取问题ID
|
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();
|
},
|
|
mounted() {
|
this.loadExceptionList();
|
this.getDeptOptions();
|
},
|
|
methods: {
|
// 格式化日期时间
|
formatDateTime(dateTime) {
|
if (!dateTime) return "";
|
try {
|
const date = new Date(dateTime);
|
if (isNaN(date.getTime())) {
|
return dateTime;
|
}
|
return (
|
date.toLocaleDateString().replace(/\//g, "-") +
|
" " +
|
date.toTimeString().split(" ")[0]
|
);
|
} catch (error) {
|
console.error("日期格式化错误:", error);
|
return dateTime;
|
}
|
},
|
/** 查询科室列表 */
|
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;
|
},
|
// 解析患者描述信息
|
parsePatDesc(patdesc) {
|
if (!patdesc) return [];
|
|
try {
|
const parts = patdesc.split("|");
|
const items = [];
|
|
if (parts[0]) items.push({ label: "姓名", value: parts[0] });
|
if (parts[1]) items.push({ label: "电话", value: parts[1] });
|
if (parts[2]) items.push({ label: "科室", value: parts[2] });
|
|
return items;
|
} catch (error) {
|
console.error("解析患者信息失败:", error);
|
return [];
|
}
|
},
|
|
// 检查质管权限
|
checkQualityPermission() {
|
// 这里可以根据实际权限系统实现
|
const userRoles = this.$store.getters.roles || [];
|
return (
|
userRoles.includes("quality_manager") || userRoles.includes("admin")
|
);
|
},
|
|
// 获取状态标签类型
|
getStatusTagType(handleFlag) {
|
switch (handleFlag) {
|
case "0":
|
return "warning"; // 未处理
|
case "1":
|
return "success"; // 已处理
|
default:
|
return "info";
|
}
|
},
|
|
// 获取状态文本
|
getStatusText(handleFlag) {
|
switch (handleFlag) {
|
case "0":
|
return "未处理";
|
case "1":
|
return "已处理";
|
default:
|
return "未知";
|
}
|
},
|
|
// 播放录音
|
handlePlayAudio(url) {
|
this.audioUrl = url;
|
this.$nextTick(() => {
|
const audioPlayer = this.$refs.audioPlayer;
|
if (audioPlayer) {
|
audioPlayer.play().catch((error) => {
|
console.error("播放失败:", error);
|
this.$message.error("音频播放失败");
|
});
|
}
|
});
|
},
|
|
// 构建查询参数
|
buildQueryParams() {
|
const params = {
|
pageNum: this.filterParams.pageNum,
|
pageSize: this.filterParams.pageSize,
|
};
|
|
if (this.filterParams.todeptcode) {
|
params.todeptcode = this.filterParams.todeptcode;
|
}
|
|
if (this.filterParams.handleFlag !== "") {
|
params.handleFlag = this.filterParams.handleFlag;
|
}
|
|
if (this.filterParams.templateType) {
|
params.templateType = this.filterParams.templateType;
|
}
|
|
// if (this.filterParams.scriptid) {
|
// params.scriptid = this.filterParams.scriptid;
|
// }
|
if (this.filterParams.scriptids) {
|
params.scriptids = this.filterParams.scriptids.split(",");
|
}
|
|
return params;
|
},
|
|
// 加载异常列表
|
async loadExceptionList() {
|
this.loading = true;
|
try {
|
const params = this.buildQueryParams();
|
const response = await tracelist(params);
|
|
if (response && response.code === 200) {
|
this.exceptionList = response.rows || [];
|
this.total = response.total || 0;
|
} else {
|
this.exceptionList = [];
|
this.total = 0;
|
this.$message.error(response?.msg || "加载异常列表失败");
|
}
|
} catch (error) {
|
console.error("加载异常列表失败:", error);
|
this.$message.error("加载异常列表失败,请稍后重试");
|
this.exceptionList = [];
|
this.total = 0;
|
} finally {
|
this.loading = false;
|
}
|
},
|
|
// 处理筛选
|
handleFilter() {
|
this.filterParams.pageNum = 1;
|
this.loadExceptionList();
|
},
|
|
// 重置筛选
|
handleResetFilter() {
|
this.filterParams = {
|
todeptcode: "",
|
handleFlag: "",
|
templateType: "",
|
scriptids: null, // 保留问题ID
|
pageNum: 1,
|
pageSize: 10,
|
};
|
this.selectedExceptionIds = [];
|
this.loadExceptionList();
|
},
|
|
// 处理选择变化
|
handleSelectionChange(selection) {
|
this.selectedExceptionIds = selection.map((item) => item.id);
|
},
|
|
// 处理批量提交
|
handleBatchSubmit() {
|
if (this.selectedExceptionIds.length === 0) {
|
this.$message.warning("请先选择要处理的异常反馈");
|
return;
|
}
|
|
// 重置批量处理表单
|
this.batchProcessForm = {
|
handleFlag: "",
|
ccdepts: [],
|
handleresult: "",
|
handledesc: "",
|
};
|
|
this.batchDialogVisible = true;
|
},
|
|
// 返回异常列表
|
handleGoBack() {
|
this.$router.push("/satisfaction/exception/list");
|
},
|
|
// 查看详情
|
handleViewDetail(row) {
|
this.selectedRecordId = row.id;
|
this.selectedRecordData = row;
|
|
// 生成弹框标题
|
let title = "异常反馈详情";
|
if (row.patdesc) {
|
const patientName = row.patdescJson.sendname;
|
if (patientName) {
|
title = `${patientName} - ${title}`;
|
}
|
}
|
this.detailDialogTitle = title;
|
|
this.detailDialogVisible = true;
|
},
|
|
// 处理详情弹框关闭
|
handleDetailDialogClose() {
|
this.detailDialogVisible = false;
|
this.selectedRecordId = null;
|
this.selectedRecordData = null;
|
},
|
|
// 处理完成后的回调
|
handleProcessed() {
|
this.loadExceptionList();
|
},
|
|
// 处理单个异常
|
handleProcess(row) {
|
this.currentExceptionId = row.id;
|
|
// 初始化表单数据
|
this.processForm = {
|
handleFlag: row.handleFlag === "0" ? "1" : "0",
|
ccdepts: row.ccdepts ? row.ccdepts.split(",") : [],
|
handleresult: row.handleresult || "",
|
handledesc: row.handledesc || "",
|
finaloption: row.finaloption || "",
|
};
|
|
this.processDialogVisible = true;
|
},
|
|
// 提交处理
|
async submitProcess() {
|
this.$refs.processForm.validate(async (valid) => {
|
if (!valid) {
|
return;
|
}
|
|
this.processing = true;
|
|
try {
|
// 准备提交数据
|
const submitData = {
|
id: this.currentExceptionId,
|
handleFlag: this.processForm.handleFlag,
|
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(",")
|
: this.processForm.ccdepts,
|
};
|
// TODO: 这里需要调用实际的处理接口
|
await traceedit(submitData);
|
|
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
this.$message.success("处理提交成功");
|
this.processDialogVisible = false;
|
this.loadExceptionList();
|
} catch (error) {
|
console.error("处理提交失败:", error);
|
this.$message.error("处理提交失败,请稍后重试");
|
} finally {
|
this.processing = false;
|
}
|
});
|
},
|
|
// 提交批量处理
|
async submitBatchProcess() {
|
this.$refs.batchProcessForm.validate(async (valid) => {
|
if (!valid) {
|
return;
|
}
|
|
this.batchProcessing = true;
|
// 显示进度条
|
this.batchProgress = {
|
visible: true,
|
percentage: 0,
|
processed: 0,
|
total: this.selectedExceptionIds.length,
|
};
|
try {
|
// 准备批量提交数据
|
const processData = {
|
handleFlag: this.batchProcessForm.handleFlag,
|
handleresult: this.batchProcessForm.handleresult,
|
handledesc: this.batchProcessForm.handledesc,
|
ccdepts: Array.isArray(this.batchProcessForm.ccdepts)
|
? this.batchProcessForm.ccdepts.join(",")
|
: this.batchProcessForm.ccdepts,
|
};
|
|
// 控制并发数
|
const CONCURRENT_LIMIT = 10; // 同时最多3个请求
|
const totalCount = this.selectedExceptionIds.length;
|
const results = [];
|
let successCount = 0;
|
let failCount = 0;
|
|
this.$message.info(`开始批量处理 ${totalCount} 条记录...`);
|
|
// 分组处理
|
for (
|
let i = 0;
|
i < this.selectedExceptionIds.length;
|
i += CONCURRENT_LIMIT
|
) {
|
const batchIds = this.selectedExceptionIds.slice(
|
i,
|
i + CONCURRENT_LIMIT
|
);
|
|
// 并发处理当前批次
|
const batchPromises = batchIds.map((id) =>
|
traceedit({
|
id: id,
|
...processData,
|
})
|
.then((result) => ({
|
id,
|
success: result && result.code === 200,
|
error: result?.msg,
|
}))
|
.catch((error) => ({
|
id,
|
success: false,
|
error: error.message,
|
}))
|
);
|
|
const batchResults = await Promise.all(batchPromises);
|
results.push(...batchResults);
|
|
// 更新统计
|
batchResults.forEach((result) => {
|
if (result.success) {
|
successCount++;
|
} else {
|
failCount++;
|
console.error(`处理记录 ${result.id} 失败:`, result.error);
|
}
|
});
|
// 更新进度
|
this.batchProgress.processed = i + 1;
|
this.batchProgress.percentage = Math.round(
|
((i + 1) / totalCount) * 100
|
);
|
// 显示进度
|
console.log(
|
`进度: ${Math.min(
|
i + CONCURRENT_LIMIT,
|
totalCount
|
)}/${totalCount}`
|
);
|
}
|
|
// 处理结果提示
|
if (successCount === totalCount) {
|
this.$message.success(`已成功处理全部 ${totalCount} 条异常反馈`);
|
} else {
|
this.$message.warning(
|
`已处理 ${successCount} 条,失败 ${failCount} 条异常反馈`
|
);
|
}
|
|
this.batchDialogVisible = false;
|
this.selectedExceptionIds = [];
|
this.loadExceptionList();
|
} catch (error) {
|
console.error("批量处理失败:", error);
|
this.$message.error("批量处理失败,请稍后重试");
|
} finally {
|
this.batchProcessing = false;
|
this.batchProgress.visible = false;
|
}
|
});
|
},
|
|
// 分页大小变化
|
handleSizeChange(size) {
|
this.filterParams.pageSize = size;
|
this.filterParams.pageNum = 1;
|
this.loadExceptionList();
|
},
|
|
// 页码变化
|
handlePageChange(page) {
|
this.filterParams.pageNum = page;
|
this.loadExceptionList();
|
},
|
},
|
};
|
</script>
|
|
<style lang="scss" scoped>
|
.batch-process {
|
padding: 20px;
|
background-color: #f5f7fa;
|
min-height: 100vh;
|
|
.page-header {
|
margin-bottom: 20px;
|
padding: 20px;
|
background: linear-gradient(135deg, #5788fe 0%, #66b1ff 100%);
|
border-radius: 8px;
|
color: white;
|
|
.header-content {
|
.page-title {
|
margin: 0 0 8px 0;
|
font-size: 20px;
|
font-weight: 600;
|
}
|
|
.page-description {
|
margin: 0 0 20px 0;
|
opacity: 0.9;
|
font-size: 14px;
|
}
|
|
.header-actions {
|
display: flex;
|
gap: 10px;
|
}
|
}
|
}
|
|
.list-section {
|
.filter-section {
|
margin-bottom: 20px;
|
|
.filter-form {
|
display: flex;
|
flex-wrap: wrap;
|
align-items: center;
|
|
::v-deep .el-form-item {
|
margin-bottom: 0;
|
margin-right: 20px;
|
|
&:last-child {
|
margin-right: 0;
|
}
|
}
|
}
|
}
|
|
.exception-table {
|
::v-deep .el-table__header-wrapper {
|
th {
|
background-color: #f8f9fa;
|
font-weight: 600;
|
color: #333;
|
}
|
}
|
|
.detail-content {
|
text-align: left;
|
font-size: 12px;
|
line-height: 1.5;
|
|
.question-text {
|
color: #303133;
|
margin-bottom: 5px;
|
font-weight: 500;
|
}
|
|
.answer-text {
|
color: #f56c6c;
|
margin-bottom: 5px;
|
}
|
|
.matched-text {
|
color: #e6a23c;
|
font-style: italic;
|
}
|
|
strong {
|
color: #606266;
|
font-weight: 600;
|
}
|
}
|
|
.patient-info {
|
.patient-row {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 8px;
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
|
.patient-item {
|
flex: 1;
|
display: flex;
|
justify-content: flex-start;
|
align-items: center;
|
padding: 0 5px;
|
|
&.full-width {
|
flex: 1 0 100%;
|
margin-left: 0;
|
margin-right: 0;
|
}
|
|
.label {
|
font-size: 12px;
|
color: #606266;
|
margin-right: 5px;
|
white-space: nowrap;
|
}
|
|
.value {
|
font-size: 12px;
|
color: #333;
|
font-weight: 500;
|
text-align: right;
|
word-break: break-all;
|
}
|
}
|
}
|
}
|
|
.fill-info,
|
.handle-info {
|
font-size: 12px;
|
|
.info-item {
|
display: flex;
|
justify-content: flex-start;
|
align-items: center;
|
margin-bottom: 5px;
|
padding: 2px 0;
|
|
.label {
|
color: #606266;
|
min-width: 50px;
|
}
|
|
.value {
|
color: #333;
|
font-weight: 500;
|
// text-align: right;
|
flex: 1;
|
|
&.time {
|
color: #909399;
|
font-size: 11px;
|
}
|
}
|
}
|
}
|
|
.no-data {
|
color: #909399;
|
font-style: italic;
|
font-size: 12px;
|
}
|
}
|
|
.pagination-section {
|
display: flex;
|
justify-content: center;
|
padding: 20px 0 0 0;
|
}
|
}
|
}
|
|
@media (max-width: 768px) {
|
.batch-process {
|
padding: 10px;
|
|
.page-header {
|
.header-actions {
|
flex-direction: column;
|
align-items: stretch;
|
}
|
}
|
|
.list-section {
|
.filter-section {
|
.filter-form {
|
::v-deep .el-form-item {
|
width: 100%;
|
margin-right: 0;
|
margin-bottom: 10px;
|
}
|
}
|
}
|
}
|
}
|
}
|
</style>
|