feat: 省市区存储文字、工厂关联用户账号、大屏图表主题优化
- 省市区字段改为存储文字名称而非编码 - 工厂序列化器新增usernames字段,列表和详情页展示关联用户账号 - 地区分布统计改为仅按省份聚合 - 新增ECharts screen-dark主题,统一配色和字号 - 大屏卡片背景由纯黑改为深蓝渐变 Made-with: Cursor
This commit is contained in:
parent
2ce0d75c41
commit
c6ba742f9d
|
|
@ -7,25 +7,32 @@ class FactorySerializer(serializers.ModelSerializer):
|
||||||
工厂序列化器
|
工厂序列化器
|
||||||
"""
|
"""
|
||||||
material_count = serializers.SerializerMethodField()
|
material_count = serializers.SerializerMethodField()
|
||||||
|
usernames = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Factory
|
model = Factory
|
||||||
fields = ['id', 'dealer_name', 'product_category', 'factory_name',
|
fields = ['id', 'dealer_name', 'product_category', 'factory_name',
|
||||||
'brand', 'province', 'city', 'district',
|
'brand', 'province', 'city', 'district',
|
||||||
'address', 'website', 'created_at', 'updated_at', 'material_count']
|
'address', 'website', 'created_at', 'updated_at',
|
||||||
read_only_fields = ['id', 'created_at', 'updated_at', 'material_count']
|
'material_count', 'usernames']
|
||||||
|
read_only_fields = ['id', 'created_at', 'updated_at', 'material_count', 'usernames']
|
||||||
|
|
||||||
def get_material_count(self, obj):
|
def get_material_count(self, obj):
|
||||||
"""
|
|
||||||
获取工厂的材料数量
|
|
||||||
"""
|
|
||||||
return obj.materials.count()
|
return obj.materials.count()
|
||||||
|
|
||||||
|
def get_usernames(self, obj):
|
||||||
|
return list(obj.users.values_list('username', flat=True))
|
||||||
|
|
||||||
|
|
||||||
class FactoryListSerializer(serializers.ModelSerializer):
|
class FactoryListSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
工厂列表序列化器(简化版)
|
工厂列表序列化器(简化版)
|
||||||
"""
|
"""
|
||||||
|
usernames = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Factory
|
model = Factory
|
||||||
fields = ['id', 'factory_name', 'brand', 'province', 'city', 'dealer_name']
|
fields = ['id', 'factory_name', 'brand', 'province', 'city', 'dealer_name', 'usernames']
|
||||||
|
|
||||||
|
def get_usernames(self, obj):
|
||||||
|
return list(obj.users.values_list('username', flat=True))
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ def overview_statistics(request):
|
||||||
count=Count('id')
|
count=Count('id')
|
||||||
).order_by('-count')
|
).order_by('-count')
|
||||||
|
|
||||||
# 按地区的工厂数量分布
|
# 按省份的工厂数量分布
|
||||||
region_stats = Factory.objects.values('province', 'city').annotate(
|
region_stats = Factory.objects.values('province').annotate(
|
||||||
count=Count('id')
|
count=Count('id')
|
||||||
).order_by('-count')
|
).order_by('-count')
|
||||||
|
|
||||||
|
|
@ -167,7 +167,7 @@ def factory_statistics(request):
|
||||||
if request.user.role != 'admin':
|
if request.user.role != 'admin':
|
||||||
return Response({"detail": "无权访问"}, status=403)
|
return Response({"detail": "无权访问"}, status=403)
|
||||||
|
|
||||||
region_stats = list(Factory.objects.values('province', 'city').annotate(
|
region_stats = list(Factory.objects.values('province').annotate(
|
||||||
count=Count('id')
|
count=Count('id')
|
||||||
).order_by('-count'))
|
).order_by('-count'))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
.screen-body {
|
.screen-body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: radial-gradient(circle at top left, #203450 0%, #0c1422 45%, #070b12 100%);
|
background: radial-gradient(circle at top left, #203450 0%, #0c1422 45%, #070b12 100%);
|
||||||
color: #e6edf5;
|
color: #e6edf5;
|
||||||
|
|
@ -101,11 +101,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.screen-card {
|
.screen-card {
|
||||||
background: rgba(7, 14, 24, 0.92);
|
background: linear-gradient(145deg, rgba(20, 38, 62, 0.88), rgba(12, 24, 44, 0.92));
|
||||||
border: 1px solid rgba(159, 226, 255, 0.18);
|
border: 1px solid rgba(159, 226, 255, 0.12);
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
box-shadow: 0 14px 40px rgba(0, 0, 0, 0.45);
|
box-shadow: 0 14px 40px rgba(0, 0, 0, 0.35);
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
@ -147,9 +147,9 @@
|
||||||
.stat-card {
|
.stat-card {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
background: linear-gradient(140deg, rgba(78, 134, 184, 0.35), rgba(8, 18, 28, 0.9));
|
background: linear-gradient(140deg, rgba(78, 134, 184, 0.3), rgba(16, 30, 52, 0.85));
|
||||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
border: 1px solid rgba(159, 226, 255, 0.1);
|
||||||
box-shadow: inset 0 0 20px rgba(159, 226, 255, 0.08);
|
box-shadow: inset 0 0 20px rgba(159, 226, 255, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-card .label {
|
.stat-card .label {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
|
||||||
|
const COLORS = [
|
||||||
|
'#36d6fc', '#7b6cf6', '#f5a623', '#5dd9a7',
|
||||||
|
'#f06292', '#4fc3f7', '#ffd54f', '#9575cd',
|
||||||
|
'#4db6ac', '#ff8a65'
|
||||||
|
]
|
||||||
|
|
||||||
|
const TEXT = '#c9d7e6'
|
||||||
|
const AXIS = 'rgba(180, 200, 220, 0.55)'
|
||||||
|
const SPLIT = 'rgba(159, 226, 255, 0.06)'
|
||||||
|
|
||||||
|
echarts.registerTheme('screen-dark', {
|
||||||
|
color: COLORS,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
textStyle: { color: TEXT, fontSize: 14 },
|
||||||
|
title: { textStyle: { color: TEXT, fontSize: 16 } },
|
||||||
|
legend: {
|
||||||
|
textStyle: { color: TEXT, fontSize: 13 }
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
backgroundColor: 'rgba(12, 22, 38, 0.92)',
|
||||||
|
borderColor: 'rgba(54, 214, 252, 0.25)',
|
||||||
|
textStyle: { color: '#e6edf5', fontSize: 13 }
|
||||||
|
},
|
||||||
|
categoryAxis: {
|
||||||
|
axisLine: { lineStyle: { color: AXIS } },
|
||||||
|
axisTick: { lineStyle: { color: AXIS } },
|
||||||
|
axisLabel: { color: TEXT, fontSize: 13 },
|
||||||
|
splitLine: { lineStyle: { color: SPLIT } }
|
||||||
|
},
|
||||||
|
valueAxis: {
|
||||||
|
axisLine: { lineStyle: { color: AXIS } },
|
||||||
|
axisTick: { lineStyle: { color: AXIS } },
|
||||||
|
axisLabel: { color: TEXT, fontSize: 13 },
|
||||||
|
splitLine: { lineStyle: { color: SPLIT } }
|
||||||
|
},
|
||||||
|
line: {
|
||||||
|
smooth: true,
|
||||||
|
symbolSize: 6
|
||||||
|
},
|
||||||
|
pie: {
|
||||||
|
label: { color: TEXT, fontSize: 13 }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const THEME = 'screen-dark'
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
<el-descriptions-item label="地区">{{ formatRegion(factory.province, factory.city, factory.district) }}</el-descriptions-item>
|
<el-descriptions-item label="地区">{{ formatRegion(factory.province, factory.city, factory.district) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="地址">{{ factory.address }}</el-descriptions-item>
|
<el-descriptions-item label="地址">{{ factory.address }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="官网">{{ factory.website }}</el-descriptions-item>
|
<el-descriptions-item label="官网">{{ factory.website }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="用户账号">{{ (factory.usernames || []).join('、') || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="材料数量">{{ factory.material_count }}</el-descriptions-item>
|
<el-descriptions-item label="材料数量">{{ factory.material_count }}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,11 @@
|
||||||
<el-table-column prop="factory_name" label="工厂全称" />
|
<el-table-column prop="factory_name" label="工厂全称" />
|
||||||
<el-table-column prop="brand" label="品牌" />
|
<el-table-column prop="brand" label="品牌" />
|
||||||
<el-table-column prop="dealer_name" label="经销商" />
|
<el-table-column prop="dealer_name" label="经销商" />
|
||||||
|
<el-table-column label="用户账号">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ (scope.row.usernames || []).join('、') || '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="地区">
|
<el-table-column label="地区">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ formatRegion(scope.row.province, scope.row.city, scope.row.district) }}
|
{{ formatRegion(scope.row.province, scope.row.city, scope.row.district) }}
|
||||||
|
|
@ -54,6 +59,7 @@
|
||||||
<el-cascader
|
<el-cascader
|
||||||
v-model="regionValue"
|
v-model="regionValue"
|
||||||
:options="regionOptions"
|
:options="regionOptions"
|
||||||
|
:props="{ value: 'label' }"
|
||||||
clearable
|
clearable
|
||||||
@change="onRegionChange"
|
@change="onRegionChange"
|
||||||
/>
|
/>
|
||||||
|
|
@ -79,7 +85,7 @@ import { useRouter } from 'vue-router'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { regionData } from 'element-china-area-data'
|
import { regionData } from 'element-china-area-data'
|
||||||
import { useAuth } from '@/store/auth'
|
import { useAuth } from '@/store/auth'
|
||||||
import { formatRegion } from '@/utils/region'
|
import { formatRegion, regionLabel } from '@/utils/region'
|
||||||
import { fetchFactories, fetchFactoryDetail, createFactory, updateFactory, deleteFactory } from '@/api/factory'
|
import { fetchFactories, fetchFactoryDetail, createFactory, updateFactory, deleteFactory } from '@/api/factory'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
@ -154,7 +160,7 @@ const openEdit = async (row) => {
|
||||||
currentId.value = row.id
|
currentId.value = row.id
|
||||||
const detail = await fetchFactoryDetail(row.id)
|
const detail = await fetchFactoryDetail(row.id)
|
||||||
Object.assign(form, detail)
|
Object.assign(form, detail)
|
||||||
regionValue.value = [detail.province, detail.city, detail.district].filter(Boolean)
|
regionValue.value = [detail.province, detail.city, detail.district].filter(Boolean).map(regionLabel)
|
||||||
dialogTitle.value = '编辑工厂'
|
dialogTitle.value = '编辑工厂'
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="screen-grid">
|
<div class="screen-grid">
|
||||||
<div class="screen-card" style="grid-column: span 6;">
|
<div class="screen-card" style="grid-column: span 6;">
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
|
import { THEME } from '@/utils/chartTheme'
|
||||||
import { regionLabel, formatRegion } from '@/utils/region'
|
import { regionLabel, formatRegion } from '@/utils/region'
|
||||||
import { fetchFactoryStats } from '@/api/statistics'
|
import { fetchFactoryStats } from '@/api/statistics'
|
||||||
|
|
||||||
|
|
@ -45,19 +46,23 @@ const onResize = () => charts.forEach((chart) => chart.resize())
|
||||||
|
|
||||||
const initCharts = () => {
|
const initCharts = () => {
|
||||||
charts = [
|
charts = [
|
||||||
echarts.init(regionChart.value),
|
echarts.init(regionChart.value, THEME),
|
||||||
echarts.init(categoryChart.value)
|
echarts.init(categoryChart.value, THEME)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateCharts = (data) => {
|
const updateCharts = (data) => {
|
||||||
const regionData = (data.region_stats || []).map((item) => ({
|
const regionData = (data.region_stats || []).map((item) => ({
|
||||||
name: `${regionLabel(item.province)}-${regionLabel(item.city)}`,
|
name: regionLabel(item.province),
|
||||||
value: item.count
|
value: item.count
|
||||||
}))
|
}))
|
||||||
charts[0].setOption({
|
charts[0].setOption({
|
||||||
tooltip: { trigger: 'item' },
|
tooltip: { trigger: 'item' },
|
||||||
series: [{ type: 'pie', radius: ['30%', '70%'], data: regionData }]
|
series: [{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['30%', '70%'],
|
||||||
|
data: regionData
|
||||||
|
}]
|
||||||
})
|
})
|
||||||
|
|
||||||
const factoryStats = data.factory_category_stats || []
|
const factoryStats = data.factory_category_stats || []
|
||||||
|
|
@ -76,10 +81,10 @@ const updateCharts = (data) => {
|
||||||
|
|
||||||
charts[1].setOption({
|
charts[1].setOption({
|
||||||
tooltip: { trigger: 'axis' },
|
tooltip: { trigger: 'axis' },
|
||||||
legend: { textStyle: { color: '#c9d7e6' } },
|
legend: {},
|
||||||
grid: { left: 40, right: 20, top: 30, bottom: 40 },
|
grid: { left: 40, right: 20, top: 30, bottom: 40 },
|
||||||
xAxis: { type: 'category', data: factoriesAxis, axisLabel: { color: '#9fb3c8', rotate: 20 } },
|
xAxis: { type: 'category', data: factoriesAxis, axisLabel: { rotate: 20 } },
|
||||||
yAxis: { type: 'value', axisLabel: { color: '#9fb3c8' } },
|
yAxis: { type: 'value' },
|
||||||
series
|
series
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="toolbar" style="margin-bottom: 16px;">
|
<div class="toolbar" style="margin-bottom: 16px;">
|
||||||
<el-select v-model="subcategory" placeholder="按材料子类筛选" clearable @change="loadData" style="width: 220px">
|
<el-select v-model="subcategory" placeholder="按材料子类筛选" clearable @change="loadData" style="width: 220px">
|
||||||
|
|
@ -42,6 +42,7 @@
|
||||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
|
import { THEME } from '@/utils/chartTheme'
|
||||||
import { fetchMaterialStats } from '@/api/statistics'
|
import { fetchMaterialStats } from '@/api/statistics'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
@ -62,9 +63,9 @@ const onResize = () => charts.forEach((chart) => chart.resize())
|
||||||
|
|
||||||
const initCharts = () => {
|
const initCharts = () => {
|
||||||
charts = [
|
charts = [
|
||||||
echarts.init(starChart.value),
|
echarts.init(starChart.value, THEME),
|
||||||
echarts.init(advChart.value),
|
echarts.init(advChart.value, THEME),
|
||||||
echarts.init(sceneChart.value)
|
echarts.init(sceneChart.value, THEME)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,15 +79,16 @@ const updateCharts = (data) => {
|
||||||
const levels = data.levels || [1, 2, 3]
|
const levels = data.levels || [1, 2, 3]
|
||||||
charts[0].setOption({
|
charts[0].setOption({
|
||||||
tooltip: { trigger: 'axis' },
|
tooltip: { trigger: 'axis' },
|
||||||
legend: { textStyle: { color: '#c9d7e6' } },
|
legend: {},
|
||||||
xAxis: { type: 'category', data: levels.map((l) => `${l}星`), axisLabel: { color: '#9fb3c8' } },
|
grid: { left: 40, right: 20, top: 30, bottom: 30 },
|
||||||
yAxis: { type: 'value', axisLabel: { color: '#9fb3c8' } },
|
xAxis: { type: 'category', data: levels.map((l) => `${l}星`) },
|
||||||
|
yAxis: { type: 'value' },
|
||||||
series: [
|
series: [
|
||||||
{ name: '质量', type: 'line', data: data.star_stats.quality_level, smooth: true },
|
{ name: '质量', type: 'line', data: data.star_stats.quality_level },
|
||||||
{ name: '耐久', type: 'line', data: data.star_stats.durability_level, smooth: true },
|
{ name: '耐久', type: 'line', data: data.star_stats.durability_level },
|
||||||
{ name: '环保', type: 'line', data: data.star_stats.eco_level, smooth: true },
|
{ name: '环保', type: 'line', data: data.star_stats.eco_level },
|
||||||
{ name: '低碳', type: 'line', data: data.star_stats.carbon_level, smooth: true },
|
{ name: '低碳', type: 'line', data: data.star_stats.carbon_level },
|
||||||
{ name: '总评', type: 'line', data: data.star_stats.score_level, smooth: true }
|
{ name: '总评', type: 'line', data: data.star_stats.score_level }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -103,17 +105,20 @@ const updateCharts = (data) => {
|
||||||
}))
|
}))
|
||||||
charts[1].setOption({
|
charts[1].setOption({
|
||||||
tooltip: { trigger: 'axis' },
|
tooltip: { trigger: 'axis' },
|
||||||
legend: { textStyle: { color: '#c9d7e6' } },
|
legend: {},
|
||||||
xAxis: { type: 'category', data: advTypes.map((adv) => mapLabel(choices.advantage, adv)), axisLabel: { color: '#9fb3c8' } },
|
grid: { left: 40, right: 20, top: 30, bottom: 30 },
|
||||||
yAxis: { type: 'value', axisLabel: { color: '#9fb3c8' } },
|
xAxis: { type: 'category', data: advTypes.map((adv) => mapLabel(choices.advantage, adv)) },
|
||||||
|
yAxis: { type: 'value' },
|
||||||
series
|
series
|
||||||
})
|
})
|
||||||
|
|
||||||
const sceneStats = data.application_scene_stats || []
|
const sceneStats = data.application_scene_stats || []
|
||||||
charts[2].setOption({
|
charts[2].setOption({
|
||||||
xAxis: { type: 'category', data: sceneStats.map((item) => mapLabel(choices.application_scene, item.application_scene)), axisLabel: { color: '#9fb3c8' } },
|
tooltip: { trigger: 'axis' },
|
||||||
yAxis: { type: 'value', axisLabel: { color: '#9fb3c8' } },
|
grid: { left: 40, right: 20, top: 20, bottom: 30 },
|
||||||
series: [{ type: 'bar', data: sceneStats.map((item) => item.count), itemStyle: { color: '#f2b24c' } }]
|
xAxis: { type: 'category', data: sceneStats.map((item) => mapLabel(choices.application_scene, item.application_scene)) },
|
||||||
|
yAxis: { type: 'value' },
|
||||||
|
series: [{ type: 'bar', data: sceneStats.map((item) => item.count), barWidth: '50%' }]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="stat-cards">
|
<div class="stat-cards">
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
|
|
@ -55,6 +55,7 @@
|
||||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
|
import { THEME } from '@/utils/chartTheme'
|
||||||
import { regionLabel } from '@/utils/region'
|
import { regionLabel } from '@/utils/region'
|
||||||
import { fetchOverviewStats } from '@/api/statistics'
|
import { fetchOverviewStats } from '@/api/statistics'
|
||||||
|
|
||||||
|
|
@ -71,10 +72,10 @@ const onResize = () => charts.forEach((chart) => chart.resize())
|
||||||
|
|
||||||
const initCharts = () => {
|
const initCharts = () => {
|
||||||
charts = [
|
charts = [
|
||||||
echarts.init(majorChart.value),
|
echarts.init(majorChart.value, THEME),
|
||||||
echarts.init(subChart.value),
|
echarts.init(subChart.value, THEME),
|
||||||
echarts.init(brandChart.value),
|
echarts.init(brandChart.value, THEME),
|
||||||
echarts.init(regionChart.value)
|
echarts.init(regionChart.value, THEME)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,26 +92,23 @@ const updateCharts = () => {
|
||||||
}))
|
}))
|
||||||
charts[0].setOption({
|
charts[0].setOption({
|
||||||
tooltip: { trigger: 'item' },
|
tooltip: { trigger: 'item' },
|
||||||
series: [
|
series: [{
|
||||||
{
|
type: 'pie',
|
||||||
type: 'pie',
|
radius: ['40%', '70%'],
|
||||||
radius: ['40%', '70%'],
|
data: majorData
|
||||||
data: majorData
|
}]
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const subData = stats.value.material_subcategory_stats || []
|
const subData = stats.value.material_subcategory_stats || []
|
||||||
charts[1].setOption({
|
charts[1].setOption({
|
||||||
grid: { left: 80, right: 20, top: 20, bottom: 20 },
|
grid: { left: 80, right: 20, top: 20, bottom: 20 },
|
||||||
xAxis: { type: 'value', axisLabel: { color: '#9fb3c8' } },
|
xAxis: { type: 'value' },
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
data: subData.map((item) => item.material_subcategory),
|
data: subData.map((item) => item.material_subcategory)
|
||||||
axisLabel: { color: '#9fb3c8' }
|
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{ type: 'bar', data: subData.map((item) => item.count), itemStyle: { color: '#7cb4e3' } }
|
{ type: 'bar', data: subData.map((item) => item.count), barWidth: '60%' }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -119,31 +117,29 @@ const updateCharts = () => {
|
||||||
value: item.count
|
value: item.count
|
||||||
}))
|
}))
|
||||||
charts[2].setOption({
|
charts[2].setOption({
|
||||||
series: [
|
tooltip: { trigger: 'item' },
|
||||||
{
|
series: [{
|
||||||
type: 'treemap',
|
type: 'treemap',
|
||||||
data: brandData,
|
data: brandData,
|
||||||
label: { show: true, color: '#0b121c' },
|
label: { show: true, color: '#e8f0f8', fontSize: 12 },
|
||||||
itemStyle: {
|
itemStyle: { borderColor: 'rgba(12, 24, 44, 0.6)', borderWidth: 2 },
|
||||||
borderColor: '#0b121c'
|
levels: [{
|
||||||
}
|
itemStyle: { borderColor: 'rgba(12, 24, 44, 0.8)', borderWidth: 3, gapWidth: 3 }
|
||||||
}
|
}]
|
||||||
]
|
}]
|
||||||
})
|
})
|
||||||
|
|
||||||
const regionData = (stats.value.region_stats || []).map((item) => ({
|
const regionData = (stats.value.region_stats || []).map((item) => ({
|
||||||
name: `${regionLabel(item.province)}-${regionLabel(item.city)}`,
|
name: regionLabel(item.province),
|
||||||
value: item.count
|
value: item.count
|
||||||
}))
|
}))
|
||||||
charts[3].setOption({
|
charts[3].setOption({
|
||||||
tooltip: { trigger: 'item' },
|
tooltip: { trigger: 'item' },
|
||||||
series: [
|
series: [{
|
||||||
{
|
type: 'pie',
|
||||||
type: 'pie',
|
radius: ['30%', '70%'],
|
||||||
radius: ['30%', '70%'],
|
data: regionData
|
||||||
data: regionData
|
}]
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue