Commit ec6d323d158bcdc79dd035ad15baf162a17e47ae

Authored by xp.Huang
2 parents 7fd02e45 06064c91

Merge branch 'ww' into 'main'

fix: DEFECT-963 create data component fetch api happend error

See merge request huang/yun-teng-iot-front!424
@@ -44,6 +44,7 @@ enum DeviceUrl { @@ -44,6 +44,7 @@ enum DeviceUrl {
44 GET_SLAVE_DEVICE = '/device/list/slave', 44 GET_SLAVE_DEVICE = '/device/list/slave',
45 GET_DEVICE_ATTRIBUTE = '/device/attributes', 45 GET_DEVICE_ATTRIBUTE = '/device/attributes',
46 GET_DEVICE = '/device/list', 46 GET_DEVICE = '/device/list',
  47 + GET_DEVICE_RELATION = '/device/device/relation',
47 } 48 }
48 49
49 /** 50 /**
@@ -224,3 +225,10 @@ export const sendCommandOneway = (params: SendCommandParams) => { @@ -224,3 +225,10 @@ export const sendCommandOneway = (params: SendCommandParams) => {
224 { joinPrefix: false } 225 { joinPrefix: false }
225 ); 226 );
226 }; 227 };
  228 +
  229 +export const getDeviceRelation = (params: { deviceId: string; isSlave: boolean }) => {
  230 + return defHttp.get<string>({
  231 + url: DeviceUrl.GET_DEVICE_RELATION,
  232 + params,
  233 + });
  234 +};
1 import { RadioRecord } from '../../../views/visual/board/detail/config/util'; 1 import { RadioRecord } from '../../../views/visual/board/detail/config/util';
  2 +import { DeviceTypeEnum } from '../../device/model/deviceModel';
2 3
3 export interface AddDataBoardParams { 4 export interface AddDataBoardParams {
4 name: string; 5 name: string;
@@ -79,12 +80,14 @@ export interface DataSource { @@ -79,12 +80,14 @@ export interface DataSource {
79 componentInfo: ComponentInfo; 80 componentInfo: ComponentInfo;
80 deviceName: string; 81 deviceName: string;
81 deviceProfileId: string; 82 deviceProfileId: string;
  83 + tbDeviceId: string;
82 84
83 // front usage 85 // front usage
84 uuid?: string; 86 uuid?: string;
85 width?: number; 87 width?: number;
86 height?: number; 88 height?: number;
87 radio?: RadioRecord; 89 radio?: RadioRecord;
  90 + deviceType?: DeviceTypeEnum;
88 } 91 }
89 92
90 export interface DataComponentRecord { 93 export interface DataComponentRecord {
@@ -163,7 +163,7 @@ @@ -163,7 +163,7 @@
163 <template> 163 <template>
164 <PageWrapper dense contentFullHeight contentClass="flex"> 164 <PageWrapper dense contentFullHeight contentClass="flex">
165 <OrganizationIdTree @select="handleSelect" ref="organizationIdTreeRef" /> 165 <OrganizationIdTree @select="handleSelect" ref="organizationIdTreeRef" />
166 - <section class="flex-auto p-4 configuration-list"> 166 + <section class="flex-auto p-4 w-full configuration-list">
167 <div class="flex-auto w-full bg-light-50 dark:bg-dark-900 p-4"> 167 <div class="flex-auto w-full bg-light-50 dark:bg-dark-900 p-4">
168 <BasicForm @register="registerForm" /> 168 <BasicForm @register="registerForm" />
169 </div> 169 </div>
@@ -248,7 +248,10 @@ @@ -248,7 +248,10 @@
248 <EllipsisOutlined key="ellipsis" /> 248 <EllipsisOutlined key="ellipsis" />
249 </Dropdown> 249 </Dropdown>
250 </template> 250 </template>
251 - <Card.Meta :title="item.name"> 251 + <Card.Meta>
  252 + <template #title>
  253 + <span class="truncate">{{ item.name }}</span>
  254 + </template>
252 <template #description> 255 <template #description>
253 <div class="truncate h-11"> 256 <div class="truncate h-11">
254 <div class="truncate">{{ item.organizationDTO.name }}</div> 257 <div class="truncate">{{ item.organizationDTO.name }}</div>
@@ -13,12 +13,6 @@ @@ -13,12 +13,6 @@
13 <Tabs.TabPane :key="FunctionType.SERVICE" tab="服务" /> 13 <Tabs.TabPane :key="FunctionType.SERVICE" tab="服务" />
14 <!-- <Tabs.TabPane :key="FunctionType.EVENTS" tab="事件" /> --> 14 <!-- <Tabs.TabPane :key="FunctionType.EVENTS" tab="事件" /> -->
15 <template #tabBarExtraContent> 15 <template #tabBarExtraContent>
16 - <Button @click="handlePremitter">  
17 - <template #icon>  
18 - <SortAscendingOutlined />  
19 - </template>  
20 - 格式化  
21 - </Button>  
22 <Button class="ml-2" @click="handleCopy"> 16 <Button class="ml-2" @click="handleCopy">
23 <template #icon> 17 <template #icon>
24 <CopyOutlined /> 18 <CopyOutlined />
@@ -30,24 +24,21 @@ @@ -30,24 +24,21 @@
30 </div> 24 </div>
31 <div class="relative"> 25 <div class="relative">
32 <Spin :spinning="loading"> 26 <Spin :spinning="loading">
33 - <div id="jsoneditor" ref="jsoneditorEl"></div>  
34 - <div class="absolute top-0 left-0 w-full h-full"></div> 27 + <Textarea :disabled="true" :auto-size="{ minRows: 20, maxRows: 20 }" :value="jsonValue" />
35 </Spin> 28 </Spin>
36 </div> 29 </div>
37 </div> 30 </div>
38 </template> 31 </template>
39 <script lang="ts" setup> 32 <script lang="ts" setup>
40 - import { ref, unref, onMounted, nextTick } from 'vue';  
41 - import { CopyOutlined, SortAscendingOutlined } from '@ant-design/icons-vue';  
42 - import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard'; 33 + import { ref, unref, onMounted } from 'vue';
  34 + import { CopyOutlined } from '@ant-design/icons-vue';
43 import { useMessage } from '/@/hooks/web/useMessage'; 35 import { useMessage } from '/@/hooks/web/useMessage';
44 - import jsoneditor from 'jsoneditor';  
45 - import 'jsoneditor/dist/jsoneditor.min.css';  
46 - import { Button, Typography, TypographyParagraph, Tabs, Spin } from 'ant-design-vue'; 36 + import { Button, Typography, TypographyParagraph, Tabs, Spin, Textarea } from 'ant-design-vue';
47 import { FunctionType } from './config'; 37 import { FunctionType } from './config';
48 import useCommon from '../hook/useCommon'; 38 import useCommon from '../hook/useCommon';
49 import { DeviceRecord } from '/@/api/device/model/deviceModel'; 39 import { DeviceRecord } from '/@/api/device/model/deviceModel';
50 import { getModelTsl } from '/@/api/device/modelOfMatter'; 40 import { getModelTsl } from '/@/api/device/modelOfMatter';
  41 + import { useClipboard } from '@vueuse/core';
51 42
52 const props = defineProps<{ 43 const props = defineProps<{
53 record: DeviceRecord; 44 record: DeviceRecord;
@@ -61,40 +52,17 @@ @@ -61,40 +52,17 @@
61 52
62 const jsonValue = ref(); 53 const jsonValue = ref();
63 54
64 - const jsonInstance = ref<{  
65 - set: (value?: Recordable) => void;  
66 - get: () => Recordable;  
67 - }>();  
68 -  
69 - const jsoneditorEl = ref<HTMLDivElement>();  
70 -  
71 const activeKey = ref(FunctionType.PROPERTIES); 55 const activeKey = ref(FunctionType.PROPERTIES);
72 56
73 - onMounted(() => {  
74 - nextTick(() => {  
75 - let options = {  
76 - mode: 'code',  
77 - mainMenuBar: false,  
78 - statusBar: false,  
79 - };  
80 - let editor = new jsoneditor(jsoneditorEl.value, options);  
81 - editor.set(jsonValue.value);  
82 - jsonInstance.value = editor;  
83 - });  
84 - });  
85 -  
86 - const { clipboardRef, copiedRef } = useCopyToClipboard();  
87 - 57 + const { copy, isSupported } = useClipboard({ source: jsonValue });
88 const handleCopy = () => { 58 const handleCopy = () => {
89 try { 59 try {
90 - const valueRef = unref(jsonInstance)?.get();  
91 - const value = JSON.stringify(unref(valueRef));  
92 - if (!value) { 60 + if (!jsonValue.value) {
93 createMessage.warning('请输入要拷贝的内容!'); 61 createMessage.warning('请输入要拷贝的内容!');
94 return; 62 return;
95 } 63 }
96 - clipboardRef.value = value;  
97 - if (unref(copiedRef)) { 64 + if (unref(isSupported)) {
  65 + copy();
98 createMessage.success('复制成功!'); 66 createMessage.success('复制成功!');
99 } 67 }
100 } catch (e) { 68 } catch (e) {
@@ -103,25 +71,18 @@ @@ -103,25 +71,18 @@
103 }; 71 };
104 72
105 const getFormData = () => { 73 const getFormData = () => {
106 - const value = unref(jsonInstance)?.get();  
107 - if (!value) return;  
108 - return value; 74 + return JSON.parse(jsonValue.value);
109 }; 75 };
110 76
111 const resetFormData = () => { 77 const resetFormData = () => {
112 - unref(jsonInstance)?.set();  
113 - };  
114 -  
115 - const handlePremitter = () => {  
116 - const value = unref(jsonInstance)?.get();  
117 - return unref(jsonInstance)?.set(value); 78 + jsonValue.value = null;
118 }; 79 };
119 80
120 const handleSwitchTsl = async (functionType: FunctionType) => { 81 const handleSwitchTsl = async (functionType: FunctionType) => {
121 try { 82 try {
122 loading.value = true; 83 loading.value = true;
123 const record = await getModelTsl({ deviceProfileId: props.record.id, functionType }); 84 const record = await getModelTsl({ deviceProfileId: props.record.id, functionType });
124 - jsonInstance.value?.set(record); 85 + jsonValue.value = JSON.stringify(record, null, 2);
125 } catch (error) { 86 } catch (error) {
126 } finally { 87 } finally {
127 loading.value = false; 88 loading.value = false;
@@ -138,10 +99,3 @@ @@ -138,10 +99,3 @@
138 resetFormData, 99 resetFormData,
139 }); 100 });
140 </script> 101 </script>
141 -  
142 -<style lang="less" scope>  
143 - #jsoneditor {  
144 - width: 100%;  
145 - height: 450px;  
146 - }  
147 -</style>  
@@ -41,9 +41,9 @@ @@ -41,9 +41,9 @@
41 > 41 >
42 <label class="switch"> 42 <label class="switch">
43 <input 43 <input
44 - :value="props.value?.value" 44 + :value="!!props.value?.value"
45 type="checkbox" 45 type="checkbox"
46 - :checked="props.value?.value" 46 + :checked="!!props.value?.value"
47 @change="handleChange" 47 @change="handleChange"
48 /> 48 />
49 <div class="button"> 49 <div class="button">
1 import { DataComponentRecord, DataSource } from '/@/api/dataBoard/model'; 1 import { DataComponentRecord, DataSource } from '/@/api/dataBoard/model';
  2 +import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
2 3
3 export interface ControlComponentLayout { 4 export interface ControlComponentLayout {
4 [key: string]: any; 5 [key: string]: any;
@@ -14,6 +15,8 @@ export interface ControlComponentValue { @@ -14,6 +15,8 @@ export interface ControlComponentValue {
14 fontColor?: string; 15 fontColor?: string;
15 slaveDeviceId?: string; 16 slaveDeviceId?: string;
16 deviceProfileId?: string; 17 deviceProfileId?: string;
  18 + deviceType?: DeviceTypeEnum;
  19 + organizationId?: string;
17 } 20 }
18 21
19 export const ControlComponentDefaultConfig: ControlComponentValue = { 22 export const ControlComponentDefaultConfig: ControlComponentValue = {
@@ -34,7 +37,9 @@ export const transformControlConfig = ( @@ -34,7 +37,9 @@ export const transformControlConfig = (
34 attributeRename: dataSourceRecord.attributeRename, 37 attributeRename: dataSourceRecord.attributeRename,
35 deviceProfileId: dataSourceRecord.deviceProfileId, 38 deviceProfileId: dataSourceRecord.deviceProfileId,
36 deviceId: dataSourceRecord.deviceId, 39 deviceId: dataSourceRecord.deviceId,
  40 + deviceType: dataSourceRecord.deviceType,
37 slaveDeviceId: dataSourceRecord.slaveDeviceId, 41 slaveDeviceId: dataSourceRecord.slaveDeviceId,
  42 + organizationId: dataSourceRecord.organizationId,
38 } as ControlComponentValue, 43 } as ControlComponentValue,
39 }; 44 };
40 }; 45 };
1 import { ControlComponentValue } from './control.config'; 1 import { ControlComponentValue } from './control.config';
2 import { getDeviceProfile } from '/@/api/alarm/position'; 2 import { getDeviceProfile } from '/@/api/alarm/position';
3 -import { sendCommandOneway } from '/@/api/dataBoard'; 3 +import { getDeviceRelation, sendCommandOneway } from '/@/api/dataBoard';
  4 +import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
4 import { getModelServices } from '/@/api/device/modelOfMatter'; 5 import { getModelServices } from '/@/api/device/modelOfMatter';
5 import { useMessage } from '/@/hooks/web/useMessage'; 6 import { useMessage } from '/@/hooks/web/useMessage';
6 import { isString } from '/@/utils/is'; 7 import { isString } from '/@/utils/is';
@@ -8,7 +9,9 @@ import { isString } from '/@/utils/is'; @@ -8,7 +9,9 @@ import { isString } from '/@/utils/is';
8 const { createMessage } = useMessage(); 9 const { createMessage } = useMessage();
9 export function useSendCommand() { 10 export function useSendCommand() {
10 const sendCommand = async (record: ControlComponentValue, value: any) => { 11 const sendCommand = async (record: ControlComponentValue, value: any) => {
11 - const { deviceId, deviceProfileId, attribute } = record; 12 + if (!record) return;
  13 + const { deviceProfileId, attribute, deviceType } = record;
  14 + let { deviceId } = record;
12 if (!deviceId) return; 15 if (!deviceId) return;
13 try { 16 try {
14 const list = await getDeviceProfile(); 17 const list = await getDeviceProfile();
@@ -23,6 +26,12 @@ export function useSendCommand() { @@ -23,6 +26,12 @@ export function useSendCommand() {
23 const sendCommand = record?.functionJson.inputData?.at(0)?.serviceCommand || ''; 26 const sendCommand = record?.functionJson.inputData?.at(0)?.serviceCommand || '';
24 params = isString(sendCommand) ? sendCommand : JSON.stringify(sendCommand); 27 params = isString(sendCommand) ? sendCommand : JSON.stringify(sendCommand);
25 } 28 }
  29 + if (deviceType === DeviceTypeEnum.SENSOR) {
  30 + deviceId = await getDeviceRelation({
  31 + deviceId,
  32 + isSlave: deviceType === DeviceTypeEnum.SENSOR,
  33 + });
  34 + }
26 await sendCommandOneway({ 35 await sendCommandOneway({
27 deviceId, 36 deviceId,
28 value: { 37 value: {
@@ -27,15 +27,19 @@ @@ -27,15 +27,19 @@
27 const { dataSource = [] } = params; 27 const { dataSource = [] } = params;
28 const deviceRecord = dataSource?.at(0) || ({} as DataSource); 28 const deviceRecord = dataSource?.at(0) || ({} as DataSource);
29 if (!deviceRecord.organizationId) return; 29 if (!deviceRecord.organizationId) return;
30 - const deviceList = await getAllDeviceByOrg(deviceRecord.organizationId); 30 + const deviceList = await getAllDeviceByOrg(
  31 + deviceRecord.organizationId,
  32 + deviceRecord.deviceProfileId
  33 + );
31 const options = deviceList 34 const options = deviceList
32 - .filter((item) => item.id === deviceRecord.deviceId)  
33 - .map((item) => ({ ...item, label: item.name, value: item.id })); 35 + .filter((item) => item.tbDeviceId === deviceRecord.deviceId)
  36 + .map((item) => ({ ...item, label: item.name, value: item.tbDeviceId }));
34 const attKey = dataSource.map((item) => ({ 37 const attKey = dataSource.map((item) => ({
35 ...item, 38 ...item,
36 label: item.attribute, 39 label: item.attribute,
37 value: item.attribute, 40 value: item.attribute,
38 })); 41 }));
  42 + console.log(options);
39 updateSchema([ 43 updateSchema([
40 { 44 {
41 field: SchemaFiled.DEVICE_ID, 45 field: SchemaFiled.DEVICE_ID,
@@ -22,6 +22,7 @@ @@ -22,6 +22,7 @@
22 import { formatToDateTime } from '/@/utils/dateUtil'; 22 import { formatToDateTime } from '/@/utils/dateUtil';
23 import { isEqual } from 'lodash-es'; 23 import { isEqual } from 'lodash-es';
24 import { useAsyncQueue } from '/@/views/device/localtion/useAsyncQueue'; 24 import { useAsyncQueue } from '/@/views/device/localtion/useAsyncQueue';
  25 + import { useMessage } from '/@/hooks/web/useMessage';
25 26
26 // useVisualBoardContext(); 27 // useVisualBoardContext();
27 type TrackRecord = Record<'lng' | 'lat' | 'ts', number>; 28 type TrackRecord = Record<'lng' | 'lat' | 'ts', number>;
@@ -199,7 +200,13 @@ @@ -199,7 +200,13 @@
199 return (trackAni.value || {})._status; 200 return (trackAni.value || {})._status;
200 }); 201 });
201 202
  203 + const { createMessage } = useMessage();
202 const handlePlay = () => { 204 const handlePlay = () => {
  205 + const { start, end } = timeRange;
  206 +
  207 + if (!props.random && (!start || !end)) {
  208 + createMessage.warning('请先选择时间范围');
  209 + }
203 if (unref(getTrackPlayStatus) === TrackAnimationStatus.DONE) unref(trackAni).start(); 210 if (unref(getTrackPlayStatus) === TrackAnimationStatus.DONE) unref(trackAni).start();
204 else if (unref(getTrackPlayStatus) === TrackAnimationStatus.PLAY) unref(trackAni).pause(); 211 else if (unref(getTrackPlayStatus) === TrackAnimationStatus.PLAY) unref(trackAni).pause();
205 else if (unref(getTrackPlayStatus) === TrackAnimationStatus.PAUSE) unref(trackAni).continue(); 212 else if (unref(getTrackPlayStatus) === TrackAnimationStatus.PAUSE) unref(trackAni).continue();
@@ -289,7 +289,7 @@ @@ -289,7 +289,7 @@
289 <Alert type="info" show-icon v-if="isMapComponent"> 289 <Alert type="info" show-icon v-if="isMapComponent">
290 <template #description> 290 <template #description>
291 <div> 291 <div>
292 - 地图组件,需绑定两个数据源,且数据源为同一设备。第一数据源为经度,第二数据源为度,否则地图组件不能正常显示。 292 + 地图组件,需绑定两个数据源,且数据源为同一设备。第一数据源为经度,第二数据源为度,否则地图组件不能正常显示。
293 </div> 293 </div>
294 </template> 294 </template>
295 </Alert> 295 </Alert>
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 import { nextTick, Ref, ref, unref } from 'vue'; 2 import { nextTick, Ref, ref, unref } from 'vue';
3 import { getDeviceHistoryInfo } from '/@/api/alarm/position'; 3 import { getDeviceHistoryInfo } from '/@/api/alarm/position';
4 - import { Empty } from 'ant-design-vue'; 4 + import { Empty, Tooltip, Button } from 'ant-design-vue';
5 import { useECharts } from '/@/hooks/web/useECharts'; 5 import { useECharts } from '/@/hooks/web/useECharts';
6 import { AggregateDataEnum } from '/@/views/device/localtion/config.data'; 6 import { AggregateDataEnum } from '/@/views/device/localtion/config.data';
7 import { useGridLayout } from '/@/hooks/component/useGridLayout'; 7 import { useGridLayout } from '/@/hooks/component/useGridLayout';
@@ -14,17 +14,29 @@ @@ -14,17 +14,29 @@
14 import { useModalInner } from '/@/components/Modal'; 14 import { useModalInner } from '/@/components/Modal';
15 import { getAllDeviceByOrg } from '/@/api/dataBoard'; 15 import { getAllDeviceByOrg } from '/@/api/dataBoard';
16 import { useHistoryData } from '/@/views/device/list/hook/useHistoryData'; 16 import { useHistoryData } from '/@/views/device/list/hook/useHistoryData';
  17 + import { BasicTable, useTable } from '/@/components/Table';
  18 + import { LineChartOutlined, BarsOutlined } from '@ant-design/icons-vue';
  19 + import { formatToDateTime } from '/@/utils/dateUtil';
17 20
18 type DeviceOption = Record<'label' | 'value' | 'organizationId', string>; 21 type DeviceOption = Record<'label' | 'value' | 'organizationId', string>;
19 22
20 defineEmits(['register']); 23 defineEmits(['register']);
21 24
  25 + enum Mode {
  26 + TABLE = 'table',
  27 + CHART = 'chart',
  28 + }
  29 +
  30 + const mode = ref<Mode>(Mode.CHART);
  31 +
22 const chartRef = ref(); 32 const chartRef = ref();
23 33
24 const loading = ref(false); 34 const loading = ref(false);
25 35
26 const isNull = ref(false); 36 const isNull = ref(false);
27 37
  38 + const historyData = ref<{ ts: number; value: string; name: string }[]>([]);
  39 +
28 const { deviceAttrs, getDeviceKeys, getSearchParams, setChartOptions, getDeviceAttribute } = 40 const { deviceAttrs, getDeviceKeys, getSearchParams, setChartOptions, getDeviceAttribute } =
29 useHistoryData(); 41 useHistoryData();
30 42
@@ -52,30 +64,74 @@ @@ -52,30 +64,74 @@
52 loading: loading as unknown as boolean, 64 loading: loading as unknown as boolean,
53 }, 65 },
54 async submitFunc() { 66 async submitFunc() {
55 - // 表单验证  
56 - await method.validate();  
57 - const value = method.getFieldsValue();  
58 - const searchParams = getSearchParams(value);  
59 - if (!hasDeviceAttr()) return;  
60 - // 发送请求  
61 - loading.value = true;  
62 - const res = await getDeviceHistoryInfo(searchParams);  
63 - loading.value = false;  
64 - // 判断数据对象是否为空  
65 - if (!Object.keys(res).length) {  
66 - isNull.value = false;  
67 - return;  
68 - } else {  
69 - isNull.value = true;  
70 - }  
71 -  
72 - const selectedKeys = unref(deviceAttrs).find(  
73 - (item) => item.identifier === value[SchemaFiled.KEYS]  
74 - );  
75 - setOptions(setChartOptions(res, selectedKeys)); 67 + search();
76 }, 68 },
77 }); 69 });
78 70
  71 + const search = async () => {
  72 + // 表单验证
  73 + await method.validate();
  74 + const value = method.getFieldsValue();
  75 + const searchParams = getSearchParams(value);
  76 + if (!hasDeviceAttr()) return;
  77 + // 发送请求
  78 + loading.value = true;
  79 + const res = await getDeviceHistoryInfo(searchParams);
  80 + historyData.value = getTableHistoryData(res);
  81 + loading.value = false;
  82 + // 判断数据对象是否为空
  83 + if (!Object.keys(res).length) {
  84 + isNull.value = false;
  85 + return;
  86 + } else {
  87 + isNull.value = true;
  88 + }
  89 +
  90 + const selectedKeys = unref(deviceAttrs).find(
  91 + (item) => item.identifier === value[SchemaFiled.KEYS]
  92 + );
  93 + setOptions(setChartOptions(res, selectedKeys));
  94 + };
  95 +
  96 + const getTableHistoryData = (record: Recordable<{ ts: number; value: string }[]>) => {
  97 + const keys = Object.keys(record);
  98 + const list = keys.reduce((prev, next) => {
  99 + const list = record[next].map((item) => {
  100 + return {
  101 + ...item,
  102 + name: next,
  103 + };
  104 + });
  105 + return [...prev, ...list];
  106 + }, []);
  107 + return list;
  108 + };
  109 +
  110 + const [registerTable] = useTable({
  111 + showIndexColumn: false,
  112 + showTableSetting: false,
  113 + dataSource: historyData,
  114 + maxHeight: 300,
  115 + size: 'small',
  116 + columns: [
  117 + {
  118 + title: '属性',
  119 + dataIndex: 'name',
  120 + },
  121 + {
  122 + title: '值',
  123 + dataIndex: 'value',
  124 + },
  125 + {
  126 + title: '更新时间',
  127 + dataIndex: 'ts',
  128 + format: (val) => {
  129 + return formatToDateTime(val, 'YYYY-MM-DD HH:mm:ss');
  130 + },
  131 + },
  132 + ],
  133 + });
  134 +
79 const getDeviceDataKey = async (record: DeviceOption) => { 135 const getDeviceDataKey = async (record: DeviceOption) => {
80 const { organizationId, value } = record; 136 const { organizationId, value } = record;
81 try { 137 try {
@@ -115,9 +171,9 @@ @@ -115,9 +171,9 @@
115 agg: AggregateDataEnum.NONE, 171 agg: AggregateDataEnum.NONE,
116 limit: 7, 172 limit: 7,
117 }); 173 });
118 - 174 + historyData.value = getTableHistoryData(res);
119 // 判断对象是否为空 175 // 判断对象是否为空
120 - if (!Object.keys(res).length) { 176 + if (!Object.keys(unref(historyData)).length) {
121 isNull.value = false; 177 isNull.value = false;
122 return; 178 return;
123 } else { 179 } else {
@@ -181,6 +237,10 @@ @@ -181,6 +237,10 @@
181 const handleCancel = () => { 237 const handleCancel = () => {
182 destory(); 238 destory();
183 }; 239 };
  240 +
  241 + const switchMode = (flag: Mode) => {
  242 + mode.value = flag;
  243 + };
184 </script> 244 </script>
185 245
186 <template> 246 <template>
@@ -201,14 +261,65 @@ @@ -201,14 +261,65 @@
201 <BasicForm @register="register" /> 261 <BasicForm @register="register" />
202 </section> 262 </section>
203 <section class="bg-white p-3" style="min-height: 350px"> 263 <section class="bg-white p-3" style="min-height: 350px">
204 - <div v-show="isNull" ref="chartRef" :style="{ height: '350px', width: '100%' }"> 264 + <div v-show="mode === Mode.CHART" class="flex h-70px items-center justify-end p-2">
  265 + <Tooltip title="图表模式">
  266 + <Button
  267 + :class="[mode === Mode.CHART && '!bg-blue-500 svg:text-light-50']"
  268 + class="!p-2 !children:flex flex justify-center items-center border-r-0"
  269 + @click="switchMode(Mode.CHART)"
  270 + >
  271 + <LineChartOutlined />
  272 + </Button>
  273 + </Tooltip>
  274 +
  275 + <Tooltip title="列表模式">
  276 + <Button
  277 + class="!p-2 !children:flex flex justify-center items-center"
  278 + @click="switchMode(Mode.TABLE)"
  279 + >
  280 + <BarsOutlined />
  281 + </Button>
  282 + </Tooltip>
  283 + </div>
  284 +
  285 + <div
  286 + v-show="isNull && mode === Mode.CHART"
  287 + ref="chartRef"
  288 + :style="{ height: '350px', width: '100%' }"
  289 + >
205 <Loading :loading="loading" :absolute="true" /> 290 <Loading :loading="loading" :absolute="true" />
206 </div> 291 </div>
207 <Empty 292 <Empty
  293 + v-if="mode === Mode.CHART"
208 class="h-350px flex flex-col justify-center items-center" 294 class="h-350px flex flex-col justify-center items-center"
209 description="暂无数据,请选择设备查询" 295 description="暂无数据,请选择设备查询"
210 v-show="!isNull" 296 v-show="!isNull"
211 /> 297 />
  298 +
  299 + <BasicTable v-show="mode === Mode.TABLE" @register="registerTable">
  300 + <template #toolbar>
  301 + <div v-show="mode === Mode.TABLE" class="flex h-70px items-center justify-end p-2">
  302 + <Tooltip title="图表模式">
  303 + <Button
  304 + class="!p-2 !children:flex flex justify-center items-center border-r-0"
  305 + @click="switchMode(Mode.CHART)"
  306 + >
  307 + <LineChartOutlined />
  308 + </Button>
  309 + </Tooltip>
  310 +
  311 + <Tooltip title="列表模式">
  312 + <Button
  313 + :class="[mode === Mode.TABLE && '!bg-blue-500 svg:text-light-50']"
  314 + class="!p-2 !children:flex flex justify-center items-center"
  315 + @click="switchMode(Mode.TABLE)"
  316 + >
  317 + <BarsOutlined />
  318 + </Button>
  319 + </Tooltip>
  320 + </div>
  321 + </template>
  322 + </BasicTable>
212 </section> 323 </section>
213 </section> 324 </section>
214 </BasicModal> 325 </BasicModal>
@@ -143,7 +143,7 @@ @@ -143,7 +143,7 @@
143 }; 143 };
144 144
145 const handleOpenDetailModal = () => { 145 const handleOpenDetailModal = () => {
146 - openModal(); 146 + openModal(true, { isEdit: false });
147 }; 147 };
148 148
149 const handleRemove = async (record: DataBoardRecord) => { 149 const handleRemove = async (record: DataBoardRecord) => {