feat(销售页面): 优化表单数据显示逻辑并添加二级顶部API
重构RawDataCards组件表单数据显示逻辑,支持两种不同格式的数据源 在sale.vue中添加表单数据加载功能并传递给子组件 添加secondTop.js包含二级顶部页面所需的所有API接口 调整SalesTimelineWithTaskList.vue中的未到课显示文案
This commit is contained in:
60
my-vue-app/src/api/secondTop.js
Normal file
60
my-vue-app/src/api/secondTop.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import https from '../utils/https'
|
||||||
|
|
||||||
|
// 中心总业绩 /api/v1/level_four/overview/overall_center_performance
|
||||||
|
export const getOverallCenterPerformance = (params) => {
|
||||||
|
return https.post('/api/v1/level_four/overview/overall_center_performance', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 活跃组数 /api/v1/level_four/overview/total_group_count
|
||||||
|
export const getTotalGroupCount = (params) => {
|
||||||
|
return https.post('/api/v1/level_four/overview/total_group_count', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 中心转化率 /api/v1/level_four/overview/center_conversion_rate
|
||||||
|
export const getCenterConversionRate = (params) => {
|
||||||
|
return https.post('/api/v1/level_four/overview/center_conversion_rate', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 中心通话次数 /api/v1/level_four/overview/total_call_count
|
||||||
|
export const getTotalCallCount = (params) => {
|
||||||
|
return https.post('/api/v1/level_four/overview/total_call_count', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增客户 /api/v1/level_four/overview/new_customer
|
||||||
|
export const getNewCustomer = (params) => {
|
||||||
|
return https.post('/api/v1/level_four/overview/new_customer', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定金转化 /api/v1/level_four/overview/deposit_conversion_rate
|
||||||
|
export const getDepositConversionRate = (params) => {
|
||||||
|
return https.post('/api/v1/level_four/overview/deposit_conversion_rate', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户类型 /api/v1/level_four/overview/customer_type_distribution
|
||||||
|
export const getCustomerTypeDistribution = (params) => {
|
||||||
|
return https.post('/api/v1/level_four/overview/customer_type_distribution', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户迫切解决的问题 /api/v1/level_four/overview/urgent_need_to_address
|
||||||
|
export const getUrgentNeedToAddress = (params) => {
|
||||||
|
return https.post('/api/v1/level_four/overview/urgent_need_to_address', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取高级经理列表 /api/v1/level_four/overview/center_advanced_managers
|
||||||
|
export const getCenterAdvancedManagerList = (params) => {
|
||||||
|
return https.post('/api/v1/level_four/overview/center_advanced_managers', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据传来的高级经理名字来获取组业绩排名 /api/v1/level_four/overview/team_ranking
|
||||||
|
export const getTeamRanking = (params) => {
|
||||||
|
return https.post('/api/v1/level_four/overview/team_ranking', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据传来的组名字来获取组业绩详情 /api/v1/level_four/overview/team_ranking_info
|
||||||
|
export const getTeamRankingInfo = (params) => {
|
||||||
|
return https.post('/api/v1/level_four/overview/team_ranking_info', params)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -113,6 +113,10 @@ const props = defineProps({
|
|||||||
selectedContact: {
|
selectedContact: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
formInfo: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -121,26 +125,59 @@ const activeTab = ref('chat')
|
|||||||
|
|
||||||
// 表单字段数据
|
// 表单字段数据
|
||||||
const formFields = computed(() => {
|
const formFields = computed(() => {
|
||||||
const contact = props.selectedContact
|
const formData = props.formInfo
|
||||||
if (!contact || !contact.details) {
|
if (!formData || Object.keys(formData).length === 0) {
|
||||||
return [
|
return [
|
||||||
{ label: '姓名', value: '暂无数据' },
|
{ label: '姓名', value: '暂无数据' },
|
||||||
{ label: '联系方式', value: '暂无数据' },
|
{ label: '联系方式', value: '暂无数据' },
|
||||||
{ label: '意向课程', value: '暂无数据' },
|
{ label: '孩子信息', value: '暂无数据' },
|
||||||
{ label: '预算范围', value: '暂无数据' }
|
{ label: '地区', value: '暂无数据' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
// 检查是否为第一种格式(包含name, mobile等字段)
|
||||||
{ label: '客户姓名', value: contact.name || '暂无' },
|
if (formData.name || formData.mobile || formData.child_name) {
|
||||||
{ label: '孩子姓名', value: contact.details.childName || '暂无' },
|
const fields = [
|
||||||
{ label: '孩子年龄', value: contact.details.childAge ? `${contact.details.childAge}岁` : '暂无' },
|
{ label: '客户姓名', value: formData.name || '暂无' },
|
||||||
{ label: '关注问题', value: contact.details.concerns?.join('、') || '暂无' },
|
{ label: '联系方式', value: formData.mobile || '暂无' },
|
||||||
{ label: '预算范围', value: contact.details.budget || '暂无' },
|
{ label: '孩子姓名', value: formData.child_name || '暂无' },
|
||||||
{ label: '偏好时间', value: contact.details.preferredTime || '暂无' },
|
{ label: '孩子性别', value: formData.child_gender || '暂无' },
|
||||||
{ label: '销售阶段', value: contact.salesStage || '暂无' },
|
{ label: '孩子教育', value: formData.child_education || '暂无' },
|
||||||
{ label: '健康度', value: contact.health ? `${contact.health}%` : '暂无' }
|
{ label: '关系', value: formData.child_relation || '暂无' },
|
||||||
|
{ label: '职业', value: formData.occupation || '暂无' },
|
||||||
|
{ label: '地区', value: formData.territory || '暂无' }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 如果有additional_info,添加前3个问题
|
||||||
|
if (formData.additional_info && Array.isArray(formData.additional_info)) {
|
||||||
|
formData.additional_info.slice(0, 3).forEach((item, index) => {
|
||||||
|
fields.push({
|
||||||
|
label: `问题${index + 1}`,
|
||||||
|
value: `${item.topic}: ${item.answer}`
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二种格式(expandXXX字段)
|
||||||
|
const fields = [
|
||||||
|
{ label: '孩子姓名', value: formData.expandTwentyNine || '暂无' },
|
||||||
|
{ label: '孩子性别', value: formData.expandTwentyFive || '暂无' },
|
||||||
|
{ label: '孩子教育', value: formData.expandTwo || '暂无' },
|
||||||
|
{ label: '关系', value: formData.expandTwentyOne || '暂无' },
|
||||||
|
{ label: '职业', value: formData.expandOne || '暂无' },
|
||||||
|
{ label: '学习状态', value: formData.expandFive || '暂无' },
|
||||||
|
{ label: '沟通情况', value: formData.expandEight || '暂无' },
|
||||||
|
{ label: '主要问题', value: formData.expandTwentySeven || '暂无' },
|
||||||
|
{ label: '关注领域', value: formData.expandFifteen || '暂无' },
|
||||||
|
{ label: '学习成绩', value: formData.expandFourteen || '暂无' },
|
||||||
|
{ label: '孩子数量', value: formData.expandTwenty || '暂无' },
|
||||||
|
{ label: '预期时间', value: formData.expandThirty || '暂无' }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
return fields.filter(field => field.value !== '暂无' && field.value !== null)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 聊天数据
|
// 聊天数据
|
||||||
@@ -371,7 +408,7 @@ const callRecords = computed(() => {
|
|||||||
|
|
||||||
.tab-content {
|
.tab-content {
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
max-height: 400px;
|
max-height: 450px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
.content-header {
|
.content-header {
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="course-details-content">
|
<div class="course-details-content">
|
||||||
<div v-if="!selectedContactDetails.class_situation || Object.keys(selectedContactDetails.class_situation).length === 0" class="no-data">
|
<div v-if="!selectedContactDetails.class_situation || Object.keys(selectedContactDetails.class_situation).length === 0" class="no-data">
|
||||||
暂无到课记录
|
未到课
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="course-lessons">
|
<div v-else class="course-lessons">
|
||||||
<div
|
<div
|
||||||
@@ -300,7 +300,7 @@ const getAttendedLessons = (classSituation, classNum) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果没有 class_num,则使用 class_situation
|
// 如果没有 class_num,则使用 class_situation
|
||||||
if (!classSituation) return '暂无到课记录';
|
if (!classSituation) return '未到课';
|
||||||
|
|
||||||
if (Array.isArray(classSituation)) {
|
if (Array.isArray(classSituation)) {
|
||||||
return classSituation.join(' ');
|
return classSituation.join(' ');
|
||||||
@@ -310,10 +310,10 @@ const getAttendedLessons = (classSituation, classNum) => {
|
|||||||
.map(key => parseInt(key))
|
.map(key => parseInt(key))
|
||||||
.filter(num => !isNaN(num))
|
.filter(num => !isNaN(num))
|
||||||
.sort((a, b) => a - b);
|
.sort((a, b) => a - b);
|
||||||
return lessonNumbers.length > 0 ? lessonNumbers.join(' ') : '暂无到课记录';
|
return lessonNumbers.length > 0 ? lessonNumbers.join(' ') : '未到课';
|
||||||
}
|
}
|
||||||
|
|
||||||
return '暂无到课记录';
|
return '未到课';
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,7 @@
|
|||||||
<div class="section-content">
|
<div class="section-content">
|
||||||
<RawDataCards
|
<RawDataCards
|
||||||
:selected-contact="selectedContact"
|
:selected-contact="selectedContact"
|
||||||
|
:form-info="formInfo"
|
||||||
@view-form-data="handleViewFormData"
|
@view-form-data="handleViewFormData"
|
||||||
@view-chat-data="handleViewChatData"
|
@view-chat-data="handleViewChatData"
|
||||||
@view-call-data="handleViewCallData" />
|
@view-call-data="handleViewCallData" />
|
||||||
@@ -481,6 +482,98 @@ async function getCustomerForm() {
|
|||||||
const res = await getCustomerFormInfo(params)
|
const res = await getCustomerFormInfo(params)
|
||||||
if(res.code === 200) {
|
if(res.code === 200) {
|
||||||
formInfo.value = res.data
|
formInfo.value = res.data
|
||||||
|
/**
|
||||||
|
* data
|
||||||
|
:
|
||||||
|
{name: "柏媛媛", child_name: "淼淼", child_gender: "女", occupation: "无业", child_education: "高一",…}
|
||||||
|
additional_info
|
||||||
|
:
|
||||||
|
[{topic: "孩子目前是否能正常上学?", answer: "不稳定:经常请假或迟到,断断续续。"},…]
|
||||||
|
0
|
||||||
|
:
|
||||||
|
{topic: "孩子目前是否能正常上学?", answer: "不稳定:经常请假或迟到,断断续续。"}
|
||||||
|
answer
|
||||||
|
:
|
||||||
|
"不稳定:经常请假或迟到,断断续续。"
|
||||||
|
topic
|
||||||
|
:
|
||||||
|
"孩子目前是否能正常上学?"
|
||||||
|
1
|
||||||
|
:
|
||||||
|
{topic: "您当前最困扰的孩子的问题,更接近以下哪种描述?", answer: "比如手机不离手、激烈争吵、破坏规则。"}
|
||||||
|
answer
|
||||||
|
:
|
||||||
|
"比如手机不离手、激烈争吵、破坏规则。"
|
||||||
|
topic
|
||||||
|
:
|
||||||
|
"您当前最困扰的孩子的问题,更接近以下哪种描述?"
|
||||||
|
2
|
||||||
|
:
|
||||||
|
{topic: "面对上述问题,您和孩子的互动模式通常是?", answer: "几乎零交流,像生活在两个世界。"}
|
||||||
|
answer
|
||||||
|
:
|
||||||
|
"几乎零交流,像生活在两个世界。"
|
||||||
|
topic
|
||||||
|
:
|
||||||
|
"面对上述问题,您和孩子的互动模式通常是?"
|
||||||
|
3
|
||||||
|
:
|
||||||
|
{topic: "您认为造成今天这个局面的主要责任在于?", answer: "各方面原因都有,感觉很复杂。"}
|
||||||
|
answer
|
||||||
|
:
|
||||||
|
"各方面原因都有,感觉很复杂。"
|
||||||
|
topic
|
||||||
|
:
|
||||||
|
"您认为造成今天这个局面的主要责任在于?"
|
||||||
|
4
|
||||||
|
:
|
||||||
|
{topic: "此刻,您内心最强烈的感受是?", answer: "内疚和迷茫"}
|
||||||
|
answer
|
||||||
|
:
|
||||||
|
"内疚和迷茫"
|
||||||
|
topic
|
||||||
|
:
|
||||||
|
"此刻,您内心最强烈的感受是?"
|
||||||
|
5
|
||||||
|
:
|
||||||
|
{topic: "您目前最迫切的期望是?", answer: "让他少玩手机,能跟我好好说话。"}
|
||||||
|
answer
|
||||||
|
:
|
||||||
|
"让他少玩手机,能跟我好好说话。"
|
||||||
|
topic
|
||||||
|
:
|
||||||
|
"您目前最迫切的期望是?"
|
||||||
|
child_education
|
||||||
|
:
|
||||||
|
"高一"
|
||||||
|
child_gender
|
||||||
|
:
|
||||||
|
"女"
|
||||||
|
child_name
|
||||||
|
:
|
||||||
|
"淼淼"
|
||||||
|
child_relation
|
||||||
|
:
|
||||||
|
"母亲"
|
||||||
|
created_at
|
||||||
|
:
|
||||||
|
"2025-08-12T10:41:39.798000"
|
||||||
|
mobile
|
||||||
|
:
|
||||||
|
"15061153145"
|
||||||
|
name
|
||||||
|
:
|
||||||
|
"柏媛媛"
|
||||||
|
occupation
|
||||||
|
:
|
||||||
|
"无业"
|
||||||
|
territory
|
||||||
|
:
|
||||||
|
"江苏省 盐城市 滨海县"
|
||||||
|
updated_at
|
||||||
|
:
|
||||||
|
"2025-08-12T10:41:41.534000"
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 静默处理错误
|
// 静默处理错误
|
||||||
@@ -691,8 +784,10 @@ const handleStageSelect = (stage, extraData = null) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleViewFormData = (contact) => {
|
const handleViewFormData = async (contact) => {
|
||||||
// TODO: 实现表单数据查看逻辑
|
// 获取客户表单数据
|
||||||
|
await getCustomerForm();
|
||||||
|
console.log('表单数据已加载:', formInfo.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleViewChatData = (contact) => {
|
const handleViewChatData = (contact) => {
|
||||||
|
|||||||
@@ -158,17 +158,19 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import CenterOverview from './components/CenterOverview.vue'
|
import CenterOverview from './components/CenterOverview.vue'
|
||||||
// import ResourceAllocation from './components/ResourceAllocation.vue'
|
|
||||||
import GroupComparison from './components/GroupComparison.vue'
|
import GroupComparison from './components/GroupComparison.vue'
|
||||||
import GroupRanking from './components/GroupRanking.vue'
|
import GroupRanking from './components/GroupRanking.vue'
|
||||||
// import GroupDetails from './components/GroupDetails.vue'
|
|
||||||
import ActionItems from './components/ActionItems.vue'
|
import ActionItems from './components/ActionItems.vue'
|
||||||
import CustomerDetail from './components/CustomerDetail.vue'
|
import CustomerDetail from './components/CustomerDetail.vue'
|
||||||
import CustomerType from './components/CustomerType.vue'
|
import CustomerType from './components/CustomerType.vue'
|
||||||
import GoodMusic from './components/GoodMusic.vue'
|
import GoodMusic from './components/GoodMusic.vue'
|
||||||
import ProblemRanking from './components/ProblemRanking.vue'
|
import ProblemRanking from './components/ProblemRanking.vue'
|
||||||
import seniorManager from './components/seniorManager.vue'
|
import seniorManager from './components/seniorManager.vue'
|
||||||
|
import {getOverallCenterPerformance,getTotalGroupCount,getCenterConversionRate,getTotalCallCount,getNewCustomer
|
||||||
|
, getDepositConversionRate,getCustomerTypeDistribution,getUrgentNeedToAddress,getCenterAdvancedManagerList,getTeamRanking,getTeamRankingInfo
|
||||||
|
} from '@/api/secondTop.js'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useUserStore } from '@/stores/user.js'
|
||||||
// 组别数据
|
// 组别数据
|
||||||
const groups = [
|
const groups = [
|
||||||
{
|
{
|
||||||
@@ -678,6 +680,43 @@ const groups = [
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
// 路由实例
|
||||||
|
const router = useRouter();
|
||||||
|
// 用户store实例
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
// 获取通用请求参数的函数
|
||||||
|
const getRequestParams = () => {
|
||||||
|
const params = {}
|
||||||
|
// 只从路由参数获取
|
||||||
|
const routeUserLevel = router.currentRoute.value.query.user_level || router.currentRoute.value.params.user_level
|
||||||
|
const routeUserName = router.currentRoute.value.query.user_name || router.currentRoute.value.params.user_name
|
||||||
|
// 如果路由有参数,使用路由参数
|
||||||
|
if (routeUserLevel) {
|
||||||
|
params.user_level = routeUserLevel.toString()
|
||||||
|
}
|
||||||
|
if (routeUserName) {
|
||||||
|
params.user_name = routeUserName
|
||||||
|
}
|
||||||
|
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
// 中心整体概览
|
||||||
|
const overallCenterPerformance = ref({
|
||||||
|
|
||||||
|
})
|
||||||
|
// 中心总业绩
|
||||||
|
async function CenterOverallCenterPerformance() {
|
||||||
|
const params = getRequestParams()
|
||||||
|
try {
|
||||||
|
const res = await getOverallCenterPerformance(params)
|
||||||
|
if (res.code === 200) {
|
||||||
|
overallCenterPerformance.value = res.data
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取中心整体概览失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 当前选中的组别,默认为第一个
|
// 当前选中的组别,默认为第一个
|
||||||
const selectedGroup = ref(groups[0])
|
const selectedGroup = ref(groups[0])
|
||||||
|
|||||||
Reference in New Issue
Block a user