| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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>æ¶é´ç»´åº¦ · é¨è¯æ£è
满æåº¦è¶å¿</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>ç§å®¤ç»´åº¦ · é¨è¯æ£è
满æåº¦å¾åæå</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 = ['第ä¸å£åº¦', '第äºå£åº¦', '第ä¸å£åº¦', '第åå£åº¦']; |
| | | 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: 'é¨è¯æ£è
满æåº¦', 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('请å
许弹åºçªå£ä»¥å¯¼åº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> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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: "å»çæ¯å¦ç¨æ¨å¬å¾æçæ¹å¼è§£éé®é¢ï¼", 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: "æ¤å£«æ¯å¦ç¨æ¨å¬å¾æçæ¹å¼è§£éé®é¢ï¼", 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> |
| | |
| | | <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> |
| | |
| | | <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 { |
| | |
| | | </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> |
| | |
| | | </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', '馿¬¡éé访å表')"> |
| | |
| | | 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) => { |
| | |
| | | // 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`, |