diff --git a/my-vue-app/src/utils/https.js b/my-vue-app/src/utils/https.js index fed8748..68cbdab 100644 --- a/my-vue-app/src/utils/https.js +++ b/my-vue-app/src/utils/https.js @@ -5,7 +5,7 @@ import { useUserStore } from '@/stores/user' // 创建axios实例 const service = axios.create({ - baseURL: 'http://192.168.15.53:8890' || '', // API基础路径,支持完整URL + baseURL: 'http://192.168.15.121:8890' || '', // API基础路径,支持完整URL timeout: 100000, // 请求超时时间 headers: { 'Content-Type': 'application/json;charset=UTF-8' diff --git a/my-vue-app/src/views/maneger/components/MemberDetails.vue b/my-vue-app/src/views/maneger/components/MemberDetails.vue index 976dae3..22f64e2 100644 --- a/my-vue-app/src/views/maneger/components/MemberDetails.vue +++ b/my-vue-app/src/views/maneger/components/MemberDetails.vue @@ -165,23 +165,23 @@ const tooltip = reactive({ const metricDescriptions = { totalCalls: { title: '总通话次数计算方式', - description: '统计该成员在选定时间范围内的所有外呼和接听通话的总次数,包括有效通话和无效通话。' + description: '统计在选定时间范围内的所有外呼和接听通话的总次数,包括有效通话和无效通话。' }, callTime: { title: '通话时长计算方式', - description: '累计该成员所有有效通话的时长,以小时为单位显示,精确到小数点后一位。' + description: '累计所有有效通话的时长,以小时为单位显示,精确到小数点后一位。' }, newClients: { title: '新增客户计算方式', - description: '统计该成员在选定时间范围内新建档的客户数量,不包括重复录入的客户。' + description: '在选定时间范围内新建档的客户数量,不包括重复录入的客户。' }, deals: { title: '成交单数计算方式', - description: '统计该成员在选定时间范围内成功签约的订单数量,包括全款和定金订单。' + description: '在选定时间范围内成功签约的订单数量,包括全款和定金订单。' }, conversionRate: { title: '转化率计算方式', - description: '成交单数 ÷ 新增客户数 × 100%,反映该成员将潜在客户转化为成交客户的能力。' + description: '成交单数 ÷ 新增客户数 × 100%' } } diff --git a/my-vue-app/src/views/person/components/SalesTimelineWithTaskList.vue b/my-vue-app/src/views/person/components/SalesTimelineWithTaskList.vue index 7e67c50..9cc5a64 100644 --- a/my-vue-app/src/views/person/components/SalesTimelineWithTaskList.vue +++ b/my-vue-app/src/views/person/components/SalesTimelineWithTaskList.vue @@ -25,9 +25,12 @@

{{ stage.displayName || stage.name }}

{{ stage.name === '全部' ? customersCount : stage.count }} - 位客户 +
-
{{ getPercentage(stage.count) }}%
+
+ {{ stage.unName }} +
+
{{ getPercentage(stage.count, stage.id, stage.name) }}%
@@ -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 { diff --git a/my-vue-app/src/views/secondTop/components/CenterOverview.vue b/my-vue-app/src/views/secondTop/components/CenterOverview.vue index 30eb5fb..99ea236 100644 --- a/my-vue-app/src/views/secondTop/components/CenterOverview.vue +++ b/my-vue-app/src/views/secondTop/components/CenterOverview.vue @@ -1,14 +1,28 @@