123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- <script setup lang="ts">
- import { computed, ref, watch } from 'vue';
- import VChart from 'vue-echarts';
- import dayjs from 'dayjs';
- import { LineChart } from 'echarts/charts';
- import {
- AxisPointerComponent,
- DatasetComponent,
- DataZoomComponent,
- GridComponent,
- LegendComponent,
- MarkLineComponent,
- TooltipComponent,
- } from 'echarts/components';
- import { use } from 'echarts/core';
- import { CanvasRenderer } from 'echarts/renderers';
- import SvgIcon from '@/components/SvgIcon.vue';
- import { useRequest } from '@/hooks/request';
- import { useViewVisible } from '@/hooks/view-visible';
- import { t } from '@/i18n';
- import { getDevWorkHistoryData } from '@/api';
- import { getEChartsColors, timeSorter } from '@/utils';
- import { TimeRangePreset } from '@/constants';
- import type { Dayjs } from 'dayjs';
- import type { LineSeriesOption } from 'echarts/charts';
- import type {
- DatasetComponentOption,
- DataZoomComponentOption,
- GridComponentOption,
- LegendComponentOption,
- TooltipComponentOption,
- } from 'echarts/components';
- import type { ComposeOption } from 'echarts/core';
- import type { DevWorkHistoryDataItem, OptionItem, RangeValue } from '@/types';
- use([
- CanvasRenderer,
- AxisPointerComponent,
- MarkLineComponent,
- LegendComponent,
- TooltipComponent,
- DatasetComponent,
- GridComponent,
- LineChart,
- DataZoomComponent,
- ]);
- type EChartsOption = ComposeOption<
- | LegendComponentOption
- | TooltipComponentOption
- | DatasetComponentOption
- | GridComponentOption
- | LineSeriesOption
- | DataZoomComponentOption
- >;
- interface Props {
- devId: number;
- paramCodes: string[];
- }
- const props = defineProps<Props>();
- const { visible, showView, hideView } = useViewVisible();
- const { isLoading, handleRequest } = useRequest();
- const timeRange = ref<RangeValue>([dayjs().add(-1, 'd'), dayjs()]);
- const currTimeRangePreset = ref<TimeRangePreset>(TimeRangePreset.recent1Day);
- const timeRangePresets = computed<OptionItem<TimeRangePreset>[]>(() => {
- return [
- { value: TimeRangePreset.recent1Day, label: t('common.recent1Day') },
- { value: TimeRangePreset.recent7Day, label: t('common.recent7Day') },
- { value: TimeRangePreset.recent30Day, label: t('common.recent30Day') },
- { value: TimeRangePreset.custom, label: t('common.custom') },
- ];
- });
- const disabledDate = (current: Dayjs) => {
- return current && current > dayjs().endOf('day');
- };
- const setTimeRange = () => {
- switch (currTimeRangePreset.value) {
- case TimeRangePreset.recent1Day:
- timeRange.value = [dayjs().add(-1, 'd'), dayjs()];
- break;
- case TimeRangePreset.recent7Day:
- timeRange.value = [dayjs().add(-7, 'd'), dayjs()];
- break;
- case TimeRangePreset.recent30Day:
- timeRange.value = [dayjs().add(-30, 'd'), dayjs()];
- break;
- }
- };
- const handleRangePresetChange = () => {
- setTimeRange();
- if (currTimeRangePreset.value !== TimeRangePreset.custom) {
- getParamHistoryData();
- }
- };
- const handleTimeRangeChange = () => {
- currTimeRangePreset.value = TimeRangePreset.custom;
- getParamHistoryData();
- };
- watch(visible, () => {
- if (visible.value) {
- getParamHistoryData();
- }
- });
- const historyData = ref<DevWorkHistoryDataItem[]>([]);
- const paramLabel = computed<string>(() => {
- if (historyData.value.length) {
- const { deviceParamName, unit } = historyData.value[0];
- return `${deviceParamName}(${unit})`;
- }
- return '';
- });
- const option = computed<EChartsOption>(() => {
- const unitText = historyData.value[0]?.unit ?? '';
- const source = convertToEChartsDataset();
- const series = source[0].slice(1).map((name) => ({
- type: 'line',
- seriesLayoutBy: 'column',
- name,
- showSymbol: false,
- })) as LineSeriesOption;
- return {
- color: getEChartsColors(1),
- legend: {
- show: false,
- },
- tooltip: {
- show: true,
- trigger: 'axis',
- formatter(params) {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const tempParms = params as any[];
- const time = dayjs(tempParms[0].axisValue).format('MM-DD HH:mm');
- let tableHtml = '<table class="echarts-tooltip-electricity">';
- tableHtml += `<tr><th>${time}</th></tr>`;
- tempParms.forEach((param) => {
- tableHtml += `<tr><td>${param.marker}${param.seriesName}</td><td>${param.value[1] ?? '-'}${unitText}</td></tr>`;
- });
- tableHtml += '</table>';
- return tableHtml;
- },
- },
- dataset: {
- source,
- },
- xAxis: {
- type: 'category',
- axisLine: {
- lineStyle: {
- type: 'dashed',
- color: '#EBEBEB',
- },
- },
- axisLabel: {
- color: '#999',
- fontSize: 10,
- formatter(value) {
- return dayjs(value).format('MM-DD HH:mm');
- },
- },
- axisTick: {
- show: false,
- },
- },
- yAxis: {
- axisLabel: {
- color: '#999',
- fontSize: 10,
- },
- splitLine: {
- lineStyle: {
- type: 'dashed',
- color: '#EBEBEB',
- },
- },
- },
- series,
- grid: {
- top: 10,
- right: 30,
- bottom: 90,
- left: 30,
- containLabel: true,
- },
- dataZoom: [
- {
- type: 'inside',
- start: 0,
- end: 100,
- bottom: 35,
- handleStyle: {
- label: { show: false },
- },
- showDetail: false,
- },
- {
- start: 0,
- end: 100,
- bottom: 35,
- handleStyle: {
- label: { show: false },
- },
- showDetail: false,
- },
- ],
- };
- });
- const getParamHistoryData = () => {
- handleRequest(async () => {
- const [startTime, endTime] = timeRange.value;
- const data = await getDevWorkHistoryData({
- deviceIds: [props.devId],
- deviceParamCode: props.paramCodes,
- startTime: startTime.format('YYYY-MM-DD HH:mm:ss'),
- endTime: endTime.format('YYYY-MM-DD HH:mm:ss'),
- });
- if (data.length) {
- historyData.value = data[0].hisVOS;
- } else {
- historyData.value = [];
- }
- });
- };
- type EChartsDatasetItemValue = string | number | null;
- const convertToEChartsDataset = () => {
- const times: string[] = [];
- const dataMap: Record<string, { time: string; value: string | number }[]> = {};
- historyData.value.forEach((item) => {
- const paramName = item.deviceParamName;
- dataMap[paramName] = [];
- item.value.forEach((val) => {
- if (!times.includes(val.time)) {
- times.push(val.time);
- }
- dataMap[paramName].push({
- time: val.time,
- value: val.value,
- });
- });
- });
- const source: EChartsDatasetItemValue[][] = [['time', ...Object.keys(dataMap)]];
- times.sort(timeSorter);
- times.forEach((time) => {
- const row: EChartsDatasetItemValue[] = [time];
- Object.values(dataMap).forEach((values) => {
- const found = values.find((v) => v.time === time);
- row.push(found ? found.value : null);
- });
- source.push(row);
- });
- return source;
- };
- const handleClose = () => {
- historyData.value = [];
- currTimeRangePreset.value = TimeRangePreset.recent1Day;
- setTimeRange();
- };
- defineExpose({
- showView,
- hideView,
- });
- </script>
- <template>
- <AModal
- v-model:open="visible"
- wrap-class-name="dev-work-param-data-modal"
- :title="$t('deviceWorkStatus.viewData')"
- :width="920"
- :z-index="1001"
- centered
- :footer="null"
- :after-close="handleClose"
- >
- <ASpin class="center-loading" :spinning="isLoading" />
- <div class="param-data-select">
- <span class="param-data-label">{{ $t('common.selectTime') }}</span>
- <ASelect v-model:value="currTimeRangePreset" class="param-preset-picker" @change="handleRangePresetChange">
- <ASelectOption v-for="item in timeRangePresets" :key="item.value" :value="item.value">
- {{ item.label }}
- </ASelectOption>
- </ASelect>
- <ARangePicker
- class="param-date-picker"
- v-model:value="timeRange"
- :allow-clear="false"
- :separator="$t('common.to')"
- :disabled-date="disabledDate"
- @change="handleTimeRangeChange"
- >
- <template #suffixIcon>
- <SvgIcon name="calendar" color="#333" />
- </template>
- </ARangePicker>
- </div>
- <div class="param-name-unit">{{ paramLabel }}</div>
- <VChart class="param-data-chart" :option="option" />
- </AModal>
- </template>
- <style lang="scss">
- .dev-work-param-data-modal {
- .ant-modal,
- .ant-modal > div,
- .ant-modal-content {
- height: 498px;
- }
- .ant-modal-content {
- display: flex;
- flex-direction: column;
- overflow: hidden;
- }
- .ant-modal-body {
- display: flex;
- flex-direction: column;
- height: calc(100% - 32px);
- }
- }
- </style>
- <style lang="scss" scoped>
- .param-data-select {
- margin-bottom: 16px;
- }
- .param-data-label {
- font-size: 14px;
- line-height: 24px;
- color: #333;
- }
- .param-preset-picker {
- width: 110px;
- margin-inline: 12px;
- }
- .param-date-picker {
- width: 256px;
- :deep(.ant-picker-range-separator) {
- padding-right: 16px;
- }
- }
- .param-name-unit {
- height: 17px;
- margin-left: 24px;
- font-size: 12px;
- line-height: 17px;
- color: #999;
- }
- .param-data-chart {
- height: 100%;
- }
- </style>
|