|
|
@@ -0,0 +1,330 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <a-modal
|
|
|
+ v-model:visible="visible"
|
|
|
+ width="98%"
|
|
|
+ :title="id ? $t('searchTable.form.edit') : $t('searchTable.form.add')"
|
|
|
+ @cancel="() => handleCancel()"
|
|
|
+ >
|
|
|
+ <template #title>{{ t('dashboard.dialog.title') }}</template>
|
|
|
+ <a-descriptions :data="deviceInfo" bordered :column="2" />
|
|
|
+ <a-form
|
|
|
+ :model="form"
|
|
|
+ auto-label-width
|
|
|
+ ref="formRef"
|
|
|
+ style="margin-top: 16px"
|
|
|
+ >
|
|
|
+ <a-row :gutter="8">
|
|
|
+ <a-col :span="10">
|
|
|
+ <a-form-item
|
|
|
+ field="name"
|
|
|
+ :label="t('dashboard.form.name')"
|
|
|
+ :rules="getRules(t).required"
|
|
|
+ >
|
|
|
+ <a-range-picker
|
|
|
+ showTime
|
|
|
+ :defaultValue="['2019-08-08 00:00:00', '2019-08-18 00:00:00']"
|
|
|
+ @select="onSelect"
|
|
|
+ @change="onChange"
|
|
|
+ :style="{ width: '300px' }"
|
|
|
+ />
|
|
|
+ </a-form-item>
|
|
|
+ </a-col>
|
|
|
+ <a-divider :style="{ height: '33px' }" direction="vertical" />
|
|
|
+ <a-col :flex="'33px'">
|
|
|
+ <a-space :size="10">
|
|
|
+ <a-button type="primary" @click="search">
|
|
|
+ <template #icon>
|
|
|
+ <icon-search />
|
|
|
+ </template>
|
|
|
+ {{ t('searchTable.form.search') }}
|
|
|
+ </a-button>
|
|
|
+ <a-button @click="reset">
|
|
|
+ <template #icon>
|
|
|
+ <icon-refresh />
|
|
|
+ </template>
|
|
|
+ {{ t('searchTable.form.reset') }}
|
|
|
+ </a-button>
|
|
|
+ </a-space>
|
|
|
+ </a-col>
|
|
|
+ </a-row>
|
|
|
+ </a-form>
|
|
|
+ <div class="chart-container">
|
|
|
+ <v-chart
|
|
|
+ ref="chartRef"
|
|
|
+ :option="deviceType === 3 ? sensorChartOptions : chartOptions"
|
|
|
+ autoresize
|
|
|
+ class="chart"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <a-button @click="handleCancel">取消</a-button>
|
|
|
+ <!-- 只保留取消按钮,确认按钮被隐藏 -->
|
|
|
+ </template>
|
|
|
+ </a-modal>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+<script setup lang="ts" name="DeviceInfoDialog">
|
|
|
+import { reactive, ref, shallowRef, watch, getCurrentInstance } from 'vue';
|
|
|
+import { getDeviceDetails, saveDeviceDetails } from '@/api/dashboard';
|
|
|
+import type { DataList } from '@/api/dashboard';
|
|
|
+import { useI18n } from 'vue-i18n';
|
|
|
+import { getRules, DeviceInfo } from '@/utils/const';
|
|
|
+import VChart from 'vue-echarts';
|
|
|
+import { useResizeObserver } from '@vueuse/core';
|
|
|
+import { use } from 'echarts/core';
|
|
|
+import { LineChart } from 'echarts/charts';
|
|
|
+import {
|
|
|
+ TitleComponent,
|
|
|
+ TooltipComponent,
|
|
|
+ GridComponent,
|
|
|
+} from 'echarts/components';
|
|
|
+import { CanvasRenderer } from 'echarts/renderers';
|
|
|
+import type { ComposeOption } from 'echarts/core';
|
|
|
+import type { LineSeriesOption } from 'echarts/charts';
|
|
|
+import type {
|
|
|
+ TitleComponentOption,
|
|
|
+ TooltipComponentOption,
|
|
|
+ GridComponentOption,
|
|
|
+} from 'echarts/components';
|
|
|
+
|
|
|
+use([
|
|
|
+ TitleComponent,
|
|
|
+ TooltipComponent,
|
|
|
+ GridComponent,
|
|
|
+ LineChart,
|
|
|
+ CanvasRenderer,
|
|
|
+]);
|
|
|
+
|
|
|
+type EChartsOption = ComposeOption<
|
|
|
+ | TitleComponentOption
|
|
|
+ | TooltipComponentOption
|
|
|
+ | GridComponentOption
|
|
|
+ | LineSeriesOption
|
|
|
+>;
|
|
|
+
|
|
|
+const formRef = ref();
|
|
|
+const { t } = useI18n();
|
|
|
+
|
|
|
+const this_ = getCurrentInstance()?.appContext.config.globalProperties;
|
|
|
+
|
|
|
+interface EditDialogProps {
|
|
|
+ modelValue: boolean;
|
|
|
+ id: number | null;
|
|
|
+ deviceInfo: DeviceInfo[];
|
|
|
+ deviceType: number | null;
|
|
|
+}
|
|
|
+interface EditDialogEmits {
|
|
|
+ (e: 'update:modelValue', value: boolean): void;
|
|
|
+}
|
|
|
+const props = withDefaults(defineProps<EditDialogProps>(), {
|
|
|
+ modelValue: false,
|
|
|
+ id: null,
|
|
|
+ deviceType: 1,
|
|
|
+});
|
|
|
+const emit = defineEmits<EditDialogEmits>();
|
|
|
+const visible = shallowRef<boolean>(false);
|
|
|
+// 响应式图表配置
|
|
|
+const chartOptions = ref<EChartsOption>({
|
|
|
+ title: {
|
|
|
+ text: '24-hour status of equipment',
|
|
|
+ left: 'center',
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ // 关键配置:让图表充分利用空间
|
|
|
+ top: '15%',
|
|
|
+ left: '2%', // 左边距
|
|
|
+ right: '2%', // 右边距
|
|
|
+ bottom: '1%',
|
|
|
+ containLabel: true, // 包含坐标轴标签
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: [
|
|
|
+ '1',
|
|
|
+ '2',
|
|
|
+ '3',
|
|
|
+ '4',
|
|
|
+ '5',
|
|
|
+ '6',
|
|
|
+ '7',
|
|
|
+ '8',
|
|
|
+ '9',
|
|
|
+ '10',
|
|
|
+ '11',
|
|
|
+ '12',
|
|
|
+ '13',
|
|
|
+ '14',
|
|
|
+ '15',
|
|
|
+ '16',
|
|
|
+ '17',
|
|
|
+ '18',
|
|
|
+ '19',
|
|
|
+ '20',
|
|
|
+ '21',
|
|
|
+ '22',
|
|
|
+ '23',
|
|
|
+ '24',
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'category', // 改为类目轴
|
|
|
+ data: ['Offline', 'Normal', 'Warning', 'Alarm'], // 状态列表
|
|
|
+ boundaryGap: true,
|
|
|
+ axisLabel: {
|
|
|
+ formatter: (value: string) => {
|
|
|
+ // 可以根据需要进行国际化处理
|
|
|
+ return value;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ // 设置 y 轴位置对应关系
|
|
|
+ axisTick: {
|
|
|
+ alignWithLabel: true,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '状态值',
|
|
|
+ type: 'line',
|
|
|
+ // 数据需要映射到类目轴的索引
|
|
|
+ data: [
|
|
|
+ 1, 2, 3, 1, 0, 2, 3, 1, 2, 1, 0, 2, 3, 1, 2, 1, 0, 2, 3, 1, 2, 1, 0, 2,
|
|
|
+ ],
|
|
|
+ smooth: true,
|
|
|
+ // 可以添加不同状态的颜色
|
|
|
+ lineStyle: {
|
|
|
+ width: 3,
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: (params: any) => {
|
|
|
+ const statusMap: Record<number, string> = {
|
|
|
+ 0: '#999999', // Offline - 灰色
|
|
|
+ 1: '#52c41a', // Normal - 绿色
|
|
|
+ 2: '#faad14', // Warning - 橙色
|
|
|
+ 3: '#ff4d4f', // Alarm - 红色
|
|
|
+ };
|
|
|
+ return statusMap[params.data] || '#999999';
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+});
|
|
|
+// 响应式图表配置
|
|
|
+const sensorChartOptions = ref<EChartsOption>({
|
|
|
+ title: {
|
|
|
+ text: '24-hour status of equipment',
|
|
|
+ left: 'center',
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ // 关键配置:让图表充分利用空间
|
|
|
+ top: '15%',
|
|
|
+ left: '2%', // 左边距
|
|
|
+ right: '2%', // 右边距
|
|
|
+ bottom: '1%',
|
|
|
+ containLabel: true, // 包含坐标轴标签
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: [
|
|
|
+ '1',
|
|
|
+ '2',
|
|
|
+ '3',
|
|
|
+ '4',
|
|
|
+ '5',
|
|
|
+ '6',
|
|
|
+ '7',
|
|
|
+ '8',
|
|
|
+ '9',
|
|
|
+ '10',
|
|
|
+ '11',
|
|
|
+ '12',
|
|
|
+ '13',
|
|
|
+ '14',
|
|
|
+ '15',
|
|
|
+ '16',
|
|
|
+ '17',
|
|
|
+ '18',
|
|
|
+ '19',
|
|
|
+ '20',
|
|
|
+ '21',
|
|
|
+ '22',
|
|
|
+ '23',
|
|
|
+ '24',
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ min: 0, // 设置最小值
|
|
|
+ max: 100, // 设置最大值
|
|
|
+ interval: 10, // 设置刻度间隔
|
|
|
+ axisLabel: {
|
|
|
+ formatter: '{value} °C',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '温度',
|
|
|
+ type: 'line',
|
|
|
+ data: [
|
|
|
+ 20, 10, 50, 80, 70, 10, 40, 90, 30, 60, 20, 10, 50, 80, 70, 10, 40, 90,
|
|
|
+ 30, 60, 40, 70, 20, 50, 20, 10, 50, 80, 70, 10, 40, 90, 30, 60, 20, 10,
|
|
|
+ 50, 80, 70, 10, 40, 90, 20, 10, 50, 80, 70, 10, 40, 90, 30, 60, 20, 10,
|
|
|
+ 50, 80, 70, 10, 40, 90, 20, 10, 50, 80, 70, 10, 40, 90, 30, 60, 20, 10,
|
|
|
+ 50, 80, 70, 10, 40, 90, 50, 80, 70, 10, 40, 90, 20, 10, 50, 80, 70, 10,
|
|
|
+ 40, 90, 30, 60, 20, 10, 50, 80, 70, 10, 40, 90, 20, 10, 50, 80, 70, 10,
|
|
|
+ 40, 90, 30, 60, 20, 10, 50, 80, 70, 10, 40, 90, 0, 80, 70, 10, 40, 90,
|
|
|
+ 20, 10, 50, 80, 70, 10, 40, 90, 30, 60, 20, 10, 50, 80, 70, 10, 40, 90,
|
|
|
+ 50, 80, 70, 10, 40, 90, 20, 10, 50, 80, 70, 10, 40, 90, 30, 60, 20, 10,
|
|
|
+ 50, 80, 70, 10, 40, 90, 20, 10, 50, 80, 70, 10, 40, 90, 30, 60, 20, 10,
|
|
|
+ 50, 80, 70, 10, 40, 90,
|
|
|
+ ],
|
|
|
+ smooth: true,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+});
|
|
|
+watch(
|
|
|
+ () => props.modelValue,
|
|
|
+ value => {
|
|
|
+ visible.value = value;
|
|
|
+ if (value && props.id) {
|
|
|
+ getDeviceDetails({ id: props.id }).then(res => {
|
|
|
+ form.value = res.data;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+);
|
|
|
+const search = () => {};
|
|
|
+const reset = () => {
|
|
|
+ form.value = formModel();
|
|
|
+};
|
|
|
+function onSelect(dateString: any, date: any) {
|
|
|
+ console.log('onSelect', dateString, date);
|
|
|
+}
|
|
|
+function onChange(dateString: any, date: any) {
|
|
|
+ console.log('onChange: ', dateString, date);
|
|
|
+}
|
|
|
+const formModel = () => {
|
|
|
+ return {
|
|
|
+ id: 0,
|
|
|
+ deviceType: null,
|
|
|
+ } as any;
|
|
|
+};
|
|
|
+const form = ref<any>(formModel());
|
|
|
+const handleCancel = () => {
|
|
|
+ form.value = formModel();
|
|
|
+ visible.value = false;
|
|
|
+ emit('update:modelValue', false);
|
|
|
+};
|
|
|
+</script>
|
|
|
+<style lang="less" scoped>
|
|
|
+.chart {
|
|
|
+ width: 100%;
|
|
|
+ height: 300px;
|
|
|
+}
|
|
|
+</style>
|