修改统计指标组件和父组件的数据处理逻辑,确保在数据未加载或返回null时显示默认值0。同时将props类型从Number改为Object,并添加默认空对象防止访问属性错误。
192 lines
5.2 KiB
Vue
192 lines
5.2 KiB
Vue
<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> |