You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

220 lines
5.5 KiB
Vue

<template>
<div
:style="{width:props.dimensions.width*props.ratioWidth+'px',height:props.dimensions.height*props.ratioHeight+'px'}">
<NodeResizer @resizeEnd="(e) => $emit('resize', e)" color="#fff" v-if="!props.isView && !props.isHideHandle && props.selected" @resize="resize" />
<div class="custom-node"
:style="{width:props.dimensions.width*props.ratioWidth+'px',height:props.dimensions.height*props.ratioHeight+'px',pointerEvents:props.isView?'auto': 'none'}">
<div style="width: 100%;height: 100%" ref="chartRef" />
</div>
<Handle v-if="!props.isView" :id="`${props.id}.-t`" type="target" :position="Position.Left" />
</div>
</template>
<script setup>
import { defineEmits, defineProps, ref } from 'vue';
import { NodeResizer } from '@vue-flow/node-resizer';
import { Handle, Position } from '@vue-flow/core';
import * as echarts from 'echarts';
const props = defineProps({
ratioWidth: {
type: Number,
required: false,
default: 1
},
ratioHeight: {
type: Number,
required: false,
default: 1
},
isView: {
type: Boolean,
required: false
},
inputData: {
type: Object,
required: false
},
id: {
type: String,
required: true
},
isHideHandle: {
type: Boolean,
required: false
},
selected: {
type: Boolean,
required: false
},
data: {
type: Object,
required: true
},
dimensions: {
type: Object,
required: true
}
});
const chartRef = ref();
let chart = null;
const colorList = ['#9E87FF', '#73DDFF', '#fe9a8b', '#F56948', '#9E87FF'];
const getOption = () => {
let value = props.inputData?.value || 0;
const width = props.dimensions?.width || 300;
const baseFontSize = width * 0.08;
const tickFontSize = baseFontSize * 0.6;
const tickDistance = width * 0.04;
const progressBarWidth = width * 0.02;
const tickLineWidth = width * 0.003;
const splitLineWidth = width * 0.006;
const chartOption = {
series: [
// 1. 指针 + 数值
{
type: 'gauge',
startAngle: 225,
endAngle: -45,
radius: '60%',
pointer: {
show: true,
length: '60%',
width: width * 0.006, // ⬅️ 指针宽度自适应
itemStyle: {
color: '#00C2FF'
}
},
axisLine: { show: false },
progress: { show: false },
axisTick: { show: false },
splitLine: { show: false },
axisLabel: { show: false },
detail: {
valueAnimation: true,
fontSize: baseFontSize,
offsetCenter: [0, '30%'],
// formatter: '{value}%',
color: '#AEEBFF'
},
data: [{ value: value }]
},
// 2. 刻度线层
{
type: 'gauge',
startAngle: 225,
endAngle: -45,
radius: '70%',
pointer: { show: false },
axisLine: { show: false },
axisTick: {
distance: -10,
length: 6,
lineStyle: {
color: '#66D9FF',
width: tickLineWidth // ⬅️ 短刻度线宽度自适应
}
},
splitLine: {
distance: -10,
length: 10,
lineStyle: {
color: '#66D9FF',
width: splitLineWidth // ⬅️ 长刻度线宽度自适应
}
},
axisLabel: { show: false },
detail: { show: false },
data: [{ value: 72 }]
},
// 3. 刻度数字层
{
type: 'gauge',
startAngle: 225,
endAngle: -45,
radius: '80%',
pointer: { show: false },
axisLine: { show: false },
axisTick: { show: false },
splitLine: { show: false },
axisLabel: {
distance: tickDistance,
color: '#B8EFFF',
fontSize: tickFontSize
},
detail: { show: false },
data: [{ value: 72 }]
},
// 4. 外圈进度条
{
type: 'gauge',
startAngle: 225,
endAngle: -45,
radius: '90%',
pointer: { show: false },
axisLine: {
lineStyle: {
width: progressBarWidth, // ⬅️ 进度条宽度自适应
color: [
[0.72, '#00D4FF'],
[1, '#004B6B']
]
}
},
progress: { show: false },
axisTick: { show: false },
splitLine: { show: false },
axisLabel: { show: false },
detail: { show: false },
data: [{ value: value }]
}
],
title: {
text: props.data?.options.title,
left: 'center',
bottom: 10,
textStyle: {
fontSize: baseFontSize * 0.5,
color: '#AEEBFF'
}
}
};
return {
...chartOption
};
};
onMounted(() => {
chart = echarts.init(chartRef.value, 'macarons', {
renderer: 'svg'
});
chart.setOption(getOption(), true);
});
watch(() => [JSON.parse(JSON.stringify(props.inputData)), JSON.parse(JSON.stringify(props.data.options))], (obj1, obj2) => {
if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {
chart && chart.setOption(getOption(), true);
}
}, { deep: true, immediate: true });
watch(() => JSON.parse(JSON.stringify(props.dimensions)), (obj1, obj2) => {
if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {
chart?.resize();
chart && chart.setOption(getOption(), true);
}
}, { deep: true, immediate: true });
const emit = defineEmits(['resize']);
const resize = (e) => {
chart.resize();
emit('resize', e, props.id);
};
</script>
<style scoped>
.custom-node {
width: 100%;
height: 100%;
}
</style>