WXL (wul)
2 天以前 feb5a669dced68415bc7e32f237f77bf9842fe8b
src/views/Satisfaction/sfstatistics/components/SatisfactionStatistics.vue
@@ -24,13 +24,16 @@
            </el-select>
          </el-form-item>
          <el-form-item label="科室" prop="deptCode">
          <!-- 科室选择 -->
          <el-form-item label="科室" prop="deptCodes">
            <el-select
              v-model="queryParams.deptCode"
              v-model="queryParams.deptCodes"
              placeholder="请选择科室"
              clearable
              filterable
              style="width: 200px"
              multiple
              collapse-tags
              style="width: 300px"
              @change="handleDeptChange"
            >
              <el-option
@@ -42,13 +45,16 @@
            </el-select>
          </el-form-item>
          <el-form-item label="病区" prop="wardCode">
          <!-- 病区选择 -->
          <el-form-item label="病区" prop="wardCodes">
            <el-select
              v-model="queryParams.wardCode"
              v-model="queryParams.wardCodes"
              placeholder="请选择病区"
              clearable
              filterable
              style="width: 200px"
              multiple
              collapse-tags
              style="width: 300px"
              @change="handleWardChange"
            >
              <el-option
@@ -95,7 +101,7 @@
      <el-card shadow="never">
        <div class="chart-container">
          <div class="chart-header">
            <h3 class="chart-title">满意度类型填报比例统计</h3>
            <h3 class="chart-title">{{ getChartTitle() }}</h3>
            <div class="statistic-info">
              <div class="statistic-item">
                <span class="statistic-label">发送问卷总量:</span>
@@ -117,6 +123,20 @@
              </div>
            </div>
          </div>
          <!-- 标签页切换 -->
          <el-tabs
            v-model="activeChartTab"
            @tab-click="handleChartTabClick"
            class="chart-tabs"
          >
            <el-tab-pane
              label="满意度类型"
              name="satisfactionType"
            ></el-tab-pane>
            <el-tab-pane label="维度统计" name="dimension"></el-tab-pane>
          </el-tabs>
          <div
            id="satisfactionBarChart"
            style="width: 100%; height: 400px"
@@ -304,6 +324,179 @@
            </div>
          </el-tab-pane>
          <el-tab-pane label="维度题目明细" name="dimensionDetail">
            <!-- 维度题目明细表格 -->
            <div class="dimension-detail-section">
              <el-table
                v-loading="dimensionDetailLoading"
                :data="dimensionDetailData"
                :border="true"
                style="width: 100%"
                row-class-name="dimension-row"
              >
                <el-table-column type="expand" width="60">
                  <template slot-scope="{ row }">
                    <div class="option-detail">
                      <el-table
                        :data="row.options"
                        :border="true"
                        style="width: 100%"
                        class="inner-table"
                      >
                        <el-table-column
                          label="选项"
                          prop="optionText"
                          align="center"
                          min-width="200"
                        />
                        <el-table-column
                          label="选择人数"
                          prop="chosenQuantity"
                          align="center"
                          min-width="120"
                        />
                        <el-table-column
                          label="选择比例"
                          prop="chosenPercentage"
                          align="center"
                          min-width="120"
                        >
                          <template slot-scope="{ row: option }">
                            {{ formatPercent(option.chosenPercentage) }}
                          </template>
                        </el-table-column>
                      </el-table>
                    </div>
                  </template>
                </el-table-column>
                <el-table-column
                  label="序号"
                  type="index"
                  align="center"
                  width="60"
                />
                <el-table-column
                  label="题目"
                  prop="scriptContent"
                  align="center"
                  min-width="300"
                >
                  <template slot-scope="{ row }">
                    <span>{{ row.scriptContent }}</span>
                    <el-tag
                      :type="row.scriptType === 1 ? 'primary' : 'success'"
                      size="mini"
                      style="margin-left: 5px"
                    >
                      {{ row.scriptType === 1 ? "单选题" : "多选题" }}
                    </el-tag>
                  </template>
                </el-table-column>
                <el-table-column
                  label="维度"
                  prop="dimensionName"
                  align="center"
                  width="120"
                >
                  <template slot-scope="{ row }">
                    <el-tag type="info" size="small">
                      {{ getDimensionLabel(row.dimension) }}
                    </el-tag>
                  </template>
                </el-table-column>
                <el-table-column
                  label="平均得分"
                  prop="averageScore"
                  align="center"
                  width="120"
                >
                  <template slot-scope="{ row }">
                    <span class="score-text">{{
                      row.averageScore.toFixed(1)
                    }}</span>
                  </template>
                </el-table-column>
                <el-table-column
                  label="最高得分"
                  prop="maxScore"
                  align="center"
                  width="120"
                >
                  <template slot-scope="{ row }">
                    <span class="score-text">{{
                      row.maxScore.toFixed(1)
                    }}</span>
                  </template>
                </el-table-column>
                <el-table-column
                  label="最低得分"
                  prop="minScore"
                  align="center"
                  width="120"
                >
                  <template slot-scope="{ row }">
                    <span class="score-text">{{
                      row.minScore.toFixed(1)
                    }}</span>
                  </template>
                </el-table-column>
                <el-table-column
                  label="答题人数"
                  prop="answerPerson"
                  align="center"
                  width="100"
                />
                <el-table-column
                  label="未答题人数"
                  prop="noAnswerPerson"
                  align="center"
                  width="100"
                />
                <el-table-column
                  label="答题率"
                  prop="answerRate"
                  align="center"
                  width="100"
                >
                  <template slot-scope="{ row }">
                    {{ formatPercent(row.answerRate) }}
                  </template>
                </el-table-column>
              </el-table>
              <!-- 维度统计汇总 -->
              <div class="dimension-summary-row">
                <div class="dimension-summary-content">
                  <div class="dimension-summary-item">
                    <span class="label">题目总数:</span>
                    <span class="value">{{ dimensionDetailData.length }}</span>
                  </div>
                  <div class="dimension-summary-item">
                    <span class="label">维度平均分:</span>
                    <span class="value">{{
                      dimensionAverageScore.toFixed(1)
                    }}</span>
                  </div>
                  <div class="dimension-summary-item">
                    <span class="label">总体答题率:</span>
                    <span class="value">{{
                      formatPercent(dimensionTotalAnswerRate)
                    }}</span>
                  </div>
                </div>
              </div>
            </div>
          </el-tab-pane>
          <el-tab-pane label="各类型统计明细" name="typeDetail">
            <!-- 各类型统计明细表格 -->
            <div class="type-detail-section">
