From 70e4d2c33cc2e3af590d1816c0c703da341538cf Mon Sep 17 00:00:00 2001
From: WXL (wul) <wl_5969728@163.com>
Date: 星期五, 04 九月 2026 14:13:32 +0800
Subject: [PATCH] 测试完成

---
 src/views/Satisfaction/configurationmyd/batch.vue | 1524 ++++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 1,152 insertions(+), 372 deletions(-)

diff --git a/src/views/Satisfaction/configurationmyd/batch.vue b/src/views/Satisfaction/configurationmyd/batch.vue
index 10d6619..59ada45 100644
--- a/src/views/Satisfaction/configurationmyd/batch.vue
+++ b/src/views/Satisfaction/configurationmyd/batch.vue
@@ -11,8 +11,9 @@
             icon="el-icon-check"
             @click="handleBatchSubmit"
             :loading="batchProcessing"
+            :disabled="selectedExceptionIds.length === 0"
           >
-            鎵归噺鎻愪氦澶勭悊
+            鎵归噺鎻愪氦澶勭悊 ({{ selectedExceptionIds.length }})
           </el-button>
           <el-button type="warning" icon="el-icon-back" @click="handleGoBack">
             杩斿洖寮傚父鍒楄〃
@@ -33,29 +34,40 @@
           >
             <el-form-item label="璐熻矗绉戝">
               <el-select
-                v-model="filterParams.deptId"
+                v-model="filterParams.todeptcode"
                 placeholder="璇烽�夋嫨绉戝"
                 clearable
+                filterable
                 style="width: 200px"
               >
                 <el-option
                   v-for="dept in deptList"
-                  :key="dept.id"
-                  :label="dept.name"
-                  :value="dept.id"
+                  :key="dept.deptCode"
+                  :label="dept.label"
+                  :value="dept.deptCode"
                 />
               </el-select>
             </el-form-item>
             <el-form-item label="澶勭悊鐘舵��">
               <el-select
-                v-model="filterParams.status"
+                v-model="filterParams.handleFlag"
                 placeholder="璇烽�夋嫨鐘舵��"
                 clearable
                 style="width: 200px"
               >
-                <el-option label="寰呭鐞�" :value="0" />
-                <el-option label="澶勭悊涓�" :value="1" />
-                <el-option label="宸插鐞�" :value="2" />
+                <el-option label="鏈鐞�" :value="'0'" />
+                <el-option label="宸插鐞�" :value="'1'" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="婊℃剰搴︾被鍨�">
+              <el-select
+                v-model="filterParams.templateType"
+                placeholder="璇烽�夋嫨妯℃澘绫诲瀷"
+                clearable
+                style="width: 200px"
+              >
+                <el-option label="璇煶妯℃澘" :value="1" />
+                <el-option label="闂嵎妯℃澘" :value="2" />
               </el-select>
             </el-form-item>
             <el-form-item>
@@ -79,6 +91,7 @@
           :border="true"
           style="width: 100%"
           @selection-change="handleSelectionChange"
+          row-key="id"
           class="exception-table"
         >
           <el-table-column type="selection" width="55" align="center" />
@@ -92,24 +105,30 @@
 
           <el-table-column
             label="璐熻矗绉戝"
-            prop="responsibilityDept"
-            width="120"
+            prop="todeptname"
+            width="200"
             align="center"
           >
             <template slot-scope="{ row }">
-              <el-tag type="primary">{{ row.responsibilityDept }}</el-tag>
+              <el-tag type="primary" v-if="row.todeptname">{{
+                row.todeptname
+              }}</el-tag>
+              <span v-else class="no-data">鏈垎閰�</span>
             </template>
           </el-table-column>
 
-          <el-table-column
-            label="涓嶆弧鎰忚鎯�"
-            prop="unsatisfactoryDetail"
-            min-width="200"
-            align="center"
-          >
+          <el-table-column label="涓嶆弧鎰忚鎯�" min-width="250" align="center">
             <template slot-scope="{ row }">
               <div class="detail-content">
-                {{ row.unsatisfactoryDetail }}
+                <div class="question-text">
+                  <strong>闂锛�</strong>{{ row.questiontext }}
+                </div>
+                <div class="answer-text">
+                  <strong>鍥炵瓟锛�</strong>{{ row.asrtext || "鏃犲洖绛�" }}
+                </div>
+                <div class="matched-text" v-if="row.matchedtext">
+                  <strong>瑙f瀽鍊硷細</strong>{{ row.matchedtext }}
+                </div>
               </div>
             </template>
           </el-table-column>
@@ -117,42 +136,48 @@
           <el-table-column label="鎮h�呬俊鎭�" width="300" align="center">
             <template slot-scope="{ row }">
               <div class="patient-info">
-                <div class="patient-item">
-                  <span class="label">濮撳悕锛�</span>
-                  <span class="value">{{ row.patientName }}</span>
+                <div class="patient-row">
+                  <div class="patient-item">
+                    <span class="label">濮撳悕锛�</span>
+                    <span class="value">{{ row.patdescJson.sendname }}</span>
+                  </div>
+                  <div class="patient-item">
+                    <span class="label">鎬у埆锛�</span>
+                    <span class="value">{{ row.patdescJson.sex }}</span>
+                  </div>
+                  <div class="patient-item">
+                    <span class="label">骞撮緞锛�</span>
+                    <span class="value">{{ row.patdescJson.age }}宀�</span>
+                  </div>
                 </div>
-                <div class="patient-item">
-                  <span class="label">鎬у埆锛�</span>
-                  <span class="value">{{
-                    row.gender === 1 ? "鐢�" : "濂�"
-                  }}</span>
-                </div>
-                <div class="patient-item">
-                  <span class="label">骞撮緞锛�</span>
-                  <span class="value">{{ row.age }}宀�</span>
-                </div>
-                <div class="patient-item">
-                  <span class="label">鐢佃瘽锛�</span>
-                  <span class="value">{{ row.phone }}</span>
+                <div class="patient-row">
+                  <div class="patient-item full-width">
+                    <span class="label">鐢佃瘽锛�</span>
+                    <span class="value">{{ row.patdescJson.phone }}</span>
+                  </div>
                 </div>
               </div>
             </template>
           </el-table-column>
 
-          <el-table-column label="鍑洪櫌淇℃伅" width="250" align="center">
+          <el-table-column label="濉啓淇℃伅" width="180" align="center">
             <template slot-scope="{ row }">
-              <div class="discharge-info">
+              <div class="fill-info">
                 <div class="info-item">
-                  <span class="label">绉戝锛�</span>
-                  <span class="value">{{ row.dischargeDept }}</span>
+                  <span class="label">濉姤鏃堕棿锛�</span>
+                  <span class="value time">{{
+                    formatDateTime(row.createTime)
+                  }}</span>
                 </div>
-                <div class="info-item">
-                  <span class="label">鐥呭尯锛�</span>
-                  <span class="value">{{ row.dischargeWard }}</span>
-                </div>
-                <div class="info-item">
-                  <span class="label">濉啓鏃堕棿锛�</span>
-                  <span class="value time">{{ row.fillTime }}</span>
+                <div v-if="row.recordurl" class="info-item">
+                  <el-button
+                    type="text"
+                    size="small"
+                    @click="handlePlayAudio(row.recordurl)"
+                    icon="el-icon-headset"
+                  >
+                    鎾斁褰曢煶
+                  </el-button>
                 </div>
               </div>
             </template>
@@ -160,14 +185,36 @@
 
           <el-table-column
             label="澶勭悊鐘舵��"
-            prop="processStatus"
+            prop="handleFlag"
             width="100"
             align="center"
           >
             <template slot-scope="{ row }">
-              <el-tag :type="getStatusTagType(row.processStatus)" effect="dark">
-                {{ getStatusText(row.processStatus) }}
+              <el-tag :type="getStatusTagType(row.handleFlag)" effect="dark">
+                {{ getStatusText(row.handleFlag) }}
               </el-tag>
