feat(api): 新增销售漏斗和黄金联络时段API接口

feat(views): 添加销售漏斗和黄金联络时段数据展示功能
refactor(views): 优化客户详情组件的数据处理逻辑
fix(views): 修复业绩数据显示字段不一致问题
style(views): 调整路由导航顶栏样式
This commit is contained in:
2025-08-19 21:45:15 +08:00
parent 182130ba6a
commit 2b75f1b568
8 changed files with 326 additions and 138 deletions

View File

@@ -1,18 +1,17 @@
<template>
<div class="sales-dashboard">
<!-- 页面加载状态 -->
<!-- <Loading :visible="isPageLoading" text="正在加载数据..." /> -->
<Loading :visible="isPageLoading" text="正在加载数据..." />
<!-- 顶部导航栏 -->
<!-- 销售时间线区域 -->
<section class="timeline-section">
<div class="section-header">
<!-- 动态顶栏根据是否有路由参数显示不同内容 -->
<!-- 路由跳转时的顶栏面包屑 + 姓名 -->
<div v-if="isRouteNavigation" class="route-header">
<div class="breadcrumb">
<span class="breadcrumb-item" @click="goBack">团队管理</span>
<span class="breadcrumb-separator">></span>
<span class="breadcrumb-item current"> {{ routeUserName }}数据驾驶舱</span>
<div v-if="isRouteNavigation" class="route-header" style="display: flex; justify-content: space-between; align-items: center;">
<div class="breadcrumb" style="display: flex; flex-direction: column;">
<span class="breadcrumb-item" @click="goBack">团队管理 >{{ routeUserName }}</span>
<span class="breadcrumb-item current"> 数据驾驶舱</span>
</div>
<div class="user-name">
{{ routeUserName }}
@@ -67,6 +66,7 @@
:selected-contact="selectedContact"
:form-info="formInfo"
:chat-info="chatRecords"
:call-info="callRecords"
@view-form-data="handleViewFormData"
@view-chat-data="handleViewChatData"
@view-call-data="handleViewCallData" />
@@ -83,7 +83,11 @@
<h2>客户详情</h2>
</div>
<div class="section-content">
<CustomerDetail :selected-contact="selectedContact" />
<CustomerDetail
:selected-contact="selectedContact"
:form-info="formInfo"
:chat-records="chatRecords"
:call-records="callRecords" />
</div>
</section>
</main>
@@ -101,13 +105,12 @@
v-else
:kpi-data="kpiData"
:funnel-data="funnelData"
:contact-time-data="contactTimeData"
:contact-time-data="goldContactTime"
:statistics-data="statisticsData"
:urgent-problem-data="urgentProblemData"
/>
</div>
</section>
</div>
</template>
@@ -123,7 +126,7 @@ import UserDropdown from "@/components/UserDropdown.vue";
import Loading from "@/components/Loading.vue";
import {getCustomerAttendance,getTodayCall,getProblemDistribution,getTableFillingRate,getAverageResponseTime,
getWeeklyActiveCommunicationRate,getTimeoutResponseRate,getCustomerCallInfo,getCustomerChatInfo,getCustomerFormInfo,
getConversionRateAndAllocatedData,getCustomerAttendanceAfterClass4,getPayMoneyCustomers} from "@/api/api.js"
getConversionRateAndAllocatedData,getCustomerAttendanceAfterClass4,getPayMoneyCustomers,getSalesFunnel,getGoldContactTime} from "@/api/api.js"
// 路由实例
const router = useRouter();
@@ -429,10 +432,7 @@ async function getTimeline() {
weChat_avatar: customer.weChat_avatar,
pay_status: customer.pay_status
})
// 后三个阶段的客户数据已存储在courseCustomers['课1-4']中不需要合并到customersList
}
}
// 成交阶段
@@ -513,7 +513,15 @@ async function getCustomerCall() {
const res = await getCustomerCallInfo(params)
if(res.code === 200) {
callRecords.value = res.data
/**
* "data": {
"user_name": "常琳",
"customer_name": "191桐桐爸爸高一男",
"record_file_addr_list": [
"http://192.168.3.112:5000/api/record/download/杨振彦-20分钟通话-25-08-19_07-23-37-744009-835.mp3"
]
}
*/
}
} catch (error) {
// 静默处理错误
@@ -531,10 +539,26 @@ const selectedContact = computed(() => {
return MOCK_DATA.contacts.find((c) => c.id === selectedContactId.value) || null;
});
const funnelData = computed(() => ({
labels: ["线索", "沟通", "意向", "预约", "成交"],
data: MOCK_DATA.personalFunnel,
}));
const funnelData = computed(() => {
if (!SalesFunnel.value || !SalesFunnel.value.sale_funnel) {
return {
labels: ["线索总数", "有效沟通", "到课数据", "预付定金", "成功签单"],
data: [0, 0, 0, 0, 0]
};
}
const funnel = SalesFunnel.value.sale_funnel;
return {
labels: ["线索总数", "有效沟通", "到课数据", "预付定金", "成功签单"],
data: [
funnel.线索总数 || 0,
funnel.有效沟通 || 0,
funnel.到课数据 || 0,
funnel.预付定金 || 0,
funnel.成功签单 || 0
]
};
});
const contactTimeData = computed(() => ({
labels: MOCK_DATA.contactTimeAnalysis.labels,
@@ -682,7 +706,6 @@ const handleStageSelect = (stage, extraData = null) => {
currentFilteredCustomers.value = [];
}
};
const handleViewFormData = async (contact) => {
// 获取客户表单数据
await getCustomerForm();
@@ -700,18 +723,54 @@ const handleViewChatData = async (contact) => {
const handleViewCallData = (contact) => {
// TODO: 实现通话录音查看逻辑
};
// 销售漏斗
const SalesFunnel = ref([])
async function CenterGetSalesFunnel() {
const params = getRequestParams()
const hasParams = params.user_name
const res = await getSalesFunnel(hasParams?params:undefined)
if(res.code === 200){
SalesFunnel.value = res.data
/**
* "data": {
"user_name": "常琳",
"user_level": 1,
"sale_funnel": {
"线索总数": 11,
"有效沟通": 9,
"到课数据": 8,
"预付定金": 0,
"成功签单": 0
}
}
*/
}
}
// 黄金联络时间段
const goldContactTime = ref([])
async function CenterGetGoldContactTime() {
const params = getRequestParams()
const hasParams = params.user_name
const res = await getGoldContactTime(hasParams?params:undefined)
if(res.code === 200){
goldContactTime.value = res.data
}
}
// LIFECYCLE HOOKS
onMounted(async () => {
try {
isPageLoading.value = true
await getCoreKpi()
await CenterGetGoldContactTime()
await CenterGetSalesFunnel()
await getCustomerForm()
await getCustomerChat()
await getUrgentProblem()
await getCustomerCall()
await getTimeline()
await getCustomerPayMoney()
// 等待数据加载完成后选择默认客户
await nextTick();
@@ -1323,10 +1382,8 @@ $primary: #3b82f6;
// 路由导航顶栏样式
.route-header {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 0 2rem;
.breadcrumb {
display: flex;