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

---
 src/views/sfstatistics/percentage/components/FirstFollowUp.vue |  925 ++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 815 insertions(+), 110 deletions(-)

diff --git a/src/views/sfstatistics/percentage/components/FirstFollowUp.vue b/src/views/sfstatistics/percentage/components/FirstFollowUp.vue
index 546bc0c..bcd91ea 100644
--- a/src/views/sfstatistics/percentage/components/FirstFollowUp.vue
+++ b/src/views/sfstatistics/percentage/components/FirstFollowUp.vue
@@ -25,6 +25,26 @@
               show-summary
               :summary-method="getInnerSummaries"
             >
+              <el-table-column label="" align="center" width="96" fixed="left">
+                <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="绉戝"
@@ -32,31 +52,82 @@
                 prop="deptname"
                 align="center"
               />
-              <el-table-column
-                label="鍑洪櫌浜烘"
-                prop="dischargeCount"
-                align="center"
-              />
+
               <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�呭垪琛�',
+                        '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
@@ -64,25 +135,118 @@
                   align="center"
                   key="needFollowUp"
                   prop="needFollowUp"
-                />
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'needFollowUpInfo',
+                          '闇�闅忚鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{
+                        scope.row.needFollowUp
+                      }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
                 <el-table-column
                   label="寰呴殢璁�"
                   align="center"
                   key="pendingFollowUp"
                   prop="pendingFollowUp"
-                />
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'pendingFollowUpInfo',
+                          '寰呴殢璁垮垪琛�',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{
+                        scope.row.pendingFollowUp
+                      }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
                 <el-table-column
                   label="闅忚鎴愬姛"
                   align="center"
                   key="followUpSuccess"
                   prop="followUpSuccess"
-                />
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'followUpSuccessInfo',
+                          '闅忚鎴愬姛鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{
+                        scope.row.followUpSuccess
+                      }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
                 <el-table-column
                   label="闅忚澶辫触"
                   align="center"
                   key="followUpFail"
                   prop="followUpFail"
-                />
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'followUpFailInfo',
+                          '闅忚澶辫触鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{
+                        scope.row.followUpFail
+                      }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  label="鎴愬姛鐜�"
+                  align="center"
+                  width="120"
+                  key="successRate"
+                  prop="successRate"
+                >
+                  <template slot-scope="scope">
+                    <span class="success-rate">{{
+                      calculateSuccessRate(
+                        scope.row.followUpSuccess,
+                        scope.row.needFollowUp,
+                        scope.row.pendingFollowUp
+                      )
+                    }}</span>
+                  </template>
+                </el-table-column>
                 <el-table-column
                   label="闅忚鐜�"
                   align="center"
@@ -115,19 +279,93 @@
                   align="center"
                   key="manual"
                   prop="manual"
-                />
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'manualInfo',
+                          '浜哄伐闅忚鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{ scope.row.manual }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  label="璇煶"
+                  align="center"
+                  key="voice"
+                  prop="voice"
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'voiceInfo',
+                          '璇煶闅忚鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{ scope.row.voice }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
                 <el-table-column
                   label="鐭俊"
                   align="center"
                   key="sms"
                   prop="sms"
-                />
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'smsInfo',
+                          '鐭俊闅忚鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{ scope.row.sms }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
                 <el-table-column
                   label="寰俊"
                   align="center"
                   key="weChat"
                   prop="weChat"
-                />
+                >
+                  <template slot-scope="scope">
+                    <el-button
+                      size="medium"
+                      type="text"
+                      @click="
+                        handleViewDetails(
+                          scope.row,
+                          'weChatInfo',
+                          '寰俊闅忚鍒楄〃',
+                          '1'
+                        )
+                      "
+                    >
+                      <span class="button-zx">{{ scope.row.weChat }}</span>
+                    </el-button>
+                  </template>
+                </el-table-column>
               </el-table-column>
             </el-table>
           </template>
@@ -135,6 +373,7 @@
 
         <!-- 琛ㄦ牸鍒楀畾涔� -->
         <el-table-column
+          v-if="queryParams.statisticaltype == 1"
           label="鍑洪櫌鐥呭尯"
           align="center"
           sortable
