You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
377 lines
12 KiB
377 lines
12 KiB
import type { EChartsOption } from 'echarts';
|
|
|
|
export function getSmartMaxAndInterval(data1: number[], data2: number[], maxSplits = 5) {
|
|
const maxVal = Math.max(...data1, ...data2, 0);
|
|
if (maxVal === 0) return { max: maxSplits, interval: 1 };
|
|
const roughInterval = maxVal / maxSplits;
|
|
const magnitude = Math.pow(10, Math.floor(Math.log10(roughInterval)));
|
|
const residual = roughInterval / magnitude;
|
|
let niceInterval: number;
|
|
if (residual <= 1) niceInterval = 1;
|
|
else if (residual <= 2) niceInterval = 2;
|
|
else if (residual <= 5) niceInterval = 5;
|
|
else niceInterval = 10;
|
|
const interval = niceInterval * magnitude;
|
|
return { max: interval * maxSplits, interval };
|
|
}
|
|
|
|
const ADMIN_PIE_COLORS = ['#2d6fcf', '#4fa0a8', '#5e9acc', '#b29f60', '#af5172', '#8a61d2', '#6f2c9b', '#9d7173', '#887456', '#9eac71'];
|
|
const NON_ADMIN_PIE_COLORS = ['#7bb06c', '#489d61', '#4e97a6', '#3953d4', '#4e3ec5', '#6f2c9b', '#9d7173', '#887456', '#9eac71', '#8e5aac'];
|
|
|
|
export function buildAdminPieOption(data: { value: number; name: string }[]): EChartsOption {
|
|
const coloredData = data.map((item, index) => ({
|
|
...item,
|
|
itemStyle: { color: ADMIN_PIE_COLORS[index % ADMIN_PIE_COLORS.length] }
|
|
}));
|
|
return {
|
|
tooltip: {
|
|
trigger: 'item',
|
|
formatter: '{b}: <br/> {c} ({d}%)',
|
|
backgroundColor: 'rgba(0, 0, 0, .5)',
|
|
textStyle: { fontSize: 12, color: 'rgba(255, 255, 255, .8)' }
|
|
},
|
|
legend: {
|
|
bottom: '0',
|
|
show: true,
|
|
textStyle: { color: '#809ABE', fontSize: 12 },
|
|
icon: 'circle',
|
|
itemWidth: 6.5,
|
|
itemHeight: 6.5,
|
|
itemGap: 15
|
|
},
|
|
color: ADMIN_PIE_COLORS,
|
|
series: [
|
|
{
|
|
name: '行政执法类别数量及占比',
|
|
type: 'pie',
|
|
radius: [52, 72],
|
|
center: ['50%', '50%'],
|
|
itemStyle: { borderRadius: 1 },
|
|
label: {
|
|
show: true,
|
|
formatter: '{b|{b}} ' + '{b|:}' + '\n ' + '{a|{c}}' + '{d| ({d}%)}',
|
|
rich: {
|
|
a: { color: '#3aaefb', fontSize: 12, lineHeight: 16 },
|
|
b: { color: 'rgba(255, 255, 255, .6)', fontSize: 12, lineHeight: 16 },
|
|
d: { color: '#3aaefb', fontSize: 12 }
|
|
}
|
|
},
|
|
labelLine: { show: true, length: 20, length2: 5, smooth: 0 },
|
|
data: coloredData
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
export function buildNonAdminPieOption(data: { value: number; name: string }[]): EChartsOption {
|
|
const coloredData = data.map((item, index) => ({
|
|
...item,
|
|
itemStyle: { color: NON_ADMIN_PIE_COLORS[index % NON_ADMIN_PIE_COLORS.length] }
|
|
}));
|
|
return {
|
|
tooltip: {
|
|
trigger: 'item',
|
|
formatter: '{b}: <br/> {c} ({d}%)',
|
|
backgroundColor: 'rgba(0, 0, 0, .5)',
|
|
textStyle: { fontSize: 12, color: 'rgba(255, 255, 255, .8)' }
|
|
},
|
|
legend: {
|
|
bottom: '-1%',
|
|
show: true,
|
|
textStyle: { color: '#809ABE', fontSize: 12 },
|
|
icon: 'circle',
|
|
itemWidth: 6.5,
|
|
itemHeight: 6.5,
|
|
itemGap: 15
|
|
},
|
|
color: NON_ADMIN_PIE_COLORS,
|
|
series: [
|
|
{
|
|
name: '非行政执法类别数量及占比',
|
|
type: 'pie',
|
|
radius: [52, 72],
|
|
center: ['50%', '40%'],
|
|
itemStyle: { borderRadius: 1 },
|
|
label: {
|
|
show: true,
|
|
formatter: '{b|{b}} ' + '{b|:}' + '\n ' + '{a|{c}}' + '{d| ({d}%)}',
|
|
rich: {
|
|
a: { color: '#3aaefb', fontSize: 12, lineHeight: 16 },
|
|
b: { color: 'rgba(255, 255, 255, .6)', fontSize: 12, lineHeight: 16 },
|
|
d: { color: '#3aaefb', fontSize: 12 }
|
|
}
|
|
},
|
|
labelLine: { show: true, length: 20, length2: 5, smooth: 0 ,maxSurfaceAngle: 80 },
|
|
data: coloredData
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
export function buildTrendLineOption(
|
|
times: string[],
|
|
ins: number[],
|
|
ent: number[],
|
|
insName: string,
|
|
entName: string,
|
|
leftAxisName: string,
|
|
rightAxisName: string = '被执法企业数量(家)'
|
|
): EChartsOption {
|
|
const { max, interval } = getSmartMaxAndInterval(ins, ent, 5);
|
|
return {
|
|
tooltip: {
|
|
trigger: 'axis',
|
|
backgroundColor: 'rgba(0, 0, 0, .5)',
|
|
borderColor: 'rgba(46, 130, 253, .5)',
|
|
axisPointer: { lineStyle: { color: 'rgba(255, 255, 255, .8)' } },
|
|
textStyle: { fontSize: 12, color: 'rgba(255, 255, 255, .8)' }
|
|
},
|
|
legend: {
|
|
data: [insName, entName],
|
|
right: 0,
|
|
top: 0,
|
|
itemGap: 15,
|
|
textStyle: { fontSize: 12, color: 'rgba(255,255,255,.5)' }
|
|
},
|
|
color: ['rgba(69,139,252,1)', '#f59036'],
|
|
grid: { left: '3%', right: '3%', bottom: '0', containLabel: true },
|
|
xAxis: [
|
|
{
|
|
type: 'category',
|
|
boundaryGap: false,
|
|
axisLabel: { show: true, color: 'rgba(255,255,255,.5)' },
|
|
axisLine: { lineStyle: { color: 'rgba(255,255,255,.1)' } },
|
|
data: times
|
|
}
|
|
],
|
|
yAxis: [
|
|
{
|
|
type: 'value',
|
|
name: leftAxisName,
|
|
position: 'left',
|
|
axisLine: { lineStyle: { color: 'rgba(69,139,252,.7)' } },
|
|
axisLabel: { formatter: '{value}', color: 'rgba(69,139,252,.7)' },
|
|
splitLine: { lineStyle: { color: 'rgba(255,255,255,.1)' } },
|
|
max,
|
|
interval
|
|
},
|
|
{
|
|
type: 'value',
|
|
name: rightAxisName,
|
|
position: 'right',
|
|
axisLine: { lineStyle: { color: 'rgba(245,144,54,.65)' } },
|
|
axisLabel: { formatter: '{value}', color: 'rgba(245,144,54,.65)' },
|
|
splitLine: { lineStyle: { color: 'rgba(255,255,255,.1)' } },
|
|
max,
|
|
interval
|
|
}
|
|
],
|
|
series: [
|
|
{
|
|
name: insName,
|
|
type: 'line',
|
|
lineStyle: { width: 2, type: 'solid', color: 'rgba(69,139,252,1)' },
|
|
itemStyle: { color: 'rgba(69,139,252,1)' },
|
|
symbol: 'circle',
|
|
symbolSize: 6,
|
|
label: { show: false },
|
|
data: ins
|
|
},
|
|
{
|
|
name: entName,
|
|
type: 'line',
|
|
yAxisIndex: 1,
|
|
lineStyle: { width: 2, type: 'solid', color: 'rgba(245,144,54,.7)' },
|
|
itemStyle: { color: 'rgba(245,144,54,.7)' },
|
|
symbol: 'circle',
|
|
symbolSize: 6,
|
|
label: { show: false },
|
|
data: ent
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
export function buildRankBarOption(names: string[], values: number[], seriesName: string): EChartsOption {
|
|
return {
|
|
tooltip: { trigger: 'axis' },
|
|
grid: { left: '5%', right: '0', bottom: '5%', top: '15%', containLabel: true },
|
|
xAxis: [
|
|
{
|
|
type: 'category',
|
|
data: names,
|
|
axisLine: { show: false },
|
|
axisTick: { show: false },
|
|
axisLabel: {
|
|
interval: 0,
|
|
rotate: 30,
|
|
color: '#999',
|
|
fontSize: 12,
|
|
formatter: (value: string) => (value.length > 6 ? value.slice(0, 6) + '...' : value)
|
|
}
|
|
}
|
|
],
|
|
yAxis: [
|
|
{
|
|
type: 'value',
|
|
splitLine: { show: true, lineStyle: { color: 'transparent' } },
|
|
axisLine: { show: false },
|
|
axisTick: { show: false },
|
|
axisLabel: { color: '#79a3ca', fontSize: 12 }
|
|
}
|
|
],
|
|
series: [
|
|
{
|
|
name: seriesName,
|
|
type: 'bar',
|
|
barWidth: '25%',
|
|
showBackground: true,
|
|
backgroundStyle: { borderRadius: [5, 5, 0, 0] },
|
|
itemStyle: { borderRadius: [5, 5, 0, 0], color: '#7ca9fb' },
|
|
label: { show: true, position: 'outside' },
|
|
data: values
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
export function buildAutoRatePieOption(data: { value: number; name: string }[]): EChartsOption {
|
|
return {
|
|
tooltip: {
|
|
trigger: 'item',
|
|
formatter: '{b}: {c} ({d}%)'
|
|
},
|
|
series: [
|
|
{
|
|
name: '委托执法数量及占比',
|
|
type: 'pie',
|
|
radius: ['42%', '60%'],
|
|
padAngle: 3,
|
|
itemStyle: { borderColor: '#fff', borderWidth: 1 },
|
|
label: {
|
|
show: true,
|
|
formatter: '{b|{b}}: {a|{c}}{d| ({d}%)}',
|
|
rich: {
|
|
a: { color: 'rgba(0, 0, 0, .6)', fontSize: 12 },
|
|
b: { color: 'rgba(0, 0, 0, .85)', fontSize: 12 },
|
|
d: { color: 'rgba(0, 0, 0, .6)', fontSize: 12 }
|
|
}
|
|
},
|
|
labelLine: { show: true, length: 30, length2: 50 },
|
|
color: ['#5292fc', '#91cc75', '#fac858', '#ef6667', '#73c0df', '#3ba272', '#fc8352', '#9b60b4', '#9ebafc', '#0047eb'],
|
|
data
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
const RING_COLORS = ['#5292fc', '#91cc75', '#fac858', '#ef6667', '#73c0df', '#3ba272', '#fc8352', '#9b60b4', '#9ebafc', '#0047eb'];
|
|
|
|
export function buildRingPieOption(name: string, data: { value: number; name: string }[]): EChartsOption {
|
|
return {
|
|
tooltip: {
|
|
trigger: 'item',
|
|
formatter: '{b}: {c} ({d}%)'
|
|
},
|
|
series: [
|
|
{
|
|
name,
|
|
type: 'pie',
|
|
radius: ['42%', '60%'],
|
|
padAngle: 3,
|
|
itemStyle: { borderColor: '#fff', borderWidth: 1 },
|
|
labelLine: { show: true, length: 30, length2: 10 },
|
|
color: RING_COLORS,
|
|
data
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
function toPercent(value: number, total: number) {
|
|
if (!total) return '0%';
|
|
return `${((value / total) * 100).toFixed(2)}%`;
|
|
}
|
|
|
|
export function buildHorizontalBarOption(
|
|
categories: string[],
|
|
values: number[],
|
|
color: string,
|
|
bgColor: string
|
|
): EChartsOption {
|
|
const total = values.reduce((s, v) => s + v, 0);
|
|
const percentData = values.map((v) => toPercent(v, total));
|
|
return {
|
|
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
|
grid: { left: '3%', right: '15%', bottom: '3%', containLabel: true },
|
|
xAxis: { type: 'value', show: false, splitLine: { show: false } },
|
|
yAxis: {
|
|
type: 'category',
|
|
data: categories,
|
|
axisLine: { show: false },
|
|
axisTick: { show: false },
|
|
splitLine: { show: false }
|
|
},
|
|
series: [
|
|
{
|
|
name: '数量',
|
|
type: 'bar',
|
|
data: values.map((value, index) => ({ value, percent: percentData[index] })),
|
|
barWidth: 12,
|
|
showBackground: true,
|
|
backgroundStyle: { borderRadius: 10, color: bgColor },
|
|
label: {
|
|
show: true,
|
|
position: 'right',
|
|
color: '#666',
|
|
formatter: '{c} ({@percent})'
|
|
},
|
|
itemStyle: { borderRadius: 8, color }
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
export function buildDistrictBarOption(
|
|
districts: string[],
|
|
singleLaw: number[],
|
|
unionLaw: number[],
|
|
enfEnt: number[],
|
|
entEeduction: number[],
|
|
compAddress: number[],
|
|
entAddress: number[]
|
|
): EChartsOption {
|
|
return {
|
|
tooltip: { trigger: 'axis' },
|
|
legend: {
|
|
top: 0,
|
|
right: 0,
|
|
itemGap: 20,
|
|
itemWidth: 10,
|
|
itemHeight: 10,
|
|
data: ['单独执法', '联合执法', '被执法企业', '入企压减率', '涉企投诉', '企业数量']
|
|
},
|
|
grid: { left: '0', right: '0', bottom: '0', top: '20%', containLabel: true },
|
|
xAxis: [{ type: 'category', data: districts, axisLine: { show: false }, axisTick: { show: false } }],
|
|
yAxis: [{ type: 'value', splitLine: { lineStyle: { color: 'rgba(255,255,255,.1)' } } }],
|
|
series: [
|
|
{ name: '单独执法', type: 'bar', stack: 'total', data: singleLaw, color: '#5292fc' },
|
|
{ name: '联合执法', type: 'bar', stack: 'total', data: unionLaw, color: '#91cc75' },
|
|
{ name: '被执法企业', type: 'bar', data: enfEnt, color: '#fac858' },
|
|
{ name: '入企压减率', type: 'bar', data: entEeduction, color: '#ef6667' },
|
|
{ name: '涉企投诉', type: 'bar', data: compAddress, color: '#73c0df' },
|
|
{ name: '企业数量', type: 'bar', data: entAddress, color: '#3ba272' }
|
|
]
|
|
};
|
|
}
|
|
|
|
export function mapObjectArrayToPie(data: Record<string, number>[]) {
|
|
return data.map((obj) => {
|
|
const [name, value] = Object.entries(obj)[0];
|
|
return { name, value: Number(value) };
|
|
});
|
|
}
|
|
|
|
export function formatPercent(value: string | number, digits = 2) {
|
|
return `${parseFloat(String(value)).toFixed(digits)}%`;
|
|
}
|
|
|