+            </template>
+          </el-table-column>
+
+          <el-table-column label="鏈�鏂板鐞嗕俊鎭�" width="180" align="center">
+            <template slot-scope="{ row }">
+              <div v-if="row.handleTime" class="handle-info">
+                <div class="info-item">
+                  <span class="label">澶勭悊浜猴細</span>
+                  <span class="value">{{ row.handleBy || "绯荤粺" }}</span>
+                </div>
+                <div class="info-item">
+                  <span class="label">澶勭悊鏃堕棿锛�</span>
+                  <span class="value time">{{
+                    formatDateTime(row.handleTime)
+                  }}</span>
+                </div>
+                <div class="info-item">
+                  <span class="label">澶勭悊璇存槑锛�</span>
+                  <span class="value">{{ formatHandledesc(row.handledesc) }}</span>
+                </div>
+              </div>
+              <span v-else class="no-data">鏈鐞�</span>
             </template>
           </el-table-column>
 
@@ -191,7 +238,7 @@
                 size="small"
                 icon="el-icon-edit"
                 @click="handleProcess(row)"
-                :disabled="row.processStatus === 2"
+                :disabled="row.handleresult === 'resolved'"
               >
                 澶勭悊
               </el-button>
@@ -219,75 +266,258 @@
     <el-dialog
       title="澶勭悊寮傚父鍙嶉"
       :visible.sync="processDialogVisible"
-      width="600px"
+      width="60%"
+      top="6vh"
       center
     >
-      <el-form
-        :model="processForm"
-        :rules="processRules"
-        ref="processForm"
-        label-width="100px"
-        size="medium"
-      >
-        <el-form-item label="澶勭悊鐘舵��" prop="status">
-          <el-select
-            v-model="processForm.status"
-            placeholder="璇烽�夋嫨澶勭悊鐘舵��"
-            style="width: 100%"
+      <div class="flow-dialog-body">
+        <!-- 宸﹀崐閮ㄥ垎锛氬鐞嗕俊鎭� -->
+        <div class="flow-left">
+          <el-form
+            :model="processForm"
+            :rules="processRules"
+            ref="processForm"
+            label-width="100px"
+            size="medium"
           >
-            <el-option label="澶勭悊涓�" :value="1" />
-            <el-option label="宸插鐞�" :value="2" />
-            <el-option label="宸查┏鍥�" :value="3" />
-          </el-select>
-        </el-form-item>
+            <el-form-item label="鏄惁娴佽浆" prop="isFlow">
+              <el-switch
+                v-model="processForm.isFlow"
+                active-value="1"
+                inactive-value="0"
+                active-text="娴佽浆"
+                inactive-text="涓嶆祦杞�"
+              />
+            </el-form-item>
 
-        <el-form-item label="鎶ュ绉戝" prop="reportDepts">
-          <el-select
-            v-model="processForm.reportDepts"
-            placeholder="璇烽�夋嫨鎶ュ绉戝"
-            multiple
-            filterable
-            collapse-tags
-            style="width: 100%"
-          >
-            <el-option
-              v-for="dept in deptList"
-              :key="dept.id"
-              :label="dept.name"
-              :value="dept.id"
-            />
-          </el-select>
-        </el-form-item>
+            <el-form-item label="澶勭悊鐘舵��" prop="handleFlag">
+              <el-select
+                v-model="processForm.handleFlag"
+                placeholder="璇烽�夋嫨澶勭悊鐘舵��"
+                style="width: 100%"
+              >
+                <el-option label="宸插鐞�" :value="'1'" />
+                <el-option label="鍙栨秷澶勭悊" :value="'0'" />
+              </el-select>
+            </el-form-item>
 
-        <el-form-item label="澶勭悊澶囨敞" prop="remark">
-          <el-input
-            v-model="processForm.remark"
-            type="textarea"
-            :rows="4"
-            placeholder="璇疯緭鍏ュ鐞嗗娉紙鏈�澶�500瀛楋級"
-            maxlength="500"
-            show-word-limit
-          />
-        </el-form-item>
+            <el-form-item label="鎶ュ绉戝" prop="ccdepts">
+              <el-select
+                v-model="processForm.ccdepts"
+                placeholder="璇烽�夋嫨鎶ュ绉戝"
+                multiple
+                filterable
+                collapse-tags
+                style="width: 100%"
+                :disabled="processForm.handleFlag !== '1'"
+              >
+                <el-option
+                  v-for="dept in deptList"
+                  :key="dept.deptCode"
+                  :label="dept.label"
+                  :value="dept.deptCode"
+                />
+              </el-select>
+            </el-form-item>
 