@@ -145,6 +384,7 @@
           :sort-method="sortChineseNumber"
         />
         <el-table-column
+          v-if="queryParams.statisticaltype == 2"
           label="绉戝"
           align="center"
           key="deptname"
@@ -156,21 +396,61 @@
           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
@@ -258,6 +538,23 @@
             </template>
           </el-table-column>
           <el-table-column
+            label="鎴愬姛鐜�"
+            align="center"
+            width="120"
+            key="successRate"
+            prop="successRate"
+          >
+            <template slot-scope="scope">
+              <span class="success-rate">{{
+                calculateSuccessRate(
+                  scope.row.followUpSuccess,
+                  scope.row.needFollowUp,
+                  scope.row.pendingFollowUp
+                )
+              }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column
             label="闅忚鐜�"
             align="center"
             width="120"
@@ -302,6 +599,19 @@
               </el-button>
             </template>
           </el-table-column>
+          <el-table-column label="璇煶" align="center" key="voice" prop="voice">
+            <template slot-scope="scope">
+              <el-button
+                size="medium"
+                type="text"
+                @click="
+                  handleViewDetails(scope.row, 'voiceInfo', '璇煶闅忚鍒楄〃')
+                "
+              >
+                <span class="button-zx">{{ scope.row.voice }}</span>
+              </el-button>
+            </template>
+          </el-table-column>
           <el-table-column label="鐭俊" align="center" key="sms" prop="sms">
             <template slot-scope="scope">
               <el-button
@@ -335,7 +645,9 @@
 
         <!-- 闅忚鎯呭喌鍒楋紙浠呬附姘村競涓尰闄㈡樉绀猴級 -->
         <el-table-column
-          v-if="orgname == '涓芥按甯備腑鍖婚櫌'"
+          v-if="
+            orgname == '涓芥按甯備腑鍖婚櫌' || orgname == '鏅畞鐣叉棌鑷不鍘夸汉姘戝尰闄�'
+          "
           align="center"
           label="闅忚鎯呭喌"
         >
@@ -388,6 +700,9 @@
 </template>
 
 <script>
+import { getSfStatisticsHyperlink } from "@/api/AiCentre/index";
+import store from "@/store";
+
 import { getSfStatistics, selectTimelyRate } from "@/api/system/user";
 import ExcelJS from "exceljs";
 import { saveAs } from "file-saver";
@@ -423,6 +738,7 @@
       expands: [],
       ids: [],
       patientqueryParams: { pn: 1, ps: 10 },
+      tasktypes: store.getters.tasktypes,
     };
   },
   methods: {
@@ -442,7 +758,7 @@
 
       delete params.leavehospitaldistrictcodes.all;
       delete params.deptcodes.all;
-
+      params.rateDay = 7;
       getSfStatistics(params)
         .then((response) => {
           this.tableData = this.customSort(response.data);
@@ -655,6 +971,8 @@
 
       if (!row.doctorStats) {
         this.loading = true;
+        params.rateDay = 7;
+
         getSfStatistics(params).then((res) => {
           this.$set(row, "doctorStats", res.data);
           this.expands = [this.getRowKey(row)];
@@ -664,7 +982,23 @@
         this.expands = [this.getRowKey(row)];
       }
     },
+    // 璁$畻鎴愬姛鐜囩殑鏂规硶
+    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) + "%";
+    },
+    // 鍦ㄧ粺璁℃眹鎬绘柟娉曚腑澶勭悊鎴愬姛鐜�
     getSummaries(param) {
       const { columns, data } = param;
       const sums = [];
@@ -674,12 +1008,36 @@
           sums[index] = "鍚堣";
           return;
         }
-        if (index === 1 || index === 2) {
+        if (index === 1) {
           sums[index] = "/";
           return;
         }
 
-        if (column.property === "followUpRate" || column.property === "rate") {
+        if (column.property === "successRate") {
+          // 鎴愬姛鐜囬渶瑕侀噸鏂拌绠楁�荤殑鎴愬姛鐜囷紝鑰屼笉鏄钩鍧囧��
+          const totalSuccess = data.reduce((sum, item) => {
+            return sum + (Number(item.followUpSuccess) || 0);
+          }, 0);
+
+          const totalNeed = data.reduce((sum, item) => {
+            return sum + (Number(item.needFollowUp) || 0);
+          }, 0);
+
+          const totalPending = data.reduce((sum, item) => {
+            return sum + (Number(item.pendingFollowUp) || 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 === "followUpRate" ||
+          column.property === "rate"
+        ) {
           const percentageValues = data
             .map((item) => {
               const value = item[column.property];
@@ -736,7 +1094,31 @@
           return;
         }
 
-        if (column.property === "followUpRate" || column.property === "rate") {
+        if (column.property === "successRate") {
+          // 鎴愬姛鐜囬渶瑕侀噸鏂拌绠楁�荤殑鎴愬姛鐜囷紝鑰屼笉鏄钩鍧囧��
+          const totalSuccess = data.reduce((sum, item) => {
+            return sum + (Number(item.followUpSuccess) || 0);
+          }, 0);
+
+          const totalNeed = data.reduce((sum, item) => {
+            return sum + (Number(item.needFollowUp) || 0);
+          }, 0);
+
+          const totalPending = data.reduce((sum, item) => {
+            return sum + (Number(item.pendingFollowUp) || 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 === "followUpRate" ||
+          column.property === "rate"
+        ) {
           const percentageValues = data
             .map((item) => {
               const value = item[column.property];
@@ -787,71 +1169,363 @@
       this.ids = selection.map((item) => item.tagid);
     },
 
-    handleViewDetails(row, infoKey, titleSuffix) {
+    handleViewDetails(row, infoKey, titleSuffix, type) {
       const title = `${
         row.leavehospitaldistrictname || row.deptname
       }${titleSuffix}`;
-      this.$emit("view-details", row[infoKey], title);
+      this.$emit("view-details", row, infoKey, title, type);
     },
 
     handleSeeDetails(row) {
       this.$emit("see-details", row);
     },
-
+    // 涓昏〃瀵煎嚭
     async exportTable() {
-      try {
-        let dateRangeString = "";
-        let sheetNameSuffix = "";
+  try {
+    let dateRangeString = "";
+    let sheetNameSuffix = "";
 
+    // 鍒ゆ柇鏄惁鏄附姘村競涓尰闄�
+    const isLishuiHospital = this.orgname == "涓芥按甯備腑鍖婚櫌";
+
+    if (
+      this.queryParams.dateRange &&
+      this.queryParams.dateRange.length === 2
+    ) {
+      const startDateStr = this.queryParams.dateRange[0];
+      const endDateStr = this.queryParams.dateRange[1];
+
+      if (isLishuiHospital) {
+        // 涓芥按甯備腑鍖婚櫌锛氬彧鏄剧ず骞存湀
+        const formatMonthOnly = (dateTimeStr) => {
+          const date = new Date(dateTimeStr);
+          const year = date.getFullYear();
+          const month = date.getMonth() + 1;
+          return `${year}骞�${month}鏈坄;
+        };
+        const startDateFormatted = formatMonthOnly(startDateStr);
+        const endDateFormatted = formatMonthOnly(endDateStr);
+        dateRangeString = `${startDateFormatted}鑷�${endDateFormatted}`;
+        sheetNameSuffix = `${startDateFormatted}鑷�${endDateFormatted}`;
+      } else {
+        // 鍏朵粬鍖婚櫌锛氭樉绀哄勾鏈堟棩
+        const formatDateForDisplay = (dateTimeStr) => {
+          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;
+      const currentYear = now.getFullYear();
+
+      if (isLishuiHospital) {
+        // 涓芥按甯備腑鍖婚櫌锛氭樉绀哄勾鏈�
+        dateRangeString = `${currentYear}骞�${currentMonth}鏈坄;
+        sheetNameSuffix = `${currentYear}骞�${currentMonth}鏈坄;
+      } else {
+        // 鍏朵粬鍖婚櫌锛氭樉绀烘湀浠�
+        dateRangeString = `${currentMonth}鏈坄;
+        sheetNameSuffix = `${currentMonth}鏈坄;
+      }
+    }
+
+    // 鏍规嵁 serviceType 鐢熸垚闅忚绫诲瀷鍚嶇О
+    let serviceTypeName = "棣栨鍑洪櫌闅忚"; // 鏂囦欢鍚嶄娇鐢ㄧ殑鍚嶇О
+    let sheetTypeName = "棣栨闅忚"; // 宸ヤ綔琛ㄤ娇鐢ㄧ殑鍚嶇О锛堢畝鍖栫増锛�
+
+    if (this.queryParams.serviceType && Array.isArray(this.queryParams.serviceType) && this.queryParams.serviceType.length > 0) {
+      if (this.tasktypes && Array.isArray(this.tasktypes)) {
+        // 杩囨护鍑哄尮閰嶇殑闅忚绫诲瀷
+        const matchedTypes = this.tasktypes.filter(task =>
+          this.queryParams.serviceType.includes(task.value)
+        );
+
+        if (matchedTypes.length === 1) {
+          // 鍗曚釜绫诲瀷
+          const label = matchedTypes[0].label;
+          serviceTypeName = `棣栨${label}`;
+          sheetTypeName = `棣栨${label}`;
+        } else if (matchedTypes.length > 1) {
+          // 澶氫釜绫诲瀷
+          const typeNames = matchedTypes.map(task => task.label);
+
+          // 鏂囦欢鍚嶏細鐢ㄦ枩鏉犲垎闅�
+          serviceTypeName = `棣栨${typeNames.join("/")}`;
+
+          // 宸ヤ綔琛ㄥ悕锛氫娇鐢ㄧ涓�涓被鍨嬫垨绠�鍖栧悕绉�
+          if (matchedTypes.length <= 2) {
+            // 濡傛灉鍙湁2涓被鍨嬶紝閮芥樉绀�
+            sheetTypeName = `棣栨${typeNames[0]}绛塦;
+          } else {
+            // 濡傛灉瓒呰繃2涓被鍨嬶紝鍙樉绀虹涓�涓�
+            sheetTypeName = `棣栨${typeNames[0]}绛塦;
+          }
+        } else if (this.queryParams.serviceType.length > 0) {
+          // 濡傛灉娌℃湁鍖归厤鐨勶紝浣跨敤鍘熷鍊�
+          const typeStr = this.queryParams.serviceType.join("/");
+          serviceTypeName = `棣栨${typeStr}`;
+          sheetTypeName = "棣栨闅忚";
+        }
+      } else if (this.queryParams.serviceType.length > 0) {
+        // 濡傛灉娌℃湁 tasktypes锛屼娇鐢ㄥ師濮嬪��
+        const typeStr = this.queryParams.serviceType.join("/");
+        serviceTypeName = `棣栨${typeStr}`;
+        sheetTypeName = "棣栨闅忚";
+      }
+    }
+
+    const excelName = `${serviceTypeName}缁熻琛╛${dateRangeString}.xlsx`;
+
+    // 娓呯悊宸ヤ綔琛ㄥ悕绉帮紝绉婚櫎闈炴硶瀛楃
+    const cleanSheetName = (name) => {
+      // Excel宸ヤ綔琛ㄥ悕涓嶈兘鍖呭惈鐨勫瓧绗�: * ? : \ / [ ]
+      return name.replace(/[*?:\\/[\]]/g, ' ');
+    };
+
+    const worksheetName = cleanSheetName(`${sheetTypeName}缁熻_${sheetNameSuffix}`);
+
+    if (!this.tableData || this.tableData.length === 0) {
+      this.$message.warning(`鏆傛棤${serviceTypeName}鏁版嵁鍙鍑篳);
+      return false;
+    }
+
+    const workbook = new ExcelJS.Workbook();
+    const worksheet = workbook.addWorksheet(worksheetName);
+
+    // 鏋勫缓琛ㄦ牸
+    this.buildExportSheet(worksheet, sheetNameSuffix);
+
+    const buffer = await workbook.xlsx.writeBuffer();
+    const blob = new Blob([buffer], {
+      type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+    });
+    saveAs(blob, excelName);
+
+    this.$message.success("瀵煎嚭鎴愬姛");
+    return true;
+  } catch (error) {
+    console.error("瀵煎嚭澶辫触:", error);
+    this.$message.error(`瀵煎嚭澶辫触: ${error.message}`);
+    return false;
+  }
+},
+    // 瀛愯〃瀵煎嚭
+    /** 瀵煎嚭鍖荤敓瀛愯〃 */
+    async exportDoctorTable(row) {
+      try {
+        const areaName =
+          row.leavehospitaldistrictname || row.deptname || "鏈煡鐥呭尯";
+
+        let dateRangeString = "";
         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}`;
+          const start = this.queryParams.dateRange[0].split(" ")[0];
+          const end = this.queryParams.dateRange[1].split(" ")[0];
+          dateRangeString = `${start}鑷�${end}`;
         } else {
-          const now = new Date();
-          const currentMonth = now.getMonth() + 1;
-          dateRangeString = `${currentMonth}鏈坄;
-          sheetNameSuffix = `${currentMonth}鏈坄;
+          dateRangeString = `${new Date().getMonth() + 1}鏈坄;
         }
 
-        const excelName = `棣栨鍑洪櫌闅忚缁熻琛╛${dateRangeString}.xlsx`;
-        const worksheetName = `棣栨闅忚缁熻_${sheetNameSuffix}`;
+        const fileName = `${areaName}鍖荤敓闅忚鍒楄〃_${dateRangeString}.xlsx`;
+        const sheetName = `${areaName}鍖荤敓闅忚`;
 
-        if (!this.tableData || this.tableData.length === 0) {
-          this.$message.warning("鏆傛棤棣栨闅忚鏁版嵁鍙鍑�");
-          return false;
+        if (!row.doctorStats || row.doctorStats.length === 0) {
+          this.$message.warning("褰撳墠鐥呭尯鏆傛棤鍖荤敓闅忚鏁版嵁");
+          return;
         }
 
         const workbook = new ExcelJS.Workbook();
-        const worksheet = workbook.addWorksheet(worksheetName);
+        const worksheet = workbook.addWorksheet(sheetName);
+        console.log(111);
 
-        // 鏋勫缓琛ㄦ牸
-        this.buildExportSheet(worksheet, sheetNameSuffix);
+        this.buildDoctorExportSheet(worksheet, row.doctorStats, areaName);
+        console.log(222);
 
         const buffer = await workbook.xlsx.writeBuffer();
-        const blob = new Blob([buffer], {
-          type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
-        });
-        saveAs(blob, excelName);
+        saveAs(
+          new Blob([buffer], {
+            type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+          }),
+          fileName
+        );
 
-        this.$message.success("瀵煎嚭鎴愬姛");
-        return true;
-      } catch (error) {
-        console.error("瀵煎嚭澶辫触:", error);
-        this.$message.error(`瀵煎嚭澶辫触: ${error.message}`);
-        return false;
+        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 },
+        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.needFollowUp,
+          item.pendingFollowUp,
+          item.followUpSuccess,
+          item.followUpFail,
+          this.calculateSuccessRate(
+            item.followUpSuccess,
+            item.needFollowUp,
+            item.pendingFollowUp
+          ),
+          item.followUpRate,
+        ]);
+        row.eachCell((cell) => {
+          cell.style = cellStyle;
+        });
+      });
+
+      // 灏忚琛�
+      const summaryRow = worksheet.addRow(this.getDoctorExportSummary(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 }, // 闅忚鐜�
+      ];
+    },
+    getDoctorExportSummary(data) {
+      const sums = ["灏忚"];
+
+      const keys = [
+        "dischargeCount",
+        "nonFollowUp",
+        "followUpNeeded",
+        "needFollowUp",
+        "pendingFollowUp",
+        "followUpSuccess",
+        "followUpFail",
+      ];
+
+      keys.forEach((key) => {
+        sums.push(data.reduce((t, r) => t + (Number(r[key]) || 0), 0));
+      });
+
+      // 鎴愬姛鐜囷紙骞冲潎鍊硷級
+      const successRates = data
+        .map((item) => {
+          const success = Number(item.followUpSuccess) || 0;
+          const need = Number(item.needFollowUp) || 0;
+          const pending = Number(item.pendingFollowUp) || 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.followUpRate))
+        .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: {
@@ -946,11 +1620,13 @@
         "寰呴殢璁�",
         "闅忚鎴愬姛",
         "闅忚澶辫触",
-        "闅忚鐜�",
-        "鍙婃椂鐜�",
+        "鎴愬姛鐜�", // 鏂板
+        "闅忚鐜�", // 鍘熶綅缃悗绉�
+        "鍙婃椂鐜�", // 鍙婃椂鐜囧悗绉�
         "浜哄伐",
-        "鐭俊",
-        "寰俊",
+        "璇煶", // 淇锛氬簲璇ユ槸璇煶
+        "鐭俊", // 鐭俊
+        "寰俊", // 寰俊
       ];
 
       secondRowHeaders.forEach((header, index) => {
@@ -973,7 +1649,7 @@
       worksheet.getCell(2, 5).value = "鏃犻渶闅忚浜烘";
       worksheet.getCell(2, 6).value = "搴旈殢璁夸汉娆�";
 
-      worksheet.mergeCells(2, 7, 2, 15);
+      worksheet.mergeCells(2, 7, 2, 16); // 浠�7鍚堝苟鍒�16锛堝師鏉ユ槸7-15锛�
       worksheet.getCell(2, 7).value = "棣栨鍑洪櫌闅忚";
       worksheet.getCell(2, 7).style = headerStyle;
 
@@ -994,15 +1670,21 @@
             item.pendingFollowUp || 0,
             item.followUpSuccess || 0,
             item.followUpFail || 0,
-            item.followUpRate || "0%",
-            item.rate ? (Number(item.rate) * 100).toFixed(2) + "%" : "0%",
+            // 鎴愬姛鐜� - 闇�瑕佸姩鎬佽绠�
+            this.calculateSuccessRate(
+              item.followUpSuccess,
+              item.needFollowUp,
+              item.pendingFollowUp
+            ),
+            item.followUpRate || "0%", // 闅忚鐜�
+            item.rate ? (Number(item.rate) * 100).toFixed(2) + "%" : "0%", // 鍙婃椂鐜�
             item.manual || 0,
-            item.sms || 0,
-            item.weChat || 0,
+            item.voice || 0, // 璇煶
+            item.sms || 0, // 鐭俊
+            item.weChat || 0, // 寰俊
           ],
           rowIndex + 4
         );
-
         dataRow.eachCell((cell) => {
           cell.style = cellStyle;
         });
@@ -1032,11 +1714,13 @@
         { width: 10 },
         { width: 10 },
         { width: 10 },
-        { width: 12 },
-        { width: 12 },
-        { width: 8 },
-        { width: 8 },
-        { width: 8 },
+        { width: 12 }, // 鎴愬姛鐜�
+        { width: 12 }, // 闅忚鐜�
+        { width: 12 }, // 鍙婃椂鐜�
+        { width: 8 }, // 浜哄伐
+        { width: 8 }, // 璇煶
+        { width: 8 }, // 鐭俊
+        { width: 8 }, // 寰俊
       ];
     },
 
@@ -1045,18 +1729,20 @@
         "鍚堣",
         "/",
         "/",
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        0,
-        "0%",
-        "0%",
-        0,
-        0,
-        0,
+        0, // 3: dischargeCount
+        0, // 4: nonFollowUp
+        0, // 5: followUpNeeded
+        0, // 6: needFollowUp
+        0, // 7: pendingFollowUp
+        0, // 8: followUpSuccess
+        0, // 9: followUpFail
+        "0%", // 10: 鎴愬姛鐜�
+        "0%", // 11: 闅忚鐜�
+        "0%", // 12: 鍙婃椂鐜�
+        0, // 13: manual
+        0, // 14: voice
+        0, // 15: sms
+        0, // 16: weChat
       ];
 
       this.tableData.forEach((item) => {
@@ -1067,42 +1753,51 @@
         summaries[7] += Number(item.pendingFollowUp) || 0;
         summaries[8] += Number(item.followUpSuccess) || 0;
         summaries[9] += Number(item.followUpFail) || 0;
-        summaries[12] += Number(item.manual) || 0;
-        summaries[13] += Number(item.sms) || 0;
-        summaries[14] += Number(item.weChat) || 0;
+        summaries[13] += Number(item.manual) || 0;
+        summaries[14] += Number(item.voice) || 0;
+        summaries[15] += Number(item.sms) || 0;
+        summaries[16] += Number(item.weChat) || 0;
       });
 
+      // 鎴愬姛鐜囪绠�
+      const totalSuccess = summaries[8]; // followUpSuccess鐨勬�诲拰
+      const totalNeed = summaries[6]; // needFollowUp鐨勬�诲拰
+      const totalPending = summaries[7]; // pendingFollowUp鐨勬�诲拰
+      const denominator = totalNeed - totalPending;
+
+      if (denominator > 0) {
+        summaries[10] = ((totalSuccess / denominator) * 100).toFixed(2) + "%";
+      } else {
+        summaries[10] = "0.00%";
+      }
+
+      // 闅忚鐜囪绠�
       const followUpRateValues = this.tableData
         .map((item) => this.extractPercentageValue(item.followUpRate))
-        .filter((value) => value !== null);
-
-      const rateValues = this.tableData
-        .map((item) => this.extractPercentageValue(item.rate))
         .filter((value) => value !== null);
 
       if (followUpRateValues.length > 0) {
         const avgFollowUpRate =
           followUpRateValues.reduce((sum, val) => sum + val, 0) /
           followUpRateValues.length;
-        summaries[10] = (avgFollowUpRate * 100).toFixed(2) + "%";
+        summaries[11] = (avgFollowUpRate * 100).toFixed(2) + "%";
       }
+
+      // 鍙婃椂鐜囪绠�
+      const rateValues = this.tableData
+        .map((item) => this.extractPercentageValue(item.rate))
+        .filter((value) => value !== null);
 
       if (rateValues.length > 0) {
         const avgRate =
           rateValues.reduce((sum, val) => sum + val, 0) / rateValues.length;
-        summaries[11] = (avgRate * 100).toFixed(2) + "%";
+        summaries[12] = (avgRate * 100).toFixed(2) + "%";
       }
 
-      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[12] = this.formatNumber(summaries[12]);
-      summaries[13] = this.formatNumber(summaries[13]);
-      summaries[14] = this.formatNumber(summaries[14]);
+      // 鏍煎紡鍖栨暟瀛�
+      [3, 4, 5, 6, 7, 8, 9, 13, 14, 15, 16].forEach((index) => {
+        summaries[index] = this.formatNumber(summaries[index]);
+      });
 
       return summaries;
     },
@@ -1117,8 +1812,8 @@
       return isNaN(num) ? null : num;
     },
 
-    selectTimelyRate(row, dateRange) {
-      console.log(row, dateRange, 88);
+    selectTimelyRate(row, queryParams) {
+      console.log(row, queryParams, 88);
 
       // const params = {
       //   ...this.patientqueryParams,
@@ -1126,9 +1821,19 @@
       //   endtime: this.parseTime(dateRange[1]),
       //   deptcode: row.deptcode,
       // };
-      this.patientqueryParams.starttime = this.parseTime(dateRange[0]);
-      this.patientqueryParams.endtime = this.parseTime(dateRange[1]);
+      this.patientqueryParams.starttime = this.parseTime(
+        queryParams.dateRange[0]
+      );
+      this.patientqueryParams.endtime = this.parseTime(
+        queryParams.dateRange[1]
+      );
       this.patientqueryParams.deptcode = row.deptcode;
+      console.log(1);
+
+      this.patientqueryParams.serviceTypes = queryParams.serviceType
+        ? queryParams.serviceType.join(",")
+        : null;
+
       return selectTimelyRate(this.patientqueryParams);
     },
     selectTimelyRates(dateRange) {

--
Gitblit v1.9.3