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 10
11 11 const alert = {
12 12 [PackagesCategoryEnum.MAP]: [
13   - '地图组件,需绑定两个数据源,且数据源为同一设备。第一数据源为经度,第二数据源为纬度,否则地图组件不能正常显示。',
  13 + '1、绑定数据源为结构体时,可以自行选择结构体里的属性作为经纬度',
  14 + '2、绑定数据源为非结构体时,第一数据源为经度,第二数据源为纬度,且数据源为同一设备,并同时上报。否则地图组件不能正常显示。',
14 15 ],
15 16 };
16 17
... ...
... ... @@ -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 {
... ...