|
@@ -13,8 +13,7 @@ |
|
@@ -13,8 +13,7 @@ |
13
|
import HistoryData from './HistoryData.vue';
|
13
|
import HistoryData from './HistoryData.vue';
|
14
|
import { BasicModal, useModal } from '/@/components/Modal';
|
14
|
import { BasicModal, useModal } from '/@/components/Modal';
|
15
|
import { getDeviceAttrs } from '/@/api/device/deviceManager';
|
15
|
import { getDeviceAttrs } from '/@/api/device/deviceManager';
|
16
|
- import { DeviceModelOfMatterAttrs, DeviceRecord } from '/@/api/device/model/deviceModel';
|
|
|
17
|
- import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
|
16
|
+ import { DeviceRecord } from '/@/api/device/model/deviceModel';
|
18
|
import { isArray, isNull, isObject } from '/@/utils/is';
|
17
|
import { isArray, isNull, isObject } from '/@/utils/is';
|
19
|
import { useGlobSetting } from '/@/hooks/setting';
|
18
|
import { useGlobSetting } from '/@/hooks/setting';
|
20
|
import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget';
|
19
|
import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget';
|
|
@@ -23,8 +22,14 @@ |
|
@@ -23,8 +22,14 @@ |
23
|
import { ObjectModelCommandDeliveryModal } from './ObjectModelCommandDeliveryModal';
|
22
|
import { ObjectModelCommandDeliveryModal } from './ObjectModelCommandDeliveryModal';
|
24
|
import { ModalParamsType } from '/#/utils';
|
23
|
import { ModalParamsType } from '/#/utils';
|
25
|
import { AreaChartOutlined } from '@ant-design/icons-vue';
|
24
|
import { AreaChartOutlined } from '@ant-design/icons-vue';
|
26
|
- import { SvgIcon } from '/@/components/Icon';
|
25
|
+ import { SvgIcon, Icon } from '/@/components/Icon';
|
27
|
import { DataTypeEnum } from '/@/enums/objectModelEnum';
|
26
|
import { DataTypeEnum } from '/@/enums/objectModelEnum';
|
|
|
27
|
+ import {
|
|
|
28
|
+ buildTableDataSourceByObjectModel,
|
|
|
29
|
+ SocketInfoDataSourceItemType,
|
|
|
30
|
+ StructValueItemType,
|
|
|
31
|
+ } from './ModelOfMatter.config';
|
|
|
32
|
+ import { useJsonParse } from '/@/hooks/business/useJsonParse';
|
28
|
|
33
|
|
29
|
interface ReceiveMessage {
|
34
|
interface ReceiveMessage {
|
30
|
data: {
|
35
|
data: {
|
|
@@ -32,17 +37,6 @@ |
|
@@ -32,17 +37,6 @@ |
32
|
};
|
37
|
};
|
33
|
}
|
38
|
}
|
34
|
|
39
|
|
35
|
- interface DataSource {
|
|
|
36
|
- key?: string;
|
|
|
37
|
- value?: string;
|
|
|
38
|
- time?: number;
|
|
|
39
|
- type?: string;
|
|
|
40
|
- boolClose?: string;
|
|
|
41
|
- boolOpen?: string;
|
|
|
42
|
- name?: string;
|
|
|
43
|
- detail: DeviceModelOfMatterAttrs;
|
|
|
44
|
- }
|
|
|
45
|
-
|
|
|
46
|
const props = defineProps<{
|
40
|
const props = defineProps<{
|
47
|
deviceDetail: DeviceRecord;
|
41
|
deviceDetail: DeviceRecord;
|
48
|
}>();
|
42
|
}>();
|
|
@@ -70,13 +64,14 @@ |
|
@@ -70,13 +64,14 @@ |
70
|
cmdId: 0,
|
64
|
cmdId: 0,
|
71
|
origin: `${socketUrl}${token}`,
|
65
|
origin: `${socketUrl}${token}`,
|
72
|
attr: undefined as string | undefined,
|
66
|
attr: undefined as string | undefined,
|
73
|
- dataSource: [] as DataSource[],
|
67
|
+ dataSource: [] as SocketInfoDataSourceItemType[],
|
|
|
68
|
+ rawDataSource: [] as SocketInfoDataSourceItemType[],
|
74
|
message: {} as ReceiveMessage['data'],
|
69
|
message: {} as ReceiveMessage['data'],
|
75
|
- attrKeys: [] as DeviceModelOfMatterAttrs[],
|
|
|
76
|
- filterAttrKeys: [] as DeviceModelOfMatterAttrs[],
|
70
|
+ attrKeys: [] as string[],
|
|
|
71
|
+ filterAttrKeys: [] as string[],
|
77
|
});
|
72
|
});
|
78
|
|
73
|
|
79
|
- const getPaginationAttrkey = computed<DeviceModelOfMatterAttrs[]>(() => {
|
74
|
+ const getPaginationAttrkey = computed(() => {
|
80
|
const { current = 1, pageSize = 10 } = pagination;
|
75
|
const { current = 1, pageSize = 10 } = pagination;
|
81
|
return (
|
76
|
return (
|
82
|
socketInfo.filterAttrKeys && socketInfo.filterAttrKeys.length
|
77
|
socketInfo.filterAttrKeys && socketInfo.filterAttrKeys.length
|
|
@@ -104,9 +99,7 @@ |
|
@@ -104,9 +99,7 @@ |
104
|
entityId: props.deviceDetail!.tbDeviceId,
|
99
|
entityId: props.deviceDetail!.tbDeviceId,
|
105
|
scope: 'LATEST_TELEMETRY',
|
100
|
scope: 'LATEST_TELEMETRY',
|
106
|
cmdId: socketInfo.cmdId,
|
101
|
cmdId: socketInfo.cmdId,
|
107
|
- keys: unref(getPaginationAttrkey)
|
|
|
108
|
- .map((item) => item.identifier)
|
|
|
109
|
- .join(','),
|
102
|
+ keys: unref(getPaginationAttrkey).join(','),
|
110
|
},
|
103
|
},
|
111
|
],
|
104
|
],
|
112
|
};
|
105
|
};
|
|
@@ -120,9 +113,7 @@ |
|
@@ -120,9 +113,7 @@ |
120
|
entityId: props.deviceDetail!.tbDeviceId,
|
113
|
entityId: props.deviceDetail!.tbDeviceId,
|
121
|
scope: 'LATEST_TELEMETRY',
|
114
|
scope: 'LATEST_TELEMETRY',
|
122
|
cmdId: socketInfo.cmdId,
|
115
|
cmdId: socketInfo.cmdId,
|
123
|
- keys: unref(getPaginationAttrkey)
|
|
|
124
|
- .map((item) => item.identifier)
|
|
|
125
|
- .join(','),
|
116
|
+ keys: unref(getPaginationAttrkey).join(','),
|
126
|
},
|
117
|
},
|
127
|
],
|
118
|
],
|
128
|
};
|
119
|
};
|
|
@@ -150,12 +141,14 @@ |
|
@@ -150,12 +141,14 @@ |
150
|
pagination.current = 1;
|
141
|
pagination.current = 1;
|
151
|
|
142
|
|
152
|
socketInfo.filterAttrKeys = value
|
143
|
socketInfo.filterAttrKeys = value
|
153
|
- ? unref(socketInfo.attrKeys).filter(
|
|
|
154
|
- (item) =>
|
|
|
155
|
- item.identifier?.toUpperCase().includes(value.toUpperCase()) ||
|
|
|
156
|
- item.name?.toUpperCase().includes(value.toUpperCase())
|
|
|
157
|
- )
|
|
|
158
|
- : socketInfo.attrKeys;
|
144
|
+ ? unref(socketInfo.rawDataSource)
|
|
|
145
|
+ .filter(
|
|
|
146
|
+ (item) =>
|
|
|
147
|
+ item.key?.toUpperCase().includes(value.toUpperCase()) ||
|
|
|
148
|
+ item.name?.toUpperCase().includes(value.toUpperCase())
|
|
|
149
|
+ )
|
|
|
150
|
+ .map((item) => item.key)
|
|
|
151
|
+ : socketInfo.rawDataSource.map((item) => item.key);
|
159
|
|
152
|
|
160
|
await nextTick();
|
153
|
await nextTick();
|
161
|
|
154
|
|
|
@@ -206,58 +199,10 @@ |
|
@@ -206,58 +199,10 @@ |
206
|
|
199
|
|
207
|
const { createMessage } = useMessage();
|
200
|
const { createMessage } = useMessage();
|
208
|
|
201
|
|
209
|
- const getUnit = (record: StructJSON) => {
|
|
|
210
|
- const { dataType } = record;
|
|
|
211
|
- const { specs, type } = dataType! || {};
|
|
|
212
|
- const unitName = isObject(specs)
|
|
|
213
|
- ? (specs as Specs).unitName && (specs as Specs).unit
|
|
|
214
|
- ? `${(specs as Specs).unitName}`
|
|
|
215
|
- : ''
|
|
|
216
|
- : '';
|
|
|
217
|
- const { boolClose, boolOpen } = (specs || {}) as Specs;
|
|
|
218
|
-
|
|
|
219
|
- return { unit: unitName, boolClose, boolOpen, type };
|
|
|
220
|
- };
|
|
|
221
|
-
|
|
|
222
|
- const isStructAndTextType = (type: DataTypeEnum) => {
|
|
|
223
|
- return [DataTypeEnum.STRUCT, DataTypeEnum.STRING].includes(type);
|
|
|
224
|
- };
|
|
|
225
|
-
|
|
|
226
|
const setDataSource = () => {
|
202
|
const setDataSource = () => {
|
227
|
- socketInfo.dataSource = (
|
|
|
228
|
- socketInfo.filterAttrKeys && socketInfo.filterAttrKeys.length
|
|
|
229
|
- ? socketInfo.filterAttrKeys
|
|
|
230
|
- : socketInfo.attrKeys
|
|
|
231
|
- ).map((item) => {
|
|
|
232
|
- const { identifier: key, name, detail, accessMode } = item;
|
|
|
233
|
- const { unit, boolClose, boolOpen, type } = getUnit(detail);
|
|
|
234
|
- const dataInfo = socketInfo.filterAttrKeys.find((item) => item.identifier === key);
|
|
|
235
|
-
|
|
|
236
|
- let time: number | undefined;
|
|
|
237
|
- let value: any | undefined;
|
|
|
238
|
- const message = socketInfo.message[key];
|
|
|
239
|
- if (message) {
|
|
|
240
|
- const [attrTime, attrValue] = message.at(0) || [];
|
|
|
241
|
- time = attrTime;
|
|
|
242
|
- value = attrValue;
|
|
|
243
|
- }
|
|
|
244
|
-
|
|
|
245
|
- return {
|
|
|
246
|
- key,
|
|
|
247
|
- value,
|
|
|
248
|
- time,
|
|
|
249
|
- name,
|
|
|
250
|
- unit,
|
|
|
251
|
- type,
|
|
|
252
|
- boolClose,
|
|
|
253
|
- boolOpen,
|
|
|
254
|
- accessMode,
|
|
|
255
|
- showHistoryDataButton: !isStructAndTextType(
|
|
|
256
|
- dataInfo?.detail.dataType?.type as unknown as DataTypeEnum
|
|
|
257
|
- ),
|
|
|
258
|
- detail: toRaw(item),
|
|
|
259
|
- };
|
|
|
260
|
- });
|
203
|
+ socketInfo.dataSource = socketInfo.filterAttrKeys.length
|
|
|
204
|
+ ? socketInfo.rawDataSource.filter((item) => socketInfo.filterAttrKeys.includes(item.key))
|
|
|
205
|
+ : socketInfo.rawDataSource;
|
261
|
};
|
206
|
};
|
262
|
|
207
|
|
263
|
const { send, close, data, open } = useWebSocket(socketInfo.origin, {
|
208
|
const { send, close, data, open } = useWebSocket(socketInfo.origin, {
|
|
@@ -272,9 +217,36 @@ |
|
@@ -272,9 +217,36 @@ |
272
|
if (value) {
|
217
|
if (value) {
|
273
|
const { data } = value;
|
218
|
const { data } = value;
|
274
|
const keys = Object.keys(data);
|
219
|
const keys = Object.keys(data);
|
275
|
- keys.forEach((key) => {
|
|
|
276
|
- socketInfo.message[key] = value.data[key];
|
|
|
277
|
- });
|
220
|
+
|
|
|
221
|
+ for (const key of keys) {
|
|
|
222
|
+ const item = socketInfo.rawDataSource.find((item) => item.key === key);
|
|
|
223
|
+ if (!item) continue;
|
|
|
224
|
+ const { type } = item;
|
|
|
225
|
+ const [firstItem] = data[key] || [];
|
|
|
226
|
+ const [time, value] = firstItem;
|
|
|
227
|
+
|
|
|
228
|
+ item.rawValue = value;
|
|
|
229
|
+
|
|
|
230
|
+ if (type === DataTypeEnum.STRUCT) {
|
|
|
231
|
+ const { flag, value: structJSON } = useJsonParse(value);
|
|
|
232
|
+ if (!flag || !isObject(item.value)) continue;
|
|
|
233
|
+
|
|
|
234
|
+ const structKeys = Object.keys(structJSON);
|
|
|
235
|
+
|
|
|
236
|
+ structKeys.forEach((key) => {
|
|
|
237
|
+ if ((item.value as Record<string, StructValueItemType>)?.[key]) {
|
|
|
238
|
+ (item.value as Record<string, StructValueItemType>)[key].value = structJSON[key];
|
|
|
239
|
+ }
|
|
|
240
|
+ });
|
|
|
241
|
+
|
|
|
242
|
+ item.time = time;
|
|
|
243
|
+
|
|
|
244
|
+ continue;
|
|
|
245
|
+ }
|
|
|
246
|
+
|
|
|
247
|
+ item.value = value;
|
|
|
248
|
+ item.time = time;
|
|
|
249
|
+ }
|
278
|
|
250
|
|
279
|
setDataSource();
|
251
|
setDataSource();
|
280
|
|
252
|
|
|
@@ -296,7 +268,7 @@ |
|
@@ -296,7 +268,7 @@ |
296
|
setTableData(socketInfo.dataSource.filter((item) => !isNull(item.value)));
|
268
|
setTableData(socketInfo.dataSource.filter((item) => !isNull(item.value)));
|
297
|
}
|
269
|
}
|
298
|
|
270
|
|
299
|
- const handleShowDetail = (record: DataSource) => {
|
271
|
+ const handleShowDetail = (record: SocketInfoDataSourceItemType) => {
|
300
|
const { key } = record;
|
272
|
const { key } = record;
|
301
|
socketInfo.attr = key;
|
273
|
socketInfo.attr = key;
|
302
|
openModal(true);
|
274
|
openModal(true);
|
|
@@ -305,23 +277,24 @@ |
|
@@ -305,23 +277,24 @@ |
305
|
onMounted(async () => {
|
277
|
onMounted(async () => {
|
306
|
const { deviceProfileId } = props.deviceDetail;
|
278
|
const { deviceProfileId } = props.deviceDetail;
|
307
|
const value = await getDeviceAttrs({ deviceProfileId });
|
279
|
const value = await getDeviceAttrs({ deviceProfileId });
|
308
|
- socketInfo.attrKeys = isArray(value) ? value : [];
|
280
|
+ socketInfo.attrKeys = isArray(value) ? value.map((item) => item.identifier) : [];
|
|
|
281
|
+ socketInfo.rawDataSource = buildTableDataSourceByObjectModel(value);
|
309
|
setDataSource();
|
282
|
setDataSource();
|
310
|
open();
|
283
|
open();
|
311
|
});
|
284
|
});
|
312
|
|
285
|
|
313
|
- const formatValue = (item: DataSource) => {
|
286
|
+ const formatValue = (item: SocketInfoDataSourceItemType) => {
|
314
|
return item.type === DataTypeEnum.BOOL
|
287
|
return item.type === DataTypeEnum.BOOL
|
315
|
? !isNull(item.value)
|
288
|
? !isNull(item.value)
|
316
|
? !!Number(item.value)
|
289
|
? !!Number(item.value)
|
317
|
? item.boolOpen
|
290
|
? item.boolOpen
|
318
|
: item.boolClose
|
291
|
: item.boolClose
|
319
|
: '--'
|
292
|
: '--'
|
320
|
- : item.value || '--';
|
293
|
+ : (item.value as string) || '--';
|
321
|
};
|
294
|
};
|
322
|
|
295
|
|
323
|
const [register, { openModal: openSendCommandModal }] = useModal();
|
296
|
const [register, { openModal: openSendCommandModal }] = useModal();
|
324
|
- const handleSendCommandModal = (data: DataSource) => {
|
297
|
+ const handleSendCommandModal = (data: SocketInfoDataSourceItemType) => {
|
325
|
openSendCommandModal(true, {
|
298
|
openSendCommandModal(true, {
|
326
|
mode: DataActionModeEnum.READ,
|
299
|
mode: DataActionModeEnum.READ,
|
327
|
record: { ...toRaw(data.detail), deviceDetail: props.deviceDetail as any },
|
300
|
record: { ...toRaw(data.detail), deviceDetail: props.deviceDetail as any },
|
|
@@ -369,7 +342,7 @@ |
|
@@ -369,7 +342,7 @@ |
369
|
:grid="grid"
|
342
|
:grid="grid"
|
370
|
:pagination="pagination"
|
343
|
:pagination="pagination"
|
371
|
>
|
344
|
>
|
372
|
- <template #renderItem="{ item }">
|
345
|
+ <template #renderItem="{ item }: { item: SocketInfoDataSourceItemType }">
|
373
|
<List.Item>
|
346
|
<List.Item>
|
374
|
<Card class="shadow-md">
|
347
|
<Card class="shadow-md">
|
375
|
<template #title>
|
348
|
<template #title>
|
|
@@ -377,6 +350,16 @@ |
|
@@ -377,6 +350,16 @@ |
377
|
</template>
|
350
|
</template>
|
378
|
<template #extra>
|
351
|
<template #extra>
|
379
|
<Space>
|
352
|
<Space>
|
|
|
353
|
+ <Tooltip
|
|
|
354
|
+ :title="item.expand ? '收起' : '展开'"
|
|
|
355
|
+ v-if="item.type === DataTypeEnum.STRUCT"
|
|
|
356
|
+ >
|
|
|
357
|
+ <Icon
|
|
|
358
|
+ :icon="item.expand ? 'ant-design:up-outlined' : 'ant-design:down-outlined'"
|
|
|
359
|
+ class="cursor-pointer svg:text-blue-500"
|
|
|
360
|
+ @click="item.expand = !item.expand"
|
|
|
361
|
+ />
|
|
|
362
|
+ </Tooltip>
|
380
|
<Tooltip title="属性下发">
|
363
|
<Tooltip title="属性下发">
|
381
|
<SvgIcon
|
364
|
<SvgIcon
|
382
|
name="send-command"
|
365
|
name="send-command"
|
|
@@ -396,14 +379,49 @@ |
|
@@ -396,14 +379,49 @@ |
396
|
</Space>
|
379
|
</Space>
|
397
|
</template>
|
380
|
</template>
|
398
|
<section class="min-h-16 flex flex-col justify-between">
|
381
|
<section class="min-h-16 flex flex-col justify-between">
|
399
|
- <div class="flex font-bold text-lg mb-4 gap-2">
|
382
|
+ <div
|
|
|
383
|
+ v-if="item.type !== DataTypeEnum.STRUCT"
|
|
|
384
|
+ class="flex font-bold text-lg mb-4 gap-2"
|
|
|
385
|
+ >
|
400
|
<Tooltip :title="formatValue(item)" placement="topLeft">
|
386
|
<Tooltip :title="formatValue(item)" placement="topLeft">
|
401
|
<div class="truncate">{{ formatValue(item) }}</div>
|
387
|
<div class="truncate">{{ formatValue(item) }}</div>
|
402
|
</Tooltip>
|
388
|
</Tooltip>
|
403
|
- <div class="text-xs flex items-center">{{ item.unit }}</div>
|
389
|
+ <div class="text-xs flex items-center">{{ item.unitName }}</div>
|
|
|
390
|
+ </div>
|
|
|
391
|
+ <div
|
|
|
392
|
+ class="mb-4 overflow-hidden flex flex-col gap-2 relative"
|
|
|
393
|
+ v-if="item.type == DataTypeEnum.STRUCT && isObject(item.value)"
|
|
|
394
|
+ :style="{ height: item.expand ? 'fit-content' : '28px' }"
|
|
|
395
|
+ >
|
|
|
396
|
+ <div
|
|
|
397
|
+ v-for="key in Object.keys(item.value)"
|
|
|
398
|
+ :key="key"
|
|
|
399
|
+ class="cursor-pointer flex justify-between items-center gap-2"
|
|
|
400
|
+ >
|
|
|
401
|
+ <div class="font-medium truncate">{{ item.value[key].name }}</div>
|
|
|
402
|
+ <div class="flex-auto font-bold text-lg text-left ml-4 truncate">
|
|
|
403
|
+ <Tooltip :title="formatValue(item)" placement="topLeft">
|
|
|
404
|
+ <span>
|
|
|
405
|
+ {{ formatValue(item.value[key] as SocketInfoDataSourceItemType) }}
|
|
|
406
|
+ </span>
|
|
|
407
|
+ </Tooltip>
|
|
|
408
|
+ <span class="text-xs font-normal ml-2">
|
|
|
409
|
+ {{ item.value[key].unitName }}
|
|
|
410
|
+ </span>
|
|
|
411
|
+ </div>
|
|
|
412
|
+ </div>
|
|
|
413
|
+ <Tooltip title="点击展开">
|
|
|
414
|
+ <div
|
|
|
415
|
+ v-show="!item.expand"
|
|
|
416
|
+ class="absolute top-2 right-0 text-blue-400 cursor-pointer text-xs"
|
|
|
417
|
+ @click="item.expand = !item.expand"
|
|
|
418
|
+ >
|
|
|
419
|
+ 更多
|
|
|
420
|
+ </div>
|
|
|
421
|
+ </Tooltip>
|
404
|
</div>
|
422
|
</div>
|
405
|
<div class="text-dark-800 text-xs">
|
423
|
<div class="text-dark-800 text-xs">
|
406
|
- {{ item.value ? formatToDateTime(item.time, 'YYYY-MM-DD HH:mm:ss') : '--' }}
|
424
|
+ {{ item.time ? formatToDateTime(item.time, 'YYYY-MM-DD HH:mm:ss') : '--' }}
|
407
|
</div>
|
425
|
</div>
|
408
|
</section>
|
426
|
</section>
|
409
|
</Card>
|
427
|
</Card>
|
|
@@ -461,6 +479,26 @@ |
|
@@ -461,6 +479,26 @@ |
461
|
.device-things-model-table-mode:deep(.ant-table-placeholder) {
|
479
|
.device-things-model-table-mode:deep(.ant-table-placeholder) {
|
462
|
height: auto;
|
480
|
height: auto;
|
463
|
}
|
481
|
}
|
|
|
482
|
+
|
|
|
483
|
+ .model-collapse {
|
|
|
484
|
+ border: none;
|
|
|
485
|
+
|
|
|
486
|
+ :deep(.ant-collapse-header) {
|
|
|
487
|
+ display: none;
|
|
|
488
|
+ }
|
|
|
489
|
+
|
|
|
490
|
+ :deep(.ant-collapse-item) {
|
|
|
491
|
+ border: none;
|
|
|
492
|
+ }
|
|
|
493
|
+
|
|
|
494
|
+ :deep(.ant-collapse-content) {
|
|
|
495
|
+ border: none;
|
|
|
496
|
+ }
|
|
|
497
|
+
|
|
|
498
|
+ :deep(.ant-collapse-content-box) {
|
|
|
499
|
+ padding: 0;
|
|
|
500
|
+ }
|
|
|
501
|
+ }
|
464
|
</style>
|
502
|
</style>
|
465
|
|
503
|
|
466
|
<style>
|
504
|
<style>
|