@@ -415,40 +608,6 @@
                </el-table-column>
                <el-table-column
                  label="趋势"
                  prop="trend"
                  align="center"
                  width="120"
                >
                  <template slot-scope="{ row }">
                    <div class="trend-cell">
                      <i
                        v-if="row.trend === 'up'"
                        class="el-icon-top trend-up"
                        :style="{ color: '#67C23A' }"
                      />
                      <i
                        v-else-if="row.trend === 'down'"
                        class="el-icon-bottom trend-down"
                        :style="{ color: '#F56C6C' }"
                      />
                      <i
                        v-else
                        class="el-icon-minus trend-stable"
                        :style="{ color: '#909399' }"
                      />
                      <span class="trend-text">{{
                        row.trend === "up"
                          ? "上升"
                          : row.trend === "down"
                          ? "下降"
                          : "稳定"
                      }}</span>
                    </div>
                  </template>
                </el-table-column>
                <el-table-column
                  label="操作"
                  align="center"
                  width="120"
@@ -508,25 +667,31 @@
<script>
import * as echarts from "echarts";
import { statistics, satisfactionGraph } from "@/api/system/user";
import {
  statistics,
  satisfactionGraph,
  statisticsByDimension,
  satisfactionGraphDimension,
} from "@/api/system/user";
import store from "@/store";
export default {
  name: "SatisfactionStatistics",
  dicts: ["dimensionality_type"],
  data() {
    return {
      // 查询参数
      queryParams: {
        type: 2,
        patientSource: "",
        deptCode: "",
        wardCode: "",
        deptCodes: [], // 改为数组,支持多选
        wardCodes: [], // 改为数组,支持多选
        dateRange: [],
      },
      // 当前激活的tab
      activeTab: "questionDetail",
      // 当前激活的图表tab
      activeChartTab: "satisfactionType",
      // 患者来源选项
      patientSourceList: [
        { value: "1", label: "门诊" },
@@ -548,9 +713,13 @@
      loading: false,
      detailLoading: false,
      typeDetailLoading: false,
      dimensionDetailLoading: false,
      // 题目明细数据
      questionDetailData: [],
      // 维度题目明细数据
      dimensionDetailData: [],
      // 题目明细查询参数
      detailQueryParams: {
@@ -561,10 +730,24 @@
      // 题目明细总数
      detailTotal: 0,
      // 维度明细查询参数
      dimensionQueryParams: {
        pageNum: 1,
        pageSize: 10,
      },
      // 维度明细总数
      dimensionTotal: 0,
      // 综合得分
      totalScore: 0,
      totalAnswerCount: 0,
      totalAnswerRate: 0,
      // 维度统计汇总
      dimensionTotalAnswerCount: 0,
      dimensionTotalAnswerRate: 0,
      dimensionAverageScore: 0,
      // 各类型统计明细数据
      typeDetailData: [],
@@ -660,16 +843,16 @@
    // 计算科室编码数组
    deptCodes() {
      if (this.queryParams.deptCode) {
        return [this.queryParams.deptCode];
      if (this.queryParams.deptCodes && this.queryParams.deptCodes.length > 0) {
        return this.queryParams.deptCodes;
      }
      return this.deptList.map((dept) => dept.value);
    },
    // 计算病区编码数组
    hospitalDistrictCodes() {
      if (this.queryParams.wardCode) {
        return [this.queryParams.wardCode];
      if (this.queryParams.wardCodes && this.queryParams.wardCodes.length > 0) {
        return this.queryParams.wardCodes;
      }
      return this.wardList.map((ward) => ward.value);
    },
@@ -729,6 +912,13 @@
      });
    },
    // 获取图表标题
    getChartTitle() {
      return this.activeChartTab === "satisfactionType"
        ? "满意度类型填报比例统计"
        : "维度填报比例统计";
    },
    // 加载数据
    async loadData() {
      await Promise.all([
@@ -742,23 +932,10 @@
    async loadChartData() {
      this.loading = true;
      try {
        const params = {
          type: this.queryParams.type,
          startTime: this.startTime,
          endTime: this.endTime,
          deptcodes: this.deptCodes,
          hospitaldistrictcodes: this.hospitalDistrictCodes,
          templateid: this.templateId,
        };
        const response = await satisfactionGraph(params);
        if (response.code === 200) {
          this.processChartData(response);
        if (this.activeChartTab === "satisfactionType") {
          await this.loadSatisfactionTypeChartData();
        } else {
          this.$message.error(response.msg || "获取图表数据失败");
          // 使用mock数据
          await this.generateMockChartData();
          await this.loadDimensionChartData();
        }
      } catch (error) {
        console.error("获取图表数据出错:", error);
@@ -770,14 +947,54 @@
      }
    },
    // 处理图表数据
    processChartData(apiData) {
    // 加载满意度类型图表数据
    async loadSatisfactionTypeChartData() {
      const params = {
        type: this.queryParams.type,
        startTime: this.startTime,
        endTime: this.endTime,
        deptcodes: this.deptCodes, // 使用计算属性
        hospitaldistrictcodes: this.hospitalDistrictCodes, // 使用计算属性
        templateid: this.templateId,
      };
      const response = await satisfactionGraph(params);
      if (response.code === 200) {
        this.processSatisfactionTypeChartData(response);
      } else {
        this.$message.error(response.msg || "获取图表数据失败");
        await this.generateMockChartData();
      }
    },
    // 加载维度图表数据
    async loadDimensionChartData() {
      const params = {
        type: this.queryParams.type,
        startTime: this.startTime,
        endTime: this.endTime,
        deptcodes: this.deptCodes, // 使用计算属性
        hospitaldistrictcodes: this.hospitalDistrictCodes, // 使用计算属性
        templateid: this.templateId,
      };
      const response = await satisfactionGraphDimension(params);
      if (response.code === 200) {
        this.processDimensionChartData(response);
      } else {
        this.$message.error(response.msg || "获取维度图表数据失败");
        await this.generateMockDimensionChartData();
      }
    },
    // 处理满意度类型图表数据
    processSatisfactionTypeChartData(apiData) {
      if (!apiData || !apiData.rows || Object.keys(apiData.rows).length === 0) {
        this.chartData = [];
        this.totalSendCount = 0;
        this.totalReceiveCount = 0;
        this.overallRecoveryRate = 0;
        this.renderChart([]);
        this.renderChart([], "satisfactionType");
        return;
      }
@@ -790,11 +1007,11 @@
      Object.entries(apiData.rows).forEach(([typeName, typeStat]) => {
        const sendCount = typeStat.subidAll || 0;
        const receiveCount = typeStat.fillCountAll || 0;
        const recoveryRate = typeStat.receiveRate || 0;
        const recoveryRate = typeStat.receiveRate.toFixed(2) || 0;
        chartData.push({
          name: typeName,
          value: recoveryRate * 100, // 转换为百分比
          value: (recoveryRate * 100).toFixed(2), // 转换为百分比
          sendCount: sendCount,
          receiveCount: receiveCount,
          averageScore: typeStat.averageScore || 0,
@@ -811,7 +1028,64 @@
      this.overallRecoveryRate = totalSend > 0 ? totalReceive / totalSend : 0;
      this.chartData = chartData;
      this.renderChart(chartData);
      this.renderChart(chartData, "satisfactionType");
    },
    // 处理维度图表数据
    processDimensionChartData(apiData) {
      if (!apiData || !apiData.rows || Object.keys(apiData.rows).length === 0) {
        this.chartData = [];
        this.totalSendCount = 0;
        this.totalReceiveCount = 0;
        this.overallRecoveryRate = 0;
        this.renderChart([], "dimension");
        return;
      }
      const chartData = [];
      let totalSend = 0;
      let totalReceive = 0;
      let index = 0;
      // 处理接口返回的维度统计
      Object.entries(apiData.rows).forEach(([dimensionCode, dimensionStat]) => {
        const sendCount = dimensionStat.subidAll || 0;
        const receiveCount = dimensionStat.fillCountAll || 0;
        const recoveryRate = dimensionStat.receiveRate || 0;
        // 获取维度标签
        const dimensionName = this.getDimensionLabel(dimensionCode);
        chartData.push({
          name: dimensionName,
          value: (recoveryRate * 100).toFixed(2), // 转换为百分比
          sendCount: sendCount,
          receiveCount: receiveCount,
          averageScore: dimensionStat.averageScore || 0,
          dimension: dimensionCode,
          itemStyle: { color: this.getChartColor(index) },
        });
        totalSend += sendCount;
        totalReceive += receiveCount;
        index++;
      });
      this.totalSendCount = totalSend;
      this.totalReceiveCount = totalReceive;
      this.overallRecoveryRate = totalSend > 0 ? totalReceive / totalSend : 0;
      this.chartData = chartData;
      this.renderChart(chartData, "dimension");
    },
    // 获取维度标签
    getDimensionLabel(dimensionCode) {
      if (!this.dict.type.dimensionality_type) return dimensionCode;
      const dimension = this.dict.type.dimensionality_type.find(
        (item) => item.value.toString() === dimensionCode.toString()
      );
      return dimension ? dimension.label : dimensionCode;
    },
    // 加载题目明细数据
@@ -823,6 +1097,8 @@
          startTime: this.startTime,
          endTime: this.endTime,
          scriptids: this.scriptIds,
          deptcodes: this.deptCodes, // 使用计算属性
          hospitaldistrictcodes: this.hospitalDistrictCodes, // 使用计算属性
          templateid: this.templateId,
        };
@@ -898,6 +1174,85 @@
      this.totalAnswerRate = apiData.totalAnswerRate || 0;
    },
    // 加载维度明细数据
    async loadDimensionDetailData() {
      this.dimensionDetailLoading = true;
      try {
        const params = {
          type: this.queryParams.type,
          startTime: this.startTime,
          endTime: this.endTime,
          deptcodes: this.deptCodes, // 使用计算属性
          hospitaldistrictcodes: this.hospitalDistrictCodes, // 使用计算属性
          templateid: this.templateId,
          questionType: this.queryParams.type || null,
          serviceTypes: this.defaultServiceTypes,
        };
        const response = await statisticsByDimension(params);
        if (response.code === 200) {
          this.processDimensionDetailData(response.rows);
        } else {
          this.$message.error(response.msg || "获取维度明细数据失败");
          const mockData = await this.generateMockDimensionDetail();
          this.dimensionDetailData = mockData;
          this.calculateDimensionSummary(mockData);
        }
      } catch (error) {
        console.error("获取维度明细数据出错:", error);
        this.$message.error("获取维度明细数据失败");
        const mockData = await this.generateMockDimensionDetail();
        this.dimensionDetailData = mockData;
        this.calculateDimensionSummary(mockData);
      } finally {
        this.dimensionDetailLoading = false;
      }
    },
    // 处理维度明细数据
    processDimensionDetailData(apiData) {
      if (!apiData || !apiData.patSatisfactionDetailEntities) {
        this.dimensionDetailData = [];
        this.calculateDimensionSummary([]);
        return;
      }
      const detailData = apiData.patSatisfactionDetailEntities.map((item) => {
        const options = [];
        if (item.matchedtextStats) {
          Object.keys(item.matchedtextStats).forEach((key) => {
            const stat = item.matchedtextStats[key];
            options.push({
              optionText: key,
              chosenQuantity: stat.count || 0,
              chosenPercentage: (stat.ratio || 0) / 100,
            });
          });
        }
        return {
          scriptContent: item.scriptContent || "",
          dimension: item.dimension || "0", // 维度代码
          dimensionName: this.getDimensionLabel(item.dimension || "0"), // 维度名称
          scriptType: 1,
          answerPerson: item.answerPerson || 0,
          noAnswerPerson: item.noAnswerPerson || 0,
          answerCount: item.answerPerson || 0,
          averageScore: item.averageScore || 0,
          maxScore: item.maxScore || 0,
          minScore: item.minScore || 0,
          answerRate: item.answerRate || 0,
          totalCount: (item.answerPerson || 0) + (item.noAnswerPerson || 0),
          options: options,
        };
      });
      this.dimensionDetailData = detailData;
      this.dimensionTotal = detailData.length;
      this.calculateDimensionSummary(detailData);
    },
    // 加载类型明细数据
    async loadTypeDetailData() {
      this.typeDetailLoading = true;
@@ -906,15 +1261,15 @@
          type: this.queryParams.type,
          startTime: this.startTime,
          endTime: this.endTime,
          deptcodes: this.deptCodes,
          hospitaldistrictcodes: this.hospitalDistrictCodes,
          deptcodes: this.deptCodes, // 使用计算属性
          hospitaldistrictcodes: this.hospitalDistrictCodes, // 使用计算属性
          templateid: this.templateId,
        };
        const response = await satisfactionGraph(params);
        if (response.code === 200) {
          this.processTypeDetailData(response.data);
          this.processTypeDetailData(response);
        } else {
          this.$message.error(response.msg || "获取类型明细数据失败");
          const mockData = await this.generateMockTypeDetail();
@@ -944,7 +1299,7 @@
      Object.entries(apiData.rows).forEach(([typeName, typeStat], index) => {
        const sendCount = typeStat.subidAll || 0;
        const receiveCount = typeStat.fillCountAll || 0;
        const recoveryRate = typeStat.receiveRate || 0;
        const recoveryRate = typeStat.receiveRate.toFixed(2) || 0;
        const averageScore = typeStat.averageScore || 0;
        typeDetail.push({
@@ -972,14 +1327,7 @@
      if (score >= 4.0) return "良好";
      if (score >= 3.0) return "一般";
      if (score >= 2.0) return "较差";
      return "差";
    },
    // 获取趋势
    getTrend(trend) {
      if (trend > 0.1) return "up";
      if (trend < -0.1) return "down";
      return "stable";
      return "未知";
    },
    // 计算综合得分
@@ -998,6 +1346,32 @@
        data.list.length > 0 ? totalScore / data.list.length : 0;
      this.totalAnswerCount = totalAnswerCount;
      this.totalAnswerRate = totalCount > 0 ? totalAnswerCount / totalCount : 0;
    },
    // 计算维度统计汇总
    calculateDimensionSummary(data) {
      if (data.length === 0) {
        this.dimensionTotalAnswerCount = 0;
        this.dimensionTotalAnswerRate = 0;
        this.dimensionAverageScore = 0;
        return;
      }
      let totalScore = 0;
      let totalAnswerCount = 0;
      let totalCount = 0;
      data.forEach((item) => {
        totalScore += item.averageScore;
        totalAnswerCount += item.answerPerson;
        totalCount += item.answerPerson + item.noAnswerPerson;
      });
      this.dimensionAverageScore =
        data.length > 0 ? totalScore / data.length : 0;
      this.dimensionTotalAnswerCount = totalAnswerCount;
      this.dimensionTotalAnswerRate =
        totalCount > 0 ? totalAnswerCount / totalCount : 0;
    },
    // 计算类型统计汇总
@@ -1039,26 +1413,34 @@
    },
    // 渲染图表
    renderChart(chartData) {
    renderChart(chartData, chartType = "satisfactionType") {
      if (!this.barChart) return;
 if (!chartData || chartData.length === 0) {
    const emptyOption = {
      title: {
        text: "暂无数据",
        left: "center",
        top: "center",
        textStyle: {
          color: "#999",
          fontSize: 16,
          fontWeight: "normal"
        }
      },
      xAxis: { show: false },
      yAxis: { show: false }
    };
    this.barChart.setOption(emptyOption);
    return;
  }
      if (!chartData || chartData.length === 0) {
        const emptyOption = {
          title: {
            text: "暂无数据",
            left: "center",
            top: "center",
            textStyle: {
              color: "#999",
              fontSize: 16,
              fontWeight: "normal",
            },
          },
          xAxis: { show: false },
          yAxis: { show: false },
        };
        this.barChart.setOption(emptyOption);
        return;
      }
      const yAxisName =
        chartType === "satisfactionType" ? "填报比例 (%)" : "填报比例 (%)";
      const tooltipFormatter =
        chartType === "satisfactionType"
          ? this.getSatisfactionTypeTooltipFormatter
          : this.getDimensionTooltipFormatter;
      const option = {
        title: {
          text: "",
@@ -1069,28 +1451,7 @@
          axisPointer: {
            type: "shadow",
          },
          formatter: (params) => {
            const data = params[0];
            return `
              <div style="margin-bottom: 5px; font-weight: bold; color: #333;">
                ${data.name}
              </div>
              <div style="margin: 2px 0;">
                <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:${
                  data.color
                };margin-right:5px;"></span>
                填报比例: <strong>${data.value.toFixed(1)}%</strong>
              </div>
              <div style="margin: 2px 0;">
                <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:#eee;margin-right:5px;"></span>
                发送问卷: <strong>${data.data.sendCount.toLocaleString()}</strong>
              </div>
              <div style="margin: 2px 0;">
                <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:#eee;margin-right:5px;"></span>
                回收问卷: <strong>${data.data.receiveCount.toLocaleString()}</strong>
              </div>
            `;
          },
          formatter: tooltipFormatter,
        },
        grid: {
          left: "3%",
@@ -1119,7 +1480,7 @@
        },
        yAxis: {
          type: "value",
          name: "填报比例 (%)",
          name: yAxisName,
          min: 0,
          max: 100,
          axisLabel: {
@@ -1163,6 +1524,66 @@
      this.barChart.setOption(option);
    },
    // 满意度类型tooltip格式化
    getSatisfactionTypeTooltipFormatter(params) {
      const data = params[0];
      return `
        <div style="margin-bottom: 5px; font-weight: bold; color: #333;">
          ${data.name}
        </div>
        <div style="margin: 2px 0;">
          <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:${
            data.color
          };margin-right:5px;"></span>
          填报比例: <strong>${data.value}%</strong>
        </div>
        <div style="margin: 2px 0;">
          <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:#eee;margin-right:5px;"></span>
          发送问卷: <strong>${data.data.sendCount.toLocaleString()}</strong>
        </div>
        <div style="margin: 2px 0;">
          <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:#eee;margin-right:5px;"></span>
          回收问卷: <strong>${data.data.receiveCount.toLocaleString()}</strong>
        </div>
        <div style="margin: 2px 0;">
          <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:#eee;margin-right:5px;"></span>
          平均分: <strong>${data.data.averageScore.toFixed(1)}</strong>
        </div>
      `;
    },
    // 维度tooltip格式化
    getDimensionTooltipFormatter(params) {
      const data = params[0];
      return `
        <div style="margin-bottom: 5px; font-weight: bold; color: #333;">
          ${data.name}
        </div>
        <div style="margin: 2px 0;">
          <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:${
            data.color
          };margin-right:5px;"></span>
          填报比例: <strong>${data.value}%</strong>
        </div>
        <div style="margin: 2px 0;">
          <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:#eee;margin-right:5px;"></span>
          发送问卷: <strong>${data.data.sendCount.toLocaleString()}</strong>
        </div>
        <div style="margin: 2px 0;">
          <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:#eee;margin-right:5px;"></span>
          回收问卷: <strong>${data.data.receiveCount.toLocaleString()}</strong>
        </div>
        <div style="margin: 2px 0;">
          <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:#eee;margin-right:5px;"></span>
          平均分: <strong>${data.data.averageScore.toFixed(1)}</strong>
        </div>
        <div style="margin: 2px 0;">
          <span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:#eee;margin-right:5px;"></span>
          维度代码: <strong>${data.data.dimension}</strong>
        </div>
      `;
    },
    // 生成Mock图表数据
    generateMockChartData() {
      return new Promise((resolve) => {
@@ -1197,11 +1618,69 @@
            value: item.recoveryRate * 100,
            sendCount: item.sendCount,
            receiveCount: item.receiveCount,
            averageScore: 4.2 + Math.random() * 0.8,
            itemStyle: { color: item.color },
          }));
          this.chartData = chartData;
          this.renderChart(chartData);
          this.renderChart(chartData, "satisfactionType");
          resolve();
        }, 300);
      });
    },
    // 生成Mock维度图表数据
    generateMockDimensionChartData() {
      return new Promise((resolve) => {
        setTimeout(() => {
          // 模拟维度数据
          const dimensionLabels = this.dict.type.dimensionality_type || [
            { value: "1", label: "维度1" },
            { value: "2", label: "维度2" },
            { value: "3", label: "维度3" },
            { value: "4", label: "维度4" },
            { value: "5", label: "维度5" },
          ];
          const data = dimensionLabels.map((dim, index) => ({
            name: dim.label,
            recoveryRate: Math.random() * 0.3 + 0.6,
            sendCount: Math.floor(Math.random() * 2000) + 1000,
            receiveCount: 0,
            averageScore: 3.5 + Math.random() * 1.5,
            dimension: dim.value,
            color: this.getChartColor(index),
          }));
          data.forEach((item) => {
            item.receiveCount = Math.floor(item.sendCount * item.recoveryRate);
          });
          this.totalSendCount = data.reduce(
            (sum, item) => sum + item.sendCount,
            0
          );
          this.totalReceiveCount = data.reduce(
            (sum, item) => sum + item.receiveCount,
            0
          );
          this.overallRecoveryRate =
            this.totalSendCount > 0
              ? this.totalReceiveCount / this.totalSendCount
              : 0;
          const chartData = data.map((item) => ({
            name: item.name,
            value: item.recoveryRate * 100,
            sendCount: item.sendCount,
            receiveCount: item.receiveCount,
            averageScore: item.averageScore,
            dimension: item.dimension,
            itemStyle: { color: item.color },
          }));
          this.chartData = chartData;
          this.renderChart(chartData, "dimension");
          resolve();
        }, 300);
      });
@@ -1301,6 +1780,111 @@
      });
    },
    // 生成Mock维度详情数据
    generateMockDimensionDetail() {
      return new Promise((resolve) => {
        setTimeout(() => {
          const questions = [
            {
              scriptContent: "医护人员服务态度",
              dimension: "1",
              dimensionName: "服务态度",
              scriptType: 1,
              answerPerson: 120,
              noAnswerPerson: 30,
              answerCount: 120,
              totalCount: 150,
              averageScore: 4.2,
              maxScore: 5.0,
              minScore: 1.0,
              answerRate: 0.8,
              options: [
                {
                  optionText: "非常满意",
                  chosenQuantity: 60,
                  chosenPercentage: 0.5,
                },
                {
                  optionText: "满意",
                  chosenQuantity: 36,
                  chosenPercentage: 0.3,
                },
                {
                  optionText: "一般",
                  chosenQuantity: 18,
                  chosenPercentage: 0.15,
                },
              ],
            },
            {
              scriptContent: "医疗技术水平",
              dimension: "2",
              dimensionName: "技术水平",
              scriptType: 1,
              answerPerson: 145,
              noAnswerPerson: 11,
              answerCount: 145,
              totalCount: 156,
              averageScore: 4.5,
              maxScore: 5,
              minScore: 3,
              answerRate: 0.93,
              options: [
                {
                  optionText: "非常满意",
                  chosenQuantity: 89,
                  chosenPercentage: 0.61,
                },
                {
                  optionText: "满意",
                  chosenQuantity: 45,
                  chosenPercentage: 0.31,
                },
                {
                  optionText: "一般",
                  chosenQuantity: 8,
                  chosenPercentage: 0.06,
                },
              ],
            },
            {
              scriptContent: "就医环境设施",
              dimension: "3",
              dimensionName: "环境设施",
              scriptType: 1,
              answerPerson: 98,
              noAnswerPerson: 22,
              answerCount: 98,
              totalCount: 120,
              averageScore: 4.0,
              maxScore: 5,
              minScore: 2,
              answerRate: 0.82,
              options: [
                {
                  optionText: "非常满意",
                  chosenQuantity: 45,
                  chosenPercentage: 0.46,
                },
                {
                  optionText: "满意",
                  chosenQuantity: 40,
                  chosenPercentage: 0.41,
                },
                {
                  optionText: "一般",
                  chosenQuantity: 10,
                  chosenPercentage: 0.1,
                },
              ],
            },
          ];
          resolve(questions);
        }, 300);
      });
    },
    // 生成Mock类型明细数据
    generateMockTypeDetail() {
      return new Promise((resolve) => {
@@ -1374,6 +1958,12 @@
        "#FF9D4D",
        "#9B8DFF",
        "#FF6B6B",
        "#66C2A5",
        "#FC8D62",
        "#8DA0CB",
        "#E78AC3",
        "#A6D854",
        "#FFD92F",
      ];
      return colors[index % colors.length];
    },
@@ -1385,14 +1975,21 @@
      }
    },
    // 处理图表Tab切换
    handleChartTabClick(tab) {
      this.activeChartTab = tab.name;
      this.loadChartData();
    },
    // 处理查询
    handleSearch() {
      this.detailQueryParams.pageNum = 1;
      this.dimensionQueryParams.pageNum = 1;
      this.loadData();
      // 强制重新渲染图表
      setTimeout(() => {
        if (this.chartData.length === 0) {
          this.renderChart([]);
          this.renderChart([], this.activeChartTab);
        }
      }, 100);
    },
@@ -1401,14 +1998,30 @@
    handleReset() {
      this.$refs.queryForm.resetFields();
      this.queryParams.dateRange = [];
        this.queryParams.deptCodes = [];  // 重置为数组
  this.queryParams.wardCodes = [];  // 重置为数组
      this.detailQueryParams.pageNum = 1;
      this.dimensionQueryParams.pageNum = 1;
      this.loadData();
    },
    // 处理Tab切换
    handleTabClick(tab) {
      if (tab.name === "typeDetail" && this.typeDetailData.length === 0) {
      if (
        tab.name === "dimensionDetail" &&
        this.dimensionDetailData.length === 0
      ) {
        this.loadDimensionDetailData();
      } else if (
        tab.name === "typeDetail" &&
        this.typeDetailData.length === 0
      ) {
        this.loadTypeDetailData();
      } else if (
        tab.name === "questionDetail" &&
        this.questionDetailData.length === 0
      ) {
        this.loadQuestionDetailData();
      }
    },
@@ -1425,6 +2038,19 @@
      this.loadQuestionDetailData();
    },
    // 处理维度明细分页大小变化
    handleDimensionSizeChange(size) {
      this.dimensionQueryParams.pageSize = size;
      this.dimensionQueryParams.pageNum = 1;
      this.loadDimensionDetailData();
    },
    // 处理维度明细页码变化
    handleDimensionPageChange(page) {
      this.dimensionQueryParams.pageNum = page;
      this.loadDimensionDetailData();
    },
    // 处理类型详情
    handleTypeDetail(row) {
      this.$message.info(`查看类型详情:${row.typeName}`);
@@ -1437,12 +2063,12 @@
    // 格式化百分比
    formatPercent(value) {
   if (value === null || value === undefined) return "-";
  const num = parseFloat(value);
  if (isNaN(num)) return "-";
  // 如果值小于1,认为是小数比例,需要乘以100
  const percentValue = num < 1 ? num * 100 : num;
  return `${percentValue.toFixed(2)}%`;
      if (value === null || value === undefined) return "-";
      const num = parseFloat(value);
      if (isNaN(num)) return "-";
      // 如果值小于1,认为是小数比例,需要乘以100
      const percentValue = num < 1 ? num * 100 : num;
      return `${percentValue.toFixed(2)}%`;
    },
    // 获取回收率样式类
@@ -1459,7 +2085,7 @@
        良好: "primary",
        一般: "warning",
        较差: "danger",
        差: "info",
        未知: "info",
      };
      return levelMap[level] || "info";
    },
@@ -1545,6 +2171,14 @@
        }
      }
    }
    .chart-tabs {
      margin-bottom: 20px;
      ::v-deep .el-tabs__header {
        margin-bottom: 0;
      }
    }
  }
  .tab-section {
@@ -1557,7 +2191,8 @@
    }
  }
  .detail-table-section {
  .detail-table-section,
  .dimension-detail-section {
    .option-detail {
      padding: 15px;
      background: #f8f9fa;
@@ -1577,7 +2212,8 @@
        padding: 12px 0;
      }
      .question-row {
      .question-row,
      .dimension-row {
        td {
          background-color: #fff;
        }
@@ -1596,19 +2232,22 @@
      font-size: 16px;
    }
    .summary-row {
    .summary-row,
    .dimension-summary-row {
      margin-top: 20px;
      padding: 20px;
      background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
      border-radius: 8px;
      border: 1px solid #dee2e6;
      .summary-content {
      .summary-content,
      .dimension-summary-content {
        display: flex;
        justify-content: space-around;
        align-items: center;
        .summary-item {
        .summary-item,
        .dimension-summary-item {
          text-align: center;
          .label {
@@ -1680,24 +2319,6 @@
        color: #409eff;
        font-size: 15px;
      }
      .trend-cell {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 5px;
        .trend-up,
        .trend-down,
        .trend-stable {
          font-size: 16px;
        }
        .trend-text {
          font-size: 13px;
          color: #666;
        }
      }
    }
    .type-summary-row {
@@ -1759,7 +2380,13 @@
    }
  }
}
/* 确保多选下拉框样式正确 */
::v-deep .el-select__tags {
  max-width: 200px;
  .el-tag {
    margin: 2px 0 2px 6px;
  }
}
@media (max-width: 768px) {
  .satisfaction-statistics {
    padding: 10px;
@@ -1791,8 +2418,10 @@
      }
    }
    .detail-table-section {
      .summary-content {
    .detail-table-section,
    .dimension-detail-section {
      .summary-content,
      .dimension-summary-content {
        flex-direction: column;
        gap: 15px;
      }