| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- <script setup lang="ts">
- import type { EChartsOption, SeriesOption } from 'echarts'
- import type { LegendInfo } from '@/types/echarts/homeAnalysisChart'
- import { ref, shallowRef, watch } from 'vue'
- import { cloneDeep } from 'lodash'
- import { nextTick } from 'vue'
- import { debounceFunc } from '@/utils/common'
- import echarts from './index'
- import type { YAXisOption } from 'echarts/types/dist/shared'
- interface Props {
- loading: boolean
- legend: Array<LegendInfo>
- data: Array<any>
- xAxisDataField: string // 用于标明x轴数据字段是来自数据中的哪个字段
- }
- const props = defineProps<Props>()
- const MAX_INTERVAL = 5 // y轴最大间隔数
- /**
- * @description: 格式化tooltip
- * @param {*} params 表格数据
- * @return {*}
- */
- const formatterTooltip = (params: Object | Array<any>) => {
- if (Array.isArray(params)) {
- let circle = `<span style="display:inline-block;margin-right:5px;border-radius:50%;
- width:10px;height:10px;left:5px;background-color:`
- let result = `<span style="font-weight:bold;">${params[0].axisValueLabel}</span>`
- params.map((item, index) => {
- let data = `${circle}${props.legend[index].color}"></span>
- <span >
- <span style="display: inline-block; box-sizing: border-box;
- padding-right: 50px;">${props.legend[index].cnName}</span><span>${item['value']}<span>`
- result += `<br/>${data}`
- })
- return result
- } else {
- return `{b0}: {c0}<br />{b1}: {c1}`
- }
- }
- // 颜色
- // const colorList = [
- // '#00e070',
- // '#1495eb',
- // '#993333', // Dark Red
- // '#FFD700', // Bright Yellow
- // '#FF00FF', // Magenta
- // '#FFA500', // Orange
- // '#800080', // Dark Purple
- // '#A52A2A', // Brown
- // '#FF4500', // Orange Red
- // '#FF6347', // Tomato
- // '#B22222', // Firebrick
- // '#FF1493', // Deep Pink
- // ]
- let baseOptions: EChartsOption = {
- grid: {
- left: '3%',
- right: '4%',
- bottom: '3%',
- containLabel: true,
- },
- xAxis: {
- type: 'category',
- axisLabel: {
- showMaxLabel: true,
- },
- axisTick: {
- alignWithLabel: true,
- },
- },
- yAxis: {
- type: 'value',
- minInterval: 1,
- },
- tooltip: {
- trigger: 'axis',
- formatter: (params: Object | Array<any>) => formatterTooltip(params),
- },
- series: [],
- }
- let baseSeries: SeriesOption = {
- symbol: 'circle',
- symbolSize: 5,
- showSymbol: false,
- // itemStyle要在lineStyle前面,这个item会把line的样式覆盖掉,并且必须要有line,不然也会被item替换掉
- itemStyle: {
- color: 'rgb(255,255,255)',
- borderColor: '#1495eb', // symbol边框颜色
- borderWidth: 2, // symbol边框宽度
- },
- lineStyle: {
- color: '#1495eb',
- },
- name: '',
- data: [],
- type: 'line',
- smooth: false,
- }
- // 图表dom对象
- const chartRef = ref<HTMLElement>()
- // 图表实例
- const chartInstance = shallowRef<echarts.ECharts>()
- // 图表尺寸变化的观察者
- let chartSizeOb: ResizeObserver | null = null
- /**
- * @description: 图表和图表实例是否都存在
- * @return {*}
- */
- const isExistChart = (): boolean => {
- if (chartRef.value && chartInstance.value) return true
- return false
- }
- /**
- * @description: 更改图的加载状态
- * @param {*} newState
- * @return {*}
- */
- const changeChartsLoading = (newState: boolean) => {
- if (!isExistChart()) return
- if (newState) chartInstance.value!.showLoading()
- else chartInstance.value!.hideLoading()
- }
- /**
- * @description: 更新图表大小
- * @return {*}
- */
- const chartResize = () => {
- nextTick(() => {
- chartInstance.value?.resize()
- })
- }
- /**
- * @description: 初始化图表的信息
- * @return {*}
- */
- const initChart = () => {
- if (!chartRef.value) return
- chartInstance.value = echarts.init(chartRef.value)
- // 只监听window会导致当侧边栏缩放时,dom大小变化但无法resize
- // 所以需要使用ovserver对整个dom进行监听
- const debounceResize = debounceFunc(chartResize, 300)
- chartSizeOb = new ResizeObserver(debounceResize)
- chartSizeOb.observe(chartRef.value!)
- }
- /**
- * @description: 创建Yaxis
- * @return {*}
- */
- const createYAxis = (): Array<YAXisOption> => {
- let yAxis: Array<YAXisOption> = []
- props.legend.forEach(item => {
- yAxis.push({
- name: item.cnName,
- nameTextStyle: {
- fontWeight: 'bold',
- },
- alignTicks: true, // 开启轴线对齐
- type: 'value',
- minInterval: 1,
- })
- })
- return yAxis
- }
- /**
- * @description: 创建一条series数据
- * @return {*}
- */
- const createSeries = (): SeriesOption[] => {
- let finalSeriesData: SeriesOption[] = []
- // 图例中包含了图例的name和颜色,那么根据这个name,
- // 去data中找到对应字段的值,形成一个series需要的data
- props.legend.forEach((item, index) => {
- let newSeries: SeriesOption = cloneDeep(baseSeries)
- let cData = cloneDeep(props.data)
- // 取出一个legend的里面的name,找到data中每条数据对应字段的值
- let newData = cData.map((data: any) => data[item.value])
- newSeries.name = item.value
- newSeries.data = newData
- newSeries.yAxisIndex = index
- newSeries.lineStyle!.color = item.color // 这里是线的颜色
- newSeries.itemStyle!.color = item.color
- finalSeriesData.push(newSeries)
- })
- return finalSeriesData
- }
- /**
- * @description: 创建xAxis的data
- * @return {*}
- */
- const createXAxis = (): string[] => {
- if (props.data.length > 0) {
- let result: string[] = props.data.map(
- (item: any) => item[props.xAxisDataField],
- )
- return result
- }
- return []
- }
- /**
- * @description: 初始化选项
- * @return {*}
- */
- const initOptions = () => {
- if (!isExistChart()) return
- chartInstance.value!.clear()
- if (props.data.length > 0) {
- let finalSeriesData = createSeries()
- let xAxisData = createXAxis()
- let yAxisData = createYAxis()
- baseOptions.series = finalSeriesData
- baseOptions.yAxis = yAxisData
- ;(baseOptions.xAxis as any).data = xAxisData
- } else {
- baseOptions = {
- title: {
- text: '暂无数据',
- textStyle: {
- fontSize: 16,
- fontWeight: 'normal',
- },
- },
- }
- }
- chartInstance.value!.setOption(baseOptions)
- }
- /**
- * @description: 观察loading状态,如果变为false证明加载完毕,需要重新设置一次数据
- * @return {*}
- */
- watch(
- () => props.loading,
- (newState: boolean) => {
- changeChartsLoading(newState)
- if (newState === false) {
- if (!chartInstance.value) initChart()
- initOptions()
- }
- },
- { deep: true },
- )
- /**
- * @description: 当图例的信息变化的时候也需要重新设置选项
- * @return {*}
- */
- watch(
- () => props.legend,
- () => {
- initOptions()
- },
- {
- deep: true,
- },
- )
- </script>
- <template>
- <div class="chart" ref="chartRef" style="width: 100%; height: 365px"></div>
- </template>
- <style scoped></style>
|