WXL
2025-12-29 cd574e3394883b79eac5d63f3b11bca852dbc7a0
src/views/business/course/components/DonationConfirmStage.vue
@@ -1,217 +1,685 @@
<template>
  <base-stage :stage-data="stageData" :case-info="caseInfo">
    <template #header>
      <el-alert
        :title="`捐献确认 - ${getStatusText()}`"
        :type="getAlertType()"
        :description="getAlertDescription()"
        show-icon
        :closable="false"
      />
    </template>
    <el-row :gutter="20" style="margin-top: 20px;">
      <el-col :span="8">
        <el-card>
          <div slot="header" class="card-header">
            <span>确认信息概览</span>
          </div>
          <div class="confirm-overview">
            <div class="overview-item">
              <span class="label">确认状态:</span>
              <el-tag :type="getStatusTag(confirmationDetails.status)">
                {{ getStatusText(confirmationDetails.status) }}
              </el-tag>
            </div>
            <div class="overview-item">
              <span class="label">确认时间:</span>
              <span>{{ formatTime(confirmationDetails.confirmTime) }}</span>
            </div>
            <div class="overview-item">
              <span class="label">确认方式:</span>
              <span>{{ confirmationDetails.method }}</span>
            </div>
            <div class="overview-item">
              <span class="label">协调员:</span>
              <span>{{ confirmationDetails.coordinator }}</span>
            </div>
          </div>
        </el-card>
      </el-col>
      <el-col :span="8">
        <el-card>
          <div slot="header" class="card-header">
            <span>家属同意情况</span>
          </div>
          <el-descriptions :column="1" size="small">
            <el-descriptions-item label="主要家属">
              {{ familyConsent.mainRelative }}
            </el-descriptions-item>
            <el-descriptions-item label="同意状态">
              <el-tag :type="familyConsent.consented ? 'success' : 'warning'">
                {{ familyConsent.consented ? '已同意' : '待确认' }}
              </el-tag>
            </el-descriptions-item>
            <el-descriptions-item label="签字时间">
              {{ formatTime(familyConsent.signTime) }}
            </el-descriptions-item>
            <el-descriptions-item label="关系证明">
              {{ familyConsent.relationshipProof }}
            </el-descriptions-item>
          </el-descriptions>
        </el-card>
      </el-col>
      <el-col :span="8">
        <el-card>
          <div slot="header" class="card-header">
            <span>法律文件</span>
          </div>
          <div class="document-list">
            <div v-for="doc in legalDocuments" :key="doc.name" class="document-item">
              <div class="doc-info">
                <i :class="doc.icon" style="color: #409EFF; margin-right: 8px;"></i>
                <span>{{ doc.name }}</span>
              </div>
              <el-tag :type="doc.status === 'completed' ? 'success' : 'warning'" size="small">
                {{ doc.status === 'completed' ? '已签署' : '待签署' }}
              </el-tag>
            </div>
          </div>
        </el-card>
      </el-col>
    </el-row>
    <el-card style="margin-top: 20px;">
      <div slot="header" class="card-header">
        <span>捐献意愿确认书</span>
  <div class="confirmation-detail">
    <el-card class="detail-card">
      <!-- 基础信息 -->
      <div slot="header" class="clearfix">
        <span class="detail-title">捐献确认基本信息</span>
        <el-button
          v-if="$route.query.confirm"
          type="primary"
          style="float: right; padding: 3px 0"
          @click="handleSave"
        >
          保存确认信息
        </el-button>
      </div>
      <div class="consent-content">
        <p>本人/家属确认,在充分了解器官捐献的相关信息后,自愿同意进行器官捐献。</p>
        <el-divider />
        <el-descriptions :column="2" border>
          <el-descriptions-item label="捐献者姓名">{{ caseInfo.donorName }}</el-descriptions-item>
          <el-descriptions-item label="捐献者身份证号">{{ confirmationDetails.idNumber }}</el-descriptions-item>
          <el-descriptions-item label="捐献器官类型">{{ confirmationDetails.organs }}</el-descriptions-item>
          <el-descriptions-item label="捐献用途">{{ confirmationDetails.purpose }}</el-descriptions-item>
          <el-descriptions-item label="签字人">{{ familyConsent.mainRelative }}</el-descriptions-item>
          <el-descriptions-item label="签字日期">{{ formatTime(familyConsent.signTime) }}</el-descriptions-item>
        </el-descriptions>
      </div>
      <el-form :model="form" ref="form" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="协调员1" prop="coordinator1">
              <el-input v-model="form.coordinator1" :readonly="!isEdit" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="协调员2" prop="coordinator2">
              <el-input v-model="form.coordinator2" :readonly="!isEdit" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="业务人员" prop="assignee">
              <el-input v-model="form.assignee" :readonly="!isEdit" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="确认状态" prop="confirmationStatus">
              <el-select
                v-model="form.confirmationStatus"
                :disabled="!isEdit"
                style="width: 100%"
              >
                <el-option label="未确认" value="0" />
                <el-option label="家属确认" value="1" />
                <el-option label="不同意捐献" value="2" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="确认时间" prop="confirmationTime">
              <el-date-picker
                v-model="form.confirmationTime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
                :disabled="!isEdit"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="家属意见备注" prop="familyRemark">
          <el-input
            type="textarea"
            :rows="3"
            v-model="form.familyRemark"
            :readonly="!isEdit"
            placeholder="记录家属的意见和沟通情况"
          />
        </el-form-item>
      </el-form>
    </el-card>
  </base-stage>
    <!-- 附件列表 -->
    <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="handleSaveAll"
          :loading="saveLoading"
        >
          保存所有附件
        </el-button>
      </div>
      <!-- 附件类型选项卡 -->
      <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">
                  <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="120"
                align="center"
                v-if="isEdit"
              >
                <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-delete"
                    style="color: #F56C6C;"
                    @click="handleRemoveAttachment(scope.row, type.value)"
                    >删除</el-button
                  >
                </template>
              </el-table-column>
              <el-table-column label="操作" width="80" align="center" v-else>
                <template slot-scope="scope">
                  <el-button
                    size="mini"
                    type="text"
                    icon="el-icon-view"
                    @click="handlePreview(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"
        :http-request="handleHttpRequest"
      >
        <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格式文件,单个文件不超过10MB
        </div>
      </el-upload>
      <span 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>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import BaseStage from './BaseStage.vue';
