From dac4393e8af2646f544f6e1ca24dab11b40c8492 Mon Sep 17 00:00:00 2001
From: WXL (wul) <wl_5969728@163.com>
Date: 星期二, 07 四月 2026 15:16:40 +0800
Subject: [PATCH] 测试完成
---
src/views/Satisfaction/sfstatistics/components/SatisfactionStatistics.vue | 1809 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1,809 insertions(+), 0 deletions(-)
diff --git a/src/views/Satisfaction/sfstatistics/components/SatisfactionStatistics.vue b/src/views/Satisfaction/sfstatistics/components/SatisfactionStatistics.vue
new file mode 100644
index 0000000..c1d3a0a
--- /dev/null
+++ b/src/views/Satisfaction/sfstatistics/components/SatisfactionStatistics.vue
@@ -0,0 +1,1809 @@
+<template>
+ <div class="satisfaction-statistics">
+ <!-- 鏌ヨ鏉′欢鍖哄煙 -->
+ <div class="query-section">
+ <el-card shadow="never">
+ <el-form
+ :model="queryParams"
+ ref="queryForm"
+ size="medium"
+ :inline="true"
+ label-width="100px"
+ class="query-form"
+ >
+ <el-form-item label="缁熻绫诲瀷" prop="patientSource">
+ <el-select
+ v-model="queryParams.type"
+ placeholder="璇烽�夋嫨缁熻绫诲瀷"
+ clearable
+ style="width: 100%"
+ >
+ <el-option label="闂嵎绫诲瀷" :value="2" />
+ <el-option label="璇煶绫诲瀷" :value="1" />
+ <el-option label="鍏ㄩ儴" :value="null" />
+ </el-select>
+ </el-form-item>
+
+ <el-form-item label="绉戝" prop="deptCode">
+ <el-select
+ v-model="queryParams.deptCode"
+ placeholder="璇烽�夋嫨绉戝"
+ clearable
+ filterable
+ style="width: 200px"
+ @change="handleDeptChange"
+ >
+ <el-option
+ v-for="dept in deptList"
+ :key="dept.value"
+ :label="dept.label"
+ :value="dept.value"
+ />
+ </el-select>
+ </el-form-item>
+
+ <el-form-item label="鐥呭尯" prop="wardCode">
+ <el-select
+ v-model="queryParams.wardCode"
+ placeholder="璇烽�夋嫨鐥呭尯"
+ clearable
+ filterable
+ style="width: 200px"
+ @change="handleWardChange"
+ >
+ <el-option
+ v-for="ward in wardList"
+ :key="ward.value"
+ :label="ward.label"
+ :value="ward.value"
+ />
+ </el-select>
+ </el-form-item>
+
+ <el-form-item label="缁熻鏃堕棿" prop="dateRange">
+ <el-date-picker
+ v-model="queryParams.dateRange"
+ type="daterange"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫棩鏈�"
+ end-placeholder="缁撴潫鏃ユ湡"
+ value-format="yyyy-MM-dd"
+ :picker-options="pickerOptions"
+ style="width: 380px"
+ />
+ </el-form-item>
+
+ <el-form-item>
+ <el-button
+ type="primary"
+ icon="el-icon-search"
+ @click="handleSearch"
+ :loading="loading"
+ >
+ 鏌ヨ
+ </el-button>
+ <el-button icon="el-icon-refresh" @click="handleReset">
+ 閲嶇疆
+ </el-button>
+ </el-form-item>
+ </el-form>
+ </el-card>
+ </div>
+
+ <!-- 婊℃剰搴︾被鍨嬬粺璁″浘琛� -->
+ <div class="chart-section">
+ <el-card shadow="never">
+ <div class="chart-container">
+ <div class="chart-header">
+ <h3 class="chart-title">婊℃剰搴︾被鍨嬪~鎶ユ瘮渚嬬粺璁�</h3>
+ <div class="statistic-info">
+ <div class="statistic-item">
+ <span class="statistic-label">鍙戦�侀棶鍗锋�婚噺锛�</span>
+ <span class="statistic-value">{{
+ totalSendCount.toLocaleString()
+ }}</span>
+ </div>
+ <div class="statistic-item">
+ <span class="statistic-label">鍥炴敹闂嵎鎬婚噺锛�</span>
+ <span class="statistic-value">{{
+ totalReceiveCount.toLocaleString()
+ }}</span>
+ </div>
+ <div class="statistic-item">
+ <span class="statistic-label">鎬讳綋鍥炴敹鐜囷細</span>
+ <span class="statistic-value">{{
+ formatPercent(overallRecoveryRate)
+ }}</span>
+ </div>
+ </div>
+ </div>
+ <div
+ id="satisfactionBarChart"
+ style="width: 100%; height: 400px"
+ ></div>
+ </div>
+ </el-card>
+ </div>
+
+ <!-- Tab鏍囩椤� -->
+ <div class="tab-section">
+ <el-card shadow="never">
+ <el-tabs v-model="activeTab" @tab-click="handleTabClick">
+ <el-tab-pane label="棰樼洰鏄庣粏缁熻" name="questionDetail">
+ <!-- 棰樼洰鏄庣粏琛ㄦ牸 -->
+ <div class="detail-table-section">
+ <el-table
+ v-loading="detailLoading"
+ :data="questionDetailData"
+ :border="true"
+ style="width: 100%"
+ row-class-name="question-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="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="answerCount"
+ align="center"
+ width="100"
+ />
+
+ <el-table-column
+ label="鏈瓟棰樹汉鏁�"
+ prop="unanswerCount"
+ align="center"
+ width="100"
+ >
+ <template slot-scope="{ row }">
+ {{ row.noAnswerPerson }}
+ </template>
+ </el-table-column>
+
+ <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="summary-row">
+ <div class="summary-content">
+ <div class="summary-item">
+ <span class="label">鎬荤瓟棰樹汉鏁帮細</span>
+ <span class="value">{{ totalAnswerCount }}</span>
+ </div>
+ <div class="summary-item">
+ <span class="label">鎬荤瓟棰樼巼锛�</span>
+ <span class="value">{{
+ formatPercent(totalAnswerRate)
+ }}</span>
+ </div>
+ </div>
+ </div>
+
+ <!-- 鍒嗛〉 -->
+ <div
+ class="pagination-section"
+ v-if="questionDetailData.length > 0"
+ >
+ <el-pagination
+ background
+ layout="total, sizes, prev, pager, next, jumper"
+ :current-page="detailQueryParams.pageNum"
+ :page-size="detailQueryParams.pageSize"
+ :page-sizes="[10, 20, 30]"
+ :total="detailTotal"
+ @size-change="handleDetailSizeChange"
+ @current-change="handleDetailPageChange"
+ />
+ </div>
+ </div>
+ </el-tab-pane>
+
+ <el-tab-pane label="鍚勭被鍨嬬粺璁℃槑缁�" name="typeDetail">
+ <!-- 鍚勭被鍨嬬粺璁℃槑缁嗚〃鏍� -->
+ <div class="type-detail-section">
+ <el-table
+ v-loading="typeDetailLoading"
+ :data="typeDetailData"
+ :border="true"
+ style="width: 100%"
+ class="type-detail-table"
+ >
+ <el-table-column
+ label="搴忓彿"
+ type="index"
+ align="center"
+ width="60"
+ />
+
+ <el-table-column
+ label="婊℃剰搴︾被鍨�"
+ prop="typeName"
+ align="center"
+ min-width="150"
+ >
+ <template slot-scope="{ row }">
+ <div class="type-name-cell">
+ <span class="type-name">{{ row.typeName }}</span>
+ <el-tag
+ v-if="row.isSpecial"
+ type="warning"
+ size="mini"
+ style="margin-left: 5px"
+ >
+ 鐗规畩
+ </el-tag>
+ </div>
+ </template>
+ </el-table-column>
+
+ <el-table-column
+ label="鍙戦�侀棶鍗锋暟"
+ prop="sendCount"
+ align="center"
+ width="120"
+ >
+ <template slot-scope="{ row }">
+ <span class="number-text">{{
+ row.sendCount.toLocaleString()
+ }}</span>
+ </template>
+ </el-table-column>
+
+ <el-table-column
+ label="鍥炴敹闂嵎鏁�"
+ prop="receiveCount"
+ align="center"
+ width="120"
+ >
+ <template slot-scope="{ row }">
+ <span class="number-text">{{
+ row.receiveCount.toLocaleString()
+ }}</span>
+ </template>
+ </el-table-column>
+
+ <el-table-column
+ label="鍥炴敹鐜�"
+ prop="recoveryRate"
+ align="center"
+ width="120"
+ >
+ <template slot-scope="{ row }">
+ <span
+ class="rate-text"
+ :class="getRateClass(row.recoveryRate)"
+ >
+ {{ formatPercent(row.recoveryRate) }}
+ </span>
+ </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="satisfactionLevel"
+ align="center"
+ width="120"
+ >
+ <template slot-scope="{ row }">
+ <el-tag
+ :type="getLevelTagType(row.satisfactionLevel)"
+ effect="dark"
+ size="small"
+ >
+ {{ row.satisfactionLevel }}
+ </el-tag>
+ </template>
+ </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"
+ fixed="right"
+ >
+ <template slot-scope="{ row }">
+ <el-button
+ type="text"
+ size="small"
+ @click="handleTypeDetail(row)"
+ >
+ 璇︽儏
+ </el-button>
+ <el-button
+ type="text"
+ size="small"
+ @click="handleExportData(row)"
+ >
+ 瀵煎嚭
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+
+ <!-- 绫诲瀷缁熻姹囨�� -->
+ <div class="type-summary-row">
+ <div class="type-summary-content">
+ <div class="type-summary-item">
+ <span class="label">绫诲瀷鎬绘暟锛�</span>
+ <span class="value">{{ typeDetailData.length }}</span>
+ </div>
+ <div class="type-summary-item">
+ <span class="label">骞冲潎鍥炴敹鐜囷細</span>
+ <span class="value">{{
+ formatPercent(averageRecoveryRate)
+ }}</span>
+ </div>
+ <div class="type-summary-item">
+ <span class="label">绫诲瀷骞冲潎鍒嗭細</span>
+ <span class="value">{{ averageTypeScore.toFixed(1) }}</span>
+ </div>
+ <div class="type-summary-item">
+ <span class="label">楂樻弧鎰忓害绫诲瀷锛�</span>
+ <span class="value high-count"
+ >{{ highSatisfactionCount }} 涓�</span
+ >
+ </div>
+ </div>
+ </div>
+ </div>
+ </el-tab-pane>
+ </el-tabs>
+ </el-card>
+ </div>
+ </div>
+</template>
+
+<script>
+import * as echarts from "echarts";
+import { statistics, satisfactionGraph } from "@/api/system/user";
+import store from "@/store";
+
+export default {
+ name: "SatisfactionStatistics",
+ data() {
+ return {
+ // 鏌ヨ鍙傛暟
+ queryParams: {
+ type: 2,
+ patientSource: "",
+ deptCode: "",
+ wardCode: "",
+ dateRange: [],
+ },
+
+ // 褰撳墠婵�娲荤殑tab
+ activeTab: "questionDetail",
+
+ // 鎮h�呮潵婧愰�夐」
+ patientSourceList: [
+ { value: "1", label: "闂ㄨ瘖" },
+ { value: "2", label: "浣忛櫌" },
+ { value: "3", label: "鎬ヨ瘖" },
+ { value: "4", label: "鍑洪櫌" },
+ ],
+
+ // 绉戝鍒楄〃
+ deptList: [],
+
+ // 鐥呭尯鍒楄〃
+ wardList: [],
+
+ // 鍥捐〃瀹炰緥
+ barChart: null,
+
+ // 鍔犺浇鐘舵��
+ loading: false,
+ detailLoading: false,
+ typeDetailLoading: false,
+
+ // 棰樼洰鏄庣粏鏁版嵁
+ questionDetailData: [],
+
+ // 棰樼洰鏄庣粏鏌ヨ鍙傛暟
+ detailQueryParams: {
+ pageNum: 1,
+ pageSize: 10,
+ },
+
+ // 棰樼洰鏄庣粏鎬绘暟
+ detailTotal: 0,
+
+ // 缁煎悎寰楀垎
+ totalScore: 0,
+ totalAnswerCount: 0,
+ totalAnswerRate: 0,
+
+ // 鍚勭被鍨嬬粺璁℃槑缁嗘暟鎹�
+ typeDetailData: [],
+
+ // 鏌辩姸鍥炬暟鎹�
+ chartData: [],
+
+ // 缁熻淇℃伅
+ totalSendCount: 0,
+ totalReceiveCount: 0,
+ overallRecoveryRate: 0,
+
+ // 绫诲瀷缁熻姹囨��
+ averageRecoveryRate: 0,
+ averageTypeScore: 0,
+ highSatisfactionCount: 0,
+
+ // 鏃ユ湡閫夋嫨鍣ㄩ�夐」
+ pickerOptions: {
+ shortcuts: [
+ {
+ text: "鏈�杩戜竴鍛�",
+ onClick(picker) {
+ const end = new Date();
+ const start = new Date();
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+ picker.$emit("pick", [start, end]);
+ },
+ },
+ {
+ text: "鏈�杩戜竴涓湀",
+ onClick(picker) {
+ const end = new Date();
+ const start = new Date();
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+ picker.$emit("pick", [start, end]);
+ },
+ },
+ {
+ text: "鏈�杩戜笁涓湀",
+ onClick(picker) {
+ const end = new Date();
+ const start = new Date();
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+ picker.$emit("pick", [start, end]);
+ },
+ },
+ ],
+ disabledDate(time) {
+ return time.getTime() > Date.now();
+ },
+ },
+
+ // 婊℃剰搴︾被鍨嬫暟鎹�
+ satisfactionTypes: [
+ { id: 401, name: "鍑洪櫌婊℃剰搴�", color: "#36B37E" },
+ { id: 402, name: "浣忛櫌婊℃剰搴�", color: "#4CAF50" },
+ { id: 403, name: "闂ㄨ瘖婊℃剰搴�", color: "#409EFF" },
+ { id: 404, name: "甯哥敤婊℃剰搴�", color: "#FF9D4D" },
+ ],
+
+ // 鏂板锛氶粯璁ゆ湇鍔$被鍨嬫暟缁�
+ defaultServiceTypes: ["6", "14", "15", "16"],
+
+ // 鏂板锛氬熀纭�妯℃澘闂ID闆嗗悎
+ scriptIds: [],
+
+ // 鏂板锛氭ā鏉縄D
+ templateId: null,
+ };
+ },
+
+ computed: {
+ // 璁$畻鏌ヨ寮�濮嬫椂闂�
+ startTime() {
+ if (this.queryParams.dateRange && this.queryParams.dateRange[0]) {
+ return this.queryParams.dateRange[0];
+ }
+ // 榛樿鏈�杩�7澶�
+ const date = new Date();
+ date.setDate(date.getDate() - 7);
+ return this.formatDate(date);
+ },
+
+ // 璁$畻鏌ヨ缁撴潫鏃堕棿
+ endTime() {
+ if (this.queryParams.dateRange && this.queryParams.dateRange[1]) {
+ return this.queryParams.dateRange[1];
+ }
+ // 榛樿浠婂ぉ
+ return this.formatDate(new Date());
+ },
+
+ // 璁$畻绉戝缂栫爜鏁扮粍
+ deptCodes() {
+ if (this.queryParams.deptCode) {
+ return [this.queryParams.deptCode];
+ }
+ return this.deptList.map((dept) => dept.value);
+ },
+
+ // 璁$畻鐥呭尯缂栫爜鏁扮粍
+ hospitalDistrictCodes() {
+ if (this.queryParams.wardCode) {
+ return [this.queryParams.wardCode];
+ }
+ return this.wardList.map((ward) => ward.value);
+ },
+ },
+
+ mounted() {
+ this.initData();
+ },
+
+ beforeDestroy() {
+ if (this.barChart) {
+ this.barChart.dispose();
+ this.barChart = null;
+ }
+ },
+
+ methods: {
+ // 鏍煎紡鍖栨棩鏈�
+ formatDate(date) {
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, "0");
+ const day = String(date.getDate()).padStart(2, "0");
+ return `${year}-${month}-${day}`;
+ },
+
+ // 鍒濆鍖栨暟鎹�
+ async initData() {
+ await this.getDeptList();
+ await this.getWardList();
+ this.initChart();
+ await this.loadData();
+ },
+
+ // 鑾峰彇绉戝鍒楄〃
+ getDeptList() {
+ return new Promise((resolve) => {
+ this.deptList = (this.$store.getters.belongDepts || []).map((dept) => {
+ return {
+ label: dept.deptName,
+ value: dept.deptCode,
+ };
+ });
+ resolve();
+ });
+ },
+
+ // 鑾峰彇鐥呭尯鍒楄〃
+ getWardList() {
+ return new Promise((resolve) => {
+ this.wardList = (this.$store.getters.belongWards || []).map((ward) => {
+ return {
+ label: ward.districtName,
+ value: ward.districtCode,
+ };
+ });
+ resolve();
+ });
+ },
+
+ // 鍔犺浇鏁版嵁
+ async loadData() {
+ await Promise.all([
+ this.loadChartData(),
+ this.loadQuestionDetailData(),
+ this.loadTypeDetailData(),
+ ]);
+ },
+
+ // 鍔犺浇鍥捐〃鏁版嵁
+ 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);
+ } else {
+ this.$message.error(response.msg || "鑾峰彇鍥捐〃鏁版嵁澶辫触");
+ // 浣跨敤mock鏁版嵁
+ await this.generateMockChartData();
+ }
+ } catch (error) {
+ console.error("鑾峰彇鍥捐〃鏁版嵁鍑洪敊:", error);
+ this.$message.error("鑾峰彇鍥捐〃鏁版嵁澶辫触");
+ // 閿欒鏃朵娇鐢╩ock鏁版嵁
+ await this.generateMockChartData();
+ } finally {
+ this.loading = false;
+ }
+ },
+
+ // 澶勭悊鍥捐〃鏁版嵁
+ processChartData(apiData) {
+ if (!apiData || !apiData.rows || Object.keys(apiData.rows).length === 0) {
+ this.chartData = [];
+ this.totalSendCount = 0;
+ this.totalReceiveCount = 0;
+ this.overallRecoveryRate = 0;
+ this.renderChart([]);
+ return;
+ }
+
+ const chartData = [];
+ let totalSend = 0;
+ let totalReceive = 0;
+ let index = 0;
+
+ // 澶勭悊鎺ュ彛杩斿洖鐨勬弧鎰忓害绫诲瀷缁熻
+ Object.entries(apiData.rows).forEach(([typeName, typeStat]) => {
+ const sendCount = typeStat.subidAll || 0;
+ const receiveCount = typeStat.fillCountAll || 0;
+ const recoveryRate = typeStat.receiveRate || 0;
+
+ chartData.push({
+ name: typeName,
+ value: recoveryRate * 100, // 杞崲涓虹櫨鍒嗘瘮
+ sendCount: sendCount,
+ receiveCount: receiveCount,
+ averageScore: typeStat.averageScore || 0,
+ 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);
+ },
+
+ // 鍔犺浇棰樼洰鏄庣粏鏁版嵁
+ async loadQuestionDetailData() {
+ this.detailLoading = true;
+ try {
+ const params = {
+ type: this.queryParams.type,
+ startTime: this.startTime,
+ endTime: this.endTime,
+ scriptids: this.scriptIds,
+ templateid: this.templateId,
+ };
+
+ const response = await statistics(params);
+
+ if (response.code === 200) {
+ this.processQuestionDetailData(response.rows);
+ } else {
+ this.$message.error(response.msg || "鑾峰彇棰樼洰鏄庣粏鏁版嵁澶辫触");
+ const mockData = await this.generateMockQuestionDetail();
+ this.questionDetailData = mockData.list;
+ this.detailTotal = mockData.total;
+ this.calculateSummary(mockData);
+ }
+ } catch (error) {
+ console.error("鑾峰彇棰樼洰鏄庣粏鏁版嵁鍑洪敊:", error);
+ this.$message.error("鑾峰彇棰樼洰鏄庣粏鏁版嵁澶辫触");
+ const mockData = await this.generateMockQuestionDetail();
+ this.questionDetailData = mockData.list;
+ this.detailTotal = mockData.total;
+ this.calculateSummary(mockData);
+ } finally {
+ this.detailLoading = false;
+ }
+ },
+
+ // 澶勭悊鎺ュ彛杩斿洖鐨勯鐩槑缁嗘暟鎹�
+ processQuestionDetailData(apiData) {
+ if (!apiData || !apiData.patSatisfactionDetailEntities) {
+ this.questionDetailData = [];
+ this.detailTotal = 0;
+ this.totalAnswerCount = 0;
+ this.totalAnswerRate = 0;
+ 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 || "",
+ 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,
+ };
+ });
+
+ const startIndex =
+ (this.detailQueryParams.pageNum - 1) * this.detailQueryParams.pageSize;
+ const endIndex = startIndex + this.detailQueryParams.pageSize;
+ const paginatedData = detailData.slice(startIndex, endIndex);
+
+ this.questionDetailData = paginatedData;
+ this.detailTotal = detailData.length;
+ this.totalAnswerCount = apiData.totalPerson || 0;
+ this.totalAnswerRate = apiData.totalAnswerRate || 0;
+ },
+
+ // 鍔犺浇绫诲瀷鏄庣粏鏁版嵁
+ async loadTypeDetailData() {
+ this.typeDetailLoading = 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.processTypeDetailData(response.data);
+ } else {
+ this.$message.error(response.msg || "鑾峰彇绫诲瀷鏄庣粏鏁版嵁澶辫触");
+ const mockData = await this.generateMockTypeDetail();
+ this.typeDetailData = mockData;
+ this.calculateTypeSummary(mockData);
+ }
+ } catch (error) {
+ console.error("鑾峰彇绫诲瀷鏄庣粏鏁版嵁鍑洪敊:", error);
+ this.$message.error("鑾峰彇绫诲瀷鏄庣粏鏁版嵁澶辫触");
+ const mockData = await this.generateMockTypeDetail();
+ this.typeDetailData = mockData;
+ this.calculateTypeSummary(mockData);
+ } finally {
+ this.typeDetailLoading = false;
+ }
+ },
+
+ // 澶勭悊绫诲瀷鏄庣粏鏁版嵁
+ processTypeDetailData(apiData) {
+ if (!apiData || !apiData.rows || Object.keys(apiData.rows).length === 0) {
+ this.typeDetailData = [];
+ this.calculateTypeSummary([]);
+ return;
+ }
+
+ const typeDetail = [];
+ Object.entries(apiData.rows).forEach(([typeName, typeStat], index) => {
+ const sendCount = typeStat.subidAll || 0;
+ const receiveCount = typeStat.fillCountAll || 0;
+ const recoveryRate = typeStat.receiveRate || 0;
+ const averageScore = typeStat.averageScore || 0;
+
+ typeDetail.push({
+ id: index + 1,
+ typeName: typeName,
+ isSpecial: false, // 鏍规嵁瀹為檯鎯呭喌鍒ゆ柇
+ sendCount: sendCount,
+ receiveCount: receiveCount,
+ recoveryRate: recoveryRate,
+ averageScore: averageScore,
+ maxScore: 5, // 榛樿鍊�
+ minScore: 0, // 榛樿鍊�
+ satisfactionLevel: this.getSatisfactionLevel(averageScore),
+ trend: "stable", // 榛樿绋冲畾
+ });
+ });
+
+ this.typeDetailData = typeDetail;
+ this.calculateTypeSummary(typeDetail);
+ },
+
+ // 鏍规嵁骞冲潎鍒嗚幏鍙栨弧鎰忓害绛夌骇
+ getSatisfactionLevel(score) {
+ if (score >= 4.5) return "浼樼";
+ 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";
+ },
+
+ // 璁$畻缁煎悎寰楀垎
+ calculateSummary(data) {
+ let totalScore = 0;
+ let totalAnswerCount = 0;
+ let totalCount = 0;
+
+ data.list.forEach((item) => {
+ totalScore += item.averageScore;
+ totalAnswerCount += item.answerCount;
+ totalCount += item.totalCount;
+ });
+
+ this.totalScore =
+ data.list.length > 0 ? totalScore / data.list.length : 0;
+ this.totalAnswerCount = totalAnswerCount;
+ this.totalAnswerRate = totalCount > 0 ? totalAnswerCount / totalCount : 0;
+ },
+
+ // 璁$畻绫诲瀷缁熻姹囨��
+ calculateTypeSummary(data) {
+ if (data.length === 0) {
+ this.averageRecoveryRate = 0;
+ this.averageTypeScore = 0;
+ this.highSatisfactionCount = 0;
+ return;
+ }
+
+ let totalRecoveryRate = 0;
+ let totalTypeScore = 0;
+ let highCount = 0;
+
+ data.forEach((item) => {
+ totalRecoveryRate += item.recoveryRate;
+ totalTypeScore += item.averageScore;
+ if (
+ item.satisfactionLevel === "浼樼" ||
+ item.satisfactionLevel === "鑹ソ"
+ ) {
+ highCount++;
+ }
+ });
+
+ this.averageRecoveryRate = totalRecoveryRate / data.length;
+ this.averageTypeScore = totalTypeScore / data.length;
+ this.highSatisfactionCount = highCount;
+ },
+
+ // 鍒濆鍖栧浘琛�
+ initChart() {
+ const chartDom = document.getElementById("satisfactionBarChart");
+ if (!chartDom) return;
+
+ this.barChart = echarts.init(chartDom);
+ window.addEventListener("resize", this.handleChartResize);
+ },
+
+ // 娓叉煋鍥捐〃
+ renderChart(chartData) {
+ 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;
+ }
+ const option = {
+ title: {
+ text: "",
+ left: "center",
+ },
+ tooltip: {
+ trigger: "axis",
+ 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>
+ `;
+ },
+ },
+ grid: {
+ left: "3%",
+ right: "4%",
+ bottom: "3%",
+ top: 60,
+ containLabel: true,
+ },
+ xAxis: {
+ type: "category",
+ data: chartData.map((item) => item.name),
+ axisLabel: {
+ interval: 0,
+ rotate: 0,
+ fontSize: 12,
+ color: "#666",
+ },
+ axisLine: {
+ lineStyle: {
+ color: "#DCDFE6",
+ },
+ },
+ axisTick: {
+ alignWithLabel: true,
+ },
+ },
+ yAxis: {
+ type: "value",
+ name: "濉姤姣斾緥 (%)",
+ min: 0,
+ max: 100,
+ axisLabel: {
+ formatter: "{value}%",
+ color: "#666",
+ },
+ axisLine: {
+ lineStyle: {
+ color: "#DCDFE6",
+ },
+ },
+ splitLine: {
+ lineStyle: {
+ type: "dashed",
+ color: "#E4E7ED",
+ },
+ },
+ },
+ series: [
+ {
+ name: "濉姤姣斾緥",
+ type: "bar",
+ barWidth: 40,
+ data: chartData,
+ itemStyle: {
+ color: (params) => {
+ return params.data.itemStyle.color;
+ },
+ },
+ label: {
+ show: true,
+ position: "top",
+ formatter: "{c}%",
+ fontSize: 12,
+ color: "#333",
+ },
+ },
+ ],
+ };
+
+ this.barChart.setOption(option);
+ },
+
+ // 鐢熸垚Mock鍥捐〃鏁版嵁
+ generateMockChartData() {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ const data = this.satisfactionTypes.map((type, index) => ({
+ name: type.name,
+ recoveryRate: Math.random() * 0.3 + 0.6,
+ sendCount: Math.floor(Math.random() * 3000) + 1500,
+ receiveCount: 0,
+ color: type.color,
+ }));
+
+ 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,
+ itemStyle: { color: item.color },
+ }));
+
+ this.chartData = chartData;
+ this.renderChart(chartData);
+ resolve();
+ }, 300);
+ });
+ },
+
+ // 鐢熸垚Mock棰樼洰璇︽儏鏁版嵁
+ generateMockQuestionDetail() {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ const questions = [
+ {
+ scriptContent: "鎮ㄥ鏈灏卞尰鐨勬暣浣撴弧鎰忕▼搴︼紵",
+ 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,
+ },
+ {
+ optionText: "涓嶆弧鎰�",
+ chosenQuantity: 6,
+ chosenPercentage: 0.05,
+ },
+ ],
+ },
+ {
+ scriptContent: "鎮ㄥ鍖绘姢浜哄憳鐨勬湇鍔℃�佸害鏄惁婊℃剰锛�",
+ 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,
+ },
+ {
+ optionText: "涓嶆弧鎰�",
+ chosenQuantity: 2,
+ chosenPercentage: 0.01,
+ },
+ {
+ optionText: "闈炲父涓嶆弧鎰�",
+ chosenQuantity: 1,
+ chosenPercentage: 0.01,
+ },
+ ],
+ },
+ ];
+
+ const startIndex =
+ (this.detailQueryParams.pageNum - 1) *
+ this.detailQueryParams.pageSize;
+ const endIndex = startIndex + this.detailQueryParams.pageSize;
+ const paginatedData = questions.slice(startIndex, endIndex);
+
+ resolve({
+ list: paginatedData,
+ total: questions.length,
+ });
+ }, 300);
+ });
+ },
+
+ // 鐢熸垚Mock绫诲瀷鏄庣粏鏁版嵁
+ generateMockTypeDetail() {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ const types = [
+ {
+ id: 401,
+ typeName: "鍑洪櫌婊℃剰搴�",
+ isSpecial: false,
+ sendCount: 2850,
+ receiveCount: 2680,
+ recoveryRate: 0.94,
+ averageScore: 4.8,
+ maxScore: 5,
+ minScore: 3.8,
+ satisfactionLevel: "浼樼",
+ trend: "up",
+ },
+ {
+ id: 402,
+ typeName: "浣忛櫌婊℃剰搴�",
+ isSpecial: false,
+ sendCount: 2620,
+ receiveCount: 2405,
+ recoveryRate: 0.918,
+ averageScore: 4.6,
+ maxScore: 5,
+ minScore: 3.5,
+ satisfactionLevel: "浼樼",
+ trend: "stable",
+ },
+ {
+ id: 403,
+ typeName: "闂ㄨ瘖婊℃剰搴�",
+ isSpecial: false,
+ sendCount: 3780,
+ receiveCount: 3220,
+ recoveryRate: 0.852,
+ averageScore: 4.3,
+ maxScore: 5,
+ minScore: 2.5,
+ satisfactionLevel: "鑹ソ",
+ trend: "up",
+ },
+ {
+ id: 404,
+ typeName: "甯哥敤婊℃剰搴�",
+ isSpecial: true,
+ sendCount: 1950,
+ receiveCount: 1780,
+ recoveryRate: 0.913,
+ averageScore: 4.5,
+ maxScore: 5,
+ minScore: 3.2,
+ satisfactionLevel: "鑹ソ",
+ trend: "stable",
+ },
+ ];
+
+ resolve(types);
+ }, 300);
+ });
+ },
+
+ // 鑾峰彇鍥捐〃棰滆壊
+ getChartColor(index) {
+ const colors = [
+ "#36B37E",
+ "#4CAF50",
+ "#409EFF",
+ "#FF9D4D",
+ "#9B8DFF",
+ "#FF6B6B",
+ ];
+ return colors[index % colors.length];
+ },
+
+ // 澶勭悊鍥捐〃鍝嶅簲寮�
+ handleChartResize() {
+ if (this.barChart) {
+ this.barChart.resize();
+ }
+ },
+
+ // 澶勭悊鏌ヨ
+ handleSearch() {
+ this.detailQueryParams.pageNum = 1;
+ this.loadData();
+ // 寮哄埗閲嶆柊娓叉煋鍥捐〃
+ setTimeout(() => {
+ if (this.chartData.length === 0) {
+ this.renderChart([]);
+ }
+ }, 100);
+ },
+
+ // 澶勭悊閲嶇疆
+ handleReset() {
+ this.$refs.queryForm.resetFields();
+ this.queryParams.dateRange = [];
+ this.detailQueryParams.pageNum = 1;
+ this.loadData();
+ },
+
+ // 澶勭悊Tab鍒囨崲
+ handleTabClick(tab) {
+ if (tab.name === "typeDetail" && this.typeDetailData.length === 0) {
+ this.loadTypeDetailData();
+ }
+ },
+
+ // 澶勭悊鏄庣粏鍒嗛〉澶у皬鍙樺寲
+ handleDetailSizeChange(size) {
+ this.detailQueryParams.pageSize = size;
+ this.detailQueryParams.pageNum = 1;
+ this.loadQuestionDetailData();
+ },
+
+ // 澶勭悊鏄庣粏椤电爜鍙樺寲
+ handleDetailPageChange(page) {
+ this.detailQueryParams.pageNum = page;
+ this.loadQuestionDetailData();
+ },
+
+ // 澶勭悊绫诲瀷璇︽儏
+ handleTypeDetail(row) {
+ this.$message.info(`鏌ョ湅绫诲瀷璇︽儏锛�${row.typeName}`);
+ },
+
+ // 澶勭悊瀵煎嚭鏁版嵁
+ handleExportData(row) {
+ this.$message.success(`姝e湪瀵煎嚭 ${row.typeName} 鏁版嵁...`);
+ },
+
+ // 鏍煎紡鍖栫櫨鍒嗘瘮
+ 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)}%`;
+ },
+
+ // 鑾峰彇鍥炴敹鐜囨牱寮忕被
+ getRateClass(rate) {
+ if (rate >= 0.9) return "rate-high";
+ if (rate >= 0.8) return "rate-medium";
+ return "rate-low";
+ },
+
+ // 鑾峰彇婊℃剰搴︾瓑绾ф爣绛剧被鍨�
+ getLevelTagType(level) {
+ const levelMap = {
+ 浼樼: "success",
+ 鑹ソ: "primary",
+ 涓�鑸�: "warning",
+ 杈冨樊: "danger",
+ 宸�: "info",
+ };
+ return levelMap[level] || "info";
+ },
+
+ // 澶勭悊绉戝閫夋嫨鍙樺寲
+ handleDeptChange() {
+ this.loadData();
+ },
+
+ // 澶勭悊鐥呭尯閫夋嫨鍙樺寲
+ handleWardChange() {
+ this.loadData();
+ },
+ },
+};
+</script>
+
+<style lang="scss" scoped>
+.satisfaction-statistics {
+ padding: 20px;
+ background-color: #f5f7fa;
+ min-height: 100vh;
+
+ .query-section {
+ margin-bottom: 20px;
+
+ .query-form {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+
+ ::v-deep .el-form-item {
+ margin-bottom: 0;
+ margin-right: 20px;
+
+ &:last-child {
+ margin-right: 0;
+ }
+ }
+ }
+ }
+
+ .chart-section {
+ margin-bottom: 20px;
+
+ .chart-container {
+ .chart-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ padding-bottom: 15px;
+ border-bottom: 1px solid #f0f0f0;
+
+ .chart-title {
+ margin: 0;
+ font-size: 16px;
+ font-weight: 600;
+ color: #303133;
+ }
+
+ .statistic-info {
+ display: flex;
+ gap: 30px;
+ align-items: center;
+
+ .statistic-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ .statistic-label {
+ font-size: 14px;
+ color: #606266;
+ }
+
+ .statistic-value {
+ font-size: 18px;
+ font-weight: 600;
+ color: #409eff;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ .tab-section {
+ ::v-deep .el-tabs__header {
+ margin-bottom: 0;
+ }
+
+ ::v-deep .el-tabs__content {
+ padding: 20px 0 0 0;
+ }
+ }
+
+ .detail-table-section {
+ .option-detail {
+ padding: 15px;
+ background: #f8f9fa;
+ border-radius: 4px;
+ margin: 10px 0;
+ }
+
+ ::v-deep .el-table {
+ th {
+ background-color: #f8f9fa;
+ font-weight: 600;
+ color: #333;
+ padding: 12px 0;
+ }
+
+ td {
+ padding: 12px 0;
+ }
+
+ .question-row {
+ td {
+ background-color: #fff;
+ }
+
+ &:hover {
+ td {
+ background-color: #f5f7fa;
+ }
+ }
+ }
+ }
+
+ .score-text {
+ font-weight: 600;
+ color: #1890ff;
+ font-size: 16px;
+ }
+
+ .summary-row {
+ margin-top: 20px;
+ padding: 20px;
+ background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
+ border-radius: 8px;
+ border: 1px solid #dee2e6;
+
+ .summary-content {
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+
+ .summary-item {
+ text-align: center;
+
+ .label {
+ font-size: 16px;
+ color: #606266;
+ margin-right: 8px;
+ }
+
+ .value {
+ font-size: 24px;
+ font-weight: 600;
+ color: #409eff;
+ }
+ }
+ }
+ }
+
+ .pagination-section {
+ display: flex;
+ justify-content: center;
+ padding: 20px 0 0 0;
+ }
+ }
+
+ .type-detail-section {
+ .type-detail-table {
+ ::v-deep .el-table__header-wrapper {
+ th {
+ background-color: #f0f7ff;
+ font-weight: 600;
+ color: #333;
+ }
+ }
+
+ .type-name-cell {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ .type-name {
+ font-weight: 500;
+ }
+ }
+
+ .number-text {
+ font-weight: 600;
+ color: #333;
+ }
+
+ .rate-text {
+ font-weight: 600;
+ font-size: 14px;
+
+ &.rate-high {
+ color: #67c23a;
+ }
+
+ &.rate-medium {
+ color: #e6a23c;
+ }
+
+ &.rate-low {
+ color: #f56c6c;
+ }
+ }
+
+ .score-text {
+ font-weight: 600;
+ 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 {
+ margin-top: 20px;
+ padding: 20px;
+ background: linear-gradient(135deg, #f0f9ff 0%, #e6f7ff 100%);
+ border-radius: 8px;
+ border: 1px solid #d0ebff;
+
+ .type-summary-content {
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 20px;
+
+ .type-summary-item {
+ text-align: center;
+ min-width: 150px;
+
+ .label {
+ font-size: 14px;
+ color: #606266;
+ margin-right: 8px;
+ }
+
+ .value {
+ font-size: 20px;
+ font-weight: 600;
+ color: #409eff;
+ }
+
+ .high-count {
+ color: #67c23a;
+ }
+ }
+ }
+ }
+ }
+
+ // 鍐呭眰琛ㄦ牸鏍峰紡
+ .inner-table {
+ ::v-deep .el-table__header-wrapper {
+ th {
+ background-color: #f0f7ff !important;
+ color: #333;
+ font-weight: 600;
+ }
+ }
+
+ ::v-deep .el-table__body-wrapper {
+ tr {
+ background-color: #fff;
+
+ &:hover {
+ background-color: #f5f7fa;
+ }
+ }
+ }
+ }
+}
+
+@media (max-width: 768px) {
+ .satisfaction-statistics {
+ padding: 10px;
+
+ .query-section {
+ .query-form {
+ ::v-deep .el-form-item {
+ width: 100%;
+ margin-right: 0;
+ margin-bottom: 10px;
+ }
+ }
+ }
+
+ .chart-section {
+ .chart-container {
+ .chart-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 15px;
+
+ .statistic-info {
+ width: 100%;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: 10px;
+ }
+ }
+ }
+ }
+
+ .detail-table-section {
+ .summary-content {
+ flex-direction: column;
+ gap: 15px;
+ }
+ }
+
+ .type-detail-section {
+ .type-summary-content {
+ flex-direction: column;
+ gap: 15px;
+ }
+ }
+ }
+}
+</style>
--
Gitblit v1.9.3