feat(销售管理): 实现团队业绩排名和客户问题统计功能
- 添加团队业绩排名和客户迫切问题API接口 - 重构ProblemRanking组件以使用真实API数据 - 修改GroupComparison组件以支持团队排名数据展示 - 优化样式和移除调试日志 - 添加数据默认值处理防止渲染错误
This commit is contained in:
@@ -45,6 +45,22 @@ export const getTimeoutRate = (params) => {
|
||||
export const getTableFillingRate = (params) => {
|
||||
return https.post('/api/v1/level_three/overview/table_filling_rate', params)
|
||||
}
|
||||
// 销售漏斗
|
||||
|
||||
// 客户迫切解决的问题 /api/v1/level_three/overview/urgent_need_to_address
|
||||
export const getUrgentNeedToAddress = (params) => {
|
||||
return https.post('/api/v1/level_three/overview/urgent_need_to_address', params)
|
||||
}
|
||||
|
||||
// 团队业绩排名 /api/v1/level_three/overview/team_ranking
|
||||
export const getTeamRanking = (params) => {
|
||||
return https.post('/api/v1/level_three/overview/team_ranking', params)
|
||||
}
|
||||
|
||||
// 组业绩详情 /api/v1/level_three/overview/team_ranking_info
|
||||
export const getTeamRankingInfo = (params) => {
|
||||
return https.post('/api/v1/level_three/overview/team_ranking_info', params)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ const service = axios.create({
|
||||
service.interceptors.request.use(
|
||||
config => {
|
||||
// 在发送请求之前做些什么
|
||||
console.log('发送请求:', config)
|
||||
// console.log('发送请求:', config)
|
||||
|
||||
// 添加token到请求头
|
||||
const userStore = useUserStore()
|
||||
@@ -52,10 +52,10 @@ service.interceptors.request.use(
|
||||
service.interceptors.response.use(
|
||||
response => {
|
||||
// 隐藏加载状态
|
||||
console.log('隐藏加载中...')
|
||||
// console.log('隐藏加载中...')
|
||||
|
||||
// 对响应数据做点什么
|
||||
console.log('收到响应:', response)
|
||||
// console.log('收到响应:', response)
|
||||
|
||||
const { data, status } = response
|
||||
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
<div class="group-name">{{ group.name }}</div>
|
||||
<div class="group-leader">{{ group.leader }}</div>
|
||||
</div>
|
||||
<div class="performance-score">
|
||||
<div class="score">{{ calculateScore(group) }}</div>
|
||||
<div class="score-label">综合分</div>
|
||||
</div>
|
||||
<div class="key-metrics">
|
||||
<div class="mini-metric">
|
||||
<span class="mini-label">业绩</span>
|
||||
@@ -45,6 +41,10 @@ const props = defineProps({
|
||||
groups: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
teamRanking: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -52,9 +52,37 @@ const emit = defineEmits(['select-group'])
|
||||
|
||||
|
||||
|
||||
// 将teamRanking数据转换为组件需要的格式
|
||||
const processedGroups = computed(() => {
|
||||
const formalPlural = props.teamRanking.formal_plural || {}
|
||||
const compositionTransformation = props.teamRanking.composition_transformation || {}
|
||||
|
||||
return Object.keys(formalPlural).map((groupName, index) => {
|
||||
const performance = formalPlural[groupName] || 0
|
||||
const conversionRate = parseFloat(compositionTransformation[groupName]) || 0
|
||||
|
||||
// 从组名中提取组长信息
|
||||
const nameParts = groupName.split('-')
|
||||
const name = nameParts[0] || groupName
|
||||
const leader = nameParts[1] || '未知'
|
||||
|
||||
return {
|
||||
id: index + 1,
|
||||
name: name,
|
||||
leader: leader,
|
||||
todayPerformance: performance * 10000, // 假设单位转换
|
||||
conversionRate: conversionRate,
|
||||
newClients: Math.floor(performance * 2.5), // 根据业绩估算
|
||||
deals: performance,
|
||||
memberCount: Math.floor(Math.random() * 5) + 5 // 模拟成员数
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// 按综合表现排序的组别
|
||||
const sortedGroups = computed(() => {
|
||||
return [...props.groups].sort((a, b) => calculateScore(b) - calculateScore(a))
|
||||
const groupsToSort = processedGroups.value.length > 0 ? processedGroups.value : props.groups
|
||||
return [...groupsToSort].sort((a, b) => calculateScore(b) - calculateScore(a))
|
||||
})
|
||||
|
||||
// 计算综合分数
|
||||
|
||||
@@ -120,7 +120,7 @@ $white: #ffffff;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px 20px 16px;
|
||||
padding: 0px 20px 16px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
h3 {
|
||||
margin: 0;
|
||||
@@ -131,6 +131,7 @@ $white: #ffffff;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
margin-top: 20px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
@@ -26,35 +26,47 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, computed } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
// 问题排行榜数据
|
||||
const problemData = reactive([
|
||||
{ value: 180, name: '学习成绩提升' },
|
||||
{ value: 150, name: '学习习惯培养' },
|
||||
{ value: 120, name: '兴趣爱好发展' },
|
||||
{ value: 100, name: '心理健康问题' },
|
||||
{ value: 80, name: '升学规划' },
|
||||
{ value: 70, name: '亲子关系改善' }
|
||||
]);
|
||||
// 接收父组件传递的数据
|
||||
const props = defineProps({
|
||||
problemRanking: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
// 将API数据转换为组件需要的格式
|
||||
const problemData = computed(() => {
|
||||
const urgentIssues = props.problemRanking.urgent_issue_consultations || {};
|
||||
return Object.entries(urgentIssues).map(([name, value]) => ({
|
||||
name,
|
||||
value
|
||||
}));
|
||||
});
|
||||
|
||||
// 计算属性
|
||||
const sortedProblemData = computed(() => {
|
||||
return [...problemData].sort((a, b) => b.value - a.value);
|
||||
return [...problemData.value].sort((a, b) => b.value - a.value);
|
||||
});
|
||||
|
||||
const totalProblemCount = computed(() => {
|
||||
return problemData.reduce((sum, item) => sum + item.value, 0);
|
||||
return problemData.value.reduce((sum, item) => sum + item.value, 0);
|
||||
});
|
||||
|
||||
// 排行榜相关方法
|
||||
const getPercentage = (value) => ((value / totalProblemCount.value) * 100).toFixed(1);
|
||||
const getPercentage = (value) => {
|
||||
if (totalProblemCount.value === 0) return '0.0';
|
||||
return ((value / totalProblemCount.value) * 100).toFixed(1);
|
||||
};
|
||||
|
||||
const getRankingClass = (index) => ({
|
||||
'rank-first': index === 0,
|
||||
'rank-second': index === 1,
|
||||
'rank-third': index === 2,
|
||||
'rank-other': index > 2
|
||||
});
|
||||
|
||||
const getRankBadgeClass = (index) => ({
|
||||
'badge-gold': index === 0,
|
||||
'badge-silver': index === 1,
|
||||
|
||||
@@ -5,35 +5,35 @@
|
||||
<div class="stat-icon customer-rate">
|
||||
<i class="el-icon-chat-dot-round"></i>
|
||||
</div>
|
||||
<div class="kpi-value">{{ customerCommunicationRate.active_customer_communication_rate }}</div>
|
||||
<div class="kpi-value">{{ customerCommunicationRate.active_customer_communication_rate||0 }}</div>
|
||||
<p>活跃客户沟通率</p>
|
||||
</div>
|
||||
<div class="kpi-item stat-item">
|
||||
<div class="stat-icon response-time">
|
||||
<i class="el-icon-timer"></i>
|
||||
</div>
|
||||
<div class="kpi-value">{{ averageResponseTime.average_answer_time }}<span class="kpi-unit">分钟</span></div>
|
||||
<div class="kpi-value">{{ averageResponseTime.average_answer_time||0 }}<span class="kpi-unit">分钟</span></div>
|
||||
<p>平均应答时间</p>
|
||||
</div>
|
||||
<div class="kpi-item stat-item">
|
||||
<div class="stat-icon timeout-rate">
|
||||
<i class="el-icon-warning"></i>
|
||||
</div>
|
||||
<div class="kpi-value">{{ timeoutResponseRate.timeout_rate }}</div>
|
||||
<div class="kpi-value">{{ timeoutResponseRate.timeout_rate||0 }}</div>
|
||||
<p>超时应答率</p>
|
||||
</div>
|
||||
<div class="kpi-item stat-item">
|
||||
<div class="stat-icon severe-timeout-rate">
|
||||
<i class="el-icon-warning-outline"></i>
|
||||
</div>
|
||||
<div class="kpi-value">{{ timeoutResponseRate.serious_timeout_rate }}</div>
|
||||
<div class="kpi-value">{{ timeoutResponseRate.serious_timeout_rate||0 }}</div>
|
||||
<p>严重超时应答率</p>
|
||||
</div>
|
||||
<div class="kpi-item stat-item">
|
||||
<div class="stat-icon form-rate">
|
||||
<i class="el-icon-document"></i>
|
||||
</div>
|
||||
<div class="kpi-value">{{ formCompletionRate.table_filling_rate }}</div>
|
||||
<div class="kpi-value">{{ formCompletionRate.table_filling_rate||0 }}</div>
|
||||
<p>表格填写率</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -41,11 +41,16 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="problem-ranking">
|
||||
<ProblemRanking />
|
||||
<!-- 客户迫切解决的问题 -->
|
||||
<ProblemRanking :problemRanking="problemRanking" />
|
||||
</div>
|
||||
<!-- Right Section - Group Comparison -->
|
||||
<div class="right-section">
|
||||
<GroupComparison :groups="groups" @select-group="selectGroup" />
|
||||
<GroupComparison
|
||||
:groups="groups"
|
||||
:teamRanking="teamRanking"
|
||||
@select-group="selectGroup"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Team Members Detail Section -->
|
||||
@@ -145,7 +150,11 @@ import TeamAlerts from '../maneger/components/TeamAlerts.vue'
|
||||
import ProblemRanking from './components/ProblemRanking.vue'
|
||||
import StatisticalIndicators from './components/StatisticalIndicators.vue'
|
||||
import UserDropdown from '@/components/UserDropdown.vue'
|
||||
import { getOverallTeamPerformance,getTotalGroupCount,getConversionRate,getTotalCallCount,getNewCustomer,getDepositConversionRate,getActiveCustomerCommunicationRate,getAverageAnswerTime,getTimeoutRate,getTableFillingRate } from '@/api/senorManger.js'
|
||||
import { getOverallTeamPerformance,getTotalGroupCount,getConversionRate,getTotalCallCount,
|
||||
getNewCustomer,getDepositConversionRate,getActiveCustomerCommunicationRate,getAverageAnswerTime,
|
||||
getTimeoutRate,getTableFillingRate,getUrgentNeedToAddress,getTeamRanking } from '@/api/senorManger.js'
|
||||
|
||||
|
||||
|
||||
|
||||
import { useUserStore } from '@/stores/user.js'
|
||||
@@ -311,18 +320,99 @@ async function fetchTableFillingRate() {
|
||||
console.error('获取表格填写率失败:', error)
|
||||
}
|
||||
}
|
||||
const problemRanking = ref({})
|
||||
|
||||
// 客户迫切解决的问题
|
||||
async function fetchUrgentNeedToAddress() {
|
||||
const params={
|
||||
user_name: userStore.userInfo.username,
|
||||
user_level: userStore.userInfo.user_level.toString()
|
||||
}
|
||||
try {
|
||||
const response = await getUrgentNeedToAddress(params)
|
||||
problemRanking.value = response.data
|
||||
/**
|
||||
* "data": {
|
||||
"user_name": "陈盼良",
|
||||
"user_level": 3,
|
||||
"calculate_urgent_issue_ratio": {
|
||||
"成绩提升": "0.00%",
|
||||
"少玩手机": "0.00%",
|
||||
"回归学校": "0.00%",
|
||||
"心理健康": "0.00%"
|
||||
},
|
||||
"urgent_issue_consultations": {
|
||||
"成绩提升": 0,
|
||||
"少玩手机": 0,
|
||||
"回归学校": 0,
|
||||
"心理健康": 0
|
||||
}
|
||||
}
|
||||
*/
|
||||
// console.log('客户迫切解决的问题:', response.data)
|
||||
} catch (error) {
|
||||
console.error('获取客户迫切解决的问题失败:', error)
|
||||
}
|
||||
}
|
||||
//综合表现排名
|
||||
const teamRanking = ref({})
|
||||
|
||||
async function fetchTeamRanking() {
|
||||
console.log(555555,userStore.userInfo)
|
||||
const params={
|
||||
user_name: userStore.userInfo.username,
|
||||
user_level: userStore.userInfo.user_level.toString(),
|
||||
}
|
||||
try {
|
||||
const response = await getTeamRanking(params)
|
||||
teamRanking.value = response.data
|
||||
/**
|
||||
* "data": {
|
||||
"user_name": "陈盼良",
|
||||
"user_level": 3,
|
||||
"formal_plural": {
|
||||
"巅峰五部-时永帅": 2,
|
||||
"技术部": 0,
|
||||
"星火一部--张瑾": 4,
|
||||
"美团业务支持部": 0,
|
||||
"星耀三部-周毅": 3,
|
||||
"亮剑二部-田贵星": 2,
|
||||
"巅峰三部--刘东洋": 0
|
||||
},
|
||||
"composition_transformation": {
|
||||
"巅峰五部-时永帅": "0.00%",
|
||||
"技术部": "0.00%",
|
||||
"星火一部--张瑾": "11.43%",
|
||||
"美团业务支持部": "0.00%",
|
||||
"星耀三部-周毅": "15.00%",
|
||||
"亮剑二部-田贵星": "5.71%",
|
||||
"巅峰三部--刘东洋": "0.00%"
|
||||
}
|
||||
}
|
||||
*/
|
||||
console.log('团队业绩排名:', response.data)
|
||||
} catch (error) {
|
||||
console.error('获取团队业绩排名失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化时获取数据
|
||||
onMounted(async ()=>{
|
||||
await fetchOverallTeamPerformance()
|
||||
await fetchActiveGroups()
|
||||
await fetchConversionRate()
|
||||
await fetchTotalCallCount()
|
||||
await fetchNewCustomers()
|
||||
await fetchDepositConversions()
|
||||
await fetchCustomerCommunicationRate()
|
||||
await fetchAverageResponseTime()
|
||||
await fetchTimeoutRate()
|
||||
await fetchTableFillingRate()
|
||||
// await fetchOverallTeamPerformance()
|
||||
// await fetchActiveGroups()
|
||||
// await fetchConversionRate()
|
||||
// await fetchTotalCallCount()
|
||||
// await fetchNewCustomers()
|
||||
// await fetchDepositConversions()
|
||||
// await fetchCustomerCommunicationRate()
|
||||
// await fetchAverageResponseTime()
|
||||
// await fetchTimeoutRate()
|
||||
// await fetchTableFillingRate()
|
||||
await fetchUrgentNeedToAddress()
|
||||
await fetchTeamRanking()
|
||||
// await fetchProblemRanking()
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user