From c4599aa279fd2f90b741608392e056fbae2282d5 Mon Sep 17 00:00:00 2001
From: WXL <wl_5969728@163.com>
Date: 星期四, 20 八月 2026 17:16:54 +0800
Subject: [PATCH] 各表单布局调整

---
 src/views/business/course/components/EthicalReviewStage.vue | 1193 ++++++++++++++++++++++++++++++++++-------------------------
 1 files changed, 681 insertions(+), 512 deletions(-)

diff --git a/src/views/business/course/components/EthicalReviewStage.vue b/src/views/business/course/components/EthicalReviewStage.vue
index 2d4b6ce..768af02 100644
--- a/src/views/business/course/components/EthicalReviewStage.vue
+++ b/src/views/business/course/components/EthicalReviewStage.vue
@@ -1,141 +1,31 @@
 <template>
   <div class="ethics-review-detail">
+    <!-- <case-basic-info :case-id="caseId" :show-attachment="true":column="3" /> -->
+
     <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-button
-            type="success"
-            @click="handleCompleteReview"
-            :disabled="form.status == '3' || form.status == '2'"
-            :loading="completeLoading"
-          >
-            瀹℃煡瀹屾垚
-          </el-button>
-
-          <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-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-descriptions title="鍩烘湰淇℃伅" :column="descriptionColumn" border size="small">
+        <el-descriptions-item label="鍙戣捣涓婚">{{ form.initiateTheme || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="鍙戣捣浜�">{{ form.initiatePerson || '-' }}</el-descriptions-item>
+      </el-descriptions>
 
-        <!-- 涓撳鐩稿叧淇℃伅 -->
-
-        <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-descriptions title="瀹℃煡缁撹" :column="descriptionColumn" border size="small" style="margin-top: 16px;">
+        <el-descriptions-item label="浼︾悊瀹℃煡缁撹">{{ expertConclusionLabel }}</el-descriptions-item>
+        <el-descriptions-item label="浼︾悊瀹℃煡缁撹鏃堕棿">{{ form.expertTime || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="瀹℃煡鎰忚" :span="descriptionColumn">{{ form.expertOpinion || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="澶囨敞" :span="descriptionColumn">{{ form.remark || '-' }}</el-descriptions-item>
+      </el-descriptions>
     </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
@@ -167,7 +57,7 @@
               <span>{{ formatDateTime(scope.row.createTime) }}</span>
             </template>
           </el-table-column>
-          <el-table-column label="鎿嶄綔" width="266">
+          <el-table-column label="鎿嶄綔" width="180">
             <template slot-scope="scope">
               <el-button
                 size="mini"
@@ -183,13 +73,6 @@
               >
                 涓嬭浇
               </el-button>
-              <el-button
-                size="mini"
-                type="danger"
-                @click="handleRemoveAttachment(scope.$index)"
-              >
-                鍒犻櫎
-              </el-button>
             </template>
           </el-table-column>
         </el-table>
@@ -204,7 +87,7 @@
           class="el-icon-folder-opened"
           style="font-size: 60px; color: #C0C4CC; margin-bottom: 20px;"
         ></i>
-        <p style="color: #909399; font-size: 14px;">鏆傛棤闄勪欢锛岃涓婁紶鐩稿叧鏂囦欢</p>
+        <p style="color: #909399; font-size: 14px;">鏆傛棤闄勪欢</p>
       </div>
     </el-card>
 
@@ -212,27 +95,6 @@
     <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>
 
       <!-- 涓撳缁熻淇℃伅 -->
@@ -348,7 +210,27 @@
             <span v-else class="no-data">-</span>
           </template>
         </el-table-column>
-
+        <!-- 鍦�"瀹℃煡鏃堕棿"鍒楀悗闈㈡坊鍔�"鎵嬬闄勪欢"鍒� -->
+        <el-table-column label="鎵嬬闄勪欢" width="120" align="center">
+          <template slot-scope="scope">
+            <template v-if="scope.row.sigin">
+              <!-- 鏈夌鍚嶏紝鏄剧ず鍙偣鍑婚瑙堢殑鍥剧墖 -->
+              <el-button
+                type="text"
+                size="mini"
+                @click="handlePreviewSignature(scope.row.sigin)"
+                class="signature-preview-btn"
+              >
+                <i class="el-icon-picture" style="margin-right: 4px;"></i>
+                鏌ョ湅绛惧悕
+              </el-button>
+            </template>
+            <template v-else>
+              <!-- 鏃犵鍚嶏紝鏄剧ず鏂滄潬 -->
+              <span class="no-signature">/</span>
+            </template>
+          </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 }">
@@ -390,246 +272,8 @@
           </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-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>
-
-      <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
@@ -757,22 +401,24 @@
 <script>
 import { getToken } from "@/utils/auth";
 import {
-  ethicalreviewgetInfoID,
+  reviewinitiateBaseInfoList,
   ethicalreviewedit,
   ethicalreviewadd,
   ethicalreviewInfo,
   ethicalreExpertTotal,
-  sendNotification
+  sendNotification,
+  sendcall
 } 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";
+import readonlyDescriptionsMixin from "./readonlyDescriptionsMixin";
 
 export default {
   name: "EthicsReviewDetail",
-  components: { CaseBasicInfo, UploadAttachment, FilePreviewDialog },
+  components: { CaseBasicInfo, FilePreviewDialog },
+  mixins: [readonlyDescriptionsMixin],
   dicts: ["sys_user_sex", "sys_ethical", "Review_status"],
   props: {
     infoid: {
@@ -861,7 +507,17 @@
           { max: 500, message: "闀垮害涓嶈兘瓒呰繃 500 涓瓧绗�", trigger: "blur" }
         ]
       },
-
+      sending: false, // 鍗曚釜鍙戦�佺姸鎬�
+      sendingAll: false, // 鍏ㄥ眬鍙戦�佺姸鎬�
+      sendingProgress: 0, // 鍙戦�佽繘搴�
+      sendingTotal: 0, // 鎬诲彂閫佹暟
+      sendingSuccessCount: 0, // 鎴愬姛鏁�
+      sendingFailCount: 0, // 澶辫触鏁�
+      sendingResults: [], // 鍙戦�佺粨鏋滃垪琛�
+      originalFormData: null, // 鍘熷琛ㄥ崟鏁版嵁
+      originalExpertList: null, // 鍘熷涓撳鍒楄〃
+      originalAttachments: null, // 鍘熷闄勪欢鍒楄〃
+      isDataLoaded: false, // 鏁版嵁鏄惁宸插姞杞�
       // 淇濆瓨鍔犺浇鐘舵��
       saveLoading: false,
       completeLoading: false,
@@ -902,7 +558,6 @@
         startTime: "",
         endTime: "",
         sendType: "0",
-        title: "浼︾悊瀹℃煡浠诲姟閫氱煡",
         content: "",
         url: ""
       },
@@ -923,17 +578,23 @@
       return this.form.ethicalreviewopinionsList || [];
     },
 
+    // 浼︾悊瀹℃煡缁撹鏂囨湰锛堝彧璇诲睍绀猴級
+    expertConclusionLabel() {
+      const map = { 0: "涓嶅悓鎰�", 1: "鍚屾剰", 2: "瀹℃煡涓�" };
+      return map[this.form.expertConclusion] || "-";
+    },
+
     // 璁$畻灞炴�э細鏅�氫笓瀹舵暟閲�
     normalExpertsCount() {
       return this.ethicalreviewopinionsList.filter(
-        expert => expert.expertType === "0"
+        expert => expert.expertType == "0"
       ).length;
     },
 
     // 璁$畻灞炴�э細涓诲涓撳鏁伴噺
     chiefExpertsCount() {
       return this.ethicalreviewopinionsList.filter(
-        expert => expert.expertType === "1"
+        expert => expert.expertType == "1"
       ).length;
     },
 
@@ -945,7 +606,7 @@
     // 璁$畻灞炴�э細宸插悓鎰忎笓瀹舵暟閲�
     approvedExpertsCount() {
       return this.ethicalreviewopinionsList.filter(
-        expert => expert.expertconclusion === "1"
+        expert => expert.expertconclusion == "1"
       ).length;
     },
 
@@ -954,7 +615,7 @@
       const total = this.totalExpertsCount;
       const approved = this.approvedExpertsCount;
 
-      if (total === 0) return "鏈鏌�";
+      if (total == 0) return "鏈鏌�";
       if (approved >= Math.ceil(total * 0.7)) {
         // 瓒呰繃70%鍚屾剰
         return "閫氳繃";
@@ -970,7 +631,7 @@
       const total = this.totalExpertsCount;
       const approved = this.approvedExpertsCount;
 
-      if (total === 0) return "info";
+      if (total == 0) return "info";
       if (approved >= Math.ceil(total * 0.7)) {
         return "success";
       } else if (approved >= Math.ceil(total * 0.5)) {
@@ -984,10 +645,10 @@
     availableNormalExperts() {
       return this.ethicalreviewopinionsList.filter(
         expert =>
-          expert.expertType === "0" &&
+          expert.expertType == "0" &&
           (!expert.receiveStatus ||
-            expert.receiveStatus === "0" ||
-            expert.receiveStatus === "1")
+            expert.receiveStatus == "0" ||
+            expert.receiveStatus == "1")
       );
     },
 
@@ -995,10 +656,10 @@
     availableChiefExperts() {
       return this.ethicalreviewopinionsList.filter(
         expert =>
-          expert.expertType === "1" &&
+          expert.expertType == "1" &&
           (!expert.receiveStatus ||
-            expert.receiveStatus === "0" ||
-            expert.receiveStatus === "1")
+            expert.receiveStatus == "0" ||
+            expert.receiveStatus == "1")
       );
     },
 
@@ -1010,7 +671,7 @@
     // 鏄惁鍙互鍙戦�佺粰涓诲涓撳锛堥渶瑕佽嚦灏�12涓櫘閫氫笓瀹跺悓鎰忥級
     canSendToChiefExpert() {
       const normalApprovedCount = this.ethicalreviewopinionsList.filter(
-        expert => expert.expertType === "0" && expert.expertconclusion === "1"
+        expert => expert.expertType == "0" && expert.expertconclusion == "1"
       ).length;
       return this.availableChiefExperts.length > 0 && normalApprovedCount >= 12;
     },
@@ -1061,9 +722,9 @@
 
     // 鍙戦�佸璇濇鏍囬
     sendDialogTitle() {
-      if (this.sendForm.expertType === "chief") {
+      if (this.sendForm.expertType == "chief") {
         return "鍙戦�佷富濮斾笓瀹跺鏌�";
-      } else if (this.sendForm.expertType === "normal") {
+      } else if (this.sendForm.expertType == "normal") {
         return "鍙戦�佹櫘閫氫笓瀹跺鏌�";
       } else {
         return "鍙戦�佷笓瀹跺鏌�";
@@ -1080,8 +741,15 @@
     }
   },
   created() {
+    this.infoid = this.infoid;
     this.caseId = this.infoid;
     this.getDetail(this.infoid, this.id);
+    // 鐩戝惉璺敱鍙樺寲锛岄槻姝㈢敤鎴风寮�椤甸潰
+    window.addEventListener("beforeunload", this.beforeUnloadHandler);
+  },
+  beforeDestroy() {
+    // 绉婚櫎浜嬩欢鐩戝惉鍣�
+    window.removeEventListener("beforeunload", this.beforeUnloadHandler);
   },
   methods: {
     // 鍒濆鍖栨柊澧炴暟鎹�
@@ -1104,36 +772,47 @@
         if (id) {
           response = await ethicalreviewInfo(id);
         } else if (infoid) {
-          response = await ethicalreviewgetInfoID({ InfoId: infoid });
+          response = await reviewinitiateBaseInfoList({ infoid: infoid });
+          if (response.data[0].id) {
+            this.id = response.data[0].id;
+            response = await ethicalreviewInfo(this.id);
+          }
         }
 
-        if (response.code === 200) {
+        if (
+          !response.data.status ||
+          response.data.status == 0 ||
+          response.data.status == 1
+        ) {
+          response.data.status = "2";
+        }
+        if (response.code == 200) {
           let detailData = {};
 
-          if (response.data && id) {
+          if (response.data && this.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;
-            // 瑙f瀽 filePatch 瀛楁
+          } else if (response.data && this.infoid) {
+            this.form = response.data[0];
             this.parseFilePatch(this.form.filePatch);
             this.initAttachmentFileList();
 
-            // 濡傛灉涓撳瀹℃煡鎰忚鍒楄〃涓嶅瓨鍦紝鍒濆鍖栦负绌烘暟缁�
             if (!this.form.ethicalreviewopinionsList) {
               this.$set(this.form, "ethicalreviewopinionsList", []);
             }
           }
 
-          // 璁剧疆 expertReviews 鐢ㄤ簬琛ㄦ牸鏄剧ず
+          // 淇濆瓨鍘熷鏁版嵁鐢ㄤ簬姣旇緝
+          this.saveOriginalData();
+
           this.expertReviews = this.form.ethicalreviewopinionsList;
+          this.isDataLoaded = true;
 
           this.$message.success("鏁版嵁鍔犺浇鎴愬姛");
         } else {
@@ -1145,6 +824,22 @@
       } finally {
         this.expertLoading = false;
       }
+    },
+
+    // 淇濆瓨鍘熷鏁版嵁
+    saveOriginalData() {
+      // 娣辨嫹璐濊〃鍗曟暟鎹�
+      this.originalFormData = JSON.parse(JSON.stringify(this.form));
+
+      // 娣辨嫹璐濅笓瀹跺垪琛�
+      this.originalExpertList = this.form.ethicalreviewopinionsList
+        ? JSON.parse(JSON.stringify(this.form.ethicalreviewopinionsList))
+        : [];
+
+      // 娣辨嫹璐濋檮浠跺垪琛�
+      this.originalAttachments = this.form.annexfilesList
+        ? JSON.parse(JSON.stringify(this.form.annexfilesList))
+        : [];
     },
 
     // 瑙f瀽 filePatch 瀛楁
@@ -1182,7 +877,7 @@
 
     // 鏋勫缓 filePatch 瀛楁
     buildFilePatch() {
-      if (!this.form.annexfilesList || this.form.annexfilesList.length === 0) {
+      if (!this.form.annexfilesList || this.form.annexfilesList.length == 0) {
         return "";
       }
       return JSON.stringify(this.form.annexfilesList);
@@ -1197,7 +892,7 @@
     handleAttachmentRemove(file) {
       if (file.url) {
         const index = this.form.annexfilesList.findIndex(
-          item => item.path === file.url || item.fileUrl === file.url
+          item => item.path == file.url || item.fileUrl == file.url
         );
         if (index > -1) {
           this.form.annexfilesList.splice(index, 1);
@@ -1214,7 +909,7 @@
 
     // 涓婁紶鎴愬姛澶勭悊
     handleUploadSuccess({ file, fileList, response }) {
-      if (response.code === 200) {
+      if (response.code == 200) {
         const attachmentObj = {
           fileName: file.name,
           path: response.data || file.url,
@@ -1238,10 +933,23 @@
 
     // 鏂囦欢棰勮
     handlePreview(file) {
+      console.log(file, "file");
+
       this.currentPreviewFile = {
         fileName: file.fileName,
         fileUrl: file.path || file.fileUrl,
         fileType: this.getFileType(file.fileName)
+      };
+      this.previewVisible = true;
+    },
+    // 鏂囦欢棰勮
+    handlePreviewSignature(file) {
+      console.log(file, "file");
+
+      this.currentPreviewFile = {
+        fileName: file,
+        fileUrl: file,
+        fileType: "png"
       };
       this.previewVisible = true;
     },
@@ -1317,13 +1025,13 @@
       // 鑱岀О鍖呭惈"涓讳换濮斿憳"鎴栬�卐xpertType涓�"1"
       return (
         (expert.title && expert.title.includes("涓讳换濮斿憳")) ||
-        expert.expertType === "1"
+        expert.expertType == "1"
       );
     },
 
     // 涓撳绫诲瀷鏂囨湰杞崲
     getExpertTypeText(type) {
-      return type === "1" ? "涓诲涓撳" : "鏅�氫笓瀹�";
+      return type == "1" ? "涓诲涓撳" : "鏅�氫笓瀹�";
     },
 
     // 瀹℃煡鐘舵�佽繃婊ゅ櫒
@@ -1372,7 +1080,7 @@
 
     // 涓撳琛屾牱寮�
     getExpertRowClassName({ row }) {
-      return row.expertType === "1" ? "chief-expert-row" : "normal-expert-row";
+      return row.expertType == "1" ? "chief-expert-row" : "normal-expert-row";
     },
 
     // 鑾峰彇涓撳鍞竴鏍囪瘑
@@ -1382,7 +1090,7 @@
 
     // 涓撳绫诲瀷鍙樻洿澶勭悊
     handleExpertTypeChange() {
-      if (this.sendForm.expertType === "chief") {
+      if (this.sendForm.expertType == "chief") {
         // 涓诲涓撳鏃犻渶璁剧疆鎴鏃堕棿
         this.sendForm.endTime = "";
       } else {
@@ -1407,9 +1115,9 @@
         if (valid) {
           this.saveLoading = true;
           // 淇濆瓨娓呯┖id渚夸簬鍚庣鏁翠綋鍒犻櫎鏂板
-          this.form.ethicalreviewopinionsList.forEach(item => {
-            item.id = null;
-          });
+          // this.form.ethicalreviewopinionsList.forEach(item => {
+          //   item.id = null;
+          // });
           try {
             const submitData = {
               ...this.form,
@@ -1428,15 +1136,19 @@
               response = await ethicalreviewadd(submitData);
             }
 
-            if (response.code === 200) {
+            if (response.code == 200) {
               this.$message.success("淇濆瓨鎴愬姛");
+              // 淇濆瓨鎴愬姛鍚庢洿鏂板師濮嬫暟鎹�
+              this.saveOriginalData();
               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 }
-                });
+              if (!this.form.id && response.data) {
+                this.form.id = response.data;
+                this.id = this.form.id;
+                // this.$router.replace({
+                //   query: { ...this.$route.query, id: this.form.id }
+                // });
               }
+              this.refreshPageData();
             } else {
               this.$message.error("淇濆瓨澶辫触锛�" + (response.msg || "鏈煡閿欒"));
             }
@@ -1471,7 +1183,7 @@
 
             const response = await ethicalreviewedit(updateData);
 
-            if (response.code === 200) {
+            if (response.code == 200) {
               this.$message.success("瀹℃煡鐘舵�佸凡鏇存柊涓哄畬鎴�");
               this.form.status = "3";
               this.form.endTime = updateData.endTime;
@@ -1523,7 +1235,7 @@
 
             const response = await ethicalreviewedit(updateData);
 
-            if (response.code === 200) {
+            if (response.code == 200) {
               this.$message.success("瀹℃煡宸蹭腑姝紝鎵�鏈変笓瀹剁姸鎬佸凡鏇存柊");
               this.form.status = "2";
             } else {
@@ -1555,7 +1267,7 @@
           try {
             const updateData = {
               ...this.form,
-              status: "2", // 瀹℃煡涓
+              status: "4", // 瀹℃煡涓
               endTime: new Date()
                 .toISOString()
                 .replace("T", " ")
@@ -1564,9 +1276,9 @@
 
             const response = await ethicalreviewedit(updateData);
 
-            if (response.code === 200) {
+            if (response.code == 200) {
               this.$message.success("瀹℃煡宸茬粨鏉�");
-              this.form.status = "2";
+              this.form.status = "4";
               this.form.endTime = updateData.endTime;
             } else {
               this.$message.error("鎿嶄綔澶辫触锛�" + (response.msg || "鏈煡閿欒"));
@@ -1586,13 +1298,255 @@
       this.expertDialogVisible = true;
       this.loadExperts();
     },
+    /**
+     * 鍒锋柊椤甸潰鏁版嵁
+     */
+    async refreshPageData() {
+      try {
+        // 閲嶇疆鏁版嵁鐘舵��
+        this.isDataLoaded = false;
 
+        // 娓呯┖褰撳墠鏁版嵁
+        this.form = {
+          id: undefined,
+          infoid: undefined,
+          caseNo: "",
+          initiateTheme: "",
+          initiatePerson: "",
+          status: "0",
+          startTime: "",
+          cutOffTime: "",
+          endTime: "",
+          expertName: "",
+          expertNo: "",
+          expertType: "0",
+          expertConclusion: "",
+          expertOpinion: "",
+          expertTime: "",
+          orderNo: 1,
+          remark: "",
+          annexfilesList: [],
+          filePatch: "",
+          ethicalreviewopinionsList: [],
+          createBy: "",
+          createTime: "",
+          updateBy: "",
+          updateTime: "",
+          delFlag: "0"
+        };
+
+        this.attachmentFileList = [];
+        this.originalFormData = null;
+        this.originalExpertList = null;
+        this.originalAttachments = null;
+
+        // 閲嶆柊鑾峰彇鏁版嵁
+        if (this.id) {
+          await this.getDetail(this.infoid, this.id);
+        } else if (this.infoid) {
+          await this.getDetail(this.infoid, null);
+        } else {
+          this.$message.warning("鏃犳硶鍒锋柊锛岀己灏戝繀瑕佺殑鍙傛暟");
+        }
+
+        this.$message.success("鏁版嵁鍒锋柊鎴愬姛");
+      } catch (error) {
+        console.error("鍒锋柊鏁版嵁澶辫触:", error);
+        this.$message.error("鍒锋柊鏁版嵁澶辫触锛岃閲嶈瘯");
+      }
+    },
+
+    /**
+     * 澶勭悊椤甸潰鍒锋柊
+     * 妫�鏌ユ槸鍚︽湁鏈繚瀛樻暟鎹紝纭鍚庡埛鏂伴〉闈�
+     */
+    handleRefresh() {
+      // 妫�鏌ユ槸鍚︽湁鏈繚瀛樼殑缂栬緫
+      if (this.hasUnsavedChanges()) {
+        this.$confirm(
+          "褰撳墠鏈夋湭淇濆瓨鐨勬暟鎹紝鍒锋柊椤甸潰灏嗕涪澶辫繖浜涙洿鏀广�傛槸鍚︾户缁埛鏂帮紵",
+          "璀﹀憡",
+          {
+            confirmButtonText: "缁х画鍒锋柊",
+            cancelButtonText: "鍙栨秷",
+            type: "warning",
+            distinguishCancelAndClose: true,
+            beforeClose: (action, instance, done) => {
+              if (action === "confirm") {
+                instance.confirmButtonLoading = true;
+                instance.confirmButtonText = "鍒锋柊涓�...";
+
+                // 寤惰繜鎵ц浠ョ‘淇漊I鏇存柊
+                setTimeout(() => {
+                  done();
+                  instance.confirmButtonLoading = false;
+
+                  // 鐢ㄦ埛纭鍒锋柊
+                  this.refreshPageData();
+                }, 300);
+              } else {
+                this.$message({
+                  type: "info",
+                  message: "宸插彇娑堝埛鏂�"
+                });
+                done();
+              }
+            }
+          }
+        ).catch(action => {
+          if (action === "cancel") {
+            this.$message({
+              type: "info",
+              message: "宸插彇娑堝埛鏂�"
+            });
+          }
+        });
+      } else {
+        // 娌℃湁鏈繚瀛樼殑缂栬緫锛岀洿鎺ュ埛鏂�
+        this.refreshPageData();
+      }
+    },
+    // 妫�鏌ユ槸鍚︽湁鏈繚瀛樼殑鏁版嵁鍙樺寲
+    hasUnsavedChanges() {
+      if (!this.isDataLoaded) {
+        return false; // 鏁版嵁鏈姞杞斤紝鏃犻渶妫�娴�
+      }
+
+      // 1. 妫�鏌ヨ〃鍗曞瓧娈靛彉鍖�
+      const formFieldsChanged = this.checkFormFieldsChanged();
+
+      // 2. 妫�鏌ヤ笓瀹跺垪琛ㄥ彉鍖�
+      const expertListChanged = this.checkExpertListChanged();
+
+      // 3. 妫�鏌ラ檮浠跺垪琛ㄥ彉鍖�
+      const attachmentsChanged = this.checkAttachmentsChanged();
+
+      return formFieldsChanged || expertListChanged || attachmentsChanged;
+    },
+
+    // 妫�鏌ヨ〃鍗曞瓧娈垫槸鍚︽湁鍙樺寲
+    checkFormFieldsChanged() {
+      if (!this.originalFormData) return false;
+
+      const formKeys = [
+        "initiateTheme",
+        "initiatePerson",
+        "status",
+        "expertConclusion",
+        "expertOpinion",
+        "expertTime",
+        "remark"
+      ];
+
+      for (const key of formKeys) {
+        if (this.form[key] !== this.originalFormData[key]) {
+          console.log(
+            `琛ㄥ崟瀛楁鍙樺寲: ${key}`,
+            this.form[key],
+            this.originalFormData[key]
+          );
+          return true;
+        }
+      }
+
+      return false;
+    },
+
+    // 妫�鏌ヤ笓瀹跺垪琛ㄥ彉鍖�
+    checkExpertListChanged() {
+      if (!this.originalExpertList || !this.form.ethicalreviewopinionsList) {
+        return false;
+      }
+
+      const original = this.originalExpertList;
+      const current = this.form.ethicalreviewopinionsList;
+
+      // 1. 妫�鏌ユ暟閲忓彉鍖�
+      if (original.length !== current.length) {
+        console.log("涓撳鏁伴噺鍙樺寲:", original.length, "->", current.length);
+        return true;
+      }
+
+      // 2. 妫�鏌ユ瘡涓笓瀹剁殑鍙樺寲
+      for (let i = 0; i < original.length; i++) {
+        const origExpert = original[i];
+        const currExpert = current[i];
+
+        // 妫�鏌ュ叧閿瓧娈靛彉鍖�
+        const fieldsToCheck = [
+          "expertconclusion",
+          "expertopinion",
+          "receiveStatus",
+          "conclusiontime",
+          "startTime",
+          "endTime",
+          "sendType"
+        ];
+
+        for (const field of fieldsToCheck) {
+          if (origExpert[field] !== currExpert[field]) {
+            console.log(
+              `涓撳${i}鐨�${field}瀛楁鍙樺寲:`,
+              origExpert[field],
+              "->",
+              currExpert[field]
+            );
+            return true;
+          }
+        }
+      }
+
+      return false;
+    },
+
+    // 妫�鏌ラ檮浠跺垪琛ㄥ彉鍖�
+    checkAttachmentsChanged() {
+      if (!this.originalAttachments || !this.form.annexfilesList) {
+        return false;
+      }
+
+      const original = this.originalAttachments;
+      const current = this.form.annexfilesList;
+
+      // 妫�鏌ユ暟閲忓彉鍖�
+      if (original.length !== current.length) {
+        console.log("闄勪欢鏁伴噺鍙樺寲:", original.length, "->", current.length);
+        return true;
+      }
+
+      // 妫�鏌ユ枃浠跺悕鍙樺寲锛堥�氬父闄勪欢涓嶄細淇敼锛屽彧澧炲垹锛�
+      const originalFileNames = original.map(f => f.fileName || f.name).sort();
+      const currentFileNames = current.map(f => f.fileName || f.name).sort();
+
+      for (let i = 0; i < originalFileNames.length; i++) {
+        if (originalFileNames[i] !== currentFileNames[i]) {
+          console.log(
+            "闄勪欢鏂囦欢鍚嶅彉鍖�:",
+            originalFileNames[i],
+            "->",
+            currentFileNames[i]
+          );
+          return true;
+        }
+      }
+
+      return false;
+    },
+
+    // 娴忚鍣ㄧ寮�椤甸潰妫�娴�
+    beforeUnloadHandler(event) {
+      if (this.hasUnsavedChanges()) {
+        const message = "鎮ㄦ湁鏈繚瀛樼殑鏇存敼锛岀‘瀹氳绂诲紑鍚楋紵";
+        event.returnValue = message; // 鏍囧噯鏂瑰紡
+        return message; // 鏌愪簺娴忚鍣ㄩ渶瑕佽繑鍥炲瓧绗︿覆
+      }
+    },
     // 鍔犺浇涓撳鍒楄〃
     async loadExperts() {
       try {
         this.expertListLoading = true;
         const params = {
-          usertype: "浼︾悊涓撳", // 浼︾悊涓撳
+          usertype: "ethical", // 浼︾悊涓撳
           pageNum: this.expertPage.pageNum,
           pageSize: this.expertPage.pageSize
         };
@@ -1602,7 +1556,7 @@
         }
 
         const response = await listExternalperson(params);
-        if (response.code === 200) {
+        if (response.code == 200) {
           this.expertList = response.rows || [];
           this.expertTotal = response.total || 0;
         } else {
@@ -1639,7 +1593,7 @@
 
     // 纭娣诲姞涓撳
     handleConfirmAddExpert() {
-      if (this.selectedExperts.length === 0) {
+      if (this.selectedExperts.length == 0) {
         this.$message.warning("璇烽�夋嫨瑕佹坊鍔犵殑涓撳");
         return;
       }
@@ -1664,7 +1618,7 @@
           expertType: isChief ? "1" : "0", // 涓讳换濮斿憳璁剧疆涓轰富濮斾笓瀹�
           deptName: expert.unitname || "",
           title: expert.title || "",
-          deptname: expert.telephone || "",
+          donorno: expert.telephone || "",
           receiveStatus: "0", // 寰呮帴鏀�
           expertconclusion: "",
           expertopinion: "",
@@ -1719,6 +1673,9 @@
 
     // 鍙戦�佺粰鏅�氫笓瀹�
     handleSendToNormalExperts() {
+      if (!this.validateAllIds(this.ethicalreviewopinionsList)) {
+        return;
+      }
       this.currentSendExperts = this.availableNormalExperts;
       this.sendForm.expertType = "normal";
       this.sendForm.endTime = ""; // 閲嶇疆鎴鏃堕棿
@@ -1727,6 +1684,9 @@
 
     // 鍙戦�佺粰涓诲涓撳
     handleSendToChiefExpert() {
+      if (!this.validateAllIds(this.ethicalreviewopinionsList)) {
+        return;
+      }
       this.currentSendExperts = this.availableChiefExperts;
       this.sendForm.expertType = "chief";
       this.sendForm.endTime = ""; // 涓诲涓撳鏃犻渶鎴鏃堕棿
@@ -1735,12 +1695,40 @@
 
     // 鍙戦�佺粰鍗曚釜涓撳
     handleSendToExpert(expert) {
+      if (!this.validateAllIds(this.ethicalreviewopinionsList)) {
+        return;
+      }
       this.currentSendExperts = [expert];
-      this.sendForm.expertType = expert.expertType === "1" ? "chief" : "normal";
-      this.sendForm.endTime = expert.expertType === "1" ? "" : ""; // 涓诲涓撳鏃犻渶鎴鏃堕棿
+      this.sendForm.expertType = expert.expertType == "1" ? "chief" : "normal";
+      this.sendForm.endTime = expert.expertType == "1" ? "" : ""; // 涓诲涓撳鏃犻渶鎴鏃堕棿
       this.sendDialogVisible = true;
     },
 
+    validateAllIds(arr, idField = "id") {
+      if (!Array.isArray(arr) || arr.length === 0) {
+        this.$message.warning("璇峰厛閫夋嫨涓撳鍒楄〃");
+        return false;
+      }
+
+      const emptyIdItems = arr.filter(item => {
+        const id = item[idField];
+        return (
+          id === undefined ||
+          id === null ||
+          id === "" ||
+          id.toString().trim() === ""
+        );
+      });
+
+      if (emptyIdItems.length > 0) {
+        this.$message.warning(
+          "褰撳墠閫変腑涓撳鍒楄〃鏈夋湭淇濆瓨鏁版嵁锛岃淇濆瓨鍒锋柊鍚庡啀鍙戦��"
+        );
+        return false;
+      }
+
+      return true;
+    },
     // 鍙戦�佸璇濇鍏抽棴
     handleSendDialogClose() {
       this.sendForm = {
@@ -1749,7 +1737,6 @@
         startTime: "",
         endTime: "",
         sendType: "0",
-        title: "浼︾悊瀹℃煡浠诲姟閫氱煡",
         content: "",
         url: ""
       };
@@ -1769,55 +1756,67 @@
         return;
       }
 
-      if (!this.sendForm.sendType) {
-        this.$message.warning("璇烽�夋嫨鍙戦�佹柟寮�");
-        return;
-      }
+      // if (!this.sendForm.sendType) {
+      //   this.$message.warning("璇烽�夋嫨鍙戦�佹柟寮�");
+      //   return;
+      // }
 
-      if (!this.sendForm.title) {
-        this.$message.warning("璇疯緭鍏ュ彂閫佹爣棰�");
-        return;
-      }
+      // if (!this.sendForm.title) {
+      //   this.$message.warning("璇疯緭鍏ュ彂閫佹爣棰�");
+      //   return;
+      // }
 
-      if (!this.sendForm.content) {
-        this.$message.warning("璇疯緭鍏ュ彂閫佸唴瀹�");
-        return;
-      }
+      // if (!this.sendForm.content) {
+      //   this.$message.warning("璇疯緭鍏ュ彂閫佸唴瀹�");
+      //   return;
+      // }
 
-      if (this.currentSendExperts.length === 0) {
+      if (this.currentSendExperts.length == 0) {
         this.$message.warning("娌℃湁鎵惧埌鍙彂閫佺殑涓撳");
         return;
       }
 
-      this.sending = true;
+      // 鍒濆鍖栧彂閫佺姸鎬�
+      this.sendingAll = true;
+      this.sendingProgress = 0;
+      this.sendingTotal = this.currentSendExperts.length;
+      this.sendingSuccessCount = 0;
+      this.sendingFailCount = 0;
+      this.sendingResults = [];
+
+      // 鍒涘缓涓�涓繘搴﹀璇濇
+      const progressDialog = this.$message({
+        type: "info",
+        message: `姝e湪鍙戦�侀�氱煡锛岃绋嶅��... (0/${this.sendingTotal})`,
+        duration: 0, // 涓嶄細鑷姩鍏抽棴
+        showClose: true
+      });
+
       try {
-        // 鍙戦�佺粰姣忎釜涓撳
-        const sendPromises = this.currentSendExperts.map(async expert => {
+        // 浣跨敤Promise鏁扮粍鏉ラ『搴忔墽琛屽彂閫�
+        for (let i = 0; i < this.currentSendExperts.length; i++) {
+          const expert = this.currentSendExperts[i];
+
+          // 鏇存柊杩涘害
+          this.sendingProgress = i;
+          progressDialog.message = `姝e湪鍙戦�侀�氱煡锛岃绋嶅��... (${i}/${this.sendingTotal})`;
+
           try {
-            // 鏋勫缓鍙戦�佹暟鎹�
-            const sendData = {
-              number: expert.deptname || "", // 鐢ㄦ埛鎵嬫満鍙�
-              title: this.sendForm.title,
-              url: this.sendForm.url || "",
+            // 鍙戦�佸崟涓笓瀹堕�氱煡
+            const result = await this.sendSingleExpert(expert, i);
+            this.sendingResults.push(result);
 
-              createTime: new Date()
-                .toISOString()
-                .replace("T", " ")
-                .substring(0, 19)
-            };
+            if (result.success) {
+              this.sendingSuccessCount++;
 
-            // 璋冪敤鍙戦�侀�氱煡鎺ュ彛
-            const response = await sendNotification(sendData);
-
-            if (response.code === 200) {
               // 鏇存柊涓撳鐘舵��
               const index = this.form.ethicalreviewopinionsList.findIndex(
                 e =>
-                  e.expertNo === expert.expertNo ||
-                  e.expertname === expert.expertname
+                  e.expertNo == expert.expertNo ||
+                  e.expertname == expert.expertname
               );
 
-              if (index !== -1) {
+              if (index != -1) {
                 this.form.ethicalreviewopinionsList[index].receiveStatus = "1"; // 宸叉帴鏀�
                 this.form.ethicalreviewopinionsList[
                   index
@@ -1842,42 +1841,49 @@
                   this.form.ethicalreviewopinionsList[index]
                 );
               }
-
-              return { success: true, expert: expert.expertname };
             } else {
-              return {
-                success: false,
-                expert: expert.expertname,
-                error: response.msg
-              };
+              this.sendingFailCount++;
             }
           } catch (error) {
             console.error(`鍙戦�佺粰涓撳 ${expert.expertname} 澶辫触:`, error);
-            return {
+            this.sendingResults.push({
               success: false,
               expert: expert.expertname,
               error: error.message
-            };
+            });
+            this.sendingFailCount++;
           }
-        });
 
-        // 绛夊緟鎵�鏈夊彂閫佸畬鎴�
-        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("鍙戦�佸け璐ワ紝璇风◢鍚庨噸璇�");
+          // 濡傛灉涓嶆槸鏈�鍚庝竴涓紝绛夊緟100ms鍐嶅彂閫佷笅涓�涓�
+          if (i < this.currentSendExperts.length - 1) {
+            await this.sleep(100);
+          }
         }
 
+        // 瀹屾垚杩涘害
+        this.sendingProgress = this.sendingTotal;
+        progressDialog.message = `鍙戦�佸畬鎴愶紝鎴愬姛 ${this.sendingSuccessCount} 涓紝澶辫触 ${this.sendingFailCount} 涓猔;
+
+        // 寤惰繜1绉掑悗鍏抽棴杩涘害瀵硅瘽妗�
+        await this.sleep(1000);
+        progressDialog.close();
+
+        // 鏄剧ず鏈�缁堢粨鏋�
+        if (this.sendingFailCount == 0) {
+          this.$message.success(
+            `鎴愬姛鍙戦�佺粰 ${this.sendingSuccessCount} 浣嶄笓瀹禶
+          );
+        } else if (this.sendingSuccessCount > 0) {
+          this.$message.warning(
+            `鎴愬姛鍙戦�佺粰 ${this.sendingSuccessCount} 浣嶄笓瀹讹紝澶辫触 ${this.sendingFailCount} 浣峘
+          );
+          // 濡傛灉鏈夊け璐ワ紝鍙互鏄剧ず璇︾粏澶辫触淇℃伅
+          this.showFailedDetails();
+        } else {
+          this.$message.error("鍏ㄩ儴鍙戦�佸け璐ワ紝璇风◢鍚庨噸璇�");
+        }
+
+        // 鍏抽棴鍙戦�佸璇濇
         this.sendDialogVisible = false;
         this.sendForm = {
           expertType: "normal",
@@ -1885,19 +1891,111 @@
           startTime: "",
           endTime: "",
           sendType: "0",
-          title: "浼︾悊瀹℃煡浠诲姟閫氱煡",
           content: "",
           url: ""
         };
         this.currentSendExperts = [];
+        // 淇濆瓨鏁翠釜鍗曟嵁
+        this.handleSave();
       } catch (error) {
-        console.error("鍙戦�佸け璐�:", error);
-        this.$message.error("鍙戦�佸け璐ワ紝璇烽噸璇�");
+        console.error("鍙戦�佽繃绋嬩腑鍙戠敓閿欒:", error);
+        progressDialog.close();
+        this.$message.error("鍙戦�佽繃绋嬩腑鍙戠敓閿欒锛岃閲嶈瘯");
       } finally {
-        this.sending = false;
+        this.sendingAll = false;
+      }
+    },
+    // 鍙戦�佸崟涓笓瀹剁殑鏂规硶
+    async sendSingleExpert(expert, index) {
+      console.log(expert, "expert", index);
+
+      try {
+        // // 鏋勫缓鍙戦�佹暟鎹�
+        // const sendData = {
+        //   number: expert.deptname || "", // 鐢ㄦ埛鎵嬫満鍙�
+        //   title: this.sendForm.title,
+        //   url: this.sendForm.url || "",
+        //   createTime: new Date()
+        //     .toISOString()
+        //     .replace("T", " ")
+        //     .substring(0, 19)
+        // };
+        const sendDatas = {
+          expertNo: expert.donorno || "", //7.14
+          id: expert.id || "",
+          infoId: this.infoid || ""
+        };
+
+        console.log(`姝e湪鍙戦�佺 ${index + 1} 涓笓瀹�: ${expert.expertname}`);
+
+        // 璋冪敤鍙戦�侀�氱煡鎺ュ彛
+        // const response = await sendNotification(sendData);
+        // const response = await sendcall({
+        //   tel: expert.donorno ? expert.donorno : 13634195431, // 杩欓噷搴旇鏄� expert.deptname 鎴� expert.phone
+        //   messageContent:
+        //     "闈掑矝澶у闄勫睘鍖婚櫌涓婃姤娼滃湪鎹愮尞妗堜緥锛岃鐧诲綍OPO绯荤粺鏌ョ湅璇︾粏淇℃伅锛屽強鏃惰繘琛屽鎺ャ�傜櫥褰曢摼鎺�:https://brdeddd.qduhosos.cn/dklejdj/deljf/index"
+        // });
+        const response = await sendNotification(sendDatas);
+
+        if (response.code == 200) {
+          return {
+            success: true,
+            expert: expert.expertname,
+            index: index
+          };
+        } else {
+          return {
+            success: false,
+            expert: expert.expertname,
+            index: index,
+            error: response.msg
+          };
+        }
+      } catch (error) {
+        console.error(`鍙戦�佺粰涓撳 ${expert.expertname} 澶辫触:`, error);
+        return {
+          success: false,
+          expert: expert.expertname,
+          index: index,
+          error: error.message
+        };
       }
     },
 
+    // 鏄剧ず澶辫触璇︽儏鐨勬柟娉�
+    showFailedDetails() {
+      const failedExperts = this.sendingResults.filter(r => !r.success);
+      if (failedExperts.length > 0) {
+        this.$confirm(
+          `鏈� ${failedExperts.length} 浣嶄笓瀹跺彂閫佸け璐ワ紝鏄惁鏌ョ湅澶辫触璇︽儏锛焋,
+          "鍙戦�佺粨鏋�",
+          {
+            confirmButtonText: "鏌ョ湅璇︽儏",
+            cancelButtonText: "鍏抽棴",
+            type: "warning"
+          }
+        )
+          .then(() => {
+            let detailMessage = "鍙戦�佸け璐ョ殑涓撳锛歕n\n";
+            failedExperts.forEach((expert, index) => {
+              detailMessage += `${index + 1}. ${expert.expert}: ${
+                expert.error
+              }\n`;
+            });
+
+            this.$alert(detailMessage, "鍙戦�佸け璐ヨ鎯�", {
+              confirmButtonText: "纭畾",
+              customClass: "failed-details-dialog"
+            });
+          })
+          .catch(() => {});
+      }
+    },
+
+    // 鐫$湢鍑芥暟锛岀敤浜庨棿闅�
+    sleep(ms) {
+      return new Promise(resolve => setTimeout(resolve, ms));
+    },
     // 鍒犻櫎涓撳瀹℃煡
     handleDeleteExpertReview(expert, index) {
       this.$confirm("纭畾瑕佸垹闄よ涓撳鐨勫鏌ヨ褰曞悧锛�", "鎻愮ず", {
@@ -1931,7 +2029,7 @@
 
         const response = await ethicalreExpertTotal(params);
 
-        if (response && response.code === 200) {
+        if (response && response.code == 200) {
           this.expertHistoryData = response.data || response[0] || null;
         } else {
           this.$message.error(
@@ -2171,11 +2269,82 @@
 .selected-case-info {
   margin-bottom: 20px;
 }
+/* 鍙戦�佽繘搴︽牱寮� */
+.send-progress-container {
+  margin: 20px 0;
+  padding: 20px;
+  background: #f5f7fa;
+  border-radius: 8px;
+}
 
+.send-stats {
+  display: flex;
+  justify-content: center;
+  gap: 30px;
+  margin-top: 10px;
+}
+
+.stat-item {
+  font-size: 14px;
+  font-weight: 500;
+  padding: 4px 12px;
+  border-radius: 4px;
+}
+
+.stat-item.success {
+  color: #67c23a;
+  background: #f0f9eb;
+}
+
+.stat-item.fail {
+  color: #f56c6c;
+  background: #fef0f0;
+}
+
+/* 澶辫触璇︽儏瀵硅瘽妗� */
+.failed-details-dialog {
+  min-width: 400px;
+  max-width: 600px;
+}
+
+.failed-details-dialog .el-message-box__content {
+  max-height: 400px;
+  overflow-y: auto;
+  white-space: pre-wrap;
+  word-break: break-word;
+}
 .case-info-card {
   border-left: 4px solid #67c23a;
 }
+/* 鍦–SS涓坊鍔� */
+:deep(.el-message-box) {
+  max-width: 500px;
+}
 
+/* 娣诲姞鏈繚瀛樼姸鎬佹牱寮� */
+.unsaved-hint {
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  background: #e6a23c;
+  color: white;
+  padding: 4px 8px;
+  border-radius: 4px;
+  font-size: 12px;
+  animation: pulse 2s infinite;
+}
+
+@keyframes pulse {
+  0% {
+    opacity: 0.8;
+  }
+  50% {
+    opacity: 1;
+  }
+  100% {
+    opacity: 0.8;
+  }
+}
 /* 鍝嶅簲寮忚璁� */
 @media (max-width: 768px) {
   .ethics-review-detail {

--
Gitblit v1.9.3