feat(api): 新增销售漏斗和黄金联络时段API接口
feat(views): 添加销售漏斗和黄金联络时段数据展示功能 refactor(views): 优化客户详情组件的数据处理逻辑 fix(views): 修复业绩数据显示字段不一致问题 style(views): 调整路由导航顶栏样式
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user