WXL (wul)
5 天以前 ba1ad6cff887cecb836a9fff87d84c146c820ad7
src/views/sfstatistics/percentage/index.vue
@@ -83,12 +83,14 @@
              >
                <el-date-picker
                  v-model="queryParams.dateRange"
                  value-format="yyyy-MM-dd"
                  value-format="yyyy-MM-dd HH:mm:ss"
                  type="daterange"
                  range-separator="至"
                  start-placeholder="开始日期"
                  end-placeholder="结束日期"
                  :default-time="['00:00:00', '23:59:59']"
                >
                  >
                </el-date-picker>
              </el-form-item>
@@ -113,7 +115,7 @@
                  plain
                  icon="el-icon-download"
                  size="medium"
                  @click="handleExport"
                  @click="exportTable"
                  >导出</el-button
                >
                <el-button
@@ -128,6 +130,8 @@
            </el-form>
            <div class="your-table-container">
              <el-table
                ref="exportTable"
                id="exportTableid"
                v-loading="loading"
                :data="userList"
                :border="true"
@@ -398,6 +402,21 @@
                    key="pendingFollowUp"
                    prop="pendingFollowUp"
                  >
                    <template slot-scope="scope">
                      <el-button
                        size="medium"
                        type="text"
                        @click="
                          viewDetails(
                            scope.row.pendingFollowUpInfo,
                            scope.row.leavehospitaldistrictname + '待随访列表'
                          )
                        "
                        ><span class="button-zx">{{
                          scope.row.pendingFollowUp
                        }}</span></el-button
                      >
                    </template>
                  </el-table-column>
                  <el-table-column
                    label="随访成功"
@@ -412,6 +431,21 @@
                    key="followUpFail"
                    prop="followUpFail"
                  >
                    <template slot-scope="scope">
                      <el-button
                        size="medium"
                        type="text"
                        @click="
                          viewDetails(
                            scope.row.followUpFailInfo,
                            scope.row.leavehospitaldistrictname + '随访失败列表'
                          )
                        "
                        ><span class="button-zx">{{
                          scope.row.followUpFail
                        }}</span></el-button
                      >
                    </template>
                  </el-table-column>
                  <el-table-column
                    label="随访率"
