WXL (wul)
2026-06-15 741805d8daa2d2baa0b6b75bc1724488baf9c6bc
src/views/Satisfaction/sfstatistics/components/components/TopicDialog.vue
@@ -1,14 +1,28 @@
<template>
  <div class="topic-dialog">
    <div class="dialog-header">
      <span class="title"> {{ configTitle }}指标详情 </span>
      <el-button
        type="primary"
        size="mini"
        icon="el-icon-download"
        :disabled="!processedTopicList.length"
        @click="exportTopicDetail"
      >
        导出
      </el-button>
    </div>
    <div class="topicdia">
      <div style="overflow-x: hidden; overflow-y: auto; max-height: 65vh">
        <!-- 修改这里:使用 processedTopicList 而不是 topicList -->
        <div
          v-for="(item, index) in topiclist"
          :key="index"
          v-for="(item, index) in processedTopicList"
          :key="item.scriptid"
          class="ttaabbcc"
        >
          <div class="describe">
            第{{ index + 1 }}题: {{ item.scriptContent }}?
            第{{ index + 1 }}题: {{ item.scriptContent }}
            <span>[{{ item.scriptType == 1 ? "单选题" : "多选题" }}]</span>
          </div>
          <div>
@@ -24,7 +38,11 @@
                label="选择人数"
                align="center"
                min-width="120"
              />
              >
                <template slot-scope="{ row }">
                  {{ row.chosenQuantity || 0 }}
                </template>
              </el-table-column>
              <el-table-column
                prop="chosenPercentage"
                label="比例"
@@ -32,8 +50,13 @@
                min-width="120"
              >
                <template slot-scope="{ row }">
                  <span v-if="row.chosenPercentage !== null && row.chosenPercentage !== undefined">
                    {{ formatPercent(row.chosenPercentage) }}
                  <span
                    v-if="
                      row.chosenPercentage !== null &&
                      row.chosenPercentage !== undefined
                    "
                  >
                    {{ (Number(row.chosenPercentage) * 100).toFixed(2) }}%
                  </span>
                  <span v-else>-</span>
                </template>
@@ -44,60 +67,308 @@
      </div>
    </div>
    <div slot="footer" class="dialog-footer" style="text-align: center; padding-top: 20px;">
    <!-- 如果没有数据 -->
    <div
      v-if="!processedTopicList.length"
      class="no-data"
      style="text-align: center; padding: 50px 0"
    >
      <el-empty description="暂无数据"></el-empty>
    </div>
    <div
      slot="footer"
      class="dialog-footer"
      style="text-align: center; padding-top: 20px"
    >
      <el-button @click="handleClose">关 闭</el-button>
    </div>
  </div>
