From e06cd3953ba8a6e0eee11c235bce9ced419a2800 Mon Sep 17 00:00:00 2001
From: WXL (wul) <wl_5969728@163.com>
Date: 星期三, 03 六月 2026 14:05:08 +0800
Subject: [PATCH] 测试完成

---
 src/views/sfstatistics/percentage/components/SecondFollowUp.vue | 1640 ++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 1,315 insertions(+), 325 deletions(-)

diff --git a/src/views/sfstatistics/percentage/components/SecondFollowUp.vue b/src/views/sfstatistics/percentage/components/SecondFollowUp.vue
index 4b03076..c3a6bae 100644
--- a/src/views/sfstatistics/percentage/components/SecondFollowUp.vue
+++ b/src/views/sfstatistics/percentage/components/SecondFollowUp.vue
@@ -25,21 +25,329 @@
               show-summary
               :summary-method="getInnerSummaries"
             >
+              <el-table-column label="" align="center" width="96" fixed="right">
+                <template slot="header">
+                  <div
+                    style="
+                      display: flex;
+                      justify-content: space-between;
+                      align-items: center;
+                    "
+                  >
+                    <el-button
+                      type="primary"
+                      size="mini"
+                      icon="el-icon-download"
+                      @click="exportDoctorTable(props.row)"
+                    >
+                      瀵煎嚭
+                    </el-button>
+                  </div>
+                </template>
+              </el-table-column>
               <el-table-column label="鍖荤敓濮撳悕" prop="drname" align="center" />
-              <el-table-column label="绉戝" width="120" prop="deptname" align="center" />
-              <el-table-column label="鍑洪櫌浜烘" prop="dischargeCount" align="center" />
-              <el-table-column label="鏃犻渶闅忚浜烘" align="center" width="100" key="nonFollowUp" prop="nonFollowUp" />
-              <el-table-column label="搴旈殢璁夸汉娆�" align="center" width="100" key="followUpNeeded" prop="followUpNeeded" />
+              <el-table-column
+                label="绉戝"
+                width="120"
+                prop="deptname"
+                align="center"
+              />
+              <el-table-column
+                label="鍑洪櫌浜烘"
+                prop="dischargeCount"
+                align="center"
+              >
+                <template slot-scope="scope">
+                  <el-button
+                    size="medium"
+                    type="text"
+                    @click="
+                      handleViewDetails(
+                        scope.row,
+                        'dischargeCountInfo',
+                        '鍑洪櫌鎮h�呭垪琛�',
+                        '1'
+                      )
+                    "
+                  >
+                    <span class="button-zx">{{
+                      scope.row.dischargeCount
+                    }}</span>
+                  </el-button>
+                </template>
+              </el-table-column>
+              <el-table-column
+                label="鏃犻渶闅忚浜烘"
+                align="center"
+                width="100"
+                key="nonFollowUp"
+                prop="nonFollowUp"
+              >
+                <template slot-scope="scope">
+                  <el-button
+                    size="medium"
+                    type="text"
+                    @click="
+                      handleViewDetails(
+                        scope.row,
+                        'nonFollowUpInfo',
+                        '鏃犻渶闅忚鍒楄〃',
+                        '1'
+                      )
+                    "
+                  >
+                    <span class="button-zx">{{ scope.row.nonFollowUp }}</span>
+                  </el-button>
+                </template>
+              </el-table-column>
+              <el-table-column
+                label="搴旈殢璁夸汉娆�"
+                align="center"
+                width="100"
+                key="followUpNeeded"
+                prop="followUpNeeded"
+              >
+                <template slot-scope="scope">
+                  <el-button
+                    size="medium"
+                    type="text"
+                    @click="
+                      handleViewDetails(
+                        scope.row,
+                        'followUpNeededInfo',
+                        '搴旈殢璁垮垪琛�',
+                        '1'
+                      )
+                    "
+                  >
+                    <span class="button-zx">{{
+                      scope.row.followUpNeeded
+                    }}</span>
+                  </el-button>
+                </template>
+              </el-table-column>
 
               <el-table-column align="center" label="鍐嶆鍑洪櫌闅忚">
-                <el-table-column label="闇�闅忚" align="center" key="needFollowUpAgain" prop="needFollowUpAgain" />
-                <el-table-column label="寰呴殢璁�" align="center" key="pendingFollowUpAgain" prop="pendingFollowUpAgain" />
-                <el-table-column label="闅忚鎴愬姛" align="center" key="followUpSuccessAgain" prop="followUpSuccessAgain" />
-                <el-table-column label="闅忚澶辫触" align="center" key="followUpFailAgain" prop="followUpFailAgain" />
-                <el-table-column label="闅忚鐜�" align="center" width="120" key="followUpRateAgain" prop="followUpRateAgain" />
-                <el-table-column  v-if="orgname != '涓芥按甯備腑鍖婚櫌'" label="浜哄伐" align="center" key="manualAgain" prop="manualAgain" />
-                <el-table-column  v-if="orgname != '涓芥按甯備腑鍖婚櫌'" label="鐭俊" align="center" key="smsAgain" prop="smsAgain" />
-                <el-table-column  v-if="orgname != '涓芥按甯備腑鍖婚櫌'" label="寰俊" align="center" key="weChatAgain" prop="weChatAgain" />
+                <el-table-column
+                  label="闇�闅忚"
+                  align="center"
+                  key="needFollowUpAgain"
+                  prop="needFollowUpAgain"
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'needFollowUpAgainInfo',
+                          '闇�闅忚鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{
+                        scope.row.needFollowUpAgain
+                      }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  label="寰呴殢璁�"
+                  align="center"
+                  key="pendingFollowUpAgain"
+                  prop="pendingFollowUpAgain"
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'pendingFollowUpAgainInfo',
+                          '寰呴殢璁垮垪琛�',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{
+                        scope.row.pendingFollowUpAgain
+                      }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  label="闅忚鎴愬姛"
+                  align="center"
+                  key="followUpSuccessAgain"
+                  prop="followUpSuccessAgain"
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'followUpSuccessAgainInfo',
+                          '闅忚鎴愬姛鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{
+                        scope.row.followUpSuccessAgain
+                      }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  label="闅忚澶辫触"
+                  align="center"
+                  key="followUpFailAgain"
+                  prop="followUpFailAgain"
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'followUpFailAgainInfo',
+                          '闅忚澶辫触鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{
+                        scope.row.followUpFailAgain
+                      }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  label="鎴愬姛鐜�"
+                  align="center"
+                  width="120"
+                  key="successRateAgain"
+                  prop="successRateAgain"
+                >
+                  <template slot-scope="scope">
+                    <span class="success-rate">{{
+                      calculateSuccessRate(
+                        scope.row.followUpSuccessAgain,
+                        scope.row.needFollowUpAgain,
+                        scope.row.pendingFollowUpAgain
+                      )
+                    }}</span>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  label="闅忚鐜�"
+                  align="center"
+                  width="120"
+                  key="followUpRateAgain"
+                  prop="followUpRateAgain"
+                />
+                <el-table-column
+                  v-if="orgname != '涓芥按甯備腑鍖婚櫌'"
+                  label="浜哄伐"
+                  align="center"
+                  key="manualAgain"
+                  prop="manualAgain"
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'manualAgainInfo',
+                          '浜哄伐闅忚鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{ scope.row.manualAgain }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  v-if="orgname != '涓芥按甯備腑鍖婚櫌'"
+                  label="璇煶"
+                  align="center"
+                  key="voiceAgain"
+                  prop="voiceAgain"
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'voiceAgainInfo',
+                          '璇煶闅忚鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{ scope.row.voiceAgain }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  v-if="orgname != '涓芥按甯備腑鍖婚櫌'"
+                  label="鐭俊"
+                  align="center"
+                  key="smsAgain"
+                  prop="smsAgain"
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'smsAgainInfo',
+                          '鐭俊闅忚鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{ scope.row.smsAgain }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  v-if="orgname != '涓芥按甯備腑鍖婚櫌'"
+                  label="寰俊"
+                  align="center"
+                  key="weChatAgain"
+                  prop="weChatAgain"
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'weChatAgainInfo',
+                          '寰俊闅忚鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{ scope.row.weChatAgain }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
               </el-table-column>
             </el-table>
           </template>
@@ -47,6 +355,7 @@
 
         <!-- 琛ㄦ牸鍒楀畾涔� -->
         <el-table-column
+          v-if="queryParams.statisticaltype == 1"
           label="鍑洪櫌鐥呭尯"
           align="center"
           sortable
@@ -56,58 +365,276 @@
           :show-overflow-tooltip="true"
           :sort-method="sortChineseNumber"
         />
