474 lines
12 KiB
Vue
474 lines
12 KiB
Vue
<template>
|
||
<div class="center-overview">
|
||
<div class="overview-header">
|
||
<h2>整体概览</h2>
|
||
<div class="stats-toggle">
|
||
<button
|
||
:class="['toggle-btn', { active: statsMode === 'monthly' }]"
|
||
@click="switchStatsMode('monthly')"
|
||
>
|
||
按月统计
|
||
</button>
|
||
<button
|
||
:class="['toggle-btn', { active: statsMode === 'period' }]"
|
||
@click="switchStatsMode('period')"
|
||
>
|
||
按期统计
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="overview-grid">
|
||
<div class="overview-card primary">
|
||
<div class="card-header">
|
||
<span class="card-title">
|
||
团队总业绩
|
||
<span class="info-icon" @mouseenter="showTooltip($event, 'teamPerformance')" @mouseleave="hideTooltip">ⓘ</span>
|
||
</span>
|
||
<span class="card-trend positive">{{ totalPerformance.team_current_vs_previous_period_deals_comparison }} vs 上期</span>
|
||
</div>
|
||
<div class="card-value">{{ totalPerformance.current_team_odd_numbers||0 }}</div>
|
||
<div class="card-subtitle">月目标完成率: {{ totalPerformance.team_monthly_target_completion_rate }}</div>
|
||
</div>
|
||
|
||
<div class="overview-card">
|
||
<div class="card-header">
|
||
<span class="card-title">
|
||
活跃组数
|
||
<span class="info-icon" @mouseenter="showTooltip($event, 'activeGroups')" @mouseleave="hideTooltip">ⓘ</span>
|
||
</span>
|
||
<span class="card-trend stable">{{ activeGroups.total_group_count }}/{{ activeGroups.total_group_count }} 组</span>
|
||
</div>
|
||
<div class="card-value">{{ activeGroups.total_group_count }} 组</div>
|
||
<div class="card-subtitle">总人数: {{ activeGroups.total_user_count }}人</div>
|
||
</div>
|
||
|
||
<div class="overview-card">
|
||
<div class="card-header">
|
||
<span class="card-title">
|
||
团队转化率
|
||
<span class="info-icon" @mouseenter="showTooltip($event, 'conversionRate')" @mouseleave="hideTooltip">ⓘ</span>
|
||
</span>
|
||
<span class="card-trend positive">{{ conversionRate.team_current_vs_previous_conversion_rate }} vs 上期</span>
|
||
</div>
|
||
<div class="card-value">{{ conversionRate.center_conversion_rate }}</div>
|
||
<div class="card-subtitle">团队平均转化率: {{ conversionRate.average_conversion_rate }}</div>
|
||
</div>
|
||
|
||
<div class="overview-card">
|
||
<div class="card-header">
|
||
<span class="card-title">
|
||
总通话次数
|
||
<span class="info-icon" @mouseenter="showTooltip($event, 'totalCalls')" @mouseleave="hideTooltip">ⓘ</span>
|
||
</span>
|
||
<span class="card-trend positive">{{ totalCalls.total_call_count_vs_yesterday }} vs 上期</span>
|
||
</div>
|
||
<div class="card-value">{{ totalCalls.total_call_count }}</div>
|
||
<div class="card-subtitle">有效通话: {{ totalCalls.effective_call_count }}次</div>
|
||
</div>
|
||
|
||
<div class="overview-card">
|
||
<div class="card-header">
|
||
<span class="card-title">
|
||
新增客户
|
||
<span class="info-icon" @mouseenter="showTooltip($event, 'newCustomers')" @mouseleave="hideTooltip">ⓘ</span>
|
||
</span>
|
||
<span class="card-trend positive">{{ newCustomers.new_customer_vs_yesterday }} vs 上期</span>
|
||
</div>
|
||
<div class="card-value">{{ newCustomers.new_customer }} 人</div>
|
||
<div class="card-subtitle">意向客户: {{ newCustomers.new_v_customer }}人</div>
|
||
</div>
|
||
|
||
<div class="overview-card">
|
||
<div class="card-header">
|
||
<span class="card-title">
|
||
定金转化
|
||
<span class="info-icon" @mouseenter="showTooltip($event, 'depositConversion')" @mouseleave="hideTooltip">ⓘ</span>
|
||
</span>
|
||
<span class="card-trend positive">{{ depositConversions.deposit_conversion_vs_previous }} vs 上期</span>
|
||
</div>
|
||
<div class="card-value">{{ depositConversions.current_deposit_conversion_rate }}</div>
|
||
<div class="card-subtitle">本月定金转化率: {{ depositConversions.monthly_deposit_conversion_rate }}</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 { computed, reactive, ref } from 'vue'
|
||
import Tooltip from '@/components/Tooltip.vue'
|
||
|
||
// 统计模式状态
|
||
const statsMode = ref('monthly') // 默认按月统计
|
||
|
||
// 定义emit事件
|
||
const emit = defineEmits(['update-check-type'])
|
||
|
||
// 切换统计模式
|
||
const switchStatsMode = (mode) => {
|
||
statsMode.value = mode
|
||
// 向父组件发送事件,修改CheckType的值
|
||
const checkTypeValue = mode === 'monthly' ? 'month' : 'period'
|
||
emit('update-check-type', checkTypeValue)
|
||
console.log('切换统计模式:', mode, '发送CheckType值:', checkTypeValue)
|
||
}
|
||
|
||
// 中心整体概览组件
|
||
const props = defineProps({
|
||
overallTeamPerformance: {
|
||
type: Object,
|
||
default: () => ({
|
||
totalPerformance: {},
|
||
activeGroups: {},
|
||
conversionRate: {},
|
||
totalCalls: {},
|
||
newCustomers: {},
|
||
depositConversions: {}
|
||
})
|
||
}
|
||
})
|
||
// 计算属性
|
||
const totalPerformance = computed(() => {
|
||
return props.overallTeamPerformance.totalPerformance
|
||
})
|
||
|
||
const activeGroups = computed(() => {
|
||
return props.overallTeamPerformance.activeGroups
|
||
})
|
||
|
||
const conversionRate = computed(() => {
|
||
return props.overallTeamPerformance.conversionRate
|
||
})
|
||
|
||
const totalCalls = computed(() => {
|
||
return props.overallTeamPerformance.totalCalls
|
||
})
|
||
|
||
const newCustomers = computed(() => {
|
||
return props.overallTeamPerformance.newCustomers
|
||
})
|
||
|
||
const depositConversions = computed(() => {
|
||
return props.overallTeamPerformance.depositConversions
|
||
})
|
||
|
||
// 工具提示状态
|
||
const tooltip = reactive({
|
||
visible: false,
|
||
x: 0,
|
||
y: 0,
|
||
title: '',
|
||
description: ''
|
||
})
|
||
|
||
// 指标描述
|
||
const metricDescriptions = {
|
||
teamPerformance: {
|
||
title: '团队总业绩计算方式',
|
||
description: '所有团队成员在选定时间范围内的成交金额总和,包括全款订单和定金订单的累计业绩。'
|
||
},
|
||
activeGroups: {
|
||
title: '活跃组数计算方式',
|
||
description: '当前有成员在线且有业务活动的团队组数,以及各组的总人数统计。'
|
||
},
|
||
conversionRate: {
|
||
title: '团队转化率计算方式',
|
||
description: '团队总成交单数 ÷ 团队总新增客户数 × 100%'
|
||
},
|
||
totalCalls: {
|
||
title: '总通话次数计算方式',
|
||
description: '所有团队成员在选定时间范围内的通话总次数,包括外呼、接听等所有通话记录。'
|
||
},
|
||
newCustomers: {
|
||
title: '新增客户计算方式',
|
||
description: '所有团队成员在选定时间范围内新建档的客户总数,包括意向客户和潜在客户。'
|
||
},
|
||
depositConversion: {
|
||
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 lang="scss" scoped>
|
||
.center-overview {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 0.5rem;
|
||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||
|
||
.overview-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 1rem;
|
||
|
||
.stats-toggle {
|
||
display: flex;
|
||
background: #f8fafc;
|
||
border-radius: 8px;
|
||
padding: 4px;
|
||
gap: 2px;
|
||
|
||
.toggle-btn {
|
||
padding: 8px 16px;
|
||
border: none;
|
||
background: transparent;
|
||
border-radius: 6px;
|
||
font-size: 0.875rem;
|
||
font-weight: 500;
|
||
color: #64748b;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
|
||
&:hover {
|
||
background: #e2e8f0;
|
||
color: #475569;
|
||
}
|
||
|
||
&.active {
|
||
background: #3b82f6;
|
||
color: white;
|
||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
h2 {
|
||
font-size: 1.3rem;
|
||
font-weight: 600;
|
||
color: #1e293b;
|
||
margin: 0 0 1.5rem 0;
|
||
}
|
||
|
||
.overview-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 1rem;
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.overview-card {
|
||
padding: 0.5rem;
|
||
border: 1px solid #e2e8f0;
|
||
border-radius: 10px;
|
||
transition: all 0.3s ease;
|
||
|
||
&:hover {
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
&.primary {
|
||
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
|
||
color: white;
|
||
border: none;
|
||
|
||
.card-title, .card-subtitle {
|
||
color: rgba(255, 255, 255, 0.9);
|
||
}
|
||
|
||
.card-title .info-icon {
|
||
color: rgba(255, 255, 255, 0.7);
|
||
|
||
&:hover {
|
||
color: rgba(255, 255, 255, 1);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
.card-trend {
|
||
color: rgba(255, 255, 255, 0.8);
|
||
}
|
||
|
||
.card-value {
|
||
color: white;
|
||
}
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.card-title {
|
||
color: #64748b;
|
||
font-size: 0.9rem;
|
||
font-weight: 500;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
|
||
.info-icon {
|
||
color: #94a3b8;
|
||
font-size: 0.75rem;
|
||
cursor: pointer;
|
||
opacity: 0.7;
|
||
transition: all 0.2s ease;
|
||
margin-left: 0.25rem;
|
||
|
||
&:hover {
|
||
opacity: 1;
|
||
color: #3b82f6;
|
||
transform: scale(1.1);
|
||
}
|
||
}
|
||
}
|
||
|
||
.card-trend {
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
|
||
&.positive {
|
||
color: #059669;
|
||
}
|
||
|
||
&.negative {
|
||
color: #dc2626;
|
||
}
|
||
|
||
&.stable {
|
||
color: #7c3aed;
|
||
}
|
||
}
|
||
|
||
.card-value {
|
||
font-size: 1.8rem;
|
||
font-weight: bold;
|
||
color: #1e293b;
|
||
margin-bottom: 0.25rem;
|
||
}
|
||
|
||
.card-subtitle {
|
||
color: #94a3b8;
|
||
font-size: 0.8rem;
|
||
}
|
||
}
|
||
|
||
.trend-section {
|
||
h3 {
|
||
font-size: 1.1rem;
|
||
font-weight: 600;
|
||
color: #1e293b;
|
||
margin: 0 0 1rem 0;
|
||
}
|
||
|
||
.trend-charts {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 1rem;
|
||
}
|
||
|
||
.trend-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
padding: 0.75rem;
|
||
background: #f8fafc;
|
||
border-radius: 8px;
|
||
|
||
.trend-label {
|
||
font-size: 0.85rem;
|
||
color: #64748b;
|
||
min-width: 80px;
|
||
}
|
||
|
||
.trend-bar {
|
||
flex: 1;
|
||
height: 6px;
|
||
background: #e2e8f0;
|
||
border-radius: 3px;
|
||
overflow: hidden;
|
||
|
||
.trend-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, #10b981, #059669);
|
||
border-radius: 3px;
|
||
transition: width 0.3s ease;
|
||
}
|
||
}
|
||
|
||
.trend-value {
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
color: #059669;
|
||
min-width: 40px;
|
||
text-align: right;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 移动端适配
|
||
@media (max-width: 768px) {
|
||
.center-overview {
|
||
padding: 1rem;
|
||
|
||
.overview-header {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 1rem;
|
||
|
||
.stats-toggle {
|
||
.toggle-btn {
|
||
padding: 6px 12px;
|
||
font-size: 0.8rem;
|
||
}
|
||
}
|
||
}
|
||
|
||
.overview-grid {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.overview-card {
|
||
padding: 1rem;
|
||
|
||
.card-value {
|
||
font-size: 1.5rem;
|
||
}
|
||
}
|
||
|
||
.trend-charts {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.center-overview {
|
||
.overview-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
}
|
||
</style> |