import {
  getConfirmationDetail,
  updateConfirmation
} from "./api/mockConfirmationApi";
export default {
  name: 'DonationConfirmStage',
  components: { BaseStage },
  props: {
    stageData: {
      type: Object,
      default: () => ({})
    },
    caseInfo: {
      type: Object,
      default: () => ({})
    }
  },
  name: "ConfirmationDetail",
  data() {
    return {
      confirmationDetails: {
        status: 'completed',
        confirmTime: '2023-12-03 11:00:00',
        method: '家属书面同意',
        coordinator: '赵协调员',
        idNumber: '110101199001011234',
        organs: '心脏、肝脏、肾脏、角膜',
        purpose: '临床移植'
      // 是否编辑模式
      isEdit: false,
      // 表单数据
      form: {
        id: undefined,
        caseNo: "",
        donorName: "",
        gender: "",
        age: "",
        diagnosis: "",
        hospitalName: "",
        coordinator1: "",
        coordinator2: "",
        assignee: "",
        confirmationStatus: "0",
        confirmationTime: "",
        familyRemark: ""
      },
      familyConsent: {
        mainRelative: '张三父亲',
        consented: true,
        signTime: '2023-12-03 10:45:00',
        relationshipProof: '户口本关系证明'
      },
      legalDocuments: [
        { name: '器官捐献同意书', icon: 'el-icon-document', status: 'completed' },
        { name: '家属关系证明', icon: 'el-icon-document', status: 'completed' },
        { name: '医疗免责声明', icon: 'el-icon-document', status: 'completed' },
        { name: '隐私保护协议', icon: 'el-icon-document', status: 'completed' }
      // 附件列表
      // 附件相关数据
      activeAttachmentType: "1",
      attachmentLoading: false,
      uploadDialogVisible: false,
      uploadLoading: false,
      saveLoading: false,
      tempFileList: [],
      currentUploadType: "",
      // 附件类型定义
      attachmentTypes: [
        { value: "1", label: "人体器官潜在捐献者登记表" },
        { value: "2", label: "人体器官捐献亲属确认登记表" },
        { value: "3", label: "捐献者及直系亲属身份证、户口簿相关证明" },
        { value: "4", label: "公民身故后人体器官(角膜)遗体捐献告知书" },
        { value: "5", label: "脑死亡判定知情同意书" },
        { value: "6", label: "心死亡判定知情同意书" }
      ],
      // 附件列表数据
      attachmentList: [
        // 模拟数据 - 实际项目中从接口获取
        {
          id: 1,
          type: "1",
          typeName: "人体器官潜在捐献者登记表",
          fileName: "潜在捐献者登记表_202512001.pdf",
          fileSize: 2548321,
          uploadTime: "2025-12-01 10:30:00",
          uploader: "张三",
          fileUrl: "/attachments/1.pdf"
        },
        {
          id: 2,
          type: "1",
          typeName: "人体器官潜在捐献者登记表",
          fileName: "补充说明.docx",
          fileSize: 512345,
          uploadTime: "2025-12-01 14:20:00",
          uploader: "李四",
          fileUrl: "/attachments/2.docx"
        }
      ]
    };
  },
  computed: {
    isEdit() {
      return this.$route.query.confirm === "true";
    },
    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.query.confirm === "true";
    if (id) {
      this.getDetail(id);
    }
    this.getAttachmentList();
  },
  methods: {
    getStatusText() {
      const status = this.stageData.status;
      return status === 'completed' ? '已完成' :
             status === 'in_progress' ? '进行中' : '未开始';
    // 获取详情
    getDetail(id) {
      getConfirmationDetail(id).then(response => {
        if (response.code === 200) {
          this.form = response.data;
        }
      });
    },
    getAlertType() {
      const status = this.stageData.status;
      return status === 'completed' ? 'success' :
             status === 'in_progress' ? 'warning' : 'info';
    // 获取附件列表
    getAttachmentList() {
      this.attachmentLoading = true;
      // 模拟附件数据
      this.attachmentList = [
        {
          id: 1,
          type: "1",
          typeName: "人体器官潜在捐献者登记表",
          fileName: "潜在捐献者登记表_202512001.pdf",
          uploadTime: "2025-12-01 10:30:00",
          uploader: "张三",
          fileSize: "2.5MB",
          fileUrl: "/attachments/1.pdf"
        },
        {
          id: 2,
          type: "2",
          typeName: "人体器官捐献亲属确认登记表",
          fileName: "亲属确认登记表_202512001.pdf",
          uploadTime: "2025-12-01 14:20:00",
          uploader: "李四",
          fileSize: "1.8MB",
          fileUrl: "/attachments/2.pdf"
        },
        {
          id: 3,
          type: "3",
          typeName: "捐献者及直系亲属身份证、户口簿相关证明",
          fileName: "身份证明_202512001.zip",
          uploadTime: "2025-12-01 16:45:00",
          uploader: "王五",
          fileSize: "5.2MB",
          fileUrl: "/attachments/3.zip"
        },
        {
          id: 4,
          type: "4",
          typeName: "公民身故后人体器官(角膜)遗体捐献告知书",
          fileName: "捐献告知书_202512001.pdf",
          uploadTime: "2025-12-02 09:15:00",
          uploader: "张三",
          fileSize: "1.2MB",
          fileUrl: "/attachments/4.pdf"
        },
        {
          id: 5,
          type: "5",
          typeName: "脑死亡判定知情同意书",
          fileName: "脑死亡判定同意书_202512001.pdf",
          uploadTime: "2025-12-02 11:30:00",
          uploader: "李四",
          fileSize: "0.8MB",
          fileUrl: "/attachments/5.pdf"
        },
        {
          id: 6,
          type: "6",
          typeName: "心死亡判定知情同意书",
          fileName: "心死亡判定同意书_202512001.pdf",
          uploadTime: "2025-12-02 13:20:00",
          uploader: "王五",
          fileSize: "0.9MB",
          fileUrl: "/attachments/6.pdf"
        }
      ];
      this.attachmentLoading = false;
    },
    getAlertDescription() {
      const status = this.stageData.status;
      return status === 'completed' ? '捐献确认流程已完成,所有法律文件已签署' :
             status === 'in_progress' ? '捐献确认流程正在进行中' : '等待开始捐献确认流程';
    // 下载附件
    handleDownload(row) {
      // 实际项目中这里调用文件下载接口
      this.$message.success(`下载文件: ${row.fileName}`);
      console.log("下载文件:", row.fileUrl);
    },
    getStatusTag(status) {
      const map = {
        'completed': 'success',
        'in_progress': 'warning',
        'pending': 'info'
      };
      return map[status] || 'info';
    // 保存确认信息
    handleSave() {
      this.$refs.form.validate(valid => {
        if (valid) {
          updateConfirmation(this.form).then(response => {
            if (response.code === 200) {
              this.$message.success("保存成功");
              this.isEdit = false;
              this.$router.push("/case/confirmation");
            }
          });
        }
      });
    },
 // 根据类型获取附件
    getAttachmentsByType(type) {
      return this.attachmentList.filter(item => item.type === type);
    },
    // 打开上传对话框
    openUploadDialog(type) {
      this.currentUploadType = type;
      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'
      ];
      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');
      if (!isTypeOk) {
        this.$message.error('文件格式不支持,请上传pdf、jpg、png、doc或docx格式文件');
        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;
    },
    // 自定义上传请求
    handleHttpRequest(options) {
      // 模拟上传过程
      return new Promise((resolve, reject) => {
        this.uploadLoading = true;
        // 模拟上传延迟
        setTimeout(() => {
          const newAttachment = {
            id: Date.now(),
            type: this.currentUploadType,
            typeName: this.getCurrentTypeLabel,
            fileName: options.file.name,
            fileSize: options.file.size,
            uploadTime: new Date().toISOString(),
            uploader: '当前用户', // 实际项目中从用户信息获取
            fileUrl: URL.createObjectURL(options.file) // 临时URL,实际项目中为服务器返回的URL
          };
          this.attachmentList.push(newAttachment);
          this.uploadLoading = false;
          resolve({ code: 200, data: newAttachment });
        }, 1500);
      });
    },
    // 提交上传
    async submitUpload() {
      if (this.tempFileList.length === 0) {
        this.$message.warning('请先选择要上传的文件');
        return;
      }
      try {
        // 依次上传所有文件
        for (const file of this.tempFileList) {
          await this.$refs.uploadRef.submit();
        }
        this.$message.success('文件上传成功');
        this.uploadDialogVisible = false;
        this.tempFileList = [];
      } catch (error) {
        this.$message.error('文件上传失败');
        console.error('上传失败:', error);
      }
    },
    // 删除附件
    handleRemoveAttachment(attachment, type) {
      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('附件删除成功');
          // 实际项目中调用删除接口
          // this.deleteAttachment(attachment.id);
        }
      }).catch(() => {});
    },
    // 预览附件
    handlePreview(attachment) {
      // 实际项目中根据文件类型调用不同的预览方式
      if (attachment.fileName.endsWith('.pdf')) {
        // 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('该文件类型暂不支持在线预览,请下载后查看');
      }
    },
    // 保存所有附件信息
    handleSaveAll() {
      this.saveLoading = true;
      // 模拟保存过程
      setTimeout(() => {
        this.$message.success('附件信息保存成功');
        this.saveLoading = false;
        // 实际项目中调用保存接口
        // this.saveAttachments();
      }, 1000);
    },
    // 文件大小格式化
    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>
.confirm-overview {
  padding: 10px 0;
.confirmation-detail {
  padding: 20px;
}
.overview-item {
.detail-card {
  margin-bottom: 20px;
}
.attachment-card {
  margin-bottom: 20px;
}
.detail-title {
  font-size: 16px;
  font-weight: bold;
}
.fixed-width .el-button {
  margin: 0 5px;
}
.confirmation-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: space-between;
  align-items: center;
  margin-bottom: 15px;
  padding: 8px 0;
  border-bottom: 1px solid #f0f0f0;
}
.overview-item .label {
  color: #606266;
  font-weight: 500;
.upload-title {
  font-size: 14px;
  font-weight: 600;
  color: #303133;
}
.document-list {
  padding: 10px 0;
}
.document-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
  padding: 8px 12px;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
}
.doc-info {
.file-info {
  display: flex;
  align-items: center;
}
.consent-content {
  padding: 20px;
  line-height: 1.6;
.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>