效果图如下
<script setup lang="ts">
import * as echarts from "echarts";
import { getDisposalStatusApi, DisposalStatusTypeNew } from "@/api/safetySupervision/index";
import { storeToRefs } from "pinia";
import { useMapStoreWithOut } from "@/store/modules/map";
const mapStore = useMapStoreWithOut();
const { currentMapCity } = storeToRefs(mapStore);
defineOptions({ name: "DisposalStatus" });
let props = defineProps({
title: {
type: String,
default: "",
},
areaParams: {
type: Object,
default: () => {},
},
});
let elRef = ref<HTMLElement>(); // 实例对象
let myCharts: Nullable<echarts.ECharts> = null; // 初始化定义
const charData = shallowRef([] as DisposalStatusTypeNew[]);
const DisposalStatus = async () => {
/* const { statusCounts } = await getDisposalStatusApi({
areaId: currentMapCity.value?.code?.toString() || "",
}); */
const {statusCounts} = await getDisposalStatusApi({ area: props.areaParams.area });
charData.value = statusCounts;
};
interface RingTheme {
center: string;
color: string;
shadowColor: string;
left: string;
}
// 定义环形图主题
const ringThemes: RingTheme[] = [
{ center: "9.5%", color: "#468EFD", shadowColor: "rgba(1,162,255,0.72)", left: "16%" },
{ center: "44.5%", color: "#1ED49F", shadowColor: "rgba(30,212,159,0.72)", left: "50%" },
{ center: "79.5%", color: "#EAD521", shadowColor: "rgba(234,213,33,0.72)", left: "85%" },
];
const createRingConfig = (percentValue: number, position: string, theme: RingTheme) => {
// 生成颜色的辅助函数
const getAlphaColor = (color: string, alpha: number) => {
return color.replace(/rgba?\(([^)]+)\)/, `rgba($1, ${alpha})`).replace(/#([0-9a-f]{3,8})/i, (_, hex) => {
let r, g, b;
if (hex.length === 3) {
r = parseInt(hex[0] + hex[0], 16);
g = parseInt(hex[1] + hex[1], 16);
b = parseInt(hex[2] + hex[2], 16);
} else {
r = parseInt(hex.slice(0, 2), 16);
g = parseInt(hex.slice(2, 4), 16);
b = parseInt(hex.slice(4, 6), 16);
}
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
});
};
return [
{
name: "外环",
type: "pie",
radius: ["55%", "60%"],
center: [position, "55%"],
startAngle: 90,
silent: true,
z: 0,
label: { show: false },
labelLine: { show: false },
hoverAnimation: false,
data: [
{
value: 100,
itemStyle: {
color: getAlphaColor(theme.color, 0.19), // 背景颜色
// 可选:添加边框
borderWidth: 1,
borderShadow: theme.shadowColor,
},
},
],
},
{
name: "内背景环",
type: "pie",
radius: "40%",
center: [position, "55%"],
startAngle: 90,
silent: true,
z: 0,
label: { show: false },
labelLine: { show: false },
hoverAnimation: false,
data: [
{
value: 100,
itemStyle: {
color: new echarts.graphic.RadialGradient(0.5, 0.5, 0.5, [
{ offset: 0, color: getAlphaColor(theme.color, 0.05) },
{ offset: 0.3, color: getAlphaColor(theme.color, 0.05) },
{ offset: 0.6, color: getAlphaColor(theme.color, 0.05) },
{ offset: 0.8, color: getAlphaColor(theme.color, 0.1) },
{ offset: 1, color: getAlphaColor(theme.color, 0.3) },
]),
// 可选:添加边框
borderColor: getAlphaColor(theme.color, 0.3),
borderWidth: 1,
borderShadow: theme.shadowColor,
},
},
],
},
{
name: "阴影",
type: "pie",
radius: ["0%", "65%"],
center: [position, "55%"],
avoidLabelOverlap: false,
startAngle: 90, // 修改为180度,从正中间开始
hoverAnimation: false,
legendHoverLink: false,
clockwise: true, // 顺时针排布
itemStyle: {
// 保持现有样式
normal: {
borderColor: "transparent",
borderWidth: "20",
},
emphasis: {
borderColor: "transparent",
borderWidth: "20",
},
},
z: 1,
label: {
normal: {
show: false,
position: "center",
},
},
labelLine: {
normal: {
show: false,
},
},
data: [
{
// 从正中间到角度计算位置的部分(显示阴影)
value: percentValue,
name: "已完成",
itemStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: getAlphaColor(theme.color, 0.005) },
{ offset: 0.3, color: getAlphaColor(theme.color, 0.05) },
{ offset: 0.6, color: getAlphaColor(theme.color, 0.15) },
{ offset: 0.8, color: getAlphaColor(theme.color, 0.35) },
{ offset: 1, color: getAlphaColor(theme.color, 0.4) },
]),
},
},
},
{
// 剩余未完成的部分(透明)
value: 100 - percentValue,
name: "未完成",
itemStyle: {
color: "transparent",
},
},
],
},
// 添加到现有series数组中
{
name: "进度点",
type: "pie",
radius: ["62%", "64%"], // 略大于外环
center: [position, "55%"],
startAngle: 90,
clockwise: true,
silent: true,
z: 2, // 确保在最顶层
hoverAnimation: false,
label: { show: false },
labelLine: { show: false },
data: [
{
// 从起点到进度点的透明部分
value: percentValue * 3.6, // 百分比转换为角度比例 (percent * 240/100)
name: "进度",
itemStyle: { color: "transparent" },
},
{
// 进度点本身
value: 1,
name: "指示点",
itemStyle: {
borderColor: "#ffffff",
borderWidth: 2,
shadowBlur: 15,
shadowColor: "#10E0FF",
// 可选:添加径向渐变
color: new echarts.graphic.RadialGradient(0.5, 0.5, 0.5, [
{ offset: 0, color: "#ffffff" },
{ offset: 0.3, color: "#73E6FF" },
{ offset: 1, color: "#10E0FF" },
]),
},
},
{
// 从进度点到终点的透明部分
value: 360 - percentValue * 3.6 - 1,
name: "剩余",
itemStyle: { color: "transparent" },
},
],
},
// 中心文字 - 显示百分比
{
name: "中心文字",
type: "gauge",
z: 3,
radius: "40%",
center: [position, "55%"],
splitNumber: 0,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: { show: false },
splitLine: { show: false },
pointer: { show: false },
detail: {
formatter: "{value}%",
offsetCenter: [0, 0],
fontSize: ".225rem",
fontWeight: "bold",
color: theme.color,
},
data: [{ value: percentValue }],
},
];
};
watchEffect(() => {
const ringTitles = charData.value?.map((item) => {
return {
type: item.statusCode,
name: item.statusName,
value: item.count,
};
});
// 图表展示
const series = [
...createRingConfig(ringTitles[0]?.value || 0, "15%", ringThemes[0]),
...createRingConfig(ringTitles[1]?.value || 0, "50%", ringThemes[1]),
...createRingConfig(ringTitles[2]?.value || 0, "85%", ringThemes[2]),
];
// 标题和箭头
const generateTitles = (): Array<{
type: string;
left: string;
top: string;
style: {
text: string;
textAlign: string;
fill: string;
fontSize: string;
fontWeight: string;
marginBottom?: string;
};
z: number;
}> => {
const elements: Array<{
type: string;
left: string;
top: string;
style: {
text: string;
textAlign: string;
fill: string;
fontSize: string;
fontWeight: string;
marginBottom?: string;
};
z: number;
}> = [];
// 标题
ringThemes.forEach((theme, index) => {
// 标题文本
elements.push({
type: "text",
left: theme.center,
top: "5%",
style: {
text: ringTitles[index]?.name || "",
textAlign: "center",
/* fill: "#A5DBFE", */
fill: theme.color,
fontSize: ".175rem",
fontWeight: "bold",
},
z: 100,
});
// 添加箭头
/* elements.push({
// type: 'image',
// left: theme.left,
// top: '12%',
// style: {
// image: time, // 图片路径
// },
// width: '16px',
// height: '12px',
type: "text",
left: theme.left,
top: "18%",
style: {
text: "▼", // 向下箭头符号
textAlign: "center",
fill: theme.shadowColor, // 使用主题颜色
fontSize: ".175rem",
fontWeight: "normal",
},
z: 100,
}); */
});
return elements;
};
let option = {
backgroundColor: "transparent",
graphic: generateTitles(),
series,
};
myCharts?.setOption(option);
});
const initChart = () => {
myCharts = echarts.init(unref(elRef) as HTMLElement);
DisposalStatus();
window.addEventListener("resize", resizeChart);
};
const resizeChart = () => {
// 监听窗口大小变化,调整图表大小
myCharts?.resize();
};
// 监听当前城市
watch(
() => props.areaParams.area,
(val) => {
if (val) {
DisposalStatus();
}
},
{ deep: true },
);
onMounted(() => {
initChart();
});
onActivated(() => {
initChart();
});
onBeforeUnmount(() => {
window.removeEventListener("resize", resizeChart);
// 销毁图表实例
myCharts?.dispose();
});
onUnmounted(() => {
window.removeEventListener("resize", resizeChart);
myCharts?.dispose();
});
</script>
<template>
<div class="title_wrap">
<div class="titleStyle">
{{ props.title }}
</div>
<div class="echartsBox" ref="elRef"></div>
</div>
</template>
<style lang="scss" scoped>
.title_wrap {
width: 100%;
height: 100%;
flex-direction: column;
.unit {
font-family: PingFangSC, "PingFang SC";
font-size: px(14);
font-weight: 400;
color: #d8f0ffcc;
}
.titleStyle {
width: 100%;
height: px(30);
padding-left: px(8);
margin-bottom: px(8);
font-family: SourceHanSansCN;
font-size: px(16);
font-weight: bold;
line-height: px(30);
color: #d8f0ff;
background: linear-gradient(90deg, #009eff99 0%, #009eff00 100%);
border-radius: px(4);
}
.echartsBox {
width: 100%;
height: calc(100% - px(30));
}
}
</style>