feat(组件): 为KPI指标添加工具提示功能并优化业绩显示

1. 在KpiMetrics和CenterOverview组件中添加工具提示功能,显示各指标的计算说明
2. 修改GroupComparison组件中业绩数据的显示方式,移除货币格式化
3. 添加Tooltip组件用于显示指标说明
4. 优化工具提示的样式和交互效果
This commit is contained in:
2025-08-22 21:39:36 +08:00
parent 0e0d297da7
commit af07a1e175
3 changed files with 262 additions and 17 deletions

View File

@@ -4,7 +4,12 @@
<div class="overview-grid">
<div class="overview-card primary">
<div class="card-header">
<span class="card-title">中心总业绩</span>
<span class="card-title">
中心总业绩
<i
@mouseenter="showTooltip($event, 'centerPerformance')"
@mouseleave="hideTooltip"></i>
</span>
<span class="card-trend positive">{{ props.overallData.CenterPerformance?.center_monthly_vs_previous_deals }} vs 上期</span>
</div>
<div class="card-value">{{ props.overallData.CenterPerformance.center_monthly_deal_count || '552,000' }} </div>
@@ -13,7 +18,12 @@
<div class="overview-card">
<div class="card-header">
<span class="card-title">活跃组数</span>
<span class="card-title">
活跃组数
<i
@mouseenter="showTooltip($event, 'activeGroups')"
@mouseleave="hideTooltip"></i>
</span>
<span class="card-trend stable">{{ props.overallData.TotalGroupCount?.center_total_team_count}}/{{ props.overallData.TotalGroupCount?.center_total_team_count }} </span>
</div>
<div class="card-value">{{ props.overallData.TotalGroupCount?.center_total_team_count || '5' }} </div>
@@ -22,7 +32,12 @@
<div class="overview-card">
<div class="card-header">
<span class="card-title">中心转化率</span>
<span class="card-title">
中心转化率
<i
@mouseenter="showTooltip($event, 'conversionRate')"
@mouseleave="hideTooltip"></i>
</span>
<span class="card-trend positive">{{ props.overallData.CenterConversionRate?.center_monthly_vs_previous_deals }}vs 上期</span>
</div>
<div class="card-value">{{ props.overallData.CenterConversionRate?.center_conversion_rate || '5.2' }}</div>
@@ -31,7 +46,12 @@
<div class="overview-card">
<div class="card-header">
<span class="card-title">总通话次数</span>
<span class="card-title">
总通话次数
<i
@mouseenter="showTooltip($event, 'totalCalls')"
@mouseleave="hideTooltip"></i>
</span>
<span class="card-trend positive">{{ props.overallData.TotalCallCount?.total_call_count_vs_yesterday}} vs 上期</span>
</div>
<div class="card-value">{{ props.overallData.TotalCallCount?.total_call_count || '1,247' }} </div>
@@ -40,7 +60,12 @@
<div class="overview-card">
<div class="card-header">
<span class="card-title">新增客户</span>
<span class="card-title">
新增客户
<i
@mouseenter="showTooltip($event, 'newCustomers')"
@mouseleave="hideTooltip"></i>
</span>
<span class="card-trend positive">{{ props.overallData.NewCustomer?.center_new_leads_vs_previous_period }} vs 上期</span>
</div>
<div class="card-value">{{ props.overallData.NewCustomer?.center_new_leads_count || '117' }} </div>
@@ -49,7 +74,12 @@
<div class="overview-card">
<div class="card-header">
<span class="card-title">定金转化</span>
<span class="card-title">
定金转化
<i
@mouseenter="showTooltip($event, 'depositConversion')"
@mouseleave="hideTooltip"></i>
</span>
<span class="card-trend positive">{{ props.overallData.DepositConversionRate?.center_deposit_conversion_vs_previous }} vs 上期</span>
</div>
<div class="card-value">{{ props.overallData.DepositConversionRate?.center_current_deposit_conversion_rate || '0' }} </div>
@@ -57,10 +87,22 @@
</div>
</div>
<!-- Tooltip组件 -->
<Tooltip
:visible="tooltip.visible"
:x="tooltip.x"
:y="tooltip.y"
:title="tooltip.title"
:description="tooltip.description"
/>
</div>
</template>
<script setup>
import { reactive } from 'vue'
import Tooltip from '@/components/Tooltip.vue'
// 中心整体概览组件
const props = defineProps({
overallData: {
@@ -75,6 +117,58 @@ const props = defineProps({
})
}
})
// Tooltip状态管理
const tooltip = reactive({
visible: false,
x: 0,
y: 0,
title: '',
description: ''
})
// 指标描述信息
const metricDescriptions = {
centerPerformance: {
title: '中心总业绩计算方式',
description: '统计本月中心所有销售团队的成交单数总和,与上期同比计算增长率。月目标完成率 = 当月实际成交单数 / 月度目标单数 × 100%'
},
activeGroups: {
title: '活跃组数计算方式',
description: '统计当前有业务活动的销售团队数量。活跃标准:本月有通话记录或成交记录的团队。总人数为所有活跃团队的人员总和'
},
conversionRate: {
title: '中心转化率计算方式',
description: '中心转化率 = 总成交客户数 / 总接触客户数 × 100%。与上期对比显示增长趋势,行业平均值作为参考基准'
},
totalCalls: {
title: '总通话次数计算方式',
description: '统计所有销售人员的通话总次数包括接听和拨出。有效通话指通话时长超过30秒的通话记录'
},
newCustomers: {
title: '新增客户计算方式',
description: '统计本期新录入系统的客户数量。意向客户指经过初步沟通,有明确购买意向的客户数量'
},
depositConversion: {
title: '定金转化计算方式',
description: '定金转化率 = 缴纳定金客户数 / 意向客户总数 × 100%。平均定金转化率为本月日均转化率'
}
}
// 显示tooltip
const showTooltip = (event, metricType) => {
const rect = event.target.getBoundingClientRect()
tooltip.visible = true
tooltip.x = rect.left + rect.width / 2
tooltip.y = rect.top - 10
tooltip.title = metricDescriptions[metricType].title
tooltip.description = metricDescriptions[metricType].description
}
// 隐藏tooltip
const hideTooltip = () => {
tooltip.visible = false
}
</script>
<style lang="scss" scoped>
@@ -169,6 +263,39 @@ const props = defineProps({
color: #94a3b8;
font-size: 0.8rem;
}
.info-icon {
display: inline-block;
width: 16px;
height: 16px;
background: #409eff;
color: white;
border-radius: 50%;
text-align: center;
line-height: 16px;
font-size: 12px;
font-weight: bold;
margin-left: 6px;
cursor: pointer;
opacity: 0.8;
transition: all 0.3s ease;
&:hover {
opacity: 1;
transform: scale(1.1);
background: #66b3ff;
}
}
// 主要卡片中的图标样式
&.primary .info-icon {
background: rgba(255, 255, 255, 0.3);
color: white;
&:hover {
background: rgba(255, 255, 255, 0.5);
}
}
}
.trend-section {

View File

@@ -32,7 +32,7 @@
<div class="key-metrics">
<div class="mini-metric">
<span class="mini-label">业绩</span>
<span class="mini-value">{{ formatCurrency(group.todayPerformance) }}</span>
<span class="mini-value">{{ group.todayPerformance }}</span>
</div>
<div class="mini-metric">
<span class="mini-label">转化</span>
@@ -175,22 +175,22 @@ const processedGroups = computed(() => {
})
}
// 处理 formal_plural 数据
// 处理 formal_plural 数据(业绩数据)
if (props.groupList.formal_plural) {
console.log('Processing formal_plural:', props.groupList.formal_plural)
Object.entries(props.groupList.formal_plural).forEach(([managerName, teamData]) => {
if (typeof teamData === 'object' && teamData !== null) {
Object.entries(teamData).forEach(([teamName, count]) => {
Object.entries(teamData).forEach(([teamName, performance]) => {
const existingGroup = groups.find(g => g.id === `${managerName}-${teamName}` || g.id === managerName)
if (existingGroup) {
existingGroup.newClients = count || 0
existingGroup.todayPerformance = performance || 0
}
})
} else if (typeof teamData === 'number') {
// 处理直接数值的情况
const existingGroup = groups.find(g => g.id === managerName)
if (existingGroup) {
existingGroup.newClients = teamData || 0
existingGroup.todayPerformance = teamData || 0
}
}
})

View File

@@ -17,7 +17,12 @@
<!-- 1. 主卡片中心总业绩 -->
<div class="kpi-card primary">
<div class="card-header">
<span class="card-label">总成交单数</span>
<span class="card-label">
总成交单数
<span class="info-icon"
@mouseenter="showTooltip($event, 'totalSales')"
@mouseleave="hideTooltip">!</span>
</span>
<span class="card-trend" :class="getTrendClass(kpiData.totalSales.trend)">
{{ formatTrend(kpiData.totalSales.trend) }} vs 上期
</span>
@@ -34,7 +39,12 @@
<!-- 2. 定金转化率 -->
<div class="kpi-card">
<div class="card-header">
<span class="card-label">定金转化率</span>
<span class="card-label">
定金转化率
<span class="info-icon"
@mouseenter="showTooltip($event, 'depositConversion')"
@mouseleave="hideTooltip">!</span>
</span>
<span class="card-trend" :class="getTrendClass(kpiData.activeTeams.trend)">
{{ formatTrend(kpiData.activeTeams.trend, true) }} vs 上期
</span>
@@ -51,7 +61,12 @@
<!-- 3. 总通话次数 -->
<div class="kpi-card">
<div class="card-header">
<span class="card-label">总通话</span>
<span class="card-label">
总通话
<span class="info-icon"
@mouseenter="showTooltip($event, 'totalCalls')"
@mouseleave="hideTooltip">!</span>
</span>
<span class="card-trend" :class="getTrendClass(kpiData.totalCalls.trend)">
{{ formatTrend(kpiData.totalCalls.trend) }} vs 上期
</span>
@@ -68,7 +83,12 @@
<!-- 4. 新增客户 -->
<div class="kpi-card">
<div class="card-header">
<span class="card-label">新增客户</span>
<span class="card-label">
新增客户
<span class="info-icon"
@mouseenter="showTooltip($event, 'newCustomers')"
@mouseleave="hideTooltip">!</span>
</span>
<span class="card-trend" :class="getTrendClass(kpiData.newCustomers.trend)">
{{ formatTrend(kpiData.newCustomers.trend) }} vs 上期
</span>
@@ -85,7 +105,12 @@
<!-- 5. 中心转化率 -->
<div class="kpi-card">
<div class="card-header">
<span class="card-label">转化率</span>
<span class="card-label">
转化率
<span class="info-icon"
@mouseenter="showTooltip($event, 'conversionRate')"
@mouseleave="hideTooltip">!</span>
</span>
<span class="card-trend" :class="getTrendClass(kpiData.conversionRate.trend)">
{{ formatTrend(kpiData.conversionRate.trend, true) }} vs 上期
</span>
@@ -96,11 +121,21 @@
</div>
</div>
</div>
<!-- Tooltip组件 -->
<Tooltip
:visible="tooltip.visible"
:x="tooltip.x"
:y="tooltip.y"
:title="tooltip.title"
:description="tooltip.description"
/>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { ref, computed, watch, reactive } from 'vue';
import Tooltip from '@/components/Tooltip.vue';
// 定义props
const props = defineProps({
@@ -117,6 +152,54 @@ const props = defineProps({
const isLoading = ref(false);
const error = ref(null);
// Tooltip状态管理
const tooltip = reactive({
visible: false,
x: 0,
y: 0,
title: '',
description: ''
});
// 指标描述
const metricDescriptions = {
totalSales: {
title: '总成交单数计算方式',
description: '统计公司在选定时间范围内所有已完成的成交订单总数,包括各个中心、各个团队的成交业绩汇总。'
},
depositConversion: {
title: '定金转化率计算方式',
description: '定金转化率 = (支付定金客户数 / 意向客户总数) × 100%,反映从意向客户到付费客户的转化效果。'
},
totalCalls: {
title: '总通话次数计算方式',
description: '统计公司所有销售人员在选定时间范围内的外呼和接听通话总次数,包括有效通话和无效通话。'
},
newCustomers: {
title: '新增客户计算方式',
description: '统计在选定时间范围内新建档的客户数量,不包括重复录入的客户,按首次录入时间计算。'
},
conversionRate: {
title: '中心转化率计算方式',
description: '中心转化率 = (成交客户数 / 总客户数) × 100%,反映整体销售转化效果和业务质量。'
}
};
// 显示tooltip
function showTooltip(event, metricType) {
const rect = event.target.getBoundingClientRect();
tooltip.visible = true;
tooltip.x = rect.left + rect.width / 2;
tooltip.y = rect.top - 10;
tooltip.title = metricDescriptions[metricType].title;
tooltip.description = metricDescriptions[metricType].description;
}
// 隐藏tooltip
function hideTooltip() {
tooltip.visible = false;
}
// 计算属性将API数据转换为组件需要的格式
const kpiData = computed(() => {
const data = props.kpiData;
@@ -355,4 +438,39 @@ function formatTrend(trend, isPercentagePoint = false) {
font-size: 40px;
}
}
/* 感叹号图标样式 */
.info-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
background: rgba(255, 255, 255, 0.2);
color: #fff;
border-radius: 50%;
font-size: 10px;
font-weight: bold;
margin-left: 6px;
cursor: pointer;
opacity: 0.7;
transition: all 0.2s ease;
}
.info-icon:hover {
opacity: 1;
background: rgba(255, 255, 255, 0.3);
transform: scale(1.1);
}
/* 非主卡片中的图标样式 */
.kpi-card:not(.primary) .info-icon {
background: rgba(0, 0, 0, 0.1);
color: #666;
}
.kpi-card:not(.primary) .info-icon:hover {
background: rgba(0, 0, 0, 0.15);
color: #333;
}
</style>