@@ -852,6 +886,198 @@
        </div>
      </div>
    </el-dialog>
    <!-- 各类详情 -->
    <el-dialog
      :title="infotitle"
      :visible.sync="infotitleVisible"
      v-loading="infotitloading"
      width="70%"
      :close-on-click-modal="false"
    >
      <div class="examine-jic">
        <div class="jic-value">
          <el-row :gutter="20">
            <!-- 选择患者列表 -->
            <el-table :data="infotitlelist" height="660" style="width: 100%">
              <el-table-column
                prop="sendname"
                align="center"
                label="姓名"
                width="100"
              >
              </el-table-column>
              <el-table-column
                prop="taskName"
                align="center"
                width="200"
                show-overflow-tooltip
                label="任务名称"
              >
              </el-table-column>
              <el-table-column
                prop="sendstate"
                align="center"
                width="200"
                label="任务状态"
              >
                <template slot-scope="scope">
                  <div v-if="scope.row.sendstate == 1">
                    <el-tag type="primary" :disable-transitions="false"
                      >表单已领取</el-tag
                    >
                  </div>
                  <div v-if="scope.row.sendstate == 2">
                    <el-tag type="primary" :disable-transitions="false"
                      >待随访</el-tag
                    >
                  </div>
                  <div v-if="scope.row.sendstate == 3">
                    <el-tag type="success" :disable-transitions="false"
                      >表单已发送</el-tag
                    >
                  </div>
                  <div v-if="scope.row.sendstate == 4">
                    <el-tag type="info" :disable-transitions="false"
                      >不执行</el-tag
                    >
                  </div>
                  <div v-if="scope.row.sendstate == 5">
                    <el-tag type="danger" :disable-transitions="false"
                      >发送失败</el-tag
                    >
                  </div>
                  <div v-if="scope.row.sendstate == 6">
                    <el-tag type="success" :disable-transitions="false"
                      >已完成</el-tag
                    >
                  </div>
                </template>
              </el-table-column>
              <el-table-column
                prop="visitTime"
                align="center"
                label="应随访时间"
                width="200"
                show-overflow-tooltip
              >
              </el-table-column>
              <el-table-column
                prop="finishtime"
                align="center"
                label="随访完成时间"
                width="200"
                show-overflow-tooltip
              >
              </el-table-column>
              <el-table-column
                label="出院日期"
                width="200"
                align="center"
                key="endtime"
                prop="endtime"
              >
                <template slot-scope="scope">
                  <span>{{ formatTime(scope.row.endtime) }}</span>
                </template></el-table-column
              >
              <el-table-column
                label="责任护士"
                width="120"
                align="center"
                key="nurseName"
                prop="nurseName"
              />
              <el-table-column
                label="主治医生"
                width="120"
                align="center"
                key="drname"
                prop="drname"
              />
              <el-table-column
                label="结果状态"
                align="center"
                key="excep"
                prop="excep"
                width="120"
              >
                <template slot-scope="scope">
                  <dict-tag
                    :options="dict.type.sys_yujing"
                    :value="scope.row.excep"
                  />
                </template>
              </el-table-column>
              <el-table-column
                label="处理意见"
                align="center"
                key="suggest"
                prop="suggest"
                width="120"
              >
                <template slot-scope="scope">
                  <dict-tag
                    :options="dict.type.sys_suggest"
                    :value="scope.row.suggest"
                  />
                </template>
              </el-table-column>
              <el-table-column
                prop="templatename"
                align="center"
                label="服务模板"
                width="200"
                show-overflow-tooltip
              >
              </el-table-column>
              <el-table-column
                prop="remark"
                align="center"
                label="服务记录"
                width="200"
                show-overflow-tooltip
              >
              </el-table-column>
              <el-table-column
                prop="bankcardno"
                align="center"
                label="呼叫状态"
                width="210"
              >
              </el-table-column>
              <el-table-column
                label="操作"
                fixed="right"
                align="center"
                width="200"
                class-name="small-padding fixed-width"
              >
                <template slot-scope="scope">
                  <el-button
                    size="medium"
                    type="text"
                    @click="SeedetailsgGo(scope.row)"
                    ><span class="button-zx"
                      ><i class="el-icon-s-order"></i>查看</span
                    ></el-button
                  >
                </template>
              </el-table-column>
            </el-table>
          </el-row>
          <pagination
            v-show="patienttotal > 0 && this.patientqueryParams.allhosp != 6"
            :total="patienttotal"
            :page.sync="patientqueryParams.pn"
            :limit.sync="patientqueryParams.ps"
            @pagination="Seedetails"
          />
        </div>
      </div>
    </el-dialog>
  </div>
</template>
@@ -864,7 +1090,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 = [
@@ -901,10 +1130,14 @@
      activeName: "first", //侧边选择
      orgname: "",
      expands: [],
      infotitlelist: [],
      // 遮罩层
      loading: false,
      Seedloading: false,
      chartDialogVisible: false,
      infotitleVisible: false,
      infotitloading: false,
      infotitle: "",
      pieChart: null,
      barLineChart: null,
      // 选中数组
@@ -994,8 +1227,6 @@
      },
      // 查询标签列表参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        serviceType: [2],
        dateRange: [],
        statisticaltype: 1,
