Commit ff945e7b727397885e9deb292ee9265a70364a00

Authored by xp.Huang
2 parents 52cfdaae e761b3a5

Merge branch 'perf/visual-board-map' into 'main_dev'

perf:看板组件地图新增选择单数据源结构体时选择经纬度

See merge request yunteng/thingskit-front!1107
@@ -10,7 +10,8 @@ @@ -10,7 +10,8 @@
10 10
11 const alert = { 11 const alert = {
12 [PackagesCategoryEnum.MAP]: [ 12 [PackagesCategoryEnum.MAP]: [
13 - '地图组件,需绑定两个数据源,且数据源为同一设备。第一数据源为经度,第二数据源为纬度,否则地图组件不能正常显示。', 13 + '1、绑定数据源为结构体时,可以自行选择结构体里的属性作为经纬度',
  14 + '2、绑定数据源为非结构体时,第一数据源为经度,第二数据源为纬度,且数据源为同一设备,并同时上报。否则地图组件不能正常显示。',
14 ], 15 ],
15 }; 16 };
16 17
@@ -34,14 +34,6 @@ @@ -34,14 +34,6 @@
34 const { unit, fontColor, showDeviceName } = item.componentInfo || {}; 34 const { unit, fontColor, showDeviceName } = item.componentInfo || {};
35 const { deviceName, deviceRename, attribute, attributeRename, deviceId, attributeName } = 35 const { deviceName, deviceRename, attribute, attributeRename, deviceId, attributeName } =
36 item; 36 item;
37 - // return {  
38 - // unit: unit ?? persetUnit,  
39 - // fontColor: fontColor ?? persetFontColor,  
40 - // showDeviceName: showDeviceName ?? persetShowDeviceName,  
41 - // attribute,  
42 - // deviceName,  
43 - // deviceRename,  
44 - // };  
45 return { 37 return {
46 unit: unit ?? presetUnit, 38 unit: unit ?? presetUnit,
47 fontColor: fontColor ?? presetFontColor, 39 fontColor: fontColor ?? presetFontColor,
@@ -58,7 +50,6 @@ @@ -58,7 +50,6 @@
58 }); 50 });
59 51
60 const getOffset = (length: number, index: number) => { 52 const getOffset = (length: number, index: number) => {
61 - // try {  
62 const offsetList = ref<any>([]); 53 const offsetList = ref<any>([]);
63 switch (length) { 54 switch (length) {
64 case 1: 55 case 1:
@@ -147,7 +138,6 @@ @@ -147,7 +138,6 @@
147 break; 138 break;
148 } 139 }
149 return unref(offsetList); 140 return unref(offsetList);
150 - // } catch {}  
151 }; 141 };
152 142
153 const series = ref( 143 const series = ref(
@@ -263,7 +253,6 @@ @@ -263,7 +253,6 @@
263 series.value.forEach((item) => { 253 series.value.forEach((item) => {
264 if (item.id === deviceId && item.attribute === attribute && value) { 254 if (item.id === deviceId && item.attribute === attribute && value) {
265 item.value = getNumberValue(value); 255 item.value = getNumberValue(value);
266 - // time.value = timespan;  
267 } 256 }
268 }); 257 });
269 }); 258 });
@@ -62,7 +62,6 @@ @@ -62,7 +62,6 @@
62 const options = (): EChartsOption => { 62 const options = (): EChartsOption => {
63 const { unit, fontColor, pointerColor, maxNumber, valueSize } = unref(getDesign); 63 const { unit, fontColor, pointerColor, maxNumber, valueSize } = unref(getDesign);
64 64
65 - // getStageColor(gradientInfo);  
66 return { 65 return {
67 series: [ 66 series: [
68 { 67 {
@@ -103,14 +102,6 @@ @@ -103,14 +102,6 @@
103 color: pointerColor, 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 title: { 105 title: {
115 show: false, 106 show: false,
116 }, 107 },
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 import { BasicModal, useModalInner } from '/@/components/Modal'; 3 import { BasicModal, useModalInner } from '/@/components/Modal';
4 import { formSchema, getHistorySearchParams, SchemaFiled } from './history.config'; 4 import { formSchema, getHistorySearchParams, SchemaFiled } from './history.config';
5 import { HistoryModalOkEmitParams } from './type'; 5 import { HistoryModalOkEmitParams } from './type';
6 - import { ref } from 'vue'; 6 + import { ref, unref } from 'vue';
7 import { getAllDeviceByOrg } from '/@/api/dataBoard'; 7 import { getAllDeviceByOrg } from '/@/api/dataBoard';
8 import { getDeviceHistoryInfo } from '/@/api/alarm/position'; 8 import { getDeviceHistoryInfo } from '/@/api/alarm/position';
9 import { DataSource } from '/@/views/visual/palette/types'; 9 import { DataSource } from '/@/views/visual/palette/types';
@@ -19,58 +19,154 @@ @@ -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 } catch (error) { 103 } catch (error) {
66 throw error; 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 const validEffective = (value = '') => { 167 const validEffective = (value = '') => {
71 return !!(value && !isNaN(value as unknown as number)); 168 return !!(value && !isNaN(value as unknown as number));
72 }; 169 };
73 - const loading = ref(false);  
74 const handleOk = async () => { 170 const handleOk = async () => {
75 try { 171 try {
76 await validate(); 172 await validate();
@@ -84,33 +180,9 @@ @@ -84,33 +180,9 @@
84 ...value, 180 ...value,
85 [SchemaFiled.KEYS]: value[SchemaFiled.KEYS].join(','), 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 emit('ok', { track, value } as HistoryModalOkEmitParams); 186 emit('ok', { track, value } as HistoryModalOkEmitParams);
115 closeModal(); 187 closeModal();
116 } catch (error) { 188 } catch (error) {
@@ -24,6 +24,14 @@ @@ -24,6 +24,14 @@
24 return props.config.option.dataSource?.at(0)?.deviceId; 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 * @description 经度key 36 * @description 经度key
29 */ 37 */
@@ -42,7 +50,7 @@ @@ -42,7 +50,7 @@
42 return !!(value && !isNaN(value as unknown as number)); 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 if (unref(getDeviceId) !== deviceId) return; 54 if (unref(getDeviceId) !== deviceId) return;
47 55
48 const { data = {} } = message; 56 const { data = {} } = message;
@@ -51,7 +59,7 @@ @@ -51,7 +59,7 @@
51 59
52 const lngData = bindMessage[unref(getLngKey)] || []; 60 const lngData = bindMessage[unref(getLngKey)] || [];
53 const [lngLatest] = lngData; 61 const [lngLatest] = lngData;
54 - const [, lng] = lngLatest; 62 + const [, lng] = lngLatest || [];
55 63
56 const latData = bindMessage[unref(getLatKey)] || []; 64 const latData = bindMessage[unref(getLatKey)] || [];
57 const [latLatest] = latData; 65 const [latLatest] = latData;
@@ -62,6 +70,34 @@ @@ -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 useMultipleDataFetch(props, updateFn); 101 useMultipleDataFetch(props, updateFn);
66 102
67 const { drawLine } = useMapTrackPlayBack(mapInstance); 103 const { drawLine } = useMapTrackPlayBack(mapInstance);
@@ -100,11 +136,3 @@ @@ -100,11 +136,3 @@
100 <div v-show="!loading" ref="wrapRef" :id="wrapId" class="w-full h-full no-drag"> </div> 136 <div v-show="!loading" ref="wrapRef" :id="wrapId" class="w-full h-full no-drag"> </div>
101 </main> 137 </main>
102 </template> 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,7 +247,6 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
247 }); 247 });
248 }, 248 },
249 placeholder: '请选择设备', 249 placeholder: '请选择设备',
250 - getPopupContainer: () => document.body,  
251 ...createPickerSearch(), 250 ...createPickerSearch(),
252 }; 251 };
253 }, 252 },
@@ -382,11 +381,18 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -382,11 +381,18 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
382 return []; 381 return [];
383 }, 382 },
384 placeholder: '请选择属性', 383 placeholder: '请选择属性',
385 - getPopupContainer: () => document.body,  
386 onChange(value: string, option: Record<'label' | 'value' | any, string>) { 384 onChange(value: string, option: Record<'label' | 'value' | any, string>) {
  385 + const { detail }: any = option || {};
387 setFieldsValue({ 386 setFieldsValue({
388 [DataSourceField.ATTRIBUTE_NAME]: value ? option.label : null, 387 [DataSourceField.ATTRIBUTE_NAME]: value ? option.label : null,
389 [DataSourceField.EXTENSION_DESC]: value ? JSON.stringify(option.extensionDesc) : '', 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 ...createPickerSearch(), 398 ...createPickerSearch(),
@@ -394,6 +400,46 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -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 field: DataSourceField.EXTENSION_DESC, 443 field: DataSourceField.EXTENSION_DESC,
398 component: 'Input', 444 component: 'Input',
399 show: false, 445 show: false,
@@ -29,6 +29,9 @@ export interface DataSource { @@ -29,6 +29,9 @@ export interface DataSource {
29 customCommand: CustomCommand; 29 customCommand: CustomCommand;
30 videoConfig?: VideoConfigType; 30 videoConfig?: VideoConfigType;
31 [key: string]: any; 31 [key: string]: any;
  32 + lal?: string;
  33 + latitude?: string | number;
  34 + longitude?: string | number;
32 } 35 }
33 36
34 export interface ExtraDataSource extends DataSource, PublicComponentOptions { 37 export interface ExtraDataSource extends DataSource, PublicComponentOptions {