WXL
2025-12-29 3b7fcf5ea471f6cb388f86d0732b8ece47a3cefc
src/views/business/course/components/DeathJudgmentStage.vue
@@ -1,206 +1,643 @@
<template>
  <base-stage :stage-data="stageData" :case-info="caseInfo">
    <template #header>
      <el-alert
        title="死亡判定阶段"
        :type="stageData.status === 'completed' ? 'success' : 'warning'"
        :description="getAlertDescription()"
        show-icon
        :closable="false"
      />
    </template>
    <el-row :gutter="20" style="margin-top: 20px;">
      <el-col :span="12">
        <el-card>
          <div slot="header" class="card-header">
            <span>判定基本信息</span>
          </div>
          <el-descriptions :column="1" border>
            <el-descriptions-item label="判定类型">
              <el-tag type="primary">脑死亡判定</el-tag>
            </el-descriptions-item>
            <el-descriptions-item label="判定时间">
              {{ formatTime(judgmentDetails.judgmentTime) }}
            </el-descriptions-item>
            <el-descriptions-item label="判定医生一">
              {{ judgmentDetails.doctor1 }}
            </el-descriptions-item>
            <el-descriptions-item label="判定医生二">
              {{ judgmentDetails.doctor2 }}
            </el-descriptions-item>
            <el-descriptions-item label="判定结果">
              <el-tag :type="judgmentDetails.result ? 'success' : 'warning'">
                {{ judgmentDetails.result ? '脑死亡确认' : '判定中' }}
              </el-tag>
            </el-descriptions-item>
          </el-descriptions>
        </el-card>
      </el-col>
      <el-col :span="12">
        <el-card>
          <div slot="header" class="card-header">
            <span>判定流程记录</span>
          </div>
          <el-steps direction="vertical" :active="judgmentSteps.active" space="100px">
            <el-step
              v-for="step in judgmentSteps.steps"
              :key="step.title"
              :title="step.title"
              :description="step.description"
              :status="step.status"
            />
          </el-steps>
        </el-card>
      </el-col>
    </el-row>
    <el-card style="margin-top: 20px;">
      <div slot="header" class="card-header">
        <span>判定详细记录</span>
  <div class="death-judgment-detail">
    <el-card class="detail-card">
      <!-- 基础信息 -->
      <div slot="header" class="clearfix">
        <span class="detail-title">死亡判定基本信息</span>
      </div>
      <el-table :data="judgmentRecords" border>
        <el-table-column label="检查项目" prop="item" width="180" />
        <el-table-column label="检查方法" prop="method" width="150" />
        <el-table-column label="检查结果" prop="result" width="120">
          <template slot-scope="scope">
            <el-tag :type="scope.row.result === '符合' ? 'success' : 'warning'">
              {{ scope.row.result }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="检查时间" width="160">
          <template slot-scope="scope">
            {{ formatTime(scope.row.time) }}
          </template>
        </el-table-column>
        <el-table-column label="检查医生" prop="doctor" width="120" />
        <el-table-column label="备注" prop="remark" min-width="200" />
      </el-table>
      <el-form :model="form" ref="form" :rules="rules" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="死亡原因" prop="deathReason">
              <el-select
                v-model="form.deathReason"
                :disabled="!isEdit"
                style="width: 100%"
              >
                <el-option label="脑死亡" value="brain_death" />
                <el-option label="心死亡" value="heart_death" />
                <el-option label="其他" value="other" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="死亡时间" prop="deathTime">
              <el-date-picker
                v-model="form.deathTime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
                :disabled="!isEdit"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="判定医生一" prop="judgmentDoctor">
              <el-input v-model="form.judgmentDoctorone" :readonly="!isEdit" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="判定医生二" prop="judgmentDoctor">
              <el-input v-model="form.judgmentDoctortwo" :readonly="!isEdit" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="8">
            <el-form-item label="登记人" prop="registrant">
              <el-input v-model="form.registrant" :readonly="!isEdit" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="死亡判定说明" prop="judgmentDescription">
          <el-input
            type="textarea"
            :rows="3"
            v-model="form.judgmentDescription"
            :readonly="!isEdit"
            placeholder="详细记录死亡判定过程和依据"
          />
        </el-form-item>
      </el-form>
    </el-card>
    <template #footer>
      <div class="action-buttons" style="margin-top: 20px; text-align: center;">
        <el-button type="primary" @click="handleViewCertificate">
          查看死亡证明
        </el-button>
        <el-button type="success" @click="handleConfirmJudgment">
          确认判定结果
    <!-- 评估表附件 -->
    <el-card class="attachment-card">
      <div slot="header" class="clearfix">
        <span class="detail-title">死亡判定评估表附件</span>
        <el-button
          v-if="isEdit"
          type="primary"
          size="mini"
          @click="openUploadDialog"
          :loading="uploadLoading"
        >
          上传附件
        </el-button>
      </div>
    </template>
  </base-stage>
      <!-- 附件类型选项卡 -->
      <el-tabs v-model="activeAttachmentType" type="card">
        <el-tab-pane
          v-for="type in attachmentTypes"
          :key="type.value"
          :label="type.label"
          :name="type.value"
        >
          <div class="attachment-upload-section">
            <div class="upload-header">
              <span class="upload-title">{{ type.label }}</span>
              <el-tooltip content="点击上传该类型评估表" placement="top">
                <el-button
                  size="mini"
                  type="primary"
                  icon="el-icon-plus"
                  @click="openUploadDialog(type.value)"
                  :disabled="!isEdit"
                >
                  添加评估表
                </el-button>
              </el-tooltip>
            </div>
            <!-- 附件列表 -->
            <el-table
              :data="getAttachmentsByType(type.value)"
              v-loading="attachmentLoading"
              style="width: 100%; margin-top: 15px;"
            >
              <el-table-column label="文件名称" min-width="200">
                <template slot-scope="scope">
                  <div class="file-info">
                    <i
                      class="el-icon-document"
                      style="margin-right: 8px; color: #409EFF;"
                    ></i>
                    <span>{{ scope.row.fileName }}</span>
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="文件类型" width="100" align="center">
                <template slot-scope="scope">
                  <el-tag size="small">{{
                    getFileType(scope.row.fileName)
                  }}</el-tag>
                </template>
              </el-table-column>
              <el-table-column label="文件大小" width="100" align="center">
                <template slot-scope="scope">
                  <span>{{ formatFileSize(scope.row.fileSize) }}</span>
                </template>
              </el-table-column>
              <el-table-column label="上传时间" width="160" align="center">
                <template slot-scope="scope">
                  <span>{{ parseTime(scope.row.uploadTime) }}</span>
                </template>
              </el-table-column>
              <el-table-column label="上传人" width="100" align="center">
                <template slot-scope="scope">
                  <span>{{ scope.row.uploader }}</span>
                </template>
              </el-table-column>
              <el-table-column label="操作" width="180" align="center">
                <template slot-scope="scope">
                  <el-button
                    size="mini"
                    type="text"
                    icon="el-icon-view"
                    @click="handlePreview(scope.row)"
                    >预览</el-button
                  >
                  <el-button
                    size="mini"
                    type="text"
                    icon="el-icon-download"
                    @click="handleDownload(scope.row)"
                    >下载</el-button
                  >
                  <el-button
                    v-if="isEdit"
                    size="mini"
                    type="text"
                    icon="el-icon-delete"
                    style="color: #F56C6C;"
                    @click="handleRemoveAttachment(scope.row)"
                    >删除</el-button
                  >
                </template>
              </el-table-column>
            </el-table>
            <div
              v-if="getAttachmentsByType(type.value).length === 0"
              class="empty-attachment"
            >
              <el-empty
                description="暂无评估表附件"
                :image-size="80"
              ></el-empty>
            </div>
          </div>
        </el-tab-pane>
      </el-tabs>
    </el-card>
    <!-- 上传对话框 -->
    <el-dialog
      :title="`上传${getCurrentTypeLabel}评估表`"
      :visible.sync="uploadDialogVisible"
      width="500px"
      :close-on-click-modal="false"
    >
      <el-upload
        ref="uploadRef"
        class="upload-demo"
        drag
        action="#"
        multiple
        :file-list="tempFileList"
        :before-upload="beforeUpload"
        :on-change="handleFileChange"
        :on-remove="handleTempRemove"
        :auto-upload="false"
      >
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">
          将评估表文件拖到此处,或<em>点击上传</em>
        </div>
        <div class="el-upload__tip" slot="tip">
          支持上传pdf、jpg、png、doc、docx、xls、xlsx格式文件,单个文件不超过10MB
        </div>
      </el-upload>
      <div slot="footer" class="dialog-footer">
        <el-button @click="uploadDialogVisible = false">取消</el-button>
        <el-button
          type="primary"
          @click="submitUpload"
          :loading="uploadLoading"
          :disabled="tempFileList.length === 0"
        >
          确认上传
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import BaseStage from './BaseStage.vue';
import {
  getDeathJudgmentDetail,
  updateDeathJudgment
} from "./api/mockDeathJudgmentApi";
export default {
  name: 'DeathJudgmentStage',
  components: { BaseStage },
  props: {
    stageData: {
      type: Object,
      default: () => ({})
    },
    caseInfo: {
      type: Object,
      default: () => ({})
    }
  },
  name: "DeathJudgmentDetail",
  data() {
    return {
      judgmentDetails: {
        judgmentTime: '2023-12-03 09:15:00',
        doctor1: '张主任',
        doctor2: '王医生',
        result: true,
        certificateNo: 'SW20231203001'
      // 是否编辑模式
      isEdit: false,
      // 保存加载状态
      saveLoading: false,
      // 表单数据
      form: {
        id: undefined,
        hospitalNo: "",
        donorName: "",
        gender: "",
        age: "",
        diagnosis: "",
        deathReason: "",
        deathTime: "",
        judgmentDoctor: "",
        judgmentDescription: "",
        registrant: "",
        registrationTime: ""
      },
      judgmentSteps: {
        active: 4,
        steps: [
          {
            title: '初步临床检查',
            description: '完成自主呼吸测试',
            status: 'finish'
          },
          {
            title: '脑干反射测试',
            description: '各项反射测试完成',
            status: 'finish'
          },
          {
            title: '确认性检查',
            description: '脑电图检查完成',
            status: 'finish'
          },
          {
            title: '最终判定',
            description: '两位医生独立判定',
            status: 'finish'
          }
      // 表单验证规则
      rules: {
        donorName: [
          { required: true, message: "捐献者姓名不能为空", trigger: "blur" }
        ],
        deathReason: [
          { required: true, message: "死亡原因不能为空", trigger: "change" }
        ],
        deathTime: [
          { required: true, message: "死亡时间不能为空", trigger: "change" }
        ],
        judgmentDoctor: [
          { required: true, message: "判定医生不能为空", trigger: "blur" }
        ]
      },
      judgmentRecords: [
        {
          item: '自主呼吸测试',
          method: '呼吸暂停试验',
          result: '符合',
          time: '2023-12-03 08:30:00',
          doctor: '张主任',
          remark: '无自主呼吸'
        },
        {
          item: '瞳孔对光反射',
          method: '光刺激测试',
          result: '符合',
          time: '2023-12-03 08:45:00',
          doctor: '王医生',
          remark: '瞳孔固定,对光反射消失'
        },
        {
          item: '脑干听觉诱发电位',
          method: 'BAEP检查',
          result: '符合',
          time: '2023-12-03 09:00:00',
          doctor: '李医生',
          remark: '脑干功能丧失'
        }
      ]
      // 附件相关数据
      activeAttachmentType: "1",
      attachmentLoading: false,
      uploadDialogVisible: false,
      uploadLoading: false,
      tempFileList: [],
      currentUploadType: "",
      // 评估表类型定义
      attachmentTypes: [
        { value: "1", label: "脑死亡判定表" },
        { value: "2", label: "脑电图评估表" },
        { value: "3", label: "短潜伏期体感诱发电位评估表" },
        { value: "4", label: "经颅多普勒超声评估记录" },
        { value: "5", label: "卫健委脑损伤质控中心 - 临床综合评估表" },
        { value: "6", label: "UW评分表" },
        { value: "7", label: "心死亡判定表" }
      ],
      // 附件列表数据
      attachmentList: []
    };
  },
  computed: {
    getCurrentTypeLabel() {
      const type = this.attachmentTypes.find(
        t => t.value === this.currentUploadType
      );
      return type ? type.label : "";
    }
  },
  created() {
    const id = this.$route.query.id;
    this.isEdit =
      this.$route.path.includes("/edit") || this.$route.path.includes("/add");
    if (id && !this.$route.path.includes("/add")) {
      this.getDetail(id);
    } else if (this.$route.path.includes("/add")) {
      this.generateHospitalNo();
    }
    this.getAttachmentList();
  },
  methods: {
    getAlertDescription() {
      if (this.stageData.status === 'completed') {
        return '脑死亡判定已完成,符合器官捐献条件';
      } else if (this.stageData.status === 'in_progress') {
        return '死亡判定流程正在进行中';
      }
      return '等待开始死亡判定流程';
    // 生成住院号
    generateHospitalNo() {
      // 模拟生成住院号:D + 时间戳后6位
      const timestamp = Date.now().toString();
      this.form.hospitalNo = "D" + timestamp.slice(-6);
    },
    handleViewCertificate() {
      this.$message.info('查看死亡证明功能');
    },
    handleConfirmJudgment() {
      this.$confirm('确认死亡判定结果吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.$message.success('死亡判定结果已确认');
    // 获取详情
    getDetail(id) {
      getDeathJudgmentDetail(id).then(response => {
        if (response.code === 200) {
          this.form = response.data;
        }
      });
    },
    // 获取附件列表
    getAttachmentList() {
      this.attachmentLoading = true;
      // 模拟附件数据 - 实际项目中从接口获取
      setTimeout(() => {
        this.attachmentList = [
          {
            id: 1,
            type: "1",
            typeName: "脑死亡判定表",
            fileName: "脑死亡判定表_202512001.pdf",
            fileSize: 2548321,
            uploadTime: "2025-12-01 10:30:00",
            uploader: "张医生",
            fileUrl: "/attachments/brain_death_1.pdf"
          },
          {
            id: 2,
            type: "2",
            typeName: "脑电图评估表",
            fileName: "脑电图评估表_202512001.docx",
            fileSize: 512345,
            uploadTime: "2025-12-01 14:20:00",
            uploader: "李医生",
            fileUrl: "/attachments/eeg_1.docx"
          }
        ];
        this.attachmentLoading = false;
      }, 500);
    },
    // 根据类型获取附件
    getAttachmentsByType(type) {
      return this.attachmentList.filter(item => item.type === type);
    },
    // 获取文件类型
    getFileType(fileName) {
      const ext = fileName
        .split(".")
        .pop()
        .toLowerCase();
      const typeMap = {
        pdf: "PDF",
        doc: "DOC",
        docx: "DOCX",
        xls: "XLS",
        xlsx: "XLSX",
        jpg: "JPG",
        jpeg: "JPEG",
        png: "PNG"
      };
      return typeMap[ext] || ext.toUpperCase();
    },
    // 打开上传对话框
    openUploadDialog(type = null) {
      this.currentUploadType = type || this.activeAttachmentType;
      this.tempFileList = [];
      this.uploadDialogVisible = true;
      this.$nextTick(() => {
        if (this.$refs.uploadRef) {
          this.$refs.uploadRef.clearFiles();
        }
      });
    },
    // 上传前校验
    beforeUpload(file) {
      const allowedTypes = [
        "application/pdf",
        "image/jpeg",
        "image/png",
        "application/msword",
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        "application/vnd.ms-excel",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
      ];
      const maxSize = 10 * 1024 * 1024; // 10MB
      // 校验文件类型
      const isTypeOk =
        allowedTypes.includes(file.type) ||
        file.name.endsWith(".pdf") ||
        file.name.endsWith(".jpg") ||
        file.name.endsWith(".jpeg") ||
        file.name.endsWith(".png") ||
        file.name.endsWith(".doc") ||
        file.name.endsWith(".docx") ||
        file.name.endsWith(".xls") ||
        file.name.endsWith(".xlsx");
      if (!isTypeOk) {
        this.$message.error(
          "文件格式不支持,请上传pdf、jpg、png、doc、docx、xls或xlsx格式文件"
        );
        return false;
      }
      // 校验文件大小
      if (file.size > maxSize) {
        this.$message.error("文件大小不能超过10MB");
        return false;
      }
      return true;
    },
    // 文件选择变化
    handleFileChange(file, fileList) {
      this.tempFileList = fileList;
    },
    // 移除临时文件
    handleTempRemove(file, fileList) {
      this.tempFileList = fileList;
    },
    // 提交上传
    async submitUpload() {
      if (this.tempFileList.length === 0) {
        this.$message.warning("请先选择要上传的文件");
        return;
      }
      this.uploadLoading = true;
      try {
        // 模拟上传过程 - 实际项目中调用上传接口
        for (const file of this.tempFileList) {
          const newAttachment = {
            id: Date.now() + Math.random(),
            type: this.currentUploadType,
            typeName: this.getCurrentTypeLabel,
            fileName: file.name,
            fileSize: file.size,
            uploadTime: new Date().toISOString(),
            uploader: "当前用户",
            fileUrl: URL.createObjectURL(file.raw)
          };
          this.attachmentList.push(newAttachment);
        }
        this.$message.success("文件上传成功");
        this.uploadDialogVisible = false;
        this.tempFileList = [];
      } catch (error) {
        this.$message.error("文件上传失败");
        console.error("上传失败:", error);
      } finally {
        this.uploadLoading = false;
      }
    },
    // 删除附件
    handleRemoveAttachment(attachment) {
      this.$confirm("确定要删除这个评估表附件吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
          const index = this.attachmentList.findIndex(
            item => item.id === attachment.id
          );
          if (index !== -1) {
            this.attachmentList.splice(index, 1);
            this.$message.success("评估表删除成功");
          }
        })
        .catch(() => {});
    },
    // 预览附件
    handlePreview(attachment) {
      if (attachment.fileName.endsWith(".pdf")) {
        window.open(attachment.fileUrl, "_blank");
      } else if (attachment.fileName.match(/\.(jpg|jpeg|png)$/i)) {
        this.$alert(
          `<img src="${attachment.fileUrl}" style="max-width: 100%;" alt="${attachment.fileName}">`,
          "图片预览",
          {
            dangerouslyUseHTMLString: true,
            customClass: "image-preview-dialog"
          }
        );
      } else {
        this.$message.info("该文件类型暂不支持在线预览,请下载后查看");
      }
    },
    // 下载附件
    handleDownload(attachment) {
      // 实际项目中调用下载接口
      const link = document.createElement("a");
      link.href = attachment.fileUrl;
      link.download = attachment.fileName;
      link.click();
      this.$message.success(`开始下载: ${attachment.fileName}`);
    },
    // 保存信息
    handleSave() {
      this.$refs.form.validate(valid => {
        if (valid) {
          this.saveLoading = true;
          // 模拟保存过程
          updateDeathJudgment(this.form)
            .then(response => {
              if (response.code === 200) {
                this.$message.success("保存成功");
                if (this.$route.path.includes("/add")) {
                  this.$router.push("/case/deathJudgment");
                } else {
                  this.isEdit = false;
                }
              }
            })
            .catch(error => {
              console.error("保存失败:", error);
              this.$message.error("保存失败");
            })
            .finally(() => {
              this.saveLoading = false;
            });
        }
      });
    },
    // 文件大小格式化
    formatFileSize(size) {
      if (size === 0) return "0 B";
      const k = 1024;
      const sizes = ["B", "KB", "MB", "GB"];
      const i = Math.floor(Math.log(size) / Math.log(k));
      return parseFloat((size / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
    },
    // 时间格式化
    parseTime(time) {
      if (!time) return "";
      const date = new Date(time);
      return `${date.getFullYear()}-${(date.getMonth() + 1)
        .toString()
        .padStart(2, "0")}-${date
        .getDate()
        .toString()
        .padStart(2, "0")} ${date
        .getHours()
        .toString()
        .padStart(2, "0")}:${date
        .getMinutes()
        .toString()
        .padStart(2, "0")}`;
    }
  }
};
</script>
<style scoped>
.action-buttons {
.death-judgment-detail {
  padding: 20px;
}
.detail-card {
  margin-bottom: 20px;
}
.attachment-card {
  margin-bottom: 20px;
}
.detail-title {
  font-size: 16px;
  font-weight: bold;
}
.attachment-upload-section {
  padding: 10px;
}
.upload-header {
  display: flex;
  justify-content: center;
  gap: 15px;
  margin-top: 20px;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
}
.upload-title {
  font-size: 14px;
  font-weight: 600;
  color: #303133;
}
.file-info {
  display: flex;
  align-items: center;
}
.empty-attachment {
  text-align: center;
  padding: 40px 0;
  color: #909399;
}
/* 图片预览对话框样式 */
:deep(.image-preview-dialog) {
  width: auto;
  max-width: 90vw;
}
:deep(.image-preview-dialog .el-message-box__content) {
  text-align: center;
}
</style>