| | |
| | | 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 type="warning" icon="el-icon-back" @click="handleGoBack"> |
| | | 返回异常列表 |
| | | </el-button> |
| | | </div> |
| | |
| | | > |
| | | <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> |
| | |
| | | > |
| | | 筛选 |
| | | </el-button> |
| | | <el-button |
| | | icon="el-icon-refresh" |
| | | @click="handleResetFilter" |
| | | > |
| | | <el-button icon="el-icon-refresh" @click="handleResetFilter"> |
| | | 重置 |
| | | </el-button> |
| | | </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 type="selection" width="55" align="center" /> |
| | | |
| | | <el-table-column |
| | | label="序号" |
| | |
| | | |
| | | <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" |
| | | > |
| | | <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> |
| | | |
| | | <el-table-column |
| | | label="操作" |
| | | width="180" |
| | | width="210" |
| | | align="center" |
| | | fixed="right" |
| | | > |
| | |
| | | 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"> |
| | | <el-button @click="processDialogVisible = false">取消</el-button> |
| | | <el-button |
| | | type="primary" |
| | | @click="submitProcess" |
| | | :loading="processing" |
| | | > |
| | | <el-button type="primary" @click="submitProcess" :loading="processing"> |
| | | 提交处理 |
| | | </el-button> |
| | | </span> |
| | |
| | | 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', |
| | | name: "BatchProcess", |
| | | components: { |
| | | DetailsAnomaly, |
| | | }, |
| | | 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 |
| | | 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: [ |
| | | { required: true, message: '请选择处理状态', trigger: 'change' } |
| | | handleFlag: [ |
| | | { required: true, message: "请选择处理状态", trigger: "change" }, |
| | | ], |
| | | remark: [ |
| | | { required: true, message: '请输入处理备注', trigger: 'blur' }, |
| | | { min: 5, max: 500, message: '备注长度在 5 到 500 个字符', trigger: 'blur' } |
| | | ] |
| | | 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(); |
| | | } |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | 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: return 'success'; // 已处理 |
| | | default: return 'info'; |
| | | 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: return '已处理'; |
| | | default: return '未知'; |
| | | 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 |
| | | pageSize: 10, |
| | | }; |
| | | this.selectedExceptionIds = []; |
| | | this.loadExceptionList(); |
| | | }, |
| | | |
| | | // 处理选择变化 |
| | | handleSelectionChange(selection) { |
| | | this.selectedExceptionIds = selection.map(item => item.id); |
| | | this.selectedExceptionIds = selection.map((item) => item.id); |
| | | }, |
| | | |
| | | // 处理批量提交 |
| | | handleBatchSubmit() { |
| | | if (this.selectedExceptionIds.length === 0) { |
| | | this.$message.warning('请先选择要处理的异常反馈'); |
| | | this.$message.warning("请先选择要处理的异常反馈"); |
| | | return; |
| | | } |
| | | |
| | | // 重置批量处理表单 |
| | | this.batchProcessForm = { |
| | | handleFlag: "", |
| | | ccdepts: [], |
| | | handleresult: "", |
| | | handledesc: "", |
| | | }; |
| | | |
| | | this.batchDialogVisible = true; |
| | | }, |
| | | |
| | | // 返回异常列表 |
| | | handleGoBack() { |
| | | this.$router.push('/satisfaction/exception/list'); |
| | | this.$router.push("/satisfaction/exception/list"); |
| | | }, |
| | | |
| | | // 查看详情 |
| | | handleViewDetail(row) { |
| | | this.$router.push({ |
| | | path: '/satisfaction/exception/detail', |
| | | query: { |
| | | id: row.questionnaireId |
| | | this.selectedRecordId = row.id; |
| | | 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.batchDialogVisible = false; |
| | | this.selectedExceptionIds = []; |
| | | this.loadExceptionList(); |
| | | } finally { |
| | | this.batchProcessing = false; |
| | | 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; |
| | | } |
| | | }); |
| | | }, |
| | |
| | | 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} 个文件`); |
| | | } |
| | | } |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | |
| | | .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; |
| | | |
| | |
| | | } |
| | | |
| | | .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 { |