| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- <script setup lang="ts">
- import * as echarts from 'echarts'
- import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
- const props = withDefaults(
- defineProps<{
- pressure: number[]
- volume: number[]
- angle: number[]
- height?: number
- }>(),
- { height: 170 },
- )
- // 曲柄角度 4 阶段(配色为根目录 frontend/src/components/WaveChart.vue 的加亮版)。
- const PHASES = [
- { start: 0, end: 120, color: '#ab47bc', name: '进气' },
- { start: 120, end: 195, color: '#ef5350', name: '膨胀' },
- { start: 195, end: 270, color: '#42a5f5', name: '压缩' },
- { start: 270, end: 360, color: '#66bb6a', name: '排气' },
- ]
- const el = ref<HTMLElement | null>(null)
- let chart: echarts.ECharts | null = null
- let observer: ResizeObserver | null = null
- function buildOption(): Record<string, unknown> {
- // 按曲柄角顺时针排序,保证同一阶段为连续折线(避免跨圈连成一条直线)。
- const order = props.volume
- .map((_, index) => index)
- .sort((a, b) => (props.angle[a] ?? 0) - (props.angle[b] ?? 0))
- const series = PHASES.map((phase) => {
- const data: [number, number][] = []
- for (const index of order) {
- const angle = props.angle[index]
- if (angle === undefined || angle < phase.start || angle >= phase.end) continue
- data.push([props.volume[index], props.pressure[index]])
- }
- return {
- type: 'line',
- name: phase.name,
- data,
- showSymbol: false,
- symbol: 'none',
- animation: false,
- sampling: 'none',
- lineStyle: { width: 1.6, color: phase.color },
- itemStyle: { color: phase.color },
- }
- }).filter((item) => item.data.length >= 2)
- return {
- animation: false,
- backgroundColor: 'transparent',
- textStyle: {
- fontFamily: '-apple-system, "PingFang SC", "Microsoft YaHei", sans-serif',
- fontWeight: 'normal',
- },
- legend: {
- top: 0,
- left: 'center',
- itemWidth: 12,
- itemHeight: 8,
- itemGap: 10,
- textStyle: { fontSize: 9, color: '#aaaaaa' },
- data: series.map((item) => item.name),
- },
- grid: { left: 46, right: 12, top: 24, bottom: 26, containLabel: false },
- xAxis: {
- type: 'value',
- name: 'V/L',
- nameTextStyle: { color: '#aaaaaa', fontSize: 9 },
- axisLine: { lineStyle: { color: '#555555' } },
- axisTick: { show: false },
- axisLabel: { color: '#aaaaaa', fontSize: 9 },
- splitLine: { show: false },
- },
- yAxis: {
- type: 'value',
- scale: true,
- axisLine: { show: false },
- axisTick: { show: false },
- axisLabel: {
- color: '#aaaaaa',
- fontSize: 9,
- formatter: (value: number) => Number(value).toFixed(2),
- },
- splitLine: { show: false },
- },
- series,
- tooltip: {
- trigger: 'item',
- backgroundColor: 'rgba(0,0,0,0.8)',
- textStyle: { fontSize: 11, color: '#ffffff' },
- formatter: (params: unknown) => {
- const item = params as { value?: [number, number]; seriesName?: string } | undefined
- const value = item?.value
- if (!value) return ''
- return `${item?.seriesName ?? ''}<br/>V ${Number(value[0]).toFixed(3)} L<br/>P ${Number(value[1]).toFixed(3)} MPa`
- },
- },
- }
- }
- function render(): void {
- if (!chart) return
- chart.setOption(buildOption() as unknown as echarts.EChartsOption, { notMerge: true })
- }
- onMounted(() => {
- if (!el.value) return
- chart = echarts.init(el.value)
- render()
- observer = new ResizeObserver(() => {
- chart?.resize()
- render()
- })
- observer.observe(el.value)
- })
- onBeforeUnmount(() => {
- observer?.disconnect()
- observer = null
- chart?.dispose()
- chart = null
- })
- watch(() => [props.pressure, props.volume, props.angle], render, { deep: true })
- </script>
- <template>
- <div ref="el" class="indicator-chart" :style="{ height: `${height}px` }" />
- </template>
- <style scoped>
- .indicator-chart {
- width: 100%;
- }
- </style>
|