From 77970a545af56e946e439ead97b23557d0b44f0f Mon Sep 17 00:00:00 2001
From: WXL (wul) <wl_5969728@163.com>
Date: 星期三, 12 八月 2026 15:54:08 +0800
Subject: [PATCH] 提交
---
src/views/Satisfaction/sfstatistics/components/ChartsPanel.vue | 268 +++++++++++++++++++++++++++++
vue.config.js | 4
src/views/Satisfaction/sfstatistics/components/DataOverview.vue | 195 +++++++++++++++++++++
src/views/sfstatistics/percentage/index.vue | 10
src/views/Satisfaction/sfstatistics/index.vue | 20 +
src/views/sfstatistics/percentage/components/CombinedFollowUp.vue | 9
6 files changed, 490 insertions(+), 16 deletions(-)
diff --git a/src/views/Satisfaction/sfstatistics/components/ChartsPanel.vue b/src/views/Satisfaction/sfstatistics/components/ChartsPanel.vue
new file mode 100644
index 0000000..c8a13c6
--- /dev/null
+++ b/src/views/Satisfaction/sfstatistics/components/ChartsPanel.vue
@@ -0,0 +1,268 @@
+<template>
+ <div class="charts-panel" ref="pdfTarget">
+ <!-- PDF瀵煎嚭鎸夐挳 -->
+ <div class="export-bar">
+ <el-button type="danger" size="small" icon="el-icon-download" @click="exportPDF">瀵煎嚭PDF</el-button>
+ </div>
+
+ <el-row :gutter="20">
+ <!-- 1. 鏃堕棿缁村害 - 鎶樼嚎鍥� -->
+ <el-col :span="12">
+ <el-card shadow="never" class="chart-card">
+ <div slot="header" class="card-header">
+ <span>鏃堕棿缁村害 路 闂ㄨ瘖鎮h�呮弧鎰忓害瓒嬪娍</span>
+ <el-tag type="success" size="small">鐜瘮 +0.97</el-tag>
+ </div>
+ <div ref="chart1" class="chart-box"></div>
+ </el-card>
+ </el-col>
+
+ <!-- 2. 鍒嗙被缁村害 - 鍒嗙粍鏌辩姸鍥� -->
+ <el-col :span="12">
+ <el-card shadow="never" class="chart-card">
+ <div slot="header" class="card-header">
+ <span>鍒嗙被缁村害 路 鍚勬寚鏍囧勾搴﹀姣�</span>
+ </div>
+ <div ref="chart2" class="chart-box"></div>
+ </el-card>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20" style="margin-top: 20px">
+ <!-- 3. 鍗曢閫夐」 - 鏁版嵁琛ㄦ牸 -->
+ <el-col :span="12">
+ <el-card shadow="never" class="chart-card">
+ <div slot="header" class="card-header">
+ <span>鍗曢 路 绗�22棰橈細鎶辨�ㄥ拰涓嶆弧鑳藉惁鍙婃椂寰楀埌鍥炲簲锛�</span>
+ </div>
+ <el-table :data="singleQuestionData" border size="small">
+ <el-table-column prop="option" label="閫夐」" />
+ <el-table-column prop="count" label="鍚堣鏁伴噺" width="120" align="center" />
+ <el-table-column prop="rate" label="鍗犳瘮姣斾緥" width="120" align="center" />
+ </el-table>
+ <div class="table-footer">鏈鏈夋晥濉啓浜烘锛�<b>26,233</b>銆�缁煎悎婊℃剰搴︼細<b>83.20%</b></div>
+ </el-card>
+ </el-col>
+
+ <!-- 4. 闄㈠尯缁村害 - 鍒嗙粍鏌辩姸鍥� -->
+ <el-col :span="12">
+ <el-card shadow="never" class="chart-card">
+ <div slot="header" class="card-header">
+ <span>闄㈠尯缁村害 路 鍚勯櫌鍖哄勾搴﹀姣�</span>
+ </div>
+ <div ref="chart4" class="chart-box"></div>
+ </el-card>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20" style="margin-top: 20px">
+ <!-- 5. 绉戝缁村害 - 鎺掑悕琛ㄦ牸 -->
+ <el-col :span="12">
+ <el-card shadow="never" class="chart-card">
+ <div slot="header" class="card-header">
+ <span>绉戝缁村害 路 闂ㄨ瘖鎮h�呮弧鎰忓害寰楀垎鎺掑悕</span>
+ </div>
+ <el-table :data="deptRankData" border size="small">
+ <el-table-column type="index" label="鎺掑悕" width="60" align="center" />
+ <el-table-column prop="dept" label="绉戝" />
+ <el-table-column prop="score" label="缁煎悎寰楀垎" width="120" align="center" />
+ </el-table>
+ </el-card>
+ </el-col>
+
+ <!-- 6. 鍗曢绉戝 - 浜ゅ弶鍒嗘瀽琛ㄦ牸 -->
+ <el-col :span="12">
+ <el-card shadow="never" class="chart-card">
+ <div slot="header" class="card-header">
+ <span>鍗曢脳绉戝 路 绗�22棰樺悇绉戝寰楀垎</span>
+ </div>
+ <el-table :data="crossDeptData" border size="small" max-height="400">
+ <el-table-column type="index" label="鎺掑悕" width="60" align="center" />
+ <el-table-column prop="dept" label="绉戝" />
+ <el-table-column prop="score" label="缁煎悎寰楀垎" width="120" align="center" />
+ </el-table>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+
+export default {
+ name: "ChartsPanel",
+ data() {
+ return {
+ singleQuestionData: [
+ { option: "闈炲父婊℃剰", count: 16513, rate: "62.95%" },
+ { option: "婊℃剰", count: 7088, rate: "27.02%" },
+ { option: "涓�鑸�", count: 1760, rate: "6.71%" },
+ { option: "涓嶆弧鎰�", count: 872, rate: "3.32%" },
+ ],
+ deptRankData: [
+ { dept: "淇濆仴绉�", score: "81.54" }, { dept: "浠嬪叆娌荤枟绉�", score: "80.72" },
+ { dept: "涓撶鎶ゅ+闂ㄨ瘖", score: "79.27" }, { dept: "搴峰鍖诲绉�", score: "78.65" },
+ { dept: "鐨偆绉�", score: "78.12" }, { dept: "鍐呭垎娉岀", score: "77.89" },
+ { dept: "娑堝寲鍐呯", score: "77.34" }, { dept: "鑲惧唴绉�", score: "76.50" },
+ { dept: "鍛煎惛鍐呯", score: "75.88" }, { dept: "蹇冭绠″唴绉�", score: "75.21" },
+ ],
+ crossDeptData: [
+ { dept: "淇濆仴绉�", score: "81.24" }, { dept: "浠嬪叆娌荤枟绉�", score: "81.22" },
+ { dept: "涓撶鎶ゅ+闂ㄨ瘖", score: "80.15" }, { dept: "搴峰鍖诲绉�", score: "79.88" },
+ { dept: "鐨偆绉�", score: "79.01" }, { dept: "鍐呭垎娉岀", score: "78.45" },
+ { dept: "娑堝寲鍐呯", score: "77.92" }, { dept: "鑲惧唴绉�", score: "77.13" },
+ { dept: "鍛煎惛鍐呯", score: "76.80" }, { dept: "蹇冭绠″唴绉�", score: "76.05" },
+ { dept: "绁炵粡鍐呯", score: "75.62" }, { dept: "琛�娑茬", score: "75.01" },
+ { dept: "椋庢箍鍏嶇柅绉�", score: "74.38" }, { dept: "鑲跨槫鍐呯", score: "73.85" },
+ { dept: "鎰熸煋绉�", score: "73.12" }, { dept: "鑰佸勾绉�", score: "72.56" },
+ { dept: "鏅绉�", score: "71.90" }, { dept: "楠ㄧ", score: "71.23" },
+ { dept: "娉屽翱澶栫", score: "70.55" }, { dept: "濡囦骇绉�", score: "69.88" },
+ ],
+ };
+ },
+ mounted() { this.$nextTick(() => { this.initCharts(); }); },
+ methods: {
+ initCharts() {
+ const quarters = ['绗竴瀛e害', '绗簩瀛e害', '绗笁瀛e害', '绗洓瀛e害'];
+ const categories = ['闅愮淇濇姢', '鐜涓庢爣璇�', '鍖诲姟浜哄憳鍥炲簲', '鍖荤敓娌熼��', '鎸傚彿浣撻獙', '鎶ゅ+娌熼��'];
+ const hospitals = ['闄㈠尯涓�', '闄㈠尯浜�', '闄㈠尯涓�'];
+ const baseGrid = { top: 20, right: 30, bottom: 30, left: 50 };
+ const baseTooltip = { backgroundColor: 'rgba(255,255,255,0.95)', borderColor: '#e8e8e8', textStyle: { color: '#333', fontSize: 13 }, extraCssText: 'box-shadow: 0 2px 12px rgba(0,0,0,0.1); border-radius: 6px; padding: 10px 14px;' };
+ const baseXAxis = { axisLine: { lineStyle: { color: '#e0e0e0' } }, axisTick: { show: false }, axisLabel: { color: '#666', fontSize: 12 } };
+ const baseYAxis = { splitLine: { lineStyle: { color: '#f0f0f0', type: 'dashed' } }, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { color: '#999', fontSize: 11 } };
+
+ // 1. 鎶樼嚎鍥� - 鏃堕棿缁村害
+ const c1 = echarts.init(this.$refs.chart1);
+ c1.setOption({
+ color: ['#5B8FF9'],
+ grid: { ...baseGrid, top: 25 },
+ tooltip: { ...baseTooltip, trigger: 'axis' },
+ xAxis: [{ ...baseXAxis, data: quarters, boundaryGap: false }],
+ yAxis: [{ ...baseYAxis, min: 86, max: 91, name: '婊℃剰搴﹀緱鍒�', nameTextStyle: { color: '#999', fontSize: 11 } }],
+ series: [{
+ name: '闂ㄨ瘖鎮h�呮弧鎰忓害', type: 'line', smooth: true, symbol: 'circle', symbolSize: 8,
+ data: [88.20, 87.32, 88.50, 89.17],
+ lineStyle: { width: 3, shadowBlur: 8, shadowColor: 'rgba(91,143,249,0.3)' },
+ itemStyle: { color: '#5B8FF9' },
+ areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: 'rgba(91,143,249,0.25)' }, { offset: 1, color: 'rgba(91,143,249,0.02)' }
+ ])},
+ }],
+ });
+
+ // 2. 鍒嗙粍鏌辩姸鍥� - 鍒嗙被缁村害
+ const c2 = echarts.init(this.$refs.chart2);
+ c2.setOption({
+ color: ['#6DC8EC', '#5B8FF9'],
+ grid: baseGrid,
+ tooltip: { ...baseTooltip, trigger: 'axis', axisPointer: { type: 'shadow', shadowStyle: { color: 'rgba(0,0,0,0.03)' } } },
+ legend: { data: ['2023骞�', '2024骞�'], bottom: 0, textStyle: { color: '#666', fontSize: 12 } },
+ xAxis: [{ ...baseXAxis, data: categories, axisLabel: { ...baseXAxis.axisLabel, interval: 0 } }],
+ yAxis: [{ ...baseYAxis, name: '寰楀垎', nameTextStyle: { color: '#999', fontSize: 11 } }],
+ series: [
+ { name: '2023骞�', type: 'bar', barWidth: 22, barGap: '30%',
+ itemStyle: { borderRadius: [6, 6, 0, 0], color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#6DC8EC' }, { offset: 1, color: '#48B0D5' }]) },
+ data: [86.34, 85.12, 83.45, 86.22, 88.55, 87.33] },
+ { name: '2024骞�', type: 'bar', barWidth: 22,
+ itemStyle: { borderRadius: [6, 6, 0, 0], color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#5B8FF9' }, { offset: 1, color: '#3D6FDB' }]) },
+ data: [88.06, 84.57, 82.88, 87.77, 88.12, 87.00] },
+ ],
+ });
+
+ // 4. 闄㈠尯缁村害 - 鍒嗙粍鏌辩姸鍥�
+ const c4 = echarts.init(this.$refs.chart4);
+ c4.setOption({
+ color: ['#6DC8EC', '#5B8FF9'],
+ grid: baseGrid,
+ tooltip: { ...baseTooltip, trigger: 'axis', axisPointer: { type: 'shadow' } },
+ legend: { data: ['2023骞�', '2024骞�'], bottom: 0, textStyle: { color: '#666', fontSize: 12 } },
+ xAxis: [{ ...baseXAxis, data: hospitals }],
+ yAxis: [{ ...baseYAxis, name: '婊℃剰搴﹀緱鍒�', nameTextStyle: { color: '#999', fontSize: 11 } }],
+ series: [
+ { name: '2023骞�', type: 'bar', barWidth: 24, barGap: '30%',
+ itemStyle: { borderRadius: [6, 6, 0, 0], color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#6DC8EC' }, { offset: 1, color: '#48B0D5' }]) },
+ data: [86.20, 87.69, 85.55],
+ label: { show: true, position: 'top', color: '#666', fontSize: 12, fontWeight: 500, formatter: p => p.value.toFixed(2) } },
+ { name: '2024骞�', type: 'bar', barWidth: 24,
+ itemStyle: { borderRadius: [6, 6, 0, 0], color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#5B8FF9' }, { offset: 1, color: '#3D6FDB' }]) },
+ data: [88.15, 88.14, 85.88],
+ label: { show: true, position: 'top', color: '#666', fontSize: 12, fontWeight: 500, formatter: p => p.value.toFixed(2) } },
+ ],
+ });
+
+ // 鍝嶅簲寮�
+ window.addEventListener('resize', () => { c1.resize(); c2.resize(); c4.resize(); });
+ },
+
+ async exportPDF() {
+ // 鍏堝皢 ECharts 瀹炰緥杞负鍥剧墖 dataURL
+ const chartRefs = [
+ { ref: this.$refs.chart1, id: 'chart1' },
+ { ref: this.$refs.chart2, id: 'chart2' },
+ { ref: this.$refs.chart4, id: 'chart4' },
+ ];
+ const chartImages = {};
+ for (const { ref, id } of chartRefs) {
+ if (ref) {
+ const instance = echarts.getInstanceByDom(ref);
+ if (instance) {
+ chartImages[id] = instance.getDataURL({ type: 'png', pixelRatio: 2, backgroundColor: '#fff' });
+ }
+ }
+ }
+
+ // 鍏嬮殕鍐呭骞舵浛鎹㈠浘琛ㄤ负鍥剧墖
+ const printContent = this.$refs.pdfTarget.cloneNode(true);
+ for (const { ref, id } of chartRefs) {
+ if (chartImages[id] && ref) {
+ const boxes = printContent.querySelectorAll('.chart-box');
+ for (const box of boxes) {
+ if (box.innerHTML.includes(id) || boxes.length === chartRefs.length) {
+ box.innerHTML = `<img src="${chartImages[id]}" style="width:100%;height:auto;" />`;
+ }
+ }
+ }
+ }
+ // 鏇村彲闈犵殑鏂瑰紡锛氭寜浣嶇疆鍖归厤
+ const chartBoxes = printContent.querySelectorAll('.chart-box');
+ const imgKeys = Object.keys(chartImages);
+ chartBoxes.forEach((box, i) => {
+ if (imgKeys[i] && chartImages[imgKeys[i]]) {
+ box.innerHTML = `<img src="${chartImages[imgKeys[i]]}" style="width:100%;height:auto;" />`;
+ }
+ });
+
+ const win = window.open('', '_blank', 'width=900,height=800');
+ if (!win) { this.$message.warning('璇峰厑璁稿脊鍑虹獥鍙d互瀵煎嚭PDF'); return; }
+ win.document.write(`
+ <html><head><meta charset="utf-8"><title>鍚勯」缁熻琛�</title>
+ <style>
+ body { font-family: 'Microsoft YaHei',sans-serif; padding: 20px; }
+ .chart-card { break-inside: avoid; margin-bottom: 20px; border: 1px solid #ebeef5; border-radius: 6px; }
+ .card-header { padding: 12px 16px; border-bottom: 1px solid #ebeef5; font-size: 15px; font-weight: 600; color: #303133; background: #f5f7fa; }
+ .chart-box img { max-width: 100%; display: block; }
+ table { width: 100%; border-collapse: collapse; margin: 12px 0; }
+ th, td { border: 1px solid #d9d9d9; padding: 8px 12px; text-align: center; font-size: 12px; }
+ th { background: #f0f2f5; font-weight: 600; }
+ .table-footer { text-align: right; padding: 8px 0; font-size: 13px; color: #606266; }
+ .export-bar, .el-tag { display: none; }
+ @media print { @page { size: A4; margin: 10mm; } }
+ </style></head><body>
+ ${printContent.innerHTML}
+ <script>window.onload=function(){window.print();}<\/script>
+ </body></html>
+ `);
+ win.document.close();
+ },
+ },
+};
+</script>
+
+<style lang="scss" scoped>
+.export-bar { text-align: right; margin-bottom: 16px; }
+.chart-card { margin-bottom: 0; }
+.chart-box { width: 100%; height: 360px; }
+.card-header { display: flex; justify-content: space-between; align-items: center; span { font-size: 15px; font-weight: 600; color: #303133; } }
+.table-footer { text-align: right; padding: 10px 0 0; font-size: 13px; color: #606266; }
+</style>
diff --git a/src/views/Satisfaction/sfstatistics/components/DataOverview.vue b/src/views/Satisfaction/sfstatistics/components/DataOverview.vue
new file mode 100644
index 0000000..18b5142
--- /dev/null
+++ b/src/views/Satisfaction/sfstatistics/components/DataOverview.vue
@@ -0,0 +1,195 @@
+<template>
+ <div class="data-overview">
+ <!-- 琛ㄦ牸涓�锛氬悇鏈嶅姟椤圭洰婊℃剰搴︾粺璁� -->
+ <el-card shadow="never" class="overview-card">
+ <div slot="header" class="card-header">
+ <span class="card-title">鍚勬湇鍔¢」鐩弧鎰忓害缁熻</span>
+ <el-button type="warning" size="small" icon="el-icon-download" @click="exportTable('table1')">瀵煎嚭Excel</el-button>
+ </div>
+ <el-table id="table1" :data="serviceSatisfaction" border size="small" :header-cell-style="{ background: '#f5f7fa', color: '#303133' }">
+ <el-table-column prop="category" label="鍒嗙被/棰樼洰" min-width="220" fixed="left" />
+ <el-table-column label="闈炲父婊℃剰" align="center">
+ <el-table-column prop="veryGoodCount" label="鏁伴噺" width="90" />
+ <el-table-column prop="veryGoodRate" label="姣斾緥" width="90" />
+ </el-table-column>
+ <el-table-column label="婊℃剰" align="center">
+ <el-table-column prop="goodCount" label="鏁伴噺" width="90" />
+ <el-table-column prop="goodRate" label="姣斾緥" width="90" />
+ </el-table-column>
+ <el-table-column label="涓�鑸�" align="center">
+ <el-table-column prop="normalCount" label="鏁伴噺" width="90" />
+ <el-table-column prop="normalRate" label="姣斾緥" width="90" />
+ </el-table-column>
+ <el-table-column label="涓嶆弧鎰�" align="center">
+ <el-table-column prop="badCount" label="鏁伴噺" width="90" />
+ <el-table-column prop="badRate" label="姣斾緥" width="90" />
+ </el-table-column>
+ <el-table-column label="涓嶉�傜敤" align="center">
+ <el-table-column prop="naCount" label="鏁伴噺" width="90" />
+ <el-table-column prop="naRate" label="姣斾緥" width="90" />
+ </el-table-column>
+ <el-table-column prop="score" label="缁煎悎寰楀垎" width="100" align="center" />
+ </el-table>
+ </el-card>
+
+ <!-- 琛ㄦ牸浜岋細鍗曢�夐/澶氶�夐缁熻 -->
+ <el-card shadow="never" class="overview-card">
+ <div slot="header" class="card-header">
+ <span class="card-title">鍗曢�夐/澶氶�夐缁熻</span>
+ <el-button type="warning" size="small" icon="el-icon-download" @click="exportTable('table2')">瀵煎嚭Excel</el-button>
+ </div>
+ <div class="question-block" v-for="(q, qi) in questionStats" :key="qi">
+ <h4 class="q-title">{{ q.title }}</h4>
+ <el-table :data="q.options" border size="small" :header-cell-style="{ background: '#f5f7fa', color: '#303133' }">
+ <el-table-column prop="option" label="閫夐」" min-width="180" />
+ <el-table-column prop="count" label="鏁伴噺" width="100" align="center" />
+ <el-table-column prop="rate" label="姣斾緥" width="100" align="center" />
+ </el-table>
+ <div v-if="q.total" class="q-footer">鏈鏈夋晥濉啓浜烘锛�<b>{{ q.total }}</b></div>
+ </div>
+ </el-card>
+
+ <!-- 琛ㄦ牸涓夛細鍚勭瀹ゆ弧鎰忓害璇勫垎瀵规瘮 -->
+ <el-card shadow="never" class="overview-card">
+ <div slot="header" class="card-header">
+ <span class="card-title">鍚勭瀹ゆ弧鎰忓害璇勫垎瀵规瘮</span>
+ <el-button type="warning" size="small" icon="el-icon-download" @click="exportTable('table3')">瀵煎嚭Excel</el-button>
+ </div>
+ <el-table id="table3" :data="deptComparison" border size="small" :header-cell-style="{ background: '#f5f7fa', color: '#303133' }">
+ <el-table-column prop="dept" label="绉戝"fixed="left" align="center" />
+ <el-table-column label="绗�1棰�" align="center">
+ <el-table-column prop="q1VeryGood" label="闈炲父婊℃剰" width="100" />
+ <el-table-column prop="q1Good" label="婊℃剰" width="90" />
+ <el-table-column prop="q1Normal" label="涓�鑸�" width="90" />
+ <el-table-column prop="q1Bad" label="涓嶆弧鎰�" width="90" />
+ <el-table-column prop="q1Score" label="寰楀垎" width="100" />
+ </el-table-column>
+ <el-table-column label="绗�2棰�" align="center">
+ <el-table-column prop="q2VeryGood" label="闈炲父婊℃剰" width="100" />
+ <el-table-column prop="q2Good" label="婊℃剰" width="90" />
+ <el-table-column prop="q2Normal" label="涓�鑸�" width="90" />
+ <el-table-column prop="q2Bad" label="涓嶆弧鎰�" width="90" />
+ <el-table-column prop="q2Score" label="寰楀垎" width="100" />
+ </el-table-column>
+ <el-table-column prop="totalScore" label="缁煎悎寰楀垎" align="center" />
+ </el-table>
+ </el-card>
+ </div>
+</template>
+
+<script>
+import XLSX from 'xlsx';
+
+export default {
+ name: "DataOverview",
+ data() {
+ return {
+ serviceSatisfaction: [
+ { category: "璇婄枟杩囩▼涓紝鍖诲姟浜哄憳鏄惁娉ㄦ剰淇濇姢鎮ㄧ殑闅愮锛�", veryGoodCount: 3219, veryGoodRate: "67.84%", goodCount: 1351, goodRate: "28.47%", normalCount: 142, normalRate: "2.99%", badCount: 33, badRate: "0.70%", naCount: 0, naRate: "0.00%", score: "87.82" },
+ { category: "鎮ㄥ鍖婚櫌鏈嶅姟璁炬柦鐨勬�讳綋鍗拌薄濡備綍锛�", veryGoodCount: 3110, veryGoodRate: "65.54%", goodCount: 1369, goodRate: "28.85%", normalCount: 217, normalRate: "4.57%", badCount: 49, badRate: "1.03%", naCount: 0, naRate: "0.00%", score: "86.30" },
+ { category: "鍖婚櫌鐨勫帟鎵�鏄惁娓呮磥鏃犲紓鍛筹紵", veryGoodCount: 2973, veryGoodRate: "62.66%", goodCount: 1504, goodRate: "31.70%", normalCount: 235, normalRate: "4.95%", badCount: 33, badRate: "0.70%", naCount: 0, naRate: "0.00%", score: "85.44" },
+ { category: "鍖婚櫌鍐呯殑璺爣鍜屾寚绀烘槸鍚︽槑纭紵", veryGoodCount: 3234, veryGoodRate: "68.16%", goodCount: 1289, goodRate: "27.17%", normalCount: 196, normalRate: "4.13%", badCount: 26, badRate: "0.55%", naCount: 0, naRate: "0.00%", score: "87.64" },
+ { category: "鍖婚櫌鐨勭┖闂村竷灞�鏄惁渚垮埄锛�", veryGoodCount: 3037, veryGoodRate: "64.00%", goodCount: 1460, goodRate: "30.77%", normalCount: 209, normalRate: "4.40%", badCount: 39, badRate: "0.82%", naCount: 0, naRate: "0.00%", score: "85.99" },
+ { category: "鐪嬬梾鏈熼棿锛屾偍鐨勬姳鎬ㄥ拰涓嶆弧鑳藉惁鍙婃椂寰楀埌鍥炲簲锛�", veryGoodCount: 3019, veryGoodRate: "63.62%", goodCount: 1317, goodRate: "27.76%", normalCount: 294, normalRate: "6.20%", badCount: 115, badRate: "2.42%", naCount: 0, naRate: "0.00%", score: "84.19" },
+ { category: "鍖荤敓鏄惁鐢ㄦ偍鍚緱鎳傜殑鏂瑰紡瑙i噴闂锛�", veryGoodCount: 3402, veryGoodRate: "71.70%", goodCount: 967, goodRate: "20.38%", normalCount: 254, normalRate: "5.35%", badCount: 122, badRate: "2.57%", naCount: 0, naRate: "0.00%", score: "87.07" },
+ { category: "鍖荤敓瀵规偍鏄惁灏婇噸锛�", veryGoodCount: 3495, veryGoodRate: "73.66%", goodCount: 897, goodRate: "18.90%", normalCount: 249, normalRate: "5.25%", badCount: 104, badRate: "2.19%", naCount: 0, naRate: "0.00%", score: "88.01" },
+ { category: "鍖荤敓鏄惁浠旂粏鍊惧惉鎮ㄨ璇濓紵", veryGoodCount: 3422, veryGoodRate: "72.12%", goodCount: 937, goodRate: "19.75%", normalCount: 251, normalRate: "5.29%", badCount: 135, badRate: "2.85%", naCount: 0, naRate: "0.00%", score: "87.05" },
+ { category: "瀵规寕鍙锋湇鍔′汉鍛樻湇鍔℃�佸害鏄惁婊℃剰", veryGoodCount: 3458, veryGoodRate: "72.88%", goodCount: 1205, goodRate: "25.40%", normalCount: 52, normalRate: "1.10%", badCount: 30, badRate: "0.63%", naCount: 0, naRate: "0.00%", score: "90.17" },
+ { category: "鎮ㄥ棰勭害鎸傚彿鏄惁婊℃剰锛�", veryGoodCount: 3527, veryGoodRate: "74.33%", goodCount: 1088, goodRate: "22.93%", normalCount: 72, normalRate: "1.52%", badCount: 58, badRate: "1.22%", naCount: 0, naRate: "0.00%", score: "90.12" },
+ { category: "鎶ゅ+瀵规偍鏄惁灏婇噸锛�", veryGoodCount: 3420, veryGoodRate: "72.08%", goodCount: 1184, goodRate: "24.95%", normalCount: 113, normalRate: "2.38%", badCount: 28, badRate: "0.59%", naCount: 0, naRate: "0.00%", score: "89.50" },
+ { category: "鎶ゅ+鏄惁鐢ㄦ偍鍚緱鎳傜殑鏂瑰紡瑙i噴闂锛�", veryGoodCount: 3346, veryGoodRate: "70.52%", goodCount: 1250, goodRate: "26.34%", normalCount: 122, normalRate: "2.57%", badCount: 27, badRate: "0.57%", naCount: 0, naRate: "0.00%", score: "88.94" },
+ { category: "鎶ゅ+鏄惁浠旂粏鍊惧惉鎮ㄨ璇濓紵", veryGoodCount: 3378, veryGoodRate: "71.19%", goodCount: 1205, goodRate: "25.40%", normalCount: 134, normalRate: "2.82%", badCount: 28, badRate: "0.59%", naCount: 0, naRate: "0.00%", score: "89.06" },
+ { category: "鎬昏", veryGoodCount: 54863, veryGoodRate: "69.02%", goodCount: 20800, goodRate: "26.17%", normalCount: 2920, normalRate: "3.67%", badCount: 908, badRate: "1.14%", naCount: 0, naRate: "0.00%", score: "87.66" },
+ ],
+ questionStats: [
+ {
+ title: "绗�1棰橈細鎮ㄥ鎴戦櫌宸ヤ綔浜哄憳鐨勬湇鍔¤川閲忔弧鎰忎笉婊℃剰锛熴�愬垎鍊煎崟閫夐銆�- 缁煎悎寰楀垎",
+ options: [
+ { option: "闈炲父婊℃剰", count: 1084, rate: "75.66%" },
+ { option: "婊℃剰", count: 1959, rate: "20.92%" },
+ { option: "涓�鑸�", count: 167, rate: "1.78%" },
+ { option: "涓嶆弧鎰�", count: 153, rate: "1.63%" },
+ ],
+ total: 3363
+ },
+ {
+ title: "绗�2棰橈細鎮ㄥ鍝儴鍒嗘湇鍔′笉婊℃剰锛熴�愬閫夐銆�",
+ options: [
+ { option: "鍖荤敓璇婃柇鍜岃瘖鐤�", count: 131, rate: "32.11%" },
+ { option: "鎶ょ悊鏈嶅姟", count: 47, rate: "11.52%" },
+ { option: "鍏朵粬鍖诲姟浜哄憳鎶�鏈按骞�", count: 30, rate: "7.33%" },
+ { option: "宸ヤ汉", count: 72, rate: "17.65%" },
+ ],
+ total: 408
+ },
+ {
+ title: "绗�3棰橈細鍖荤敓璇婃柇鍜屾不鐤椾腑鍝簺鏈嶅姟璐ㄩ噺涓嶆弧鎰忥紵銆愬閫夐銆�",
+ options: [
+ { option: "鍖荤敓璇婃柇閿欒", count: 23, rate: "15.44%" },
+ { option: "瀵规不鐤楁晥鏋滀笉婊℃剰", count: 72, rate: "48.32%" },
+ { option: "鍏朵粬", count: 54, rate: "36.24%" },
+ ],
+ total: 131
+ },
+ ],
+ deptComparison: [
+ { dept: "鍐呭垎娉岀", q1VeryGood: 103, q1Good: 34, q1Normal: 0, q1Bad: 0, q1Score: "377.00", q2VeryGood: 101, q2Good: 35, q2Normal: 1, q2Bad: 0, q2Score: "374.00", totalScore: "75.25" },
+ { dept: "鐨偆绉�", q1VeryGood: 112, q1Good: 25, q1Normal: 2, q1Bad: 3, q1Score: "388.00", q2VeryGood: 111, q2Good: 27, q2Normal: 3, q2Bad: 1, q2Score: "390.00", totalScore: "76.12" },
+ { dept: "鑲跨槫鍐呯", q1VeryGood: 24, q1Good: 12, q1Normal: 2, q1Bad: 0, q1Score: "98.00", q2VeryGood: 25, q2Good: 12, q2Normal: 1, q2Bad: 0, q2Score: "100.00", totalScore: "70.22" },
+ { dept: "娑堝寲鍐呯", q1VeryGood: 180, q1Good: 52, q1Normal: 1, q1Bad: 0, q1Score: "645.00", q2VeryGood: 168, q2Good: 62, q2Normal: 3, q2Bad: 0, q2Score: "631.00", totalScore: "73.56" },
+ { dept: "鑲惧唴绉�", q1VeryGood: 21, q1Good: 4, q1Normal: 0, q1Bad: 0, q1Score: "71.00", q2VeryGood: 19, q2Good: 6, q2Normal: 0, q2Bad: 0, q2Score: "65.00", totalScore: "68.50" },
+ ],
+ };
+ },
+ methods: {
+ exportTable(tableId) {
+ const tableNames = { table1: '鍚勬湇鍔¢」鐩弧鎰忓害缁熻', table2: '鍗曢�夐澶氶�夐缁熻', table3: '鍚勭瀹ゆ弧鎰忓害璇勫垎瀵规瘮' };
+ const name = tableNames[tableId] || '缁熻鏁版嵁';
+ const table = document.getElementById(tableId);
+ if (!table) return;
+
+ try {
+ // 浠庤〃鏍兼彁鍙栨暟鎹瀯寤哄甫鏍峰紡鐨� Excel
+ const wb = XLSX.utils.book_new();
+ const data = [];
+ table.querySelectorAll('tr').forEach(tr => {
+ const row = [];
+ tr.querySelectorAll('th,td').forEach(td => {
+ row.push((td.innerText || '').trim());
+ });
+ if (row.length) data.push(row);
+ });
+
+ const ws = XLSX.utils.aoa_to_sheet(data);
+ // 璁剧疆鍒楀
+ const colWidths = data[0] ? data[0].map(() => ({ wch: 16 })) : [];
+ ws['!cols'] = colWidths;
+
+ XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
+ XLSX.writeFile(wb, name + '.xlsx');
+ } catch (e) {
+ // fallback: CSV
+ let csv = '锘�';
+ table.querySelectorAll('tr').forEach(tr => {
+ const row = [];
+ tr.querySelectorAll('th,td').forEach(td => row.push('"' + (td.innerText || '').replace(/"/g, '""') + '"'));
+ csv += row.join(',') + '\n';
+ });
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
+ const link = document.createElement('a');
+ link.href = URL.createObjectURL(blob); link.download = name + '.csv'; link.click();
+ }
+ },
+ },
+};
+</script>
+
+<style lang="scss" scoped>
+.data-overview { .overview-card { margin-bottom: 20px; } }
+.card-header { display: flex; justify-content: space-between; align-items: center; }
+.card-title { font-size: 16px; font-weight: 600; color: #303133; }
+.question-block { margin-bottom: 24px;
+ .q-title { margin: 0 0 12px 0; font-size: 14px; color: #606266; }
+ .q-footer { margin-top: 8px; font-size: 13px; color: #909399; text-align: right; }
+}
+</style>
diff --git a/src/views/Satisfaction/sfstatistics/index.vue b/src/views/Satisfaction/sfstatistics/index.vue
index 40a875d..a6a22f1 100644
--- a/src/views/Satisfaction/sfstatistics/index.vue
+++ b/src/views/Satisfaction/sfstatistics/index.vue
@@ -3,16 +3,16 @@
<div class="statistics-main">
<el-tabs v-model="activeTab" @tab-click="handleTabChange">
<el-tab-pane label="婊℃剰搴﹂殢璁跨粺璁�" name="followup">
- <followup-statistics
- v-if="activeTab === 'followup'"
- ref="followupRef"
- />
+ <followup-statistics v-if="activeTab === 'followup'" ref="followupRef" />
</el-tab-pane>
<el-tab-pane label="婊℃剰搴︽槑缁嗙粺璁�" name="satisfaction">
- <satisfaction-statistics
- v-if="activeTab === 'satisfaction'"
- ref="satisfactionRef"
- />
+ <satisfaction-statistics v-if="activeTab === 'satisfaction'" ref="satisfactionRef" />
+ </el-tab-pane>
+ <el-tab-pane label="缁熻鏁版嵁鎬昏" name="dataOverview">
+ <data-overview v-if="activeTab === 'dataOverview'" />
+ </el-tab-pane>
+ <el-tab-pane label="鍚勯」缁熻琛�" name="chartsPanel">
+ <charts-panel v-if="activeTab === 'chartsPanel'" />
</el-tab-pane>
</el-tabs>
</div>
@@ -21,12 +21,16 @@
<script>
import FollowupStatistics from "./components/FollowupStatistics.vue";
import SatisfactionStatistics from "./components/SatisfactionStatistics.vue";
+import DataOverview from "./components/DataOverview.vue";
+import ChartsPanel from "./components/ChartsPanel.vue";
export default {
name: "Satisfaction",
components: {
FollowupStatistics,
SatisfactionStatistics,
+ DataOverview,
+ ChartsPanel,
},
data() {
return {
diff --git a/src/views/sfstatistics/percentage/components/CombinedFollowUp.vue b/src/views/sfstatistics/percentage/components/CombinedFollowUp.vue
index d5f8cb8..fc3823c 100644
--- a/src/views/sfstatistics/percentage/components/CombinedFollowUp.vue
+++ b/src/views/sfstatistics/percentage/components/CombinedFollowUp.vue
@@ -173,10 +173,7 @@
</el-button>
</template>
</el-table-column>
-
- <!-- ===== 棣栨闅忚 ===== -->
- <el-table-column align="center" label="棣栨闅忚">
- <el-table-column label="鏃犻渶闅忚" align="center" prop="nonFollowUp">
+ <el-table-column label="鏃犻渶闅忚" align="center" prop="nonFollowUp">
<template slot-scope="scope">
<el-button type="text" @click="handleViewDetails(scope.row, 'nonFollowUpInfo', '棣栨鏃犻渶闅忚鍒楄〃')">
<span class="button-zx">{{ scope.row.nonFollowUp }}</span>
@@ -190,6 +187,10 @@
</el-button>
</template>
</el-table-column>
+
+ <!-- ===== 棣栨闅忚 ===== -->
+ <el-table-column align="center" label="棣栨闅忚">
+
<el-table-column label="闇�闅忚" align="center" prop="needFollowUp">
<template slot-scope="scope">
<el-button type="text" @click="handleViewDetails(scope.row, 'needFollowUpInfo', '棣栨闇�闅忚鍒楄〃')">
diff --git a/src/views/sfstatistics/percentage/index.vue b/src/views/sfstatistics/percentage/index.vue
index b6cb27c..9a068e7 100644
--- a/src/views/sfstatistics/percentage/index.vue
+++ b/src/views/sfstatistics/percentage/index.vue
@@ -475,8 +475,14 @@
obj.deptcodes = [row.deptcode];
obj.leavehospitaldistrictcodes = [];
}
- obj.endTime = this.queryParams.endTime;
- obj.startTime = this.queryParams.startTime;
+ if (this.queryParams.endTime) {
+ obj.endTime = this.queryParams.endTime;
+ obj.startTime = this.queryParams.startTime;
+ } else {
+ obj.startTime = this.parseTime(this.queryParams.dateRange[0]);
+ obj.endTime = this.parseTime(this.queryParams.dateRange[1]);
+ }
+
obj.statisticaltype = this.queryParams.statisticaltype;
getSfStatisticsHyperlink(obj).then((response) => {
diff --git a/vue.config.js b/vue.config.js
index 7f985b0..fd08b34 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://192.168.100.10:8098`,//甯備竴
// target:`http://localhost:8095`,
--
Gitblit v1.9.3