-        <el-form-item label="闄勪欢涓婁紶">
-          <el-upload
-            class="upload-demo"
-            action="#"
-            :on-preview="handlePreview"
-            :on-remove="handleRemove"
-            :before-remove="beforeRemove"
-            :limit="3"
-            :on-exceed="handleExceed"
-            :file-list="fileList"
-          >
-            <el-button size="small" type="primary">鐐瑰嚮涓婁紶</el-button>
-            <div slot="tip" class="el-upload__tip">
-              鏀寔涓婁紶鍥剧墖銆佹枃妗g瓑闄勪欢锛屽崟涓枃浠朵笉瓒呰繃10MB
-            </div>
-          </el-upload>
-        </el-form-item>
-      </el-form>
+            <el-form-item label="澶勭悊缁撴灉" prop="handleresult">
+              <el-select
+                v-model="processForm.handleresult"
+                placeholder="璇烽�夋嫨澶勭悊缁撴灉"
+                style="width: 100%"
+                :disabled="processForm.handleFlag !== '1'"
+              >
+                <el-option label="宸茶В鍐�" value="resolved" />
+                <el-option label="宸茶В閲�" value="explained" />
+                <el-option label="宸茶浆浜�" value="transferred" />
+                <el-option label="闇�鏀硅繘" value="improvement" />
+                <el-option label="宸查┏鍥�" value="rejected" />
+              </el-select>
+            </el-form-item>
+
+            <el-form-item label="澶勭悊璇存槑" prop="handledesc">
+              <el-input
+                v-model="processForm.handledesc"
+                type="textarea"
+                :rows="4"
+                placeholder="璇疯緭鍏ュ鐞嗚鏄庯紙鏈�澶�500瀛楋級"
+                maxlength="500"
+                show-word-limit
+                :disabled="processForm.handleFlag !== '1'"
+              />
+            </el-form-item>
+
+            <el-form-item
+              label="鏈�缁堟剰瑙�"
+              prop="finaloption"
+              v-if="hasQualityPermission"
+            >
+              <el-input
+                v-model="processForm.finaloption"
+                type="textarea"
+                :rows="3"
+                placeholder="璇疯緭鍏ユ渶缁堝鐞嗘剰瑙侊紙鏈�澶�300瀛楋級"
+                maxlength="300"
+                show-word-limit
+              />
+            </el-form-item>
+          </el-form>
+        </div>
+
+        <!-- 鍙冲崐閮ㄥ垎锛氭祦杞椂闂寸嚎锛堥�変腑娴佽浆鍚庡睍寮�锛� -->
+        <div class="flow-right" v-if="processForm.isFlow === '1'">
+          <el-timeline>
+            <el-timeline-item type="primary" placement="top" color="#5788fe">
+              <div class="flow-step-title">绗竴姝� 路 娴佽浆鍙戣捣</div>
+              <el-form label-width="90px" size="small" class="flow-step-form">
+                <el-form-item label="娴佽浆浜�">
+                  <el-input
+                    v-model="processForm.flowPerson"
+                    placeholder="娴佽浆浜猴紙榛樿褰撳墠鐢ㄦ埛锛�"
+                  />
+                </el-form-item>
+                <el-form-item label="娴佽浆浜虹紪鍙�">
+                  <el-input
+                    v-model="processForm.flowPersonCode"
+                    placeholder="娴佽浆浜虹紪鍙凤紙榛樿褰撳墠鐢ㄦ埛宸ュ彿锛�"
+                  />
+                </el-form-item>
+                <el-form-item label="娴佽浆鏃堕棿">
+                  <el-date-picker
+                    v-model="processForm.flowTime"
+                    type="datetime"
+                    value-format="yyyy-MM-dd HH:mm:ss"
+                    placeholder="娴佽浆鏃堕棿"
+                    style="width: 100%"
+                  />
+                </el-form-item>
+                <el-form-item label="娴佽浆璇存槑">
+                  <el-input
+                    v-model="processForm.flowDesc"
+                    type="textarea"
+                    :rows="2"
+                    placeholder="璇疯緭鍏ユ祦杞鏄�"
+                  />
+                </el-form-item>
+                <el-form-item label="娴佽浆绉戝">
+                  <el-select
+                    v-model="processForm.flowDepts"
+                    multiple
+                    filterable
+                    collapse-tags
+                    placeholder="璇烽�夋嫨娴佽浆绉戝"
+                    style="width: 100%"
+                    @change="onFlowDeptChange"
+                  >
+                    <el-option
+                      v-for="dept in deptList"
+                      :key="dept.deptCode"
+                      :label="dept.label"
+                      :value="dept.deptCode"
+                    />
+                  </el-select>
+                </el-form-item>
+                <el-form-item label="娴佽浆绉戝code">
+                  <el-select
+                    v-model="processForm.flowDeptCodes"
+                    multiple
+                    disabled
+                    placeholder="閫夋嫨绉戝鍚庤嚜鍔ㄧ敓鎴�"
+                    style="width: 100%"
+                  >
+                    <el-option
+                      v-for="code in processForm.flowDeptCodes"
+                      :key="code"
+                      :label="code"
+                      :value="code"
+                    />
+                  </el-select>
+                </el-form-item>
+                <el-form-item label="鎺ㄩ�佽礋璐d汉">
+                  <el-select
+                    v-model="processForm.pushUsers"
+                    multiple
+                    filterable
+                    collapse-tags
+                    placeholder="璇烽�夋嫨鎺ㄩ�佽礋璐d汉"
+                    style="width: 100%"
+                    @change="onPushUserChange"
+                  >
+                    <el-option
+                      v-for="u in userList"
+                      :key="u.userId"
+                      :label="u.nickName || u.userName"
+                      :value="u.userId"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-form>
+            </el-timeline-item>
+
+            <el-timeline-item type="primary" placement="top" color="#e6a23c">
+              <div class="flow-step-title">绗簩姝� 路 绉戝鍙嶉</div>
+              <el-table
+                :data="processForm.flowFeedbacks"
+                border
+                size="mini"
+                v-if="processForm.flowFeedbacks.length > 0"
+              >
+                <el-table-column
+                  label="绉戝"
+                  prop="deptName"
+                  width="120"
+                  align="center"
+                />
+                <el-table-column label="鏄惁鍚屾剰" width="130" align="center">
+                  <template slot-scope="scope">
+                    <el-select
+                      v-model="scope.row.agree"
+                      placeholder="璇烽�夋嫨"
+                      size="mini"
+                    >
+                      <el-option label="鍚屾剰" value="1" />
+                      <el-option label="涓嶅悓鎰�" value="0" />
+                    </el-select>
+                  </template>
+                </el-table-column>
+                <el-table-column label="鍏蜂綋璇存槑" min-width="200" align="center">
+                  <template slot-scope="scope">
+                    <el-input
+                      v-model="scope.row.desc"
+                      placeholder="璇疯緭鍏ュ叿浣撹鏄�"
+                      size="mini"
+                    />
+                  </template>
+                </el-table-column>
+              </el-table>
+              <div v-else class="flow-empty-tip">
+                璇峰厛鍦ㄧ涓�姝ラ�夋嫨娴佽浆绉戝
+              </div>
+            </el-timeline-item>
+
+            <el-timeline-item type="primary" placement="top" color="#67c23a">
+              <div class="flow-step-title">绗笁姝� 路 澶勭悊缁撴灉</div>
+              <el-form label-width="90px" size="small" class="flow-step-form">
+                <el-form-item label="澶勭悊浜�">
+                  <el-input
+                    v-model="processForm.handleBy"
+                    placeholder="澶勭悊浜�"
+                  />
+                </el-form-item>
+                <el-form-item label="澶勭悊鏃堕棿">
+                  <el-date-picker
+                    v-model="processForm.handleTime"
+                    type="datetime"
+                    value-format="yyyy-MM-dd HH:mm:ss"
+                    placeholder="澶勭悊鏃堕棿"
+                    style="width: 100%"
+                  />
+                </el-form-item>
+              </el-form>
+            </el-timeline-item>
+          </el-timeline>
+        </div>
+      </div>
       <span slot="footer" class="dialog-footer">
         <el-button @click="processDialogVisible = false">鍙栨秷</el-button>
         <el-button type="primary" @click="submitProcess" :loading="processing">
@@ -310,44 +540,60 @@
         label-width="100px"
         size="medium"
       >
-        <el-form-item label="澶勭悊鐘舵��" prop="status">
+        <el-form-item label="澶勭悊鐘舵��" prop="handleFlag">
           <el-select
-            v-model="batchProcessForm.status"
+            v-model="batchProcessForm.handleFlag"
             placeholder="璇烽�夋嫨澶勭悊鐘舵��"
             style="width: 100%"
           >
-            <el-option label="澶勭悊涓�" :value="1" />
-            <el-option label="宸插鐞�" :value="2" />
-            <el-option label="宸查┏鍥�" :value="3" />
+            <el-option label="宸插鐞�" :value="'1'" />
+            <el-option label="鍙栨秷澶勭悊" :value="'0'" />
           </el-select>
         </el-form-item>
 
-        <el-form-item label="鎶ュ绉戝" prop="reportDepts">
+        <el-form-item label="鎶ュ绉戝" prop="ccdepts">
           <el-select
-            v-model="batchProcessForm.reportDepts"
+            v-model="batchProcessForm.ccdepts"
             placeholder="璇烽�夋嫨鎶ュ绉戝"
             multiple
             filterable
             collapse-tags
             style="width: 100%"
+            :disabled="batchProcessForm.handleFlag !== '1'"
           >
             <el-option
               v-for="dept in deptList"
-              :key="dept.id"
-              :label="dept.name"
-              :value="dept.id"
+              :key="dept.deptCode"
+              :label="dept.label"
+              :value="dept.deptCode"
             />
           </el-select>
         </el-form-item>
 
-        <el-form-item label="澶勭悊澶囨敞" prop="remark">
+        <el-form-item label="澶勭悊缁撴灉" prop="handleresult">
+          <el-select
+            v-model="batchProcessForm.handleresult"
+            placeholder="璇烽�夋嫨澶勭悊缁撴灉"
+            style="width: 100%"
+            :disabled="batchProcessForm.handleFlag !== '1'"
+          >
+            <el-option label="宸茶В鍐�" value="resolved" />
+            <el-option label="宸茶В閲�" value="explained" />
+            <el-option label="宸茶浆浜�" value="transferred" />
+            <el-option label="闇�鏀硅繘" value="improvement" />
+            <el-option label="宸查┏鍥�" value="rejected" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="澶勭悊璇存槑" prop="handledesc">
           <el-input
-            v-model="batchProcessForm.remark"
+            v-model="batchProcessForm.handledesc"
             type="textarea"
             :rows="4"
-            placeholder="璇疯緭鍏ュ鐞嗗娉紙鏈�澶�500瀛楋級"
+            placeholder="璇疯緭鍏ュ鐞嗚鏄庯紙鏈�澶�500瀛楋級"
             maxlength="500"
             show-word-limit
+            :disabled="batchProcessForm.handleFlag !== '1'"
           />
         </el-form-item>
       </el-form>
@@ -358,24 +604,57 @@
           @click="submitBatchProcess"
           :loading="batchProcessing"
         >
-          鎵归噺鎻愪氦
+          鎵归噺鎻愪氦 ({{ selectedExceptionIds.length }})
         </el-button>
       </span>
+    </el-dialog>
+    <!-- 杩涘害瀵硅瘽妗� -->
+    <el-dialog
+      title="鎵归噺澶勭悊杩涘害"
+      :visible.sync="batchProgress.visible"
+      width="400px"
+      :close-on-click-modal="false"
+      :show-close="false"
+      :close-on-press-escape="false"
+    >
+      <div class="progress-content">
+        <el-progress
+          :percentage="batchProgress.percentage"
+          :status="batchProgress.percentage === 100 ? 'success' : ''"
+        />
+        <div class="progress-info">
+          宸插鐞� {{ batchProgress.processed }}/{{ batchProgress.total }} 鏉¤褰�
+        </div>
+      </div>
     </el-dialog>
     <!-- 寮傚父璇︽儏寮规 -->
     <Details-anomaly
       :visible="detailDialogVisible"
       :record-id="selectedRecordId"
       :title="detailDialogTitle"
