WXL (wul)
19 小时以前 3cf6f5204b0ade9aa5022d5fcecbef095c575918
src/views/sfstatistics/percentage/index.vue
@@ -113,7 +113,7 @@
                  plain
                  icon="el-icon-download"
                  size="medium"
                  @click="handleExport"
                  @click="exportTable"
                  >导出</el-button
                >
                <el-button
@@ -128,6 +128,8 @@
            </el-form>
            <div class="your-table-container">
              <el-table
                ref="exportTable"
                id="exportTableid"
                v-loading="loading"
                :data="userList"
                :border="true"
@@ -864,7 +866,10 @@
} from "@/api/system/label";
import store from "@/store";
import { getSfStatistics, selectTimelyRate } from "@/api/system/user";
import * as XLSX from "xlsx";
import FileSaver from "file-saver";
import ExcelJS from "exceljs";
import { saveAs } from "file-saver";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
const shortcuts = [
@@ -1089,7 +1094,7 @@
          sums[index] = "合计";
          return;
        }
         if (index === 1||index === 2) {
        if (index === 1 || index === 2) {
          sums[index] = "/";
          return;
        }
@@ -1460,31 +1465,293 @@
        })
        .catch(() => {});
    },
    /** 导出按钮操作 */
    handleExport() {
      const params = {
        ...this.queryParams,
        // 如果选择了"全部",则传所有病区/科室代码
        leavehospitaldistrictcodes:
          this.queryParams.leavehospitaldistrictcodes.includes("all")
            ? this.allWardCodes
            : this.queryParams.leavehospitaldistrictcodes,
        deptcodes: this.queryParams.deptcodes.includes("all")
          ? this.allDeptCodes
          : this.queryParams.deptcodes,
      };
       delete params.leavehospitaldistrictcodes.all;
      delete params.deptcodes.all;
      console.log(params);
    // 导出方法
    // 替换您原来的 exportTable 方法
    async exportTable() {
      try {
        // 获取当前日期
        const now = new Date();
        // 获取当前月份(注意月份从0开始,需要加1)
        const currentMonth = now.getMonth() + 1;
        // 构建文件名
        const excelName = `${currentMonth}月出院随访统计表.xlsx`;
        // 创建新的工作簿和工作表
        const workbook = new ExcelJS.Workbook();
        const worksheet = workbook.addWorksheet("随访统计");
      this.download(
        "smartor/serviceSubtask/getSfStatisticsExport",
        {
          ...params,
        },
        `user_${new Date().getTime()}.xlsx`
      );
        // 定义样式
        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" },
          },
          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" } },
          },
        };
        const summaryStyle = {
          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" } },
          },
        };
        // 1. 首先,创建并设置第二行(子表头)的所有单元格
        const secondRowHeaders = [
          "", // A2 展开列占位(其值将由第一行合并后的主单元格决定)
          "出院病区",
          "科室",
          "出院人次",
          "无需随访人次",
          "应随访人次", // B2 to F2
          // 首次出院随访子表头
          "需随访",
          "待随访",
          "随访成功",
          "随访失败",
          "随访率",
          "及时率",
          "人工",
          "短信",
          "微信",
          // 再次出院随访子表头
          "需随访",
          "待随访",
          "随访成功",
          "随访失败",
          "随访率",
          "人工",
          "短信",
          "微信",
        ];
        // 添加第二行并设置样式
        secondRowHeaders.forEach((header, index) => {
          // 注意:列索引从1开始,对应A列是1,B列是2,以此类推。
          const cell = worksheet.getCell(2, index + 1);
          cell.value = header;
          cell.style = headerStyle;
        });
        // 2. 然后,创建第一行的主标题单元格并设置样式,紧接着进行纵向合并
        // 合并 A1:A2 并设置值
        worksheet.mergeCells(1, 1, 2, 1); // 合并 A1 到 A2
        worksheet.getCell(1, 1).value = ""; // 设置主单元格(A1)的值
        worksheet.getCell(1, 1).style = headerStyle; // 设置主单元格样式
        // 合并 B1:B2 并设置值
        worksheet.mergeCells(1, 2, 2, 2); // 合并 B1 到 B2
        worksheet.getCell(1, 2).value = "出院病区";
        worksheet.getCell(1, 2).style = headerStyle;
        // 合并 C1:C2 并设置值
        worksheet.mergeCells(1, 3, 2, 3); // 合并 C1 到 C2
        worksheet.getCell(1, 3).value = "科室";
        worksheet.getCell(1, 3).style = headerStyle;
        // 合并 D1:D2 并设置值
        worksheet.mergeCells(1, 4, 2, 4); // 合并 D1 到 D2
        worksheet.getCell(1, 4).value = "出院人次";
        worksheet.getCell(1, 4).style = headerStyle;
        // 合并 E1:E2 并设置值
        worksheet.mergeCells(1, 5, 2, 5); // 合并 E1 到 E2
        worksheet.getCell(1, 5).value = "无需随访人次";
        worksheet.getCell(1, 5).style = headerStyle;
        // 合并 F1:F2 并设置值
        worksheet.mergeCells(1, 6, 2, 6); // 合并 F1 到 F2
        worksheet.getCell(1, 6).value = "应随访人次";
        worksheet.getCell(1, 6).style = headerStyle;
        // 3. 设置第一行的横向合并标题(这些保持不变,因为只涉及第一行)
        // 首次出院随访(合并G1到O1)
        worksheet.mergeCells("G1:O1");
        worksheet.getCell("G1").value = "首次出院随访";
        worksheet.getCell("G1").style = headerStyle;
        // 再次出院随访(合并P1到W1)
        worksheet.mergeCells("P1:W1");
        worksheet.getCell("P1").value = "再次出院随访";
        worksheet.getCell("P1").style = headerStyle;
        // 4. 设置行高(可选,但建议设置)
        worksheet.getRow(1).height = 28; // 第一行行高
        worksheet.getRow(2).height = 25; // 第二行行高
        // 添加数据行
        this.userList.forEach((item, rowIndex) => {
          const dataRow = worksheet.addRow([
            "", // 展开列
            item.leavehospitaldistrictname || "",
            item.deptname || "",
            item.dischargeCount || 0,
            item.nonFollowUp || 0,
            item.followUpNeeded || 0,
            // 首次出院随访数据
            item.needFollowUp || 0,
            item.pendingFollowUp || 0,
            item.followUpSuccess || 0,
            item.followUpFail || 0,
            item.followUpRate || "0%",
            item.rate ? (Number(item.rate) * 100).toFixed(2) + "%" : "0%",
            item.manual || 0,
            item.sms || 0,
            item.weChat || 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,
          ]);
          // 应用数据行样式
          dataRow.eachCell((cell) => {
            cell.style = cellStyle;
          });
          dataRow.height = 24;
        });
        // 添加合计行
        const summaries = this.getSummaries({
          columns: [
            { property: "" },
            { property: "leavehospitaldistrictname" },
            { property: "deptname" },
            { property: "dischargeCount" },
            { property: "nonFollowUp" },
            { property: "followUpNeeded" },
            { property: "needFollowUp" },
            { property: "pendingFollowUp" },
            { property: "followUpSuccess" },
            { property: "followUpFail" },
            { property: "followUpRate" },
            { property: "rate" },
            { property: "manual" },
            { property: "sms" },
            { property: "weChat" },
            { property: "needFollowUpAgain" },
            { property: "pendingFollowUpAgain" },
            { property: "followUpSuccessAgain" },
            { property: "followUpFailAgain" },
            { property: "followUpRateAgain" },
            { property: "manualAgain" },
            { property: "smsAgain" },
            { property: "weChatAgain" },
          ],
          data: this.userList,
        });
        const summaryRow = worksheet.addRow(summaries);
        summaryRow.eachCell((cell, colNumber) => {
          cell.style = summaryStyle;
          // 第一列显示"合计"
          if (colNumber === 1) {
            cell.value = "合计";
          }
        });
        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: 12 },
          { width: 8 },
          { width: 8 },
          { width: 8 },
          // 再次出院随访列
          { width: 10 },
          { width: 10 },
          { width: 10 },
          { width: 10 },
          { width: 12 },
          { width: 8 },
          { width: 8 },
          { width: 8 },
        ];
        // 生成并下载文件
        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;
      }
    },
    // 显示图表弹窗
    showChartDialog() {
@@ -1946,9 +2213,21 @@
}
// 百分比字段特殊样式
.your-table-container ::v-deep .el-table__footer .el-table__cell[data-field="followUpRate"] .cell,
.your-table-container ::v-deep .el-table__footer .el-table__cell[data-field="rate"] .cell,
.your-table-container ::v-deep .el-table__footer .el-table__cell[data-field="followUpRateAgain"] .cell {
.your-table-container
  ::v-deep
  .el-table__footer
  .el-table__cell[data-field="followUpRate"]
  .cell,
.your-table-container
  ::v-deep
  .el-table__footer
  .el-table__cell[data-field="rate"]
  .cell,
.your-table-container
  ::v-deep
  .el-table__footer
  .el-table__cell[data-field="followUpRateAgain"]
  .cell {
  color: #e6a23c !important;
  font-weight: 700 !important;
}