AlarmChart.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. <template>
  2. <div>
  3. <div class="alarm-toolbar"><el-radio-group v-model="period" @change="load"><el-radio-button value="CURVE" label="CURVE">事件前后</el-radio-button><el-radio-button value="DAY" label="DAY">事件当天</el-radio-button><el-radio-button value="DAILY" label="DAILY">日均值</el-radio-button><el-radio-button value="MONTHLY" label="MONTHLY">月均值</el-radio-button></el-radio-group><span class="muted">{{ period === 'CURVE' ? '报警前后 60 分钟' : period === 'DAY' ? '事件当天' : period === 'DAILY' ? '截至事件日的 7 天' : '截至事件日的 6 个月' }}</span></div>
  4. <p v-if="result.latestSample" class="muted">最新监测值:{{ result.latestSample.value ?? '无效采样' }} {{ result.alarm?.warningUnit }} · {{ result.latestSample.time }}</p>
  5. <el-alert v-if="error" :title="error" type="error" show-icon :closable="false" />
  6. <div v-loading="loading" ref="host" class="alarm-chart"></div>
  7. <el-empty v-if="!loading && !validCount && !error" :description="result.message || '所选区间暂无有效采集数据'" :image-size="55" />
  8. <div class="muted">{{ result.message }}<template v-if="result.supported"> · 有效采样 {{ validCount }} / {{ sampleCount }} · {{ result.startTime?.replace('T', ' ') }} — {{ result.endTime?.replace('T', ' ') }}</template></div>
  9. </div>
  10. </template>
  11. <script setup>
  12. import { ref, computed, watch, nextTick, onBeforeUnmount, onMounted } from 'vue'
  13. import * as echarts from 'echarts'
  14. import { getAlarmAnalysis } from '@/api/alarm'
  15. import { seriesPoints, sampleTime } from './utils/alarmOperationsModel'
  16. const props = defineProps({ alarmId: String, scope: { type: Object, required: true } })
  17. const period = ref('CURVE'), host = ref(), loading = ref(false), error = ref(''), result = ref({})
  18. const validCount = computed(() => (result.value.samples || []).reduce((sum, p) => sum + Number(p.validCount), 0))
  19. const sampleCount = computed(() => (result.value.samples || []).reduce((sum, p) => sum + Number(p.sampleCount), 0))
  20. let generation = 0, chart, observer
  21. async function load() {
  22. const token = ++generation; result.value = {}; error.value = ''; chart?.clear()
  23. if (!props.alarmId) return
  24. loading.value = true
  25. try {
  26. const response = await getAlarmAnalysis(props.alarmId, { ...props.scope, period: period.value })
  27. if (token !== generation) return
  28. result.value = response.data || {}; await nextTick(); draw()
  29. } catch (e) { if (token === generation) error.value = e.message || '曲线加载失败' }
  30. finally { if (token === generation) loading.value = false }
  31. }
  32. function draw() {
  33. if (!host.value) return
  34. if (!chart) chart = echarts.init(host.value)
  35. const alarm = result.value.alarm || {}, points = result.value.samples || []
  36. const raw = period.value === 'CURVE' || period.value === 'DAY'
  37. const event = raw && alarm.actualValue != null && alarm.createTime ? [{ name: '报警触发值', type: 'scatter', symbolSize: 12, itemStyle: { color: '#f56c6c' }, data: [[sampleTime(alarm.createTime), Number(alarm.actualValue)]] }] : []
  38. const limits = [['下限', alarm.minValue], ['上限', alarm.maxValue]].filter(([, v]) => v !== null && v !== undefined).map(([name, v]) => ({ name, yAxis: Number(v) }))
  39. chart.setOption({ tooltip: { trigger: 'axis', renderMode: 'richText' }, grid: { left: 65, right: 30, bottom: 60, top: 35 }, xAxis: { type: 'time', min: result.value.startTime ? sampleTime(result.value.startTime) : undefined, max: result.value.endTime ? sampleTime(result.value.endTime) : undefined }, yAxis: { type: 'value', scale: true, name: alarm.warningUnit || '' }, dataZoom: [{ type: 'inside' }, { type: 'slider', height: 20 }], series: [{ name: alarm.warningType || '监测值', type: 'line', showSymbol: points.length < 100, connectNulls: false, data: seriesPoints(points, raw), lineStyle: { color: '#409eff', width: 2 }, markLine: { silent: true, symbol: 'none', lineStyle: { color: '#e6a23c', type: 'dashed' }, data: limits } }, ...event] }, true)
  40. }
  41. watch(() => [props.alarmId, JSON.stringify(props.scope)], () => { period.value = 'CURVE'; load() }, { immediate: true })
  42. onMounted(() => { observer = new ResizeObserver(() => chart?.resize()); observer.observe(host.value); if (result.value.samples) draw() })
  43. onBeforeUnmount(() => { ++generation; observer?.disconnect(); chart?.dispose() })
  44. </script>
  45. <style scoped>.alarm-chart{width:100%;height:360px}</style>