feat: 添加路由权限控制和高级经理数据动态加载
refactor(router): 实现基于用户等级的路由守卫 feat(secondTop): 动态加载高级经理列表数据 fix(SalesTimelineWithTaskList): 修正默认职业和教育显示 perf(CustomerType): 优化图表数据绑定和交互 chore: 更新API基础路径配置
This commit is contained in:
@@ -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(() => {
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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()
|
||||
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user