Commit aa402d4f5d01a718fb6e555a938e26a4544d5f9c

Authored by ww
1 parent 97fdd4f3

perf: histroy data all add table mode && chart mode

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 import { DeviceTypeEnum } from '../../device/model/deviceModel';
  3 +import { DataType } from '../../device/model/modelOfMatterModel';
3 4
4 export interface AddDataBoardParams { 5 export interface AddDataBoardParams {
5 name: string; 6 name: string;
@@ -149,8 +150,6 @@ export interface ComponentInfoDetail { @@ -149,8 +150,6 @@ export interface ComponentInfoDetail {
149 data: { componentData: DataComponentRecord[]; componentLayout: Layout[] }; 150 data: { componentData: DataComponentRecord[]; componentLayout: Layout[] };
150 } 151 }
151 152
152 -export type DataType = 'BOOL' | 'DOUBLE' | 'INT' | 'STRUCT' | 'TExT';  
153 -  
154 export interface DeviceAttributeParams { 153 export interface DeviceAttributeParams {
155 deviceProfileId: string; 154 deviceProfileId: string;
156 dataType?: DataType; 155 dataType?: DataType;
@@ -159,7 +158,7 @@ export interface DeviceAttributeParams { @@ -159,7 +158,7 @@ export interface DeviceAttributeParams {
159 export interface DeviceAttributeRecord { 158 export interface DeviceAttributeRecord {
160 name: string; 159 name: string;
161 identifier: string; 160 identifier: string;
162 - detail: DataType; 161 + detail: { dataType: DataType };
163 } 162 }
164 163
165 export interface SendCommandParams { 164 export interface SendCommandParams {
1 -import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; 1 +import {
  2 + DataTypeEnum,
  3 + FunctionType,
  4 +} from '/@/views/device/profiles/step/cpns/physical/cpns/config';
2 5
3 export interface Specs { 6 export interface Specs {
4 min: string; 7 min: string;
@@ -16,7 +19,7 @@ export interface Specs { @@ -16,7 +19,7 @@ export interface Specs {
16 } 19 }
17 20
18 export interface DataType { 21 export interface DataType {
19 - type: string; 22 + type: DataTypeEnum;
20 specs?: Partial<Specs> | StructJSON[]; 23 specs?: Partial<Specs> | StructJSON[];
21 } 24 }
22 25
@@ -3,7 +3,7 @@ import { findDictItemByCode } from '/@/api/system/dict'; @@ -3,7 +3,7 @@ import { findDictItemByCode } from '/@/api/system/dict';
3 import { FormSchema } from '/@/components/Table'; 3 import { FormSchema } from '/@/components/Table';
4 import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; 4 import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
5 5
6 -export enum DateTypeEnum { 6 +export enum DataTypeEnum {
7 IS_NUMBER_INT = 'INT', 7 IS_NUMBER_INT = 'INT',
8 IS_NUMBER_DOUBLE = 'DOUBLE', 8 IS_NUMBER_DOUBLE = 'DOUBLE',
9 IS_STRING = 'TEXT', 9 IS_STRING = 'TEXT',
@@ -84,8 +84,8 @@ export const formSchemas: FormSchema[] = [ @@ -84,8 +84,8 @@ export const formSchemas: FormSchema[] = [
84 span: 18, 84 span: 18,
85 }, 85 },
86 ifShow: ({ values }) => 86 ifShow: ({ values }) =>
87 - values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_INT ||  
88 - values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_DOUBLE, 87 + values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT ||
  88 + values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE,
89 rules: [{ validator: validateValueRange }], 89 rules: [{ validator: validateValueRange }],
90 }, 90 },
91 { 91 {
@@ -104,8 +104,8 @@ export const formSchemas: FormSchema[] = [ @@ -104,8 +104,8 @@ export const formSchemas: FormSchema[] = [
104 }, 104 },
105 }, 105 },
106 ifShow: ({ values }) => 106 ifShow: ({ values }) =>
107 - values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_INT ||  
108 - values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_DOUBLE, 107 + values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT ||
  108 + values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE,
109 dynamicRules: ({ model }) => { 109 dynamicRules: ({ model }) => {
110 const valueRange = model[FormField.VALUE_RANGE] || {}; 110 const valueRange = model[FormField.VALUE_RANGE] || {};
111 const { min = 0, max = 0 } = valueRange; 111 const { min = 0, max = 0 } = valueRange;
@@ -166,8 +166,8 @@ export const formSchemas: FormSchema[] = [ @@ -166,8 +166,8 @@ export const formSchemas: FormSchema[] = [
166 }; 166 };
167 }, 167 },
168 ifShow: ({ values }) => 168 ifShow: ({ values }) =>
169 - values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_INT ||  
170 - values[FormField.TYPE] === DateTypeEnum.IS_NUMBER_DOUBLE, 169 + values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_INT ||
  170 + values[FormField.TYPE] === DataTypeEnum.IS_NUMBER_DOUBLE,
171 }, 171 },
172 { 172 {
173 field: FormField.BOOL_CLOSE, 173 field: FormField.BOOL_CLOSE,
@@ -180,7 +180,7 @@ export const formSchemas: FormSchema[] = [ @@ -180,7 +180,7 @@ export const formSchemas: FormSchema[] = [
180 componentProps: { 180 componentProps: {
181 placeholder: '如:关', 181 placeholder: '如:关',
182 }, 182 },
183 - ifShow: ({ values }) => values[FormField.TYPE] === DateTypeEnum.IS_BOOL, 183 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_BOOL,
184 dynamicRules: ({ model }) => { 184 dynamicRules: ({ model }) => {
185 const close = model[FormField.BOOL_CLOSE]; 185 const close = model[FormField.BOOL_CLOSE];
186 const open = model[FormField.BOOL_OPEN]; 186 const open = model[FormField.BOOL_OPEN];
@@ -205,7 +205,7 @@ export const formSchemas: FormSchema[] = [ @@ -205,7 +205,7 @@ export const formSchemas: FormSchema[] = [
205 componentProps: { 205 componentProps: {
206 placeholder: '如:开', 206 placeholder: '如:开',
207 }, 207 },
208 - ifShow: ({ values }) => values[FormField.TYPE] === DateTypeEnum.IS_BOOL, 208 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_BOOL,
209 dynamicRules: ({ model }) => { 209 dynamicRules: ({ model }) => {
210 const close = model[FormField.BOOL_CLOSE]; 210 const close = model[FormField.BOOL_CLOSE];
211 const open = model[FormField.BOOL_OPEN]; 211 const open = model[FormField.BOOL_OPEN];
@@ -236,7 +236,7 @@ export const formSchemas: FormSchema[] = [ @@ -236,7 +236,7 @@ export const formSchemas: FormSchema[] = [
236 suffix: () => '字节', 236 suffix: () => '字节',
237 }; 237 };
238 }, 238 },
239 - ifShow: ({ values }) => values[FormField.TYPE] === DateTypeEnum.IS_STRING, 239 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_STRING,
240 }, 240 },
241 { 241 {
242 field: FormField.ACCESS_MODE, 242 field: FormField.ACCESS_MODE,
@@ -264,7 +264,7 @@ export const formSchemas: FormSchema[] = [ @@ -264,7 +264,7 @@ export const formSchemas: FormSchema[] = [
264 valueField: 'value', 264 valueField: 'value',
265 changeEvent: 'update:value', 265 changeEvent: 'update:value',
266 colProps: { span: 24 }, 266 colProps: { span: 24 },
267 - ifShow: ({ values }) => values[FormField.TYPE] === DateTypeEnum.IS_STRUCT, 267 + ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.IS_STRUCT,
268 rules: [{ required: true, validator: validateJSON }], 268 rules: [{ required: true, validator: validateJSON }],
269 }, 269 },
270 { 270 {
1 -import { DateTypeEnum } from './config'; 1 +import { DataTypeEnum } from './config';
2 import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; 2 import { StructJSON } from '/@/api/device/model/modelOfMatterModel';
3 import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; 3 import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config';
4 4
@@ -14,7 +14,7 @@ export interface OpenModalParams { @@ -14,7 +14,7 @@ export interface OpenModalParams {
14 14
15 export interface StructFormValue 15 export interface StructFormValue
16 extends Partial<Record<Exclude<FormField, FormField.VALUE_RANGE | FormField.STRUCT>, string>> { 16 extends Partial<Record<Exclude<FormField, FormField.VALUE_RANGE | FormField.STRUCT>, string>> {
17 - [FormField.TYPE]: DateTypeEnum; 17 + [FormField.TYPE]: DataTypeEnum;
18 [FormField.VALUE_RANGE]?: { 18 [FormField.VALUE_RANGE]?: {
19 [FormField.MIN]: string; 19 [FormField.MIN]: string;
20 [FormField.MAX]: string; 20 [FormField.MAX]: string;
1 import { cloneDeep } from 'lodash-es'; 1 import { cloneDeep } from 'lodash-es';
2 -import { DateTypeEnum } from './config'; 2 +import { DataTypeEnum } from './config';
3 import { StructFormValue } from './type'; 3 import { StructFormValue } from './type';
4 import { DataType, ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 4 import { DataType, ModelOfMatterParams, StructJSON } from '/@/api/device/model/modelOfMatterModel';
5 import { isArray } from '/@/utils/is'; 5 import { isArray } from '/@/utils/is';
@@ -24,21 +24,21 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON { @@ -24,21 +24,21 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON {
24 let dataType = {} as unknown as DataType; 24 let dataType = {} as unknown as DataType;
25 25
26 switch (type) { 26 switch (type) {
27 - case DateTypeEnum.IS_NUMBER_INT: 27 + case DataTypeEnum.IS_NUMBER_INT:
28 dataType = { 28 dataType = {
29 type, 29 type,
30 specs: { valueRange, step, unit, unitName }, 30 specs: { valueRange, step, unit, unitName },
31 }; 31 };
32 break; 32 break;
33 33
34 - case DateTypeEnum.IS_NUMBER_DOUBLE: 34 + case DataTypeEnum.IS_NUMBER_DOUBLE:
35 dataType = { 35 dataType = {
36 type, 36 type,
37 specs: { valueRange, step, unit, unitName }, 37 specs: { valueRange, step, unit, unitName },
38 }; 38 };
39 break; 39 break;
40 40
41 - case DateTypeEnum.IS_BOOL: 41 + case DataTypeEnum.IS_BOOL:
42 dataType = { 42 dataType = {
43 type, 43 type,
44 specs: { 44 specs: {
@@ -48,14 +48,14 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON { @@ -48,14 +48,14 @@ export function transfromToStructJSON(value: StructFormValue): StructJSON {
48 }; 48 };
49 break; 49 break;
50 50
51 - case DateTypeEnum.IS_STRING: 51 + case DataTypeEnum.IS_STRING:
52 dataType = { 52 dataType = {
53 type, 53 type,
54 specs: { length }, 54 specs: { length },
55 }; 55 };
56 break; 56 break;
57 57
58 - case DateTypeEnum.IS_STRUCT: 58 + case DataTypeEnum.IS_STRUCT:
59 dataType = { 59 dataType = {
60 type, 60 type,
61 specs: specs! as unknown as ModelOfMatterParams[], 61 specs: specs! as unknown as ModelOfMatterParams[],
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { nextTick, onMounted, onUnmounted, Ref, ref, unref } from 'vue'; 2 + import { computed, nextTick, onMounted, onUnmounted, Ref, ref, unref } from 'vue';
3 import { getDeviceHistoryInfo } from '/@/api/alarm/position'; 3 import { getDeviceHistoryInfo } from '/@/api/alarm/position';
4 - import { Empty, Spin } from 'ant-design-vue'; 4 + import { Empty, Spin, Tooltip, Button } from 'ant-design-vue';
5 import { useECharts } from '/@/hooks/web/useECharts'; 5 import { useECharts } from '/@/hooks/web/useECharts';
6 import { AggregateDataEnum, selectDeviceAttrSchema } from '/@/views/device/localtion/config.data'; 6 import { AggregateDataEnum, selectDeviceAttrSchema } from '/@/views/device/localtion/config.data';
7 import { useTimePeriodForm } from '/@/views/device/localtion/cpns/TimePeriodForm'; 7 import { useTimePeriodForm } from '/@/views/device/localtion/cpns/TimePeriodForm';
@@ -11,6 +11,10 @@ @@ -11,6 +11,10 @@
11 import { useGridLayout } from '/@/hooks/component/useGridLayout'; 11 import { useGridLayout } from '/@/hooks/component/useGridLayout';
12 import { ColEx } from '/@/components/Form/src/types'; 12 import { ColEx } from '/@/components/Form/src/types';
13 import { useHistoryData } from '../../hook/useHistoryData'; 13 import { useHistoryData } from '../../hook/useHistoryData';
  14 + import { formatToDateTime } from '/@/utils/dateUtil';
  15 + import { useTable, BasicTable } from '/@/components/Table';
  16 + import { LineChartOutlined, BarsOutlined } from '@ant-design/icons-vue';
  17 + import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
14 18
15 interface DeviceDetail { 19 interface DeviceDetail {
16 tbDeviceId: string; 20 tbDeviceId: string;
@@ -22,15 +26,33 @@ @@ -22,15 +26,33 @@
22 attr?: string; 26 attr?: string;
23 }>(); 27 }>();
24 28
  29 + enum Mode {
  30 + TABLE = 'table',
  31 + CHART = 'chart',
  32 + }
  33 +
  34 + const mode = ref<Mode>(Mode.CHART);
  35 +
25 const chartRef = ref(); 36 const chartRef = ref();
26 37
27 const loading = ref(false); 38 const loading = ref(false);
28 39
29 const isNull = ref(false); 40 const isNull = ref(false);
30 41
  42 + const historyData = ref<{ ts: number; value: string; name: string }[]>([]);
  43 +
31 const { deviceAttrs, getDeviceKeys, getSearchParams, setChartOptions, getDeviceAttribute } = 44 const { deviceAttrs, getDeviceKeys, getSearchParams, setChartOptions, getDeviceAttribute } =
32 useHistoryData(); 45 useHistoryData();
33 46
  47 + const getIdentifierNameMapping = computed(() => {
  48 + const mapping = {};
  49 + unref(deviceAttrs).forEach((item) => {
  50 + const { identifier, name } = item;
  51 + mapping[identifier] = name;
  52 + });
  53 + return mapping;
  54 + });
  55 +
34 function hasDeviceAttr() { 56 function hasDeviceAttr() {
35 if (!unref(deviceAttrs).length) { 57 if (!unref(deviceAttrs).length) {
36 return false; 58 return false;
@@ -39,6 +61,34 @@ @@ -39,6 +61,34 @@
39 } 61 }
40 } 62 }
41 63
  64 + const [registerTable] = useTable({
  65 + showIndexColumn: false,
  66 + showTableSetting: false,
  67 + dataSource: historyData,
  68 + maxHeight: 300,
  69 + size: 'small',
  70 + columns: [
  71 + {
  72 + title: '属性',
  73 + dataIndex: 'name',
  74 + format: (text) => {
  75 + return unref(getIdentifierNameMapping)[text];
  76 + },
  77 + },
  78 + {
  79 + title: '值',
  80 + dataIndex: 'value',
  81 + },
  82 + {
  83 + title: '更新时间',
  84 + dataIndex: 'ts',
  85 + format: (val) => {
  86 + return formatToDateTime(val, 'YYYY-MM-DD HH:mm:ss');
  87 + },
  88 + },
  89 + ],
  90 + });
  91 +
42 const { setOptions, getInstance } = useECharts(chartRef as Ref<HTMLDivElement>); 92 const { setOptions, getInstance } = useECharts(chartRef as Ref<HTMLDivElement>);
43 93
44 const [register, method] = useTimePeriodForm({ 94 const [register, method] = useTimePeriodForm({
@@ -57,6 +107,7 @@ @@ -57,6 +107,7 @@
57 ...searchParams, 107 ...searchParams,
58 entityId: props.deviceDetail.tbDeviceId, 108 entityId: props.deviceDetail.tbDeviceId,
59 }); 109 });
  110 + historyData.value = getTableHistoryData(res);
60 // 判断数据对象是否为空 111 // 判断数据对象是否为空
61 if (!Object.keys(res).length) { 112 if (!Object.keys(res).length) {
62 isNull.value = false; 113 isNull.value = false;
@@ -74,11 +125,36 @@ @@ -74,11 +125,36 @@
74 }, 125 },
75 }); 126 });
76 127
  128 + const getTableHistoryData = (record: Recordable<{ ts: number; value: string }[]>) => {
  129 + const keys = Object.keys(record);
  130 + const list = keys.reduce((prev, next) => {
  131 + const list = record[next].map((item) => {
  132 + return {
  133 + ...item,
  134 + name: next,
  135 + };
  136 + });
  137 + return [...prev, ...list];
  138 + }, []);
  139 + return list;
  140 + };
  141 +
77 const getDeviceDataKey = async () => { 142 const getDeviceDataKey = async () => {
78 try { 143 try {
79 await getDeviceAttribute(props.deviceDetail); 144 await getDeviceAttribute(props.deviceDetail);
80 if (props.attr) { 145 if (props.attr) {
81 method.setFieldsValue({ keys: props.attr }); 146 method.setFieldsValue({ keys: props.attr });
  147 + const attrInfo = unref(deviceAttrs).find((item) => item.identifier === props.attr);
  148 + console.log({ attrInfo });
  149 + if (
  150 + [DataTypeEnum.IS_STRING, DataTypeEnum.IS_STRUCT].includes(
  151 + attrInfo?.detail.dataType.type as unknown as DataTypeEnum
  152 + )
  153 + ) {
  154 + mode.value = Mode.TABLE;
  155 + } else {
  156 + mode.value = Mode.CHART;
  157 + }
82 } 158 }
83 } catch (error) {} 159 } catch (error) {}
84 }; 160 };
@@ -110,6 +186,7 @@ @@ -110,6 +186,7 @@
110 agg: AggregateDataEnum.NONE, 186 agg: AggregateDataEnum.NONE,
111 limit: 7, 187 limit: 7,
112 }); 188 });
  189 + historyData.value = getTableHistoryData(res);
113 190
114 // 判断对象是否为空 191 // 判断对象是否为空
115 if (!Object.keys(res).length) { 192 if (!Object.keys(res).length) {
@@ -123,6 +200,10 @@ @@ -123,6 +200,10 @@
123 setOptions(setChartOptions(res, selectedKeys)); 200 setOptions(setChartOptions(res, selectedKeys));
124 }; 201 };
125 202
  203 + const switchMode = (flag: Mode) => {
  204 + mode.value = flag;
  205 + };
  206 +
126 onMounted(async () => { 207 onMounted(async () => {
127 await getDeviceDataKey(); 208 await getDeviceDataKey();
128 await openHistoryPanel(); 209 await openHistoryPanel();
@@ -139,10 +220,60 @@ @@ -139,10 +220,60 @@
139 <TimePeriodForm @register="register" /> 220 <TimePeriodForm @register="register" />
140 </section> 221 </section>
141 <section class="bg-white p-3"> 222 <section class="bg-white p-3">
142 - <div v-show="isNull" ref="chartRef" :style="{ height: '400px', width: '100%' }">  
143 - <Spin :spinning="loading" :absolute="true" />  
144 - </div>  
145 - <Empty v-show="!isNull" /> 223 + <Spin :spinning="loading" :absolute="true">
  224 + <div v-show="mode === Mode.CHART" class="flex h-70px items-center justify-end p-2">
  225 + <Tooltip title="图表模式">
  226 + <Button
  227 + :class="[mode === Mode.CHART && '!bg-blue-500 svg:text-light-50']"
  228 + class="!p-2 !children:flex flex justify-center items-center border-r-0"
  229 + @click="switchMode(Mode.CHART)"
  230 + >
  231 + <LineChartOutlined />
  232 + </Button>
  233 + </Tooltip>
  234 +
  235 + <Tooltip title="列表模式">
  236 + <Button
  237 + class="!p-2 !children:flex flex justify-center items-center"
  238 + @click="switchMode(Mode.TABLE)"
  239 + >
  240 + <BarsOutlined />
  241 + </Button>
  242 + </Tooltip>
  243 + </div>
  244 + <div
  245 + v-show="isNull && mode === Mode.CHART"
  246 + ref="chartRef"
  247 + :style="{ height: '400px', width: '100%' }"
  248 + >
  249 + </div>
  250 + <Empty v-show="!isNull && mode === Mode.CHART" />
  251 +
  252 + <BasicTable v-show="mode === Mode.TABLE" @register="registerTable">
  253 + <template #toolbar>
  254 + <div v-show="mode === Mode.TABLE" class="flex h-70px items-center justify-end p-2">
  255 + <Tooltip title="图表模式">
  256 + <Button
  257 + class="!p-2 !children:flex flex justify-center items-center border-r-0"
  258 + @click="switchMode(Mode.CHART)"
  259 + >
  260 + <LineChartOutlined />
  261 + </Button>
  262 + </Tooltip>
  263 +
  264 + <Tooltip title="列表模式">
  265 + <Button
  266 + :class="[mode === Mode.TABLE && '!bg-blue-500 svg:text-light-50']"
  267 + class="!p-2 !children:flex flex justify-center items-center"
  268 + @click="switchMode(Mode.TABLE)"
  269 + >
  270 + <BarsOutlined />
  271 + </Button>
  272 + </Tooltip>
  273 + </div>
  274 + </template>
  275 + </BasicTable>
  276 + </Spin>
146 </section> 277 </section>
147 </section> 278 </section>
148 </template> 279 </template>
@@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
18 import { computed } from '@vue/reactivity'; 18 import { computed } from '@vue/reactivity';
19 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; 19 import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel';
20 import { isArray, isObject } from '/@/utils/is'; 20 import { isArray, isObject } from '/@/utils/is';
21 - import { DateTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config'; 21 + import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
22 22
23 interface ReceiveMessage { 23 interface ReceiveMessage {
24 data: { 24 data: {
@@ -136,8 +136,8 @@ @@ -136,8 +136,8 @@
136 : ''; 136 : '';
137 }; 137 };
138 138
139 - const isStructAndTextType = (type: DateTypeEnum) => {  
140 - return [DateTypeEnum.IS_STRUCT, DateTypeEnum.IS_STRING].includes(type); 139 + const isStructAndTextType = (type: DataTypeEnum) => {
  140 + return [DataTypeEnum.IS_STRUCT, DataTypeEnum.IS_STRING].includes(type);
141 }; 141 };
142 142
143 const { send, close, data } = useWebSocket(socketInfo.origin, { 143 const { send, close, data } = useWebSocket(socketInfo.origin, {
@@ -170,7 +170,7 @@ @@ -170,7 +170,7 @@
170 name, 170 name,
171 unit, 171 unit,
172 showHistoryDataButton: !isStructAndTextType( 172 showHistoryDataButton: !isStructAndTextType(
173 - dataInfo?.detail.dataType?.type as unknown as DateTypeEnum 173 + dataInfo?.detail.dataType?.type as unknown as DataTypeEnum
174 ), 174 ),
175 }; 175 };
176 }); 176 });
@@ -242,13 +242,7 @@ @@ -242,13 +242,7 @@
242 <span class="text-base font-normal">{{ item.name }}</span> 242 <span class="text-base font-normal">{{ item.name }}</span>
243 </template> 243 </template>
244 <template #extra> 244 <template #extra>
245 - <Button  
246 - v-if="item.showHistoryDataButton"  
247 - type="link"  
248 - class="!p-0"  
249 - @click="handleShowDetail(item)"  
250 - >历史数据</Button  
251 - > 245 + <Button type="link" class="!p-0" @click="handleShowDetail(item)">历史数据</Button>
252 </template> 246 </template>
253 <section class="min-h-16 flex flex-col justify-between"> 247 <section class="min-h-16 flex flex-col justify-between">
254 <div class="flex font-bold text-lg mb-4 gap-2"> 248 <div class="flex font-bold text-lg mb-4 gap-2">
@@ -48,15 +48,66 @@ @@ -48,15 +48,66 @@
48 @cancel="handleCancelModal" 48 @cancel="handleCancelModal"
49 > 49 >
50 <TimePeriodForm @register="timePeriodRegister" /> 50 <TimePeriodForm @register="timePeriodRegister" />
51 - <!-- <BasicForm @register="registerForm" /> -->  
52 - <div v-show="isNull" ref="chartRef" :style="{ height: '550px', width }">  
53 - <Loading :loading="loading" :absolute="true" />  
54 - </div>  
55 - <Empty  
56 - :style="{ height: '550px', width }"  
57 - class="flex flex-col justify-center items-center"  
58 - v-show="!isNull"  
59 - /> 51 + <section>
  52 + <Spin :spinning="loading">
  53 + <div v-show="mode === Mode.CHART" class="flex h-70px items-center justify-end p-2">
  54 + <Tooltip title="图表模式">
  55 + <Button
  56 + :class="[mode === Mode.CHART && '!bg-blue-500 svg:text-light-50']"
  57 + class="!p-2 !children:flex flex justify-center items-center border-r-0"
  58 + @click="switchMode(Mode.CHART)"
  59 + >
  60 + <LineChartOutlined />
  61 + </Button>
  62 + </Tooltip>
  63 +
  64 + <Tooltip title="列表模式">
  65 + <Button
  66 + class="!p-2 !children:flex flex justify-center items-center"
  67 + @click="switchMode(Mode.TABLE)"
  68 + >
  69 + <BarsOutlined />
  70 + </Button>
  71 + </Tooltip>
  72 + </div>
  73 + <div
  74 + v-show="isNull && mode === Mode.CHART"
  75 + ref="chartRef"
  76 + :style="{ height: '400px', width }"
  77 + >
  78 + </div>
  79 + <Empty
  80 + :style="{ height: '550px', width }"
  81 + class="flex flex-col justify-center items-center"
  82 + v-show="!isNull && mode === Mode.CHART"
  83 + />
  84 +
  85 + <BasicTable v-show="mode === Mode.TABLE" @register="registerAttrTable">
  86 + <template #toolbar>
  87 + <div v-show="mode === Mode.TABLE" class="flex h-70px items-center justify-end p-2">
  88 + <Tooltip title="图表模式">
  89 + <Button
  90 + class="!p-2 !children:flex flex justify-center items-center border-r-0"
  91 + @click="switchMode(Mode.CHART)"
  92 + >
  93 + <LineChartOutlined />
  94 + </Button>
  95 + </Tooltip>
  96 +
  97 + <Tooltip title="列表模式">
  98 + <Button
  99 + :class="[mode === Mode.TABLE && '!bg-blue-500 svg:text-light-50']"
  100 + class="!p-2 !children:flex flex justify-center items-center"
  101 + @click="switchMode(Mode.TABLE)"
  102 + >
  103 + <BarsOutlined />
  104 + </Button>
  105 + </Tooltip>
  106 + </div>
  107 + </template>
  108 + </BasicTable>
  109 + </Spin>
  110 + </section>
60 </BasicModal> 111 </BasicModal>
61 <DeviceDetailDrawer 112 <DeviceDetailDrawer
62 @register="registerDetailDrawer" 113 @register="registerDetailDrawer"
@@ -69,12 +120,21 @@ @@ -69,12 +120,21 @@
69 </div> 120 </div>
70 </template> 121 </template>
71 <script lang="ts"> 122 <script lang="ts">
72 - import { defineComponent, ref, nextTick, unref, onMounted, Ref, onUnmounted } from 'vue'; 123 + import {
  124 + defineComponent,
  125 + ref,
  126 + nextTick,
  127 + unref,
  128 + onMounted,
  129 + Ref,
  130 + onUnmounted,
  131 + computed,
  132 + } from 'vue';
73 import { useScript } from '/@/hooks/web/useScript'; 133 import { useScript } from '/@/hooks/web/useScript';
74 import { formSchema, columns } from './config.data'; 134 import { formSchema, columns } from './config.data';
75 import { BasicTable, useTable } from '/@/components/Table'; 135 import { BasicTable, useTable } from '/@/components/Table';
76 import { devicePage } from '/@/api/alarm/contact/alarmContact'; 136 import { devicePage } from '/@/api/alarm/contact/alarmContact';
77 - import { Tag, Empty } from 'ant-design-vue'; 137 + import { Tag, Empty, Spin, Tooltip, Button } from 'ant-design-vue';
78 import { DeviceState } from '/@/api/device/model/deviceModel'; 138 import { DeviceState } from '/@/api/device/model/deviceModel';
79 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils'; 139 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
80 import { useModal, BasicModal } from '/@/components/Modal'; 140 import { useModal, BasicModal } from '/@/components/Modal';
@@ -91,14 +151,14 @@ @@ -91,14 +151,14 @@
91 import djh from '/@/assets/images/djh.png'; 151 import djh from '/@/assets/images/djh.png';
92 import online from '/@/assets/images/online1.png'; 152 import online from '/@/assets/images/online1.png';
93 import lx1 from '/@/assets/images/lx1.png'; 153 import lx1 from '/@/assets/images/lx1.png';
94 - import Loading from '/@/components/Loading/src/Loading.vue';  
95 import { TimePeriodForm, useTimePeriodForm } from './cpns/TimePeriodForm'; 154 import { TimePeriodForm, useTimePeriodForm } from './cpns/TimePeriodForm';
96 import { selectDeviceAttrSchema } from './config.data'; 155 import { selectDeviceAttrSchema } from './config.data';
97 import { defaultSchemas } from './cpns/TimePeriodForm/config'; 156 import { defaultSchemas } from './cpns/TimePeriodForm/config';
98 import { QueryWay, SchemaFiled, AggregateDataEnum } from './cpns/TimePeriodForm/config'; 157 import { QueryWay, SchemaFiled, AggregateDataEnum } from './cpns/TimePeriodForm/config';
99 - import { Spin } from 'ant-design-vue';  
100 import { useAsyncQueue } from './useAsyncQueue'; 158 import { useAsyncQueue } from './useAsyncQueue';
101 import { useHistoryData } from '../list/hook/useHistoryData'; 159 import { useHistoryData } from '../list/hook/useHistoryData';
  160 + import { LineChartOutlined, BarsOutlined } from '@ant-design/icons-vue';
  161 + import { formatToDateTime } from '/@/utils/dateUtil';
102 162
103 interface DeviceInfo { 163 interface DeviceInfo {
104 alarmStatus: 0 | 1; 164 alarmStatus: 0 | 1;
@@ -121,9 +181,12 @@ @@ -121,9 +181,12 @@
121 Empty, 181 Empty,
122 BasicModal, 182 BasicModal,
123 DeviceDetailDrawer, 183 DeviceDetailDrawer,
124 - Loading,  
125 TimePeriodForm, 184 TimePeriodForm,
126 Spin, 185 Spin,
  186 + Tooltip,
  187 + Button,
  188 + LineChartOutlined,
  189 + BarsOutlined,
127 }, 190 },
128 props: { 191 props: {
129 width: { 192 width: {
@@ -136,6 +199,12 @@ @@ -136,6 +199,12 @@
136 }, 199 },
137 }, 200 },
138 setup() { 201 setup() {
  202 + enum Mode {
  203 + TABLE = 'table',
  204 + CHART = 'chart',
  205 + }
  206 +
  207 + const mode = ref<Mode>(Mode.CHART);
139 let entityId = ''; 208 let entityId = '';
140 let globalRecord: any = {}; 209 let globalRecord: any = {};
141 const wrapRef = ref<HTMLDivElement | null>(null); 210 const wrapRef = ref<HTMLDivElement | null>(null);
@@ -149,11 +218,64 @@ @@ -149,11 +218,64 @@
149 const [registerModal, { openModal }] = useModal(); 218 const [registerModal, { openModal }] = useModal();
150 const BMapInstance = ref<Nullable<any>>(null); 219 const BMapInstance = ref<Nullable<any>>(null);
151 220
  221 + const historyData = ref<{ ts: number; value: string; name: string }[]>([]);
  222 +
152 const loading = ref(false); 223 const loading = ref(false);
153 224
154 const { deviceAttrs, getDeviceKeys, getDeviceAttribute, getSearchParams, setChartOptions } = 225 const { deviceAttrs, getDeviceKeys, getDeviceAttribute, getSearchParams, setChartOptions } =
155 useHistoryData(); 226 useHistoryData();
156 227
  228 + const getTableHistoryData = (record: Recordable<{ ts: number; value: string }[]>) => {
  229 + const keys = Object.keys(record);
  230 + const list = keys.reduce((prev, next) => {
  231 + const list = record[next].map((item) => {
  232 + return {
  233 + ...item,
  234 + name: next,
  235 + };
  236 + });
  237 + return [...prev, ...list];
  238 + }, []);
  239 + return list;
  240 + };
  241 +
  242 + const getIdentifierNameMapping = computed(() => {
  243 + const mapping = {};
  244 + unref(deviceAttrs).forEach((item) => {
  245 + const { identifier, name } = item;
  246 + mapping[identifier] = name;
  247 + });
  248 + return mapping;
  249 + });
  250 +
  251 + const [registerAttrTable] = useTable({
  252 + showIndexColumn: false,
  253 + showTableSetting: false,
  254 + dataSource: historyData,
  255 + maxHeight: 300,
  256 + size: 'small',
  257 + columns: [
  258 + {
  259 + title: '属性',
  260 + dataIndex: 'name',
  261 + format: (text) => {
  262 + return unref(getIdentifierNameMapping)[text];
  263 + },
  264 + },
  265 + {
  266 + title: '值',
  267 + dataIndex: 'value',
  268 + },
  269 + {
  270 + title: '更新时间',
  271 + dataIndex: 'ts',
  272 + format: (val) => {
  273 + return formatToDateTime(val, 'YYYY-MM-DD HH:mm:ss');
  274 + },
  275 + },
  276 + ],
  277 + });
  278 +
157 const [registerTable] = useTable({ 279 const [registerTable] = useTable({
158 api: devicePage, 280 api: devicePage,
159 columns, 281 columns,
@@ -352,6 +474,7 @@ @@ -352,6 +474,7 @@
352 loading.value = true; 474 loading.value = true;
353 const res = await getDeviceHistoryInfo({ ...searchParams, entityId }); 475 const res = await getDeviceHistoryInfo({ ...searchParams, entityId });
354 loading.value = false; 476 loading.value = false;
  477 + historyData.value = getTableHistoryData(res);
355 // 判断数据对象是否为空 478 // 判断数据对象是否为空
356 if (!Object.keys(res).length) { 479 if (!Object.keys(res).length) {
357 isNull.value = false; 480 isNull.value = false;
@@ -397,6 +520,7 @@ @@ -397,6 +520,7 @@
397 limit: 7, 520 limit: 7,
398 }); 521 });
399 522
  523 + historyData.value = getTableHistoryData(res);
400 // 判断对象是否为空 524 // 判断对象是否为空
401 if (!Object.keys(res).length) { 525 if (!Object.keys(res).length) {
402 isNull.value = false; 526 isNull.value = false;
@@ -447,7 +571,13 @@ @@ -447,7 +571,13 @@
447 openGatewayDetailDrawer(true, data); 571 openGatewayDetailDrawer(true, data);
448 }; 572 };
449 573
  574 + const switchMode = (flag: Mode) => {
  575 + mode.value = flag;
  576 + };
  577 +
450 return { 578 return {
  579 + mode,
  580 + Mode,
451 wrapRef, 581 wrapRef,
452 registerTable, 582 registerTable,
453 deviceRowClick, 583 deviceRowClick,
@@ -464,6 +594,8 @@ @@ -464,6 +594,8 @@
464 registerGatewayDetailDrawer, 594 registerGatewayDetailDrawer,
465 handleOpenTbDeviceDetail, 595 handleOpenTbDeviceDetail,
466 handleOpenGatewayDetail, 596 handleOpenGatewayDetail,
  597 + switchMode,
  598 + registerAttrTable,
467 }; 599 };
468 }, 600 },
469 }); 601 });
@@ -42,7 +42,7 @@ export enum AssessMode { @@ -42,7 +42,7 @@ export enum AssessMode {
42 /** 42 /**
43 * 新增参数 动态显示表单 43 * 新增参数 动态显示表单
44 */ 44 */
45 -export enum DateTypeEnum { 45 +export enum DataTypeEnum {
46 IS_NUMBER_INT = 'INT', 46 IS_NUMBER_INT = 'INT',
47 IS_NUMBER_DOUBLE = 'DOUBLE', 47 IS_NUMBER_DOUBLE = 'DOUBLE',
48 IS_STRING = 'TEXT', 48 IS_STRING = 'TEXT',
@@ -51,18 +51,18 @@ export enum DateTypeEnum { @@ -51,18 +51,18 @@ export enum DateTypeEnum {
51 } 51 }
52 52
53 const isNumber = (type: string) => { 53 const isNumber = (type: string) => {
54 - return type === DateTypeEnum.IS_NUMBER_INT || type === DateTypeEnum.IS_NUMBER_DOUBLE; 54 + return type === DataTypeEnum.IS_NUMBER_INT || type === DataTypeEnum.IS_NUMBER_DOUBLE;
55 }; 55 };
56 56
57 const isString = (type: string) => { 57 const isString = (type: string) => {
58 - return type === DateTypeEnum.IS_STRING; 58 + return type === DataTypeEnum.IS_STRING;
59 }; 59 };
60 const isStruct = (type: string) => { 60 const isStruct = (type: string) => {
61 - return type === DateTypeEnum.IS_STRUCT; 61 + return type === DataTypeEnum.IS_STRUCT;
62 }; 62 };
63 63
64 const isBool = (type: string) => { 64 const isBool = (type: string) => {
65 - return type === DateTypeEnum.IS_BOOL; 65 + return type === DataTypeEnum.IS_BOOL;
66 }; 66 };
67 67
68 export const serviceSchemas = (tcpDeviceFlag: boolean): FormSchema[] => { 68 export const serviceSchemas = (tcpDeviceFlag: boolean): FormSchema[] => {
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import { nextTick, Ref, ref, unref } from 'vue'; 2 + import { computed, nextTick, Ref, ref, unref } from 'vue';
3 import { getDeviceHistoryInfo } from '/@/api/alarm/position'; 3 import { getDeviceHistoryInfo } from '/@/api/alarm/position';
4 - import { Empty, Tooltip, Button } from 'ant-design-vue'; 4 + import { Empty, Tooltip, Button, Spin } 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';
@@ -9,7 +9,6 @@ @@ -9,7 +9,6 @@
9 import { DataSource } from '/@/api/dataBoard/model'; 9 import { DataSource } from '/@/api/dataBoard/model';
10 import { useForm, BasicForm } from '/@/components/Form'; 10 import { useForm, BasicForm } from '/@/components/Form';
11 import { formSchema, SchemaFiled } from '../config/historyTrend.config'; 11 import { formSchema, SchemaFiled } from '../config/historyTrend.config';
12 - import { Loading } from '/@/components/Loading';  
13 import BasicModal from '/@/components/Modal/src/BasicModal.vue'; 12 import BasicModal from '/@/components/Modal/src/BasicModal.vue';
14 import { useModalInner } from '/@/components/Modal'; 13 import { useModalInner } from '/@/components/Modal';
15 import { getAllDeviceByOrg } from '/@/api/dataBoard'; 14 import { getAllDeviceByOrg } from '/@/api/dataBoard';
@@ -43,11 +42,7 @@ @@ -43,11 +42,7 @@
43 const { setOptions, destory } = useECharts(chartRef as Ref<HTMLDivElement>); 42 const { setOptions, destory } = useECharts(chartRef as Ref<HTMLDivElement>);
44 43
45 function hasDeviceAttr() { 44 function hasDeviceAttr() {
46 - if (!unref(deviceAttrs).length) {  
47 - return false;  
48 - } else {  
49 - return true;  
50 - } 45 + return !!unref(deviceAttrs).length;
51 } 46 }
52 47
53 const [register, method] = useForm({ 48 const [register, method] = useForm({
@@ -107,6 +102,15 @@ @@ -107,6 +102,15 @@
107 return list; 102 return list;
108 }; 103 };
109 104
  105 + const getIdentifierNameMapping = computed(() => {
  106 + const mapping = {};
  107 + unref(deviceAttrs).forEach((item) => {
  108 + const { identifier, name } = item;
  109 + mapping[identifier] = name;
  110 + });
  111 + return mapping;
  112 + });
  113 +
110 const [registerTable] = useTable({ 114 const [registerTable] = useTable({
111 showIndexColumn: false, 115 showIndexColumn: false,
112 showTableSetting: false, 116 showTableSetting: false,
@@ -117,6 +121,9 @@ @@ -117,6 +121,9 @@
117 { 121 {
118 title: '属性', 122 title: '属性',
119 dataIndex: 'name', 123 dataIndex: 'name',
  124 + format: (value) => {
  125 + return unref(getIdentifierNameMapping)[value];
  126 + },
120 }, 127 },
121 { 128 {
122 title: '值', 129 title: '值',
@@ -173,7 +180,7 @@ @@ -173,7 +180,7 @@
173 }); 180 });
174 historyData.value = getTableHistoryData(res); 181 historyData.value = getTableHistoryData(res);
175 // 判断对象是否为空 182 // 判断对象是否为空
176 - if (!Object.keys(unref(historyData)).length) { 183 + if (!Object.keys(res).length) {
177 isNull.value = false; 184 isNull.value = false;
178 return; 185 return;
179 } else { 186 } else {
@@ -261,65 +268,66 @@ @@ -261,65 +268,66 @@
261 <BasicForm @register="register" /> 268 <BasicForm @register="register" />
262 </section> 269 </section>
263 <section class="bg-white p-3" style="min-height: 350px"> 270 <section class="bg-white p-3" style="min-height: 350px">
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 - >  
290 - <Loading :loading="loading" :absolute="true" />  
291 - </div>  
292 - <Empty  
293 - v-if="mode === Mode.CHART"  
294 - class="h-350px flex flex-col justify-center items-center"  
295 - description="暂无数据,请选择设备查询"  
296 - v-show="!isNull"  
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> 271 + <Spin :spinning="loading" :absolute="true">
  272 + <div v-show="mode === Mode.CHART" class="flex h-70px items-center justify-end p-2">
  273 + <Tooltip title="图表模式">
  274 + <Button
  275 + :class="[mode === Mode.CHART && '!bg-blue-500 svg:text-light-50']"
  276 + class="!p-2 !children:flex flex justify-center items-center border-r-0"
  277 + @click="switchMode(Mode.CHART)"
  278 + >
  279 + <LineChartOutlined />
  280 + </Button>
  281 + </Tooltip>
  282 +
  283 + <Tooltip title="列表模式">
  284 + <Button
  285 + class="!p-2 !children:flex flex justify-center items-center"
  286 + @click="switchMode(Mode.TABLE)"
  287 + >
  288 + <BarsOutlined />
  289 + </Button>
  290 + </Tooltip>
  291 + </div>
  292 +
  293 + <div
  294 + v-show="isNull && mode === Mode.CHART"
  295 + ref="chartRef"
  296 + :style="{ height: '350px', width: '100%' }"
  297 + >
  298 + </div>
  299 + <Empty
  300 + v-if="mode === Mode.CHART"
  301 + class="h-350px flex flex-col justify-center items-center"
  302 + description="暂无数据,请选择设备查询"
  303 + v-show="!isNull"
  304 + />
  305 +
  306 + <BasicTable v-show="mode === Mode.TABLE" @register="registerTable">
  307 + <template #toolbar>
  308 + <div v-show="mode === Mode.TABLE" class="flex h-70px items-center justify-end p-2">
  309 + <Tooltip title="图表模式">
  310 + <Button
  311 + class="!p-2 !children:flex flex justify-center items-center border-r-0"
  312 + @click="switchMode(Mode.CHART)"
  313 + >
  314 + <LineChartOutlined />
  315 + </Button>
  316 + </Tooltip>
  317 +
  318 + <Tooltip title="列表模式">
  319 + <Button
  320 + :class="[mode === Mode.TABLE && '!bg-blue-500 svg:text-light-50']"
  321 + class="!p-2 !children:flex flex justify-center items-center"
  322 + @click="switchMode(Mode.TABLE)"
  323 + >
  324 + <BarsOutlined />
  325 + </Button>
  326 + </Tooltip>
  327 + </div>
  328 + </template>
  329 + </BasicTable>
  330 + </Spin>
323 </section> 331 </section>
324 </section> 332 </section>
325 </BasicModal> 333 </BasicModal>