-        <el-table-column label="绉戝" align="center" key="deptname" prop="deptname" :show-overflow-tooltip="true" />
-        <el-table-column label="鍑洪櫌浜烘" align="center" key="dischargeCount" prop="dischargeCount" />
-        <el-table-column label="鏃犻渶闅忚浜烘" align="center" width="100" key="nonFollowUp" prop="nonFollowUp" />
-        <el-table-column label="搴旈殢璁夸汉娆�" align="center" width="100" key="followUpNeeded" prop="followUpNeeded" />
+        <el-table-column
+          v-if="queryParams.statisticaltype == 2"
+          label="绉戝"
+          align="center"
+          key="deptname"
+          prop="deptname"
+          :show-overflow-tooltip="true"
+        />
+        <el-table-column
+          label="鍑洪櫌浜烘"
+          align="center"
+          key="dischargeCount"
+          prop="dischargeCount"
+        >
+          <template slot-scope="scope">
+            <el-button
+              size="medium"
+              type="text"
+              @click="
+                handleViewDetails(
+                  scope.row,
+                  'dischargeCountInfo',
+                  '鍑洪櫌鎮h�呭垪琛�'
+                )
+              "
+            >
+              <span class="button-zx">{{ scope.row.dischargeCount }}</span>
+            </el-button>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="鏃犻渶闅忚浜烘"
+          align="center"
+          width="100"
+          key="nonFollowUp"
+          prop="nonFollowUp"
+        >
+          <template slot-scope="scope">
+            <el-button
+              size="medium"
+              type="text"
+              @click="
+                handleViewDetails(scope.row, 'nonFollowUpInfo', '鏃犻渶闅忚鍒楄〃')
+              "
+            >
+              <span class="button-zx">{{ scope.row.nonFollowUp }}</span>
+            </el-button>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="搴旈殢璁夸汉娆�"
+          align="center"
+          width="100"
+          key="followUpNeeded"
+          prop="followUpNeeded"
+        >
+          <template slot-scope="scope">
+            <el-button
+              size="medium"
+              type="text"
+              @click="
+                handleViewDetails(scope.row, 'followUpNeededInfo', '搴旈殢璁垮垪琛�')
+              "
+            >
+              <span class="button-zx">{{ scope.row.followUpNeeded }}</span>
+            </el-button>
+          </template>
+        </el-table-column>
 
         <el-table-column align="center" label="鍐嶆鍑洪櫌闅忚">
-          <el-table-column label="闇�闅忚" align="center" key="needFollowUpAgain" prop="needFollowUpAgain">
+          <el-table-column
+            label="闇�闅忚"
+            align="center"
+            key="needFollowUpAgain"
+            prop="needFollowUpAgain"
+          >
             <template slot-scope="scope">
-              <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'needFollowUpAgainInfo', '鍐嶆闅忚闇�闅忚鍒楄〃')">
+              <el-button
+                size="medium"
+                type="text"
+                @click="
+                  handleViewDetails(
+                    scope.row,
+                    'needFollowUpAgainInfo',
+                    '鍐嶆闅忚闇�闅忚鍒楄〃'
+                  )
+                "
+              >
                 <span class="button-zx">{{ scope.row.needFollowUpAgain }}</span>
               </el-button>
             </template>
           </el-table-column>
-          <el-table-column label="寰呴殢璁�" align="center" key="pendingFollowUpAgain" prop="pendingFollowUpAgain">
+          <el-table-column
+            label="寰呴殢璁�"
+            align="center"
+            key="pendingFollowUpAgain"
+            prop="pendingFollowUpAgain"
+          >
             <template slot-scope="scope">
-              <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'pendingFollowUpAgainInfo', '鍐嶆闅忚寰呴殢璁垮垪琛�')">
-                <span class="button-zx">{{ scope.row.pendingFollowUpAgain }}</span>
+              <el-button
+                size="medium"
+                type="text"
+                @click="
+                  handleViewDetails(
+                    scope.row,
+                    'pendingFollowUpAgainInfo',
+                    '鍐嶆闅忚寰呴殢璁垮垪琛�'
+                  )
+                "
+              >
+                <span class="button-zx">{{
+                  scope.row.pendingFollowUpAgain
+                }}</span>
               </el-button>
             </template>
           </el-table-column>
-          <el-table-column label="闅忚鎴愬姛" align="center" key="followUpSuccessAgain" prop="followUpSuccessAgain">
+          <el-table-column
+            label="闅忚鎴愬姛"
+            align="center"
+            key="followUpSuccessAgain"
+            prop="followUpSuccessAgain"
+          >
             <template slot-scope="scope">
-              <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'followUpSuccessAgainInfo', '鍐嶆闅忚闅忚鎴愬姛鍒楄〃')">
-                <span class="button-zx">{{ scope.row.followUpSuccessAgain }}</span>
+              <el-button
+                size="medium"
+                type="text"
+                @click="
+                  handleViewDetails(
+                    scope.row,
+                    'followUpSuccessAgainInfo',
+                    '鍐嶆闅忚闅忚鎴愬姛鍒楄〃'
+                  )
+                "
+              >
+                <span class="button-zx">{{
+                  scope.row.followUpSuccessAgain
+                }}</span>
               </el-button>
             </template>
           </el-table-column>
-          <el-table-column label="闅忚澶辫触" align="center" key="followUpFailAgain" prop="followUpFailAgain">
+          <el-table-column
+            label="闅忚澶辫触"
+            align="center"
+            key="followUpFailAgain"
+            prop="followUpFailAgain"
+          >
             <template slot-scope="scope">
-              <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'followUpFailAgainInfo', '鍐嶆闅忚闅忚澶辫触鍒楄〃')">
+              <el-button
+                size="medium"
+                type="text"
+                @click="
+                  handleViewDetails(
+                    scope.row,
+                    'followUpFailAgainInfo',
+                    '鍐嶆闅忚闅忚澶辫触鍒楄〃'
+                  )
+                "
+              >
                 <span class="button-zx">{{ scope.row.followUpFailAgain }}</span>
               </el-button>
             </template>
           </el-table-column>
-          <el-table-column label="闅忚鐜�" align="center" width="120" key="followUpRateAgain" prop="followUpRateAgain" />
-          <el-table-column v-if="orgname != '涓芥按甯備腑鍖婚櫌'" label="浜哄伐" align="center" key="manualAgain" prop="manualAgain">
+          <el-table-column
+            label="鎴愬姛鐜�"
+            align="center"
+            width="120"
+            key="successRateAgain"
+            prop="successRateAgain"
+          >
             <template slot-scope="scope">
-              <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'manualAgainInfo', '鍐嶆闅忚浜哄伐闅忚鍒楄〃')">
+              <span class="success-rate">{{
+                calculateSuccessRate(
+                  scope.row.followUpSuccessAgain,
+                  scope.row.needFollowUpAgain,
+                  scope.row.pendingFollowUpAgain
+                )
+              }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            label="闅忚鐜�"
+            align="center"
+            width="120"
+            key="followUpRateAgain"
+            prop="followUpRateAgain"
+          />
+          <el-table-column
+            v-if="orgname != '涓芥按甯備腑鍖婚櫌'"
+            label="浜哄伐"
+            align="center"
+            key="manualAgain"
+            prop="manualAgain"
+          >
+            <template slot-scope="scope">
+              <el-button
+                size="medium"
+                type="text"
+                @click="
+                  handleViewDetails(
+                    scope.row,
+                    'manualAgainInfo',
+                    '鍐嶆闅忚浜哄伐闅忚鍒楄〃'
+                  )
+                "
+              >
                 <span class="button-zx">{{ scope.row.manualAgain }}</span>
               </el-button>
             </template>
           </el-table-column>
-          <el-table-column v-if="orgname != '涓芥按甯備腑鍖婚櫌'" label="鐭俊" align="center" key="smsAgain" prop="smsAgain">
+          <el-table-column
+            v-if="orgname != '涓芥按甯備腑鍖婚櫌'"
+            label="璇煶"
+            align="center"
+            key="voiceAgain"
+            prop="voiceAgain"
+          >
             <template slot-scope="scope">
-              <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'smsAgainInfo', '鍐嶆闅忚鐭俊闅忚鍒楄〃')">
+              <el-button
+                size="medium"
+                type="text"
+                @click="
+                  handleViewDetails(scope.row, 'voiceAgainInfo', '璇煶闅忚鍒楄〃')
+                "
+              >
+                <span class="button-zx">{{ scope.row.voiceAgain }}</span>
+              </el-button>
+            </template>
+          </el-table-column>
+          <el-table-column
+            v-if="orgname != '涓芥按甯備腑鍖婚櫌'"
+            label="鐭俊"
+            align="center"
+            key="smsAgain"
+            prop="smsAgain"
+          >
+            <template slot-scope="scope">
+              <el-button
+                size="medium"
+                type="text"
+                @click="
+                  handleViewDetails(
+                    scope.row,
+                    'smsAgainInfo',
+                    '鍐嶆闅忚鐭俊闅忚鍒楄〃'
+                  )
+                "
+              >
                 <span class="button-zx">{{ scope.row.smsAgain }}</span>
               </el-button>
             </template>
           </el-table-column>
