|
@@ -0,0 +1,287 @@
|
|
|
+<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,
|
|
|
+ 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 { TimeRangePreset } from '@/constants';
|
|
|
+
|
|
|
+import type { Dayjs } from 'dayjs';
|
|
|
+import type { LineSeriesOption } from 'echarts/charts';
|
|
|
+import type {
|
|
|
+ DatasetComponentOption,
|
|
|
+ GridComponentOption,
|
|
|
+ LegendComponentOption,
|
|
|
+ TooltipComponentOption,
|
|
|
+} from 'echarts/components';
|
|
|
+import type { ComposeOption } from 'echarts/core';
|
|
|
+import type { OptionItem, RangeValue } from '@/types';
|
|
|
+
|
|
|
+use([
|
|
|
+ CanvasRenderer,
|
|
|
+ AxisPointerComponent,
|
|
|
+ MarkLineComponent,
|
|
|
+ LegendComponent,
|
|
|
+ TooltipComponent,
|
|
|
+ DatasetComponent,
|
|
|
+ GridComponent,
|
|
|
+ LineChart,
|
|
|
+]);
|
|
|
+
|
|
|
+type EChartsOption = ComposeOption<
|
|
|
+ LegendComponentOption | TooltipComponentOption | DatasetComponentOption | GridComponentOption | LineSeriesOption
|
|
|
+>;
|
|
|
+
|
|
|
+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 = () => {
|
|
|
+ getParamHistoryData();
|
|
|
+};
|
|
|
+
|
|
|
+watch(visible, () => {
|
|
|
+ if (visible.value) {
|
|
|
+ getParamHistoryData();
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+const historyData = ref<Record<string, string | number>[]>([]);
|
|
|
+
|
|
|
+const option = computed<EChartsOption>(() => {
|
|
|
+ return {
|
|
|
+ color: ['#32BAC0'],
|
|
|
+ legend: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ show: true,
|
|
|
+ trigger: 'axis',
|
|
|
+ },
|
|
|
+ dataset: {
|
|
|
+ dimensions: ['time', ...props.paramCodes],
|
|
|
+ source: historyData.value,
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ type: 'dashed',
|
|
|
+ color: '#EBEBEB',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999',
|
|
|
+ formatter(value) {
|
|
|
+ return dayjs(value).format('MM-DD HH:mm');
|
|
|
+ },
|
|
|
+ },
|
|
|
+ axisTick: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999',
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ type: 'dashed',
|
|
|
+ color: '#EBEBEB',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ series: props.paramCodes.map((item) => ({
|
|
|
+ type: 'line',
|
|
|
+ name: item,
|
|
|
+ })),
|
|
|
+ grid: {
|
|
|
+ top: 10,
|
|
|
+ right: 0,
|
|
|
+ bottom: 20,
|
|
|
+ left: 30,
|
|
|
+ },
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+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].deviceParamMapList;
|
|
|
+ } else {
|
|
|
+ historyData.value = [];
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+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"
|
|
|
+ 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>
|
|
|
+ <span class="param-name-unit">{{ '制冷量(kw)' }}</span>
|
|
|
+ <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 {
|
|
|
+ margin-left: 24px;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 17px;
|
|
|
+ color: #999;
|
|
|
+}
|
|
|
+
|
|
|
+.param-data-chart {
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+</style>
|