Compare commits

...

2 Commits

Author SHA1 Message Date
182130ba6a feat(GoodMusic): 替换模拟API为真实语音转文本API调用
refactor(DetailedDataTable): 简化选中人员判断逻辑

feat(RankingList): 将排行榜数据获取逻辑移至组件内部

refactor(topone): 移除冗余代码并优化排行榜数据流

chore: 删除不再使用的DataTable组件
2025-08-18 21:54:20 +08:00
d182673552 refactor(views): 统一将 period 相关命名改为 periods
修改多个组件中的 class 名称、变量名和事件名,将 period 改为 periods 以保持命名一致性
移除部分注释掉的代码和冗余注释
2025-08-18 20:57:53 +08:00
9 changed files with 87 additions and 585 deletions

View File

@@ -177,6 +177,7 @@
<script> <script>
import { SimpleChatService } from '@/utils/ChatService.js' import { SimpleChatService } from '@/utils/ChatService.js'
import MarkdownIt from 'markdown-it' import MarkdownIt from 'markdown-it'
import axios from 'axios'
export default { export default {
name: 'GroupRank', name: 'GroupRank',
@@ -314,7 +315,10 @@ export default {
try { try {
// 模拟转换过程 // 模拟转换过程
await new Promise(resolve => setTimeout(resolve, 2000)) await axios.post('http://192.168.3.104:8000/api/asr/sync?priority=10', {
url: recording.url
})
// 这里应该调用实际的语音转文本API // 这里应该调用实际的语音转文本API
// 目前使用模拟数据 // 目前使用模拟数据

View File

@@ -2,7 +2,7 @@
<div class="dashboard-card"> <div class="dashboard-card">
<div class="card-header"> <div class="card-header">
<h3>沟通总数据</h3> <h3>沟通总数据</h3>
<span class="metric-period">本周</span> <span class="metric-periods">本周</span>
</div> </div>
<div class="communication-cards"> <div class="communication-cards">
<div class="comm-card"> <div class="comm-card">
@@ -68,7 +68,7 @@ defineProps({
font-size: 18px; font-size: 18px;
} }
.metric-period { .metric-periods {
font-size: 14px; font-size: 14px;
color: #666; color: #666;
} }

View File

@@ -1,233 +0,0 @@
<template>
<div class="dashboard-card table-section">
<div class="card-header">
<h3>详细数据表格</h3>
</div>
<div class="data-table-container">
<!-- 筛选器 -->
<div class="table-filters">
<div class="filter-group">
<label>部门:</label>
<select v-model="filters.department">
<option value="">全部部门</option>
<option value="销售一部">销售一部</option>
<option value="销售二部">销售二部</option>
<option value="销售三部">销售三部</option>
</select>
</div>
<div class="filter-group">
<label>职位:</label>
<select v-model="filters.position">
<option value="">全部职位</option>
<option value="销售经理">销售经理</option>
<option value="销售专员">销售专员</option>
<option value="销售助理">销售助理</option>
</select>
</div>
<div class="filter-group">
<label>时间范围:</label>
<select v-model="filters.timeRange">
<option value="today">今日</option>
<option value="week">本周</option>
<option value="month">本月</option>
<option value="quarter">本季度</option>
</select>
</div>
<div class="filter-group">
<label>成交状态:</label>
<select v-model="filters.dealStatus">
<option value="">全部状态</option>
<option value="已成交">已成交</option>
<option value="跟进中">跟进中</option>
<option value="已失效">已失效</option>
</select>
</div>
</div>
<!-- 数据表格 -->
<div class="data-table">
<table>
<thead>
<tr>
<th>人员</th>
<th @click="$emit('sort-by', 'dealRate')" class="sortable">
成交率
<span class="sort-icon" :class="{ active: sortField === 'dealRate' }">
{{ sortOrder === 'desc' ? '↓' : '↑' }}
</span>
</th>
<th @click="$emit('sort-by', 'callDuration')" class="sortable">
通话时长
<span class="sort-icon" :class="{ active: sortField === 'callDuration' }">
{{ sortOrder === 'desc' ? '↓' : '↑' }}
</span>
</th>
<th>通话次数</th>
<th>成交金额</th>
<th>部门</th>
</tr>
</thead>
<tbody>
<tr v-for="person in filteredTableData" :key="person.id" @click="$emit('select-person', person)" :class="{ selected: selectedPerson && selectedPerson.id === person.id }">
<td>
<div class="person-info">
<div class="person-avatar">{{ person.name.charAt(0) }}</div>
<div>
<div class="person-name">{{ person.name }}</div>
<div class="person-position">{{ person.position }}</div>
</div>
</div>
</td>
<td>
<div class="deal-rate">
<span class="rate-value" :class="getRateClass(person.dealRate)">{{ person.dealRate }}%</span>
<div class="rate-bar">
<div class="rate-fill" :style="{ width: person.dealRate + '%', backgroundColor: getRateColor(person.dealRate) }"></div>
</div>
</div>
</td>
<td>{{ formatDuration(person.callDuration) }}</td>
<td>{{ person.callCount }}</td>
<td>¥{{ person.dealAmount.toLocaleString() }}</td>
<td>{{ person.department }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script setup>
import { defineProps, defineEmits, reactive } from 'vue';
defineProps({
filteredTableData: Array,
selectedPerson: Object,
sortField: String,
sortOrder: String,
getRateClass: Function,
getRateColor: Function,
formatDuration: Function
});
defineEmits(['sort-by', 'select-person']);
const filters = reactive({
department: '',
position: '',
timeRange: 'month',
dealStatus: ''
});
</script>
<style scoped>
.dashboard-card.table-section {
grid-column: 1 / 3;
}
.data-table-container {
margin-top: 20px;
}
.table-filters {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.filter-group {
display: flex;
align-items: center;
gap: 8px;
}
.filter-group label {
font-size: 14px;
}
.filter-group select {
padding: 6px 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
.data-table table {
width: 100%;
border-collapse: collapse;
}
.data-table th, .data-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.data-table th.sortable {
cursor: pointer;
}
.sort-icon {
opacity: 0.5;
}
.sort-icon.active {
opacity: 1;
}
.person-info {
display: flex;
align-items: center;
}
.person-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #4CAF50;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
margin-right: 10px;
}
.person-name {
font-weight: bold;
}
.person-position {
font-size: 12px;
color: #666;
}
.deal-rate .rate-value {
font-weight: bold;
}
.deal-rate .rate-bar {
height: 6px;
background-color: #f0f0f0;
border-radius: 3px;
margin-top: 5px;
}
.deal-rate .rate-fill {
height: 100%;
border-radius: 3px;
}
tbody tr {
cursor: pointer;
transition: background-color 0.2s;
}
tbody tr:hover {
background-color: #f5f5f5;
}
tbody tr.selected {
background-color: #e8f5e9;
}
</style>

View File

@@ -50,7 +50,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="(person, index) in displayTableData" :key="index" @click="$emit('update:selectedPerson', person)" @dblclick="handlePersonDoubleClick(person)" :class="{ selected: selectedPerson && (selectedPerson.sale_name === person.sale_name || selectedPerson.leader_name === person.leader_name) }"> <tr v-for="(person, index) in displayTableData" :key="index" @click="$emit('update:selectedPerson', person)" @dblclick="handlePersonDoubleClick(person)" :class="{ selected: selectedPerson === person }">
<td> <td>
<div class="person-info"> <div class="person-info">
<div class="person-avatar">{{ (person.sale_name || person.leader_name).charAt(0) }}</div> <div class="person-avatar">{{ (person.sale_name || person.leader_name).charAt(0) }}</div>
@@ -202,7 +202,6 @@ const handlePersonDoubleClick = (person) => {
default: default:
targetPath = '/second-top'; targetPath = '/second-top';
} }
// 路由跳转 // 路由跳转
router.push({ router.push({
path: targetPath, path: targetPath,
@@ -212,8 +211,6 @@ const handlePersonDoubleClick = (person) => {
} }
}); });
}; };
// 移除formatDuration函数不再需要
</script> </script>
<style scoped> <style scoped>

View File

@@ -4,7 +4,7 @@
<h3>转化对比图</h3> <h3>转化对比图</h3>
<div class="time-selector"> <div class="time-selector">
<select v-model="selectedTimeRange" @change="handleTimeRangeChange" class="time-select"> <select v-model="selectedTimeRange" @change="handleTimeRangeChange" class="time-select">
<option value="period">本期 vs 上期</option> <option value="periods">本期 vs 上期</option>
<option value="month">本月 vs 上月</option> <option value="month">本月 vs 上月</option>
</select> </select>
</div> </div>
@@ -67,15 +67,15 @@ const props = defineProps({
const emit = defineEmits(['time-range-change']); const emit = defineEmits(['time-range-change']);
const selectedTimeRange = ref('period'); const selectedTimeRange = ref('periods');
// 计算属性:当前和上一期的标签 // 计算属性:当前和上一期的标签
const currentPeriodLabel = computed(() => { const currentPeriodLabel = computed(() => {
return selectedTimeRange.value === 'period' ? '本期' : '本月'; return selectedTimeRange.value === 'periods' ? '本期' : '本月';
}); });
const previousPeriodLabel = computed(() => { const previousPeriodLabel = computed(() => {
return selectedTimeRange.value === 'period' ? '上期' : '上月'; return selectedTimeRange.value === 'periods' ? '上期' : '上月';
}); });
// 计算属性:图表数据 // 计算属性:图表数据

View File

@@ -73,7 +73,7 @@ const props = defineProps({
} }
}); });
const emit = defineEmits(['period-change', 'ranking-type-change']); const emit = defineEmits(['periods-change', 'ranking-type-change']);
const rankingPeriod = ref('month'); const rankingPeriod = ref('month');
const rankingType = ref('red'); // 'red' 为红榜,'black' 为黑榜 const rankingType = ref('red'); // 'red' 为红榜,'black' 为黑榜
@@ -94,7 +94,7 @@ const displayData = computed(() => {
}); });
const handlePeriodChange = () => { const handlePeriodChange = () => {
emit('period-change', rankingPeriod.value); emit('periods-change', rankingPeriod.value);
}; };
const handleRankingTypeChange = (type) => { const handleRankingTypeChange = (type) => {
@@ -168,7 +168,7 @@ const handleRankingTypeChange = (type) => {
color: #2c3e50; color: #2c3e50;
} }
.period-select { .periods-select {
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 6px; border-radius: 6px;
padding: 8px 12px; padding: 8px 12px;
@@ -178,11 +178,11 @@ const handleRankingTypeChange = (type) => {
transition: border-color 0.3s ease; transition: border-color 0.3s ease;
} }
.period-select:hover { .periods-select:hover {
border-color: #3498db; border-color: #3498db;
} }
.period-select:focus { .periods-select:focus {
outline: none; outline: none;
border-color: #3498db; border-color: #3498db;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2); box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);

View File

@@ -2,7 +2,7 @@
<div class="dashboard-card"> <div class="dashboard-card">
<div class="card-header"> <div class="card-header">
<h3>团队业绩排行榜</h3> <h3>团队业绩排行榜</h3>
<select v-model="rankingPeriod" class="period-select" @change="onPeriodChange"> <select v-model="rankingPeriod" class="periods-select" @change="onPeriodChange">
<option value="periods">本期</option> <option value="periods">本期</option>
<option value="month">月度</option> <option value="month">月度</option>
<option value="year">年度</option> <option value="year">年度</option>
@@ -17,9 +17,9 @@
<div class="employee-info"> <div class="employee-info">
<div class="employee-name">{{ item.name }}</div> <div class="employee-name">{{ item.name }}</div>
</div> </div>
<div class="employee-dept">转化率{{ item.average_deals_per_member }}</div> <!-- <div class="employee-dept">转化率{{ item.average_deals_per_member }}</div> -->
<div class="performance-value"> <div class="performance-value">
单数{{ formatNumber(item.performance) }} 成单数{{ formatNumber(item.performance) }}
</div> </div>
</div> </div>
@@ -29,21 +29,70 @@
</template> </template>
<script setup> <script setup>
import { defineProps, ref, defineEmits } from 'vue'; import { defineProps, ref, defineEmits, computed, onMounted } from 'vue';
import { getCenterPerformanceRank } from '@/api/top';
defineProps({ const props = defineProps({
rankingData: Array,
formatNumber: Function, formatNumber: Function,
getRankClass: Function getRankClass: Function
}); });
const emit = defineEmits(['period-change']); const emit = defineEmits(['periods-change']);
const rankingPeriod = ref('periods'); const rankingPeriod = ref('periods');
const centerSalesRank = ref({});
// 计算属性:转换 centerSalesRank 数据格式
const rankingData = computed(() => {
if (!centerSalesRank.value) {
return []; // 返回空数组
}
// 根据不同的时间段获取对应的数据字段
let rankList = null;
if (rankingPeriod.value === 'periods' && centerSalesRank.value.center_performance_periods_rank) {
rankList = centerSalesRank.value.center_performance_periods_rank;
} else if (rankingPeriod.value === 'month' && centerSalesRank.value.center_performance_month_rank) {
rankList = centerSalesRank.value.center_performance_month_rank;
} else if (rankingPeriod.value === 'year' && centerSalesRank.value.center_performance_year_rank) {
rankList = centerSalesRank.value.center_performance_year_rank;
}
if (!rankList) {
return [];
}
return rankList.map((item, index) => ({
id: index + 1,
name: item.center_leader,
performance: item.total_deals,
average_deals_per_member: item.average_deals_per_member
}));
});
// 获取全中心业绩排行榜数据
async function getCenterSalesRank(data) {
const params = {
check_type: data // periods、month、year
};
try {
const res = await getCenterPerformanceRank(params);
console.log('获取中心业绩排行榜:', res);
centerSalesRank.value = res.data;
} catch (error) {
console.error('获取全中心业绩排行榜失败:', error);
}
}
const onPeriodChange = () => { const onPeriodChange = () => {
emit('period-change', rankingPeriod.value); getCenterSalesRank(rankingPeriod.value);
emit('periods-change', rankingPeriod.value);
}; };
// 组件挂载时获取初始数据
onMounted(() => {
getCenterSalesRank(rankingPeriod.value);
});
</script> </script>
<style scoped> <style scoped>
@@ -70,7 +119,7 @@ const onPeriodChange = () => {
font-size: 18px; font-size: 18px;
} }
.period-select { .periods-select {
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 4px; border-radius: 4px;
padding: 6px 10px; padding: 6px 10px;

View File

@@ -50,7 +50,7 @@ defineProps({
font-size: 18px; font-size: 18px;
} }
.metric-period { .metric-periods {
font-size: 14px; font-size: 14px;
color: #666; color: #666;
} }

View File

@@ -34,7 +34,7 @@
:ranking-data="formattedSalesRankingData" :ranking-data="formattedSalesRankingData"
:format-number="formatNumber" :format-number="formatNumber"
:get-rank-class="getRankClass" :get-rank-class="getRankClass"
@period-change="handleRankingPeriodChange" @periods-change="handleRankingPeriodChange"
@ranking-type-change="getCompanySalesRank" @ranking-type-change="getCompanySalesRank"
/> />
<!-- 优质通话 --> <!-- 优质通话 -->
@@ -48,10 +48,8 @@
<div class="dashboard-row row-3"> <div class="dashboard-row row-3">
<!-- 业绩排行榜 --> <!-- 业绩排行榜 -->
<ranking-list <ranking-list
:ranking-data="formattedRankingData"
:format-number="formatNumber" :format-number="formatNumber"
:get-rank-class="getRankClass" :get-rank-class="getRankClass"
@period-change="getCenterSalesRank"
/> />
<!-- 客户类型占比 --> <!-- 客户类型占比 -->
<customer-type :customer-data="customerTypeRatio" @category-change="getCustomerTypeRatio" /> <customer-type :customer-data="customerTypeRatio" @category-change="getCustomerTypeRatio" />
@@ -63,14 +61,13 @@
<CampManagement /> <CampManagement />
</div> </div>
<!-- 第五行 --> <!-- 第五行 -->
<div class="dashboard-row row-4"> <div class="dashboard-row">
<DetailedDataTable <DetailedDataTable
:table-data="detailData" :table-data="detailData"
:level-tree="levelTree" :level-tree="levelTree"
v-model:selected-person="selectedPerson" v-model:selected-person="selectedPerson"
@filter-change="handleFilterChange" @filter-change="handleFilterChange"
/> />
<DataDetailCard :selected-person="selectedPerson" />
</div> </div>
<!-- 新建任务模态框 --> <!-- 新建任务模态框 -->
<div <div
@@ -155,13 +152,12 @@ import RankingList from "./components/RankingList.vue";
import PersonalSalesRanking from "./components/PersonalSalesRanking.vue"; import PersonalSalesRanking from "./components/PersonalSalesRanking.vue";
import CommunicationData from "./components/CommunicationData.vue"; import CommunicationData from "./components/CommunicationData.vue";
import QualityCalls from "./components/QualityCalls.vue"; import QualityCalls from "./components/QualityCalls.vue";
import DataTable from "./components/DataTable.vue"; // import DataTable from "./components/DataTable.vue";
import DataDetail from "./components/DataDetail.vue"; import DataDetail from "./components/DataDetail.vue";
import CampManagement from "./components/CampManagement.vue"; import CampManagement from "./components/CampManagement.vue";
import DetailedDataTable from "./components/DetailedDataTable.vue"; import DetailedDataTable from "./components/DetailedDataTable.vue";
import DataDetailCard from "./components/DataDetailCard.vue";
import { getOverallCompanyPerformance,getCompanyDepositConversionRate,getCompanyTotalCallCount,getCompanyNewCustomer,getCompanyConversionRate,getCompanyRealTimeProgress import { getOverallCompanyPerformance,getCompanyDepositConversionRate,getCompanyTotalCallCount,getCompanyNewCustomer,getCompanyConversionRate,getCompanyRealTimeProgress
,getCompanyConversionRateVsLast,getSalesMonthlyPerformance,getCenterPerformanceRank,getCustomerTypeDistribution,getUrgentNeedToAddress,getLevelTree,getDetailedDataTable ,getCompanyConversionRateVsLast,getSalesMonthlyPerformance,getCustomerTypeDistribution,getUrgentNeedToAddress,getLevelTree,getDetailedDataTable
} from "@/api/top"; } from "@/api/top";
const rankingPeriod = ref("month"); const rankingPeriod = ref("month");
const rankingData = ref([ const rankingData = ref([
@@ -433,7 +429,7 @@ const formattedComparisonData = computed(() => {
} }
// 根据 check_type 确定时间范围键 // 根据 check_type 确定时间范围键
const timeRangeKey = checkType === 'month' ? 'month' : 'period'; const timeRangeKey = checkType === 'month' ? 'month' : 'periods';
const stageOrder = ['线索总数', '加微', '到课', '付定金', '成交']; const stageOrder = ['线索总数', '加微', '到课', '付定金', '成交'];
const comparisonArray = stageOrder.map(stageName => ({ const comparisonArray = stageOrder.map(stageName => ({
@@ -445,7 +441,7 @@ const formattedComparisonData = computed(() => {
// 同时返回period和month两个键确保组件能找到对应数据 // 同时返回period和month两个键确保组件能找到对应数据
const result = { const result = {
period: comparisonArray, periods: comparisonArray,
month: comparisonArray month: comparisonArray
}; };
@@ -455,80 +451,12 @@ const formattedComparisonData = computed(() => {
async function getConversionComparison(data) { async function getConversionComparison(data) {
const params={ const params={
check_type:data check_type:data //month periods
} }
try { try {
const res = await getCompanyConversionRateVsLast(params) const res = await getCompanyConversionRateVsLast(params)
console.log(111111,res) console.log(111111,res)
conversionComparison.value = res.data conversionComparison.value = res.data
/**
* data
:
{user_name: "赵世敬", user_level: 5, check_type: "month",…}
check_type
:
"month"
company_current_rate
:
{线索总数: 14051, 加微: 3238, 到课: 7614, 付定金: 168, 成交: 136}
付定金
:
168
到课
:
7614
加微
:
3238
成交
:
136
线索总数
:
14051
company_current_vs_last_rate
:
{线索总数: "-20.16%", 加微: "-2.70%", 到课: "+114.90%", 付定金: "+∞%", 成交: "+∞%"}
付定金
:
"+∞%"
到课
:
"+114.90%"
加微
:
"-2.70%"
成交
:
"+∞%"
线索总数
:
"-20.16%"
company_last_rate
:
{线索总数: 17598, 加微: 3328, 到课: 3543, 付定金: 0, 成交: 0}
付定金
:
0
到课
:
3543
加微
:
3328
成交
:
0
线索总数
:
17598
user_level
:
5
user_name
:
"赵世敬"
*/
} catch (error) { } catch (error) {
console.error("获取转化对比失败:", error); console.error("获取转化对比失败:", error);
} }
@@ -567,7 +495,7 @@ const formattedSalesRankingData = computed(() => {
}); });
// 处理销售排行榜期间变化 // 处理销售排行榜期间变化
const handleRankingPeriodChange = (period) => { const handleRankingPeriodChange = (periods) => {
// 根据期间参数调用相应的函数,这里默认调用红榜数据 // 根据期间参数调用相应的函数,这里默认调用红榜数据
getCompanySalesRank('red'); getCompanySalesRank('red');
}; };
@@ -579,200 +507,11 @@ async function getCompanySalesRank(Rank) {
try { try {
const res = await getSalesMonthlyPerformance(params) const res = await getSalesMonthlyPerformance(params)
companySalesRank.value = res.data companySalesRank.value = res.data
/**
* "data": {
"user_name": "赵世敬",
"user_level": 5,
"rank_type": "red",
"sales_monthly_performance_red": [
{
"name": "贾星草",
"department": "洋葱管理层",
"deal_count": 2,
"conversion_rate": "2.78%",
"rank": 1
},
{
"name": "常志洁",
"department": "星火二部--王志恒",
"deal_count": 2,
"conversion_rate": "4.17%",
"rank": 2
},
{
"name": "李俊",
"department": "星火二部--王志恒",
"deal_count": 2,
"conversion_rate": "3.77%",
"rank": 3
},
{
"name": "高有桔",
"department": "勇士一部-张茂华",
"deal_count": 2,
"conversion_rate": "3.12%",
"rank": 4
},
{
"name": "马肖剑",
"department": "星耀三部-周毅",
"deal_count": 1,
"conversion_rate": "1.05%",
"rank": 5
},
{
"name": "刘思雨",
"department": "星耀三部-周毅",
"deal_count": 1,
"conversion_rate": "2.27%",
"rank": 6
},
{
"name": "王慧",
"department": "聚星三部--张卓",
"deal_count": 1,
"conversion_rate": "2.00%",
"rank": 7
},
{
"name": "寇帅杰",
"department": "巅峰一部-贾星草",
"deal_count": 1,
"conversion_rate": "2.13%",
"rank": 8
},
{
"name": "王奥博",
"department": "巅峰二部-纪洋洋",
"deal_count": 1,
"conversion_rate": "1.14%",
"rank": 9
},
{
"name": "董富忠",
"department": "巅峰三部--刘东洋",
"deal_count": 1,
"conversion_rate": "0.95%",
"rank": 10
}
]
}
*/
// 黑榜数据
/**
* "data": {
"user_name": "赵世敬",
"user_level": 5,
"rank_type": "black",
"sales_monthly_performance_black": [
{
"name": "马然",
"department": "美团业务支持部",
"deal_count": 0,
"conversion_rate": "0.00%",
"rank": 1
},
{
"name": "郭可英",
"department": "技术部",
"deal_count": 0,
"conversion_rate": "0.00%",
"rank": 2
},
{
"name": "杨启晨",
"department": "星火一部--张瑾",
"deal_count": 0,
"conversion_rate": "0.00%",
"rank": 3
},
{
"name": "程慧仟",
"department": "星耀三部-周毅",
"deal_count": 0,
"conversion_rate": "0.00%",
"rank": 4
},
{
"name": "常琳",
"department": "亮剑二部-田贵星",
"deal_count": 0,
"conversion_rate": "0.00%",
"rank": 5
},
{
"name": "李晓雪",
"department": "星火一部--张瑾",
"deal_count": 0,
"conversion_rate": "0.00%",
"rank": 6
},
{
"name": "杨朵朵",
"department": "星耀三部-周毅",
"deal_count": 0,
"conversion_rate": "0.00%",
"rank": 7
},
{
"name": "张明起",
"department": "星耀一部-吕明月",
"deal_count": 0,
"conversion_rate": "0.00%",
"rank": 8
},
{
"name": "刘英杰",
"department": "星耀一部-吕明月",
"deal_count": 0,
"conversion_rate": "0.00%",
"rank": 9
},
{
"name": "孟凡玉",
"department": "星耀一部-吕明月",
"deal_count": 0,
"conversion_rate": "0.00%",
"rank": 10
}
]
}
*/
} catch (error) { } catch (error) {
console.error("获取销售月度业绩红黑榜失败:", error); console.error("获取销售月度业绩红黑榜失败:", error);
} }
} }
// 获取全中心业绩排行榜 // 获取全中心业绩排行榜逻辑已移至 RankingList 组件
const centerSalesRank = ref({});
// 计算属性:转换 centerSalesRank 数据格式以适配 ranking-list 组件
const formattedRankingData = computed(() => {
if (!centerSalesRank.value || !centerSalesRank.value.center_performance_periods_rank) {
return rankingData.value; // 返回默认数据
}
const rankList = centerSalesRank.value.center_performance_periods_rank;
return rankList.map((item, index) => ({
id: index + 1,
name: item.center_leader,
performance: item.total_deals,
average_deals_per_member: item.average_deals_per_member
}));
});
async function getCenterSalesRank(data) {
const params={
check_type:data //periods、month、year
}
try {
const res = await getCenterPerformanceRank(params)
console.log(1222222,res)
centerSalesRank.value = res.data
} catch (error) {
console.error("获取全中心业绩排行榜失败:", error);
}
}
// 客户类型占比 // 客户类型占比
const customerTypeRatio = ref({}); const customerTypeRatio = ref({});
async function getCustomerTypeRatio(data) { async function getCustomerTypeRatio(data) {
@@ -805,55 +544,6 @@ async function CusotomGetLevelTree() {
const res = await getLevelTree() const res = await getLevelTree()
console.log(1222222,res) console.log(1222222,res)
levelTree.value = res.data levelTree.value = res.data
/**
* "data": {
"user_name": "赵世敬",
"user_level": 5,
"level_tree": {
"center_leaders": [
{
"name": "郭可英",
"advanced_managers": [
{
"name": "李小燕",
"managers": []
},
{
"name": "郭子奇",
"managers": [
{
"name": "杨朵朵"
},
{
"name": "张明起"
}
]
}
]
},
{
"name": "刘瑞",
"advanced_managers": [
{
"name": "陈盼良",
"managers": [
{
"name": "马然"
},
{
"name": "杨启晨"
},
{
"name": "韦少杰"
}
]
}
]
}
]
}
}
*/
} catch (error) { } catch (error) {
console.error("获取级别树失败:", error); console.error("获取级别树失败:", error);
} }
@@ -862,19 +552,15 @@ async function CusotomGetLevelTree() {
const detailData = ref({}); const detailData = ref({});
async function getDetailData(params) { async function getDetailData(params) {
if(params?.center_leader){ if(params?.center_leader){
// alert(11111)
try { try {
const res = await getDetailedDataTable(params) const res = await getDetailedDataTable(params)
console.log('详细数据表格:', res)
detailData.value = res.data detailData.value = res.data
} catch (error) { } catch (error) {
console.error("获取详细数据表格失败:", error); console.error("获取详细数据表格失败:", error);
} }
}else{ }else{
// alert(22222)
try { try {
const res = await getDetailedDataTable() const res = await getDetailedDataTable()
console.log('详细数据表格:', res)
detailData.value = res.data detailData.value = res.data
} catch (error) { } catch (error) {
console.error("获取详细数据表格失败:", error); console.error("获取详细数据表格失败:", error);
@@ -895,7 +581,6 @@ onMounted(async() => {
await getTotalDeals() await getTotalDeals()
await getConversionComparison('month') await getConversionComparison('month')
await getCompanySalesRank('red') await getCompanySalesRank('red')
await getCenterSalesRank('periods')
await getCustomerTypeRatio('child_education') await getCustomerTypeRatio('child_education')
await getCustomerUrgency() await getCustomerUrgency()
await CusotomGetLevelTree() await CusotomGetLevelTree()
@@ -978,7 +663,7 @@ onMounted(async() => {
margin: 0; margin: 0;
} }
.metric-period { .metric-periods {
font-size: 12px; font-size: 12px;
color: #718096; color: #718096;
background: #edf2f7; background: #edf2f7;
@@ -1317,7 +1002,7 @@ onMounted(async() => {
margin: 0; margin: 0;
} }
.period-select { .periods-select {
padding: 4px 8px; padding: 4px 8px;
border: 1px solid #e2e8f0; border: 1px solid #e2e8f0;
border-radius: 4px; border-radius: 4px;