-          <el-table-column v-if="orgname != '涓芥按甯備腑鍖婚櫌'" label="寰俊" align="center" key="weChatAgain" prop="weChatAgain">
+          <el-table-column
+            v-if="orgname != '涓芥按甯備腑鍖婚櫌'"
+            label="寰俊"
+            align="center"
+            key="weChatAgain"
+            prop="weChatAgain"
+          >
             <template slot-scope="scope">
-              <el-button size="medium" type="text" @click="handleViewDetails(scope.row, 'weChatAgainInfo', '鍐嶆闅忚寰俊闅忚鍒楄〃')">
+              <el-button
+                size="medium"
+                type="text"
+                @click="
+                  handleViewDetails(
+                    scope.row,
+                    'weChatAgainInfo',
+                    '鍐嶆闅忚寰俊闅忚鍒楄〃'
+                  )
+                "
+              >
                 <span class="button-zx">{{ scope.row.weChatAgain }}</span>
               </el-button>
             </template>
@@ -124,163 +651,251 @@
 import { saveAs } from "file-saver";
 
 export default {
-  name: 'SecondFollowUp',
+  name: "SecondFollowUp",
   props: {
     queryParams: {
       type: Object,
-      required: true
+      required: true,
     },
     flatArrayhospit: {
       type: Array,
-      default: () => []
+      default: () => [],
     },
     flatArraydept: {
       type: Array,
-      default: () => []
+      default: () => [],
     },
     options: {
       type: Array,
-      default: () => []
+      default: () => [],
     },
     orgname: {
       type: String,
-      default: ''
-    }
+      default: "",
+    },
   },
   data() {
     return {
       tableData: [],
       loading: false,
       expands: [],
-      ids: []
-    }
+      ids: [],
+    };
   },
   methods: {
     loadData() {
-      this.loading = true
+      this.loading = true;
       const params = {
         ...this.queryParams,
         visitCount: 2,
-        leavehospitaldistrictcodes: this.queryParams.leavehospitaldistrictcodes.includes("all")
-          ? this.getAllWardCodes()
-          : this.queryParams.leavehospitaldistrictcodes,
+        leavehospitaldistrictcodes:
+          this.queryParams.leavehospitaldistrictcodes.includes("all")
+            ? this.getAllWardCodes()
+            : this.queryParams.leavehospitaldistrictcodes,
         deptcodes: this.queryParams.deptcodes.includes("all")
           ? this.getAllDeptCodes()
-          : this.queryParams.deptcodes
-      }
+          : this.queryParams.deptcodes,
+      };
 
-      delete params.leavehospitaldistrictcodes.all
-      delete params.deptcodes.all
+      delete params.leavehospitaldistrictcodes.all;
+      delete params.deptcodes.all;
+      params.rateDay = 7;
 
       getSfStatistics(params)
-        .then(response => {
-          this.tableData = this.customSort(response.data)
+        .then((response) => {
+          this.tableData = this.customSort(response.data);
         })
-        .catch(error => {
-          console.error("鑾峰彇鍐嶆闅忚鏁版嵁澶辫触:", error)
-          this.$message.error("鑾峰彇鍐嶆闅忚鏁版嵁澶辫触")
+        .catch((error) => {
+          console.error("鑾峰彇鍐嶆闅忚鏁版嵁澶辫触:", error);
+          this.$message.error("鑾峰彇鍐嶆闅忚鏁版嵁澶辫触");
         })
         .finally(() => {
-          this.loading = false
-        })
+          this.loading = false;
+        });
     },
 
     getAllWardCodes() {
       return this.flatArrayhospit
-        .filter(item => item.value !== 'all')
-        .map(item => item.value)
+        .filter((item) => item.value !== "all")
+        .map((item) => item.value);
     },
 
     getAllDeptCodes() {
       return this.flatArraydept
-        .filter(item => item.value !== 'all')
-        .map(item => item.value)
+        .filter((item) => item.value !== "all")
+        .map((item) => item.value);
     },
 
     customSort(data) {
       const order = [
-        "涓�","浜�","涓�","鍥�","浜�","鍏�","涓�","鍏�","涔�","鍗�",
-        "鍗佷竴","鍗佷簩","鍗佷笁","鍗佸洓","鍗佷簲","鍗佸叚","鍗佷竷","鍗佸叓","鍗佷節","浜屽崄",
-        "浜屽崄涓�","浜屽崄浜�","浜屽崄涓�","浜屽崄鍥�","浜屽崄浜�","浜屽崄鍏�","浜屽崄涓�","浜屽崄鍏�","浜屽崄涔�","涓夊崄",
-        "涓夊崄涓�","涓夊崄浜�","涓夊崄涓�","涓夊崄鍥�","涓夊崄浜�","涓夊崄鍏�","涓夊崄涓�","涓夊崄鍏�","涓夊崄涔�","鍥涘崄",
-        "鍥涘崄涓�","鍥涘崄浜�","鍥涘崄涓�","鍥涘崄鍥�","鍥涘崄浜�"
-      ]
+        "涓�",
+        "浜�",
+        "涓�",
+        "鍥�",
+        "浜�",
+        "鍏�",
+        "涓�",
+        "鍏�",
+        "涔�",
+        "鍗�",
+        "鍗佷竴",
+        "鍗佷簩",
+        "鍗佷笁",
+        "鍗佸洓",
+        "鍗佷簲",
+        "鍗佸叚",
+        "鍗佷竷",
+        "鍗佸叓",
+        "鍗佷節",
+        "浜屽崄",
+        "浜屽崄涓�",
+        "浜屽崄浜�",
+        "浜屽崄涓�",
+        "浜屽崄鍥�",
+        "浜屽崄浜�",
+        "浜屽崄鍏�",
+        "浜屽崄涓�",
+        "浜屽崄鍏�",
+        "浜屽崄涔�",
+        "涓夊崄",
+        "涓夊崄涓�",
+        "涓夊崄浜�",
+        "涓夊崄涓�",
+        "涓夊崄鍥�",
+        "涓夊崄浜�",
+        "涓夊崄鍏�",
+        "涓夊崄涓�",
+        "涓夊崄鍏�",
+        "涓夊崄涔�",
+        "鍥涘崄",
+        "鍥涘崄涓�",
+        "鍥涘崄浜�",
+        "鍥涘崄涓�",
+        "鍥涘崄鍥�",
+        "鍥涘崄浜�",
+      ];
 
       return data.sort((a, b) => {
         const getIndex = (name) => {
-          if (!name || typeof name !== "string") return -1
-          const chineseMatch = name.match(/^([涓�浜屼笁鍥涗簲鍏竷鍏節鍗乚+)/)
+          if (!name || typeof name !== "string") return -1;
+          const chineseMatch = name.match(/^([涓�浜屼笁鍥涗簲鍏竷鍏節鍗乚+)/);
           if (chineseMatch && chineseMatch[1]) {
-            return order.indexOf(chineseMatch[1])
+            return order.indexOf(chineseMatch[1]);
           }
-          const arabicMatch = name.match(/^(\d+)/)
+          const arabicMatch = name.match(/^(\d+)/);
           if (arabicMatch && arabicMatch[1]) {
-            const num = parseInt(arabicMatch[1], 10)
+            const num = parseInt(arabicMatch[1], 10);
             if (num >= 1 && num <= 45) {
-              return num - 1
+              return num - 1;
             }
           }
-          return -1
-        }
+          return -1;
+        };
 
-        const indexA = getIndex(a.leavehospitaldistrictname)
-        const indexB = getIndex(b.leavehospitaldistrictname)
+        const indexA = getIndex(a.leavehospitaldistrictname);
+        const indexB = getIndex(b.leavehospitaldistrictname);
 
         if (indexA === -1 && indexB === -1) {
-          return (a.leavehospitaldistrictname || "").localeCompare(b.leavehospitaldistrictname || "")
+          return (a.leavehospitaldistrictname || "").localeCompare(
+            b.leavehospitaldistrictname || ""
+          );
         }
-        if (indexA === -1) return 1
-        if (indexB === -1) return -1
-        return indexA - indexB
-      })
+        if (indexA === -1) return 1;
+        if (indexB === -1) return -1;
+        return indexA - indexB;
+      });
     },
 
     sortChineseNumber(aRow, bRow) {
-      const a = aRow.leavehospitaldistrictname
-      const b = bRow.leavehospitaldistrictname
+      const a = aRow.leavehospitaldistrictname;
+      const b = bRow.leavehospitaldistrictname;
 
       const chineseNumMap = {
-        涓�:1,浜�:2,涓�:3,鍥�:4,浜�:5,鍏�:6,涓�:7,鍏�:8,涔�:9,鍗�:10,
-        鍗佷竴:11,鍗佷簩:12,鍗佷笁:13,鍗佸洓:14,鍗佷簲:15,鍗佸叚:16,鍗佷竷:17,鍗佸叓:18,鍗佷節:19,浜屽崄:20,
-        浜屽崄涓�:21,浜屽崄浜�:22,浜屽崄涓�:23,浜屽崄鍥�:24,浜屽崄浜�:25,浜屽崄鍏�:26,浜屽崄涓�:27,浜屽崄鍏�:28,浜屽崄涔�:29,涓夊崄:30,
-        涓夊崄涓�:31,涓夊崄浜�:32,涓夊崄涓�:33,涓夊崄鍥�:34,涓夊崄浜�:35,涓夊崄鍏�:36,涓夊崄涓�:37,涓夊崄鍏�:38,涓夊崄涔�:39,鍥涘崄:40,
-        鍥涘崄涓�:41,鍥涘崄浜�:42,鍥涘崄涓�:43,鍥涘崄鍥�:44,鍥涘崄浜�:45
-      }
+        涓�: 1,
+        浜�: 2,
+        涓�: 3,
+        鍥�: 4,
+        浜�: 5,
+        鍏�: 6,
+        涓�: 7,
+        鍏�: 8,
+        涔�: 9,
+        鍗�: 10,
+        鍗佷竴: 11,
+        鍗佷簩: 12,
+        鍗佷笁: 13,
+        鍗佸洓: 14,
+        鍗佷簲: 15,
+        鍗佸叚: 16,
+        鍗佷竷: 17,
+        鍗佸叓: 18,
+        鍗佷節: 19,
+        浜屽崄: 20,
+        浜屽崄涓�: 21,
+        浜屽崄浜�: 22,
+        浜屽崄涓�: 23,
+        浜屽崄鍥�: 24,
+        浜屽崄浜�: 25,
+        浜屽崄鍏�: 26,
+        浜屽崄涓�: 27,
+        浜屽崄鍏�: 28,
+        浜屽崄涔�: 29,
+        涓夊崄: 30,
+        涓夊崄涓�: 31,
+        涓夊崄浜�: 32,
+        涓夊崄涓�: 33,
+        涓夊崄鍥�: 34,
+        涓夊崄浜�: 35,
+        涓夊崄鍏�: 36,
+        涓夊崄涓�: 37,
+        涓夊崄鍏�: 38,
+        涓夊崄涔�: 39,
+        鍥涘崄: 40,
+        鍥涘崄涓�: 41,
+        鍥涘崄浜�: 42,
+        鍥涘崄涓�: 43,
+        鍥涘崄鍥�: 44,
+        鍥涘崄浜�: 45,
+      };
 
       const getNumberFromText = (text) => {
-        if (!text || typeof text !== "string") return -1
-        const match = text.match(/^([涓�浜屼笁鍥涗簲鍏竷鍏節鍗乚+)/)
+        if (!text || typeof text !== "string") return -1;
+        const match = text.match(/^([涓�浜屼笁鍥涗簲鍏竷鍏節鍗乚+)/);
         if (match && match[1]) {
-          const chineseNum = match[1]
-          return chineseNumMap[chineseNum] !== undefined ? chineseNumMap[chineseNum] : -1
+          const chineseNum = match[1];
+          return chineseNumMap[chineseNum] !== undefined
+            ? chineseNumMap[chineseNum]
+            : -1;
         }
-        const arabicMatch = text.match(/^(\d+)/)
+        const arabicMatch = text.match(/^(\d+)/);
         if (arabicMatch && arabicMatch[1]) {
-          const num = parseInt(arabicMatch[1], 10)
-          return num >= 1 && num <= 45 ? num : -1
+          const num = parseInt(arabicMatch[1], 10);
+          return num >= 1 && num <= 45 ? num : -1;
         }
-        return -1
-      }
+        return -1;
+      };
 
-      const numA = getNumberFromText(a)
-      const numB = getNumberFromText(b)
+      const numA = getNumberFromText(a);
+      const numB = getNumberFromText(b);
 
       if (numA === -1 && numB === -1) {
-        return (a || "").localeCompare(b || "")
+        return (a || "").localeCompare(b || "");
       }
-      if (numA === -1) return 1
-      if (numB === -1) return -1
-      return numA - numB
+      if (numA === -1) return 1;
+      if (numB === -1) return -1;
+      return numA - numB;
     },
 
     getRowKey(row) {
-      return row.statisticaltype === 1 ? row.leavehospitaldistrictcode : row.deptcode
+      return row.statisticaltype === 1
+        ? row.leavehospitaldistrictcode
+        : row.deptcode;
     },
 
     handleRowClick(row) {
       if (this.expands.includes(this.getRowKey(row))) {
-        this.expands = []
-        return
+        this.expands = [];
+        return;
       }
 
       const params = {
@@ -290,222 +905,521 @@
           : this.queryParams.deptcodes,
         leavehospitaldistrictcodes: [row.leavehospitaldistrictcode],
         drcode: "1",
-        visitCount: 2
-      }
+        visitCount: 2,
+      };
 
