Newer
Older
CorrOLFront / src / components / Echart / PieChart.vue
tanyue on 5 Mar 2024 5 KB 20240305 初始提交
<script lang="ts" setup name="PieChart">
import type { ECharts } from 'echarts'
import { init, number } from 'echarts'
import type { Ref } from 'vue'
import type { ECBasicOption } from 'echarts/types/dist/shared'
import type { pieDataI, pieOption, pieSeriesOption } from './echart-interface'
import tdTheme from './theme.json' // 引入默认主题
const props = defineProps({
  /**
   * id
   */
  id: {
    type: String,
    default: 'chart',
  },
  /**
   * 加载中
   */
  loading: {
    type: Boolean,
    default: false,
  },
  /**
   * 标题
   */
  title: {
    type: String,
    default: '',
  },
  /**
   * 图例是否显示
   */
  legend: {
    type: Object,
    default: () => {
      return {
        show: true,
        orient: 'vertical',
        right: '20%',
        top: 'center',
        icon: 'circle',
        itemWidth: 12,
        itemHeight: 12,
        itemStyle: {
          fontSize: 18,
        },
      }
    },
  },
  /**
   * 文本标签是否显示
   */
  labelShow: {
    type: Boolean,
    default: true,
  },
  /**
   * 图表宽
   */
  width: {
    type: String,
    default: '100%',
  },
  /**
   * 图表高
   */
  height: {
    type: String,
    default: '100%',
  },
  /**
   * 饼图的半径[内半径,外半径]
   */
  radius: {
    type: Array,
    default: () => {
      return ['30%', '45%']
    },
  },
  /**
   * 距离容器右侧距离,默认0,为legend在右侧时做准备
   */
  right: {
    type: [Number, String],
    default: 0,
  },
  /**
   * 是否要展示成玫瑰图,或玫瑰图类型:radius|area
   */
  roseType: {
    type: [String, Boolean],
    default: false,
  },
  /**
   * 显示值类型:percentage百分比|value数值
   */
  valueType: {
    type: String,
    default: 'percentage',
  },
  /**
   * 数据,格式为 [{name:'xxx',value:0},...]
   */
  data: {
    type: Array<pieDataI>,
    default: () => { return [] },
  },
  /**
   * 颜色
   */
  colors: {
    type: Array,
    default: () => { return ['#3d7eff', '#caddff'] },
  },
  /**
   * 网格配置
   */
  grid: {
    type: Object,
    default: () => {
      return {
        top: '10%',
        left: '5%',
        right: '50%',
        bottom: 20,
        containLabel: true, // 是否包含坐标轴的刻度标签
      }
    },
  },
  /**
   * 标签文字颜色
   */
  fontColor: {
    type: String,
    default: '#000000',
  },
  /**
   * 标签格式化
   */
  labelFormatter: {
    type: String,
    default: '',
  },
  /**
   * 标签位置, outside饼图扇区外侧, inside饼图扇区内部, center饼图中心位置
   */
  labelPosition: {
    type: String,
    default: 'center',
  },
  /**
   * 悬停格式化
   */
  hoverFormatter: {
    type: String,
    default: '{b}<br/>数量:{c}<br/>占比:{d}%',
  },

})

// 图表对象
let chart: ECharts
const chartRef: Ref<HTMLElement | null> = ref(null)

// 监听数据变化
watch(
  [() => props.data], ([newNums], [oldNums]) => {
    refreshChart()
  },
  {
    immediate: true,
    deep: true,
  },
)
// 构建option
function buildOption() {
  const option: pieOption = {
    grid: props.grid, // 网格
    legend: props.legend, // 图例
    tooltip: {
      show: true,
      trigger: 'item',
      formatter: props.hoverFormatter,
      textStyle: {
        fontSize: 15,
      },
    }, // 提示框
    series: [] as pieSeriesOption[],
  }
  // 标题
  if (props.title) {
    option.title = {
      show: true,
      text: props.title,
    }
  }
  // 标题
  if (props.colors) {
    option.color = props.colors as string[]
  }
  // 数据
  if (props.data) {
    const series: pieSeriesOption = {
      animationType: 'expansion', // 沿圆弧展开效果 expansion|scale
      type: 'pie',
      avoidLabelOverlap: true, // 是否启用防止标签重叠策略
      roseType: props.roseType, // 玫瑰图类型
      radius: props.radius as string[], // 饼图的半径[内半径,外半径]
      center: ['50%', '50%'], // 圆心坐标[横坐标,纵坐标]
      startAngle: 140,
      right: props.right,
      label: {
        show: props.labelShow,
        position: 'center',
        formatter: props.labelFormatter ? props.labelFormatter : (props.valueType == 'percentage' ? '{style1|{b}}\n{style2|{d}%}' : '{style1|{b}}\n{style2|{c}}'),
        fontSize: 20,
        fontWeight: 'bold',
        rich: {
          style1: {
            color: props.fontColor,
            fontSize: 16,
          },
          style2: {
            color: props.fontColor,
            fontWeight: 'bolder',
            fontSize: 19,
          },
        },
        emphasis: {
          show: true,
          textStyle: {
            fontSize: 22,
            fontWeight: 'bold',
          },
        },
      },
      data: props.data,
    }
    option.series = [series]
  }
  return option
}
// 初始化图表
function initChart() {
  chart = init(chartRef.value as HTMLElement, tdTheme)
  chart.setOption({})
}

// 刷新图表
function refreshChart() {
  if (chart) {
    const option = buildOption()
    chart.setOption(option as unknown as ECBasicOption, true)
    chart.resize()
  }
}

window.addEventListener('resize', () => {
  chart.resize()
})

onMounted(() => {
  initChart()
})
</script>

<template>
  <div :id="id" ref="chartRef" v-loading="loading" class="chart" :style="{ height, width }" />
</template>

<style lang="scss" scoped>
.chart {
  width: 100%;
  height: 100%;
}
</style>