From 3453ba7e5243022ad4388da1515dc75ad8d81f94 Mon Sep 17 00:00:00 2001
From: WXL <wl_5969728@163.com>
Date: 星期日, 17 五月 2026 15:00:23 +0800
Subject: [PATCH] 近期调试

---
 src/views/business/course/components/EthicalReviewStage.vue | 2299 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 2,142 insertions(+), 157 deletions(-)

diff --git a/src/views/business/course/components/EthicalReviewStage.vue b/src/views/business/course/components/EthicalReviewStage.vue
index 47e4877..7b874b4 100644
--- a/src/views/business/course/components/EthicalReviewStage.vue
+++ b/src/views/business/course/components/EthicalReviewStage.vue
@@ -1,206 +1,2191 @@
 <template>
-  <base-stage :stage-data="stageData" :case-info="caseInfo">
-    <template #header>
-      <el-alert
-        title="浼︾悊瀹℃煡闃舵"
-        :type="getAlertType()"
-        :description="getAlertDescription()"
-        show-icon
-        :closable="false"
-      />
-    </template>
+  <div class="ethics-review-detail">
 
-    <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="濮斿憳浼氬悕绉�">
-              {{ 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-col>
+    <el-card class="detail-card">
+      <!-- 浼︾悊瀹℃煡鍩烘湰淇℃伅 -->
+      <div slot="header" class="clearfix">
+        <span class="detail-title">浼︾悊瀹℃煡鍩烘湰淇℃伅</span>
+        <div style="float: right;">
+          <el-button type="primary" @click="handleSave" :loading="saveLoading">
+            淇濆瓨
+          </el-button>
 
-      <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-steps>
-        </el-card>
-      </el-col>
-    </el-row>
+          <el-button
+            type="success"
+            @click="handleCompleteReview"
+            :disabled="form.status == '3' || form.status == '2'"
+            :loading="completeLoading"
+          >
+            瀹℃煡瀹屾垚
+          </el-button>
 
-    <el-card style="margin-top: 20px;">
-      <div slot="header" class="card-header">
-        <span>濮斿憳瀹℃煡鎰忚</span>
+          <el-button
+            type="warning"
+            @click="handleSuspendReview"
+            :disabled="form.status == '2' || form.status == '3'"
+            :loading="suspendLoading"
+          >
+            瀹℃煡涓
+          </el-button>
+
+          <el-button
+            type="danger"
+            @click="handleEndReview"
+            :disabled="form.status == '2'"
+            :loading="endLoading"
+          >
+            缁撴潫瀹℃煡
+          </el-button>
+        </div>
       </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-form :model="form" ref="form" :rules="rules" label-width="120px">
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="鍙戣捣涓婚" prop="initiateTheme">
+              <el-input
+                v-model="form.initiateTheme"
+                placeholder="璇疯緭鍏ュ彂璧蜂富棰�"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="鍙戣捣浜�" prop="initiatePerson">
+              <el-input v-model="form.initiatePerson" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="瀹℃煡鐘舵��" prop="status">
+              <el-select v-model="form.status" style="width: 100%">
+                <el-option
+                  v-for="dict in dict.type.sys_ethical"
+                  :key="dict.value"
+                  :label="dict.label"
+                  :value="dict.value"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- 涓撳鐩稿叧淇℃伅 -->
+
+        <el-row :gutter="20">
+          <el-col :span="8">
+            <el-form-item label="浼︾悊瀹℃煡缁撹" prop="expertConclusion">
+              <el-select v-model="form.expertConclusion" style="width: 100%">
+                <el-option label="鍚屾剰" value="1" />
+                <el-option label="瀹℃煡涓�" value="2" />
+                <el-option label="涓嶅悓鎰�" value="0" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="浼︾悊瀹℃煡缁撹鏃堕棿" prop="expertTime">
+              <el-date-picker
+                v-model="form.expertTime"
+                type="datetime"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="瀹℃煡鎰忚" prop="expertOpinion">
+              <el-input
+                type="textarea"
+                :rows="2"
+                v-model="form.expertOpinion"
+                placeholder="璇疯緭鍏ユ剰瑙�"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="澶囨敞" prop="remark">
+              <el-input
+                type="textarea"
+                :rows="3"
+                v-model="form.remark"
+                placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-card>
+
+    <!-- 闄勪欢涓婁紶 -->
+    <el-card class="attachment-card">
+      <div slot="header" class="clearfix">
+        <span class="detail-title">鐩稿叧闄勪欢</span>
+      </div>
+
+      <!-- 浣跨敤 UploadAttachment 缁勪欢 -->
+      <UploadAttachment
+        ref="uploadAttachment"
+        :file-list="attachmentFileList"
+        :limit="10"
+        accept=".pdf,.jpg,.jpeg,.png,.doc,.docx,.xls,.xlsx"
+        @change="handleAttachmentChange"
+        @upload-success="handleUploadSuccess"
+        @upload-error="handleUploadError"
+        @remove="handleAttachmentRemove"
+      />
+
+      <!-- 闄勪欢鍒楄〃 -->
+      <div
+        class="attachment-list"
+        v-if="form.annexfilesList && form.annexfilesList.length > 0"
+      >
+        <div class="list-title">
+          宸蹭笂浼犻檮浠� ({{ form.annexfilesList.length }})
+        </div>
+        <el-table :data="form.annexfilesList" style="width: 100%" size="small">
+          <el-table-column label="鏂囦欢鍚�" min-width="200">
+            <template slot-scope="scope">
+              <i
+                class="el-icon-document"
+                style="margin-right: 8px; color: #409EFF;"
+              ></i>
+              <span class="file-name">{{ scope.row.fileName }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="鏂囦欢绫诲瀷" width="100">
+            <template slot-scope="scope">
+              <el-tag size="small">{{
+                getFileType(scope.row.fileName)
+              }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="鍒涘缓鏃堕棿" width="160">
+            <template slot-scope="scope">
+              <span>{{ formatDateTime(scope.row.createTime) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="鎿嶄綔" width="266">
+            <template slot-scope="scope">
+              <el-button
+                size="mini"
+                type="primary"
+                @click="handlePreview(scope.row)"
+              >
+                棰勮
+              </el-button>
+              <el-button
+                size="mini"
+                type="success"
+                @click="handleDownload(scope.row)"
+              >
+                涓嬭浇
+              </el-button>
+              <el-button
+                size="mini"
+                type="danger"
+                @click="handleRemoveAttachment(scope.$index)"
+              >
+                鍒犻櫎
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+
+      <!-- 绌虹姸鎬� -->
+      <div
+        v-if="!form.annexfilesList || form.annexfilesList.length == 0"
+        class="empty-attachment"
+      >
+        <i
+          class="el-icon-folder-opened"
+          style="font-size: 60px; color: #C0C4CC; margin-bottom: 20px;"
+        ></i>
+        <p style="color: #909399; font-size: 14px;">鏆傛棤闄勪欢锛岃涓婁紶鐩稿叧鏂囦欢</p>
+      </div>
+    </el-card>
+
+    <!-- 涓撳瀹℃煡鎯呭喌 -->
+    <el-card class="expert-card">
+      <div slot="header" class="clearfix">
+        <span class="detail-title">涓撳瀹℃煡鎯呭喌</span>
+        <div style="float: right;">
+          <el-button size="mini" type="primary" @click="handleAddExpert">
+            娣诲姞涓撳
+          </el-button>
+          <el-button
+            size="mini"
+            type="primary"
+            @click="handleSendToNormalExperts"
+            :disabled="!canSendToNormalExperts"
+          >
+            鍙戦�佷笓瀹�
+          </el-button>
+          <el-button
+            size="mini"
+            type="success"
+            @click="handleSendToChiefExpert"
+            :disabled="!canSendToChiefExpert"
+          >
+            鍙戦�佷富濮斾笓瀹�
+          </el-button>
+        </div>
+      </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">{{ normalExpertsCount }}浜�</span>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="stat-item">
+              <span class="stat-label">涓诲涓撳:</span>
+              <span class="stat-value">{{ chiefExpertsCount }}浜�</span>
+            </div>
+          </el-col>
+          <el-col :span="6">
+            <div class="stat-item">
+              <span class="stat-label">宸插悓鎰�:</span>
+              <span class="stat-value"
+                >{{ approvedExpertsCount }}/{{ totalExpertsCount }}</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="ethicalreviewopinionsList"
+        v-loading="expertLoading"
+        style="width: 100%"
+        height="600"
+        :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">
-            <el-tag :type="scope.row.vote === '鍚屾剰' ? 'success' : 'danger'">
-              {{ scope.row.vote }}
+            <span
+              class="expert-name-link"
+              @click="handleViewExpertHistory(scope.row)"
+            >
+              {{ scope.row.expertname }}
+            </span>
+            <el-tag
+              v-if="scope.row.expertType == '1'"
+              size="mini"
+              type="danger"
+              style="margin-left: 5px;"
+              >涓诲</el-tag
+            >
+          </template>
+        </el-table-column>
+
+        <el-table-column label="涓撳缂栧彿" width="120" align="center">
+          <template slot-scope="scope">
+            <span>{{ scope.row.expertNo || "-" }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="涓撳绫诲瀷" width="100" align="center">
+          <template slot-scope="scope">
+            <span
+              :class="
+                scope.row.expertType == '1' ? 'chief-expert' : 'normal-expert'
+              "
+            >
+              {{ getExpertTypeText(scope.row.expertType) }}
+            </span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="瀹℃煡鐘舵��" width="100" align="center">
+          <template slot-scope="scope">
+            <el-tag
+              :type="getReviewStatusFilter(scope.row.receiveStatus)"
+              size="small"
+            >
+              {{ getReviewStatusText(scope.row.receiveStatus) }}
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="瀹℃煡鏃堕棿" width="160">
+
+        <el-table-column label="涓撳缁撹" width="120" align="center">
           <template slot-scope="scope">
-            {{ formatTime(scope.row.reviewTime) }}
+            <el-tag
+              v-if="scope.row.expertconclusion"
+              :type="getConclusionFilter(scope.row.expertconclusion)"
+              size="small"
+            >
+              {{ getConclusionText(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="100" align="center">
+          <template slot-scope="scope">
+            <span>{{ scope.row.conclusionorder || "-" }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="鎴鏃堕棿" width="160" align="center">
+          <template slot-scope="scope">
+            <span>{{
+              scope.row.endTime ? formatDateTime(scope.row.endTime) : "鏈缃�"
+            }}</span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="瀹℃煡鏃堕棿" width="160" align="center">
+          <template slot-scope="scope">
+            <span>{{
+              scope.row.conclusiontime
+                ? formatDateTime(scope.row.conclusiontime)
+                : "鏈鏌�"
+            }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="鍙戦�佹椂闂�" width="160" align="center">
+          <template slot-scope="scope">
+            <span>{{
+              scope.row.startTime
+                ? formatDateTime(scope.row.startTime)
+                : "鏈彂閫�"
+            }}</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.receiveStatus == '2' ||
+                  scope.row.receiveStatus == '3' ||
+                  scope.row.receiveStatus == '4'
+              "
+              :class="{
+                'sent-button':
+                  scope.row.receiveStatus == '2' ||
+                  scope.row.receiveStatus == '3'
+              }"
+            >
+              {{
+                scope.row.receiveStatus == "2" || scope.row.receiveStatus == "3"
+                  ? "宸插彂閫�"
+                  : "鍙戦��"
+              }}
+            </el-button>
+            <el-button
+              v-if="scope.row.receiveStatus == 0"
+              size="mini"
+              type="text"
+              icon="el-icon-delete"
+              @click="handleDeleteExpertReview(scope.row, scope.$index)"
+              style="color: #f56c6c;"
+            >
+              鍒犻櫎
+            </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-button>
+    <!-- 娣诲姞涓撳瀵硅瘽妗� -->
+    <el-dialog
+      title="娣诲姞涓撳"
+      :visible.sync="expertDialogVisible"
+      width="800px"
+      @close="handleExpertDialogClose"
+    >
+      <div style="margin-bottom: 20px;">
+        <el-input
+          v-model="expertSearchQuery"
+          placeholder="璇疯緭鍏ヤ笓瀹跺鍚嶆垨缂栧彿鎼滅储"
+          style="width: 300px; margin-right: 10px;"
+          @keyup.enter.native="handleSearchExperts"
+        >
+          <el-button
+            slot="append"
+            icon="el-icon-search"
+            @click="handleSearchExperts"
+          ></el-button>
+        </el-input>
+        <el-select
+          v-model="filterExpertType"
+          placeholder="涓撳绫诲瀷"
+          style="width: 150px; margin-right: 10px;"
+          @change="handleSearchExperts"
+        >
+          <el-option label="鍏ㄩ儴" value=""></el-option>
+          <el-option label="鏅�氫笓瀹�" value="0"></el-option>
+          <el-option label="涓讳换濮斿憳" value="1"></el-option>
+        </el-select>
+        <el-button type="primary" @click="handleSearchExperts">鎼滅储</el-button>
+        <el-button @click="handleResetSearch">閲嶇疆</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>
+
+      <el-table
+        :data="filteredExpertList"
+        v-loading="expertListLoading"
+        style="width: 100%"
+        max-height="400"
+        @selection-change="handleExpertSelectionChange"
+      >
+        <el-table-column type="selection" width="55"></el-table-column>
+        <el-table-column
+          label="涓撳濮撳悕"
+          prop="username"
+          width="120"
+        ></el-table-column>
+        <el-table-column
+          label="涓撳缂栧彿"
+          prop="userno"
+          width="120"
+        ></el-table-column>
+        <el-table-column label="涓撳绫诲瀷" width="100">
+          <template slot-scope="scope">
+            <el-tag
+              size="small"
+              :type="getIsChiefExpert(scope.row) ? 'danger' : ''"
+            >
+              {{ getIsChiefExpert(scope.row) ? "涓讳换濮斿憳" : "鏅�氫笓瀹�" }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="鍗曚綅鍚嶇О"
+          prop="unitname"
+          show-overflow-tooltip
+        ></el-table-column>
+        <el-table-column
+          label="鑱岀О"
+          prop="title"
+          width="100"
+        ></el-table-column>
+        <el-table-column
+          label="鑱旂郴鐢佃瘽"
+          prop="telephone"
+          width="120"
+        ></el-table-column>
+      </el-table>
+
+      <div style="margin-top: 20px; text-align: center;">
+        <el-pagination
+          @size-change="handlePageSizeChange"
+          @current-change="handlePageChange"
+          :current-page="expertPage.pageNum"
+          :page-sizes="[10, 20, 50, 100]"
+          :page-size="expertPage.pageSize"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="filteredExpertTotal"
+        ></el-pagination>
+      </div>
+
+      <div slot="footer">
+        <el-button @click="expertDialogVisible = false">鍙栨秷</el-button>
+        <el-button
+          type="primary"
+          @click="handleConfirmAddExpert"
+          :disabled="selectedExperts.length == 0"
+          >纭畾娣诲姞</el-button
+        >
+      </div>
+    </el-dialog>
+
+    <!-- 鍙戦�佷笓瀹跺璇濇 -->
+    <el-dialog
+      :title="sendDialogTitle"
+      :visible.sync="sendDialogVisible"
+      width="500px"
+      @close="handleSendDialogClose"
+    >
+      <el-form :model="sendForm" ref="sendForm" label-width="100px">
+        <el-form-item label="涓撳绫诲瀷" prop="expertType">
+          <el-radio-group
+            v-model="sendForm.expertType"
+            @change="handleExpertTypeChange"
+          >
+            <el-radio label="normal">鏅�氫笓瀹�</el-radio>
+            <el-radio label="chief">涓诲涓撳</el-radio>
+          </el-radio-group>
+        </el-form-item>
+
+        <el-form-item label="鍙戦�佹椂闂�" prop="startTime" required>
+          <el-date-picker
+            v-model="sendForm.startTime"
+            type="datetime"
+            placeholder="璇烽�夋嫨鍙戦�佹椂闂�"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            style="width: 100%"
+          />
+        </el-form-item>
+
+        <el-form-item
+          label="鎴鏃堕棿"
+          prop="endTime"
+          :required="sendForm.expertType !== 'chief'"
+        >
+          <el-date-picker
+            v-model="sendForm.endTime"
+            type="datetime"
+            placeholder="璇烽�夋嫨鎴鏃堕棿"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            style="width: 100%"
+            :disabled="sendForm.expertType === 'chief'"
+          />
+          <div v-if="sendForm.expertType !== 'chief'" style="margin-top: 5px;">
+            <el-button-group>
+              <el-button size="mini" @click="setEndTime(0.5)"
+                >鍗婂皬鏃跺悗</el-button
+              >
+              <el-button size="mini" @click="setEndTime(1)">涓�灏忔椂鍚�</el-button>
+              <el-button size="mini" @click="setEndTime(2)">涓ゅ皬鏃跺悗</el-button>
+              <el-button size="mini" @click="setEndTime(24)">涓�澶╁悗</el-button>
+            </el-button-group>
+          </div>
+          <div
+            v-if="sendForm.expertType === 'chief'"
+            style="font-size: 12px; color: #999; margin-top: 5px;"
+          >
+            涓诲涓撳鏃犻渶璁剧疆鎴鏃堕棿
+          </div>
+        </el-form-item>
+
+        <el-form-item label="鍙戦�佹柟寮�" prop="sendType" required>
+          <el-select
+            v-model="sendForm.sendType"
+            placeholder="璇烽�夋嫨鍙戦�佹柟寮�"
+            style="width: 100%"
+          >
+            <el-option label="绯荤粺鍙戦��" value="0"></el-option>
+            <el-option label="閭欢鍙戦��" value="1"></el-option>
+            <el-option label="鐭俊鍙戦��" value="2"></el-option>
+            <el-option label="鍏朵粬鏂瑰紡" value="3"></el-option>
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="鍙戦�佹爣棰�" prop="title" required>
+          <el-input v-model="sendForm.title" placeholder="璇疯緭鍏ュ彂閫佹爣棰�" />
+        </el-form-item>
+
+        <el-form-item label="鍙戦�佸唴瀹�" prop="content" required>
+          <el-input
+            type="textarea"
+            :rows="4"
+            v-model="sendForm.content"
+            placeholder="璇疯緭鍏ュ彂閫佺粰涓撳鐨勫鏌ュ唴瀹硅鏄�"
+          />
+        </el-form-item>
+
+        <el-form-item label="璺宠浆閾炬帴" prop="url">
+          <el-input
+            v-model="sendForm.url"
+            placeholder="璇疯緭鍏ヨ烦杞摼鎺ワ紙鍙�夛級"
+          />
+        </el-form-item>
+      </el-form>
+      <div slot="footer">
+        <el-button @click="sendDialogVisible = false">鍙栨秷</el-button>
+        <el-button type="primary" @click="handleSendConfirm" :loading="sending"
+          >纭鍙戦��</el-button
+        >
+      </div>
+    </el-dialog>
+
+    <!-- 涓撳鍘嗗彶瀹℃壒鎯呭喌瀵硅瘽妗� -->
+    <el-dialog
+      title="涓撳鍘嗗彶瀹℃壒鎯呭喌"
+      :visible.sync="expertHistoryDialogVisible"
+      width="600px"
+    >
+      <div v-loading="expertHistoryLoading">
+        <div v-if="expertHistoryData" style="padding: 20px;">
+          <el-row :gutter="20" style="margin-bottom: 20px;">
+            <el-col :span="12">
+              <div class="history-stat-item">
+                <div class="history-stat-label">涓撳濮撳悕</div>
+                <div class="history-stat-value">
+                  {{ currentExpertInfo.expertname || "-" }}
+                </div>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="history-stat-item">
+                <div class="history-stat-label">涓撳缂栧彿</div>
+                <div class="history-stat-value">
+                  {{ currentExpertInfo.expertNo || "-" }}
+                </div>
+              </div>
+            </el-col>
+          </el-row>
+
+          <el-divider></el-divider>
+
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <div class="history-stat-item">
+                <div class="history-stat-label">鎬诲鎵规暟閲�</div>
+                <div
+                  class="history-stat-value"
+                  style="font-size: 24px; color: #409EFF;"
+                >
+                  {{ expertHistoryData.count || 0 }}
+                </div>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="history-stat-item">
+                <div class="history-stat-label">宸叉帴鏀舵暟閲�</div>
+                <div class="history-stat-value">
+                  {{ expertHistoryData.acceptCount || 0 }}
+                </div>
+              </div>
+            </el-col>
+          </el-row>
+
+          <el-row :gutter="20" style="margin-top: 20px;">
+            <el-col :span="12">
+              <div class="history-stat-item">
+                <div class="history-stat-label">鏈帴鏀舵暟閲�</div>
+                <div class="history-stat-value">
+                  {{ expertHistoryData.notAcceptCount || 0 }}
+                </div>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="history-stat-item">
+                <div class="history-stat-label">鏈夋剰瑙佹暟閲�</div>
+                <div class="history-stat-value">
+                  {{ expertHistoryData.opinionCount || 0 }}
+                </div>
+              </div>
+            </el-col>
+          </el-row>
+
+          <el-row :gutter="20" style="margin-top: 20px;">
+            <el-col :span="12">
+              <div class="history-stat-item">
+                <div class="history-stat-label">鏃犳剰瑙佹暟閲�</div>
+                <div class="history-stat-value">
+                  {{ expertHistoryData.notOpinionCount || 0 }}
+                </div>
+              </div>
+            </el-col>
+            <el-col :span="12">
+              <div class="history-stat-item">
+                <div class="history-stat-label">鏈夐檮浠舵暟閲�</div>
+                <div class="history-stat-value">
+                  {{ expertHistoryData.annexCount || 0 }}
+                </div>
+              </div>
+            </el-col>
+          </el-row>
+
+          <el-row :gutter="20" style="margin-top: 20px;">
+            <el-col :span="12">
+              <div class="history-stat-item">
+                <div class="history-stat-label">鏃犻檮浠舵暟閲�</div>
+                <div class="history-stat-value">
+                  {{ expertHistoryData.notAnnexCount || 0 }}
+                </div>
+              </div>
+            </el-col>
+          </el-row>
+        </div>
+        <div v-else style="text-align: center; padding: 40px 0;">
+          <i
+            class="el-icon-info"
+            style="font-size: 60px; color: #C0C4CC; margin-bottom: 20px;"
+          ></i>
+          <p style="color: #909399; font-size: 14px;">鏆傛棤鍘嗗彶瀹℃壒鏁版嵁</p>
         </div>
       </div>
-    </el-card>
-  </base-stage>
+      <div slot="footer">
+        <el-button @click="expertHistoryDialogVisible = false">鍏抽棴</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 鏂囦欢棰勮寮圭獥 -->
+    <FilePreviewDialog
+      :visible="previewVisible"
+      :file="currentPreviewFile"
+      @close="previewVisible = false"
+      @download="handleDownload"
+    />
+  </div>
 </template>
 
 <script>
-import BaseStage from './BaseStage.vue';
+import { getToken } from "@/utils/auth";
+import {
+  reviewinitiateBaseInfoList,
+  ethicalreviewedit,
+  ethicalreviewadd,
+  ethicalreviewInfo,
+  ethicalreExpertTotal,
+  sendNotification
+} from "@/api/businessApi";
+import { listExternalperson } from "@/api/project/externalperson";
+import CaseBasicInfo from "@/components/CaseBasicInfo";
+import UploadAttachment from "@/components/UploadAttachment";
+import FilePreviewDialog from "@/components/FilePreviewDialog";
+import dayjs from "dayjs";
 
 export default {
-  name: 'EthicalReviewStage',
-  components: { BaseStage },
-  props: {
-    stageData: {
-      type: Object,
-      default: () => ({})
-    },
-    caseInfo: {
-      type: Object,
-      default: () => ({})
+  name: "EthicsReviewDetail",
+  components: { CaseBasicInfo, UploadAttachment, FilePreviewDialog },
+  dicts: ["sys_user_sex", "sys_ethical", "Review_status"],
+    props: {
+    infoid: {
+      type: String,
+      default: true
     }
   },
   data() {
     return {
-      reviewCommittee: {
-        name: '鍖婚櫌浼︾悊瀹℃煡濮斿憳浼�',
-        meetingTime: '2023-12-03 15:20:00',
-        members: ['寮犳暀鎺�', '鏉庝富浠�', '鐜嬪尰鐢�', '璧靛鍛�', '閽变笓瀹�'],
-        conclusion: true,
-        chairman: '寮犳暀鎺�'
+      // 椤甸潰妯″紡
+      isEdit: false,
+      // 鍩烘湰淇℃伅
+      id: undefined,
+      caseId: null,
+      caseNo: "",
+
+      // 琛ㄥ崟鏁版嵁
+      form: {
+        // 鍩虹淇℃伅
+        id: undefined,
+        infoid: undefined,
+        caseNo: "",
+        initiateTheme: "",
+        initiatePerson: "",
+
+        // 鐘舵�佸拰鏃堕棿
+        status: "0", // 0:寰呭鏌�, 1:瀹℃煡涓�, 2:瀹℃煡涓, 3:瀹℃煡瀹屾垚
+        startTime: "",
+        cutOffTime: "",
+        endTime: "",
+
+        // 涓撳淇℃伅
+        expertName: "",
+        expertNo: "",
+        expertType: "0",
+        expertConclusion: "",
+        expertOpinion: "",
+        expertTime: "",
+        orderNo: 1,
+
+        // 澶囨敞
+        remark: "",
+
+        // 闄勪欢淇℃伅
+        annexfilesList: [],
+        filePatch: "",
+
+        // 涓撳瀹℃煡鎰忚鍒楄〃
+        ethicalreviewopinionsList: [],
+
+        // 绯荤粺瀛楁
+        createBy: "",
+        createTime: "",
+        updateBy: "",
+        updateTime: "",
+        delFlag: "0"
       },
-      reviewProgress: {
-        active: 4,
-        steps: [
+
+      // 琛ㄥ崟楠岃瘉瑙勫垯
+      rules: {
+        initiateTheme: [
+          { required: true, message: "鍙戣捣涓婚涓嶈兘涓虹┖", trigger: "blur" },
           {
-            title: '鏉愭枡鍒濆',
-            description: '鐢宠鏉愭枡瀹屾暣鎬у鏌�',
-            status: 'finish'
-          },
-          {
-            title: '濮斿憳璇勫',
-            description: '鍚勫鍛樼嫭绔嬪鏌�',
-            status: 'finish'
-          },
-          {
-            title: '浼氳璁ㄨ',
-            description: '濮斿憳浼氶泦浣撹璁�',
-            status: 'finish'
-          },
-          {
-            title: '褰㈡垚鍐宠',
-            description: '鎶曠エ褰㈡垚鏈�缁堝喅璁�',
-            status: 'finish'
+            min: 2,
+            max: 100,
+            message: "闀垮害鍦� 2 鍒� 100 涓瓧绗�",
+            trigger: "blur"
           }
+        ],
+        initiatePerson: [
+          { required: true, message: "鍙戣捣浜轰笉鑳戒负绌�", trigger: "blur" }
+        ],
+        status: [
+          { required: true, message: "瀹℃煡鐘舵�佷笉鑳戒负绌�", trigger: "change" }
+        ],
+        expertName: [
+          { max: 50, message: "闀垮害涓嶈兘瓒呰繃 50 涓瓧绗�", trigger: "blur" }
+        ],
+        expertNo: [
+          { max: 50, message: "闀垮害涓嶈兘瓒呰繃 50 涓瓧绗�", trigger: "blur" }
+        ],
+        expertConclusion: [
+          { max: 2, message: "闀垮害涓嶈兘瓒呰繃 2 涓瓧绗�", trigger: "change" }
+        ],
+        remark: [
+          { max: 500, message: "闀垮害涓嶈兘瓒呰繃 500 涓瓧绗�", trigger: "blur" }
         ]
       },
-      reviewComments: [
-        {
-          memberName: '寮犳暀鎺�',
-          specialty: '鍖诲浼︾悊',
-          comment: '鎹愮尞绋嬪簭绗﹀悎浼︾悊瑙勮寖锛屽悓鎰忛�氳繃',
-          vote: '鍚屾剰',
-          reviewTime: '2023-12-03 14:30:00'
-        },
-        {
-          memberName: '鏉庝富浠�',
-          specialty: '涓村簥鍖诲',
-          comment: '鍖荤枟绋嬪簭瑙勮寖锛屾棤浼︾悊闂',
-          vote: '鍚屾剰',
-          reviewTime: '2023-12-03 14:45:00'
-        },
-        {
-          memberName: '鐜嬪尰鐢�',
-          specialty: '娉曞緥鍖诲',
-          comment: '娉曞緥鏂囦欢榻愬叏锛岀▼搴忓悎娉�',
-          vote: '鍚屾剰',
-          reviewTime: '2023-12-03 15:00:00'
-        }
-      ],
-      resolutionContent: '缁忎鸡鐞嗗鏌ュ鍛樹細瀹℃煡锛岃鍣ㄥ畼鎹愮尞妗堜緥绗﹀悎鍖诲浼︾悊瑕佹眰锛屾崘鐚▼搴忚鑼冿紝瀹跺睘鎰忔効鐪熷疄鏈夋晥锛屽悓鎰忚繘琛屽櫒瀹樻崘鐚��'
+
+      // 淇濆瓨鍔犺浇鐘舵��
+      saveLoading: false,
+      completeLoading: false,
+      suspendLoading: false,
+      endLoading: false,
+      sending: false,
+
+      // 闄勪欢鐩稿叧
+      attachmentFileList: [],
+
+      // 棰勮鐩稿叧
+      previewVisible: false,
+      currentPreviewFile: null,
+
+      // 涓撳瀹℃煡鏁版嵁
+      expertReviews: [],
+      expertLoading: false,
+      attachmentLoading: false,
+
+      // 娣诲姞涓撳瀵硅瘽妗�
+      expertDialogVisible: false,
+      expertList: [],
+      expertListLoading: false,
+      expertSearchQuery: "",
+      filterExpertType: "",
+      expertPage: {
+        pageNum: 1,
+        pageSize: 50
+      },
+      expertTotal: 0,
+      selectedExperts: [],
+
+      // 鍙戦�佸璇濇
+      sendDialogVisible: false,
+      sendForm: {
+        expertType: "normal",
+        expertIds: [],
+        startTime: "",
+        endTime: "",
+        sendType: "0",
+        title: "浼︾悊瀹℃煡浠诲姟閫氱煡",
+        content: "",
+        url: ""
+      },
+
+      // 涓撳鍘嗗彶瀹℃壒鎯呭喌
+      expertHistoryDialogVisible: false,
+      expertHistoryLoading: false,
+      expertHistoryData: null,
+      currentExpertInfo: {},
+
+      // 褰撳墠鍙戦�佺殑涓撳
+      currentSendExperts: []
     };
   },
+  computed: {
+    // 璁$畻灞炴�э細鑾峰彇涓撳鍒楄〃锛堢‘淇濆搷搴斿紡锛�
+    ethicalreviewopinionsList() {
+      return this.form.ethicalreviewopinionsList || [];
+    },
+
+    // 璁$畻灞炴�э細鏅�氫笓瀹舵暟閲�
+    normalExpertsCount() {
+      return this.ethicalreviewopinionsList.filter(
+        expert => expert.expertType === "0"
+      ).length;
+    },
+
+    // 璁$畻灞炴�э細涓诲涓撳鏁伴噺
+    chiefExpertsCount() {
+      return this.ethicalreviewopinionsList.filter(
+        expert => expert.expertType === "1"
+      ).length;
+    },
+
+    // 璁$畻灞炴�э細鎬讳笓瀹舵暟閲�
+    totalExpertsCount() {
+      return this.ethicalreviewopinionsList.length;
+    },
+
+    // 璁$畻灞炴�э細宸插悓鎰忎笓瀹舵暟閲�
+    approvedExpertsCount() {
+      return this.ethicalreviewopinionsList.filter(
+        expert => expert.expertconclusion === "1"
+      ).length;
+    },
+
+    // 璁$畻灞炴�э細鎬讳綋缁撹
+    overallConclusionText() {
+      const total = this.totalExpertsCount;
+      const approved = this.approvedExpertsCount;
+
+      if (total === 0) return "鏈鏌�";
+      if (approved >= Math.ceil(total * 0.7)) {
+        // 瓒呰繃70%鍚屾剰
+        return "閫氳繃";
+      } else if (approved >= Math.ceil(total * 0.5)) {
+        // 瓒呰繃50%鍚屾剰
+        return "淇敼鍚庨�氳繃";
+      } else {
+        return "涓嶉�氳繃";
+      }
+    },
+
+    overallConclusionFilter() {
+      const total = this.totalExpertsCount;
+      const approved = this.approvedExpertsCount;
+
+      if (total === 0) return "info";
+      if (approved >= Math.ceil(total * 0.7)) {
+        return "success";
+      } else if (approved >= Math.ceil(total * 0.5)) {
+        return "warning";
+      } else {
+        return "danger";
+      }
+    },
+
+    // 鍙彂閫佺殑鏅�氫笓瀹�
+    availableNormalExperts() {
+      return this.ethicalreviewopinionsList.filter(
+        expert =>
+          expert.expertType === "0" &&
+          (!expert.receiveStatus ||
+            expert.receiveStatus === "0" ||
+            expert.receiveStatus === "1")
+      );
+    },
+
+    // 鍙彂閫佺殑涓诲涓撳
+    availableChiefExperts() {
+      return this.ethicalreviewopinionsList.filter(
+        expert =>
+          expert.expertType === "1" &&
+          (!expert.receiveStatus ||
+            expert.receiveStatus === "0" ||
+            expert.receiveStatus === "1")
+      );
+    },
+
+    // 鏄惁鍙互鍙戦�佺粰鏅�氫笓瀹�
+    canSendToNormalExperts() {
+      return this.availableNormalExperts.length > 0;
+    },
+
+    // 鏄惁鍙互鍙戦�佺粰涓诲涓撳锛堥渶瑕佽嚦灏�12涓櫘閫氫笓瀹跺悓鎰忥級
+    canSendToChiefExpert() {
+      const normalApprovedCount = this.ethicalreviewopinionsList.filter(
+        expert => expert.expertType === "0" && expert.expertconclusion === "1"
+      ).length;
+      return this.availableChiefExperts.length > 0 && normalApprovedCount >= 12;
+    },
+
+    // 宸插瓨鍦ㄧ殑涓撳缂栧彿鍒楄〃
+    existingExpertNos() {
+      return this.ethicalreviewopinionsList
+        .map(expert => expert.expertNo)
+        .filter(no => no);
+    },
+
+    // 宸插瓨鍦ㄧ殑涓撳濮撳悕鍒楄〃
+    existingExpertNames() {
+      return this.ethicalreviewopinionsList
+        .map(expert => expert.expertname)
+        .filter(name => name);
+    },
+
+    // 杩囨护鍚庣殑涓撳鍒楄〃锛堟帓闄ゅ凡瀛樺湪鐨勪笓瀹讹級
+    filteredExpertList() {
+      if (!this.expertList.length) return [];
+
+      return this.expertList.filter(expert => {
+        // 濡傛灉涓撳鏈夌紪鍙凤紝妫�鏌ョ紪鍙锋槸鍚﹀凡瀛樺湪
+        if (expert.userno && this.existingExpertNos.includes(expert.userno)) {
+          return false;
+        }
+        // 濡傛灉涓撳鏈夊鍚嶏紝妫�鏌ュ鍚嶆槸鍚﹀凡瀛樺湪
+        if (
+          expert.username &&
+          this.existingExpertNames.includes(expert.username)
+        ) {
+          return false;
+        }
+        return true;
+      });
+    },
+
+    // 杩囨护鍚庣殑涓撳鎬绘暟
+    filteredExpertTotal() {
+      return this.filteredExpertList.length;
+    },
+
+    // 褰撳墠鐢ㄦ埛淇℃伅
+    currentUser() {
+      return JSON.parse(sessionStorage.getItem("user") || "{}");
+    },
+
+    // 鍙戦�佸璇濇鏍囬
+    sendDialogTitle() {
+      if (this.sendForm.expertType === "chief") {
+        return "鍙戦�佷富濮斾笓瀹跺鏌�";
+      } else if (this.sendForm.expertType === "normal") {
+        return "鍙戦�佹櫘閫氫笓瀹跺鏌�";
+      } else {
+        return "鍙戦�佷笓瀹跺鏌�";
+      }
+    }
+  },
+  watch: {
+    // 鐩戝惉琛ㄥ崟涓殑涓撳鍒楄〃鍙樺寲
+    "form.ethicalreviewopinionsList": {
+      handler(newVal) {
+        console.log("涓撳鍒楄〃鍙樺寲:", newVal);
+      },
+      deep: true
+    }
+  },
+  created() {
+
+    this.caseId = this.infoid;
+    this.getDetail(this.infoid, this.id);
+  },
   methods: {
-    getAlertType() {
-      const status = this.stageData.status;
-      return status === 'completed' ? 'success' :
-             status === 'in_progress' ? 'warning' : 'info';
+    // 鍒濆鍖栨柊澧炴暟鎹�
+    initNewData() {
+      this.form.infoid = this.infoid;
+      this.form.caseNo = this.$route.query.caseNo || "";
+      this.form.initiatePerson = this.currentUser.username || "褰撳墠鐢ㄦ埛";
+      this.form.startTime = new Date()
+        .toISOString()
+        .replace("T", " ")
+        .substring(0, 19);
+      this.form.createBy = this.currentUser.username || "admin";
     },
-    getAlertDescription() {
-      const status = this.stageData.status;
-      return status === 'completed' ? '浼︾悊瀹℃煡宸查�氳繃锛屽彲浠ヨ繘琛屽櫒瀹樺垎閰�' :
-             status === 'in_progress' ? '浼︾悊瀹℃煡娴佺▼姝e湪杩涜涓�' : '绛夊緟寮�濮嬩鸡鐞嗗鏌ユ祦绋�';
+
+    // 鑾峰彇璇︽儏
+    async getDetail(infoid, id) {
+      try {
+        this.expertLoading = true;
+        let response = {};
+        if (id) {
+          response = await ethicalreviewInfo(id);
+        } else if (infoid) {
+          response = await reviewinitiateBaseInfoList({ infoid: infoid });
+        }
+
+        if (response.code === 200) {
+          let detailData = {};
+
+          if (response.data && id) {
+            this.form = response.data;
+            // 瑙f瀽 filePatch 瀛楁
+            this.parseFilePatch(this.form.filePatch);
+            this.initAttachmentFileList();
+
+            // 濡傛灉涓撳瀹℃煡鎰忚鍒楄〃涓嶅瓨鍦紝鍒濆鍖栦负绌烘暟缁�
+            if (!this.form.ethicalreviewopinionsList) {
+              this.$set(this.form, "ethicalreviewopinionsList", []);
+            }
+          } else if (response.data && infoid) {
+            this.form = response.data[0];
+            // 瑙f瀽 filePatch 瀛楁
+            this.parseFilePatch(this.form.filePatch);
+            this.initAttachmentFileList();
+
+            // 濡傛灉涓撳瀹℃煡鎰忚鍒楄〃涓嶅瓨鍦紝鍒濆鍖栦负绌烘暟缁�
+            if (!this.form.ethicalreviewopinionsList) {
+              this.$set(this.form, "ethicalreviewopinionsList", []);
+            }
+          }
+
+          // 璁剧疆 expertReviews 鐢ㄤ簬琛ㄦ牸鏄剧ず
+          this.expertReviews = this.form.ethicalreviewopinionsList;
+
+          this.$message.success("鏁版嵁鍔犺浇鎴愬姛");
+        } else {
+          this.$message.error("鑾峰彇璇︽儏澶辫触锛�" + (response.msg || "鏈煡閿欒"));
+        }
+      } catch (error) {
+        console.error("鑾峰彇浼︾悊瀹℃煡璇︽儏澶辫触:", error);
+        this.$message.error("鏁版嵁鍔犺浇澶辫触");
+      } finally {
+        this.expertLoading = false;
+      }
     },
-    handleViewResolution() {
-      this.$message.info('鏌ョ湅浼︾悊瀹℃煡鍐宠鏂囦欢鍔熻兘');
+
+    // 瑙f瀽 filePatch 瀛楁
+    parseFilePatch(filePatch) {
+      if (!filePatch) {
+        this.form.annexfilesList = [];
+        return;
+      }
+
+      try {
+        this.form.annexfilesList = JSON.parse(filePatch);
+      } catch (error) {
+        console.error("瑙f瀽 filePatch 瀛楁澶辫触:", error);
+        this.form.annexfilesList = [];
+      }
+    },
+
+    // 鍒濆鍖栭檮浠舵枃浠跺垪琛�
+    initAttachmentFileList() {
+      if (this.form.annexfilesList && this.form.annexfilesList.length > 0) {
+        this.attachmentFileList = this.form.annexfilesList.map(item => ({
+          uid:
+            item.id ||
+            Math.random()
+              .toString(36)
+              .substr(2, 9),
+          name: item.fileName,
+          url: item.path || item.fileUrl,
+          status: "success"
+        }));
+      } else {
+        this.attachmentFileList = [];
+      }
+    },
+
+    // 鏋勫缓 filePatch 瀛楁
+    buildFilePatch() {
+      if (!this.form.annexfilesList || this.form.annexfilesList.length === 0) {
+        return "";
+      }
+      return JSON.stringify(this.form.annexfilesList);
+    },
+
+    // 闄勪欢鍙樺寲澶勭悊
+    handleAttachmentChange(fileList) {
+      this.attachmentFileList = fileList;
+    },
+
+    // 闄勪欢绉婚櫎澶勭悊
+    handleAttachmentRemove(file) {
+      if (file.url) {
+        const index = this.form.annexfilesList.findIndex(
+          item => item.path === file.url || item.fileUrl === file.url
+        );
+        if (index > -1) {
+          this.form.annexfilesList.splice(index, 1);
+        }
+      }
+    },
+
+    // 鎵嬪姩鍒犻櫎闄勪欢
+    handleRemoveAttachment(index) {
+      this.form.annexfilesList.splice(index, 1);
+      this.attachmentFileList.splice(index, 1);
+      this.$message.success("闄勪欢鍒犻櫎鎴愬姛");
+    },
+
+    // 涓婁紶鎴愬姛澶勭悊
+    handleUploadSuccess({ file, fileList, response }) {
+      if (response.code === 200) {
+        const attachmentObj = {
+          fileName: file.name,
+          path: response.data || file.url,
+          fileUrl: response.data || file.url,
+          type: this.getFileExtension(file.name),
+          createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
+          infoid: this.infoid,
+          delFlag: 0
+        };
+
+        this.form.annexfilesList.push(attachmentObj);
+        this.$message.success("鏂囦欢涓婁紶鎴愬姛");
+      }
+    },
+
+    // 涓婁紶閿欒澶勭悊
+    handleUploadError({ file, fileList, error }) {
+      console.error("闄勪欢涓婁紶澶辫触:", error);
+      this.$message.error("鏂囦欢涓婁紶澶辫触锛岃閲嶈瘯");
+    },
+
+    // 鏂囦欢棰勮
+    handlePreview(file) {
+      this.currentPreviewFile = {
+        fileName: file.fileName,
+        fileUrl: file.path || file.fileUrl,
+        fileType: this.getFileType(file.fileName)
+      };
+      this.previewVisible = true;
+    },
+
+    // 鏂囦欢涓嬭浇
+    handleDownload(file) {
+      const fileUrl = file.path || file.fileUrl;
+      const fileName = file.fileName;
+
+      if (fileUrl) {
+        const link = document.createElement("a");
+        link.href = fileUrl;
+        link.download = fileName;
+        link.style.display = "none";
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+        this.$message.success("寮�濮嬩笅杞芥枃浠�");
+      } else {
+        this.$message.warning("鏂囦欢璺緞涓嶅瓨鍦紝鏃犳硶涓嬭浇");
+      }
+    },
+
+    // 鑾峰彇鏂囦欢绫诲瀷
+    getFileType(fileName) {
+      if (!fileName) return "other";
+
+      const extension = fileName
+        .split(".")
+        .pop()
+        .toLowerCase();
+      const imageTypes = ["jpg", "jpeg", "png", "gif", "bmp", "webp"];
+      const pdfTypes = ["pdf"];
+      const officeTypes = ["doc", "docx", "xls", "xlsx", "ppt", "pptx"];
+
+      if (imageTypes.includes(extension)) return "image";
+      if (pdfTypes.includes(extension)) return "pdf";
+      if (officeTypes.includes(extension)) return "office";
+      return "other";
+    },
+
+    // 鑾峰彇鏂囦欢鎵╁睍鍚�
+    getFileExtension(filename) {
+      return filename
+        .split(".")
+        .pop()
+        .toLowerCase();
+    },
+
+    // 鏃ユ湡鏃堕棿鏍煎紡鍖�
+    formatDateTime(dateTime) {
+      if (!dateTime) return "";
+
+      try {
+        const date = new Date(dateTime);
+        if (isNaN(date.getTime())) return dateTime;
+
+        const year = date.getFullYear();
+        const month = String(date.getMonth() + 1).padStart(2, "0");
+        const day = String(date.getDate()).padStart(2, "0");
+        const hours = String(date.getHours()).padStart(2, "0");
+        const minutes = String(date.getMinutes()).padStart(2, "0");
+        const seconds = String(date.getSeconds()).padStart(2, "0");
+
+        return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+      } catch (error) {
+        return dateTime;
+      }
+    },
+
+    // 鍒ゆ柇鏄惁涓轰富浠诲鍛�
+    getIsChiefExpert(expert) {
+      // 鑱岀О鍖呭惈"涓讳换濮斿憳"鎴栬�卐xpertType涓�"1"
+      return (
+        (expert.title && expert.title.includes("涓讳换濮斿憳")) ||
+        expert.expertType === "1"
+      );
+    },
+
+    // 涓撳绫诲瀷鏂囨湰杞崲
+    getExpertTypeText(type) {
+      return type === "1" ? "涓诲涓撳" : "鏅�氫笓瀹�";
+    },
+
+    // 瀹℃煡鐘舵�佽繃婊ゅ櫒
+    getReviewStatusFilter(status) {
+      const statusMap = {
+        "0": "info", // 寰呮帴鏀�
+        "1": "warning", // 鏈帴鏀�
+        "2": "success", // 宸叉帴鏀�
+        "3": "danger", // 瓒呮椂
+        "4": "danger", // 涓
+        "5": "success" // 瀹屾垚
+      };
+      return statusMap[status] || "info";
+    },
+
+    getReviewStatusText(status) {
+      const statusMap = {
+        "0": "寰呮帴鏀�",
+        "1": "鏈帴鏀�",
+        "2": "宸叉帴鏀�",
+        "3": "瓒呮椂",
+        "4": "涓",
+        "5": "瀹屾垚"
+      };
+      return statusMap[status] || "鏈煡";
+    },
+
+    // 缁撹杩囨护鍣�
+    getConclusionFilter(conclusion) {
+      const conclusionMap = {
+        "1": "success", // 鍚屾剰
+        "2": "warning", // 瀹℃煡涓�
+        "0": "danger" // 涓嶅悓鎰�
+      };
+      return conclusionMap[conclusion] || "info";
+    },
+
+    getConclusionText(conclusion) {
+      const conclusionMap = {
+        "1": "鍚屾剰",
+        "2": "瀹℃煡涓�",
+        "0": "涓嶅悓鎰�"
+      };
+      return conclusionMap[conclusion] || "鏈煡";
+    },
+
+    // 涓撳琛屾牱寮�
+    getExpertRowClassName({ row }) {
+      return row.expertType === "1" ? "chief-expert-row" : "normal-expert-row";
+    },
+
+    // 鑾峰彇涓撳鍞竴鏍囪瘑
+    getExpertKey(expert) {
+      return expert.id || expert.expertNo || expert.expertname;
+    },
+
+    // 涓撳绫诲瀷鍙樻洿澶勭悊
+    handleExpertTypeChange() {
+      if (this.sendForm.expertType === "chief") {
+        // 涓诲涓撳鏃犻渶璁剧疆鎴鏃堕棿
+        this.sendForm.endTime = "";
+      } else {
+        // 鏅�氫笓瀹堕噸缃埅姝㈡椂闂翠负褰撳墠鏃堕棿
+        this.sendForm.endTime = "";
+      }
+    },
+
+    // 璁剧疆鎴鏃堕棿蹇嵎閿�
+    setEndTime(hours) {
+      const now = new Date();
+      const endTime = new Date(now.getTime() + hours * 60 * 60 * 1000);
+      this.sendForm.endTime = endTime
+        .toISOString()
+        .replace("T", " ")
+        .substring(0, 19);
+    },
+
+    // 淇濆瓨淇℃伅
+    async handleSave() {
+      this.$refs.form.validate(async valid => {
+        if (valid) {
+          this.saveLoading = true;
+          // 淇濆瓨娓呯┖id渚夸簬鍚庣鏁翠綋鍒犻櫎鏂板
+          this.form.ethicalreviewopinionsList.forEach(item=>{
+            item.id=null
+          })
+          try {
+            const submitData = {
+              ...this.form,
+              // 纭繚蹇呰瀛楁
+              infoid: this.infoid,
+              caseNo: this.caseNo,
+              // 鏋勫缓 filePatch 瀛楁
+              filePatch: this.buildFilePatch()
+            };
+
+            let response = null;
+
+            if (submitData.id) {
+              response = await ethicalreviewedit(submitData);
+            } else {
+              response = await ethicalreviewadd(submitData);
+            }
+
+            if (response.code === 200) {
+              this.$message.success("淇濆瓨鎴愬姛");
+              this.isEdit = false;
+              if (!this.form.id && response.data && response.data.id) {
+                this.form.id = response.data.id;
+                this.$router.replace({
+                  query: { ...this.$route.query, id: this.form.id }
+                });
+              }
+            } else {
+              this.$message.error("淇濆瓨澶辫触锛�" + (response.msg || "鏈煡閿欒"));
+            }
+          } catch (error) {
+            console.error("淇濆瓨澶辫触:", error);
+            this.$message.error("淇濆瓨澶辫触锛岃閲嶈瘯");
+          } finally {
+            this.saveLoading = false;
+          }
+        }
+      });
+    },
+
+    // 瀹℃煡瀹屾垚
+    async handleCompleteReview() {
+      this.$confirm("纭畾瑕佸皢鏈浼︾悊瀹℃煡鐘舵�佽涓哄畬鎴愬悧锛�", "鎻愮ず", {
+        confirmButtonText: "纭畾",
+        cancelButtonText: "鍙栨秷",
+        type: "warning"
+      })
+        .then(async () => {
+          this.completeLoading = true;
+          try {
+            const updateData = {
+              ...this.form,
+              status: "3", // 瀹℃煡瀹屾垚
+              endTime: new Date()
+                .toISOString()
+                .replace("T", " ")
+                .substring(0, 19)
+            };
+
+            const response = await ethicalreviewedit(updateData);
+
+            if (response.code === 200) {
+              this.$message.success("瀹℃煡鐘舵�佸凡鏇存柊涓哄畬鎴�");
+              this.form.status = "3";
+              this.form.endTime = updateData.endTime;
+            } else {
+              this.$message.error("鎿嶄綔澶辫触锛�" + (response.msg || "鏈煡閿欒"));
+            }
+          } catch (error) {
+            console.error("鏇存柊瀹℃煡鐘舵�佸け璐�:", error);
+            this.$message.error("鏇存柊瀹℃煡鐘舵�佸け璐�");
+          } finally {
+            this.completeLoading = false;
+          }
+        })
+        .catch(() => {});
+    },
+
+    // 瀹℃煡涓
+    async handleSuspendReview() {
+      this.$confirm(
+        "纭畾瑕佷腑姝㈡湰娆′鸡鐞嗗鏌ュ悧锛熸墍鏈変笓瀹剁殑瀹℃煡鐘舵�佸皢鍙樹负涓銆�",
+        "鎻愮ず",
+        {
+          confirmButtonText: "纭畾",
+          cancelButtonText: "鍙栨秷",
+          type: "warning"
+        }
+      )
+        .then(async () => {
+          this.suspendLoading = true;
+          try {
+            // 鏇存柊鎵�鏈変笓瀹剁殑鎺ユ敹鐘舵�佷负涓
+            if (
+              this.form.ethicalreviewopinionsList &&
+              this.form.ethicalreviewopinionsList.length > 0
+            ) {
+              this.form.ethicalreviewopinionsList.forEach(expert => {
+                expert.receiveStatus = "4"; // 涓鐘舵��
+                expert.updateTime = new Date()
+                  .toISOString()
+                  .replace("T", " ")
+                  .substring(0, 19);
+              });
+            }
+
+            const updateData = {
+              ...this.form,
+              status: "2" // 瀹℃煡涓
+            };
+
+            const response = await ethicalreviewedit(updateData);
+
+            if (response.code === 200) {
+              this.$message.success("瀹℃煡宸蹭腑姝紝鎵�鏈変笓瀹剁姸鎬佸凡鏇存柊");
+              this.form.status = "2";
+            } else {
+              this.$message.error("鎿嶄綔澶辫触锛�" + (response.msg || "鏈煡閿欒"));
+            }
+          } catch (error) {
+            console.error("涓瀹℃煡澶辫触:", error);
+            this.$message.error("涓瀹℃煡澶辫触");
+          } finally {
+            this.suspendLoading = false;
+          }
+        })
+        .catch(() => {});
+    },
+
+    // 缁撴潫瀹℃煡
+    async handleEndReview() {
+      this.$confirm(
+        "纭畾瑕佺粨鏉熸湰娆′鸡鐞嗗鏌ュ悧锛熺粨鏉熷悗灏嗘棤娉曚慨鏀逛笓瀹跺鏌ョ粨鏋溿��",
+        "鎻愮ず",
+        {
+          confirmButtonText: "纭畾",
+          cancelButtonText: "鍙栨秷",
+          type: "warning"
+        }
+      )
+        .then(async () => {
+          this.endLoading = true;
+          try {
+            const updateData = {
+              ...this.form,
+              status: "2", // 瀹℃煡涓
+              endTime: new Date()
+                .toISOString()
+                .replace("T", " ")
+                .substring(0, 19)
+            };
+
+            const response = await ethicalreviewedit(updateData);
+
+            if (response.code === 200) {
+              this.$message.success("瀹℃煡宸茬粨鏉�");
+              this.form.status = "2";
+              this.form.endTime = updateData.endTime;
+            } else {
+              this.$message.error("鎿嶄綔澶辫触锛�" + (response.msg || "鏈煡閿欒"));
+            }
+          } catch (error) {
+            console.error("缁撴潫瀹℃煡澶辫触:", error);
+            this.$message.error("缁撴潫瀹℃煡澶辫触");
+          } finally {
+            this.endLoading = false;
+          }
+        })
+        .catch(() => {});
+    },
+
+    // 鎵撳紑娣诲姞涓撳瀵硅瘽妗�
+    handleAddExpert() {
+      this.expertDialogVisible = true;
+      this.loadExperts();
+    },
+
+    // 鍔犺浇涓撳鍒楄〃
+    async loadExperts() {
+      try {
+        this.expertListLoading = true;
+        const params = {
+          usertype: "浼︾悊涓撳", // 浼︾悊涓撳
+          pageNum: this.expertPage.pageNum,
+          pageSize: this.expertPage.pageSize
+        };
+
+        if (this.expertSearchQuery) {
+          params.username = this.expertSearchQuery;
+        }
+
+        const response = await listExternalperson(params);
+        if (response.code === 200) {
+          this.expertList = response.rows || [];
+          this.expertTotal = response.total || 0;
+        } else {
+          this.$message.error(
+            "鍔犺浇涓撳鍒楄〃澶辫触锛�" + (response.msg || "鏈煡閿欒")
+          );
+        }
+      } catch (error) {
+        console.error("鍔犺浇涓撳鍒楄〃澶辫触:", error);
+        this.$message.error("鍔犺浇涓撳鍒楄〃澶辫触");
+      } finally {
+        this.expertListLoading = false;
+      }
+    },
+
+    // 鎼滅储涓撳
+    handleSearchExperts() {
+      this.expertPage.pageNum = 1;
+      this.loadExperts();
+    },
+
+    // 閲嶇疆鎼滅储
+    handleResetSearch() {
+      this.expertSearchQuery = "";
+      this.filterExpertType = "";
+      this.expertPage.pageNum = 1;
+      this.loadExperts();
+    },
+
+    // 涓撳閫夋嫨鍙樺寲
+    handleExpertSelectionChange(selection) {
+      this.selectedExperts = selection;
+    },
+
+    // 纭娣诲姞涓撳
+    handleConfirmAddExpert() {
+      if (this.selectedExperts.length === 0) {
+        this.$message.warning("璇烽�夋嫨瑕佹坊鍔犵殑涓撳");
+        return;
+      }
+
+      // 纭繚ethicalreviewopinionsList瀛樺湪
+      if (!this.form.ethicalreviewopinionsList) {
+        this.$set(this.form, "ethicalreviewopinionsList", []);
+      }
+
+      // 娣诲姞涓撳鍒板垪琛�
+      this.selectedExperts.forEach(expert => {
+        // 鍒ゆ柇鏄惁涓轰富浠诲鍛�
+        const isChief = this.getIsChiefExpert(expert);
+
+        const expertReview = {
+          id: undefined,
+          infoid: this.infoid,
+          nitiateId: this.form.id || undefined,
+          caseNo: this.form.caseNo,
+          expertname: expert.username,
+          expertNo: expert.userno,
+          expertType: isChief ? "1" : "0", // 涓讳换濮斿憳璁剧疆涓轰富濮斾笓瀹�
+          deptName: expert.unitname || "",
+          title: expert.title || "",
+          deptname: expert.telephone || "",
+          receiveStatus: "0", // 寰呮帴鏀�
+          expertconclusion: "",
+          expertopinion: "",
+          conclusionannex: "",
+          conclusionorder: this.form.ethicalreviewopinionsList.length + 1,
+          conclusiontime: "",
+          startTime: "",
+          endTime: "",
+          sendType: "",
+          createBy: this.currentUser.username || "admin",
+          createTime: new Date()
+            .toISOString()
+            .replace("T", " ")
+            .substring(0, 19),
+          updateBy: this.currentUser.username || "admin",
+          updateTime: new Date()
+            .toISOString()
+            .replace("T", " ")
+            .substring(0, 19),
+          delFlag: "0"
+        };
+
+        // 浣跨敤push娣诲姞锛岀‘淇濆搷搴斿紡
+        this.form.ethicalreviewopinionsList.push(expertReview);
+      });
+
+      this.$message.success(`鎴愬姛娣诲姞 ${this.selectedExperts.length} 浣嶄笓瀹禶);
+      this.expertDialogVisible = false;
+      this.selectedExperts = [];
+    },
+
+    // 娣诲姞涓撳瀵硅瘽妗嗗叧闂�
+    handleExpertDialogClose() {
+      this.selectedExperts = [];
+      this.expertSearchQuery = "";
+      this.filterExpertType = "";
+      this.expertPage.pageNum = 1;
+    },
+
+    // 椤电爜鍙樺寲
+    handlePageChange(pageNum) {
+      this.expertPage.pageNum = pageNum;
+      this.loadExperts();
+    },
+
+    // 姣忛〉鏉℃暟鍙樺寲
+    handlePageSizeChange(pageSize) {
+      this.expertPage.pageSize = pageSize;
+      this.expertPage.pageNum = 1;
+      this.loadExperts();
+    },
+
+    // 鍙戦�佺粰鏅�氫笓瀹�
+    handleSendToNormalExperts() {
+      this.currentSendExperts = this.availableNormalExperts;
+      this.sendForm.expertType = "normal";
+      this.sendForm.endTime = ""; // 閲嶇疆鎴鏃堕棿
+      this.sendDialogVisible = true;
+    },
+
+    // 鍙戦�佺粰涓诲涓撳
+    handleSendToChiefExpert() {
+      this.currentSendExperts = this.availableChiefExperts;
+      this.sendForm.expertType = "chief";
+      this.sendForm.endTime = ""; // 涓诲涓撳鏃犻渶鎴鏃堕棿
+      this.sendDialogVisible = true;
+    },
+
+    // 鍙戦�佺粰鍗曚釜涓撳
+    handleSendToExpert(expert) {
+      this.currentSendExperts = [expert];
+      this.sendForm.expertType = expert.expertType === "1" ? "chief" : "normal";
+      this.sendForm.endTime = expert.expertType === "1" ? "" : ""; // 涓诲涓撳鏃犻渶鎴鏃堕棿
+      this.sendDialogVisible = true;
+    },
+
+    // 鍙戦�佸璇濇鍏抽棴
+    handleSendDialogClose() {
+      this.sendForm = {
+        expertType: "normal",
+        expertIds: [],
+        startTime: "",
+        endTime: "",
+        sendType: "0",
+        title: "浼︾悊瀹℃煡浠诲姟閫氱煡",
+        content: "",
+        url: ""
+      };
+      this.currentSendExperts = [];
+    },
+
+    // 纭鍙戦��
+    async handleSendConfirm() {
+      if (!this.sendForm.startTime) {
+        this.$message.warning("璇烽�夋嫨鍙戦�佹椂闂�");
+        return;
+      }
+
+      // 鏅�氫笓瀹堕渶瑕佹埅姝㈡椂闂达紝涓诲涓撳涓嶉渶瑕�
+      if (this.sendForm.expertType !== "chief" && !this.sendForm.endTime) {
+        this.$message.warning("璇烽�夋嫨鎴鏃堕棿");
+        return;
+      }
+
+      if (!this.sendForm.sendType) {
+        this.$message.warning("璇烽�夋嫨鍙戦�佹柟寮�");
+        return;
+      }
+
+      if (!this.sendForm.title) {
+        this.$message.warning("璇疯緭鍏ュ彂閫佹爣棰�");
+        return;
+      }
+
+      if (!this.sendForm.content) {
+        this.$message.warning("璇疯緭鍏ュ彂閫佸唴瀹�");
+        return;
+      }
+
+      if (this.currentSendExperts.length === 0) {
+        this.$message.warning("娌℃湁鎵惧埌鍙彂閫佺殑涓撳");
+        return;
+      }
+
+      this.sending = true;
+      try {
+        // 鍙戦�佺粰姣忎釜涓撳
+        const sendPromises = this.currentSendExperts.map(async expert => {
+          try {
+            // 鏋勫缓鍙戦�佹暟鎹�
+            const sendData = {
+              number: expert.deptname || "", // 鐢ㄦ埛鎵嬫満鍙�
+              title: this.sendForm.title,
+              url: this.sendForm.url || "",
+
+              createTime: new Date()
+                .toISOString()
+                .replace("T", " ")
+                .substring(0, 19)
+            };
+
+            // 璋冪敤鍙戦�侀�氱煡鎺ュ彛
+            const response = await sendNotification(sendData);
+
+            if (response.code === 200) {
+              // 鏇存柊涓撳鐘舵��
+              const index = this.form.ethicalreviewopinionsList.findIndex(
+                e =>
+                  e.expertNo === expert.expertNo ||
+                  e.expertname === expert.expertname
+              );
+
+              if (index !== -1) {
+                this.form.ethicalreviewopinionsList[index].receiveStatus = "1"; // 宸叉帴鏀�
+                this.form.ethicalreviewopinionsList[
+                  index
+                ].startTime = this.sendForm.startTime;
+                this.form.ethicalreviewopinionsList[
+                  index
+                ].endTime = this.sendForm.endTime;
+                this.form.ethicalreviewopinionsList[
+                  index
+                ].sendType = this.sendForm.sendType;
+                this.form.ethicalreviewopinionsList[
+                  index
+                ].updateTime = new Date()
+                  .toISOString()
+                  .replace("T", " ")
+                  .substring(0, 19);
+
+                // 浣跨敤Vue.set纭繚鍝嶅簲寮忔洿鏂�
+                this.$set(
+                  this.form.ethicalreviewopinionsList,
+                  index,
+                  this.form.ethicalreviewopinionsList[index]
+                );
+              }
+
+              return { success: true, expert: expert.expertname };
+            } else {
+              return {
+                success: false,
+                expert: expert.expertname,
+                error: response.msg
+              };
+            }
+          } catch (error) {
+            console.error(`鍙戦�佺粰涓撳 ${expert.expertname} 澶辫触:`, error);
+            return {
+              success: false,
+              expert: expert.expertname,
+              error: error.message
+            };
+          }
+        });
+
+        // 绛夊緟鎵�鏈夊彂閫佸畬鎴�
+        const results = await Promise.all(sendPromises);
+
+        // 缁熻鍙戦�佺粨鏋�
+        const successCount = results.filter(r => r.success).length;
+        const failCount = results.filter(r => !r.success).length;
+
+        if (failCount === 0) {
+          this.$message.success(`鎴愬姛鍙戦�佺粰 ${successCount} 浣嶄笓瀹禶);
+        } else if (successCount > 0) {
+          this.$message.warning(
+            `鎴愬姛鍙戦�佺粰 ${successCount} 浣嶄笓瀹讹紝澶辫触 ${failCount} 浣峘
+          );
+        } else {
+          this.$message.error("鍙戦�佸け璐ワ紝璇风◢鍚庨噸璇�");
+        }
+
+        this.sendDialogVisible = false;
+        this.sendForm = {
+          expertType: "normal",
+          expertIds: [],
+          startTime: "",
+          endTime: "",
+          sendType: "0",
+          title: "浼︾悊瀹℃煡浠诲姟閫氱煡",
+          content: "",
+          url: ""
+        };
+        this.currentSendExperts = [];
+      } catch (error) {
+        console.error("鍙戦�佸け璐�:", error);
+        this.$message.error("鍙戦�佸け璐ワ紝璇烽噸璇�");
+      } finally {
+        this.sending = false;
+      }
+    },
+
+    // 鍒犻櫎涓撳瀹℃煡
+    handleDeleteExpertReview(expert, index) {
+      this.$confirm("纭畾瑕佸垹闄よ涓撳鐨勫鏌ヨ褰曞悧锛�", "鎻愮ず", {
+        confirmButtonText: "纭畾",
+        cancelButtonText: "鍙栨秷",
+        type: "warning"
+      })
+        .then(() => {
+          // 浠庢暟缁勪腑鍒犻櫎涓撳
+          this.form.ethicalreviewopinionsList.splice(index, 1);
+          this.$message.success("涓撳瀹℃煡璁板綍宸插垹闄�");
+        })
+        .catch(() => {});
+    },
+
+    // 鏌ョ湅涓撳鍘嗗彶瀹℃壒鎯呭喌
+    async handleViewExpertHistory(expert) {
+      if (!expert.expertNo) {
+        this.$message.warning("璇ヤ笓瀹舵病鏈夌紪鍙凤紝鏃犳硶鏌ヨ鍘嗗彶瀹℃壒鎯呭喌");
+        return;
+      }
+
+      this.currentExpertInfo = expert;
+      this.expertHistoryLoading = true;
+      this.expertHistoryDialogVisible = true;
+
+      try {
+        const params = {
+          expertNo: expert.expertNo
+        };
+
+        const response = await ethicalreExpertTotal(params);
+
+        if (response && response.code === 200) {
+          this.expertHistoryData = response.data || response[0] || null;
+        } else {
+          this.$message.error(
+            "鏌ヨ涓撳鍘嗗彶瀹℃壒鎯呭喌澶辫触锛�" + (response?.msg || "鏈煡閿欒")
+          );
+          this.expertHistoryData = null;
+        }
+      } catch (error) {
+        console.error("鏌ヨ涓撳鍘嗗彶瀹℃壒鎯呭喌澶辫触:", error);
+        this.$message.error("鏌ヨ涓撳鍘嗗彶瀹℃壒鎯呭喌澶辫触");
+        this.expertHistoryData = null;
+      } finally {
+        this.expertHistoryLoading = false;
+      }
+    },
+
+    // 鏃堕棿鏍煎紡鍖�
+    parseTime(time) {
+      if (!time) return "";
+      const date = new Date(time);
+      if (isNaN(date.getTime())) return 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 {
+.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;
+}
+
+.file-info {
+  display: flex;
+  align-items: center;
+}
+
+.empty-attachment {
+  text-align: center;
+  padding: 40px 0;
+  color: #909399;
+}
+
+/* 涓撳绫诲瀷鏍峰紡 */
+.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;
+}
+
+/* 涓撳濮撳悕閾炬帴鏍峰紡 */
+.expert-name-link {
+  color: #409eff;
+  cursor: pointer;
+  text-decoration: none;
+  transition: color 0.3s;
+}
+
+.expert-name-link:hover {
+  color: #66b1ff;
+  text-decoration: underline;
+}
+
+/* 鍘嗗彶缁熻鏍峰紡 */
+.history-stat-item {
+  padding: 10px;
+  border-radius: 4px;
+  background-color: #f5f7fa;
+  text-align: center;
+}
+
+.history-stat-label {
+  font-size: 12px;
+  color: #909399;
+  margin-bottom: 5px;
+}
+
+.history-stat-value {
+  font-size: 18px;
+  font-weight: bold;
+  color: #303133;
+}
+
+.form-section {
+  margin-bottom: 16px;
+}
+
+.section-header {
+  display: flex;
+  align-items: center;
+  font-weight: bold;
+  color: #303133;
+}
+
+.dialog-footer {
   text-align: right;
-  margin-top: 30px;
+  padding: 20px 0 0;
+}
+
+.attachment-section {
+  margin-bottom: 16px;
+}
+
+.attachment-header {
+  display: flex;
+  align-items: center;
+  margin-bottom: 16px;
+  padding: 8px 0;
+  border-bottom: 1px solid #ebeef5;
+}
+
+.attachment-title {
+  font-weight: bold;
+  margin: 0 8px;
+}
+
+.attachment-tip {
+  font-size: 12px;
+  color: #909399;
+}
+
+.attachment-list {
+  margin-top: 16px;
+}
+
+.list-title {
+  font-weight: bold;
+  margin-bottom: 12px;
+  color: #303133;
+}
+
+.file-name {
+  font-size: 13px;
+}
+
+/* 妗堜緥淇℃伅灞曠ず鏍峰紡 */
+.selected-case-info {
+  margin-bottom: 20px;
+}
+
+.case-info-card {
+  border-left: 4px solid #67c23a;
+}
+
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 768px) {
+  .ethics-review-detail {
+    padding: 10px;
+  }
+
+  .expert-stats .el-col {
+    margin-bottom: 10px;
+  }
 }
 </style>

--
Gitblit v1.9.3