-      delete params.leavehospitaldistrictcodes.all
-      delete params.deptcodes.all
+      delete params.leavehospitaldistrictcodes.all;
+      delete params.deptcodes.all;
 
       if (!row.doctorStats) {
-        this.loading = true
+        this.loading = true;
+        params.rateDay = 7;
+
         getSfStatistics(params).then((res) => {
-          this.$set(row, "doctorStats", res.data)
-          this.expands = [this.getRowKey(row)]
-          this.loading = false
-        })
+          this.$set(row, "doctorStats", res.data);
+          this.expands = [this.getRowKey(row)];
+          this.loading = false;
+        });
       } else {
-        this.expands = [this.getRowKey(row)]
+        this.expands = [this.getRowKey(row)];
       }
     },
 
+    // 鍦ㄧ粺璁℃眹鎬绘柟娉曚腑澶勭悊鎴愬姛鐜�
     getSummaries(param) {
-      const { columns, data } = param
-      const sums = []
+      const { columns, data } = param;
+      const sums = [];
 
       columns.forEach((column, index) => {
         if (index === 0) {
-          sums[index] = "鍚堣"
-          return
+          sums[index] = "鍚堣";
+          return;
         }
-        if (index === 1 || index === 2) {
-          sums[index] = "/"
-          return
+        if (index === 1) {
+          sums[index] = "/";
+          return;
         }
 
-        if (column.property === "followUpRateAgain") {
+        if (column.property === "successRateAgain") {
+          // 鎴愬姛鐜囬渶瑕侀噸鏂拌绠楁�荤殑鎴愬姛鐜囷紝鑰屼笉鏄钩鍧囧��
+          const totalSuccess = data.reduce((sum, item) => {
+            return sum + (Number(item.followUpSuccessAgain) || 0);
+          }, 0);
+
+          const totalNeed = data.reduce((sum, item) => {
+            return sum + (Number(item.needFollowUpAgain) || 0);
+          }, 0);
+
+          const totalPending = data.reduce((sum, item) => {
+            return sum + (Number(item.pendingFollowUpAgain) || 0);
+          }, 0);
+
+          const denominator = totalNeed - totalPending;
+
+          if (denominator > 0) {
+            sums[index] = ((totalSuccess / denominator) * 100).toFixed(2) + "%";
+          } else {
+            sums[index] = "0.00%";
+          }
+        } else if (column.property === "followUpRateAgain") {
           const percentageValues = data
             .map((item) => {
-              const value = item[column.property]
-              if (!value || value === "-" || value === "0%") return null
+              const value = item[column.property];
+              if (!value || value === "-" || value === "0%") return null;
               if (typeof value === "string" && value.includes("%")) {
-                const numValue = parseFloat(value.replace("%", "")) / 100
-                return isNaN(numValue) ? null : numValue
+                const numValue = parseFloat(value.replace("%", "")) / 100;
+                return isNaN(numValue) ? null : numValue;
               } else {
-                const numValue = parseFloat(value)
-                return isNaN(numValue) ? null : numValue
+                const numValue = parseFloat(value);
+                return isNaN(numValue) ? null : numValue;
               }
             })
-            .filter((value) => value !== null && value !== 0)
+            .filter((value) => value !== null && value !== 0);
 
           if (percentageValues.length > 0) {
-            const average = percentageValues.reduce((sum, value) => sum + value, 0) / percentageValues.length
-            sums[index] = (average * 100).toFixed(2) + "%"
+            const average =
+              percentageValues.reduce((sum, value) => sum + value, 0) /
+              percentageValues.length;
+            sums[index] = (average * 100).toFixed(2) + "%";
           } else {
-            sums[index] = "0.00%"
+            sums[index] = "0.00%";
           }
         } else {
           const values = data.map((item) => {
-            const value = item[column.property]
-            if (value === "-" || value === "" || value === null) return 0
-            return Number(value) || 0
-          })
+            const value = item[column.property];
+            if (value === "-" || value === "" || value === null) return 0;
+            return Number(value) || 0;
+          });
 
           if (!values.every((value) => isNaN(value))) {
-            sums[index] = values.reduce((prev, curr) => prev + curr, 0)
-            sums[index] = this.formatNumber(sums[index])
+            sums[index] = values.reduce((prev, curr) => prev + curr, 0);
+            sums[index] = this.formatNumber(sums[index]);
           } else {
-            sums[index] = "-"
+            sums[index] = "-";
           }
         }
-      })
+      });
 
