环形饼状图

效果图如下
在这里插入图片描述

<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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值