WXL
2025-12-29 2431480f5859ef40dfdf0eb19e1ba6ddebbd9ef2
src/views/business/course/components/EthicalReviewStage.vue
@@ -1,206 +1,1487 @@
<template>
  <base-stage :stage-data="stageData" :case-info="caseInfo">
    <template #header>
      <el-alert
        title="伦理审查阶段"
        :type="getAlertType()"
        :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 class="ethics-review-detail">
    <el-card class="detail-card">
      <!-- 基础信息 -->
      <div slot="header" class="clearfix">
        <span class="detail-title">伦理审查基本信息</span>
          </div>
          <el-descriptions :column="1" border>
            <el-descriptions-item label="委员会名称">
              {{ reviewCommittee.name }}
            </el-descriptions-item>
            <el-descriptions-item label="审查会议时间">
              {{ formatTime(reviewCommittee.meetingTime) }}
            </el-descriptions-item>
            <el-descriptions-item label="参会委员">
              {{ reviewCommittee.members.length }} 人
            </el-descriptions-item>
            <el-descriptions-item label="审查结论">
              <el-tag :type="reviewCommittee.conclusion ? 'success' : 'warning'">
                {{ reviewCommittee.conclusion ? '审查通过' : '审查中' }}
              </el-tag>
            </el-descriptions-item>
            <el-descriptions-item label="主席签字">
              {{ reviewCommittee.chairman }}
            </el-descriptions-item>
          </el-descriptions>
        </el-card>
      <el-form :model="form" ref="form" :rules="rules" label-width="120px">
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="伦理结论" prop="ethicsConclusion">
              <el-select v-model="form.ethicsConclusion" style="width: 100%">
                <el-option label="审查中" value="reviewing" />
                <el-option label="同意" value="approved" />
                <el-option
                  label="修改后同意"
                  value="approved_with_modifications"
                />
                <el-option label="修改后重审" value="re-review" />
                <el-option label="不同意" value="disapproved" />
                <el-option label="终止审查" value="terminated" />
              </el-select>
            </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-card>
          <div slot="header" class="card-header">
            <span>审查流程进度</span>
          </div>
          <el-steps direction="vertical" :active="reviewProgress.active" space="80px">
            <el-step
              v-for="step in reviewProgress.steps"
              :key="step.title"
              :title="step.title"
              :description="step.description"
              :status="step.status"
          <el-col :span="8">
            <el-form-item label="审查时间" prop="reviewTime">
              <el-date-picker
                v-model="form.reviewTime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                style="width: 100%"
            />
          </el-steps>
        </el-card>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="登记人" prop="registrant">
              <el-input v-model="form.registrant" />
            </el-form-item>
      </el-col>
    </el-row>
    <el-card style="margin-top: 20px;">
      <div slot="header" class="card-header">
        <span>委员审查意见</span>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="伦理意见" prop="ethicsOpinion">
              <el-input
                type="textarea"
                :rows="3"
                v-model="form.ethicsOpinion"
                placeholder="请输入伦理审查意见"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="登记时间" prop="registrationTime">
          <el-date-picker
            v-model="form.registrationTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            style="width: 100%"
          />
        </el-form-item>
      </el-form>
    </el-card>
    <!-- 附件上传 -->
    <el-card class="attachment-card">
      <div slot="header" class="clearfix">
        <span class="detail-title">相关附件</span>
        <el-button type="primary" size="mini" @click="handleUploadAttachment">
          上传附件
        </el-button>
      </div>
      <el-table :data="reviewComments" border>
        <el-table-column label="委员姓名" prop="memberName" width="120" />
        <el-table-column label="专业领域" prop="specialty" width="120" />
        <el-table-column label="审查意见" prop="comment" min-width="200" />
        <el-table-column label="投票结果" width="100">
      <el-table :data="attachments" style="width: 100%">
        <el-table-column label="文件名称" min-width="200">
          <template slot-scope="scope">
            <el-tag :type="scope.row.vote === '同意' ? 'success' : 'danger'">
              {{ scope.row.vote }}
            </el-tag>
            <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="160">
        <el-table-column label="文件类型" width="100" align="center">
          <template slot-scope="scope">
            {{ formatTime(scope.row.reviewTime) }}
            <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="120" align="center">
          <template slot-scope="scope">
            <el-button
              size="mini"
              type="text"
              icon="el-icon-view"
              @click="handlePreviewAttachment(scope.row)"
              >预览</el-button
            >
            <el-button
              size="mini"
              type="text"
              icon="el-icon-download"
              @click="handleDownloadAttachment(scope.row)"
              >下载</el-button
            >
          </template>
        </el-table-column>
      </el-table>
    </el-card>
    <el-card style="margin-top: 20px;">
      <div slot="header" class="card-header">
        <span>审查决议文件</span>
        <el-button type="primary" size="small" @click="handleViewResolution">
          查看决议文件
    <!-- 专家审查情况 -->
    <el-card class="expert-card">
      <div slot="header" class="clearfix">
        <span class="detail-title"
          >专家审查情况 (18位专家 + 1位主任专家)</span
        >
        <div style="float: right;">
          <el-button
            size="mini"
            type="primary"
            @click="handleSendToNormalExperts"
            :disabled="!canSendToNormalExperts"
          >
            发送专家
          </el-button>
          <el-button
            size="mini"
            type="success"
            @click="handleSendToChiefExpert"
            :disabled="!canSendToChiefExpert"
          >
            发送主任专家
          </el-button>
          <el-button
            size="mini"
            type="warning"
            @click="handleBatchSend"
            :disabled="!canBatchSend"
          >
            批量发送
        </el-button>
      </div>
      <div class="resolution-content">
        <p><strong>伦理审查决议:</strong></p>
        <p>{{ resolutionContent }}</p>
        <el-divider />
        <div class="signature-area">
          <p>伦理委员会主席:{{ reviewCommittee.chairman }}</p>
          <p>日期:{{ formatTime(reviewCommittee.meetingTime) }}</p>
        </div>
 <!-- 专家统计信息 -->
      <div
        class="expert-stats"
        style="margin-top: 20px; padding: 15px; background: #f5f7fa; border-radius: 4px;"
      >
        <el-row :gutter="20">
          <el-col :span="6">
            <div class="stat-item">
              <span class="stat-label">专家已同意:</span>
              <span class="stat-value">{{ approvedNormalExperts }}/18</span>
      </div>
    </el-card>
  </base-stage>