+      :record-data="selectedRecordData"
       @update:visible="handleDetailDialogClose"
       @processed="handleProcessed"
       @close="handleDetailDialogClose"
+    />
+
+    <!-- 褰曢煶鎾斁鍣� -->
+    <audio
+      v-if="audioUrl"
+      :src="audioUrl"
+      ref="audioPlayer"
+      controls
+      style="display: none"
     />
   </div>
 </template>
 
 <script>
 import DetailsAnomaly from "./components/DetailsAnomaly.vue";
+import { tracelist, traceedit } from "@/api/AiCentre/index";
+import dayjs from "dayjs";
+import { deptTreeSelect, listUser } from "@/api/system/user";
+
 export default {
   name: "BatchProcess",
   components: {
@@ -383,10 +662,15 @@
   },
   data() {
     return {
-      // 娣诲姞浠ヤ笅鏁版嵁
+      // 璇︽儏寮规鐩稿叧
       detailDialogVisible: false,
       selectedRecordId: null,
+      selectedRecordData: null,
       detailDialogTitle: "寮傚父鍙嶉璇︽儏",
+
+      // 闊抽鎾斁
+      audioUrl: "",
+
       // 褰撳墠澶勭悊鐨勫紓甯窱D
       currentExceptionId: null,
 
@@ -395,8 +679,10 @@
 
       // 杩囨护鍙傛暟
       filterParams: {
-        deptId: "",
-        status: "",
+        todeptcode: "",
+        handleFlag: "",
+        templateType: null,
+        scriptids: null,
         pageNum: 1,
         pageSize: 10,
       },
@@ -406,19 +692,13 @@
       processing: false,
       batchProcessing: false,
 
+      // 鏉冮檺鎺у埗
+      hasQualityPermission: false, // 鏄惁鍏锋湁璐ㄧ鏉冮檺
+
       // 绉戝鍒楄〃
-      deptList: [
-        { id: 1, name: "蹇冭绠″唴绉�" },
-        { id: 2, name: "绁炵粡鍐呯" },
-        { id: 3, name: "鏅绉�" },
-        { id: 4, name: "楠ㄧ" },
-        { id: 5, name: "濡囦骇绉�" },
-        { id: 6, name: "鍎跨" },
-        { id: 7, name: "鎬ヨ瘖绉�" },
-        { id: 8, name: "鍛煎惛鍐呯" },
-        { id: 9, name: "娑堝寲鍐呯" },
-        { id: 10, name: "鍐呭垎娉岀" },
-      ],
+      deptList: [],
+      // 鐢ㄦ埛鍒楄〃锛堟祦杞礋璐d汉鍊欓�夛級
+      userList: [],
 
       // 寮傚父鍒楄〃鏁版嵁
       exceptionList: [],
@@ -427,183 +707,199 @@
       // 澶勭悊瀵硅瘽妗�
       processDialogVisible: false,
       processForm: {
-        status: "",
-        reportDepts: [],
-        remark: "",
+        handleFlag: "",
+        ccdepts: [],
+        handleresult: "",
+        handledesc: "",
+        finaloption: "",
+        // 娴佽浆鐩稿叧
+        isFlow: "0",
+        flowPerson: "",
+        flowPersonCode: "",
+        flowTime: "",
+        flowDesc: "",
+        flowDepts: [],
+        flowDeptNames: [],
+        flowDeptCodes: [],
+        pushUsers: [],
+        pushUserNames: [],
+        pushUserNos: [],
+        flowFeedbacks: [],
+        handleBy: "",
+        handleTime: "",
+      },
+      batchProgress: {
+        visible: false,
+        percentage: 0,
+        processed: 0,
+        total: 0,
       },
       processRules: {
-        status: [
+        handleFlag: [
           { required: true, message: "璇烽�夋嫨澶勭悊鐘舵��", trigger: "change" },
         ],
-        remark: [
-          { required: true, message: "璇疯緭鍏ュ鐞嗗娉�", trigger: "blur" },
+        handleresult: [
           {
-            min: 5,
-            max: 500,
-            message: "澶囨敞闀垮害鍦� 5 鍒� 500 涓瓧绗�",
+            required: true,
+            message: "璇烽�夋嫨澶勭悊缁撴灉",
+            trigger: "change",
+            validator: (rule, value, callback) => {
+              if (this.processForm.handleFlag === "1" && !value) {
+                callback(new Error("璇烽�夋嫨澶勭悊缁撴灉"));
+              } else {
+                callback();
+              }
+            },
+          },
+        ],
+        handledesc: [
+          {
+            required: true,
+            message: "璇疯緭鍏ュ鐞嗚鏄�",
             trigger: "blur",
+            validator: (rule, value, callback) => {
+              if (
+                this.processForm.handleFlag === "1" &&
+                (!value || value.trim().length < 3)
+              ) {
+                callback(new Error("澶勭悊璇存槑鑷冲皯3涓瓧绗�"));
+              } else {
+                callback();
+              }
+            },
           },
         ],
       },
-      fileList: [],
 
       // 鎵归噺澶勭悊瀵硅瘽妗�
       batchDialogVisible: false,
       batchProcessForm: {
-        status: "",
-        reportDepts: [],
-        remark: "",
+        handleFlag: "",
+        ccdepts: [],
+        handleresult: "",
+        handledesc: "",
       },
     };
   },
 
+  created() {
+    // 浠庤矾鐢卞弬鏁拌幏鍙栭棶棰業D
+    this.filterParams.scriptids =
+      this.$route.query.questionId || this.$route.query.questionIds || null;
+    // if (this.$route.query.questionId) {
+    // } else if (this.$route.query.questionIds) {
+    //   console.log(
+    //     this.$route.query.questionIds,
+    //     "this.$route.query.questionIds"
+    //   );
+
+    this.filterParams.templateType = Number(this.$route.query.type) || null;
+
+    //   this.filterParams.scriptid = null;
+    // }
+    this.hasQualityPermission = this.checkQualityPermission();
+  },
+
   mounted() {
     this.loadExceptionList();
+    this.getDeptOptions();
+    this.getUserOptions();
   },
 
   methods: {
-    // 鍔犺浇寮傚父鍒楄〃
-    async loadExceptionList() {
-      this.loading = true;
+    // 鏍煎紡鍖栨棩鏈熸椂闂�
+    formatDateTime(dateTime) {
+      if (!dateTime) return "";
       try {
-        // Mock 鏁版嵁
-        await new Promise((resolve) => {
-          setTimeout(() => {
-            this.exceptionList = [
-              {
-                id: 1,
-                responsibilityDept: "蹇冭绠″唴绉�",
-                unsatisfactoryDetail:
-                  "鍖荤敓鏌ユ埧鏃堕棿澶煭锛屾矡閫氫笉澶熷厖鍒嗭紝瀵圭梾鎯呰В閲婁笉澶熻缁�",
-                patientName: "寮犲厛鐢�",
-                gender: 1,
-                age: 45,
-                phone: "138****1234",
-                dischargeDept: "蹇冭绠″唴绉�",
-                dischargeWard: "鍐呯涓�鐥呭尯",
-                fillTime: "2024-01-15 10:30:25",
-                processStatus: 0,
-                questionnaireId: 1001,
-              },
-              {
-                id: 2,
-                responsibilityDept: "绁炵粡鍐呯",
-                unsatisfactoryDetail:
-                  "鎶ゅ+鎵撻拡鎶�鏈笉浣筹紝鎵庝簡涓夋鎵嶆垚鍔燂紝涓旀�佸害涓嶅鑰愬績",
-                patientName: "鏉庡コ澹�",
-                gender: 0,
-                age: 38,
-                phone: "139****5678",
-                dischargeDept: "绁炵粡鍐呯",
-                dischargeWard: "鍐呯浜岀梾鍖�",
-                fillTime: "2024-01-14 16:20:10",
-                processStatus: 0,
-                questionnaireId: 1002,
-              },
-              {
-                id: 3,
-                responsibilityDept: "鏅绉�",
-                unsatisfactoryDetail: "鏈悗鎹㈣嵂涓嶅強鏃讹紝浼ゅ彛鐤肩棝鏃舵病鏈夊強鏃跺鐞�",
-                patientName: "鐜嬪厛鐢�",
-                gender: 1,
-                age: 52,
-                phone: "137****9012",
-                dischargeDept: "鏅绉�",
-                dischargeWard: "澶栫涓�鐥呭尯",
-                fillTime: "2024-01-13 09:15:45",
-                processStatus: 1,
-                questionnaireId: 1003,
-              },
-              {
-                id: 4,
-                responsibilityDept: "楠ㄧ",
-                unsatisfactoryDetail: "搴峰鎸囧涓嶅涓撲笟锛屽鎭㈠杩囩▼鎻忚堪涓嶆竻妤�",
-                patientName: "鍒樺コ澹�",
-                gender: 0,
-                age: 65,
-                phone: "136****3456",
-                dischargeDept: "楠ㄧ",
-                dischargeWard: "澶栫浜岀梾鍖�",
-                fillTime: "2024-01-12 14:40:30",
-                processStatus: 0,
-                questionnaireId: 1004,
-              },
-              {
-                id: 5,
-                responsibilityDept: "濡囦骇绉�",
-                unsatisfactoryDetail:
-                  "浜у墠妫�鏌ユ帓闃熸椂闂磋繃闀匡紝绛夊緟鏈熼棿娌℃湁浼戞伅搴т綅",
-                patientName: "闄堝コ澹�",
-                gender: 0,
-                age: 28,
-                phone: "135****7890",
-                dischargeDept: "濡囦骇绉�",
-                dischargeWard: "濡囦骇绉戠梾鍖�",
-                fillTime: "2024-01-11 11:25:15",
-                processStatus: 2,
-                questionnaireId: 1005,
-              },
-              {
-                id: 6,
-                responsibilityDept: "鍎跨",
-                unsatisfactoryDetail:
-                  "鍎跨鐢ㄨ嵂鍓傞噺浜や唬涓嶆竻鏅帮紝鐢ㄨ嵂娉ㄦ剰浜嬮」娌℃湁璇存槑",
-                patientName: "璧靛疂瀹�",
-                gender: 1,
-                age: 5,
-                phone: "134****1234",
-                dischargeDept: "鍎跨",
-                dischargeWard: "鍎跨鐥呭尯",
-                fillTime: "2024-01-10 15:50:20",
-                processStatus: 0,
-                questionnaireId: 1006,
-              },
-              {
-                id: 7,
-                responsibilityDept: "鎬ヨ瘖绉�",
-                unsatisfactoryDetail: "鎬ヨ瘖绛夊緟鏃堕棿杩囬暱锛岀梾鎯呮病鏈夊緱鍒板強鏃惰瘎浼�",
-                patientName: "瀛欏厛鐢�",
-                gender: 1,
-                age: 40,
-                phone: "133****5678",
-                dischargeDept: "鎬ヨ瘖绉�",
-                dischargeWard: "鎬ヨ瘖鐥呭尯",
-                fillTime: "2024-01-09 10:15:40",
-                processStatus: 0,
-                questionnaireId: 1007,
-              },
-              {
-                id: 8,
-                responsibilityDept: "鍛煎惛鍐呯",
-                unsatisfactoryDetail: "鍖荤敓寮�鑽緝澶氾紝璐圭敤杈冮珮锛屾病鏈夎鏄庡繀瑕佹��",
-                patientName: "鍛ㄥコ澹�",
-                gender: 0,
-                age: 55,
-                phone: "132****9012",
-                dischargeDept: "鍛煎惛鍐呯",
-                dischargeWard: "鍐呯涓�鐥呭尯",
-                fillTime: "2024-01-08 13:30:55",
-                processStatus: 1,
-                questionnaireId: 1008,
-              },
-            ];
-            this.total = this.exceptionList.length;
-            resolve();
-          }, 500);
+        const date = new Date(dateTime);
+        if (isNaN(date.getTime())) {
+          return dateTime;
+        }
+        return (
+          date.toLocaleDateString().replace(/\//g, "-") +
+          " " +
+          date.toTimeString().split(" ")[0]
+        );
+      } catch (error) {
+        console.error("鏃ユ湡鏍煎紡鍖栭敊璇�:", error);
+        return dateTime;
+      }
+    },
+    /** 鏌ヨ绉戝鍒楄〃 */
+    getDeptOptions() {
+      deptTreeSelect()
+        .then((res) => {
+          if (res.code == 200) {
+            this.deptList = this.flattenArray(res.data) || [];
+          }
+        })
+        .catch((error) => {
+          console.error("鑾峰彇绉戝鍒楄〃澶辫触:", error);
+          this.$message.error("鑾峰彇绉戝鍒楄〃澶辫触");
         });
-      } finally {
-        this.loading = false;
+    },
+    /** 鏌ヨ鐢ㄦ埛鍒楄〃锛堟祦杞礋璐d汉鍊欓�夛級 */
+    getUserOptions() {
+      listUser({ pageNum: 1, pageSize: 999 })
+        .then((res) => {
+          if (res.code == 200) {
+            this.userList = res.rows || [];
+          }
+        })
+        .catch((error) => {
+          console.error("鑾峰彇鐢ㄦ埛鍒楄〃澶辫触:", error);
+          this.$message.error("鑾峰彇鐢ㄦ埛鍒楄〃澶辫触");
+        });
+    },
+    flattenArray(multiArray) {
+      let result = [];
+
+      function flatten(element) {
+        if (element.children && element.children.length > 0) {
+          element.children.forEach((child) => flatten(child));
+        } else {
+          let item = JSON.parse(JSON.stringify(element));
+          result.push(item);
+        }
+      }
+
+      multiArray.forEach((element) => flatten(element));
+      return result;
+    },
+    // 瑙f瀽鎮h�呮弿杩颁俊鎭�
+    parsePatDesc(patdesc) {
+      if (!patdesc) return [];
+
+      try {
+        const parts = patdesc.split("|");
+        const items = [];
+
+        if (parts[0]) items.push({ label: "濮撳悕", value: parts[0] });
+        if (parts[1]) items.push({ label: "鐢佃瘽", value: parts[1] });
+        if (parts[2]) items.push({ label: "绉戝", value: parts[2] });
+
+        return items;
+      } catch (error) {
+        console.error("瑙f瀽鎮h�呬俊鎭け璐�:", error);
+        return [];
       }
     },
 
+    // 妫�鏌ヨ川绠℃潈闄�
+    checkQualityPermission() {
+      // 杩欓噷鍙互鏍规嵁瀹為檯鏉冮檺绯荤粺瀹炵幇
+      const userRoles = this.$store.getters.roles || [];
+      return (
+        userRoles.includes("quality_manager") || userRoles.includes("admin")
+      );
+    },
+
     // 鑾峰彇鐘舵�佹爣绛剧被鍨�
-    getStatusTagType(status) {
-      switch (status) {
-        case 0:
-          return "warning"; // 寰呭鐞�
-        case 1:
-          return "primary"; // 澶勭悊涓�
-        case 2:
+    getStatusTagType(handleFlag) {
+      switch (handleFlag) {
+        case "0":
+          return "warning"; // 鏈鐞�
+        case "1":
           return "success"; // 宸插鐞�
         default:
           return "info";
@@ -611,16 +907,82 @@
     },
 
     // 鑾峰彇鐘舵�佹枃鏈�
-    getStatusText(status) {
-      switch (status) {
-        case 0:
-          return "寰呭鐞�";
-        case 1:
-          return "澶勭悊涓�";
-        case 2:
+    getStatusText(handleFlag) {
+      switch (handleFlag) {
+        case "0":
+          return "鏈鐞�";
+        case "1":
           return "宸插鐞�";
         default:
           return "鏈煡";
+      }
+    },
+
+    // 鎾斁褰曢煶
+    handlePlayAudio(url) {
+      this.audioUrl = url;
+      this.$nextTick(() => {
+        const audioPlayer = this.$refs.audioPlayer;
+        if (audioPlayer) {
+          audioPlayer.play().catch((error) => {
+            console.error("鎾斁澶辫触:", error);
+            this.$message.error("闊抽鎾斁澶辫触");
+          });
+        }
+      });
+    },
+
+    // 鏋勫缓鏌ヨ鍙傛暟
+    buildQueryParams() {
+      const params = {
+        pageNum: this.filterParams.pageNum,
+        pageSize: this.filterParams.pageSize,
+      };
+
+      if (this.filterParams.todeptcode) {
+        params.todeptcode = this.filterParams.todeptcode;
+      }
+
+      if (this.filterParams.handleFlag !== "") {
+        params.handleFlag = this.filterParams.handleFlag;
+      }
+
+      if (this.filterParams.templateType) {
+        params.templateType = this.filterParams.templateType;
+      }
+
+      // if (this.filterParams.scriptid) {
+      //   params.scriptid = this.filterParams.scriptid;
+      // }
+      if (this.filterParams.scriptids) {
+        params.scriptids = this.filterParams.scriptids.split(",");
+      }
+
+      return params;
+    },
+
+    // 鍔犺浇寮傚父鍒楄〃
+    async loadExceptionList() {
+      this.loading = true;
+      try {
+        const params = this.buildQueryParams();
+        const response = await tracelist(params);
+
+        if (response && response.code === 200) {
+          this.exceptionList = response.rows || [];
+          this.total = response.total || 0;
+        } else {
+          this.exceptionList = [];
+          this.total = 0;
+          this.$message.error(response?.msg || "鍔犺浇寮傚父鍒楄〃澶辫触");
+        }
+      } catch (error) {
+        console.error("鍔犺浇寮傚父鍒楄〃澶辫触:", error);
+        this.$message.error("鍔犺浇寮傚父鍒楄〃澶辫触锛岃绋嶅悗閲嶈瘯");
+        this.exceptionList = [];
+        this.total = 0;
+      } finally {
+        this.loading = false;
       }
     },
 
@@ -633,11 +995,16 @@
     // 閲嶇疆绛涢��
     handleResetFilter() {
       this.filterParams = {
-        deptId: "",
-        status: "",
+        todeptcode: "",
+        handleFlag: "",
+        scriptids:
+          this.$route.query.questionId || this.$route.query.questionIds || null,
+        templateType: Number(this.$route.query.type) || null,
+
         pageNum: 1,
         pageSize: 10,
       };
+      this.selectedExceptionIds = [];
       this.loadExceptionList();
     },
 
@@ -652,55 +1019,313 @@
         this.$message.warning("璇峰厛閫夋嫨瑕佸鐞嗙殑寮傚父鍙嶉");
         return;
       }
+
+      // 閲嶇疆鎵归噺澶勭悊琛ㄥ崟
+      this.batchProcessForm = {
+        handleFlag: "",
+        ccdepts: [],
+        handleresult: "",
+        handledesc: "",
+      };
+
       this.batchDialogVisible = true;
     },
 
     // 杩斿洖寮傚父鍒楄〃
     handleGoBack() {
-      this.$router.push("/satisfaction/exception/list");
+      // this.$router.push("/satisfaction/exception/list");
+      this.$router.push("/Intelligentcenter/dispose");
     },
 
     // 鏌ョ湅璇︽儏
     handleViewDetail(row) {
       this.selectedRecordId = row.id;
-      this.detailDialogTitle = `${row.patientName} - 寮傚父鍙嶉璇︽儏`;
+      this.selectedRecordData = row;
+
+      // 鐢熸垚寮规鏍囬
+      let title = "寮傚父鍙嶉璇︽儏";
+      if (row.patdesc) {
+        const patientName = row.patdescJson.sendname;
+        if (patientName) {
+          title = `${patientName} - ${title}`;
+        }
+      }
+      this.detailDialogTitle = title;
+
       this.detailDialogVisible = true;
     },
+
     // 澶勭悊璇︽儏寮规鍏抽棴
     handleDetailDialogClose() {
       this.detailDialogVisible = false;
       this.selectedRecordId = null;
-    }, // 澶勭悊瀹屾垚鍚庣殑鍥炶皟
+      this.selectedRecordData = null;
+    },
+
+    // 澶勭悊瀹屾垚鍚庣殑鍥炶皟
     handleProcessed() {
-      // 閲嶆柊鍔犺浇鏁版嵁
       this.loadExceptionList();
     },
+
     // 澶勭悊鍗曚釜寮傚父
     handleProcess(row) {
       this.currentExceptionId = row.id;
+
+      const nickName = this.$store.state.user.nickName || "";
+      const now = dayjs().format("YYYY-MM-DD HH:mm:ss");
+
+      // 鏄惁娴佽浆锛堝悗绔� circulationStatus锛�0 涓嶆祦杞� / 1 娴佽浆锛�
+      const isFlow = String(row.circulationStatus) === "1" ? "1" : "0";
+
+      // 鎶ュ绉戝锛堝吋瀹规暟缁勬垨閫楀彿鍒嗛殧瀛楃涓诧級
+      const ccdepts = Array.isArray(row.ccdepts)
+        ? row.ccdepts
+        : row.ccdepts
+        ? row.ccdepts.split(",")
+        : [];
+
+      // 娴佽浆绉戝 code / 鍚嶇О
+      const flowDeptCodes = row.circulationDeptCode
+        ? row.circulationDeptCode.split(",")
+        : [];
+      const flowDeptNames = row.circulationDeptName
+        ? row.circulationDeptName.split(",")
+        : [];
+
+      // 鎺ㄩ�佽礋璐d汉锛氬悗绔瓨宸ュ彿(userName)锛屼笅鎷� value 鐢� userId锛岄渶鍥炴槧
+      const pushUserNos = row.circulationDeptPersonCode
+        ? row.circulationDeptPersonCode.split(",")
+        : [];
+      const pushUserNames = row.circulationDeptPersonName
+        ? row.circulationDeptPersonName.split(",")
+        : [];
+      const pushUsers = pushUserNos.map((no) => {
+        const u = this.userList.find((item) => item.userName === no);
+        return u ? u.userId : no;
+      });
+
+      // 绉戝鍙嶉锛氫粠 handledesc JSON 涓繕鍘�
+      let flowFeedbacks = [];
+      try {
+        const parsed =
+          typeof row.handledesc === "string" &&
+          row.handledesc.trim().startsWith("{")
+            ? JSON.parse(row.handledesc)
+            : null;
+        if (parsed && parsed.flow && Array.isArray(parsed.flow.feedbacks)) {
+          flowFeedbacks = parsed.flow.feedbacks;
+        }
+      } catch (e) {
+        // handledesc 闈� JSON锛屽拷鐣�
+      }
+
+      // 娴佽浆鏃堕棿锛氬悗绔� Date 瀛楁锛岀粺涓�杞垚 yyyy-MM-dd HH:mm:ss 渚涙棩鏈熼�夋嫨鍣ㄥ洖鏄�
+      let flowTime = now;
+      if (row.circulationTime) {
+        const d = new Date(
+          typeof row.circulationTime === "number"
+            ? row.circulationTime
+            : String(row.circulationTime).replace(" ", "T")
+        );
+        if (!isNaN(d.getTime())) {
+          const pad = (n) => String(n).padStart(2, "0");
+          flowTime =
+            d.getFullYear() +
+            "-" +
+            pad(d.getMonth() + 1) +
+            "-" +
+            pad(d.getDate()) +
+            " " +
+            pad(d.getHours()) +
+            ":" +
+            pad(d.getMinutes()) +
+            ":" +
+            pad(d.getSeconds());
+        }
+      }
+
+      // 鍒濆鍖栬〃鍗曟暟鎹�
       this.processForm = {
-        status: row.processStatus === 0 ? 1 : row.processStatus,
-        reportDepts: [],
-        remark: "",
+        handleFlag: "1",
+        ccdepts,
+        handleresult: row.handleresult || "",
+        handledesc: this.formatHandledesc(row.handledesc),
+        finaloption: row.finaloption || "",
+        // 娴佽浆鐩稿叧
+        isFlow,
+        flowPerson: row.circulationPersonName || nickName,
+        flowPersonCode:
+          row.circulationPersonCode || this.$store.state.user.userName || "",
+        flowTime,
+        flowDesc: row.circulationExplain || "",
+        flowDepts: [...flowDeptCodes],
+        flowDeptNames: [...flowDeptNames],
+        flowDeptCodes: [...flowDeptCodes],
+        pushUsers,
+        pushUserNames: [...pushUserNames],
+        pushUserNos: [...pushUserNos],
+        flowFeedbacks,
+        handleBy: nickName,
+        handleTime: now,
       };
+
       this.processDialogVisible = true;
+    },
+
+    // 娴佽浆绉戝鍙樺寲锛氬悓姝ョ瀹ゅ悕绉般�佺瀹ode锛屽苟鐢熸垚鍙嶉琛ㄦ牸
+    onFlowDeptChange(codes) {
+      this.processForm.flowDeptCodes = [...codes];
+      this.processForm.flowDeptNames = codes.map((code) => {
+        const dept = this.deptList.find((item) => item.deptCode === code);
+        return dept ? dept.label : code;
+      });
+      // 鐢熸垚/缁存姢绗簩姝ュ弽棣堣〃鏍硷紙淇濈暀宸插~鍐欑殑鍙嶉锛�
+      this.processForm.flowFeedbacks = codes.map((code) => {
+        const existing = this.processForm.flowFeedbacks.find(
+          (item) => item.deptCode === code
+        );
+        if (existing) return existing;
+        const dept = this.deptList.find((item) => item.deptCode === code);
+        return {
+          deptCode: code,
+          deptName: dept ? dept.label : code,
+          agree: "",
+          desc: "",
+        };
+      });
+    },
+
+    // 鎺ㄩ�佽礋璐d汉鍙樺寲锛氬悓姝ヨ礋璐d汉鏄电О銆佸伐鍙�
+    onPushUserChange(ids) {
+      this.processForm.pushUserNames = ids.map((id) => {
+        const user = this.userList.find((u) => String(u.userId) === String(id));
+        return user ? user.nickName || user.userName : "";
+      });
+      this.processForm.pushUserNos = ids.map((id) => {
+        const user = this.userList.find((u) => String(u.userId) === String(id));
+        return user ? user.userName || "" : "";
+      });
+    },
+
+    // 缁勮 handledesc锛氭祦杞椂浠� JSON 褰㈠紡淇濆瓨鍘熷鐞嗚鏄庝笌娴佽浆璇存槑銆佺瀹ゅ弽棣�
+    buildHandledesc() {
+      if (this.processForm.isFlow !== "1") {
+        return this.processForm.handledesc;
+      }
+      const flow = {
+        feedbacks: this.processForm.flowFeedbacks.map((f) => ({
+          deptCode: f.deptCode,
+          deptName: f.deptName,
+          agree: f.agree,
+          desc: f.desc,
+        })),
+      };
+      return JSON.stringify({
+        desc: this.processForm.handledesc,
+        flow,
+      });
+    },
+
+    // 瑙f瀽 handledesc锛氳嫢鏄祦杞� JSON 鍒欏彇鍏朵腑鐨勫鐞嗚鏄庯紝鍚﹀垯鍘熸牱杩斿洖
+    formatHandledesc(val) {
+      if (!val) return "";
+      if (typeof val !== "string") return val;
+      const trimmed = val.trim();
+      if (trimmed.startsWith("{")) {
+        try {
+          const obj = JSON.parse(trimmed);
+          if (obj && obj.desc != null) return obj.desc;
+        } catch (e) {
+          // 涓嶆槸鍚堟硶 JSON锛屾寜鍘熸枃鏈繑鍥�
+        }
+      }
+      return val;
+    },
+
+    // 娴佽浆鏃堕棿杞负甯︽椂鍖虹殑 ISO 鏍煎紡锛屼緵鍚庣 Date 绫诲瀷瀛楁鍙嶅簭鍒楀寲
+    toISODateTime(str) {
+      if (!str) return null;
+      const d = new Date(String(str).replace(" ", "T"));
+      if (isNaN(d.getTime())) return null;
+      const pad = (n) => String(n).padStart(2, "0");
+      const offset = -d.getTimezoneOffset();
+      const sign = offset >= 0 ? "+" : "-";
+      const abs = Math.abs(offset);
+      const tz = sign + pad(Math.floor(abs / 60)) + ":" + pad(abs % 60);
+      return (
+        d.getFullYear() +
+        "-" + pad(d.getMonth() + 1) +
+        "-" + pad(d.getDate()) +
+        "T" + pad(d.getHours()) +
+        ":" + pad(d.getMinutes()) +
+        ":" + pad(d.getSeconds()) +
+        "." + String(d.getMilliseconds()).padStart(3, "0") +
+        tz
+      );
     },
 
     // 鎻愪氦澶勭悊
     async submitProcess() {
       this.$refs.processForm.validate(async (valid) => {
-        if (valid) {
-          this.processing = true;
-          try {
-            // Mock API璋冪敤
-            await new Promise((resolve) => setTimeout(resolve, 1000));
+        if (!valid) {
+          return;
+        }
 
-            this.$message.success("澶勭悊鎻愪氦鎴愬姛");
-            this.processDialogVisible = false;
-            this.loadExceptionList();
-          } finally {
-            this.processing = false;
-          }
+        // 娴佽浆鏃舵牎楠屽繀椤婚�夋嫨娴佽浆绉戝
+        if (
+          this.processForm.isFlow === "1" &&
+          (!this.processForm.flowDepts || this.processForm.flowDepts.length === 0)
+        ) {
+          this.$message.warning("璇峰厛閫夋嫨娴佽浆绉戝");
+          return;
+        }
+
+        this.processing = true;
+
+        try {
+          // 鍑嗗鎻愪氦鏁版嵁
+          const submitData = {
+            id: this.currentExceptionId,
+            handleFlag: this.processForm.handleFlag,
+            handleresult: this.processForm.handleresult,
+            handledesc: this.buildHandledesc(),
+            finaloption: this.processForm.finaloption,
+            handleBy:
+              this.processForm.handleBy || this.$store.state.user.nickName,
+            handleTime:
+              this.processForm.handleTime ||
+              dayjs().format("YYYY-MM-DD HH:mm:ss"),
+            // 灏嗘暟缁勮浆鎹负閫楀彿鍒嗛殧鐨勫瓧绗︿覆
+            ccdepts: Array.isArray(this.processForm.ccdepts)
+              ? this.processForm.ccdepts.join(",")
+              : this.processForm.ccdepts,
+            // 娴佽浆鐩稿叧涓昏〃瀛楁锛堝悗绔凡瀹氱锛�
+            circulationPersonName: this.processForm.flowPerson,
+            circulationPersonCode:
+              this.processForm.flowPersonCode ||
+              this.$store.state.user.userName ||
+              "",
+            circulationStatus: this.processForm.isFlow,
+            circulationDeptName: this.processForm.flowDeptNames.join(","),
+            circulationDeptCode: this.processForm.flowDeptCodes.join(","),
+            circulationDeptPersonName: this.processForm.pushUserNames.join(","),
+            circulationDeptPersonCode: this.processForm.pushUserNos.join(","),
+            circulationExplain: this.processForm.flowDesc,
+            circulationTime: this.toISODateTime(this.processForm.flowTime),
+          };
+          // TODO: 杩欓噷闇�瑕佽皟鐢ㄥ疄闄呯殑澶勭悊鎺ュ彛
+          await traceedit(submitData);
+
+          // await new Promise((resolve) => setTimeout(resolve, 1000));
+
+          this.$message.success("澶勭悊鎻愪氦鎴愬姛");
+          this.processDialogVisible = false;
+          this.loadExceptionList();
+        } catch (error) {
+          console.error("澶勭悊鎻愪氦澶辫触:", error);
+          this.$message.error("澶勭悊鎻愪氦澶辫触锛岃绋嶅悗閲嶈瘯");
+        } finally {
+          this.processing = false;
         }
       });
     },
@@ -708,21 +1333,111 @@
     // 鎻愪氦鎵归噺澶勭悊
     async submitBatchProcess() {
       this.$refs.batchProcessForm.validate(async (valid) => {
-        if (valid) {
-          this.batchProcessing = true;
-          try {
-            // Mock API璋冪敤
-            await new Promise((resolve) => setTimeout(resolve, 1500));
+        if (!valid) {
+          return;
+        }
 
-            this.$message.success(
-              `宸叉壒閲忓鐞� ${this.selectedExceptionIds.length} 鏉″紓甯稿弽棣坄
+        this.batchProcessing = true;
+        // 鏄剧ず杩涘害鏉�
+        this.batchProgress = {
+          visible: true,
+          percentage: 0,
+          processed: 0,
+          total: this.selectedExceptionIds.length,
+        };
+        try {
+          // 鍑嗗鎵归噺鎻愪氦鏁版嵁
+          const processData = {
+            handleFlag: this.batchProcessForm.handleFlag,
+            handleresult: this.batchProcessForm.handleresult,
+            handledesc: this.batchProcessForm.handledesc,
+            ccdepts: Array.isArray(this.batchProcessForm.ccdepts)
+              ? this.batchProcessForm.ccdepts.join(",")
+              : this.batchProcessForm.ccdepts,
+          };
+
+          // 鎺у埗骞跺彂鏁�
+          const CONCURRENT_LIMIT = 10; // 鍚屾椂鏈�澶�3涓姹�
+          const totalCount = this.selectedExceptionIds.length;
+          const results = [];
+          let successCount = 0;
+          let failCount = 0;
+
+          this.$message.info(`寮�濮嬫壒閲忓鐞� ${totalCount} 鏉¤褰�...`);
+
+          // 鍒嗙粍澶勭悊
+          for (
+            let i = 0;
+            i < this.selectedExceptionIds.length;
+            i += CONCURRENT_LIMIT
+          ) {
+            const batchIds = this.selectedExceptionIds.slice(
+              i,
+              i + CONCURRENT_LIMIT
             );
-            this.batchDialogVisible = false;
-            this.selectedExceptionIds = [];
-            this.loadExceptionList();
-          } finally {
-            this.batchProcessing = false;
+
+            // 骞跺彂澶勭悊褰撳墠鎵规
+            const batchPromises = batchIds.map((id) =>
+              traceedit({
+                id: id,
+                ...processData,
+              })
+                .then((result) => ({
+                  id,
+                  success: result && result.code === 200,
+                  error: result?.msg,
+                }))
+                .catch((error) => ({
+                  id,
+                  success: false,
+                  error: error.message,
+                }))
+            );
+
+            const batchResults = await Promise.all(batchPromises);
+            results.push(...batchResults);
+
+            // 鏇存柊缁熻
+            batchResults.forEach((result) => {
+              if (result.success) {
+                successCount++;
+              } else {
+                failCount++;
+                console.error(`澶勭悊璁板綍 ${result.id} 澶辫触:`, result.error);
+              }
+            });
+            // 鏇存柊杩涘害
+            this.batchProgress.processed = i + 1;
+            this.batchProgress.percentage = Math.round(
+              ((i + 1) / totalCount) * 100
+            );
+            // 鏄剧ず杩涘害
+            console.log(
+              `杩涘害: ${Math.min(
+                i + CONCURRENT_LIMIT,
+                totalCount
+              )}/${totalCount}`
+            );
           }
+
+          // 澶勭悊缁撴灉鎻愮ず
+          if (successCount === totalCount) {
+            this.$message.success(`宸叉垚鍔熷鐞嗗叏閮� ${totalCount} 鏉″紓甯稿弽棣坄);
+          } else {
+            this.$message.warning(
+              `宸插鐞� ${successCount} 鏉★紝澶辫触 ${failCount} 鏉″紓甯稿弽棣坄
+            );
+          }
+
+          this.batchDialogVisible = false;
+          this.selectedExceptionIds = [];
+          this.loadExceptionList();
+        } catch (error) {
+          console.error("鎵归噺澶勭悊澶辫触:", error);
+          this.$message.error("鎵归噺澶勭悊澶辫触锛岃绋嶅悗閲嶈瘯");
+        } finally {
+          this.batchProcessing = false;
+          this.batchProgress.visible = false;
         }
       });
     },
@@ -738,27 +1453,6 @@
     handlePageChange(page) {
       this.filterParams.pageNum = page;
       this.loadExceptionList();
-    },
-
-    // 鏂囦欢涓婁紶鐩稿叧鏂规硶
-    handlePreview(file) {
-      console.log("棰勮鏂囦欢:", file);
-    },
-
-    handleRemove(file, fileList) {
-      console.log("绉婚櫎鏂囦欢:", file, fileList);
-    },
-
-    beforeRemove(file) {
-      return this.$confirm(`纭畾绉婚櫎 ${file.name}锛焋);
-    },
-
-    handleExceed(files, fileList) {
-      this.$message.warning(
-        `褰撳墠闄愬埗閫夋嫨 3 涓枃浠讹紝鏈閫夋嫨浜� ${files.length} 涓枃浠讹紝鍏遍�夋嫨浜� ${
-          files.length + fileList.length
-        } 涓枃浠禶
-      );
     },
   },
 };
@@ -827,63 +1521,108 @@
       }
 
       .detail-content {
-        font-size: 13px;
-        color: #606266;
-        line-height: 1.5;
         text-align: left;
+        font-size: 12px;
+        line-height: 1.5;
+
+        .question-text {
+          color: #303133;
+          margin-bottom: 5px;
+          font-weight: 500;
+        }
+
+        .answer-text {
+          color: #f56c6c;
+          margin-bottom: 5px;
+        }
+
+        .matched-text {
+          color: #e6a23c;
+          font-style: italic;
+        }
+
+        strong {
+          color: #606266;
+          font-weight: 600;
+        }
       }
 
       .patient-info {
-        .patient-item {
+        .patient-row {
           display: flex;
           justify-content: space-between;
           align-items: center;
-          margin-bottom: 5px;
-          padding: 2px 0;
+          margin-bottom: 8px;
 
-          .label {
-            font-size: 12px;
-            color: #606266;
-            min-width: 40px;
+          &:last-child {
+            margin-bottom: 0;
           }
 
-          .value {
-            font-size: 13px;
-            color: #333;
-            font-weight: 500;
-            text-align: right;
+          .patient-item {
             flex: 1;
+            display: flex;
+            justify-content: flex-start;
+            align-items: center;
+            padding: 0 5px;
+
+            &.full-width {
+              flex: 1 0 100%;
+              margin-left: 0;
+              margin-right: 0;
+            }
+
+            .label {
+              font-size: 12px;
+              color: #606266;
+              margin-right: 5px;
+              white-space: nowrap;
+            }
+
+            .value {
+              font-size: 12px;
+              color: #333;
+              font-weight: 500;
+              text-align: right;
+              word-break: break-all;
+            }
           }
         }
       }
 
-      .discharge-info {
+      .fill-info,
+      .handle-info {
+        font-size: 12px;
+
         .info-item {
           display: flex;
-          justify-content: space-between;
+          justify-content: flex-start;
           align-items: center;
           margin-bottom: 5px;
           padding: 2px 0;
 
           .label {
-            font-size: 12px;
             color: #606266;
             min-width: 50px;
           }
 
           .value {
-            font-size: 13px;
             color: #333;
             font-weight: 500;
-            text-align: right;
+            // text-align: right;
             flex: 1;
 
             &.time {
-              font-size: 12px;
               color: #909399;
+              font-size: 11px;
             }
           }
         }
+      }
+
+      .no-data {
+        color: #909399;
+        font-style: italic;
+        font-size: 12px;
       }
     }
 
@@ -895,6 +1634,47 @@
   }
 }
 
+// 澶勭悊寮规宸﹀彸甯冨眬
+.flow-dialog-body {
+  display: flex;
+  align-items: stretch;
+
+  .flow-left {
+    flex: 1;
+    min-width: 320px;
+    padding-right: 24px;
+    border-right: 1px solid #ebeef5;
+  }
+
+  .flow-right {
+    flex: 1;
+    min-width: 320px;
+    padding-left: 24px;
+    max-height: 60vh;
+    overflow-y: auto;
+
+    .flow-step-title {
+      font-weight: 600;
+      color: #303133;
+      margin-bottom: 12px;
+      font-size: 14px;
+    }
+
+    .flow-step-form {
+      ::v-deep .el-form-item {
+        margin-bottom: 12px;
+      }
+    }
+
+    .flow-empty-tip {
+      padding: 20px;
+      text-align: center;
+      color: #909399;
+      font-size: 13px;
+    }
+  }
+}
+
 @media (max-width: 768px) {
   .batch-process {
     padding: 10px;

--
Gitblit v1.9.3