<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="患者来源" prop="patientSource">
|
<el-select
|
v-model="queryParams.patientSource"
|
placeholder="请选择患者来源"
|
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"
|
>
|
<template slot-scope="{ row }">
|
<div class="option-detail">
|
<el-table
|
:data="row.options"
|
:border="true"
|
style="width: 100%"
|
class="inner-table"
|
>
|
<el-table-column
|
label="选项"
|
prop="optionText"
|
align="center"
|
min-width="200"
|
/>
|
<el-table-column
|
label="选择人数"
|
prop="chosenQuantity"
|
align="center"
|
min-width="120"
|
/>
|
<el-table-column
|
label="选择比例"
|
prop="chosenPercentage"
|
align="center"
|
min-width="120"
|
>
|
<template slot-scope="{ row: option }">
|
{{ formatPercent(option.chosenPercentage) }}
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
label="序号"
|
type="index"
|
align="center"
|
width="60"
|
/>
|
|
<el-table-column
|
label="题目"
|
prop="scriptContent"
|
align="center"
|
min-width="300"
|
>
|
<template slot-scope="{ row }">
|
<span>{{ row.scriptContent }}?</span>
|
<el-tag
|
:type="row.scriptType === 1 ? 'primary' : 'success'"
|
size="mini"
|
style="margin-left: 5px"
|
>
|
{{ row.scriptType === 1 ? '单选题' : '多选题' }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
label="平均得分"
|
prop="averageScore"
|
align="center"
|
width="120"
|
>
|
<template slot-scope="{ row }">
|
<span class="score-text">{{ row.averageScore.toFixed(1) }}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
label="最高得分"
|
prop="maxScore"
|
align="center"
|
width="120"
|
>
|
<template slot-scope="{ row }">
|
<span class="score-text">{{ row.maxScore.toFixed(1) }}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
label="最低得分"
|
prop="minScore"
|
align="center"
|
width="120"
|
>
|
<template slot-scope="{ row }">
|
<span class="score-text">{{ row.minScore.toFixed(1) }}</span>
|
</template>
|
</el-table-column>
|
|
<el-table-column
|
label="答题人数"
|
prop="answerCount"
|
align="center"
|
width="100"
|
/>
|
|
<el-table-column
|
label="未答题人数"
|
prop="unanswerCount"
|
align="center"
|
width="100"
|
>
|
<template slot-scope="{ row }">
|
{{ row.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>
|
|
<!-- 分页 -->
|
<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>
|
|
<script>
|
export default {
|
name: 'SatisfactionStatistics',
|
data() {
|
return {
|
// 查询参数
|
queryParams: {
|
patientSource: '',
|
deptCode: '',
|
wardCode: '',
|
dateRange: []
|
},
|
|
// 患者来源选项
|
patientSourceList: [
|
{ value: '1', label: '门诊' },
|
{ value: '2', label: '住院' },
|
{ value: '3', label: '急诊' },
|
{ value: '4', label: '体检' }
|
],
|
|
// 科室列表
|
deptList: [],
|
|
// 病区列表
|
wardList: [],
|
|
// 图表实例
|
barChart: null,
|
|
// 加载状态
|
loading: false,
|
detailLoading: false,
|
|
// 题目明细数据
|
questionDetailData: [],
|
|
// 题目明细查询参数
|
detailQueryParams: {
|
pageNum: 1,
|
pageSize: 10
|
},
|
|
// 题目明细总数
|
detailTotal: 0,
|
|
// 综合得分
|
totalScore: 0,
|
totalAnswerCount: 0,
|
totalAnswerRate: 0,
|
|
// 日期选择器选项
|
pickerOptions: {
|
shortcuts: [
|
{
|
text: '最近一周',
|
onClick(picker) {
|
const end = new Date();
|
const start = new Date();
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
|
picker.$emit('pick', [start, end]);
|
}
|
},
|
{
|
text: '最近一个月',
|
onClick(picker) {
|
const end = new Date();
|
const start = new Date();
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
|
picker.$emit('pick', [start, end]);
|
}
|
},
|
{
|
text: '最近三个月',
|
onClick(picker) {
|
const end = new Date();
|
const start = new Date();
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
|
picker.$emit('pick', [start, end]);
|
}
|
}
|
],
|
disabledDate(time) {
|
return time.getTime() > Date.now();
|
}
|
},
|
|
// 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' }
|
]
|
};
|
},
|
|
mounted() {
|
this.initData();
|
this.initChart();
|
},
|
|
beforeDestroy() {
|
if (this.barChart) {
|
this.barChart.dispose();
|
this.barChart = null;
|
}
|
},
|
|
methods: {
|
// 初始化数据
|
async initData() {
|
await this.getDeptList();
|
await this.getWardList();
|
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: '儿科' }
|
];
|
resolve();
|
}, 100);
|
});
|
},
|
|
// 获取病区列表
|
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: '儿科病区' }
|
];
|
resolve();
|
}, 100);
|
});
|
},
|
|
// 加载数据
|
async loadData() {
|
await Promise.all([
|
this.loadChartData(),
|
this.loadQuestionDetailData()
|
]);
|
},
|
|
// 加载图表数据
|
loadChartData() {
|
this.loading = true;
|
return new Promise((resolve) => {
|
setTimeout(() => {
|
this.renderChart(this.generateChartData());
|
this.loading = false;
|
resolve();
|
}, 500);
|
});
|
},
|
|
// 加载题目明细数据
|
loadQuestionDetailData() {
|
this.detailLoading = true;
|
return new Promise((resolve) => {
|
setTimeout(() => {
|
const mockData = this.generateMockQuestionDetail();
|
this.questionDetailData = mockData.list;
|
this.detailTotal = mockData.total;
|
|
// 计算综合得分
|
this.calculateSummary(mockData);
|
this.detailLoading = false;
|
resolve();
|
}, 500);
|
});
|
},
|
|
// 计算综合得分
|
calculateSummary(data) {
|
let totalScore = 0;
|
let totalAnswerCount = 0;
|
let totalCount = 0;
|
|
data.list.forEach(item => {
|
totalScore += item.averageScore;
|
totalAnswerCount += item.answerCount;
|
totalCount += item.totalCount;
|
});
|
|
this.totalScore = data.list.length > 0 ? totalScore / data.list.length : 0;
|
this.totalAnswerCount = totalAnswerCount;
|
this.totalAnswerRate = totalCount > 0 ? totalAnswerCount / totalCount : 0;
|
},
|
|
// 初始化图表
|
initChart() {
|
const echarts = require('echarts');
|
const chartDom = document.getElementById('satisfactionBarChart');
|
if (!chartDom) return;
|
|
this.barChart = echarts.init(chartDom);
|
|
// 监听窗口变化
|
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 {
|
categories,
|
legend: this.mockSatisfactionTypes.map(type => type.name),
|
series
|
};
|
},
|
|
// 渲染图表
|
renderChart(chartData) {
|
if (!this.barChart) return;
|
|
const option = {
|
title: {
|
text: '满意度类型统计',
|
left: 'center',
|
textStyle: {
|
fontSize: 16,
|
fontWeight: 'normal',
|
color: '#333'
|
}
|
},
|
tooltip: {
|
trigger: 'axis',
|
axisPointer: {
|
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'
|
}
|
},
|
grid: {
|
left: '3%',
|
right: '4%',
|
bottom: '3%',
|
top: 80,
|
containLabel: true
|
},
|
xAxis: {
|
type: 'category',
|
data: chartData.categories,
|
axisLabel: {
|
interval: 0,
|
rotate: 0,
|
fontSize: 12,
|
color: '#666'
|
},
|
axisLine: {
|
lineStyle: {
|
color: '#DCDFE6'
|
}
|
},
|
axisTick: {
|
alignWithLabel: true
|
}
|
},
|
yAxis: {
|
type: 'value',
|
name: '百分比 (%)',
|
min: 0,
|
max: 100,
|
axisLabel: {
|
formatter: '{value}%',
|
color: '#666'
|
},
|
axisLine: {
|
lineStyle: {
|
color: '#DCDFE6'
|
}
|
},
|
splitLine: {
|
lineStyle: {
|
type: 'dashed',
|
color: '#E4E7ED'
|
}
|
}
|
},
|
series: chartData.series
|
};
|
|
this.barChart.setOption(option);
|
},
|
|
// 生成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 }
|
]
|
}
|
];
|
|
// 分页处理
|
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
|
};
|
},
|
|
// 处理图表响应式
|
handleChartResize() {
|
if (this.barChart) {
|
this.barChart.resize();
|
}
|
},
|
|
// 处理查询
|
handleSearch() {
|
this.detailQueryParams.pageNum = 1;
|
this.loadData();
|
},
|
|
// 处理重置
|
handleReset() {
|
this.$refs.queryForm.resetFields();
|
this.queryParams.dateRange = [];
|
this.detailQueryParams.pageNum = 1;
|
this.loadData();
|
},
|
|
// 处理明细分页大小变化
|
handleDetailSizeChange(size) {
|
this.detailQueryParams.pageSize = size;
|
this.detailQueryParams.pageNum = 1;
|
this.loadQuestionDetailData();
|
},
|
|
// 处理明细页码变化
|
handleDetailPageChange(page) {
|
this.detailQueryParams.pageNum = page;
|
this.loadQuestionDetailData();
|
},
|
|
// 格式化百分比
|
formatPercent(value) {
|
if (value === null || value === undefined) return '-';
|
const num = parseFloat(value);
|
if (isNaN(num)) return '-';
|
return `${(num * 100).toFixed(2)}%`;
|
}
|
}
|
};
|
</script>
|
|
<style lang="scss" scoped>
|
.satisfaction-statistics {
|
.query-section {
|
background: #fff;
|
padding: 20px;
|
border-radius: 4px;
|
margin-bottom: 20px;
|
|
.query-form {
|
display: flex;
|
flex-wrap: wrap;
|
|
::v-deep .el-form-item {
|
margin-bottom: 20px;
|
|
&:not(:last-child) {
|
margin-right: 20px;
|
}
|
}
|
}
|
}
|
|
.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;
|
margin-bottom: 20px;
|
padding-bottom: 10px;
|
border-bottom: 1px solid #f0f0f0;
|
}
|
|
#satisfactionBarChart {
|
width: 100%;
|
height: 400px;
|
}
|
}
|
}
|
|
.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;
|
}
|
|
.option-detail {
|
padding: 20px;
|
background: #f8f9fa;
|
border-radius: 4px;
|
margin: 10px 0;
|
}
|
|
::v-deep .el-table {
|
th {
|
background-color: #f8f9fa;
|
font-weight: 600;
|
color: #333;
|
}
|
|
.question-row {
|
td {
|
background-color: #fff;
|
}
|
|
&:hover {
|
td {
|
background-color: #f5f7fa;
|
}
|
}
|
}
|
}
|
|
.score-text {
|
font-weight: 600;
|
color: #1890ff;
|
font-size: 16px;
|
}
|
|
.summary-row {
|
margin-top: 20px;
|
padding: 20px;
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
border-radius: 4px;
|
border: 1px solid #dee2e6;
|
|
.summary-content {
|
display: flex;
|
justify-content: space-around;
|
align-items: center;
|
|
.summary-item {
|
text-align: center;
|
|
.label {
|
font-size: 16px;
|
color: #666;
|
margin-right: 8px;
|
}
|
|
.value {
|
font-size: 24px;
|
font-weight: 600;
|
color: #1890ff;
|
}
|
}
|
}
|
}
|
}
|
|
.pagination-section {
|
display: flex;
|
justify-content: flex-end;
|
background: #fff;
|
padding: 20px;
|
border-radius: 4px;
|
margin-top: 20px;
|
}
|
|
// 内层表格样式
|
.inner-table {
|
::v-deep .el-table__header-wrapper {
|
th {
|
background-color: #f0f7ff !important;
|
color: #333;
|
font-weight: 600;
|
}
|
}
|
|
::v-deep .el-table__body-wrapper {
|
tr {
|
background-color: #fff;
|
|
&:hover {
|
background-color: #f5f7fa;
|
}
|
}
|
}
|
}
|
}
|
</style>
|