@@ -1089,7 +1320,7 @@
          sums[index] = "合计";
          return;
        }
         if (index === 1||index === 2) {
        if (index === 1 || index === 2) {
          sums[index] = "/";
          return;
        }
@@ -1324,6 +1555,11 @@
        this.Seedloading = false;
      });
    },
    viewDetails(row, title) {
      this.infotitleVisible = true;
      this.infotitle = title;
      this.infotitlelist = row;
    },
    SeedetailsgGo(row) {
      this.SeedetailsVisible = false;
      let type = "";
@@ -1460,16 +1696,353 @@
        })
        .catch(() => {});
    },
    /** 导出按钮操作 */
    handleExport() {
      this.download(
        "smartor/serviceSubtask/getSfStatisticsExport",
        {
          ...this.queryParams,
        },
        `user_${new Date().getTime()}.xlsx`
      );
    // 导出方法
    // 替换您原来的 exportTable 方法
    async exportTable() {
      try {
        // 1. 获取并格式化日期范围
        let dateRangeString = ""; // 用于文件名
        let sheetNameSuffix = ""; // 用于工作表名
        // 检查是否存在选中的日期范围
        if (
          this.queryParams.dateRange &&
          this.queryParams.dateRange.length === 2
        ) {
          const startDateStr = this.queryParams.dateRange[0]; // 开始日期字符串,例如 "2026-01-01 00:00:00"
          const endDateStr = this.queryParams.dateRange[1]; // 结束日期字符串
          // 格式化日期为 YYYY-MM-DD(去掉时间部分)
          const formatDateForDisplay = (dateTimeStr) => {
            return dateTimeStr.split(" ")[0]; // 取空格前的部分,即 "YYYY-MM-DD"
          };
          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}月`;
        }
        // 2. 动态构建文件名和工作表名
        const excelName = `出院随访统计表_${dateRangeString}.xlsx`;
        const worksheetName = `随访统计_${sheetNameSuffix}`; // 工作表名不能超过31个字符[2](@ref)
        // 创建新的工作簿和工作表
        const workbook = new ExcelJS.Workbook();
        const worksheet = workbook.addWorksheet(worksheetName); // 使用动态工作表名
        // 定义样式(新增总标题样式)
        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" } },
          },
        };
        // 定义样式
        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. 添加总标题行(第一行)
        worksheet.mergeCells(1, 1, 1, 23); // 合并A1到W1的所有列[1,4](@ref)
        const titleCell = worksheet.getCell(1, 1);
        titleCell.value = `${sheetNameSuffix}出院随访统计表`; // 使用文件名作为总标题
        titleCell.style = titleStyle;
        worksheet.getRow(1).height = 35; // 设置总标题行高
        // 1. 首先,创建并设置第二行(子表头)的所有单元格
        const secondRowHeaders = [
          "", // A2 展开列占位(其值将由第一行合并后的主单元格决定)
          "出院病区",
          "科室",
          "出院人次",
          "无需随访人次",
          "应随访人次", // B2 to F2
          // 首次出院随访子表头
          "需随访",
          "待随访",
          "随访成功",
          "随访失败",
          "随访率",
          "及时率",
          "人工",
          "短信",
          "微信",
          // 再次出院随访子表头
          "需随访",
          "待随访",
          "随访成功",
          "随访失败",
          "随访率",
          "人工",
          "短信",
          "微信",
        ];
        // 添加第二行(原第一行下移)
        secondRowHeaders.forEach((header, index) => {
          const cell = worksheet.getCell(3, index + 1); // 改为第3行
          cell.value = header;
          cell.style = headerStyle;
        });
        // 3. 调整原合并单元格位置(原第1行合并单元格下移到第2行)
        // 合并 A2:A3
        worksheet.mergeCells(2, 1, 3, 1);
        worksheet.getCell(2, 1).value = "";
        worksheet.getCell(2, 1).style = headerStyle;
        // 合并 B2:B3
        worksheet.mergeCells(2, 2, 3, 2);
        worksheet.getCell(2, 2).value = "出院病区";
        worksheet.getCell(2, 2).style = headerStyle;
        // 合并 C2:C3
        worksheet.mergeCells(2, 3, 3, 3);
        worksheet.getCell(2, 3).value = "科室";
        worksheet.getCell(2, 3).style = headerStyle;
        // 合并 D2:D3
        worksheet.mergeCells(2, 4, 3, 4);
        worksheet.getCell(2, 4).value = "出院人次";
        worksheet.getCell(2, 4).style = headerStyle;
        // 合并 E2:E3
        worksheet.mergeCells(2, 5, 3, 5);
        worksheet.getCell(2, 5).value = "无需随访人次";
        worksheet.getCell(2, 5).style = headerStyle;
        // 合并 F2:F3
        worksheet.mergeCells(2, 6, 3, 6);
        worksheet.getCell(2, 6).value = "应随访人次";
        worksheet.getCell(2, 6).style = headerStyle;
        // 4. 调整横向合并标题位置(下移到第2行)
        // 首次出院随访(合并G2:O2)
        worksheet.mergeCells(2, 7, 2, 15); // G2:O2
        worksheet.getCell(2, 7).value = "首次出院随访";
        worksheet.getCell(2, 7).style = headerStyle;
        // 再次出院随访(合并P2:W2)
        worksheet.mergeCells(2, 16, 2, 23); // P2:W2
        worksheet.getCell(2, 16).value = "再次出院随访";
        worksheet.getCell(2, 16).style = headerStyle;
        // 5. 设置行高
        worksheet.getRow(1).height = 35; // 总标题行高
        worksheet.getRow(2).height = 28; // 原第一行下移
        worksheet.getRow(3).height = 25; // 原第二行下移
        // 6. 添加数据行(注意行索引需要+1,因为上面插入了一行)
        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,
            ],
            rowIndex + 4
          ); // 从第4行开始添加数据(原第3行)
          // 应用数据行样式
          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() {
@@ -1931,9 +2504,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;
}