-      return sums
+      return sums;
     },
 
     getInnerSummaries(param) {
-      const { columns, data } = param
-      const sums = []
+      const { columns, data } = param;
+      const sums = [];
 
       columns.forEach((column, index) => {
         if (index === 0) {
-          sums[index] = "灏忚"
-          return
+          sums[index] = "灏忚";
+          return;
         }
 
         if (column.property === "drname" || column.property === "deptname") {
-          sums[index] = "-"
-          return
+          sums[index] = "-";
+          return;
         }
 
-        if (column.property === "followUpRateAgain") {
+        if (column.property === "successRateAgain") {
+          // 鎴愬姛鐜囬渶瑕侀噸鏂拌绠楁�荤殑鎴愬姛鐜囷紝鑰屼笉鏄钩鍧囧��
+          const totalSuccess = data.reduce((sum, item) => {
+            return sum + (Number(item.followUpSuccessAgain) || 0);
+          }, 0);
+
+          const totalNeed = data.reduce((sum, item) => {
+            return sum + (Number(item.needFollowUpAgain) || 0);
+          }, 0);
+
+          const totalPending = data.reduce((sum, item) => {
+            return sum + (Number(item.pendingFollowUpAgain) || 0);
+          }, 0);
+
+          const denominator = totalNeed - totalPending;
+
+          if (denominator > 0) {
+            sums[index] = ((totalSuccess / denominator) * 100).toFixed(2) + "%";
+          } else {
+            sums[index] = "0.00%";
+          }
+        } else if (column.property === "followUpRateAgain") {
           const percentageValues = data
             .map((item) => {
-              const value = item[column.property]
-              if (!value || value === "-" || value === "0%") return null
+              const value = item[column.property];
+              if (!value || value === "-" || value === "0%") return null;
               if (typeof value === "string" && value.includes("%")) {
-                const numValue = parseFloat(value.replace("%", "")) / 100
-                return isNaN(numValue) ? null : numValue
+                const numValue = parseFloat(value.replace("%", "")) / 100;
+                return isNaN(numValue) ? null : numValue;
               } else {
-                const numValue = parseFloat(value)
-                return isNaN(numValue) ? null : numValue
+                const numValue = parseFloat(value);
+                return isNaN(numValue) ? null : numValue;
               }
             })
-            .filter((value) => value !== null && value !== 0)
+            .filter((value) => value !== null && value !== 0);
 
           if (percentageValues.length > 0) {
-            const average = percentageValues.reduce((sum, value) => sum + value, 0) / percentageValues.length
-            sums[index] = (average * 100).toFixed(2) + "%"
+            const average =
+              percentageValues.reduce((sum, value) => sum + value, 0) /
+              percentageValues.length;
+            sums[index] = (average * 100).toFixed(2) + "%";
           } else {
-            sums[index] = "0.00%"
+            sums[index] = "0.00%";
           }
         } else {
           const values = data.map((item) => {
-            const value = item[column.property]
-            if (value === "-" || value === "" || value === null) return 0
-            return Number(value) || 0
-          })
+            const value = item[column.property];
+            if (value === "-" || value === "" || value === null) return 0;
+            return Number(value) || 0;
+          });
 
           if (!values.every((value) => isNaN(value))) {
-            sums[index] = values.reduce((prev, curr) => prev + curr, 0)
-            sums[index] = this.formatNumber(sums[index])
+            sums[index] = values.reduce((prev, curr) => prev + curr, 0);
+            sums[index] = this.formatNumber(sums[index]);
           } else {
-            sums[index] = "-"
+            sums[index] = "-";
           }
         }
-      })
+      });
 
-      return sums
+      return sums;
     },
 
     formatNumber(num) {
-      if (isNaN(num)) return "-"
-      return Number.isInteger(num) ? num.toString() : num.toFixed(0)
+      if (isNaN(num)) return "-";
+      return Number.isInteger(num) ? num.toString() : num.toFixed(0);
     },
 
     handleSelectionChange(selection) {
-      this.ids = selection.map((item) => item.tagid)
+      this.ids = selection.map((item) => item.tagid);
     },
 
-    handleViewDetails(row, infoKey, titleSuffix) {
-      const title = `${row.leavehospitaldistrictname || row.deptname}${titleSuffix}`
-      this.$emit('view-details', row[infoKey], title)
+    handleViewDetails(row, infoKey, titleSuffix, type) {
+      const title = `${
+        row.leavehospitaldistrictname || row.deptname
+      }${titleSuffix}`;
+      this.$emit("view-details", row, infoKey, title, type);
     },
+    // 璁$畻鎴愬姛鐜囩殑鏂规硶
+    calculateSuccessRate(followUpSuccess, needFollowUp, pendingFollowUp) {
+      const success = Number(followUpSuccess) || 0;
+      const need = Number(needFollowUp) || 0;
+      const pending = Number(pendingFollowUp) || 0;
 
+      // 鍒嗘瘝 = 闇�闅忚 - 寰呴殢璁�
+      const denominator = need - pending;
+
+      if (denominator <= 0) {
+        return "0.00%";
+      }
+
+      const rate = (success / denominator) * 100;
+      return rate.toFixed(2) + "%";
+    },
     async exportTable() {
       try {
-        let dateRangeString = ""
-        let sheetNameSuffix = ""
+        let dateRangeString = "";
+        let sheetNameSuffix = "";
 
-        if (this.queryParams.dateRange && this.queryParams.dateRange.length === 2) {
-          const startDateStr = this.queryParams.dateRange[0]
-          const endDateStr = this.queryParams.dateRange[1]
+        if (
+          this.queryParams.dateRange &&
+          this.queryParams.dateRange.length === 2
+        ) {
+          const startDateStr = this.queryParams.dateRange[0];
+          const endDateStr = this.queryParams.dateRange[1];
           const formatDateForDisplay = (dateTimeStr) => {
-            return dateTimeStr.split(" ")[0]
-          }
-          const startDateFormatted = formatDateForDisplay(startDateStr)
-          const endDateFormatted = formatDateForDisplay(endDateStr)
-          dateRangeString = `${startDateFormatted}鑷�${endDateFormatted}`
-          sheetNameSuffix = `${startDateFormatted}鑷�${endDateFormatted}`
+            return dateTimeStr.split(" ")[0];
+          };
+          const startDateFormatted = formatDateForDisplay(startDateStr);
+          const endDateFormatted = formatDateForDisplay(endDateStr);
+          dateRangeString = `${startDateFormatted}鑷�${endDateFormatted}`;
+          sheetNameSuffix = `${startDateFormatted}鑷�${endDateFormatted}`;
         } else {
-          const now = new Date()
-          const currentMonth = now.getMonth() + 1
-          dateRangeString = `${currentMonth}鏈坄
-          sheetNameSuffix = `${currentMonth}鏈坄
+          const now = new Date();
+          const currentMonth = now.getMonth() + 1;
+          dateRangeString = `${currentMonth}鏈坄;
+          sheetNameSuffix = `${currentMonth}鏈坄;
         }
 
-        const excelName = `鍐嶆鍑洪櫌闅忚缁熻琛╛${dateRangeString}.xlsx`
-        const worksheetName = `鍐嶆闅忚缁熻_${sheetNameSuffix}`
+        const excelName = `鍐嶆鍑洪櫌闅忚缁熻琛╛${dateRangeString}.xlsx`;
+        const worksheetName = `鍐嶆闅忚缁熻_${sheetNameSuffix}`;
 
         if (!this.tableData || this.tableData.length === 0) {
-          this.$message.warning("鏆傛棤鍐嶆闅忚鏁版嵁鍙鍑�")
-          return false
+          this.$message.warning("鏆傛棤鍐嶆闅忚鏁版嵁鍙鍑�");
+          return false;
         }
 
-        const workbook = new ExcelJS.Workbook()
-        const worksheet = workbook.addWorksheet(worksheetName)
+        const workbook = new ExcelJS.Workbook();
+        const worksheet = workbook.addWorksheet(worksheetName);
 
         // 鏋勫缓琛ㄦ牸
-        this.buildExportSheet(worksheet, sheetNameSuffix)
+        this.buildExportSheet(worksheet, sheetNameSuffix);
 
-        const buffer = await workbook.xlsx.writeBuffer()
+        const buffer = await workbook.xlsx.writeBuffer();
         const blob = new Blob([buffer], {
-          type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
-        })
-        saveAs(blob, excelName)
+          type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+        });
+        saveAs(blob, excelName);
 
-        this.$message.success("瀵煎嚭鎴愬姛")
-        return true
+        this.$message.success("瀵煎嚭鎴愬姛");
+        return true;
       } catch (error) {
-        console.error("瀵煎嚭澶辫触:", error)
-        this.$message.error(`瀵煎嚭澶辫触: ${error.message}`)
-        return false
+        console.error("瀵煎嚭澶辫触:", error);
+        this.$message.error(`瀵煎嚭澶辫触: ${error.message}`);
+        return false;
       }
     },
