From d0c13711e4a03409c28d7a01716218689e52c11c Mon Sep 17 00:00:00 2001
From: WXL (wul) <wl_5969728@163.com>
Date: 星期五, 27 三月 2026 10:44:24 +0800
Subject: [PATCH] 测试完成
---
src/views/Satisfaction/sfstatistics/components/SatisfactionStatistics.vue | 1789 +++++++++++++++++++++++++++++++++++++++--------------------
vue.config.js | 4
2 files changed, 1,187 insertions(+), 606 deletions(-)
diff --git a/src/views/Satisfaction/sfstatistics/components/SatisfactionStatistics.vue b/src/views/Satisfaction/sfstatistics/components/SatisfactionStatistics.vue
index 678ecfd..a6463e6 100644
--- a/src/views/Satisfaction/sfstatistics/components/SatisfactionStatistics.vue
+++ b/src/views/Satisfaction/sfstatistics/components/SatisfactionStatistics.vue
@@ -1,293 +1,566 @@
<template>
<div class="satisfaction-statistics">
+ <!-- 鏌ヨ鏉′欢鍖哄煙 -->
<div class="query-section">
- <el-form
- :model="queryParams"
- ref="queryForm"
- size="medium"
- :inline="true"
- label-width="100px"
- class="query-form"
- >
- <el-form-item label="鎮h�呮潵婧�" prop="patientSource">
- <el-select
- v-model="queryParams.patientSource"
- placeholder="璇烽�夋嫨鎮h�呮潵婧�"
- clearable
- style="width: 200px"
- >
- <el-option
- v-for="source in patientSourceList"
- :key="source.value"
- :label="source.label"
- :value="source.value"
- />
- </el-select>
- </el-form-item>
-
- <el-form-item label="绉戝" prop="deptCode">
- <el-select
- v-model="queryParams.deptCode"
- placeholder="璇烽�夋嫨绉戝"
- clearable
- filterable
- style="width: 200px"
- >
- <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"
- >
- <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>
- </div>
-
- <!-- 婊℃剰搴﹀垎绫荤粺璁″浘琛� -->
- <div class="chart-section">
- <div class="chart-container">
- <div class="chart-title">婊℃剰搴︾被鍨嬬粺璁�</div>
- <div id="satisfactionBarChart" style="width: 100%; height: 400px"></div>
- </div>
- </div>
-
- <!-- 棰樼洰鏄庣粏琛ㄦ牸 -->
- <div class="detail-table-section">
- <div class="section-title">棰樼洰鏄庣粏缁熻</div>
-
- <el-table
- v-loading="detailLoading"
- :data="questionDetailData"
- :border="true"
- style="width: 100%"
- row-class-name="question-row"
- >
- <el-table-column
- type="expand"
- width="60"
+ <el-card shadow="never">
+ <el-form
+ :model="queryParams"
+ ref="queryForm"
+ size="medium"
+ :inline="true"
+ label-width="100px"
+ class="query-form"
>
- <template slot-scope="{ row }">
- <div class="option-detail">
+ <el-form-item label="鎮h�呮潵婧�" prop="patientSource">
+ <el-select
+ v-model="queryParams.patientSource"
+ placeholder="璇烽�夋嫨鎮h�呮潵婧�"
+ clearable
+ style="width: 200px"
+ >
+ <el-option
+ v-for="source in patientSourceList"
+ :key="source.value"
+ :label="source.label"
+ :value="source.value"
+ />
+ </el-select>
+ </el-form-item>
+
+ <el-form-item label="绉戝" prop="deptCode">
+ <el-select
+ v-model="queryParams.deptCode"
+ placeholder="璇烽�夋嫨绉戝"
+ clearable
+ filterable
+ style="width: 200px"
+ >
+ <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"
+ >
+ <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
- :data="row.options"
+ v-loading="detailLoading"
+ :data="questionDetailData"
:border="true"
style="width: 100%"
- class="inner-table"
+ 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="閫夐」"
- prop="optionText"
+ label="搴忓彿"
+ type="index"
align="center"
- min-width="200"
+ width="60"
/>
+
<el-table-column
- label="閫夋嫨浜烘暟"
- prop="chosenQuantity"
+ label="棰樼洰"
+ prop="scriptContent"
align="center"
- min-width="120"
- />
- <el-table-column
- label="閫夋嫨姣斾緥"
- prop="chosenPercentage"
- align="center"
- min-width="120"
+ min-width="300"
>
- <template slot-scope="{ row: option }">
- {{ formatPercent(option.chosenPercentage) }}
+ <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.totalCount - row.answerCount }}
+ </template>
+ </el-table-column>
+
+ <el-table-column
+ label="绛旈鐜�"
+ prop="answerRate"
+ align="center"
+ width="100"
+ >
+ <template slot-scope="{ row }">
+ {{ formatPercent(row.answerCount / row.totalCount) }}
</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">{{ totalScore.toFixed(1) }}</span>
+ </div>
+ <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>
- </template>
- </el-table-column>
+ </el-tab-pane>
- <el-table-column
- label="搴忓彿"
- type="index"
- align="center"
- width="60"
- />
+ <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="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="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="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="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="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="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="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="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="answerCount"
- align="center"
- width="100"
- />
+ <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="unanswerCount"
- align="center"
- width="100"
- >
- <template slot-scope="{ row }">
- {{ row.totalCount - row.answerCount }}
- </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="answerRate"
- align="center"
- width="100"
- >
- <template slot-scope="{ row }">
- {{ formatPercent(row.answerCount / row.totalCount) }}
- </template>
- </el-table-column>
- </el-table>
+ <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>
- <!-- 缁煎悎寰楀垎琛� -->
- <div class="summary-row">
- <div class="summary-content">
- <div class="summary-item">
- <span class="label">缁煎悎寰楀垎锛�</span>
- <span class="value">{{ totalScore.toFixed(1) }}</span>
- </div>
- <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>
+ <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>
- <!-- 鍒嗛〉 -->
- <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"
- />
+ <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";
+
export default {
- name: 'SatisfactionStatistics',
+ name: "SatisfactionStatistics",
data() {
return {
// 鏌ヨ鍙傛暟
queryParams: {
- patientSource: '',
- deptCode: '',
- wardCode: '',
- dateRange: []
+ patientSource: "",
+ deptCode: "",
+ wardCode: "",
+ dateRange: [],
},
+
+ // 褰撳墠婵�娲荤殑tab
+ activeTab: "questionDetail",
// 鎮h�呮潵婧愰�夐」
patientSourceList: [
- { value: '1', label: '闂ㄨ瘖' },
- { value: '2', label: '浣忛櫌' },
- { value: '3', label: '鎬ヨ瘖' },
- { value: '4', label: '浣撴' }
+ { value: "1", label: "闂ㄨ瘖" },
+ { value: "2", label: "浣忛櫌" },
+ { value: "3", label: "鎬ヨ瘖" },
+ { value: "4", label: "鍑洪櫌" },
],
// 绉戝鍒楄〃
@@ -302,6 +575,7 @@
// 鍔犺浇鐘舵��
loading: false,
detailLoading: false,
+ typeDetailLoading: false,
// 棰樼洰鏄庣粏鏁版嵁
questionDetailData: [],
@@ -309,7 +583,7 @@
// 棰樼洰鏄庣粏鏌ヨ鍙傛暟
detailQueryParams: {
pageNum: 1,
- pageSize: 10
+ pageSize: 10,
},
// 棰樼洰鏄庣粏鎬绘暟
@@ -320,58 +594,67 @@
totalAnswerCount: 0,
totalAnswerRate: 0,
+ // 鍚勭被鍨嬬粺璁℃槑缁嗘暟鎹�
+ typeDetailData: [],
+
+ // 缁熻淇℃伅
+ totalSendCount: 12560,
+ totalReceiveCount: 10240,
+ overallRecoveryRate: 0,
+
+ // 绫诲瀷缁熻姹囨��
+ averageRecoveryRate: 0,
+ averageTypeScore: 0,
+ highSatisfactionCount: 0,
+
// 鏃ユ湡閫夋嫨鍣ㄩ�夐」
pickerOptions: {
shortcuts: [
{
- text: '鏈�杩戜竴鍛�',
+ text: "鏈�杩戜竴鍛�",
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
- picker.$emit('pick', [start, end]);
- }
+ picker.$emit("pick", [start, end]);
+ },
},
{
- text: '鏈�杩戜竴涓湀',
+ text: "鏈�杩戜竴涓湀",
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
- picker.$emit('pick', [start, end]);
- }
+ picker.$emit("pick", [start, end]);
+ },
},
{
- text: '鏈�杩戜笁涓湀',
+ text: "鏈�杩戜笁涓湀",
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
- picker.$emit('pick', [start, end]);
- }
- }
+ picker.$emit("pick", [start, end]);
+ },
+ },
],
disabledDate(time) {
return time.getTime() > Date.now();
- }
+ },
},
- // Mock鏁版嵁 - 婊℃剰搴﹀垎绫�
- mockSatisfactionCategories: ['鏈嶅姟鎬佸害', '鎶�鏈按骞�', '鐜璁炬柦', '娌熼�氭晥鏋�', '绛夊緟鏃堕棿', '鏀惰垂鍚堢悊鎬�'],
- // Mock鏁版嵁 - 婊℃剰搴︾被鍨�
- mockSatisfactionTypes: [
- { name: '闈炲父婊℃剰', value: 85, color: '#36B37E' },
- { name: '婊℃剰', value: 72, color: '#4CAF50' },
- { name: '涓�鑸�', value: 60, color: '#FF9D4D' },
- { name: '涓嶆弧鎰�', value: 15, color: '#FF5C5C' },
- { name: '闈炲父涓嶆弧鎰�', value: 5, color: '#F44336' }
- ]
+ // 婊℃剰搴︾被鍨嬫暟鎹�
+ satisfactionTypes: [
+ { id: 401, name: "鍑洪櫌婊℃剰搴�", color: "#36B37E" },
+ { id: 402, name: "浣忛櫌婊℃剰搴�", color: "#4CAF50" },
+ { id: 403, name: "闂ㄨ瘖婊℃剰搴�", color: "#409EFF" },
+ { id: 404, name: "甯哥敤婊℃剰搴�", color: "#FF9D4D" },
+ ],
};
},
mounted() {
this.initData();
- this.initChart();
},
beforeDestroy() {
@@ -386,21 +669,21 @@
async initData() {
await this.getDeptList();
await this.getWardList();
+ this.initChart();
await this.loadData();
},
// 鑾峰彇绉戝鍒楄〃
getDeptList() {
- // 妯℃嫙API璋冪敤鑾峰彇绉戝鍒楄〃
return new Promise((resolve) => {
setTimeout(() => {
this.deptList = [
- { value: 'dept001', label: '蹇冭绠″唴绉�' },
- { value: 'dept002', label: '绁炵粡鍐呯' },
- { value: 'dept003', label: '鏅绉�' },
- { value: 'dept004', label: '楠ㄧ' },
- { value: 'dept005', label: '濡囦骇绉�' },
- { value: 'dept006', label: '鍎跨' }
+ { value: "dept001", label: "蹇冭绠″唴绉�" },
+ { value: "dept002", label: "绁炵粡鍐呯" },
+ { value: "dept003", label: "鏅绉�" },
+ { value: "dept004", label: "楠ㄧ" },
+ { value: "dept005", label: "濡囦骇绉�" },
+ { value: "dept006", label: "鍎跨" },
];
resolve();
}, 100);
@@ -409,16 +692,15 @@
// 鑾峰彇鐥呭尯鍒楄〃
getWardList() {
- // 妯℃嫙API璋冪敤鑾峰彇鐥呭尯鍒楄〃
return new Promise((resolve) => {
setTimeout(() => {
this.wardList = [
- { value: 'ward001', label: '鍐呯涓�鐥呭尯' },
- { value: 'ward002', label: '鍐呯浜岀梾鍖�' },
- { value: 'ward003', label: '澶栫涓�鐥呭尯' },
- { value: 'ward004', label: '澶栫浜岀梾鍖�' },
- { value: 'ward005', label: '濡囦骇绉戠梾鍖�' },
- { value: 'ward006', label: '鍎跨鐥呭尯' }
+ { value: "ward001", label: "鍐呯涓�鐥呭尯" },
+ { value: "ward002", label: "鍐呯浜岀梾鍖�" },
+ { value: "ward003", label: "澶栫涓�鐥呭尯" },
+ { value: "ward004", label: "澶栫浜岀梾鍖�" },
+ { value: "ward005", label: "濡囦骇绉戠梾鍖�" },
+ { value: "ward006", label: "鍎跨鐥呭尯" },
];
resolve();
}, 100);
@@ -429,37 +711,51 @@
async loadData() {
await Promise.all([
this.loadChartData(),
- this.loadQuestionDetailData()
+ this.loadQuestionDetailData(),
+ this.loadTypeDetailData(),
]);
},
// 鍔犺浇鍥捐〃鏁版嵁
- loadChartData() {
+ async loadChartData() {
this.loading = true;
- return new Promise((resolve) => {
- setTimeout(() => {
- this.renderChart(this.generateChartData());
- this.loading = false;
- resolve();
- }, 500);
- });
+ try {
+ // 妯℃嫙API璋冪敤
+ const chartData = await this.generateChartData();
+ this.renderChart(chartData);
+
+ // 璁$畻鎬讳綋鍥炴敹鐜�
+ this.overallRecoveryRate = this.totalReceiveCount / this.totalSendCount;
+ } finally {
+ this.loading = false;
+ }
},
// 鍔犺浇棰樼洰鏄庣粏鏁版嵁
- loadQuestionDetailData() {
+ async loadQuestionDetailData() {
this.detailLoading = true;
- return new Promise((resolve) => {
- setTimeout(() => {
- const mockData = this.generateMockQuestionDetail();
- this.questionDetailData = mockData.list;
- this.detailTotal = mockData.total;
+ try {
+ const mockData = await this.generateMockQuestionDetail();
+ this.questionDetailData = mockData.list;
+ this.detailTotal = mockData.total;
+ this.calculateSummary(mockData);
+ } finally {
+ this.detailLoading = false;
+ }
+ },
- // 璁$畻缁煎悎寰楀垎
- this.calculateSummary(mockData);
- this.detailLoading = false;
- resolve();
- }, 500);
- });
+ // 鍔犺浇绫诲瀷鏄庣粏鏁版嵁
+ async loadTypeDetailData() {
+ this.typeDetailLoading = true;
+ try {
+ const mockData = await this.generateMockTypeDetail();
+ this.typeDetailData = mockData;
+
+ // 璁$畻绫诲瀷缁熻姹囨��
+ this.calculateTypeSummary(mockData);
+ } finally {
+ this.typeDetailLoading = false;
+ }
},
// 璁$畻缁煎悎寰楀垎
@@ -468,48 +764,94 @@
let totalAnswerCount = 0;
let totalCount = 0;
- data.list.forEach(item => {
+ 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.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 echarts = require('echarts');
- const chartDom = document.getElementById('satisfactionBarChart');
+ const chartDom = document.getElementById("satisfactionBarChart");
if (!chartDom) return;
this.barChart = echarts.init(chartDom);
-
- // 鐩戝惉绐楀彛鍙樺寲
- window.addEventListener('resize', this.handleChartResize);
+ window.addEventListener("resize", this.handleChartResize);
},
// 鐢熸垚鍥捐〃鏁版嵁
generateChartData() {
- const categories = this.mockSatisfactionCategories;
- const series = this.mockSatisfactionTypes.map(type => ({
- name: type.name,
- type: 'bar',
- barWidth: 25,
- stack: '婊℃剰搴�',
- data: categories.map(() => Math.floor(Math.random() * 20) + 10), // 闅忔満鏁版嵁
- itemStyle: {
- color: type.color
- }
- }));
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ const data = this.satisfactionTypes.map((type) => ({
+ name: type.name,
+ recoveryRate: Math.random() * 0.3 + 0.6, // 60%-90%鐨勫洖鏀剁巼
+ sendCount: Math.floor(Math.random() * 3000) + 1500, // 1500-4500
+ receiveCount: 0,
+ color: type.color,
+ }));
- return {
- categories,
- legend: this.mockSatisfactionTypes.map(type => type.name),
- series
- };
+ // 璁$畻鍥炴敹鏁伴噺
+ 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
+ );
+
+ resolve({
+ data: data.map((item) => ({
+ name: item.name,
+ value: item.recoveryRate * 100, // 杞崲涓虹櫨鍒嗘瘮
+ sendCount: item.sendCount,
+ receiveCount: item.receiveCount,
+ itemStyle: { color: item.color },
+ })),
+ });
+ }, 300);
+ });
},
// 娓叉煋鍥捐〃
@@ -518,92 +860,103 @@
const option = {
title: {
- text: '婊℃剰搴︾被鍨嬬粺璁�',
- left: 'center',
- textStyle: {
- fontSize: 16,
- fontWeight: 'normal',
- color: '#333'
- }
+ text: "",
+ left: "center",
},
tooltip: {
- trigger: 'axis',
+ trigger: "axis",
axisPointer: {
- type: 'shadow'
+ type: "shadow",
},
formatter: (params) => {
- let result = `<div style="margin-bottom: 5px; font-weight: bold;">${params[0].name}</div>`;
- let total = 0;
-
- params.forEach(param => {
- result += `<div style="margin: 2px 0;">
- <span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${param.color};margin-right:5px;"></span>
- ${param.seriesName}: <strong>${param.value}%</strong>
- </div>`;
- total += param.value;
- });
-
- result += `<div style="margin-top: 5px; padding-top: 5px; border-top: 1px solid #eee;">
- <strong>鎬昏: ${total}%</strong>
- </div>`;
- return result;
- }
- },
- legend: {
- data: chartData.legend,
- top: 20,
- textStyle: {
- fontSize: 12,
- color: '#666'
- }
+ 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: 80,
- containLabel: true
+ left: "3%",
+ right: "4%",
+ bottom: "3%",
+ top: 60,
+ containLabel: true,
},
xAxis: {
- type: 'category',
- data: chartData.categories,
+ type: "category",
+ data: chartData.data.map((item) => item.name),
axisLabel: {
interval: 0,
rotate: 0,
fontSize: 12,
- color: '#666'
+ color: "#666",
},
axisLine: {
lineStyle: {
- color: '#DCDFE6'
- }
+ color: "#DCDFE6",
+ },
},
axisTick: {
- alignWithLabel: true
- }
+ alignWithLabel: true,
+ },
},
yAxis: {
- type: 'value',
- name: '鐧惧垎姣� (%)',
+ type: "value",
+ name: "濉姤姣斾緥 (%)",
min: 0,
max: 100,
axisLabel: {
- formatter: '{value}%',
- color: '#666'
+ formatter: "{value}%",
+ color: "#666",
},
axisLine: {
lineStyle: {
- color: '#DCDFE6'
- }
+ color: "#DCDFE6",
+ },
},
splitLine: {
lineStyle: {
- type: 'dashed',
- color: '#E4E7ED'
- }
- }
+ type: "dashed",
+ color: "#E4E7ED",
+ },
+ },
},
- series: chartData.series
+ series: [
+ {
+ name: "濉姤姣斾緥",
+ type: "bar",
+ barWidth: 40,
+ data: chartData.data,
+ itemStyle: {
+ color: (params) => {
+ return params.data.itemStyle.color;
+ },
+ },
+ label: {
+ show: true,
+ position: "top",
+ formatter: "{c}%",
+ fontSize: 12,
+ color: "#333",
+ },
+ },
+ ],
};
this.barChart.setOption(option);
@@ -611,180 +964,196 @@
// 鐢熸垚Mock棰樼洰璇︽儏鏁版嵁
generateMockQuestionDetail() {
- const questions = [
- {
- scriptContent: '鎮ㄥ鍖绘姢浜哄憳鐨勬湇鍔℃�佸害鏄惁婊℃剰',
- scriptType: 1, // 1: 鍗曢�夐, 2: 澶氶�夐
- totalCount: 156,
- answerCount: 145,
- averageScore: 4.5,
- maxScore: 5,
- minScore: 3,
- 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 }
- ]
- },
- {
- scriptContent: '鎮ㄥ鍖荤敓鐨勮瘖鐤楁按骞冲拰鎶�鏈兘鍔涜瘎浠峰浣�',
- scriptType: 1,
- totalCount: 156,
- answerCount: 142,
- averageScore: 4.7,
- maxScore: 5,
- minScore: 3,
- options: [
- { optionText: '闈炲父涓撲笟', chosenQuantity: 95, chosenPercentage: 0.67 },
- { optionText: '姣旇緝涓撲笟', chosenQuantity: 40, chosenPercentage: 0.28 },
- { optionText: '涓�鑸�', chosenQuantity: 5, chosenPercentage: 0.04 },
- { optionText: '涓嶅涓撲笟', chosenQuantity: 2, chosenPercentage: 0.01 },
- { optionText: '闈炲父涓嶄笓涓�', chosenQuantity: 0, chosenPercentage: 0 }
- ]
- },
- {
- scriptContent: '鎮ㄥ鍖婚櫌鐨勭幆澧冨拰鍗敓鐘跺喌鏄惁婊℃剰',
- scriptType: 1,
- totalCount: 156,
- answerCount: 138,
- averageScore: 4.3,
- maxScore: 5,
- minScore: 2,
- options: [
- { optionText: '闈炲父婊℃剰', chosenQuantity: 75, chosenPercentage: 0.54 },
- { optionText: '婊℃剰', chosenQuantity: 50, chosenPercentage: 0.36 },
- { optionText: '涓�鑸�', chosenQuantity: 10, chosenPercentage: 0.07 },
- { optionText: '涓嶆弧鎰�', chosenQuantity: 3, chosenPercentage: 0.02 },
- { optionText: '闈炲父涓嶆弧鎰�', chosenQuantity: 0, chosenPercentage: 0 }
- ]
- },
- {
- scriptContent: '鎮ㄨ涓哄尰鎶や汉鍛樹笌鎮ㄧ殑娌熼�氭槸鍚﹀厖鍒�',
- scriptType: 1,
- totalCount: 156,
- answerCount: 140,
- averageScore: 4.6,
- maxScore: 5,
- minScore: 3,
- options: [
- { optionText: '娌熼�氶潪甯稿厖鍒�', chosenQuantity: 85, chosenPercentage: 0.61 },
- { optionText: '娌熼�氭瘮杈冨厖鍒�', chosenQuantity: 45, chosenPercentage: 0.32 },
- { optionText: '娌熼�氫竴鑸�', chosenQuantity: 8, chosenPercentage: 0.06 },
- { optionText: '娌熼�氫笉澶熷厖鍒�', chosenQuantity: 2, chosenPercentage: 0.01 },
- { optionText: '娌熼�氶潪甯镐笉鍏呭垎', chosenQuantity: 0, chosenPercentage: 0 }
- ]
- },
- {
- scriptContent: '鎮ㄥ绛夊緟灏辫瘖鍜屾不鐤楃殑鏃堕棿鏄惁婊℃剰',
- scriptType: 1,
- totalCount: 156,
- answerCount: 135,
- averageScore: 4.0,
- maxScore: 5,
- minScore: 1,
- options: [
- { optionText: '绛夊緟鏃堕棿寰堢煭', chosenQuantity: 60, chosenPercentage: 0.44 },
- { optionText: '绛夊緟鏃堕棿鍚堢悊', chosenQuantity: 55, chosenPercentage: 0.41 },
- { optionText: '绛夊緟鏃堕棿杈冮暱', chosenQuantity: 15, chosenPercentage: 0.11 },
- { optionText: '绛夊緟鏃堕棿寰堥暱', chosenQuantity: 5, chosenPercentage: 0.04 },
- { optionText: '鏃犳硶蹇嶅彈鐨勭瓑寰�', chosenQuantity: 0, chosenPercentage: 0 }
- ]
- },
- {
- scriptContent: '鎮ㄥ鍖婚櫌鏀惰垂鐨勯�忔槑搴﹀拰鍚堢悊鎬ц瘎浠峰浣�',
- scriptType: 1,
- totalCount: 156,
- answerCount: 130,
- averageScore: 4.2,
- maxScore: 5,
- minScore: 2,
- options: [
- { optionText: '闈炲父閫忔槑鍚堢悊', chosenQuantity: 70, chosenPercentage: 0.54 },
- { optionText: '姣旇緝閫忔槑鍚堢悊', chosenQuantity: 45, chosenPercentage: 0.35 },
- { optionText: '涓�鑸�', chosenQuantity: 10, chosenPercentage: 0.08 },
- { optionText: '涓嶅お閫忔槑', chosenQuantity: 5, chosenPercentage: 0.04 },
- { optionText: '闈炲父涓嶉�忔槑', chosenQuantity: 0, chosenPercentage: 0 }
- ]
- },
- {
- scriptContent: '鎮ㄤ細鍚戜翰鍙嬫帹鑽愭垜浠尰闄㈠悧',
- scriptType: 1,
- totalCount: 156,
- answerCount: 148,
- averageScore: 4.8,
- maxScore: 5,
- minScore: 3,
- options: [
- { optionText: '闈炲父鎰挎剰鎺ㄨ崘', chosenQuantity: 100, chosenPercentage: 0.68 },
- { optionText: '姣旇緝鎰挎剰鎺ㄨ崘', chosenQuantity: 40, chosenPercentage: 0.27 },
- { optionText: '涓�鑸�', chosenQuantity: 6, chosenPercentage: 0.04 },
- { optionText: '涓嶅お鎰挎剰鎺ㄨ崘', chosenQuantity: 2, chosenPercentage: 0.01 },
- { optionText: '缁濆涓嶄細鎺ㄨ崘', chosenQuantity: 0, chosenPercentage: 0 }
- ]
- },
- {
- scriptContent: '鎮ㄥ浠ヤ笅鍝簺鏂归潰姣旇緝婊℃剰锛堝閫夛級',
- scriptType: 2, // 澶氶�夐
- totalCount: 156,
- answerCount: 150,
- averageScore: 4.4,
- maxScore: 5,
- minScore: 3,
- options: [
- { optionText: '鍖荤枟鎶�鏈按骞�', chosenQuantity: 120, chosenPercentage: 0.8 },
- { optionText: '鏈嶅姟鎬佸害', chosenQuantity: 110, chosenPercentage: 0.73 },
- { optionText: '鐜鍗敓', chosenQuantity: 90, chosenPercentage: 0.6 },
- { optionText: '鍖荤枟璁惧', chosenQuantity: 85, chosenPercentage: 0.57 },
- { optionText: '鏀惰垂閫忔槑搴�', chosenQuantity: 70, chosenPercentage: 0.47 },
- { optionText: '绛夊緟鏃堕棿', chosenQuantity: 60, chosenPercentage: 0.4 }
- ]
- },
- {
- scriptContent: '鎮ㄨ涓哄尰闄㈠摢浜涙柟闈㈤渶瑕佹敼杩涳紙澶氶�夛級',
- scriptType: 2,
- totalCount: 156,
- answerCount: 125,
- averageScore: 3.8,
- maxScore: 5,
- minScore: 2,
- options: [
- { optionText: '绛夊緟鏃堕棿杩囬暱', chosenQuantity: 80, chosenPercentage: 0.64 },
- { optionText: '灏辫瘖娴佺▼澶嶆潅', chosenQuantity: 70, chosenPercentage: 0.56 },
- { optionText: '璐圭敤杈冮珮', chosenQuantity: 60, chosenPercentage: 0.48 },
- { optionText: '鍋滆溅鍥伴毦', chosenQuantity: 50, chosenPercentage: 0.4 },
- { optionText: '鎸囧紩鏍囪瘑涓嶆竻', chosenQuantity: 40, chosenPercentage: 0.32 },
- { optionText: '缃戠粶棰勭害涓嶄究', chosenQuantity: 30, chosenPercentage: 0.24 }
- ]
- },
- {
- scriptContent: '鎮ㄥ鏈浣忛櫌鐨勬暣浣撲綋楠岃瘎鍒�',
- scriptType: 1,
- totalCount: 156,
- answerCount: 152,
- averageScore: 4.6,
- maxScore: 5,
- minScore: 3,
- options: [
- { optionText: '5鍒嗭紙闈炲父婊℃剰锛�', chosenQuantity: 90, chosenPercentage: 0.59 },
- { optionText: '4鍒嗭紙婊℃剰锛�', chosenQuantity: 50, chosenPercentage: 0.33 },
- { optionText: '3鍒嗭紙涓�鑸級', chosenQuantity: 10, chosenPercentage: 0.07 },
- { optionText: '2鍒嗭紙涓嶆弧鎰忥級', chosenQuantity: 2, chosenPercentage: 0.01 },
- { optionText: '1鍒嗭紙闈炲父涓嶆弧鎰忥級', chosenQuantity: 0, chosenPercentage: 0 }
- ]
- }
- ];
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ const questions = [
+ {
+ scriptContent: "鎮ㄥ鍖绘姢浜哄憳鐨勬湇鍔℃�佸害鏄惁婊℃剰",
+ scriptType: 1,
+ totalCount: 156,
+ answerCount: 145,
+ averageScore: 4.5,
+ maxScore: 5,
+ minScore: 3,
+ 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,
+ },
+ ],
+ },
+ {
+ scriptContent: "鎮ㄥ鍖荤敓鐨勮瘖鐤楁按骞冲拰鎶�鏈兘鍔涜瘎浠峰浣�",
+ scriptType: 1,
+ totalCount: 156,
+ answerCount: 142,
+ averageScore: 4.7,
+ maxScore: 5,
+ minScore: 3,
+ options: [
+ {
+ optionText: "闈炲父涓撲笟",
+ chosenQuantity: 95,
+ chosenPercentage: 0.67,
+ },
+ {
+ optionText: "姣旇緝涓撲笟",
+ chosenQuantity: 40,
+ chosenPercentage: 0.28,
+ },
+ {
+ optionText: "涓�鑸�",
+ chosenQuantity: 5,
+ chosenPercentage: 0.04,
+ },
+ {
+ optionText: "涓嶅涓撲笟",
+ chosenQuantity: 2,
+ chosenPercentage: 0.01,
+ },
+ {
+ optionText: "闈炲父涓嶄笓涓�",
+ chosenQuantity: 0,
+ chosenPercentage: 0,
+ },
+ ],
+ },
+ {
+ scriptContent: "鎮ㄥ鍖婚櫌鐨勭幆澧冨拰鍗敓鐘跺喌鏄惁婊℃剰",
+ scriptType: 1,
+ totalCount: 156,
+ answerCount: 138,
+ averageScore: 4.3,
+ maxScore: 5,
+ minScore: 2,
+ options: [
+ {
+ optionText: "闈炲父婊℃剰",
+ chosenQuantity: 75,
+ chosenPercentage: 0.54,
+ },
+ {
+ optionText: "婊℃剰",
+ chosenQuantity: 50,
+ chosenPercentage: 0.36,
+ },
+ {
+ optionText: "涓�鑸�",
+ chosenQuantity: 10,
+ chosenPercentage: 0.07,
+ },
+ {
+ optionText: "涓嶆弧鎰�",
+ chosenQuantity: 3,
+ chosenPercentage: 0.02,
+ },
+ {
+ optionText: "闈炲父涓嶆弧鎰�",
+ chosenQuantity: 0,
+ chosenPercentage: 0,
+ },
+ ],
+ },
+ ];
- // 鍒嗛〉澶勭悊
- const startIndex = (this.detailQueryParams.pageNum - 1) * this.detailQueryParams.pageSize;
- const endIndex = startIndex + this.detailQueryParams.pageSize;
- const paginatedData = questions.slice(startIndex, endIndex);
+ const startIndex =
+ (this.detailQueryParams.pageNum - 1) *
+ this.detailQueryParams.pageSize;
+ const endIndex = startIndex + this.detailQueryParams.pageSize;
+ const paginatedData = questions.slice(startIndex, endIndex);
- return {
- list: paginatedData,
- total: questions.length
- };
+ resolve({
+ list: paginatedData,
+ total: questions.length,
+ });
+ }, 300);
+ });
+ },
+
+ // 鐢熸垚Mock绫诲瀷鏄庣粏鏁版嵁
+ generateMockTypeDetail() {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ // 鍦� generateMockTypeDetail 鏂规硶涓浛鎹负锛�
+ 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);
+ });
},
// 澶勭悊鍥捐〃鍝嶅簲寮�
@@ -808,6 +1177,13 @@
this.loadData();
},
+ // 澶勭悊Tab鍒囨崲
+ handleTabClick(tab) {
+ if (tab.name === "typeDetail" && this.typeDetailData.length === 0) {
+ this.loadTypeDetailData();
+ }
+ },
+
// 澶勭悊鏄庣粏鍒嗛〉澶у皬鍙樺寲
handleDetailSizeChange(size) {
this.detailQueryParams.pageSize = size;
@@ -821,80 +1197,131 @@
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 '-';
+ if (value === null || value === undefined) return "-";
const num = parseFloat(value);
- if (isNaN(num)) return '-';
+ if (isNaN(num)) return "-";
return `${(num * 100).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";
+ },
+ },
};
</script>
<style lang="scss" scoped>
.satisfaction-statistics {
+ padding: 20px;
+ background-color: #f5f7fa;
+ min-height: 100vh;
+
.query-section {
- background: #fff;
- padding: 20px;
- border-radius: 4px;
margin-bottom: 20px;
.query-form {
display: flex;
flex-wrap: wrap;
+ align-items: center;
::v-deep .el-form-item {
- margin-bottom: 20px;
+ margin-bottom: 0;
+ margin-right: 20px;
- &:not(:last-child) {
- margin-right: 20px;
+ &:last-child {
+ margin-right: 0;
}
}
}
}
.chart-section {
- background: #fff;
- padding: 20px;
- border-radius: 4px;
margin-bottom: 20px;
.chart-container {
- width: 100%;
-
- .chart-title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
+ .chart-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
margin-bottom: 20px;
- padding-bottom: 10px;
+ padding-bottom: 15px;
border-bottom: 1px solid #f0f0f0;
- }
- #satisfactionBarChart {
- width: 100%;
- height: 400px;
+ .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;
+ }
+ }
+ }
}
}
}
- .detail-table-section {
- background: #fff;
- padding: 20px;
- border-radius: 4px;
-
- .section-title {
- font-size: 16px;
- font-weight: 600;
- color: #333;
- margin-bottom: 20px;
- padding-bottom: 10px;
- border-bottom: 1px solid #f0f0f0;
+ .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: 20px;
+ padding: 15px;
background: #f8f9fa;
border-radius: 4px;
margin: 10px 0;
@@ -905,6 +1332,11 @@
background-color: #f8f9fa;
font-weight: 600;
color: #333;
+ padding: 12px 0;
+ }
+
+ td {
+ padding: 12px 0;
}
.question-row {
@@ -930,7 +1362,7 @@
margin-top: 20px;
padding: 20px;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
- border-radius: 4px;
+ border-radius: 8px;
border: 1px solid #dee2e6;
.summary-content {
@@ -943,27 +1375,129 @@
.label {
font-size: 16px;
- color: #666;
+ color: #606266;
margin-right: 8px;
}
.value {
font-size: 24px;
font-weight: 600;
- color: #1890ff;
+ color: #409eff;
}
}
}
}
+
+ .pagination-section {
+ display: flex;
+ justify-content: center;
+ padding: 20px 0 0 0;
+ }
}
- .pagination-section {
- display: flex;
- justify-content: flex-end;
- background: #fff;
- padding: 20px;
- border-radius: 4px;
- margin-top: 20px;
+ .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;
+ }
+ }
+ }
+ }
}
// 鍐呭眰琛ㄦ牸鏍峰紡
@@ -987,4 +1521,51 @@
}
}
}
+
+@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>
diff --git a/vue.config.js b/vue.config.js
index 5a49e1f..79d9dfb 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -36,8 +36,8 @@
// detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: {
// target: `https://www.health-y.cn/lssf`,
- target: `http://192.168.100.10:8096`,
- // target: `http://192.168.100.10:8094`,//鐪佺珛鍚屽痉
+ // target: `http://192.168.100.10:8096`,
+ target: `http://192.168.100.10:8094`,//鐪佺珛鍚屽痉
// target: `http://192.168.100.10:8095`,//鏂板崕
// target:`http://localhost:8095`,
// target:`http://35z1t16164.qicp.vip`,
--
Gitblit v1.9.3