</template>
          </el-col>
          <el-col :span="6">
            <div class="stat-item">
              <span class="stat-label">主任专家状态:</span>
              <span class="stat-value">{{ chiefExpertStatus }}</span>
            </div>
          </el-col>
          <el-col :span="6">
            <div class="stat-item">
              <span class="stat-label">总完成进度:</span>
              <span class="stat-value">{{ completionRate }}%</span>
            </div>
          </el-col>
          <el-col :span="6">
            <div class="stat-item">
              <span class="stat-label">审查结果:</span>
              <span class="stat-value">
                <el-tag :type="overallConclusionFilter">
                  {{ overallConclusionText }}
                </el-tag>
              </span>
            </div>
          </el-col>
        </el-row>
      </div>
      <!-- 专家审查表格 -->
      <el-table
        :data="expertReviews"
        v-loading="expertLoading"
        style="width: 100%"
        heiht="300"
        :row-class-name="getExpertRowClassName"
      >
        <el-table-column label="序号" width="60" align="center" type="index" />
        <el-table-column
          label="专家姓名"
          width="120"
          align="center"
          fixed="left"
        >
          <template slot-scope="scope">
            <span>{{ scope.row.expertName }}</span>
            <el-tag
              v-if="scope.row.isChief"
              size="mini"
              type="danger"
              style="margin-left: 5px;"
              >主任</el-tag
            >
          </template>
        </el-table-column>
        <el-table-column label="专家类型" width="100" align="center">
          <template slot-scope="scope">
            <span :class="scope.row.isChief ? 'chief-expert' : 'normal-expert'">
              {{ scope.row.isChief ? "主任专家" : "专家" }}
            </span>
          </template>
        </el-table-column>
        <el-table-column label="审查状态" width="100" align="center">
          <template slot-scope="scope">
            <el-tag :type="statusFilter(scope.row.reviewStatus)" size="small">
              {{ statusTextFilter(scope.row.reviewStatus) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="专家结论" width="120" align="center">
          <template slot-scope="scope">
            <el-tag
              v-if="scope.row.expertConclusion"
              :type="conclusionFilter(scope.row.expertConclusion)"
              size="small"
            >
              {{ conclusionTextFilter(scope.row.expertConclusion) }}
            </el-tag>
            <span v-else class="no-data">-</span>
          </template>
        </el-table-column>
        <el-table-column label="审查意见" min-width="200" show-overflow-tooltip>
          <template slot-scope="scope">
            <span :class="{ 'expert-opinion': scope.row.expertOpinion }">
              {{ scope.row.expertOpinion || "暂无意见" }}
            </span>
          </template>
        </el-table-column>
        <el-table-column label="审查时间" width="160" align="center">
          <template slot-scope="scope">
            <span>{{
              scope.row.reviewTime ? parseTime(scope.row.reviewTime) : "未审查"
            }}</span>
          </template>
        </el-table-column>
        <el-table-column label="发送时间" width="160" align="center">
          <template slot-scope="scope">
            <span>{{
              scope.row.reviewTime ? parseTime(scope.row.reviewTime) : "未发送"
            }}</span>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="180" align="center" fixed="right">
          <template slot-scope="scope">
            <el-button
              size="mini"
              type="text"
              icon="el-icon-s-promotion"
              @click="handleSendToExpert(scope.row)"
              :disabled="scope.row.reviewStatus === 'submitted'"
              :class="{ 'sent-button': scope.row.reviewStatus === 'submitted' }"
            >
              {{ scope.row.reviewStatus === "submitted" ? "已发送" : "发送" }}
            </el-button>
            <el-button
              size="mini"
              type="text"
              icon="el-icon-edit"
              @click="handleEditExpertReview(scope.row)"
              :disabled="scope.row.reviewStatus !== 'submitted'"
            >
              编辑
            </el-button>
            <el-button
              size="mini"
              type="text"
              icon="el-icon-view"
              @click="handleViewExpertReview(scope.row)"
            >
              详情
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-card>
    <!-- 发送专家对话框 -->
    <el-dialog
      title="发送专家审查"
      :visible.sync="sendDialogVisible"
      width="500px"
    >
      <el-form :model="sendForm" ref="sendForm" label-width="100px">
        <el-form-item label="专家类型" prop="expertType">
          <el-radio-group v-model="sendForm.expertType">
            <el-radio label="normal">专家</el-radio>
            <el-radio label="chief">主任专家</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item
          label="选择专家"
          prop="expertIds"
          v-if="sendForm.expertType === 'normal'"
        >
          <el-select
            v-model="sendForm.expertIds"
            multiple
            placeholder="请选择专家"
            style="width: 100%"
          >
            <el-option
              v-for="expert in availableExperts"
              :key="expert.id"
              :label="expert.name"
              :value="expert.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="发送内容" prop="content">
          <el-input
            type="textarea"
            :rows="4"
            v-model="sendForm.content"
            placeholder="请输入发送给专家的审查内容说明"
          />
        </el-form-item>
      </el-form>
      <div slot="footer">
        <el-button @click="sendDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="handleSendConfirm"
          >确认发送</el-button
        >
      </div>
    </el-dialog>
  </div>
</template>
<script>
import BaseStage from './BaseStage.vue';
import {
  getEthicsReviewDetail,
  updateEthicsReview,
  sendExpertReview,
  endEthicsReview,
  uploadAttachment,
  deleteAttachment,
  getAttachments
} from "./api/ethicsReview";
export default {
  name: 'EthicalReviewStage',
  components: { BaseStage },
  props: {
    stageData: {
      type: Object,
      default: () => ({})
    },
    caseInfo: {
      type: Object,
      default: () => ({})
    }
  },
  name: "EthicsReviewDetail",
  data() {
    return {
      reviewCommittee: {
        name: '医院伦理审查委员会',
        meetingTime: '2023-12-03 15:20:00',
        members: ['张教授', '李主任', '王医生', '赵委员', '钱专家'],
        conclusion: true,
        chairman: '张教授'
      // 表单数据
      form: {
        id: undefined,
        hospitalNo: "",
        donorName: "",
        gender: "",
        age: "",
        diagnosis: "",
        ethicsConclusion: "reviewing",
        ethicsOpinion: "",
        reviewTime: "",
        registrant: "",
        registrationTime: new Date()
          .toISOString()
          .replace("T", " ")
          .substring(0, 19)
      },
      reviewProgress: {
        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" }
        ],
        ethicsConclusion: [
          { required: true, message: "伦理结论不能为空", trigger: "change" }
        ],
        reviewTime: [
          { required: true, message: "审查时间不能为空", trigger: "change" }
        ]
      },
      reviewComments: [
      // 保存加载状态
      saveLoading: false,
      // 附件数据
      attachments: [],
      expertReviews: [
        // 专家(18位)- 初始状态为申请中
        {
          memberName: '张教授',
          specialty: '医学伦理',
          comment: '捐献程序符合伦理规范,同意通过',
          vote: '同意',
          reviewTime: '2023-12-03 14:30:00'
          id: 1,
          expertName: "张教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          memberName: '李主任',
          specialty: '临床医学',
          comment: '医疗程序规范,无伦理问题',
          vote: '同意',
          reviewTime: '2023-12-03 14:45:00'
          id: 2,
          expertName: "李教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          memberName: '王医生',
          specialty: '法律医学',
          comment: '法律文件齐全,程序合法',
          vote: '同意',
          reviewTime: '2023-12-03 15:00:00'
          id: 3,
          expertName: "王教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 4,
          expertName: "刘教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 5,
          expertName: "陈教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 6,
          expertName: "杨教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 7,
          expertName: "黄教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 8,
          expertName: "赵教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 9,
          expertName: "周教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 10,
          expertName: "吴教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 11,
          expertName: "徐教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 12,
          expertName: "孙教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 13,
          expertName: "朱教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 14,
          expertName: "马教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 15,
          expertName: "胡教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 16,
          expertName: "林教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 17,
          expertName: "郭教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        {
          id: 18,
          expertName: "何教授",
          isChief: false,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        },
        // 主任专家(1位)
        {
          id: 19,
          expertName: "主任专家",
          isChief: true,
          reviewStatus: "applying",
          expertConclusion: "",
          expertOpinion: "",
          reviewTime: ""
        }
      ],
      resolutionContent: '经伦理审查委员会审查,该器官捐献案例符合医学伦理要求,捐献程序规范,家属意愿真实有效,同意进行器官捐献。'
      expertLoading: false,
      attachmentLoading: false,
      // 发送对话框
      sendDialogVisible: false,
      sendForm: {
        expertType: "normal",
        expertIds: [],
        content: ""
      },
      // 上传相关
      uploadDialogVisible: false,
      uploadLoading: false,
      tempFileList: [],
      // 可用专家列表
      availableExperts: [
        { id: 1, name: "张教授", type: "normal" },
        { id: 2, name: "李教授", type: "normal" },
        { id: 3, name: "王教授", type: "normal" },
        { id: 4, name: "赵主任", type: "chief" }
      ]
    };
  },
  computed: {
    // 计算属性:专家同意数量
    approvedNormalExperts() {
      return this.expertReviews.filter(
        expert => !expert.isChief && expert.expertConclusion === "approved"
      ).length;
    },
    // 计算属性:主任专家状态
    chiefExpertStatus() {
      const chiefExpert = this.expertReviews.find(expert => expert.isChief);
      return chiefExpert
        ? this.statusTextFilter(chiefExpert.reviewStatus)
        : "未分配";
    },
    // 计算属性:完成进度
    completionRate() {
      const totalExperts = this.expertReviews.length;
      const completedExperts = this.expertReviews.filter(
        expert => expert.reviewStatus === "submitted"
      ).length;
      return totalExperts > 0
        ? Math.round((completedExperts / totalExperts) * 100)
        : 0;
    },
    // 计算属性:总体结论
    overallConclusionText() {
      if (this.approvedNormalExperts >= 12) {
        return "通过";
      } else if (this.approvedNormalExperts >= 9) {
        return "修改后通过";
      } else {
        return "不通过";
      }
    },
    overallConclusionFilter() {
      if (this.approvedNormalExperts >= 12) {
        return "success";
      } else if (this.approvedNormalExperts >= 9) {
        return "warning";
      } else {
        return "danger";
      }
    },
    // 是否可以发送给专家
    canSendToNormalExperts() {
      return (
        this.expertReviews.filter(
          expert => !expert.isChief && expert.reviewStatus === "applying"
        ).length > 0
      );
    },
    // 是否可以发送给主任专家(需要至少12个专家同意)
    canSendToChiefExpert() {
      return (
        this.approvedNormalExperts >= 12 &&
        this.expertReviews.filter(
          expert => expert.isChief && expert.reviewStatus === "applying"
        ).length > 0
      );
    },
    // 是否可以批量发送
    canBatchSend() {
      return (
        this.expertReviews.filter(expert => expert.reviewStatus === "applying")
          .length > 0
      );
    },
    // 是否可以发送专家审查
    canSendToExperts() {
      return this.form.id && this.form.ethicsConclusion === "reviewing";
    },
    // 当前用户信息
    currentUser() {
      return JSON.parse(sessionStorage.getItem("user") || "{}");
    }
  },
  created() {
    const id = this.$route.query.id;
    if (id) {
      this.getDetail(id);
      this.getAttachments(id);
      // 不再需要从接口获取专家列表,使用固定的expertReviews数据
    } else if (this.$route.path.includes("/add")) {
      this.generateHospitalNo();
      this.form.registrant = this.currentUser.username || "当前用户";
    }
  },
  methods: {
    getAlertType() {
      const status = this.stageData.status;
      return status === 'completed' ? 'success' :
             status === 'in_progress' ? 'warning' : 'info';
    // 生成住院号
    generateHospitalNo() {
      const timestamp = Date.now().toString();
      this.form.hospitalNo = "D" + timestamp.slice(-6);
    },
    getAlertDescription() {
      const status = this.stageData.status;
      return status === 'completed' ? '伦理审查已通过,可以进行器官分配' :
             status === 'in_progress' ? '伦理审查流程正在进行中' : '等待开始伦理审查流程';
    getExpertRowClassName({ row }) {
      return row.isChief ? "chief-expert-row" : "normal-expert-row";
    },
    handleViewResolution() {
      this.$message.info('查看伦理审查决议文件功能');
    // 获取详情
    getDetail(id) {
      getEthicsReviewDetail(id)
        .then(response => {
          if (response.code === 200) {
            this.form = response.data;
          }
        })
        .catch(error => {
          console.error("获取伦理审查详情失败:", error);
          this.$message.error("获取详情失败");
        });
    },
    // 获取专家审查列表
    getExpertReviews(ethicsReviewId) {
      this.expertLoading = true;
      // 模拟数据 - 实际项目中从接口获取
      setTimeout(() => {
        this.expertReviews = [
          // 专家(18位)
          {
            id: 1,
            expertName: "张教授",
            isChief: false,
            reviewStatus: "submitted",
            expertConclusion: "approved",
            expertOpinion: "符合伦理要求",
            reviewTime: "2025-12-01 10:30:00"
          },
          {
            id: 2,
            expertName: "李教授",
            isChief: false,
            reviewStatus: "submitted",
            expertConclusion: "approved",
            expertOpinion: "方案设计合理",
            reviewTime: "2025-12-01 11:20:00"
          },
          {
            id: 3,
            expertName: "王教授",
            isChief: false,
            reviewStatus: "applying",
            expertConclusion: "",
            expertOpinion: "",
            reviewTime: ""
          },
          // 主任专家(1位)
          {
            id: 19,
            expertName: "赵主任",
            isChief: true,
            reviewStatus: "applying",
            expertConclusion: "",
            expertOpinion: "",
            reviewTime: ""
          }
        ];
        this.expertLoading = false;
      }, 500);
    },
    // 获取附件列表
    getAttachments(ethicsReviewId) {
      this.attachmentLoading = true;
      getAttachments(ethicsReviewId)
        .then(response => {
          if (response.code === 200) {
            this.attachments = response.data;
          }
          this.attachmentLoading = false;
        })
        .catch(error => {
          console.error("获取附件列表失败:", error);
          this.attachmentLoading = false;
        });
    },
    // 状态过滤器
    statusFilter(status) {
      const statusMap = {
        applying: "info",
        submitted: "success"
      };
      return statusMap[status] || "info";
    },
    statusTextFilter(status) {
      const statusMap = {
        applying: "申请中",
        submitted: "已提交"
      };
      return statusMap[status] || "未知";
    },
    // 结论过滤器
    conclusionFilter(conclusion) {
      const conclusionMap = {
        approved: "success",
        approved_with_modifications: "warning",
        disapproved: "danger"
      };
      return conclusionMap[conclusion] || "info";
    },
    conclusionTextFilter(conclusion) {
      const conclusionMap = {
        approved: "同意",
        approved_with_modifications: "修改后同意",
        disapproved: "不同意"
      };
      return conclusionMap[conclusion] || "未知";
    },
    // 保存信息
    handleSave() {
      this.$refs.form.validate(valid => {
        if (valid) {
          this.saveLoading = true;
          const apiMethod = this.form.id ? updateEthicsReview : addEthicsReview;
          apiMethod(this.form)
            .then(response => {
              if (response.code === 200) {
                this.$message.success("保存成功");
                if (!this.form.id) {
                  this.form.id = response.data.id;
                  this.$router.replace({
                    query: { ...this.$route.query, id: this.form.id }
                  });
                }
              }
            })
            .catch(error => {
              console.error("保存失败:", error);
              this.$message.error("保存失败");
            })
            .finally(() => {
              this.saveLoading = false;
            });
        }
      });
    },
    // 发送专家审查
    handleSendToExperts() {
      this.sendDialogVisible = true;
    },
    // 发送给专家
    handleSendToNormalExperts() {
      const normalExperts = this.expertReviews.filter(
        expert => !expert.isChief && expert.reviewStatus === "applying"
      );
      this.sendForm.expertIds = normalExperts.map(expert => expert.id);
      this.sendForm.expertType = "normal";
      this.sendDialogVisible = true;
    },
    // 发送给主任专家
    handleSendToChiefExpert() {
      const chiefExpert = this.expertReviews.find(
        expert => expert.isChief && expert.reviewStatus === "applying"
      );
      if (chiefExpert) {
        this.sendForm.expertIds = [chiefExpert.id];
        this.sendForm.expertType = "chief";
        this.sendDialogVisible = true;
      }
    },
    // 批量发送
    handleBatchSend() {
      const applyingExperts = this.expertReviews.filter(
        expert => expert.reviewStatus === "applying"
      );
      this.sendForm.expertIds = applyingExperts.map(expert => expert.id);
      this.sendForm.expertType = "batch";
      this.sendDialogVisible = true;
    },
    // 发送给单个专家
    handleSendToExpert(expert) {
      this.sendForm.expertIds = [expert.id];
      this.sendForm.expertType = expert.isChief ? "chief" : "normal";
      this.sendDialogVisible = true;
    },
    // 确认发送
    handleSendConfirm() {
      if (this.sendForm.expertIds.length === 0) {
        this.$message.warning("请选择要发送的专家");
        return;
      }
      sendExpertReview({
        ethicsReviewId: this.form.id,
        expertIds: this.sendForm.expertIds,
        content: this.sendForm.content
      })
        .then(response => {
          if (response.code === 200) {
            this.$message.success("发送成功");
            this.sendDialogVisible = false;
            this.getExpertReviews(this.form.id);
            this.sendForm = {
              expertType: "normal",
              expertIds: [],
              content: ""
            };
          }
        })
        .catch(error => {
          console.error("发送失败:", error);
          this.$message.error("发送失败");
        });
    },
    // 结束审查
    handleEndReview() {
      this.$confirm(
        "确定要结束本次伦理审查吗?结束后将无法修改专家审查结果。",
        "提示",
        {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning"
        }
      )
        .then(() => {
          endEthicsReview(this.form.id)
            .then(response => {
              if (response.code === 200) {
                this.$message.success("审查已结束");
                this.form.ethicsConclusion = "terminated";
              }
            })
            .catch(error => {
              console.error("结束审查失败:", error);
              this.$message.error("结束审查失败");
            });
        })
        .catch(() => {});
    },
    // 编辑专家审查
    handleEditExpertReview(expert) {
      this.$prompt("请输入审查意见", "编辑专家审查", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        inputValue: expert.expertOpinion || "",
        inputValidator: value => {
          if (!value || value.trim() === "") {
            return "审查意见不能为空";
          }
          return true;
        }
      })
        .then(({ value }) => {
          // 模拟更新专家审查
          const index = this.expertReviews.findIndex(e => e.id === expert.id);
          if (index !== -1) {
            this.expertReviews[index].expertOpinion = value;
            this.$message.success("审查意见已更新");
          }
        })
        .catch(() => {});
    },
    // 查看专家审查详情
    handleViewExpertReview(expert) {
      this.$alert(
        `
        <div>
          <p><strong>专家姓名:</strong>${expert.expertName}</p>
          <p><strong>专家类型:</strong>${
            expert.isChief ? "主任专家" : "专家"
          }</p>
          <p><strong>审查状态:</strong>${this.statusTextFilter(
            expert.reviewStatus
          )}</p>
          <p><strong>专家结论:</strong>${
            expert.expertConclusion
              ? this.conclusionTextFilter(expert.expertConclusion)
              : "未提交"
          }</p>
          <p><strong>审查意见:</strong>${expert.expertOpinion || "无"}</p>
          <p><strong>审查时间:</strong>${expert.reviewTime || "未审查"}</p>
        </div>
      `,
        "专家审查详情",
        {
          dangerouslyUseHTMLString: true,
          customClass: "expert-review-detail-dialog"
        }
      );
    },
    // 上传附件
    handleUploadAttachment() {
      this.uploadDialogVisible = true;
    },
    // 上传前校验
    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;
      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("文件格式不支持");
        return false;
      }
      if (file.size > maxSize) {
        this.$message.error("文件大小不能超过10MB");
        return false;
      }
      return true;
    },
    // 文件选择变化
    handleFileChange(file, fileList) {
      this.tempFileList = fileList;
    },
    // 提交上传
    submitUpload() {
      if (this.tempFileList.length === 0) {
        this.$message.warning("请先选择要上传的文件");
        return;
      }
      this.uploadLoading = true;
      const uploadPromises = this.tempFileList.map(file => {
        const formData = new FormData();
        formData.append("file", file.raw);
        formData.append("ethicsReviewId", this.form.id);
        return uploadAttachment(formData);
      });
      Promise.all(uploadPromises)
        .then(responses => {
          this.$message.success("文件上传成功");
          this.uploadDialogVisible = false;
          this.tempFileList = [];
          this.getAttachments(this.form.id);
        })
        .catch(error => {
          console.error("上传失败:", error);
          this.$message.error("文件上传失败");
        })
        .finally(() => {
          this.uploadLoading = false;
        });
    },
    // 预览附件
    handlePreviewAttachment(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("该文件类型暂不支持在线预览,请下载后查看");
      }
    },
    // 下载附件
    handleDownloadAttachment(attachment) {
      const link = document.createElement("a");
      link.href = attachment.fileUrl;
      link.download = attachment.fileName;
      link.click();
      this.$message.success(`开始下载: ${attachment.fileName}`);
    },
    // 删除附件
    handleRemoveAttachment(attachment) {
      this.$confirm("确定要删除这个附件吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
          deleteAttachment(attachment.id)
            .then(response => {
              if (response.code === 200) {
                this.$message.success("附件删除成功");
                this.getAttachments(this.form.id);
              }
            })
            .catch(error => {
              console.error("删除附件失败:", error);
              this.$message.error("删除附件失败");
            });
        })
        .catch(() => {});
    },
    // 获取文件类型
    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();
    },
    // 文件大小格式化
    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>
.resolution-content {
.ethics-review-detail {
  padding: 20px;
  line-height: 1.8;
  background-color: #f5f7fa;
}
.signature-area {
  text-align: right;
  margin-top: 30px;
.detail-card {
  margin-bottom: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.expert-card {
  margin-bottom: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.attachment-card {
  margin-bottom: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.detail-title {
  font-size: 18px;
  font-weight: 600;
  color: #303133;
}
.expert-stats {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: rgb(43, 181, 245);
  border-radius: 8px;
  margin-bottom: 20px;
}
.stat-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px;
}
.stat-label {
  font-size: 12px;
  opacity: 0.9;
  margin-bottom: 5px;
}
.stat-value {
  font-size: 18px;
  font-weight: bold;
}
.upload-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 15px;
  padding: 10px;
  background-color: #f8f9fa;
  border-radius: 4px;
}
.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(.el-form-item__label) {
  font-weight: 500;
}
:deep(.el-input__inner) {
  border-radius: 4px;
}
:deep(.el-textarea__inner) {
  border-radius: 4px;
  resize: vertical;
}
/* 表格样式优化 */
:deep(.el-table) {
  border-radius: 8px;
  overflow: hidden;
}
:deep(.el-table th) {
  background-color: #f5f7fa;
  color: #606266;
  font-weight: 500;
}
:deep(.el-table .cell) {
  padding: 8px 12px;
}
/* 按钮样式优化 */
:deep(.el-button--primary) {
  background: linear-gradient(135deg, #409eff 0%, #3375e0 100%);
  border: none;
  border-radius: 4px;
}
:deep(.el-button--success) {
  background: linear-gradient(135deg, #67c23a 0%, #529b2f 100%);
  border: none;
  border-radius: 4px;
}
:deep(.el-button--warning) {
  background: linear-gradient(135deg, #e6a23c 0%, #d18c2a 100%);
  border: none;
  border-radius: 4px;
}
:deep(.el-button--danger) {
  background: linear-gradient(135deg, #f56c6c 0%, #e05b5b 100%);
  border: none;
  border-radius: 4px;
}
/* 标签样式 */
:deep(.el-tag) {
  border-radius: 12px;
  border: none;
  font-weight: 500;
}
/* 对话框样式优化 */
:deep(.el-dialog) {
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}
:deep(.el-dialog__header) {
  background: linear-gradient(135deg, #f5f7fa 0%, #e4e7ed 100%);
  border-bottom: 1px solid #e4e7ed;
  padding: 15px 20px;
}
:deep(.el-dialog__title) {
  font-weight: 600;
  color: #303133;
}
/* 上传组件样式 */
:deep(.el-upload-dragger) {
  border: 2px dashed #dcdfe6;
  border-radius: 6px;
  background-color: #fafafa;
  transition: all 0.3s ease;
}
:deep(.el-upload-dragger:hover) {
  border-color: #409eff;
  background-color: #f0f7ff;
}
/* 响应式设计 */
@media (max-width: 768px) {
  .ethics-review-detail {
    padding: 10px;
  }
  .expert-stats .el-col {
    margin-bottom: 10px;
  }
  .upload-header {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }
}
/* 动画效果 */
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}
/* 加载状态 */
.loading-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 200px;
}
/* 专家类型样式 */
.normal-expert {
  color: #409eff;
  font-weight: 500;
}
.chief-expert {
  color: #f56c6c;
  font-weight: 600;
}
/* 专家行样式 */
:deep(.normal-expert-row) {
  background-color: #fafafa;
}
:deep(.chief-expert-row) {
  background-color: #fff7e6;
}
:deep(.normal-expert-row:hover) {
  background-color: #f0f7ff;
}
:deep(.chief-expert-row:hover) {
  background-color: #ffecc2;
}
/* 无数据样式 */
.no-data {
  color: #909399;
  font-style: italic;
}
/* 专家意见样式 */
.expert-opinion {
  color: #303133;
  line-height: 1.5;
}
/* 已发送按钮样式 */
.sent-button {
  color: #67c23a !important;
}
/* 表格行悬停效果 */
:deep(.el-table__row:hover) {
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  transition: all 0.3s ease;
}
/* 自定义滚动条 */
:deep(::-webkit-scrollbar) {
  width: 6px;
  height: 6px;
}
:deep(::-webkit-scrollbar-track) {
  background: #f1f1f1;
  border-radius: 3px;
}
:deep(::-webkit-scrollbar-thumb) {
  background: #c1c1c1;
  border-radius: 3px;
}
:deep(::-webkit-scrollbar-thumb:hover) {
  background: #a8a8a8;
}
/* 专家审查表格特殊样式 */
.expert-table-special :deep(.el-table__row) {
  transition: all 0.3s ease;
}
.expert-table-special :deep(.el-table__row:hover) {
  background-color: #f0f7ff;
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* 主任专家行高亮 */
:deep(.chief-expert-row) {
  background-color: #fff7e6 !important;
}
:deep(.chief-expert-row:hover) {
  background-color: #ffecc2 !important;
}
</style>