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.
241 lines
5.7 KiB
Vue
241 lines
5.7 KiB
Vue
<template>
|
|
<ContentWrap class="chart-panel">
|
|
<div class="chart-panel__header">
|
|
<div class="chart-panel__title">
|
|
{{ t('DataCollection.RunOverview.statusDistributionTitle') }}
|
|
</div>
|
|
<div class="chart-panel__actions">
|
|
<el-select model-value="hour" class="!w-120px">
|
|
<el-option value="hour" :label="t('DataCollection.RunOverview.granularityHour')" />
|
|
</el-select>
|
|
<el-button class="chart-panel__icon-btn">
|
|
<Icon icon="ep:data-analysis" />
|
|
</el-button>
|
|
<el-button class="chart-panel__icon-btn">
|
|
<Icon icon="ep:download" />
|
|
</el-button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="chart-panel__body">
|
|
<div class="chart-panel__main">
|
|
<Echart :options="barOption" height="320px" />
|
|
</div>
|
|
<div class="chart-panel__side">
|
|
<div class="chart-panel__side-title">{{ t('DataCollection.RunOverview.summaryTitle') }}</div>
|
|
<Echart :options="pieOption" height="320px" />
|
|
<div class="chart-panel__legend">
|
|
<div v-for="item in summary" :key="item.status" class="chart-panel__legend-item">
|
|
<span class="dot" :style="{ background: statusColors[item.status] }"></span>
|
|
<span class="label">{{ statusLabelMap[item.status] }}</span>
|
|
<span class="value">{{ item.percent.toFixed(2) }}% ({{ item.hours.toFixed(2) }}h)</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</ContentWrap>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
import type { EChartsOption } from 'echarts'
|
|
import { Echart as EchartChart } from '@/components/Echart'
|
|
import type { HourlyStatusItem, RunStatus, StatusSummaryItem } from './types'
|
|
|
|
const Echart = EchartChart
|
|
|
|
const props = defineProps<{
|
|
hourlyStatus: HourlyStatusItem[]
|
|
summary: StatusSummaryItem[]
|
|
}>()
|
|
|
|
const { t } = useI18n()
|
|
|
|
const statusColors: Record<RunStatus, string> = {
|
|
running: '#67c35b',
|
|
standby: '#f5c243',
|
|
fault: '#ff6b6b',
|
|
offline: '#cfd4df'
|
|
}
|
|
|
|
const statusLabelMap = computed<Record<RunStatus, string>>(() => ({
|
|
running: t('DataCollection.RunOverview.legend.running'),
|
|
standby: t('DataCollection.RunOverview.legend.standby'),
|
|
fault: t('DataCollection.RunOverview.legend.fault'),
|
|
offline: t('DataCollection.RunOverview.legend.offline')
|
|
}))
|
|
|
|
const barOption = computed<EChartsOption>(() => ({
|
|
color: [
|
|
statusColors.running,
|
|
statusColors.standby,
|
|
statusColors.fault,
|
|
statusColors.offline
|
|
],
|
|
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
|
legend: {
|
|
top: 8,
|
|
itemWidth: 14,
|
|
itemHeight: 8,
|
|
textStyle: { color: '#667085', fontSize: 12 }
|
|
},
|
|
grid: { left: 48, right: 18, top: 46, bottom: 36 },
|
|
xAxis: {
|
|
type: 'category',
|
|
data: props.hourlyStatus.map((item) => item.hour),
|
|
axisTick: { show: false },
|
|
axisLine: { lineStyle: { color: '#d0d5dd' } },
|
|
axisLabel: { color: '#667085', fontSize: 11 }
|
|
},
|
|
yAxis: {
|
|
type: 'value',
|
|
min: 0,
|
|
max: 100,
|
|
interval: 20,
|
|
axisLabel: { color: '#667085', formatter: '{value}%' },
|
|
splitLine: { lineStyle: { color: '#eef2f6' } }
|
|
},
|
|
series: [
|
|
{
|
|
name: statusLabelMap.value.running,
|
|
type: 'bar',
|
|
stack: 'status',
|
|
barWidth: 18,
|
|
data: props.hourlyStatus.map((item) => item.running)
|
|
},
|
|
{
|
|
name: statusLabelMap.value.standby,
|
|
type: 'bar',
|
|
stack: 'status',
|
|
barWidth: 18,
|
|
data: props.hourlyStatus.map((item) => item.standby)
|
|
},
|
|
{
|
|
name: statusLabelMap.value.fault,
|
|
type: 'bar',
|
|
stack: 'status',
|
|
barWidth: 18,
|
|
data: props.hourlyStatus.map((item) => item.fault)
|
|
},
|
|
{
|
|
name: statusLabelMap.value.offline,
|
|
type: 'bar',
|
|
stack: 'status',
|
|
barWidth: 18,
|
|
data: props.hourlyStatus.map((item) => item.offline)
|
|
}
|
|
]
|
|
}))
|
|
|
|
const pieOption = computed<EChartsOption>(() => ({
|
|
color: props.summary.map((item) => statusColors[item.status]),
|
|
tooltip: { trigger: 'item', formatter: '{b}: {c}%' },
|
|
graphic: [
|
|
{
|
|
type: 'text',
|
|
left: 'center',
|
|
top: '42%',
|
|
style: {
|
|
text: `${t('DataCollection.RunOverview.totalTimeLabel')}\n24.00 h`,
|
|
textAlign: 'center',
|
|
fill: '#101828',
|
|
fontSize: 14,
|
|
fontWeight: 600,
|
|
lineHeight: 24
|
|
}
|
|
}
|
|
],
|
|
series: [
|
|
{
|
|
name: t('DataCollection.RunOverview.summaryTitle'),
|
|
type: 'pie',
|
|
radius: ['55%', '76%'],
|
|
center: ['50%', '50%'],
|
|
label: { show: false },
|
|
data: props.summary.map((item) => ({
|
|
name: statusLabelMap.value[item.status],
|
|
value: item.percent
|
|
}))
|
|
}
|
|
]
|
|
}))
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.chart-panel {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.chart-panel__header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.chart-panel__title,
|
|
.chart-panel__side-title {
|
|
color: #101828;
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.chart-panel__actions {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: center;
|
|
}
|
|
|
|
.chart-panel__icon-btn {
|
|
width: 36px;
|
|
padding: 0;
|
|
}
|
|
|
|
.chart-panel__body {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1.7fr) minmax(280px, 0.8fr);
|
|
gap: 12px;
|
|
}
|
|
|
|
.chart-panel__side {
|
|
padding: 8px 8px 0;
|
|
}
|
|
|
|
.chart-panel__legend {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
margin-top: -8px;
|
|
}
|
|
|
|
.chart-panel__legend-item {
|
|
display: grid;
|
|
grid-template-columns: 12px 1fr auto;
|
|
gap: 8px;
|
|
align-items: center;
|
|
color: #344054;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.dot {
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.label {
|
|
color: #667085;
|
|
}
|
|
|
|
.value {
|
|
color: #101828;
|
|
font-weight: 600;
|
|
}
|
|
|
|
@media (max-width: 1100px) {
|
|
.chart-panel__body {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style>
|