feat: 添加路由权限控制和高级经理数据动态加载

refactor(router): 实现基于用户等级的路由守卫
feat(secondTop): 动态加载高级经理列表数据
fix(SalesTimelineWithTaskList): 修正默认职业和教育显示
perf(CustomerType): 优化图表数据绑定和交互
chore: 更新API基础路径配置
This commit is contained in:
2025-08-15 14:05:24 +08:00
parent 814961d84a
commit 88a73f5b52
6 changed files with 202 additions and 78 deletions

View File

@@ -2,8 +2,7 @@
<div class="chart-container">
<div class="chart-header">
<h3>客户类型占比</h3>
<select v-model="customerTypeCategory" @change="updateChart" class="chart-select">
<option value="age">年龄</option>
<select v-model="customerTypeCategory" @change="handleCategoryChange" class="chart-select">
<option value="profession">职业</option>
<option value="childGrade">孩子年级</option>
<option value="region">地域</option>
@@ -16,30 +15,88 @@
</template>
<script setup>
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue';
import { ref, reactive, onMounted, onBeforeUnmount, computed, watch } from 'vue';
import * as echarts from 'echarts';
// 定义props
const props = defineProps({
customerData: {
type: Object,
default: () => ({})
}
})
// 定义emit
const emit = defineEmits(['categoryChange'])
const customerTypeChartRef = ref(null);
let customerTypeChart = null;
const customerTypeCategory = ref('age');
const customerTypeData = reactive({
age: [{ value: 120, name: '18-25岁' }, { value: 200, name: '26-35岁' }, { value: 150, name: '36-45岁' }, { value: 80, name: '46-55岁' }, { value: 50, name: '55岁以上' }],
profession: [{ value: 180, name: '企业管理者' }, { value: 120, name: '教师' }, { value: 100, name: '医生' }, { value: 90, name: '工程师' }, { value: 110, name: '其他' }],
childGrade: [{ value: 80, name: '幼儿园' }, { value: 150, name: '小学' }, { value: 180, name: '初中' }, { value: 120, name: '高中' }, { value: 70, name: '大学' }],
region: [{ value: 200, name: '北京' }, { value: 150, name: '上海' }, { value: 120, name: '广州' }, { value: 100, name: '深圳' }, { value: 130, name: '其他' }]
});
const customerTypeCategory = ref('profession');
// 计算属性将API数据转换为图表所需格式
const chartData = computed(() => {
if (!props.customerData || !props.customerData.customer_type_distribution) {
return []
}
return props.customerData.customer_type_distribution.map(item => ({
name: item.category,
value: parseFloat(item.ratio.replace('%', '')) || 0
}))
})
const updateChart = () => {
if (!customerTypeChart) return;
const currentData = customerTypeData[customerTypeCategory.value];
// 使用真实数据
const currentData = chartData.value
if (!currentData || currentData.length === 0) {
// 如果没有数据,显示空状态
const option = {
title: {
text: '暂无数据',
left: 'center',
top: 'middle',
textStyle: {
color: '#999',
fontSize: 16
}
}
}
customerTypeChart.setOption(option, true)
return
}
const option = {
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function(params) {
return params[0].name + '<br/>' + params[0].seriesName + ': ' + params[0].value + '%'
}
},
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: { type: 'category', data: currentData.map(item => item.name), axisTick: { alignWithLabel: true } },
yAxis: { type: 'value' },
xAxis: {
type: 'category',
data: currentData.map(item => item.name),
axisTick: { alignWithLabel: true },
axisLabel: {
interval: 0,
rotate: currentData.length > 5 ? 45 : 0
}
},
yAxis: {
type: 'value',
axisLabel: {
formatter: '{value}%'
}
},
series: [{
name: '客户数量', type: 'bar', barWidth: '60%',
name: '占比',
type: 'bar',
barWidth: '60%',
data: currentData.map(item => item.value),
itemStyle: {
color: (params) => ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de'][params.dataIndex % 5]
@@ -57,9 +114,30 @@ const initChart = () => {
const resizeChart = () => customerTypeChart?.resize();
// 处理类别选择变化
const handleCategoryChange = () => {
// 映射选择值到API参数
const distributionTypeMap = {
'profession': 'occupation',
'childGrade': 'child_education',
'region': 'territory'
}
// 通知父组件选择变化
emit('categoryChange', distributionTypeMap[customerTypeCategory.value])
}
// 监听数据变化
watch(chartData, () => {
updateChart()
}, { deep: true })
onMounted(() => {
initChart();
customerTypeChart = echarts.init(customerTypeChartRef.value);
updateChart();
window.addEventListener('resize', resizeChart);
// 初始化时触发一次事件,加载默认数据
handleCategoryChange()
});
onBeforeUnmount(() => {

View File

@@ -56,6 +56,10 @@ const props = defineProps({
groups: {
type: Array,
required: true
},
seniorManagerData: {
type: Object,
default: () => ({})
}
})
@@ -65,13 +69,21 @@ const emit = defineEmits(['select-group', 'manager-change'])
const selectedManager = ref('all')
// 高级经理列表
const seniorManagers = ref([
{ id: 'manager1', name: '张经理' },
{ id: 'manager2', name: '李经理' },
{ id: 'manager3', name: '王经理' },
{ id: 'manager4', name: '刘经理' },
{ id: 'manager5', name: '陈经理' }
])
const seniorManagers = computed(() => {
if (props.seniorManagerData && props.seniorManagerData.center_advanced_managers) {
return props.seniorManagerData.center_advanced_managers.map((name, index) => ({
id: `manager_${index}`,
name: name
}))
}
return [
{ id: 'manager1', name: '张经理' },
{ id: 'manager2', name: '李经理' },
{ id: 'manager3', name: '王经理' },
{ id: 'manager4', name: '刘经理' },
{ id: 'manager5', name: '陈经理' }
]
})
// 处理经理选择变化
const handleManagerChange = () => {

View File

@@ -36,7 +36,7 @@
</div>
<div class="BB-section">
<!--客户类型占比-->
<CustomerType />
<CustomerType :customer-data="customerTypeDistribution" @category-change="handleCustomerTypeChange" />
<!-- 优秀录音 -->
<GoodMusic />
<!-- 客户问题排行 -->
@@ -51,7 +51,7 @@
<!-- Right Section - Group Comparison -->
<div class="right-section">
<GroupComparison :groups="groups" @select-group="selectGroup" />
<GroupComparison :groups="groups" :senior-manager-data="seniorManagerList" @select-group="selectGroup" />
</div>
</div>
@@ -285,18 +285,25 @@
}
}
// 客户类型
async function CenterCustomerType() {
async function CenterCustomerType(distributionType = 'occupation') {
const params = getRequestParams()
const hasParams = params.user_name
// 添加distribution_type参数
const requestParams = hasParams ? { ...params, distribution_type: distributionType } : { distribution_type: distributionType }
try {
const res = await getCustomerTypeDistribution(hasParams ? params : undefined)
const res = await getCustomerTypeDistribution(requestParams)
if (res.code === 200) {
customerTypeDistribution.value = res.data
}
} catch (error) {
console.error('获取中心整体概览失败:', error)
console.error('获取客户类型分布失败:', error)
}
}
// 处理客户类型选择变化
const handleCustomerTypeChange = (distributionType) => {
CenterCustomerType(distributionType)
}
// 客户迫切解决的问题
async function CenterUrgentNeedToAddress() {
const params = getRequestParams()
@@ -305,46 +312,29 @@
const res = await getUrgentNeedToAddress(hasParams ? params : undefined)
if (res.code === 200) {
urgentNeedToAddress.value = res.data
}
} catch (error) {
console.error('获取中心整体概览失败:', error)
}
}
// 综合排名---高级经理列表
const seniorManagerList = ref([])
async function CenterSeniorManagerList() {
const params = getRequestParams()
const hasParams = params.user_name
try {
const res = await getCenterAdvancedManagerList(hasParams ? params : undefined)
if (res.code === 200) {
seniorManagerList.value = res.data
/**
* data
:
{user_name: "刘瑞", user_level: 4,…}
center_urgent_issue_counts
:
{成绩提升: 1, 少玩手机: 1, 回归学校: 0, 心理健康: 0}
回归学校
:
0
少玩手机
:
1
心理健康
:
0
成绩提升
:
1
center_urgent_issue_ratio
:
{成绩提升: "50.00%", 少玩手机: "50.00%", 回归学校: "0.00%", 心理健康: "0.00%"}
回归学校
:
"0.00%"
少玩手机
:
"50.00%"
心理健康
:
"0.00%"
成绩提升
:
"50.00%"
user_level
:
4
user_name
:
"刘瑞"
* "data": {
"user_name": "刘瑞",
"user_level": 4,
"center_advanced_managers": [
"陈盼良"
]
}
*/
}
} catch (error) {
@@ -396,6 +386,7 @@ user_name
await CenterDepositConversionRate()
await CenterCustomerType()
await CenterUrgentNeedToAddress()
await CenterSeniorManagerList()
})