| | |
| | | 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-form-item label="负责科室"> |
| | | <el-select |
| | | v-model="filterParams.deptId" |
| | | v-model="filterParams.todeptcode" |
| | | placeholder="请选择科室" |
| | | clearable |
| | | filterable |
| | | style="width: 200px" |
| | | > |
| | | <el-option |
| | | v-for="dept in deptList" |
| | | :key="dept.id" |
| | | :label="dept.name" |
| | | :value="dept.id" |
| | | :key="dept.deptCode" |
| | | :label="dept.label" |
| | | :value="dept.deptCode" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="处理状态"> |
| | | <el-select |
| | | v-model="filterParams.status" |
| | | v-model="filterParams.handleFlag" |
| | | placeholder="请选择状态" |
| | | clearable |
| | | style="width: 200px" |
| | | > |
| | | <el-option label="待处理" :value="0" /> |
| | | <el-option label="处理中" :value="1" /> |
| | | <el-option label="已处理" :value="2" /> |
| | | <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> |
| | |
| | | :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="负责科室" |
| | | prop="responsibilityDept" |
| | | width="120" |
| | | prop="todeptname" |
| | | width="200" |
| | | align="center" |
| | | > |
| | | <template slot-scope="{ row }"> |
| | | <el-tag type="primary">{{ row.responsibilityDept }}</el-tag> |
| | | <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="不满意详情" |
| | | prop="unsatisfactoryDetail" |
| | | min-width="200" |
| | | align="center" |
| | | > |
| | | <el-table-column label="不满意详情" min-width="250" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <div class="detail-content"> |
| | | {{ row.unsatisfactoryDetail }} |
| | | <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-item"> |
| | | <span class="label">姓名:</span> |
| | | <span class="value">{{ row.patientName }}</span> |
| | | <div class="patient-row"> |
| | | <div class="patient-item"> |
| | | <span class="label">姓名:</span> |
| | | <span class="value">{{ row.patientName }}</span> |
| | | </div> |
| | | <div class="patient-item"> |
| | | <span class="label">性别:</span> |
| | | <span class="value">{{ |
| | | row.gender === 1 ? "男" : "女" |
| | | }}</span> |
| | | </div> |
| | | <div class="patient-item"> |
| | | <span class="label">年龄:</span> |
| | | <span class="value">{{ row.age }}岁</span> |
| | | </div> |
| | | </div> |
| | | <div class="patient-item"> |
| | | <span class="label">性别:</span> |
| | | <span class="value">{{ |
| | | row.gender === 1 ? "男" : "女" |
| | | }}</span> |
| | | </div> |
| | | <div class="patient-item"> |
| | | <span class="label">年龄:</span> |
| | | <span class="value">{{ row.age }}岁</span> |
| | | </div> |
| | | <div class="patient-item"> |
| | | <span class="label">电话:</span> |
| | | <span class="value">{{ row.phone }}</span> |
| | | <div class="patient-row"> |
| | | <div class="patient-item full-width"> |
| | | <span class="label">电话:</span> |
| | | <span class="value">{{ row.phone }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column label="出院信息" width="250" align="center"> |
| | | <el-table-column label="填写信息" width="180" align="center"> |
| | | <template slot-scope="{ row }"> |
| | | <div class="discharge-info"> |
| | | <div class="fill-info"> |
| | | <div class="info-item"> |
| | | <span class="label">科室:</span> |
| | | <span class="value">{{ row.dischargeDept }}</span> |
| | | <span class="label">填报时间:</span> |
| | | <span class="value time">{{ |
| | | formatDateTime(row.createTime) |
| | | }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <span class="label">病区:</span> |
| | | <span class="value">{{ row.dischargeWard }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <span class="label">填写时间:</span> |
| | | <span class="value time">{{ row.fillTime }}</span> |
| | | <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 |
| | | label="处理状态" |
| | | prop="processStatus" |
| | | prop="handleFlag" |
| | | width="100" |
| | | align="center" |
| | | > |
| | | <template slot-scope="{ row }"> |
| | | <el-tag :type="getStatusTagType(row.processStatus)" effect="dark"> |
| | | {{ getStatusText(row.processStatus) }} |
| | | <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> |
| | | |
| | |
| | | size="small" |
| | | icon="el-icon-edit" |
| | | @click="handleProcess(row)" |
| | | :disabled="row.processStatus === 2" |
| | | :disabled="row.handleFlag === '1'" |
| | | > |
| | | 处理 |
| | | </el-button> |
| | |
| | | label-width="100px" |
| | | size="medium" |
| | | > |
| | | <el-form-item label="处理状态" prop="status"> |
| | | <el-form-item label="处理状态" prop="handleFlag"> |
| | | <el-select |
| | | v-model="processForm.status" |
| | | v-model="processForm.handleFlag" |
| | | placeholder="请选择处理状态" |
| | | style="width: 100%" |
| | | > |
| | | <el-option label="处理中" :value="1" /> |
| | | <el-option label="已处理" :value="2" /> |
| | | <el-option label="已驳回" :value="3" /> |
| | | <el-option label="已处理" :value="'1'" /> |
| | | <el-option label="取消处理" :value="'0'" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="报备科室" prop="reportDepts"> |
| | | <el-form-item label="报备科室" prop="ccdepts"> |
| | | <el-select |
| | | v-model="processForm.reportDepts" |
| | | 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.id" |
| | | :label="dept.name" |
| | | :value="dept.id" |
| | | :key="dept.deptCode" |
| | | :label="dept.label" |
| | | :value="dept.deptCode" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="处理备注" prop="remark"> |
| | | <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.remark" |
| | | v-model="processForm.handledesc" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请输入处理备注(最多500字)" |
| | | placeholder="请输入处理说明(最多500字)" |
| | | maxlength="500" |
| | | show-word-limit |
| | | :disabled="processForm.handleFlag !== '1'" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="附件上传"> |
| | | <el-upload |
| | | class="upload-demo" |
| | | action="#" |
| | | :on-preview="handlePreview" |
| | | :on-remove="handleRemove" |
| | | :before-remove="beforeRemove" |
| | | :limit="3" |
| | | :on-exceed="handleExceed" |
| | | :file-list="fileList" |
| | | > |
| | | <el-button size="small" type="primary">点击上传</el-button> |
| | | <div slot="tip" class="el-upload__tip"> |
| | | 支持上传图片、文档等附件,单个文件不超过10MB |
| | | </div> |
| | | </el-upload> |
| | | <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"> |
| | |
| | | label-width="100px" |
| | | size="medium" |
| | | > |
| | | <el-form-item label="处理状态" prop="status"> |
| | | <el-form-item label="处理状态" prop="handleFlag"> |
| | | <el-select |
| | | v-model="batchProcessForm.status" |
| | | v-model="batchProcessForm.handleFlag" |
| | | placeholder="请选择处理状态" |
| | | style="width: 100%" |
| | | > |
| | | <el-option label="处理中" :value="1" /> |
| | | <el-option label="已处理" :value="2" /> |
| | | <el-option label="已驳回" :value="3" /> |
| | | <el-option label="已处理" :value="'1'" /> |
| | | <el-option label="取消处理" :value="'0'" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="报备科室" prop="reportDepts"> |
| | | <el-form-item label="报备科室" prop="ccdepts"> |
| | | <el-select |
| | | v-model="batchProcessForm.reportDepts" |
| | | 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.id" |
| | | :label="dept.name" |
| | | :value="dept.id" |
| | | :key="dept.deptCode" |
| | | :label="dept.label" |
| | | :value="dept.deptCode" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="处理备注" prop="remark"> |
| | | <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.remark" |
| | | v-model="batchProcessForm.handledesc" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请输入处理备注(最多500字)" |
| | | placeholder="请输入处理说明(最多500字)" |
| | | maxlength="500" |
| | | show-word-limit |
| | | :disabled="batchProcessForm.handleFlag !== '1'" |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | |
| | | @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 { deptTreeSelect } from "@/api/system/user"; |
| | | |
| | | export default { |
| | | name: "BatchProcess", |
| | | components: { |
| | |
| | | }, |
| | | data() { |
| | | return { |
| | | // 添加以下数据 |
| | | // 详情弹框相关 |
| | | detailDialogVisible: false, |
| | | selectedRecordId: null, |
| | | selectedRecordData: null, |
| | | detailDialogTitle: "异常反馈详情", |
| | | |
| | | // 音频播放 |
| | | audioUrl: "", |
| | | |
| | | // 当前处理的异常ID |
| | | currentExceptionId: null, |
| | | |
| | |
| | | |
| | | // 过滤参数 |
| | | filterParams: { |
| | | deptId: "", |
| | | status: "", |
| | | todeptcode: "", |
| | | handleFlag: "", |
| | | templateType: "", |
| | | scriptid: null, |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | }, |
| | |
| | | processing: false, |
| | | batchProcessing: false, |
| | | |
| | | // 权限控制 |
| | | hasQualityPermission: false, // 是否具有质管权限 |
| | | |
| | | // 科室列表 |
| | | deptList: [ |
| | | { id: 1, name: "心血管内科" }, |
| | | { id: 2, name: "神经内科" }, |
| | | { id: 3, name: "普外科" }, |
| | | { id: 4, name: "骨科" }, |
| | | { id: 5, name: "妇产科" }, |
| | | { id: 6, name: "儿科" }, |
| | | { id: 7, name: "急诊科" }, |
| | | { id: 8, name: "呼吸内科" }, |
| | | { id: 9, name: "消化内科" }, |
| | | { id: 10, name: "内分泌科" }, |
| | | ], |
| | | deptList: [], |
| | | |
| | | // 异常列表数据 |
| | | exceptionList: [], |
| | |
| | | // 处理对话框 |
| | | processDialogVisible: false, |
| | | processForm: { |
| | | status: "", |
| | | reportDepts: [], |
| | | remark: "", |
| | | handleFlag: "", |
| | | ccdepts: [], |
| | | handleresult: "", |
| | | handledesc: "", |
| | | finaloption: "", |
| | | }, |
| | | batchProgress: { |
| | | visible: false, |
| | | percentage: 0, |
| | | processed: 0, |
| | | total: 0, |
| | | }, |
| | | processRules: { |
| | | status: [ |
| | | handleFlag: [ |
| | | { required: true, message: "请选择处理状态", trigger: "change" }, |
| | | ], |
| | | remark: [ |
| | | { required: true, message: "请输入处理备注", trigger: "blur" }, |
| | | handleresult: [ |
| | | { |
| | | min: 5, |
| | | max: 500, |
| | | message: "备注长度在 5 到 500 个字符", |
| | | 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(); |
| | | } |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | fileList: [], |
| | | |
| | | // 批量处理对话框 |
| | | batchDialogVisible: false, |
| | | batchProcessForm: { |
| | | status: "", |
| | | reportDepts: [], |
| | | remark: "", |
| | | handleFlag: "", |
| | | ccdepts: [], |
| | | handleresult: "", |
| | | handledesc: "", |
| | | }, |
| | | }; |
| | | }, |
| | | |
| | | 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.hasQualityPermission = this.checkQualityPermission(); |
| | | }, |
| | | |
| | | mounted() { |
| | | this.loadExceptionList(); |
| | | this.getDeptOptions(); |
| | | }, |
| | | |
| | | methods: { |
| | | // 加载异常列表 |
| | | async loadExceptionList() { |
| | | this.loading = true; |
| | | // 格式化日期时间 |
| | | formatDateTime(dateTime) { |
| | | if (!dateTime) return ""; |
| | | try { |
| | | // Mock 数据 |
| | | await new Promise((resolve) => { |
| | | setTimeout(() => { |
| | | this.exceptionList = [ |
| | | { |
| | | id: 1, |
| | | responsibilityDept: "心血管内科", |
| | | unsatisfactoryDetail: |
| | | "医生查房时间太短,沟通不够充分,对病情解释不够详细", |
| | | patientName: "张先生", |
| | | gender: 1, |
| | | age: 45, |
| | | phone: "138****1234", |
| | | dischargeDept: "心血管内科", |
| | | dischargeWard: "内科一病区", |
| | | fillTime: "2024-01-15 10:30:25", |
| | | processStatus: 0, |
| | | questionnaireId: 1001, |
| | | }, |
| | | { |
| | | id: 2, |
| | | responsibilityDept: "神经内科", |
| | | unsatisfactoryDetail: |
| | | "护士打针技术不佳,扎了三次才成功,且态度不够耐心", |
| | | patientName: "李女士", |
| | | gender: 0, |
| | | age: 38, |
| | | phone: "139****5678", |
| | | dischargeDept: "神经内科", |
| | | dischargeWard: "内科二病区", |
| | | fillTime: "2024-01-14 16:20:10", |
| | | processStatus: 0, |
| | | questionnaireId: 1002, |
| | | }, |
| | | { |
| | | id: 3, |
| | | responsibilityDept: "普外科", |
| | | unsatisfactoryDetail: "术后换药不及时,伤口疼痛时没有及时处理", |
| | | patientName: "王先生", |
| | | gender: 1, |
| | | age: 52, |
| | | phone: "137****9012", |
| | | dischargeDept: "普外科", |
| | | dischargeWard: "外科一病区", |
| | | fillTime: "2024-01-13 09:15:45", |
| | | processStatus: 1, |
| | | questionnaireId: 1003, |
| | | }, |
| | | { |
| | | id: 4, |
| | | responsibilityDept: "骨科", |
| | | unsatisfactoryDetail: "康复指导不够专业,对恢复过程描述不清楚", |
| | | patientName: "刘女士", |
| | | gender: 0, |
| | | age: 65, |
| | | phone: "136****3456", |
| | | dischargeDept: "骨科", |
| | | dischargeWard: "外科二病区", |
| | | fillTime: "2024-01-12 14:40:30", |
| | | processStatus: 0, |
| | | questionnaireId: 1004, |
| | | }, |
| | | { |
| | | id: 5, |
| | | responsibilityDept: "妇产科", |
| | | unsatisfactoryDetail: |
| | | "产前检查排队时间过长,等待期间没有休息座位", |
| | | patientName: "陈女士", |
| | | gender: 0, |
| | | age: 28, |
| | | phone: "135****7890", |
| | | dischargeDept: "妇产科", |
| | | dischargeWard: "妇产科病区", |
| | | fillTime: "2024-01-11 11:25:15", |
| | | processStatus: 2, |
| | | questionnaireId: 1005, |
| | | }, |
| | | { |
| | | id: 6, |
| | | responsibilityDept: "儿科", |
| | | unsatisfactoryDetail: |
| | | "儿童用药剂量交代不清晰,用药注意事项没有说明", |
| | | patientName: "赵宝宝", |
| | | gender: 1, |
| | | age: 5, |
| | | phone: "134****1234", |
| | | dischargeDept: "儿科", |
| | | dischargeWard: "儿科病区", |
| | | fillTime: "2024-01-10 15:50:20", |
| | | processStatus: 0, |
| | | questionnaireId: 1006, |
| | | }, |
| | | { |
| | | id: 7, |
| | | responsibilityDept: "急诊科", |
| | | unsatisfactoryDetail: "急诊等待时间过长,病情没有得到及时评估", |
| | | patientName: "孙先生", |
| | | gender: 1, |
| | | age: 40, |
| | | phone: "133****5678", |
| | | dischargeDept: "急诊科", |
| | | dischargeWard: "急诊病区", |
| | | fillTime: "2024-01-09 10:15:40", |
| | | processStatus: 0, |
| | | questionnaireId: 1007, |
| | | }, |
| | | { |
| | | id: 8, |
| | | responsibilityDept: "呼吸内科", |
| | | unsatisfactoryDetail: "医生开药较多,费用较高,没有说明必要性", |
| | | patientName: "周女士", |
| | | gender: 0, |
| | | age: 55, |
| | | phone: "132****9012", |
| | | dischargeDept: "呼吸内科", |
| | | dischargeWard: "内科一病区", |
| | | fillTime: "2024-01-08 13:30:55", |
| | | processStatus: 1, |
| | | questionnaireId: 1008, |
| | | }, |
| | | ]; |
| | | this.total = this.exceptionList.length; |
| | | resolve(); |
| | | }, 500); |
| | | 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("获取科室列表失败"); |
| | | }); |
| | | } finally { |
| | | this.loading = false; |
| | | }, |
| | | 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(status) { |
| | | switch (status) { |
| | | case 0: |
| | | return "warning"; // 待处理 |
| | | case 1: |
| | | return "primary"; // 处理中 |
| | | case 2: |
| | | getStatusTagType(handleFlag) { |
| | | switch (handleFlag) { |
| | | case "0": |
| | | return "warning"; // 未处理 |
| | | case "1": |
| | | return "success"; // 已处理 |
| | | default: |
| | | return "info"; |
| | |
| | | }, |
| | | |
| | | // 获取状态文本 |
| | | getStatusText(status) { |
| | | switch (status) { |
| | | case 0: |
| | | return "待处理"; |
| | | case 1: |
| | | return "处理中"; |
| | | case 2: |
| | | 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; |
| | | } |
| | | |
| | | 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; |
| | | } |
| | | }, |
| | | |
| | |
| | | // 重置筛选 |
| | | handleResetFilter() { |
| | | this.filterParams = { |
| | | deptId: "", |
| | | status: "", |
| | | todeptcode: "", |
| | | handleFlag: "", |
| | | templateType: "", |
| | | scriptid: null, // 保留问题ID |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | }; |
| | | this.selectedExceptionIds = []; |
| | | this.loadExceptionList(); |
| | | }, |
| | | |
| | |
| | | this.$message.warning("请先选择要处理的异常反馈"); |
| | | return; |
| | | } |
| | | |
| | | // 重置批量处理表单 |
| | | this.batchProcessForm = { |
| | | handleFlag: "", |
| | | ccdepts: [], |
| | | handleresult: "", |
| | | handledesc: "", |
| | | }; |
| | | |
| | | this.batchDialogVisible = true; |
| | | }, |
| | | |
| | |
| | | // 查看详情 |
| | | handleViewDetail(row) { |
| | | this.selectedRecordId = row.id; |
| | | this.detailDialogTitle = `${row.patientName} - 异常反馈详情`; |
| | | this.selectedRecordData = row; |
| | | |
| | | // 生成弹框标题 |
| | | let title = "异常反馈详情"; |
| | | if (row.patdesc) { |
| | | const patientName = row.patdesc.split("|")[0]; |
| | | 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 = { |
| | | status: row.processStatus === 0 ? 1 : row.processStatus, |
| | | reportDepts: [], |
| | | remark: "", |
| | | 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) { |
| | | this.processing = true; |
| | | try { |
| | | // Mock API调用 |
| | | await new Promise((resolve) => setTimeout(resolve, 1000)); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | |
| | | this.$message.success("处理提交成功"); |
| | | this.processDialogVisible = false; |
| | | this.loadExceptionList(); |
| | | } finally { |
| | | this.processing = false; |
| | | } |
| | | 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, |
| | | // 将数组转换为逗号分隔的字符串 |
| | | 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) { |
| | | this.batchProcessing = true; |
| | | try { |
| | | // Mock API调用 |
| | | await new Promise((resolve) => setTimeout(resolve, 1500)); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | |
| | | this.$message.success( |
| | | `已批量处理 ${this.selectedExceptionIds.length} 条异常反馈` |
| | | 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 |
| | | ); |
| | | this.batchDialogVisible = false; |
| | | this.selectedExceptionIds = []; |
| | | this.loadExceptionList(); |
| | | } finally { |
| | | this.batchProcessing = false; |
| | | |
| | | // 并发处理当前批次 |
| | | 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; |
| | | } |
| | | }); |
| | | }, |
| | |
| | | handlePageChange(page) { |
| | | this.filterParams.pageNum = page; |
| | | this.loadExceptionList(); |
| | | }, |
| | | |
| | | // 文件上传相关方法 |
| | | handlePreview(file) { |
| | | console.log("预览文件:", file); |
| | | }, |
| | | |
| | | handleRemove(file, fileList) { |
| | | console.log("移除文件:", file, fileList); |
| | | }, |
| | | |
| | | beforeRemove(file) { |
| | | return this.$confirm(`确定移除 ${file.name}?`); |
| | | }, |
| | | |
| | | handleExceed(files, fileList) { |
| | | this.$message.warning( |
| | | `当前限制选择 3 个文件,本次选择了 ${files.length} 个文件,共选择了 ${ |
| | | files.length + fileList.length |
| | | } 个文件` |
| | | ); |
| | | }, |
| | | }, |
| | | }; |
| | |
| | | } |
| | | |
| | | .detail-content { |
| | | font-size: 13px; |
| | | color: #606266; |
| | | line-height: 1.5; |
| | | 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-item { |
| | | .patient-row { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 5px; |
| | | padding: 2px 0; |
| | | margin-bottom: 8px; |
| | | |
| | | .label { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | min-width: 40px; |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .value { |
| | | font-size: 13px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | text-align: right; |
| | | .patient-item { |
| | | flex: 1; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | 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; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .discharge-info { |
| | | .fill-info, |
| | | .handle-info { |
| | | font-size: 12px; |
| | | |
| | | .info-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | |
| | | padding: 2px 0; |
| | | |
| | | .label { |
| | | font-size: 12px; |
| | | color: #606266; |
| | | min-width: 50px; |
| | | } |
| | | |
| | | .value { |
| | | font-size: 13px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | text-align: right; |
| | | flex: 1; |
| | | |
| | | &.time { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | font-size: 11px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .no-data { |
| | | color: #909399; |
| | | font-style: italic; |
| | | font-size: 12px; |
| | | } |
| | | } |
| | | |
| | | .pagination-section { |