style(ops): 优化工单状态分布和工牌队列统计图表样式

状态分布饼图:
- 使用 Ant Design 色板统一配色
- legend 显示数量和百分比(rich text)
- 中心数字加大加粗,饼块增大圆角
- emphasis 放大效果增强

工牌队列柱状图:
- 最大值柱子高亮深色,其余渐变浅色
- 柱顶显示数值标签
- tooltip 改为卡片式样式
- hover 加深色强调效果,圆角加大

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
lzh
2026-03-01 16:18:44 +08:00
parent 919dcfb420
commit 0c5ac04069

View File

@@ -400,58 +400,73 @@ function getTimeTrendChartOptions(): ECOption {
function getStatusDistributionChartOptions(): ECOption {
const { statusDistribution } = statsData.value;
const STATUS_COLORS: Record<string, string> = {
待处理: '#f5a623',
排队中: '#8b5cf6',
已派单: '#3b82f6',
已到岗: '#06b6d4',
已完成: '#10b981',
已取消: '#f43f5e',
已暂停: '#94a3b8',
待处理: '#faad14',
排队中: '#722ed1',
已派单: '#1677ff',
已到岗: '#13c2c2',
已完成: '#52c41a',
已取消: '#ff4d4f',
已暂停: '#8c8c8c',
};
const total = statusDistribution.reduce((sum, item) => sum + item.value, 0);
return {
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(255, 255, 255, 0.96)',
borderColor: '#e5e7eb',
borderColor: '#f0f0f0',
borderWidth: 1,
textStyle: { color: '#374151', fontSize: 13 },
padding: [8, 12],
textStyle: { color: '#262626', fontSize: 13 },
formatter: (params: any) => {
const marker = `<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:${params.color};margin-right:6px;"></span>`;
return `${marker}${params.name}<br/><span style="font-weight:600">${params.value}</span>个 · ${params.percent}%`;
return `${marker}${params.name}<br/><span style="font-size:16px;font-weight:600">${params.value}</span> <span style="color:#8c8c8c;margin-left:4px">${params.percent}%</span>`;
},
},
legend: {
orient: 'vertical',
right: '2%',
right: '4%',
top: 'center',
itemWidth: 8,
itemHeight: 8,
itemGap: 12,
itemWidth: 10,
itemHeight: 10,
itemGap: 14,
icon: 'circle',
textStyle: { fontSize: 12, color: '#6b7280' },
textStyle: { fontSize: 12, color: '#595959' },
formatter: (name: string) => {
const item = statusDistribution.find((d) => d.name === name);
if (!item) return name;
const pct = total > 0 ? ((item.value / total) * 100).toFixed(1) : '0';
return `${name} {val|${item.value}} {pct|${pct}%}`;
},
textStyle: {
fontSize: 12,
color: '#595959',
rich: {
val: { fontSize: 13, fontWeight: 600, color: '#262626', padding: [0, 4, 0, 8] },
pct: { fontSize: 11, color: '#8c8c8c' },
},
},
},
graphic: [
{
type: 'text',
left: '28%',
top: '40%',
left: '23%',
top: '38%',
style: {
text: `${total}`,
fontSize: 22,
fontWeight: '600',
fill: '#1f2937',
fontSize: 26,
fontWeight: '700',
fill: '#262626',
textAlign: 'center',
},
},
{
type: 'text',
left: '28%',
top: '54%',
left: '23%',
top: '55%',
style: {
text: '近7天工单',
fontSize: 11,
fill: '#9ca3af',
fill: '#bfbfbf',
textAlign: 'center',
},
},
@@ -460,30 +475,31 @@ function getStatusDistributionChartOptions(): ECOption {
{
name: '工单状态分布',
type: 'pie',
radius: ['50%', '72%'],
center: ['30%', '50%'],
radius: ['48%', '70%'],
center: ['25%', '50%'],
avoidLabelOverlap: false,
label: { show: false },
emphasis: {
scale: true,
scaleSize: 6,
scaleSize: 8,
label: {
show: true,
fontSize: 13,
fontSize: 14,
fontWeight: '600',
color: '#374151',
color: '#262626',
formatter: '{b}\n{c}个',
lineHeight: 20,
},
},
itemStyle: {
borderColor: '#fff',
borderWidth: 2,
borderRadius: 4,
borderRadius: 6,
},
data: statusDistribution.map((item) => ({
...item,
itemStyle: {
color: STATUS_COLORS[item.name] || '#d1d5db',
color: STATUS_COLORS[item.name] || '#d9d9d9',
},
})),
},
@@ -682,25 +698,32 @@ function getFunctionTypeRankingChartOptions(): ECOption {
function getBadgeQueueChartOptions(): ECOption {
const queue = statsData.value.badgeQueueStats;
if (!queue || queue.dates.length === 0) return {};
const maxVal = Math.max(...queue.queueData, 1);
return {
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(255, 255, 255, 0.96)',
borderColor: '#f0f0f0',
borderWidth: 1,
padding: [8, 12],
textStyle: { color: '#262626', fontSize: 13 },
formatter: (params: any) => {
const param = params[0];
return `${param.name}<br/>队列数量: ${param.value}`;
return `<span style="color:#8c8c8c">${param.name}</span><br/><span style="font-size:16px;font-weight:600;color:#722ed1">${param.value}</span> <span style="color:#8c8c8c">个排队</span>`;
},
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '10%',
top: '15%',
containLabel: true,
},
xAxis: [{
type: 'category',
data: queue.dates,
axisLine: { lineStyle: { color: '#d9d9d9' } },
axisLine: { lineStyle: { color: '#f0f0f0' } },
axisTick: { show: false },
axisLabel: { color: '#8c8c8c', fontSize: 11 },
}],
yAxis: [{
@@ -708,24 +731,47 @@ function getBadgeQueueChartOptions(): ECOption {
name: '队列数',
nameTextStyle: { color: '#8c8c8c', fontSize: 12 },
axisLine: { show: false },
axisTick: { show: false },
axisLabel: { color: '#8c8c8c' },
splitLine: { lineStyle: { color: '#f0f0f0', type: 'dashed' } },
splitLine: { lineStyle: { color: '#f5f5f5', type: 'dashed' } },
}],
series: [{
name: '队列数量',
type: 'bar',
barWidth: '50%',
data: queue.queueData,
barWidth: '40%',
data: queue.queueData.map((val) => ({
value: val,
itemStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: '#722ed1' },
{ offset: 1, color: '#b37feb' },
{ offset: 0, color: val === maxVal ? '#531dab' : '#722ed1' },
{ offset: 1, color: val === maxVal ? '#9254de' : '#d3adf7' },
],
},
borderRadius: [4, 4, 0, 0],
borderRadius: [6, 6, 0, 0],
},
})),
label: {
show: true,
position: 'top',
fontSize: 11,
fontWeight: '600',
color: '#722ed1',
formatter: (params: any) => params.value > 0 ? params.value : '',
},
emphasis: {
itemStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: '#391085' },
{ offset: 1, color: '#722ed1' },
],
},
},
},
}],
};