| | |
| | | <el-button type="primary" size="small" icon="el-icon-search" @click="refreshAll">查询</el-button> |
| | | <el-button size="small" icon="el-icon-refresh" @click="handleReset">重置</el-button> |
| | | </div> |
| | | <el-button type="danger" size="small" icon="el-icon-download" @click="exportPDF">导出PDF</el-button> |
| | | <el-button type="danger" size="small" icon="el-icon-download" @click="exportExcel">导出Excel</el-button> |
| | | </div> |
| | | |
| | | <el-row :gutter="20"> |
| | |
| | | |
| | | <script> |
| | | import * as echarts from 'echarts'; |
| | | import ExcelJS from 'exceljs'; |
| | | import { saveAs } from 'file-saver'; |
| | | import { mydTrend, sltdMydTotalByDimension, sltdMydTotalByScore ,mzMydScoreRank} from "@/api/AiCentre/satisfaction"; |
| | | import { getissuelist } from "@/api/AiCentre/questionnaire"; |
| | | |
| | |
| | | singleQuestionContent: '', |
| | | singleDeptWards: [], |
| | | |
| | | // 排名维度筛选 |
| | | rankDimension: 'dept', |
| | | // 排名维度筛选(默认按病区) |
| | | rankDimension: 'ward', |
| | | rankDeptWards: [], |
| | | crossDimension: 'dept', |
| | | crossDimension: 'ward', |
| | | crossDeptWards: [], |
| | | |
| | | // 图表数据 |
| | |
| | | }); |
| | | }, |
| | | |
| | | async exportPDF() { |
| | | 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' }); |
| | | // 导出:图表转图片 + 表格,内容与页面一致(直接下载 xlsx,不依赖打印) |
| | | async exportExcel() { |
| | | try { |
| | | const wb = new ExcelJS.Workbook(); |
| | | |
| | | // 图表 sheet |
| | | const addImageSheet = (sheetName, title, refEl, tip) => { |
| | | const ws = wb.addWorksheet(sheetName, { |
| | | views: [{ showGridLines: false }], |
| | | }); |
| | | ws.getCell('A1').value = title; |
| | | ws.getCell('A1').font = { size: 14, bold: true }; |
| | | ws.mergeCells('A1:F1'); |
| | | let anchorRow = 2.2; |
| | | if (tip) { |
| | | ws.getCell('A2').value = tip; |
| | | ws.mergeCells('A2:F2'); |
| | | anchorRow = 2.8; |
| | | } |
| | | } |
| | | for (let c = 1; c <= 6; c++) { |
| | | ws.getColumn(c).width = 18; |
| | | } |
| | | const inst = refEl ? echarts.getInstanceByDom(refEl) : null; |
| | | if (inst) { |
| | | const url = inst.getDataURL({ |
| | | type: 'png', |
| | | pixelRatio: 2, |
| | | backgroundColor: '#fff', |
| | | }); |
| | | const imgId = wb.addImage({ |
| | | base64: url.split(',')[1], |
| | | extension: 'png', |
| | | }); |
| | | ws.addImage(imgId, { |
| | | tl: { col: 0.1, row: anchorRow }, |
| | | ext: { width: 900, height: 400 }, |
| | | }); |
| | | } else { |
| | | ws.getCell(`A${Math.ceil(anchorRow)}`).value = '暂无数据'; |
| | | } |
| | | return ws; |
| | | }; |
| | | |
| | | addImageSheet( |
| | | '综合得分趋势', |
| | | '患者满意度综合得分趋势', |
| | | this.$refs.chart1, |
| | | `环比 ${this.trendMoM}` |
| | | ); |
| | | addImageSheet( |
| | | '分类维度对比', |
| | | `分类维度 · 各指标${this.intervalLabel}对比`, |
| | | this.$refs.chart2, |
| | | '' |
| | | ); |
| | | addImageSheet( |
| | | '院区维度对比', |
| | | `院区维度 · 各院区${this.intervalLabel}对比`, |
| | | this.$refs.chart4, |
| | | '' |
| | | ); |
| | | |
| | | // 单题 · 选项统计 |
| | | const wsQ = wb.addWorksheet('单题选项统计'); |
| | | wsQ.addRow(['单题 · 选项统计']).font = { size: 14, bold: true }; |
| | | wsQ.mergeCells(1, 1, 1, 3); |
| | | const qHead = wsQ.addRow(['选项', '合计数量', '占比比例']); |
| | | qHead.font = { bold: true }; |
| | | (this.singleQuestionData || []).forEach((r) => { |
| | | wsQ.addRow([r.option, r.count, r.rate]); |
| | | }); |
| | | wsQ.addRow([]); |
| | | wsQ.addRow([ |
| | | `本题有效填写人次:${this.singleQuestionMeta.total} 综合得分:${this.singleQuestionMeta.score}`, |
| | | '', |
| | | '', |
| | | ]); |
| | | wsQ.mergeCells(wsQ.rowCount, 1, wsQ.rowCount, 3); |
| | | wsQ.columns.forEach((c, i) => { |
| | | c.width = i === 0 ? 36 : 16; |
| | | }); |
| | | |
| | | // 排名表格 |
| | | const addRankSheet = (sheetName, title, rows, dimLabel) => { |
| | | const ws = wb.addWorksheet(sheetName); |
| | | ws.addRow([title]).font = { size: 14, bold: true }; |
| | | ws.mergeCells(1, 1, 1, 4); |
| | | const head = ws.addRow(['排名', dimLabel, '综合得分', '项数']); |
| | | head.font = { bold: true }; |
| | | (rows || []).forEach((r) => { |
| | | ws.addRow([r.rank, r.name, r.score, r.itemCount]); |
| | | }); |
| | | if (!rows || !rows.length) { |
| | | ws.addRow(['暂无数据', '', '', '']); |
| | | } |
| | | ws.columns.forEach((c, i) => { |
| | | c.width = i === 1 ? 30 : 12; |
| | | }); |
| | | }; |
| | | addRankSheet( |
| | | `${this.rankDimLabel}排名`, |
| | | `${this.rankDimLabel}维度 · 门诊患者满意度得分排名`, |
| | | this.rankRows, |
| | | this.rankDimLabel |
| | | ); |
| | | addRankSheet( |
| | | `单题×${this.crossDimLabel}排名`, |
| | | `单题×${this.crossDimLabel} · 得分排名`, |
| | | this.crossRows, |
| | | this.crossDimLabel |
| | | ); |
| | | |
| | | const buffer = await wb.xlsx.writeBuffer(); |
| | | const blob = new Blob([buffer], { |
| | | type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', |
| | | }); |
| | | const range = (this.queryParams.dateRange || []).join('_'); |
| | | saveAs(blob, `各项统计表${range ? '_' + range : ''}.xlsx`); |
| | | this.$message.success('导出成功'); |
| | | } catch (e) { |
| | | console.error('导出失败:', e); |
| | | this.$message.error('导出失败:' + (e.message || e)); |
| | | } |
| | | |
| | | const printContent = this.$refs.pdfTarget.cloneNode(true); |
| | | 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, .filter-bar, .el-tag, .el-select, .el-radio-group { 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(); |
| | | }, |
| | | }, |
| | | }; |