</template>
<script>
import ExcelJS from "exceljs";
import { saveAs } from "file-saver";
export default {
  name: 'TopicDialog',
  name: "TopicDialog",
  props: {
    rowData: {
      type: Object,
      default: () => ({})
      default: () => ({}),
    },
    queryParams: {
      type: Object,
      default: () => ({})
    }
      default: () => ({}),
    },
    topType: {
      type: String,
    },
    topicList: {
      type: [Array, Object],
      default: () => ({}),
    },
  },
  data() {
    return {
      topiclist: []
      processedTopicList: [],
      orgname: "", // 新增:医院名称
    };
  },
  mounted() {
    this.loadData();
  computed: {
    configTitle() {
      const key = this.queryParams?.configKey;
      return key === "returnVisitCount" ? "复诊通知" : "满意度";
    },
  },
  created() {
    // 获取医院名称
    this.orgname = localStorage.getItem("orgname") || "";
  },
  watch: {
    topicList: {
      immediate: true,
      handler(newVal) {
        this.processTopicList(newVal);
      },
    },
  },
  methods: {
    // 加载数据
    async loadData() {
    processTopicList(data) {
      if (!data || typeof data !== "object") {
        this.processedTopicList = [];
        return;
      }
      const result = [];
      Object.keys(data).forEach((key) => {
        const item = data[key];
        if (item && item.scriptContent) {
          const processedItem = JSON.parse(JSON.stringify(item));
          if (processedItem.details && Array.isArray(processedItem.details)) {
            processedItem.details = processedItem.details.filter(
              (detail) => detail && detail.optionText
            );
          }
          result.push(processedItem);
        }
      });
      this.processedTopicList = result;
    },
    // 格式化日期范围字符串(与主页面一致)
    formatDateRangeForExport() {
      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 = (dateStr) => {
            const date = new Date(dateStr);
            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 = (dateStr) => {
            return dateStr.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}月`;
        }
      }
      return { dateRangeString, sheetNameSuffix };
    },
    // 导出题目明细
    async exportTopicDetail() {
      if (!this.processedTopicList.length) {
        this.$message.warning("暂无数据可导出");
        return;
      }
      try {
        // 这里从父组件传递数据,不需要重新调用API
        this.topiclist = this.$parent.topiclist || [];
        // 获取格式化后的日期范围
        const { dateRangeString, sheetNameSuffix } =
          this.formatDateRangeForExport();
        const workbook = new ExcelJS.Workbook();
        const sheetName = `${
          this.rowData.leavehospitaldistrictname || this.rowData.deptname
        }${this.configTitle}明细_${sheetNameSuffix}`;
        const worksheet = workbook.addWorksheet(sheetName);
        // 样式定义(保持不变)
        const titleStyle = {
          font: { name: "微软雅黑", size: 14, bold: true },
          alignment: { horizontal: "center", vertical: "middle" },
          fill: {
            type: "pattern",
            pattern: "solid",
            fgColor: { argb: "FFF5F7FA" },
          },
        };
        const subtitleStyle = {
          font: { name: "微软雅黑", size: 12, bold: true },
          alignment: { horizontal: "left", vertical: "middle" },
        };
        const headerStyle = {
          font: { name: "微软雅黑", size: 11, bold: true },
          fill: {
            type: "pattern",
            pattern: "solid",
            fgColor: { argb: "FFEBEEF5" },
          },
          alignment: { horizontal: "center", vertical: "middle" },
          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, 4);
        worksheet.getCell(
          1,
          1
        ).value = `${this.configTitle}题目明细(${dateRangeString})`;
        worksheet.getCell(1, 1).style = titleStyle;
        worksheet.mergeCells(2, 1, 2, 4);
        worksheet.getCell(2, 1).value = `统计对象:${
          this.rowData.leavehospitaldistrictname || this.rowData.deptname
        }`;
        worksheet.getCell(2, 1).style = subtitleStyle;
        let currentRow = 4;
        // 逐题写入
        this.processedTopicList.forEach((item, index) => {
          worksheet.mergeCells(currentRow, 1, currentRow, 4);
          worksheet.getCell(currentRow, 1).value = `第${index + 1}题:${
            item.scriptContent
          } [${item.scriptType == 1 ? "单选题" : "多选题"}]`;
          worksheet.getCell(currentRow, 1).style = subtitleStyle;
          currentRow++;
          const headerRow = worksheet.addRow([
            "问题选项",
            "选择人数",
            "占比",
            "",
          ]);
          headerRow.eachCell((cell) => {
            cell.style = headerStyle;
          });
          currentRow++;
          item.details.forEach((detail) => {
            const percent =
              detail.chosenPercentage != null
                ? (Number(detail.chosenPercentage) * 100).toFixed(2) + "%"
                : "-";
            const row = worksheet.addRow([
              detail.optionText,
              detail.chosenQuantity || 0,
              percent,
              "",
            ]);
            row.eachCell((cell) => {
              cell.style = cellStyle;
            });
            currentRow++;
          });
          currentRow++;
        });
        // 设置列宽
        worksheet.columns = [
          { width: 40 },
          { width: 12 },
          { width: 12 },
          { width: 10 },
        ];
        // 生成文件名(与主页面保持一致)
        const fileName = `${
          this.rowData.leavehospitaldistrictname || this.rowData.deptname
        }${this.configTitle}明细_${dateRangeString}.xlsx`;
        const buffer = await workbook.xlsx.writeBuffer();
        saveAs(
          new Blob([buffer], {
            type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
          }),
          fileName
        );
        this.$message.success("导出成功");
      } catch (error) {
        console.error('加载题目详情失败:', error);
        this.$message.error('加载题目详情失败');
        console.error("导出失败:", error);
        this.$message.error(`导出失败: ${error.message}`);
      }
    },
    // 格式化百分比
    formatPercent(value) {
      if (value === null || value === undefined) return '-';
      if (value === null || value === undefined) return "-";
      const num = parseFloat(value);
      if (isNaN(num)) return '-';
      return `${(num * 100).toFixed(2)}%`;
      if (isNaN(num)) return "-";
      return `${num.toFixed(2)}%`;
    },
    // 关闭对话框
    handleClose() {
      this.$emit('close');
    }
  }
      this.$emit("close");
    },
  },
};
</script>
@@ -137,6 +408,21 @@
    font-size: 14px;
  }
  .dialog-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 12px 16px;
    border-bottom: 1px solid #ebeef5;
    background: #fafafa;
    .title {
      font-size: 16px;
      font-weight: 600;
      color: #303133;
    }
  }
  ::v-deep .el-table th {
    background-color: #f1f5f9;
    color: #333;