Files
DJKB/my-vue-app/src/views/senorManger/components/StatisticalIndicators.vue
chenpanliang b9f74dc810 fix(统计指标): 修复数据未加载时显示异常问题
修改统计指标组件和父组件的数据处理逻辑,确保在数据未加载或返回null时显示默认值0。同时将props类型从Number改为Object,并添加默认空对象防止访问属性错误。
2025-11-25 15:26:34 +08:00

192 lines
5.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="stat-card kpi-card">
<div class="kpi-grid stats-grid-inner">
<div class="kpi-item stat-item">
<div class="stat-icon customer-rate">
<i class="el-icon-chat-dot-round"></i>
</div>
<div class="kpi-value">{{ (customerCommunicationRate && customerCommunicationRate.active_customer_communication_rate) || 0 }}</div>
<p>活跃客户沟通率 <i class="el-icon-warning info-icon" @mouseenter="showTooltip($event, 'customerCommunicationRate')" @mouseleave="hideTooltip"></i></p>
</div>
<div class="kpi-item stat-item">
<div class="stat-icon response-time">
<i class="el-icon-timer"></i>
</div>
<div class="kpi-value">{{ (averageResponseTime && averageResponseTime.average_answer_time)||0 }}<span class="kpi-unit">分钟</span></div>
<p>平均应答时间 <i class="el-icon-warning info-icon" @mouseenter="showTooltip($event, 'averageResponseTime')" @mouseleave="hideTooltip"></i></p>
</div>
<div class="kpi-item stat-item">
<div class="stat-icon timeout-rate">
<i class="el-icon-warning"></i>
</div>
<div class="kpi-value">{{ (timeoutResponseRate && timeoutResponseRate.timeout_rate)||0 }}</div>
<p>超时应答率 <i class="el-icon-warning info-icon" @mouseenter="showTooltip($event, 'timeoutResponseRate')" @mouseleave="hideTooltip"></i></p>
</div>
<div class="kpi-item stat-item">
<div class="stat-icon severe-timeout-rate">
<i class="el-icon-warning-outline"></i>
</div>
<div class="kpi-value">{{ (timeoutResponseRate && timeoutResponseRate.serious_timeout_rate)||0 }}</div>
<p>严重超时应答率 <i class="el-icon-warning info-icon" @mouseenter="showTooltip($event, 'severeTimeoutRate')" @mouseleave="hideTooltip"></i></p>
</div>
<div class="kpi-item stat-item">
<div class="stat-icon form-rate">
<i class="el-icon-document"></i>
</div>
<div class="kpi-value">{{ (formCompletionRate && formCompletionRate.table_filling_rate)||0 }}</div>
<p>表格填写率 <i class="el-icon-warning info-icon" @mouseenter="showTooltip($event, 'formCompletionRate')" @mouseleave="hideTooltip"></i></p>
</div>
</div>
<Tooltip
:visible="tooltip.visible"
:x="tooltip.x"
:y="tooltip.y"
:title="tooltip.title"
:description="tooltip.description"
/>
</div>
</template>
<script setup>
import { defineProps, reactive } from 'vue';
import Tooltip from '@/components/Tooltip.vue';
defineProps({
customerCommunicationRate: {
type: Object,
default: () => ({})
},
averageResponseTime: {
type: Object,
default: () => ({})
},
timeoutResponseRate: {
type: Object,
default: () => ({})
},
severeTimeoutRate: {
type: Object,
default: () => ({})
},
formCompletionRate: {
type: Object,
default: () => ({})
}
});
// 工具提示状态
const tooltip = reactive({
visible: false,
x: 0,
y: 0,
title: '',
description: ''
});
// 指标描述
const metricDescriptions = {
customerCommunicationRate: {
title: '活跃客户沟通率计算方式',
description: '有效沟通的活跃客户数 ÷ 总活跃客户数 × 100%'
},
averageResponseTime: {
title: '平均应答时间计算方式',
description: '所有通话的应答时间总和 ÷ 通话总次数'
},
timeoutResponseRate: {
title: '超时应答率计算方式',
description: '超时应答的通话次数 ÷ 总通话次数 × 100%'
},
severeTimeoutRate: {
title: '严重超时应答率计算方式',
description: '严重超时应答的通话次数 ÷ 总通话次数 × 100%'
},
formCompletionRate: {
title: '表格填写率计算方式',
description: '已完成填写的表格数量 ÷ 应填写的表格总数 × 100%'
}
};
// 显示工具提示
const showTooltip = (event, metricType) => {
const metric = metricDescriptions[metricType];
if (metric) {
tooltip.title = metric.title;
tooltip.description = metric.description;
tooltip.x = event.clientX;
tooltip.y = event.clientY;
tooltip.visible = true;
}
};
// 隐藏工具提示
const hideTooltip = () => {
tooltip.visible = false;
};
</script>
<style scoped>
.stat-card {
background-color: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.card-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 15px;
}
.kpi-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
}
.kpi-item {
text-align: center;
}
.stat-icon {
font-size: 24px;
margin-bottom: 10px;
}
.kpi-value {
font-size: 24px;
font-weight: bold;
}
.kpi-unit {
font-size: 14px;
margin-left: 4px;
}
p {
font-size: 14px;
color: #666;
margin-top: 5px;
}
.customer-rate { color: #409EFF; }
.response-time { color: #67C23A; }
.timeout-rate { color: #E6A23C; }
.severe-timeout-rate { color: #F56C6C; }
.form-rate { color: #909399; }
.info-icon {
color: #909399;
font-size: 12px;
cursor: pointer;
opacity: 0.7;
transition: opacity 0.3s ease;
margin-left: 4px;
}
.info-icon:hover {
opacity: 1;
color: #409EFF;
}
</style>