feat(统计模式): 添加按月/按期统计切换功能并优化客户阶段显示
- 在CenterOverview组件中添加统计模式切换按钮 - 实现统计模式切换逻辑并触发数据重新加载 - 优化SalesTimelineWithTaskList的客户阶段显示和计算逻辑 - 更新API调用参数以支持不同统计模式 - 调整样式和布局以适应新功能
This commit is contained in:
@@ -25,9 +25,12 @@
|
||||
<h3 class="stage-title">{{ stage.displayName || stage.name }}</h3>
|
||||
<div class="stage-stats">
|
||||
<span class="stage-count">{{ stage.name === '全部' ? customersCount : stage.count }}</span>
|
||||
<span class="stage-label">位客户</span>
|
||||
<span class="stage-label">位</span>
|
||||
</div>
|
||||
<div v-if="stage.name !== '全部'" class="stage-percentage">{{ getPercentage(stage.count) }}%</div>
|
||||
<div class="stage-unName">
|
||||
<span class="stage-unName" v-if="stage.unName">{{ stage.unName }}</span>
|
||||
</div>
|
||||
<div v-if="stage.name !== '全部'" class="stage-percentage">{{ getPercentage(stage.count, stage.id, stage.name) }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -217,6 +220,50 @@ const getStageCount = (stageType) => {
|
||||
return props.payMoneyCustomersCount || (props.payMoneyCustomersList?.length || 0);
|
||||
}
|
||||
|
||||
// 待填表单阶段:统计customer_occupation或customer_child_education字段为'未知'的客户数量
|
||||
if (stageType === '待填表单') {
|
||||
if (props.customersList?.length) {
|
||||
return props.customersList.filter(customer => {
|
||||
return (customer.customer_occupation === '未知' || !customer.customer_occupation) ||
|
||||
(customer.customer_child_education === '未知' || !customer.customer_child_education);
|
||||
}).length;
|
||||
}
|
||||
return props.data[stageType] || 0;
|
||||
}
|
||||
|
||||
// 待到课阶段:统计没有到课数据的客户数量
|
||||
if (stageType === '待到课') {
|
||||
if (props.customersList?.length) {
|
||||
return props.customersList.filter(customer => {
|
||||
// 根据getAttendedLessons函数的逻辑判断是否为'未到课'
|
||||
const classNum = customer.class_num;
|
||||
const classSituation = customer.class_situation;
|
||||
|
||||
// 优先检查class_num字段
|
||||
if (classNum && Array.isArray(classNum) && classNum.length > 0) {
|
||||
return false; // 有class_num数据,不是待到课
|
||||
}
|
||||
|
||||
// 检查class_situation字段
|
||||
if (!classSituation) return true; // 没有class_situation,是待到课
|
||||
|
||||
if (Array.isArray(classSituation)) {
|
||||
return classSituation.length === 0; // 空数组,是待到课
|
||||
}
|
||||
|
||||
if (typeof classSituation === 'object') {
|
||||
const lessonNumbers = Object.keys(classSituation)
|
||||
.map(key => parseInt(key))
|
||||
.filter(num => !isNaN(num));
|
||||
return lessonNumbers.length === 0; // 没有有效的课程数据,是待到课
|
||||
}
|
||||
|
||||
return true; // 其他情况默认为待到课
|
||||
}).length;
|
||||
}
|
||||
return props.data[stageType] || 0;
|
||||
}
|
||||
|
||||
// 课1-4阶段从courseCustomers获取数量
|
||||
if (stageType === '课1-4' && props.courseCustomers?.['课1-4']) {
|
||||
return props.courseCustomers['课1-4'].length;
|
||||
@@ -258,11 +305,11 @@ const stages = computed(() => {
|
||||
|
||||
const stageList = [
|
||||
{ id: 0, name: '全部', displayName: '全部', count: totalCount, color: '#f3f4f6' },
|
||||
{ id: 1, name: '待加微', displayName: '待加微', count: getStageCount('待加微'), color: '#e3f2fd' },
|
||||
{ id: 2, name: '待填表单', displayName: '待填表单', count: getStageCount('待填表单'), color: '#90caf9' },
|
||||
{ id: 3, name: '待入群', displayName: '待入群', count: getStageCount('待入群'), color: '#bbdefb' },
|
||||
{ id: 4, name: '待联系', displayName: '待联系', count: getStageCount('待联系'), color: '#bbdefb' },
|
||||
{ id: 5, name: '待到课', displayName: '待到课', count: getStageCount('待到课'), color: '#bbdefb' },
|
||||
{ id: 1, name: '待加微', displayName: '待加微', count: getStageCount('待加微'), color: '#e3f2fd' ,unName:'已加微'},
|
||||
{ id: 2, name: '待填表单', displayName: '待填表单', count: getStageCount('待填表单'), color: '#90caf9' ,unName:'已填表单'},
|
||||
{ id: 3, name: '待入群', displayName: '待入群', count: getStageCount('待入群'), color: '#bbdefb' ,unName:'已入群'},
|
||||
{ id: 4, name: '待联系', displayName: '待联系', count: getStageCount('待联系'), color: '#bbdefb' ,unName:'已联系'},
|
||||
{ id: 5, name: '待到课', displayName: '待到课', count: getStageCount('待到课'), color: '#bbdefb' ,unName:'已到课'},
|
||||
{ id: 6, name: '课1', displayName: '课1', count: getStageCount('课1'), color: '#81c784' },
|
||||
{ id: 7, name: '课2', displayName: '课2', count: getStageCount('课2'), color: '#64b5f6' },
|
||||
{ id: 8, name: '课3', displayName: '课3', count: getStageCount('课3'), color: '#ffb74d' },
|
||||
@@ -277,9 +324,31 @@ const stages = computed(() => {
|
||||
});
|
||||
|
||||
|
||||
const getPercentage = (count) => {
|
||||
const getPercentage = (count, stageId, stageName) => {
|
||||
if (totalCustomers.value === 0) return 0;
|
||||
return Math.round((count / totalCustomers.value) * 100);
|
||||
|
||||
// 待填表单阶段特殊计算:(总数-有数据人数)/总数
|
||||
if (stageName === '待填表单') {
|
||||
return Math.round((totalCustomers.value - count) / totalCustomers.value * 100);
|
||||
}
|
||||
|
||||
// 待到课阶段特殊计算:(总数-有到课数据人数)/总数
|
||||
if (stageName === '待到课') {
|
||||
return Math.round((totalCustomers.value - count) / totalCustomers.value * 100);
|
||||
}
|
||||
|
||||
// 阶段1-5使用现在的算法(转化率)
|
||||
if (stageId >= 1 && stageId <= 5) {
|
||||
return Math.round((totalCustomers.value - count) / totalCustomers.value * 100);
|
||||
}
|
||||
|
||||
// 阶段5-13使用直接数量除以总数
|
||||
if (stageId >5 && stageId <= 13) {
|
||||
return Math.round((count / totalCustomers.value) * 100);
|
||||
}
|
||||
|
||||
// 其他阶段保持原有逻辑
|
||||
return Math.round((totalCustomers.value - count) / totalCustomers.value * 100);
|
||||
};
|
||||
|
||||
|
||||
@@ -328,6 +397,40 @@ const selectStage = (stageName) => {
|
||||
if (props.courseCustomers?.['课1-4']) {
|
||||
filteredCustomers = props.courseCustomers['课1-4'].filter(customer => customer.type === stageName);
|
||||
}
|
||||
} else if (stageName === '待填表单') {
|
||||
// 待填表单阶段:筛选customer_occupation或customer_child_education字段为'未知'的客户
|
||||
filteredCustomers = props.customersList.filter(customer => {
|
||||
return (customer.customer_occupation === '未知' || !customer.customer_occupation) ||
|
||||
(customer.customer_child_education === '未知' || !customer.customer_child_education);
|
||||
});
|
||||
} else if (stageName === '待到课') {
|
||||
// 待到课阶段:筛选没有到课数据的客户
|
||||
filteredCustomers = props.customersList.filter(customer => {
|
||||
// 根据getAttendedLessons函数的逻辑判断是否为'未到课'
|
||||
const classNum = customer.class_num;
|
||||
const classSituation = customer.class_situation;
|
||||
|
||||
// 优先检查class_num字段
|
||||
if (classNum && Array.isArray(classNum) && classNum.length > 0) {
|
||||
return false; // 有class_num数据,不是待到课
|
||||
}
|
||||
|
||||
// 检查class_situation字段
|
||||
if (!classSituation) return true; // 没有class_situation,是待到课
|
||||
|
||||
if (Array.isArray(classSituation)) {
|
||||
return classSituation.length === 0; // 空数组,是待到课
|
||||
}
|
||||
|
||||
if (typeof classSituation === 'object') {
|
||||
const lessonNumbers = Object.keys(classSituation)
|
||||
.map(key => parseInt(key))
|
||||
.filter(num => !isNaN(num));
|
||||
return lessonNumbers.length === 0; // 没有有效的课程数据,是待到课
|
||||
}
|
||||
|
||||
return true; // 其他情况默认为待到课
|
||||
});
|
||||
} else {
|
||||
// 前6个阶段从customersList中筛选
|
||||
filteredCustomers = props.customersList.filter(customer => customer.type === stageName);
|
||||
@@ -650,8 +753,9 @@ $indigo: #4f46e5;
|
||||
|
||||
.stage-content {
|
||||
.stage-title {
|
||||
font-size: 1rem;
|
||||
color: $slate-800;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.stage-count {
|
||||
|
||||
Reference in New Issue
Block a user