+    /** 瀵煎嚭鍖荤敓瀛愯〃锛堝啀娆¢殢璁匡級 */
+    async exportDoctorTable(row) {
+      try {
+        const areaName =
+          row.leavehospitaldistrictname || row.deptname || "鏈煡鐥呭尯";
 
-    buildExportSheet(worksheet, sheetNameSuffix) {
-      const titleStyle = {
-        font: { name: "寰蒋闆呴粦", size: 16, bold: true, color: { argb: "FF000000" } },
-        fill: { type: "pattern", pattern: "solid", fgColor: { argb: "FFE6F3FF" } },
-        alignment: { vertical: "middle", horizontal: "center", wrapText: true },
-        border: {
-          top: { style: "thin", color: { argb: "FFD0D0D0" } },
-          left: { style: "thin", color: { argb: "FFD0D0D0" } },
-          bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
-          right: { style: "thin", color: { argb: "FFD0D0D0" } }
+        let dateRangeString = "";
+        if (
+          this.queryParams.dateRange &&
+          this.queryParams.dateRange.length === 2
+        ) {
+          const start = this.queryParams.dateRange[0].split(" ")[0];
+          const end = this.queryParams.dateRange[1].split(" ")[0];
+          dateRangeString = `${start}鑷�${end}`;
+        } else {
+          dateRangeString = `${new Date().getMonth() + 1}鏈坄;
         }
+
+        const fileName = `${areaName}鍖荤敓鍐嶆闅忚鍒楄〃_${dateRangeString}.xlsx`;
+        const sheetName = `${areaName}鍖荤敓鍐嶆闅忚`;
+
+        if (!row.doctorStats || row.doctorStats.length === 0) {
+          this.$message.warning("褰撳墠鐥呭尯鏆傛棤鍖荤敓鍐嶆闅忚鏁版嵁");
+          return;
+        }
+
+        const workbook = new ExcelJS.Workbook();
+        const worksheet = workbook.addWorksheet(sheetName);
+
+        this.buildDoctorExportSheet(worksheet, row.doctorStats, areaName);
+
+        const buffer = await workbook.xlsx.writeBuffer();
+        saveAs(
+          new Blob([buffer], {
+            type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+          }),
+          fileName
+        );
+
+        this.$message.success("鍖荤敓鍐嶆闅忚鍒楄〃瀵煎嚭鎴愬姛");
+      } catch (err) {
+        console.error(err);
+        this.$message.error("瀵煎嚭澶辫触");
       }
+    },
+    buildDoctorExportSheet(worksheet, data, areaName) {
+      const titleStyle = {
+        font: { name: "寰蒋闆呴粦", size: 16, bold: true },
+        alignment: { horizontal: "center", vertical: "middle" },
+      };
 
       const headerStyle = {
-        font: { name: "寰蒋闆呴粦", size: 11, bold: true, color: { argb: "FF000000" } },
-        fill: { type: "pattern", pattern: "solid", fgColor: { argb: "FFF5F7FA" } },
+        font: { name: "寰蒋闆呴粦", size: 11, bold: true },
+        fill: {
+          type: "pattern",
+          pattern: "solid",
+          fgColor: { argb: "FFF5F7FA" },
+        },
+        alignment: { horizontal: "center", vertical: "middle", wrapText: true },
+        border: {
+          top: { style: "thin" },
+          left: { style: "thin" },
+          bottom: { style: "thin" },
+          right: { style: "thin" },
+        },
+      };
+
+      const cellStyle = {
+        font: { name: "瀹嬩綋", size: 10 },
+        alignment: { horizontal: "center", vertical: "middle" },
+        border: {
+          top: { style: "thin" },
+          left: { style: "thin" },
+          bottom: { style: "thin" },
+          right: { style: "thin" },
+        },
+      };
+
+      // 鏍囬
+      worksheet.mergeCells(1, 1, 1, 10);
+      worksheet.getCell(1, 1).value = `${areaName}鍖荤敓鍐嶆闅忚鍒楄〃`;
+      worksheet.getCell(1, 1).style = titleStyle;
+      worksheet.getRow(1).height = 30;
+
+      // 琛ㄥご
+      const headers = [
+        "鍖荤敓濮撳悕",
+        "绉戝",
+        "鍑洪櫌浜烘",
+        "鏃犻渶闅忚",
+        "搴旈殢璁�",
+        "闇�闅忚",
+        "寰呴殢璁�",
+        "闅忚鎴愬姛",
+        "闅忚澶辫触",
+        "鎴愬姛鐜�", // 鏂板
+        "闅忚鐜�", // 鍘熸潵鍦ㄦ垚鍔熺巼浣嶇疆
+      ];
+
+      const headerRow = worksheet.addRow(headers);
+      headerRow.eachCell((cell) => {
+        cell.style = headerStyle;
+      });
+      worksheet.getRow(2).height = 25;
+
+      // 鏁版嵁
+      data.forEach((item) => {
+        const row = worksheet.addRow([
+          item.drname,
+          item.deptname,
+          item.dischargeCount,
+          item.nonFollowUp,
+          item.followUpNeeded,
+          item.needFollowUpAgain,
+          item.pendingFollowUpAgain,
+          item.followUpSuccessAgain,
+          item.followUpFailAgain,
+          this.calculateSuccessRate(
+            item.followUpSuccessAgain,
+            item.needFollowUpAgain,
+            item.pendingFollowUpAgain
+          ),
+          item.followUpRateAgain,
+        ]);
+        row.eachCell((cell) => {
+          cell.style = cellStyle;
+        });
+      });
+
+      // 灏忚琛�
+      const summaryRow = worksheet.addRow(
+        this.getDoctorAgainExportSummary(data)
+      );
+      summaryRow.eachCell((cell) => {
+        cell.font = { bold: true };
+        cell.fill = {
+          type: "pattern",
+          pattern: "solid",
+          fgColor: { argb: "FFF5F7FA" },
+        };
+      });
+
+      // 鍒楀
+      worksheet.columns = [
+        { width: 15 },
+        { width: 15 },
+        { width: 12 },
+        { width: 12 },
+        { width: 12 },
+        { width: 12 },
+        { width: 12 },
+        { width: 12 },
+        { width: 12 },
+        { width: 12 }, // 鎴愬姛鐜�
+        { width: 12 }, // 闅忚鐜�
+      ];
+    },
+    /** 鍐嶆闅忚 - 鍖荤敓瀛愯〃瀵煎嚭灏忚 */
+    getDoctorAgainExportSummary(data) {
+      const sums = ["灏忚"];
+
+      const keys = [
+        "dischargeCount",
+        "nonFollowUp",
+        "followUpNeeded",
+        "needFollowUpAgain",
+        "pendingFollowUpAgain",
+        "followUpSuccessAgain",
+        "followUpFailAgain",
+      ];
+
+      keys.forEach((key) => {
+        sums.push(data.reduce((t, r) => t + (Number(r[key]) || 0), 0));
+      });
+
+      // 鎴愬姛鐜囷紙骞冲潎鍊硷級
+      const successRates = data
+        .map((item) => {
+          const success = Number(item.followUpSuccessAgain) || 0;
+          const need = Number(item.needFollowUpAgain) || 0;
+          const pending = Number(item.pendingFollowUpAgain) || 0;
+          const denominator = need - pending;
+          if (denominator <= 0) return 0;
+          return success / denominator;
+        })
+        .filter((rate) => !isNaN(rate));
+
+      sums.push(
+        successRates.length
+          ? (
+              (successRates.reduce((a, b) => a + b, 0) / successRates.length) *
+              100
+            ).toFixed(2) + "%"
+          : "0.00%"
+      );
+
+      // 闅忚鐜囷紙骞冲潎鍊硷級
+      const followUpRates = data
+        .map((i) => this.extractPercentageValue(i.followUpRateAgain))
+        .filter(Boolean);
+
+      sums.push(
+        followUpRates.length
+          ? (
+              (followUpRates.reduce((a, b) => a + b, 0) /
+                followUpRates.length) *
+              100
+            ).toFixed(2) + "%"
+          : "0.00%"
+      );
+
+      return sums;
+    },
+    buildExportSheet(worksheet, sheetNameSuffix) {
+      const titleStyle = {
+        font: {
+          name: "寰蒋闆呴粦",
+          size: 16,
+          bold: true,
+          color: { argb: "FF000000" },
+        },
+        fill: {
+          type: "pattern",
+          pattern: "solid",
+          fgColor: { argb: "FFE6F3FF" },
+        },
         alignment: { vertical: "middle", horizontal: "center", wrapText: true },
         border: {
           top: { style: "thin", color: { argb: "FFD0D0D0" } },
           left: { style: "thin", color: { argb: "FFD0D0D0" } },
           bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
-          right: { style: "thin", color: { argb: "FFD0D0D0" } }
-        }
-      }
+          right: { style: "thin", color: { argb: "FFD0D0D0" } },
+        },
+      };
+
+      const headerStyle = {
+        font: {
+          name: "寰蒋闆呴粦",
+          size: 11,
+          bold: true,
+          color: { argb: "FF000000" },
+        },
+        fill: {
+          type: "pattern",
+          pattern: "solid",
+          fgColor: { argb: "FFF5F7FA" },
+        },
+        alignment: { vertical: "middle", horizontal: "center", wrapText: true },
+        border: {
+          top: { style: "thin", color: { argb: "FFD0D0D0" } },
+          left: { style: "thin", color: { argb: "FFD0D0D0" } },
+          bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
+          right: { style: "thin", color: { argb: "FFD0D0D0" } },
+        },
+      };
 
       const cellStyle = {
         font: { name: "瀹嬩綋", size: 10, color: { argb: "FF000000" } },
@@ -514,156 +1428,232 @@
           top: { style: "thin", color: { argb: "FFD0D0D0" } },
           left: { style: "thin", color: { argb: "FFD0D0D0" } },
           bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
-          right: { style: "thin", color: { argb: "FFD0D0D0" } }
-        }
-      }
+          right: { style: "thin", color: { argb: "FFD0D0D0" } },
+        },
+      };
 
       const summaryStyle = {
-        font: { name: "瀹嬩綋", size: 10, bold: true, color: { argb: "FF409EFF" } },
-        fill: { type: "pattern", pattern: "solid", fgColor: { argb: "FFF5F7FA" } },
+        font: {
+          name: "瀹嬩綋",
+          size: 10,
+          bold: true,
+          color: { argb: "FF409EFF" },
+        },
+        fill: {
+          type: "pattern",
+          pattern: "solid",
+          fgColor: { argb: "FFF5F7FA" },
+        },
         alignment: { vertical: "middle", horizontal: "center" },
         border: {
           top: { style: "thin", color: { argb: "FFD0D0D0" } },
           left: { style: "thin", color: { argb: "FFD0D0D0" } },
           bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
-          right: { style: "thin", color: { argb: "FFD0D0D0" } }
-        }
-      }
+          right: { style: "thin", color: { argb: "FFD0D0D0" } },
+        },
+      };
 
       // 娣诲姞鏍囬琛�
-      worksheet.mergeCells(1, 1, 1, 15)
-      const titleCell = worksheet.getCell(1, 1)
-      titleCell.value = `鍐嶆鍑洪櫌闅忚缁熻琛╛${sheetNameSuffix}`
-      titleCell.style = titleStyle
-      worksheet.getRow(1).height = 35
+      worksheet.mergeCells(1, 1, 1, 15);
+      const titleCell = worksheet.getCell(1, 1);
+      titleCell.value = `鍐嶆鍑洪櫌闅忚缁熻琛╛${sheetNameSuffix}`;
+      titleCell.style = titleStyle;
+      worksheet.getRow(1).height = 35;
 
       // 琛ㄥご
+      // 琛ㄥご闇�瑕佸鍔犳垚鍔熺巼鍒�
       const secondRowHeaders = [
-        "", "鍑洪櫌鐥呭尯", "绉戝", "鍑洪櫌浜烘", "鏃犻渶闅忚浜烘", "搴旈殢璁夸汉娆�",
-        "闇�闅忚", "寰呴殢璁�", "闅忚鎴愬姛", "闅忚澶辫触", "闅忚鐜�", "浜哄伐", "鐭俊", "寰俊"
-      ]
+        "",
+        "鍑洪櫌鐥呭尯",
+        "绉戝",
+        "鍑洪櫌浜烘",
+        "鏃犻渶闅忚浜烘",
+        "搴旈殢璁夸汉娆�",
+        "闇�闅忚",
+        "寰呴殢璁�",
+        "闅忚鎴愬姛",
+        "闅忚澶辫触",
+        "鎴愬姛鐜�", // 鎴愬姛鐜囧簲璇ュ湪闅忚澶辫触鍚庨潰
+        "闅忚鐜�", // 闅忚鐜囧湪鎴愬姛鐜囧悗闈�
+        "浜哄伐",
+        "璇煶", // 淇锛氬簲璇ユ槸璇煶锛屼笉鏄煭淇�
+        "鐭俊", // 鐭俊
+        "寰俊", // 寰俊
+      ];
 
       secondRowHeaders.forEach((header, index) => {
-        const cell = worksheet.getCell(3, index + 1)
-        cell.value = header
-        cell.style = headerStyle
-      })
+        const cell = worksheet.getCell(3, index + 1);
+        cell.value = header;
+        cell.style = headerStyle;
+      });
 
-      // 鍚堝苟鍗曞厓鏍�
+      // 鏇存柊鍚堝苟鍗曞厓鏍肩殑鑼冨洿
       for (let i = 1; i <= 6; i++) {
-        worksheet.mergeCells(2, i, 3, i)
-        const cell = worksheet.getCell(2, i)
-        cell.style = headerStyle
+        worksheet.mergeCells(2, i, 3, i);
+        const cell = worksheet.getCell(2, i);
+        cell.style = headerStyle;
       }
 
-      worksheet.getCell(2, 1).value = ""
-      worksheet.getCell(2, 2).value = "鍑洪櫌鐥呭尯"
-      worksheet.getCell(2, 3).value = "绉戝"
-      worksheet.getCell(2, 4).value = "鍑洪櫌浜烘"
-      worksheet.getCell(2, 5).value = "鏃犻渶闅忚浜烘"
-      worksheet.getCell(2, 6).value = "搴旈殢璁夸汉娆�"
+      worksheet.getCell(2, 1).value = "";
+      worksheet.getCell(2, 2).value = "鍑洪櫌鐥呭尯";
+      worksheet.getCell(2, 3).value = "绉戝";
+      worksheet.getCell(2, 4).value = "鍑洪櫌浜烘";
+      worksheet.getCell(2, 5).value = "鏃犻渶闅忚浜烘";
+      worksheet.getCell(2, 6).value = "搴旈殢璁夸汉娆�";
 
-      worksheet.mergeCells(2, 7, 2, 14)
-      worksheet.getCell(2, 7).value = "鍐嶆鍑洪櫌闅忚"
-      worksheet.getCell(2, 7).style = headerStyle
+      // 娉ㄦ剰锛氱敱浜庡鍔犱簡鎴愬姛鐜囧垪锛屽悎骞跺垪鏁拌澧炲姞
+      worksheet.mergeCells(2, 7, 2, 16); // 浠�7鍚堝苟鍒�16锛堝師鏉ユ槸7-14锛�
+      worksheet.getCell(2, 7).value = "鍐嶆鍑洪櫌闅忚";
+      worksheet.getCell(2, 7).style = headerStyle;
 
-      worksheet.getRow(2).height = 28
-      worksheet.getRow(3).height = 25
+      worksheet.getRow(2).height = 28;
+      worksheet.getRow(3).height = 25;
 
       // 鏁版嵁琛�
       this.tableData.forEach((item, rowIndex) => {
-        const dataRow = worksheet.addRow([
-          "",
-          item.leavehospitaldistrictname || "",
-          item.deptname || "",
-          item.dischargeCount || 0,
-          item.nonFollowUp || 0,
-          item.followUpNeeded || 0,
-          item.needFollowUpAgain || 0,
-          item.pendingFollowUpAgain || 0,
-          item.followUpSuccessAgain || 0,
-          item.followUpFailAgain || 0,
-          item.followUpRateAgain || "0%",
-          item.manualAgain || 0,
-          item.smsAgain || 0,
-          item.weChatAgain || 0
-        ], rowIndex + 4)
+        const dataRow = worksheet.addRow(
+          [
+            "",
+            item.leavehospitaldistrictname || "",
+            item.deptname || "",
+            item.dischargeCount || 0,
+            item.nonFollowUp || 0,
+            item.followUpNeeded || 0,
+            item.needFollowUpAgain || 0,
+            item.pendingFollowUpAgain || 0,
+            item.followUpSuccessAgain || 0,
+            item.followUpFailAgain || 0,
+            // 鎴愬姛鐜� - 闇�瑕佸姩鎬佽绠�
+            this.calculateSuccessRate(
+              item.followUpSuccessAgain,
+              item.needFollowUpAgain,
+              item.pendingFollowUpAgain
+            ),
+            item.followUpRateAgain || "0%", // 闅忚鐜�
+            item.manualAgain || 0,
+            item.voiceAgain || 0,
+            item.smsAgain || 0,
+            item.weChatAgain || 0,
+          ],
+          rowIndex + 4
+        );
 
         dataRow.eachCell((cell) => {
-          cell.style = cellStyle
-        })
-        dataRow.height = 24
-      })
+          cell.style = cellStyle;
+        });
+        dataRow.height = 24;
+      });
 
       // 鍚堣琛�
-      const summaries = this.getExportSummaries()
-      const summaryRow = worksheet.addRow(summaries)
+      const summaries = this.getExportSummaries();
+      const summaryRow = worksheet.addRow(summaries);
       summaryRow.eachCell((cell, colNumber) => {
-        cell.style = summaryStyle
+        cell.style = summaryStyle;
         if (colNumber === 1) {
-          cell.value = "鍚堣"
+          cell.value = "鍚堣";
         }
-      })
-      summaryRow.height = 28
+      });
+      summaryRow.height = 28;
 
       // 鍒楀
+      // 淇鍒楀
       worksheet.columns = [
-        { width: 8 }, { width: 20 }, { width: 15 }, { width: 12 }, { width: 12 }, { width: 12 },
-        { width: 10 }, { width: 10 }, { width: 10 }, { width: 10 }, { width: 12 },
-        { width: 8 }, { width: 8 }, { width: 8 }
-      ]
+        { width: 8 },
+        { width: 20 },
+        { width: 15 },
+        { width: 12 },
+        { width: 12 },
+        { width: 12 },
+        { width: 10 },
+        { width: 10 },
+        { width: 10 },
+        { width: 10 },
+        { width: 12 }, // 鎴愬姛鐜�
+        { width: 12 }, // 闅忚鐜�
+        { width: 8 }, // 浜哄伐
+        { width: 8 }, // 璇煶
+        { width: 8 }, // 鐭俊
+        { width: 8 }, // 寰俊
+      ];
     },
 
     getExportSummaries() {
-      const summaries = ["鍚堣", "/", "/", 0, 0, 0, 0, 0, 0, 0, "0%", 0, 0, 0]
+      const summaries = [
+        "鍚堣",
+        "/",
+        "/",
+        0, // 3: dischargeCount
+        0, // 4: nonFollowUp
+        0, // 5: followUpNeeded
+        0, // 6: needFollowUpAgain
+        0, // 7: pendingFollowUpAgain
+        0, // 8: followUpSuccessAgain
+        0, // 9: followUpFailAgain
+        "0%", // 10: 鎴愬姛鐜�
+        "0%", // 11: 闅忚鐜�
+        0, // 12: manualAgain
+        0, // 13: voiceAgain
+        0, // 14: smsAgain
+        0, // 15: weChatAgain
+      ];
 
       this.tableData.forEach((item) => {
-        summaries[3] += Number(item.dischargeCount) || 0
-        summaries[4] += Number(item.nonFollowUp) || 0
-        summaries[5] += Number(item.followUpNeeded) || 0
-        summaries[6] += Number(item.needFollowUpAgain) || 0
-        summaries[7] += Number(item.pendingFollowUpAgain) || 0
-        summaries[8] += Number(item.followUpSuccessAgain) || 0
-        summaries[9] += Number(item.followUpFailAgain) || 0
-        summaries[11] += Number(item.manualAgain) || 0
-        summaries[12] += Number(item.smsAgain) || 0
-        summaries[13] += Number(item.weChatAgain) || 0
-      })
+        summaries[3] += Number(item.dischargeCount) || 0;
+        summaries[4] += Number(item.nonFollowUp) || 0;
+        summaries[5] += Number(item.followUpNeeded) || 0;
+        summaries[6] += Number(item.needFollowUpAgain) || 0;
+        summaries[7] += Number(item.pendingFollowUpAgain) || 0;
+        summaries[8] += Number(item.followUpSuccessAgain) || 0;
+        summaries[9] += Number(item.followUpFailAgain) || 0;
+        summaries[12] += Number(item.manualAgain) || 0;
+        summaries[13] += Number(item.voiceAgain) || 0;
+        summaries[14] += Number(item.smsAgain) || 0;
+        summaries[15] += Number(item.weChatAgain) || 0;
+      });
 
-      const followUpRateAgainValues = this.tableData
-        .map((item) => this.extractPercentageValue(item.followUpRateAgain))
-        .filter((value) => value !== null)
+      // 鎴愬姛鐜囪绠�
+      const totalSuccess = summaries[8]; // followUpSuccessAgain鐨勬�诲拰
+      const totalNeed = summaries[6]; // needFollowUpAgain鐨勬�诲拰
+      const totalPending = summaries[7]; // pendingFollowUpAgain鐨勬�诲拰
+      const denominator = totalNeed - totalPending;
 
-      if (followUpRateAgainValues.length > 0) {
-        const avgFollowUpRateAgain = followUpRateAgainValues.reduce((sum, val) => sum + val, 0) / followUpRateAgainValues.length
-        summaries[10] = (avgFollowUpRateAgain * 100).toFixed(2) + "%"
+      if (denominator > 0) {
+        summaries[10] = ((totalSuccess / denominator) * 100).toFixed(2) + "%";
+      } else {
+        summaries[10] = "0.00%";
       }
 
-      summaries[3] = this.formatNumber(summaries[3])
-      summaries[4] = this.formatNumber(summaries[4])
-      summaries[5] = this.formatNumber(summaries[5])
-      summaries[6] = this.formatNumber(summaries[6])
-      summaries[7] = this.formatNumber(summaries[7])
-      summaries[8] = this.formatNumber(summaries[8])
-      summaries[9] = this.formatNumber(summaries[9])
-      summaries[11] = this.formatNumber(summaries[11])
-      summaries[12] = this.formatNumber(summaries[12])
-      summaries[13] = this.formatNumber(summaries[13])
+      // 闅忚鐜囪绠�
+      const followUpRateAgainValues = this.tableData
+        .map((item) => this.extractPercentageValue(item.followUpRateAgain))
+        .filter((value) => value !== null);
 
-      return summaries
+      if (followUpRateAgainValues.length > 0) {
+        const avgFollowUpRateAgain =
+          followUpRateAgainValues.reduce((sum, val) => sum + val, 0) /
+          followUpRateAgainValues.length;
+        summaries[11] = (avgFollowUpRateAgain * 100).toFixed(2) + "%";
+      }
+
+      // 鏍煎紡鍖栨暟瀛�
+      [3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15].forEach((index) => {
+        summaries[index] = this.formatNumber(summaries[index]);
+      });
+
+      return summaries;
     },
 
     extractPercentageValue(value) {
-      if (!value) return null
+      if (!value) return null;
       if (typeof value === "string" && value.includes("%")) {
-        const num = parseFloat(value.replace("%", ""))
-        return isNaN(num) ? null : num / 100
+        const num = parseFloat(value.replace("%", ""));
+        return isNaN(num) ? null : num / 100;
       }
-      const num = parseFloat(value)
-      return isNaN(num) ? null : num
-    }
-  }
-}
+      const num = parseFloat(value);
+      return isNaN(num) ? null : num;
+    },
+  },
+};
 </script>
 
 <style lang="scss" scoped>

--
Gitblit v1.9.3