Commit ff945e7b727397885e9deb292ee9265a70364a00
Merge branch 'perf/visual-board-map' into 'main_dev'
perf:看板组件地图新增选择单数据源结构体时选择经纬度 See merge request yunteng/thingskit-front!1107
Showing
7 changed files
with
231 additions
and
101 deletions
... | ... | @@ -34,14 +34,6 @@ |
34 | 34 | const { unit, fontColor, showDeviceName } = item.componentInfo || {}; |
35 | 35 | const { deviceName, deviceRename, attribute, attributeRename, deviceId, attributeName } = |
36 | 36 | item; |
37 | - // return { | |
38 | - // unit: unit ?? persetUnit, | |
39 | - // fontColor: fontColor ?? persetFontColor, | |
40 | - // showDeviceName: showDeviceName ?? persetShowDeviceName, | |
41 | - // attribute, | |
42 | - // deviceName, | |
43 | - // deviceRename, | |
44 | - // }; | |
45 | 37 | return { |
46 | 38 | unit: unit ?? presetUnit, |
47 | 39 | fontColor: fontColor ?? presetFontColor, |
... | ... | @@ -58,7 +50,6 @@ |
58 | 50 | }); |
59 | 51 | |
60 | 52 | const getOffset = (length: number, index: number) => { |
61 | - // try { | |
62 | 53 | const offsetList = ref<any>([]); |
63 | 54 | switch (length) { |
64 | 55 | case 1: |
... | ... | @@ -147,7 +138,6 @@ |
147 | 138 | break; |
148 | 139 | } |
149 | 140 | return unref(offsetList); |
150 | - // } catch {} | |
151 | 141 | }; |
152 | 142 | |
153 | 143 | const series = ref( |
... | ... | @@ -263,7 +253,6 @@ |
263 | 253 | series.value.forEach((item) => { |
264 | 254 | if (item.id === deviceId && item.attribute === attribute && value) { |
265 | 255 | item.value = getNumberValue(value); |
266 | - // time.value = timespan; | |
267 | 256 | } |
268 | 257 | }); |
269 | 258 | }); | ... | ... |
... | ... | @@ -62,7 +62,6 @@ |
62 | 62 | const options = (): EChartsOption => { |
63 | 63 | const { unit, fontColor, pointerColor, maxNumber, valueSize } = unref(getDesign); |
64 | 64 | |
65 | - // getStageColor(gradientInfo); | |
66 | 65 | return { |
67 | 66 | series: [ |
68 | 67 | { |
... | ... | @@ -103,14 +102,6 @@ |
103 | 102 | color: pointerColor, |
104 | 103 | }, |
105 | 104 | }, |
106 | - // anchor: { | |
107 | - // show: true, | |
108 | - // showAbove: true, | |
109 | - // size: 10, | |
110 | - // itemStyle: { | |
111 | - // borderWidth: 4, | |
112 | - // }, | |
113 | - // }, | |
114 | 105 | title: { |
115 | 106 | show: false, |
116 | 107 | }, | ... | ... |
... | ... | @@ -3,7 +3,7 @@ |
3 | 3 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
4 | 4 | import { formSchema, getHistorySearchParams, SchemaFiled } from './history.config'; |
5 | 5 | import { HistoryModalOkEmitParams } from './type'; |
6 | - import { ref } from 'vue'; | |
6 | + import { ref, unref } from 'vue'; | |
7 | 7 | import { getAllDeviceByOrg } from '/@/api/dataBoard'; |
8 | 8 | import { getDeviceHistoryInfo } from '/@/api/alarm/position'; |
9 | 9 | import { DataSource } from '/@/views/visual/palette/types'; |
... | ... | @@ -19,58 +19,154 @@ |
19 | 19 | ], |
20 | 20 | }); |
21 | 21 | |
22 | - const [registerModal, { closeModal }] = useModalInner(async (dataSource: DataSource[]) => { | |
23 | - try { | |
24 | - dataSource = cloneDeep(dataSource); | |
25 | - if (dataSource.length < 2) return; | |
26 | - dataSource = dataSource.splice(0, 2); | |
27 | - const deviceRecord = dataSource?.at(0) || ({} as DataSource); | |
28 | - if (!deviceRecord.organizationId) return; | |
29 | - const deviceList = await getAllDeviceByOrg( | |
30 | - deviceRecord.organizationId, | |
31 | - deviceRecord.deviceProfileId | |
32 | - ); | |
33 | - const options = deviceList | |
34 | - .filter((item) => item.tbDeviceId === deviceRecord.deviceId) | |
35 | - .map((item) => ({ ...item, label: item.alias || item.name, value: item.tbDeviceId })); | |
36 | - | |
37 | - const attKey = dataSource.map((item) => ({ | |
38 | - ...item, | |
39 | - label: item.attribute, | |
40 | - value: item.attribute, | |
41 | - })); | |
42 | - updateSchema([ | |
43 | - { | |
44 | - field: SchemaFiled.DEVICE_ID, | |
45 | - componentProps: { | |
46 | - options, | |
47 | - }, | |
22 | + const loading = ref(false); | |
23 | + const getDesign = ref(); | |
24 | + | |
25 | + const getTwoMap = async (dataSource) => { | |
26 | + if (dataSource.length < 2) return; | |
27 | + dataSource = dataSource.splice(0, 2); | |
28 | + const deviceRecord = dataSource?.at(0) || ({} as DataSource); | |
29 | + | |
30 | + if (!deviceRecord.organizationId) return; | |
31 | + const deviceList = await getAllDeviceByOrg( | |
32 | + deviceRecord.organizationId, | |
33 | + deviceRecord.deviceProfileId | |
34 | + ); | |
35 | + const options = deviceList | |
36 | + .filter((item) => item.tbDeviceId === deviceRecord.deviceId) | |
37 | + .map((item) => ({ ...item, label: item.alias || item.name, value: item.tbDeviceId })); | |
38 | + | |
39 | + const attKey = dataSource.map((item) => ({ | |
40 | + ...item, | |
41 | + label: item.attribute, | |
42 | + value: item.attribute, | |
43 | + })); | |
44 | + | |
45 | + updateSchemaMap(options, attKey, deviceRecord); | |
46 | + }; | |
47 | + | |
48 | + const getOneMap = async (dataSource) => { | |
49 | + const deviceRecord = dataSource?.[0]; | |
50 | + if (!deviceRecord.organizationId) return; | |
51 | + const deviceList = await getAllDeviceByOrg( | |
52 | + deviceRecord.organizationId, | |
53 | + deviceRecord.deviceProfileId | |
54 | + ); | |
55 | + const options = deviceList | |
56 | + .filter((item) => item.tbDeviceId === deviceRecord.deviceId) | |
57 | + .map((item) => ({ ...item, label: item.alias || item.name, value: item.tbDeviceId })); | |
58 | + | |
59 | + const attKey = dataSource?.map((item) => ({ | |
60 | + ...item, | |
61 | + label: item.attributeName, | |
62 | + value: item.attribute, | |
63 | + })); | |
64 | + updateSchemaMap(options, attKey, deviceRecord); | |
65 | + }; | |
66 | + | |
67 | + const updateSchemaMap = (options, attKey, deviceRecord) => { | |
68 | + updateSchema([ | |
69 | + { | |
70 | + field: SchemaFiled.DEVICE_ID, | |
71 | + componentProps: { | |
72 | + options, | |
48 | 73 | }, |
49 | - { | |
50 | - field: SchemaFiled.KEYS, | |
51 | - component: 'Select', | |
52 | - defaultValue: attKey.map((item) => item.value), | |
53 | - componentProps: { | |
54 | - options: attKey, | |
55 | - mode: 'multiple', | |
56 | - disabled: true, | |
57 | - }, | |
74 | + }, | |
75 | + { | |
76 | + field: SchemaFiled.KEYS, | |
77 | + component: 'Select', | |
78 | + defaultValue: attKey.map((item) => item.value), | |
79 | + componentProps: { | |
80 | + options: attKey, | |
81 | + mode: 'multiple', | |
82 | + disabled: true, | |
58 | 83 | }, |
59 | - ]); | |
84 | + }, | |
85 | + ]); | |
60 | 86 | |
61 | - setFieldsValue({ | |
62 | - [SchemaFiled.DEVICE_ID]: deviceRecord.deviceId, | |
63 | - [SchemaFiled.KEYS]: attKey.map((item) => item.value), | |
64 | - }); | |
87 | + setFieldsValue({ | |
88 | + [SchemaFiled.DEVICE_ID]: deviceRecord.deviceId, | |
89 | + [SchemaFiled.KEYS]: attKey.map((item) => item.value), | |
90 | + }); | |
91 | + }; | |
92 | + | |
93 | + const [registerModal, { closeModal }] = useModalInner(async (dataSource: DataSource[]) => { | |
94 | + try { | |
95 | + getDesign.value = dataSource; | |
96 | + dataSource = cloneDeep(dataSource); | |
97 | + //判断选择是不是结构体 | |
98 | + if (dataSource.length == 1 && dataSource?.[0]?.lal) { | |
99 | + getOneMap(dataSource); | |
100 | + return; | |
101 | + } | |
102 | + getTwoMap(dataSource); | |
65 | 103 | } catch (error) { |
66 | 104 | throw error; |
67 | 105 | } |
68 | 106 | }); |
69 | 107 | |
108 | + const getMultipleDataSource = async (res) => { | |
109 | + let timespanList = Object.keys(res).reduce((prev, next) => { | |
110 | + const ts = res[next].map((item) => item.ts); | |
111 | + return [...prev, ...ts]; | |
112 | + }, [] as number[]); | |
113 | + timespanList = [...new Set(timespanList)]; | |
114 | + | |
115 | + const track: Record<'lng' | 'lat', number>[] = []; | |
116 | + const keys = Object.keys(res); | |
117 | + | |
118 | + for (const ts of timespanList) { | |
119 | + const list: { ts: number; value: number }[] = []; | |
120 | + for (const key of keys) { | |
121 | + const record = res[key].find((item) => ts === item.ts); | |
122 | + if (!validEffective(record?.value)) { | |
123 | + continue; | |
124 | + } | |
125 | + list.push(record as any); | |
126 | + } | |
127 | + | |
128 | + if (list.length === 2 && list.every(Boolean)) { | |
129 | + const lng = list.at(0)?.value; | |
130 | + const lat = list.at(1)?.value; | |
131 | + if (lng && lat) track.push({ lng, lat }); | |
132 | + } | |
133 | + } | |
134 | + return track; | |
135 | + }; | |
136 | + | |
137 | + // 单数据源选择结构体 | |
138 | + const getSingleDataSource = async (res) => { | |
139 | + const [keys] = Object.keys(res); | |
140 | + const track: Record<'lng' | 'lat', number>[] = []; | |
141 | + const dataSource = res[keys]?.map((item) => { | |
142 | + return { | |
143 | + value: item.value ? JSON.parse(item.value) : {}, | |
144 | + }; | |
145 | + }); | |
146 | + const list: { value: number }[] = []; | |
147 | + dataSource?.forEach((item) => { | |
148 | + if ( | |
149 | + //判断结构体得值是不是数字 | |
150 | + Object.values(item.value).every((item1) => { | |
151 | + return validEffective(item1 as any); | |
152 | + }) | |
153 | + ) { | |
154 | + list.push(item.value); | |
155 | + } | |
156 | + }); | |
157 | + const { latitude, longitude } = unref(getDesign)?.[0]; //获取选择的经纬度选项 | |
158 | + | |
159 | + list.forEach((item) => { | |
160 | + const lng = item[longitude]; | |
161 | + const lat = item[latitude]; | |
162 | + if (lng && lat) track.push({ lng, lat }); | |
163 | + }); | |
164 | + return track; | |
165 | + }; | |
166 | + | |
70 | 167 | const validEffective = (value = '') => { |
71 | 168 | return !!(value && !isNaN(value as unknown as number)); |
72 | 169 | }; |
73 | - const loading = ref(false); | |
74 | 170 | const handleOk = async () => { |
75 | 171 | try { |
76 | 172 | await validate(); |
... | ... | @@ -84,33 +180,9 @@ |
84 | 180 | ...value, |
85 | 181 | [SchemaFiled.KEYS]: value[SchemaFiled.KEYS].join(','), |
86 | 182 | }); |
183 | + const ifSingle = unref(getDesign)?.length === 1 && unref(getDesign)?.[0]?.lal; | |
87 | 184 | |
88 | - let timespanList = Object.keys(res).reduce((prev, next) => { | |
89 | - const ts = res[next].map((item) => item.ts); | |
90 | - return [...prev, ...ts]; | |
91 | - }, [] as number[]); | |
92 | - timespanList = [...new Set(timespanList)]; | |
93 | - | |
94 | - const track: Record<'lng' | 'lat', number>[] = []; | |
95 | - const keys = Object.keys(res); | |
96 | - | |
97 | - for (const ts of timespanList) { | |
98 | - const list: { ts: number; value: number }[] = []; | |
99 | - for (const key of keys) { | |
100 | - const record = res[key].find((item) => ts === item.ts); | |
101 | - if (!validEffective(record?.value)) { | |
102 | - continue; | |
103 | - } | |
104 | - list.push(record as any); | |
105 | - } | |
106 | - | |
107 | - if (list.length === 2 && list.every(Boolean)) { | |
108 | - const lng = list.at(0)?.value; | |
109 | - const lat = list.at(1)?.value; | |
110 | - if (lng && lat) track.push({ lng, lat }); | |
111 | - } | |
112 | - } | |
113 | - | |
185 | + const track = ifSingle ? await getSingleDataSource(res) : await getMultipleDataSource(res); | |
114 | 186 | emit('ok', { track, value } as HistoryModalOkEmitParams); |
115 | 187 | closeModal(); |
116 | 188 | } catch (error) { | ... | ... |
... | ... | @@ -24,6 +24,14 @@ |
24 | 24 | return props.config.option.dataSource?.at(0)?.deviceId; |
25 | 25 | }); |
26 | 26 | |
27 | + const getDesign = computed(() => { | |
28 | + const { option } = props.config; | |
29 | + const { dataSource } = option || {}; | |
30 | + return { | |
31 | + dataSource: dataSource, | |
32 | + }; | |
33 | + }); | |
34 | + | |
27 | 35 | /** |
28 | 36 | * @description 经度key |
29 | 37 | */ |
... | ... | @@ -42,7 +50,7 @@ |
42 | 50 | return !!(value && !isNaN(value as unknown as number)); |
43 | 51 | }; |
44 | 52 | |
45 | - const updateFn: MultipleDataFetchUpdateFn = (message, deviceId) => { | |
53 | + const getTwoMap = (message, deviceId) => { | |
46 | 54 | if (unref(getDeviceId) !== deviceId) return; |
47 | 55 | |
48 | 56 | const { data = {} } = message; |
... | ... | @@ -51,7 +59,7 @@ |
51 | 59 | |
52 | 60 | const lngData = bindMessage[unref(getLngKey)] || []; |
53 | 61 | const [lngLatest] = lngData; |
54 | - const [, lng] = lngLatest; | |
62 | + const [, lng] = lngLatest || []; | |
55 | 63 | |
56 | 64 | const latData = bindMessage[unref(getLatKey)] || []; |
57 | 65 | const [latLatest] = latData; |
... | ... | @@ -62,6 +70,34 @@ |
62 | 70 | } |
63 | 71 | }; |
64 | 72 | |
73 | + const getOneMap = (message, deviceId) => { | |
74 | + if (unref(getDeviceId) !== deviceId) return; | |
75 | + | |
76 | + const { longitude, latitude, attribute } = unref(getDesign)?.dataSource?.[0] || {}; | |
77 | + const { data } = message || {}; | |
78 | + const bindMessage = data[deviceId]; | |
79 | + const [, values] = attribute && bindMessage?.[attribute][0]; | |
80 | + | |
81 | + const mapValues = values ? JSON.parse(values) : {}; | |
82 | + const lng = longitude && mapValues[longitude]; | |
83 | + const lat = latitude && mapValues[latitude]; | |
84 | + | |
85 | + if (validEffective(lng) && validEffective(lat)) { | |
86 | + drawLine({ lng: Number(lng), lat: Number(lat) }); | |
87 | + } | |
88 | + }; | |
89 | + | |
90 | + const updateFn: MultipleDataFetchUpdateFn = (message, deviceId) => { | |
91 | + const { lal } = unref(getDesign)?.dataSource?.[0] || {}; | |
92 | + // 属性选择结构体类型时 | |
93 | + if (lal && unref(getDesign)?.dataSource?.length == 1) { | |
94 | + getOneMap(message, deviceId); | |
95 | + return; | |
96 | + } | |
97 | + // 选择两个属性时 | |
98 | + getTwoMap(message, deviceId); | |
99 | + }; | |
100 | + | |
65 | 101 | useMultipleDataFetch(props, updateFn); |
66 | 102 | |
67 | 103 | const { drawLine } = useMapTrackPlayBack(mapInstance); |
... | ... | @@ -100,11 +136,3 @@ |
100 | 136 | <div v-show="!loading" ref="wrapRef" :id="wrapId" class="w-full h-full no-drag"> </div> |
101 | 137 | </main> |
102 | 138 | </template> |
103 | - | |
104 | -<style lang="less" scoped> | |
105 | - // .map-spin-wrapper { | |
106 | - // :deep(.ant-spin-container) { | |
107 | - // @apply justify-center items-center p-2 w-full h-full; | |
108 | - // } | |
109 | - // } | |
110 | -</style> | ... | ... |
... | ... | @@ -247,7 +247,6 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
247 | 247 | }); |
248 | 248 | }, |
249 | 249 | placeholder: '请选择设备', |
250 | - getPopupContainer: () => document.body, | |
251 | 250 | ...createPickerSearch(), |
252 | 251 | }; |
253 | 252 | }, |
... | ... | @@ -382,11 +381,18 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
382 | 381 | return []; |
383 | 382 | }, |
384 | 383 | placeholder: '请选择属性', |
385 | - getPopupContainer: () => document.body, | |
386 | 384 | onChange(value: string, option: Record<'label' | 'value' | any, string>) { |
385 | + const { detail }: any = option || {}; | |
387 | 386 | setFieldsValue({ |
388 | 387 | [DataSourceField.ATTRIBUTE_NAME]: value ? option.label : null, |
389 | 388 | [DataSourceField.EXTENSION_DESC]: value ? JSON.stringify(option.extensionDesc) : '', |
389 | + // 地图组件结构体 | |
390 | + lal: | |
391 | + category === 'MAP' && value && detail?.dataType.type === 'STRUCT' | |
392 | + ? JSON.stringify(detail?.dataType.specs) | |
393 | + : null, | |
394 | + latitude: null, | |
395 | + longitude: null, | |
390 | 396 | }); |
391 | 397 | }, |
392 | 398 | ...createPickerSearch(), |
... | ... | @@ -394,6 +400,46 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
394 | 400 | }, |
395 | 401 | }, |
396 | 402 | { |
403 | + field: 'lal', | |
404 | + label: '经纬度存储的值', | |
405 | + component: 'Input', | |
406 | + show: false, | |
407 | + }, | |
408 | + { | |
409 | + field: 'longitude', | |
410 | + label: '经度', | |
411 | + colProps: { span: 8 }, | |
412 | + component: 'Select', | |
413 | + required: true, | |
414 | + ifShow: ({ model }) => category === 'MAP' && model.lal, | |
415 | + componentProps({ formModel }) { | |
416 | + const { lal } = formModel || {}; | |
417 | + return { | |
418 | + placeholder: '请选择经度', | |
419 | + options: lal | |
420 | + ? JSON.parse(lal)?.map((item) => ({ label: item.functionName, value: item.identifier })) | |
421 | + : [], | |
422 | + }; | |
423 | + }, | |
424 | + }, | |
425 | + { | |
426 | + field: 'latitude', | |
427 | + label: '纬度', | |
428 | + colProps: { span: 8 }, | |
429 | + required: true, | |
430 | + component: 'Select', | |
431 | + ifShow: ({ model }) => category === 'MAP' && model.lal, | |
432 | + componentProps({ formModel }) { | |
433 | + const { lal } = formModel || {}; | |
434 | + return { | |
435 | + placeholder: '请选择纬度', | |
436 | + options: lal | |
437 | + ? JSON.parse(lal)?.map((item) => ({ label: item.functionName, value: item.identifier })) | |
438 | + : [], | |
439 | + }; | |
440 | + }, | |
441 | + }, | |
442 | + { | |
397 | 443 | field: DataSourceField.EXTENSION_DESC, |
398 | 444 | component: 'Input', |
399 | 445 | show: false, | ... | ... |
... | ... | @@ -29,6 +29,9 @@ export interface DataSource { |
29 | 29 | customCommand: CustomCommand; |
30 | 30 | videoConfig?: VideoConfigType; |
31 | 31 | [key: string]: any; |
32 | + lal?: string; | |
33 | + latitude?: string | number; | |
34 | + longitude?: string | number; | |
32 | 35 | } |
33 | 36 | |
34 | 37 | export interface ExtraDataSource extends DataSource, PublicComponentOptions { | ... | ... |