Commit ffa666b6b9dff088e40ed4a5ae92c42c6407020f
1 parent
1fd30052
feat: basic implement data board map component
Showing
21 changed files
with
606 additions
and
177 deletions
| 1 | +import { HistoryData } from './model'; | ||
| 1 | import { defHttp } from '/@/utils/http/axios'; | 2 | import { defHttp } from '/@/utils/http/axios'; |
| 2 | 3 | ||
| 3 | // 获取设备配置 | 4 | // 获取设备配置 |
| @@ -9,7 +10,7 @@ export const getDeviceProfile = () => { | @@ -9,7 +10,7 @@ export const getDeviceProfile = () => { | ||
| 9 | 10 | ||
| 10 | // 获取历史数据 | 11 | // 获取历史数据 |
| 11 | export const getDeviceHistoryInfo = (params) => { | 12 | export const getDeviceHistoryInfo = (params) => { |
| 12 | - return defHttp.get( | 13 | + return defHttp.get<HistoryData>( |
| 13 | { | 14 | { |
| 14 | url: `/plugins/telemetry/DEVICE/${params.entityId}/values/timeseries`, | 15 | url: `/plugins/telemetry/DEVICE/${params.entityId}/values/timeseries`, |
| 15 | params: { ...params, entityId: null, orderBy: 'ASC' }, | 16 | params: { ...params, entityId: null, orderBy: 'ASC' }, |
src/api/alarm/position/model/index.ts
0 → 100644
| @@ -4,8 +4,9 @@ import { | @@ -4,8 +4,9 @@ import { | ||
| 4 | ComponentInfoDetail, | 4 | ComponentInfoDetail, |
| 5 | DataBoardList, | 5 | DataBoardList, |
| 6 | DataComponentRecord, | 6 | DataComponentRecord, |
| 7 | + DeviceAttributeParams, | ||
| 8 | + DeviceAttributeRecord, | ||
| 7 | GetDataBoardParams, | 9 | GetDataBoardParams, |
| 8 | - Layout, | ||
| 9 | MasterDeviceList, | 10 | MasterDeviceList, |
| 10 | UpdateDataBoardLayoutParams, | 11 | UpdateDataBoardLayoutParams, |
| 11 | UpdateDataBoardParams, | 12 | UpdateDataBoardParams, |
| @@ -35,7 +36,7 @@ enum DataBoardShareUrl { | @@ -35,7 +36,7 @@ enum DataBoardShareUrl { | ||
| 35 | enum DeviceUrl { | 36 | enum DeviceUrl { |
| 36 | GET_DEVICE = '/device/list/master', | 37 | GET_DEVICE = '/device/list/master', |
| 37 | GET_SLAVE_DEVICE = '/device/list/slave', | 38 | GET_SLAVE_DEVICE = '/device/list/slave', |
| 38 | - GET_DEVICE_ATTRIBUTE = '/plugins/telemetry', | 39 | + GET_DEVICE_ATTRIBUTE = '/device/attributes', |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | /** | 42 | /** |
| @@ -184,14 +185,12 @@ export const getGatewaySlaveDevice = (params: { organizationId: string; masterId | @@ -184,14 +185,12 @@ export const getGatewaySlaveDevice = (params: { organizationId: string; masterId | ||
| 184 | * @param params | 185 | * @param params |
| 185 | * @returns | 186 | * @returns |
| 186 | */ | 187 | */ |
| 187 | -export const getDeviceAttributes = (params: { entityType?: string; deviceId: string }) => { | ||
| 188 | - const { entityType = 'DEVICE', deviceId } = params; | ||
| 189 | - return defHttp.get<string[]>( | ||
| 190 | - { | ||
| 191 | - url: `${DeviceUrl.GET_DEVICE_ATTRIBUTE}/${entityType}/${deviceId}/keys/timeseries`, | 188 | +export const getDeviceAttributes = (params: DeviceAttributeParams) => { |
| 189 | + const { deviceProfileId, dataType } = params; | ||
| 190 | + return defHttp.get<DeviceAttributeRecord[]>({ | ||
| 191 | + url: `${DeviceUrl.GET_DEVICE_ATTRIBUTE}/${deviceProfileId}`, | ||
| 192 | + params: { | ||
| 193 | + dataType, | ||
| 192 | }, | 194 | }, |
| 193 | - { | ||
| 194 | - joinPrefix: false, | ||
| 195 | - } | ||
| 196 | - ); | 195 | + }); |
| 197 | }; | 196 | }; |
| @@ -133,10 +133,26 @@ export interface UpdateDataComponentParams { | @@ -133,10 +133,26 @@ export interface UpdateDataComponentParams { | ||
| 133 | 133 | ||
| 134 | export interface MasterDeviceList { | 134 | export interface MasterDeviceList { |
| 135 | deviceType: 'DIRECT_CONNECTION' | 'GATEWAY'; | 135 | deviceType: 'DIRECT_CONNECTION' | 'GATEWAY'; |
| 136 | + deviceProfileId: string; | ||
| 136 | id: string; | 137 | id: string; |
| 137 | name: string; | 138 | name: string; |
| 139 | + label?: string; | ||
| 140 | + value?: string; | ||
| 138 | } | 141 | } |
| 139 | 142 | ||
| 140 | export interface ComponentInfoDetail { | 143 | export interface ComponentInfoDetail { |
| 141 | data: { componentData: DataComponentRecord[]; componentLayout: Layout[] }; | 144 | data: { componentData: DataComponentRecord[]; componentLayout: Layout[] }; |
| 142 | } | 145 | } |
| 146 | + | ||
| 147 | +export type DataType = 'BOOL' | 'DOUBLE' | 'INT' | 'STRUCT' | 'TExT'; | ||
| 148 | + | ||
| 149 | +export interface DeviceAttributeParams { | ||
| 150 | + deviceProfileId: string; | ||
| 151 | + dataType?: DataType; | ||
| 152 | +} | ||
| 153 | + | ||
| 154 | +export interface DeviceAttributeRecord { | ||
| 155 | + name: string; | ||
| 156 | + identifier: string; | ||
| 157 | + detail: DataType; | ||
| 158 | +} |
| @@ -479,7 +479,7 @@ export const selectDeviceAttrSchema: FormSchema[] = [ | @@ -479,7 +479,7 @@ export const selectDeviceAttrSchema: FormSchema[] = [ | ||
| 479 | }, | 479 | }, |
| 480 | ]; | 480 | ]; |
| 481 | 481 | ||
| 482 | -export const eChartOptions = (series, keys): EChartsOption => { | 482 | +export const eChartOptions = (series: EChartsOption['series'], keys: string[]): EChartsOption => { |
| 483 | return { | 483 | return { |
| 484 | tooltip: { | 484 | tooltip: { |
| 485 | trigger: 'axis', | 485 | trigger: 'axis', |
| 1 | +<script lang="ts" setup> | ||
| 2 | + import { BasicForm, useForm } from '/@/components/Form'; | ||
| 3 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
| 4 | + import { | ||
| 5 | + formSchema, | ||
| 6 | + getHistorySearchParams, | ||
| 7 | + SchemaFiled, | ||
| 8 | + } from '../../../board/detail/config/historyTrend.config'; | ||
| 9 | + import { HistoryModalOkEmitParams, HistoryModalParams } from './type'; | ||
| 10 | + import { DataSource } from '/@/api/dataBoard/model'; | ||
| 11 | + import { ref } from 'vue'; | ||
| 12 | + import { getAllDeviceByOrg } from '/@/api/dataBoard'; | ||
| 13 | + import { getDeviceHistoryInfo } from '/@/api/alarm/position'; | ||
| 14 | + | ||
| 15 | + const emit = defineEmits(['register', 'ok']); | ||
| 16 | + | ||
| 17 | + const [registerForm, { updateSchema, setFieldsValue, validate, getFieldsValue }] = useForm({ | ||
| 18 | + schemas: formSchema, | ||
| 19 | + showActionButtonGroup: false, | ||
| 20 | + fieldMapToTime: [ | ||
| 21 | + [SchemaFiled.DATE_RANGE, [SchemaFiled.START_TS, SchemaFiled.END_TS], 'YYYY-MM-DD HH:ss'], | ||
| 22 | + ], | ||
| 23 | + }); | ||
| 24 | + | ||
| 25 | + const [registerModal, { closeModal }] = useModalInner(async (params: HistoryModalParams) => { | ||
| 26 | + try { | ||
| 27 | + const { dataSource = [] } = params; | ||
| 28 | + const deviceRecord = dataSource?.at(0) || ({} as DataSource); | ||
| 29 | + if (!deviceRecord.organizationId) return; | ||
| 30 | + const deviceList = await getAllDeviceByOrg(deviceRecord.organizationId); | ||
| 31 | + const options = deviceList | ||
| 32 | + .filter((item) => item.id === deviceRecord.deviceId) | ||
| 33 | + .map((item) => ({ ...item, label: item.name, value: item.id })); | ||
| 34 | + const attKey = dataSource.map((item) => ({ | ||
| 35 | + ...item, | ||
| 36 | + label: item.attribute, | ||
| 37 | + value: item.attribute, | ||
| 38 | + })); | ||
| 39 | + updateSchema([ | ||
| 40 | + { | ||
| 41 | + field: SchemaFiled.DEVICE_ID, | ||
| 42 | + componentProps: { | ||
| 43 | + options, | ||
| 44 | + }, | ||
| 45 | + }, | ||
| 46 | + { | ||
| 47 | + field: SchemaFiled.KEYS, | ||
| 48 | + component: 'Select', | ||
| 49 | + defaultValue: attKey.map((item) => item.value), | ||
| 50 | + componentProps: { | ||
| 51 | + options: attKey, | ||
| 52 | + mode: 'multiple', | ||
| 53 | + disabled: true, | ||
| 54 | + }, | ||
| 55 | + }, | ||
| 56 | + ]); | ||
| 57 | + | ||
| 58 | + setFieldsValue({ | ||
| 59 | + [SchemaFiled.DEVICE_ID]: deviceRecord.deviceId, | ||
| 60 | + [SchemaFiled.KEYS]: attKey.map((item) => item.value), | ||
| 61 | + }); | ||
| 62 | + } catch (error) { | ||
| 63 | + throw error; | ||
| 64 | + } | ||
| 65 | + }); | ||
| 66 | + | ||
| 67 | + const loading = ref(false); | ||
| 68 | + const handleOk = async () => { | ||
| 69 | + try { | ||
| 70 | + await validate(); | ||
| 71 | + let value = getFieldsValue(); | ||
| 72 | + | ||
| 73 | + value = getHistorySearchParams(value); | ||
| 74 | + | ||
| 75 | + loading.value = true; | ||
| 76 | + | ||
| 77 | + const res = await getDeviceHistoryInfo({ | ||
| 78 | + ...value, | ||
| 79 | + [SchemaFiled.KEYS]: value[SchemaFiled.KEYS].join(','), | ||
| 80 | + }); | ||
| 81 | + | ||
| 82 | + let timespanList = Object.keys(res).reduce((prev, next) => { | ||
| 83 | + const ts = res[next].map((item) => item.ts); | ||
| 84 | + return [...prev, ...ts]; | ||
| 85 | + }, [] as number[]); | ||
| 86 | + timespanList = [...new Set(timespanList)]; | ||
| 87 | + | ||
| 88 | + const track: Record<'lng' | 'lat', number>[] = []; | ||
| 89 | + const keys = Object.keys(res); | ||
| 90 | + | ||
| 91 | + for (const ts of timespanList) { | ||
| 92 | + const list: { ts: number; value: number }[] = []; | ||
| 93 | + for (const key of keys) { | ||
| 94 | + const record = res[key].find((item) => ts === item.ts); | ||
| 95 | + list.push(record as any); | ||
| 96 | + } | ||
| 97 | + if (list.every(Boolean)) { | ||
| 98 | + const lng = list.at(0)?.value; | ||
| 99 | + const lat = list.at(1)?.value; | ||
| 100 | + if (lng && lat) track.push({ lng, lat }); | ||
| 101 | + } | ||
| 102 | + } | ||
| 103 | + emit('ok', { track, value } as HistoryModalOkEmitParams); | ||
| 104 | + closeModal(); | ||
| 105 | + } catch (error) { | ||
| 106 | + throw error; | ||
| 107 | + } finally { | ||
| 108 | + loading.value = false; | ||
| 109 | + } | ||
| 110 | + }; | ||
| 111 | +</script> | ||
| 112 | + | ||
| 113 | +<template> | ||
| 114 | + <BasicModal @register="registerModal" @ok="handleOk" :ok-button-props="{ loading }"> | ||
| 115 | + <BasicForm @register="registerForm" /> | ||
| 116 | + </BasicModal> | ||
| 117 | +</template> |
| @@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
| 4 | }; | 4 | }; |
| 5 | </script> | 5 | </script> |
| 6 | <script lang="ts" setup> | 6 | <script lang="ts" setup> |
| 7 | - import { computed, onMounted, ref, unref } from 'vue'; | 7 | + import { computed, onMounted, reactive, ref, unref, watchEffect } from 'vue'; |
| 8 | import { RadioRecord } from '../../detail/config/util'; | 8 | import { RadioRecord } from '../../detail/config/util'; |
| 9 | import { MapComponentLayout, MapComponentValue } from './map.config'; | 9 | import { MapComponentLayout, MapComponentValue } from './map.config'; |
| 10 | import { | 10 | import { |
| @@ -15,8 +15,14 @@ | @@ -15,8 +15,14 @@ | ||
| 15 | import { Button, Tooltip } from 'ant-design-vue'; | 15 | import { Button, Tooltip } from 'ant-design-vue'; |
| 16 | import { FrontComponent } from '../../const/const'; | 16 | import { FrontComponent } from '../../const/const'; |
| 17 | import { buildUUID } from '/@/utils/uuid'; | 17 | import { buildUUID } from '/@/utils/uuid'; |
| 18 | + import { useModal } from '/@/components/Modal'; | ||
| 19 | + import HistoryDataModel from './HistoryDataModel.vue'; | ||
| 20 | + import { HistoryModalOkEmitParams, HistoryModalParams } from './type'; | ||
| 21 | + import { formatToDateTime } from '/@/utils/dateUtil'; | ||
| 22 | + import { isEqual } from 'lodash-es'; | ||
| 18 | 23 | ||
| 19 | // useVisualBoardContext(); | 24 | // useVisualBoardContext(); |
| 25 | + type TrackRecord = Record<'lng' | 'lat' | 'ts', number>; | ||
| 20 | 26 | ||
| 21 | const startMethodName = `trackPlayMethod_${buildUUID()}`; | 27 | const startMethodName = `trackPlayMethod_${buildUUID()}`; |
| 22 | 28 | ||
| @@ -44,19 +50,51 @@ | @@ -44,19 +50,51 @@ | ||
| 44 | const trackAni = ref<Nullable<any>>(null); | 50 | const trackAni = ref<Nullable<any>>(null); |
| 45 | let mapInstance: Nullable<Recordable> = null; | 51 | let mapInstance: Nullable<Recordable> = null; |
| 46 | 52 | ||
| 53 | + const trackList = ref<TrackRecord[]>([]); | ||
| 54 | + | ||
| 55 | + watchEffect(() => { | ||
| 56 | + if ( | ||
| 57 | + props.layout?.componentType === FrontComponent.MAP_COMPONENT_TRACK_REAL && | ||
| 58 | + props.value?.track?.length | ||
| 59 | + ) { | ||
| 60 | + const lng = props.value.track.at(0)!; | ||
| 61 | + const lat = props.value.track.at(1)!; | ||
| 62 | + const record = { | ||
| 63 | + lng: lng.value, | ||
| 64 | + lat: lat.value, | ||
| 65 | + ts: lng.ts, | ||
| 66 | + }; | ||
| 67 | + if (unref(trackList).length && isEqual(unref(trackList).at(-1), record)) return; | ||
| 68 | + trackList.value.push(record); | ||
| 69 | + | ||
| 70 | + randomAnimation(unref(trackList)); | ||
| 71 | + // marketPoint(record); | ||
| 72 | + } | ||
| 73 | + }); | ||
| 74 | + | ||
| 75 | + // function marketPoint(params: Record<'lng' | 'lat', number>) { | ||
| 76 | + // const { lng, lat } = params; | ||
| 77 | + // const BMap = (window as any).BMapGL; | ||
| 78 | + // const marker = new BMap.Marker(new BMap.Point(lng, lat)); | ||
| 79 | + // unref(mapInstance)?.centerAndZoom(new BMap.Point(lng, lat)); | ||
| 80 | + // unref(mapInstance)?.addOverlay(marker); | ||
| 81 | + // } | ||
| 82 | + | ||
| 47 | async function initMap() { | 83 | async function initMap() { |
| 48 | const wrapEl = unref(wrapRef); | 84 | const wrapEl = unref(wrapRef); |
| 49 | if (!wrapEl) return; | 85 | if (!wrapEl) return; |
| 50 | const BMapGL = (window as any).BMapGL; | 86 | const BMapGL = (window as any).BMapGL; |
| 51 | mapInstance = new BMapGL.Map(wrapId); | 87 | mapInstance = new BMapGL.Map(wrapId); |
| 52 | - const point = new BMapGL.Point(116.404, 39.915); | 88 | + const point = new BMapGL.Point(104.09457, 30.53189); |
| 53 | mapInstance!.centerAndZoom(point, 15); | 89 | mapInstance!.centerAndZoom(point, 15); |
| 54 | mapInstance!.enableScrollWheelZoom(true); | 90 | mapInstance!.enableScrollWheelZoom(true); |
| 55 | - props.layout?.componentType === FrontComponent.MAP_COMPONENT_TRACK_HISTORY && randomAnimation(); | 91 | + props.layout?.componentType === FrontComponent.MAP_COMPONENT_TRACK_HISTORY && |
| 92 | + props.random && | ||
| 93 | + randomAnimation(); | ||
| 56 | } | 94 | } |
| 57 | 95 | ||
| 58 | - const randomAnimation = () => { | ||
| 59 | - const path = [ | 96 | + const randomAnimation = (path?: Record<'lng' | 'lat', number>[], clearOverlays = true) => { |
| 97 | + path = path || [ | ||
| 60 | { | 98 | { |
| 61 | lng: 116.297611, | 99 | lng: 116.297611, |
| 62 | lat: 40.047363, | 100 | lat: 40.047363, |
| @@ -90,6 +128,8 @@ | @@ -90,6 +128,8 @@ | ||
| 90 | const point: any[] = []; | 128 | const point: any[] = []; |
| 91 | const BMapGL = (window as any).BMapGL; | 129 | const BMapGL = (window as any).BMapGL; |
| 92 | 130 | ||
| 131 | + clearOverlays && unref(mapInstance)?.clearOverlays(); | ||
| 132 | + | ||
| 93 | for (const { lng, lat } of path) { | 133 | for (const { lng, lat } of path) { |
| 94 | point.push(new BMapGL.Point(lng, lat)); | 134 | point.push(new BMapGL.Point(lng, lat)); |
| 95 | } | 135 | } |
| @@ -99,10 +139,10 @@ | @@ -99,10 +139,10 @@ | ||
| 99 | 139 | ||
| 100 | const dynamicPlayMethod = { | 140 | const dynamicPlayMethod = { |
| 101 | [startMethodName]() { | 141 | [startMethodName]() { |
| 102 | - trackAni.value = new BMapGLLib.TrackAnimation(mapInstance, pl, { | 142 | + trackAni.value = new BMapGLLib.TrackAnimation(unref(mapInstance), pl, { |
| 103 | overallView: true, | 143 | overallView: true, |
| 104 | tilt: 30, | 144 | tilt: 30, |
| 105 | - duration: 20000, | 145 | + duration: 5000, |
| 106 | delay: 300, | 146 | delay: 300, |
| 107 | }); | 147 | }); |
| 108 | trackAni.value!.start(); | 148 | trackAni.value!.start(); |
| @@ -118,11 +158,26 @@ | @@ -118,11 +158,26 @@ | ||
| 118 | initMap(); | 158 | initMap(); |
| 119 | }); | 159 | }); |
| 120 | 160 | ||
| 161 | + const timeRange = reactive<Record<'start' | 'end', Nullable<number>>>({ | ||
| 162 | + start: null, | ||
| 163 | + end: null, | ||
| 164 | + }); | ||
| 121 | const getTimeRange = computed(() => { | 165 | const getTimeRange = computed(() => { |
| 122 | - return ` - 从 ${'2020-10-20 10:10:10'} 到 ${'2020-10-20 10:10:10'}`; | 166 | + const { start, end } = timeRange; |
| 167 | + if (!start || !end) return `- 请选择`; | ||
| 168 | + return ` - 从 ${formatToDateTime(start, 'YYYY-MM-DD HH:mm:ss')} 到 ${formatToDateTime( | ||
| 169 | + end, | ||
| 170 | + 'YYYY-MM-DD HH:mm:ss' | ||
| 171 | + )}`; | ||
| 123 | }); | 172 | }); |
| 124 | 173 | ||
| 125 | - const handleTrackSwitch = () => {}; | 174 | + const [register, { openModal }] = useModal(); |
| 175 | + | ||
| 176 | + const handleTrackSwitch = () => { | ||
| 177 | + openModal(true, { | ||
| 178 | + dataSource: props.value?.dataSource || [], | ||
| 179 | + } as HistoryModalParams); | ||
| 180 | + }; | ||
| 126 | 181 | ||
| 127 | const getTrackPlayStatus = computed(() => { | 182 | const getTrackPlayStatus = computed(() => { |
| 128 | return (trackAni.value || {})._status; | 183 | return (trackAni.value || {})._status; |
| @@ -133,10 +188,17 @@ | @@ -133,10 +188,17 @@ | ||
| 133 | else if (unref(getTrackPlayStatus) === TrackAnimationStatus.PLAY) unref(trackAni).pause(); | 188 | else if (unref(getTrackPlayStatus) === TrackAnimationStatus.PLAY) unref(trackAni).pause(); |
| 134 | else if (unref(getTrackPlayStatus) === TrackAnimationStatus.PAUSE) unref(trackAni).continue(); | 189 | else if (unref(getTrackPlayStatus) === TrackAnimationStatus.PAUSE) unref(trackAni).continue(); |
| 135 | }; | 190 | }; |
| 191 | + | ||
| 192 | + const handleRenderHistroyData = (params: HistoryModalOkEmitParams) => { | ||
| 193 | + const { track, value } = params; | ||
| 194 | + track.length && randomAnimation(track as unknown as Record<'lng' | 'lat', number>[]); | ||
| 195 | + timeRange.start = value.startTs as number; | ||
| 196 | + timeRange.end = value.endTs as number; | ||
| 197 | + }; | ||
| 136 | </script> | 198 | </script> |
| 137 | 199 | ||
| 138 | <template> | 200 | <template> |
| 139 | - <div class="w-full h-full flex justify-center items-center flex-col"> | 201 | + <div class="w-full h-full flex justify-center items-center flex-col p-2"> |
| 140 | <div | 202 | <div |
| 141 | class="w-full flex" | 203 | class="w-full flex" |
| 142 | v-if="props.layout?.componentType === FrontComponent.MAP_COMPONENT_TRACK_HISTORY" | 204 | v-if="props.layout?.componentType === FrontComponent.MAP_COMPONENT_TRACK_HISTORY" |
| @@ -144,7 +206,7 @@ | @@ -144,7 +206,7 @@ | ||
| 144 | <Button type="text" class="!px-2 flex-auto !text-left truncate" @click="handleTrackSwitch"> | 206 | <Button type="text" class="!px-2 flex-auto !text-left truncate" @click="handleTrackSwitch"> |
| 145 | <div class="w-full truncate text-gray-500 flex items-center"> | 207 | <div class="w-full truncate text-gray-500 flex items-center"> |
| 146 | <ClockCircleOutlined /> | 208 | <ClockCircleOutlined /> |
| 147 | - <span class="mx-1">实时</span> | 209 | + <span class="mx-1">历史</span> |
| 148 | <Tooltip :title="getTimeRange.replace('-', '')"> | 210 | <Tooltip :title="getTimeRange.replace('-', '')"> |
| 149 | <span class="truncate"> | 211 | <span class="truncate"> |
| 150 | {{ getTimeRange }} | 212 | {{ getTimeRange }} |
| @@ -154,12 +216,16 @@ | @@ -154,12 +216,16 @@ | ||
| 154 | </Button> | 216 | </Button> |
| 155 | <Button type="text" class="!px-2 !text-gray-500" @click="handlePlay"> | 217 | <Button type="text" class="!px-2 !text-gray-500" @click="handlePlay"> |
| 156 | <PlayCircleOutlined v-show="getTrackPlayStatus !== TrackAnimationStatus.PLAY" /> | 218 | <PlayCircleOutlined v-show="getTrackPlayStatus !== TrackAnimationStatus.PLAY" /> |
| 157 | - <PauseCircleOutlined v-show="getTrackPlayStatus === TrackAnimationStatus.PLAY" /> | 219 | + <PauseCircleOutlined |
| 220 | + class="!ml-0" | ||
| 221 | + v-show="getTrackPlayStatus === TrackAnimationStatus.PLAY" | ||
| 222 | + /> | ||
| 158 | <span> | 223 | <span> |
| 159 | {{ getTrackPlayStatus !== TrackAnimationStatus.PLAY ? '播放轨迹' : '暂停播放' }} | 224 | {{ getTrackPlayStatus !== TrackAnimationStatus.PLAY ? '播放轨迹' : '暂停播放' }} |
| 160 | </span> | 225 | </span> |
| 161 | </Button> | 226 | </Button> |
| 162 | </div> | 227 | </div> |
| 163 | <div ref="wrapRef" :id="wrapId" class="w-full h-full"></div> | 228 | <div ref="wrapRef" :id="wrapId" class="w-full h-full"></div> |
| 229 | + <HistoryDataModel @register="register" @ok="handleRenderHistroyData" /> | ||
| 164 | </div> | 230 | </div> |
| 165 | </template> | 231 | </template> |
| 1 | import { FrontComponent } from '../../const/const'; | 1 | import { FrontComponent } from '../../const/const'; |
| 2 | import { ComponentConfig } from '../../types/type'; | 2 | import { ComponentConfig } from '../../types/type'; |
| 3 | +import { DataSource } from '/@/api/dataBoard/model'; | ||
| 3 | 4 | ||
| 4 | export interface MapComponentLayout { | 5 | export interface MapComponentLayout { |
| 5 | componentType?: FrontComponent; | 6 | componentType?: FrontComponent; |
| @@ -7,7 +8,8 @@ export interface MapComponentLayout { | @@ -7,7 +8,8 @@ export interface MapComponentLayout { | ||
| 7 | 8 | ||
| 8 | export interface MapComponentValue { | 9 | export interface MapComponentValue { |
| 9 | icon?: string; | 10 | icon?: string; |
| 10 | - track?: Recordable[]; | 11 | + track?: Record<'ts' | 'value', number>[]; |
| 12 | + dataSource?: DataSource[]; | ||
| 11 | } | 13 | } |
| 12 | 14 | ||
| 13 | interface Config { | 15 | interface Config { |
| @@ -22,14 +24,36 @@ export const MapRealTrackConfig: Config = { | @@ -22,14 +24,36 @@ export const MapRealTrackConfig: Config = { | ||
| 22 | componentType: FrontComponent.MAP_COMPONENT_TRACK_REAL, | 24 | componentType: FrontComponent.MAP_COMPONENT_TRACK_REAL, |
| 23 | }; | 25 | }; |
| 24 | 26 | ||
| 27 | +const getTrack = (dataSource: DataSource[]) => { | ||
| 28 | + if (dataSource.length >= 2) { | ||
| 29 | + const trackRecord = dataSource.slice(0, 2); | ||
| 30 | + if (!trackRecord.every((item) => item.componentInfo.value)) | ||
| 31 | + return { track: [], dataSource: [] }; | ||
| 32 | + | ||
| 33 | + const track = trackRecord.map((item) => { | ||
| 34 | + return { | ||
| 35 | + ts: item.componentInfo.updateTime, | ||
| 36 | + value: item.componentInfo.value || 0, | ||
| 37 | + }; | ||
| 38 | + }); | ||
| 39 | + return { track, dataSource }; | ||
| 40 | + } | ||
| 41 | + return { track: [], dataSource: [] }; | ||
| 42 | +}; | ||
| 43 | + | ||
| 25 | export const transfromMapComponentConfig: ComponentConfig['transformConfig'] = ( | 44 | export const transfromMapComponentConfig: ComponentConfig['transformConfig'] = ( |
| 26 | componentConfig: Config, | 45 | componentConfig: Config, |
| 27 | _record, | 46 | _record, |
| 28 | - _dataSourceRecord | 47 | + dataSourceRecord |
| 29 | ) => { | 48 | ) => { |
| 49 | + const { track, dataSource } = getTrack(dataSourceRecord as DataSource[]); | ||
| 30 | return { | 50 | return { |
| 31 | layout: { | 51 | layout: { |
| 32 | ...componentConfig, | 52 | ...componentConfig, |
| 33 | - }, | 53 | + } as MapComponentLayout, |
| 54 | + value: { | ||
| 55 | + track, | ||
| 56 | + dataSource, | ||
| 57 | + } as MapComponentValue, | ||
| 34 | }; | 58 | }; |
| 35 | }; | 59 | }; |
| 1 | +import { SchemaFiled } from '../../detail/config/historyTrend.config'; | ||
| 2 | +import { DataSource } from '/@/api/dataBoard/model'; | ||
| 3 | + | ||
| 4 | +export interface HistoryModalParams { | ||
| 5 | + dataSource?: DataSource[]; | ||
| 6 | +} | ||
| 7 | + | ||
| 8 | +export interface HistoryModalOkEmitParams { | ||
| 9 | + track: { | ||
| 10 | + lng: number | string; | ||
| 11 | + lat: number | string; | ||
| 12 | + }[]; | ||
| 13 | + value: Record<SchemaFiled, string | number>; | ||
| 14 | +} |
| @@ -2,11 +2,22 @@ | @@ -2,11 +2,22 @@ | ||
| 2 | import { useUpdateCenter } from '../../hook/useUpdateCenter'; | 2 | import { useUpdateCenter } from '../../hook/useUpdateCenter'; |
| 3 | import { FrontDataSourceRecord } from '../../types/type'; | 3 | import { FrontDataSourceRecord } from '../../types/type'; |
| 4 | import { createVisualBoardContext } from '../../hook/useVisualBoardContext'; | 4 | import { createVisualBoardContext } from '../../hook/useVisualBoardContext'; |
| 5 | + import { DataComponentRecord } from '/@/api/dataBoard/model'; | ||
| 6 | + import { computed } from 'vue'; | ||
| 7 | + import { frontComponentMap } from '../help'; | ||
| 8 | + import { FrontComponent } from '../../const/const'; | ||
| 5 | 9 | ||
| 6 | const props = defineProps<{ | 10 | const props = defineProps<{ |
| 11 | + record: DataComponentRecord; | ||
| 7 | dataSource: FrontDataSourceRecord[]; | 12 | dataSource: FrontDataSourceRecord[]; |
| 8 | }>(); | 13 | }>(); |
| 9 | 14 | ||
| 15 | + const isMultipleDataSource = computed(() => { | ||
| 16 | + const { record } = props; | ||
| 17 | + const componentInfo = frontComponentMap.get(record.frontId as FrontComponent); | ||
| 18 | + return componentInfo?.isMultipleDataSource; | ||
| 19 | + }); | ||
| 20 | + | ||
| 10 | const { update, add, remove } = useUpdateCenter(); | 21 | const { update, add, remove } = useUpdateCenter(); |
| 11 | 22 | ||
| 12 | createVisualBoardContext({ update, add, remove }); | 23 | createVisualBoardContext({ update, add, remove }); |
| @@ -19,24 +30,41 @@ | @@ -19,24 +30,41 @@ | ||
| 19 | <slot name="header"></slot> | 30 | <slot name="header"></slot> |
| 20 | 31 | ||
| 21 | <div class="widget-content"> | 32 | <div class="widget-content"> |
| 22 | - <div | ||
| 23 | - v-for="item in props.dataSource" | ||
| 24 | - :key="item.id" | ||
| 25 | - :style="{ width: `${item.width || 100}%`, height: `${item.height || 100}%` }" | ||
| 26 | - class="widget-item" | ||
| 27 | - > | ||
| 28 | - <div class="widget-box"> | ||
| 29 | - <div class="widget-controls-container"> | ||
| 30 | - <slot | ||
| 31 | - name="controls" | ||
| 32 | - :record="item" | ||
| 33 | - :add="add" | ||
| 34 | - :remove="remove" | ||
| 35 | - :update="update" | ||
| 36 | - ></slot> | 33 | + <template v-if="!isMultipleDataSource"> |
| 34 | + <div | ||
| 35 | + v-for="item in props.dataSource" | ||
| 36 | + :key="item.id" | ||
| 37 | + :style="{ width: `${item.width || 100}%`, height: `${item.height || 100}%` }" | ||
| 38 | + class="widget-item" | ||
| 39 | + > | ||
| 40 | + <div class="widget-box"> | ||
| 41 | + <div class="widget-controls-container"> | ||
| 42 | + <slot | ||
| 43 | + name="controls" | ||
| 44 | + :record="item" | ||
| 45 | + :add="add" | ||
| 46 | + :remove="remove" | ||
| 47 | + :update="update" | ||
| 48 | + ></slot> | ||
| 49 | + </div> | ||
| 50 | + </div> | ||
| 51 | + </div> | ||
| 52 | + </template> | ||
| 53 | + <template v-if="isMultipleDataSource"> | ||
| 54 | + <div :style="{ width: `${100}%`, height: `${100}%` }" class="widget-item"> | ||
| 55 | + <div class="widget-box"> | ||
| 56 | + <div class="widget-controls-container"> | ||
| 57 | + <slot | ||
| 58 | + name="controls" | ||
| 59 | + :record="props.dataSource" | ||
| 60 | + :add="add" | ||
| 61 | + :remove="remove" | ||
| 62 | + :update="update" | ||
| 63 | + ></slot> | ||
| 64 | + </div> | ||
| 37 | </div> | 65 | </div> |
| 38 | </div> | 66 | </div> |
| 39 | - </div> | 67 | + </template> |
| 40 | </div> | 68 | </div> |
| 41 | <slot name="footer"></slot> | 69 | <slot name="footer"></slot> |
| 42 | </section> | 70 | </section> |
| @@ -45,6 +45,9 @@ frontComponentMap.set(FrontComponent.TEXT_COMPONENT_1, { | @@ -45,6 +45,9 @@ frontComponentMap.set(FrontComponent.TEXT_COMPONENT_1, { | ||
| 45 | ComponentKey: FrontComponent.TEXT_COMPONENT_1, | 45 | ComponentKey: FrontComponent.TEXT_COMPONENT_1, |
| 46 | ComponentConfig: TextComponent1Config, | 46 | ComponentConfig: TextComponent1Config, |
| 47 | ComponentCategory: FrontComponentCategory.TEXT, | 47 | ComponentCategory: FrontComponentCategory.TEXT, |
| 48 | + isMultipleDataSource: false, | ||
| 49 | + hasHistoryTrend: true, | ||
| 50 | + hasSetting: true, | ||
| 48 | transformConfig: transformTextComponentConfig, | 51 | transformConfig: transformTextComponentConfig, |
| 49 | }); | 52 | }); |
| 50 | 53 | ||
| @@ -54,6 +57,9 @@ frontComponentMap.set(FrontComponent.TEXT_COMPONENT_3, { | @@ -54,6 +57,9 @@ frontComponentMap.set(FrontComponent.TEXT_COMPONENT_3, { | ||
| 54 | ComponentKey: FrontComponent.TEXT_COMPONENT_3, | 57 | ComponentKey: FrontComponent.TEXT_COMPONENT_3, |
| 55 | ComponentConfig: TextComponent3Config, | 58 | ComponentConfig: TextComponent3Config, |
| 56 | ComponentCategory: FrontComponentCategory.TEXT, | 59 | ComponentCategory: FrontComponentCategory.TEXT, |
| 60 | + isMultipleDataSource: false, | ||
| 61 | + hasHistoryTrend: true, | ||
| 62 | + hasSetting: true, | ||
| 57 | transformConfig: transformTextComponentConfig, | 63 | transformConfig: transformTextComponentConfig, |
| 58 | }); | 64 | }); |
| 59 | 65 | ||
| @@ -63,6 +69,9 @@ frontComponentMap.set(FrontComponent.TEXT_COMPONENT_4, { | @@ -63,6 +69,9 @@ frontComponentMap.set(FrontComponent.TEXT_COMPONENT_4, { | ||
| 63 | ComponentKey: FrontComponent.TEXT_COMPONENT_4, | 69 | ComponentKey: FrontComponent.TEXT_COMPONENT_4, |
| 64 | ComponentConfig: TextComponent4Config, | 70 | ComponentConfig: TextComponent4Config, |
| 65 | ComponentCategory: FrontComponentCategory.TEXT, | 71 | ComponentCategory: FrontComponentCategory.TEXT, |
| 72 | + isMultipleDataSource: false, | ||
| 73 | + hasHistoryTrend: true, | ||
| 74 | + hasSetting: true, | ||
| 66 | transformConfig: transformTextComponentConfig, | 75 | transformConfig: transformTextComponentConfig, |
| 67 | }); | 76 | }); |
| 68 | 77 | ||
| @@ -72,6 +81,9 @@ frontComponentMap.set(FrontComponent.TEXT_COMPONENT_5, { | @@ -72,6 +81,9 @@ frontComponentMap.set(FrontComponent.TEXT_COMPONENT_5, { | ||
| 72 | ComponentKey: FrontComponent.TEXT_COMPONENT_5, | 81 | ComponentKey: FrontComponent.TEXT_COMPONENT_5, |
| 73 | ComponentConfig: TextComponent5Config, | 82 | ComponentConfig: TextComponent5Config, |
| 74 | ComponentCategory: FrontComponentCategory.TEXT, | 83 | ComponentCategory: FrontComponentCategory.TEXT, |
| 84 | + isMultipleDataSource: false, | ||
| 85 | + hasHistoryTrend: true, | ||
| 86 | + hasSetting: true, | ||
| 75 | transformConfig: transformTextComponentConfig, | 87 | transformConfig: transformTextComponentConfig, |
| 76 | }); | 88 | }); |
| 77 | 89 | ||
| @@ -84,6 +96,9 @@ frontComponentMap.set(FrontComponent.INSTRUMENT_COMPONENT_1, { | @@ -84,6 +96,9 @@ frontComponentMap.set(FrontComponent.INSTRUMENT_COMPONENT_1, { | ||
| 84 | componentType: FrontComponent.INSTRUMENT_COMPONENT_1, | 96 | componentType: FrontComponent.INSTRUMENT_COMPONENT_1, |
| 85 | } as DashboardComponentLayout, | 97 | } as DashboardComponentLayout, |
| 86 | ComponentCategory: FrontComponentCategory.INSTRUMENT, | 98 | ComponentCategory: FrontComponentCategory.INSTRUMENT, |
| 99 | + isMultipleDataSource: false, | ||
| 100 | + hasHistoryTrend: true, | ||
| 101 | + hasSetting: true, | ||
| 87 | transformConfig: transformDashboardComponentConfig, | 102 | transformConfig: transformDashboardComponentConfig, |
| 88 | }); | 103 | }); |
| 89 | 104 | ||
| @@ -96,6 +111,9 @@ frontComponentMap.set(FrontComponent.INSTRUMENT_COMPONENT_2, { | @@ -96,6 +111,9 @@ frontComponentMap.set(FrontComponent.INSTRUMENT_COMPONENT_2, { | ||
| 96 | componentType: FrontComponent.INSTRUMENT_COMPONENT_2, | 111 | componentType: FrontComponent.INSTRUMENT_COMPONENT_2, |
| 97 | } as DashboardComponentLayout, | 112 | } as DashboardComponentLayout, |
| 98 | ComponentCategory: FrontComponentCategory.INSTRUMENT, | 113 | ComponentCategory: FrontComponentCategory.INSTRUMENT, |
| 114 | + isMultipleDataSource: false, | ||
| 115 | + hasHistoryTrend: true, | ||
| 116 | + hasSetting: true, | ||
| 99 | transformConfig: transformDashboardComponentConfig, | 117 | transformConfig: transformDashboardComponentConfig, |
| 100 | }); | 118 | }); |
| 101 | 119 | ||
| @@ -104,6 +122,9 @@ frontComponentMap.set(FrontComponent.DIGITAL_DASHBOARD_COMPONENT, { | @@ -104,6 +122,9 @@ frontComponentMap.set(FrontComponent.DIGITAL_DASHBOARD_COMPONENT, { | ||
| 104 | ComponentName: '数字仪表盘', | 122 | ComponentName: '数字仪表盘', |
| 105 | ComponentKey: FrontComponent.DIGITAL_DASHBOARD_COMPONENT, | 123 | ComponentKey: FrontComponent.DIGITAL_DASHBOARD_COMPONENT, |
| 106 | ComponentCategory: FrontComponentCategory.INSTRUMENT, | 124 | ComponentCategory: FrontComponentCategory.INSTRUMENT, |
| 125 | + isMultipleDataSource: false, | ||
| 126 | + hasHistoryTrend: true, | ||
| 127 | + hasSetting: true, | ||
| 107 | transformConfig: transformDashboardComponentConfig, | 128 | transformConfig: transformDashboardComponentConfig, |
| 108 | }); | 129 | }); |
| 109 | 130 | ||
| @@ -112,6 +133,9 @@ frontComponentMap.set(FrontComponent.PICTURE_COMPONENT_1, { | @@ -112,6 +133,9 @@ frontComponentMap.set(FrontComponent.PICTURE_COMPONENT_1, { | ||
| 112 | ComponentName: '图片组件', | 133 | ComponentName: '图片组件', |
| 113 | ComponentKey: FrontComponent.PICTURE_COMPONENT_1, | 134 | ComponentKey: FrontComponent.PICTURE_COMPONENT_1, |
| 114 | ComponentCategory: FrontComponentCategory.PICTURE, | 135 | ComponentCategory: FrontComponentCategory.PICTURE, |
| 136 | + isMultipleDataSource: false, | ||
| 137 | + hasHistoryTrend: true, | ||
| 138 | + hasSetting: false, | ||
| 115 | transformConfig: transformPictureConfig, | 139 | transformConfig: transformPictureConfig, |
| 116 | }); | 140 | }); |
| 117 | 141 | ||
| @@ -120,6 +144,9 @@ frontComponentMap.set(FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON, { | @@ -120,6 +144,9 @@ frontComponentMap.set(FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON, { | ||
| 120 | ComponentName: '控制按钮1', | 144 | ComponentName: '控制按钮1', |
| 121 | ComponentKey: FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON, | 145 | ComponentKey: FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON, |
| 122 | ComponentCategory: FrontComponentCategory.CONTROL, | 146 | ComponentCategory: FrontComponentCategory.CONTROL, |
| 147 | + isMultipleDataSource: false, | ||
| 148 | + hasHistoryTrend: true, | ||
| 149 | + hasSetting: false, | ||
| 123 | transformConfig: transformControlConfig, | 150 | transformConfig: transformControlConfig, |
| 124 | }); | 151 | }); |
| 125 | 152 | ||
| @@ -128,6 +155,9 @@ frontComponentMap.set(FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH, { | @@ -128,6 +155,9 @@ frontComponentMap.set(FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH, { | ||
| 128 | ComponentName: '控制按钮2', | 155 | ComponentName: '控制按钮2', |
| 129 | ComponentKey: FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH, | 156 | ComponentKey: FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH, |
| 130 | ComponentCategory: FrontComponentCategory.CONTROL, | 157 | ComponentCategory: FrontComponentCategory.CONTROL, |
| 158 | + isMultipleDataSource: false, | ||
| 159 | + hasHistoryTrend: true, | ||
| 160 | + hasSetting: false, | ||
| 131 | transformConfig: transformControlConfig, | 161 | transformConfig: transformControlConfig, |
| 132 | }); | 162 | }); |
| 133 | 163 | ||
| @@ -136,6 +166,9 @@ frontComponentMap.set(FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, { | @@ -136,6 +166,9 @@ frontComponentMap.set(FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, { | ||
| 136 | ComponentName: '控制按钮3', | 166 | ComponentName: '控制按钮3', |
| 137 | ComponentKey: FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, | 167 | ComponentKey: FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, |
| 138 | ComponentCategory: FrontComponentCategory.CONTROL, | 168 | ComponentCategory: FrontComponentCategory.CONTROL, |
| 169 | + isMultipleDataSource: false, | ||
| 170 | + hasHistoryTrend: true, | ||
| 171 | + hasSetting: false, | ||
| 139 | transformConfig: transformControlConfig, | 172 | transformConfig: transformControlConfig, |
| 140 | }); | 173 | }); |
| 141 | 174 | ||
| @@ -145,6 +178,9 @@ frontComponentMap.set(FrontComponent.MAP_COMPONENT_TRACK_REAL, { | @@ -145,6 +178,9 @@ frontComponentMap.set(FrontComponent.MAP_COMPONENT_TRACK_REAL, { | ||
| 145 | ComponentKey: FrontComponent.MAP_COMPONENT_TRACK_REAL, | 178 | ComponentKey: FrontComponent.MAP_COMPONENT_TRACK_REAL, |
| 146 | ComponentConfig: MapRealTrackConfig, | 179 | ComponentConfig: MapRealTrackConfig, |
| 147 | ComponentCategory: FrontComponentCategory.MAP, | 180 | ComponentCategory: FrontComponentCategory.MAP, |
| 181 | + isMultipleDataSource: true, | ||
| 182 | + hasHistoryTrend: false, | ||
| 183 | + hasSetting: false, | ||
| 148 | transformConfig: transfromMapComponentConfig, | 184 | transformConfig: transfromMapComponentConfig, |
| 149 | }); | 185 | }); |
| 150 | 186 | ||
| @@ -154,6 +190,9 @@ frontComponentMap.set(FrontComponent.MAP_COMPONENT_TRACK_HISTORY, { | @@ -154,6 +190,9 @@ frontComponentMap.set(FrontComponent.MAP_COMPONENT_TRACK_HISTORY, { | ||
| 154 | ComponentKey: FrontComponent.MAP_COMPONENT_TRACK_HISTORY, | 190 | ComponentKey: FrontComponent.MAP_COMPONENT_TRACK_HISTORY, |
| 155 | ComponentConfig: MaphistoryTrackConfig, | 191 | ComponentConfig: MaphistoryTrackConfig, |
| 156 | ComponentCategory: FrontComponentCategory.MAP, | 192 | ComponentCategory: FrontComponentCategory.MAP, |
| 193 | + isMultipleDataSource: true, | ||
| 194 | + hasHistoryTrend: false, | ||
| 195 | + hasSetting: false, | ||
| 157 | transformConfig: transfromMapComponentConfig, | 196 | transformConfig: transfromMapComponentConfig, |
| 158 | }); | 197 | }); |
| 159 | 198 |
| 1 | <script lang="ts" setup> | 1 | <script lang="ts" setup> |
| 2 | - import { CopyOutlined, DeleteOutlined, SettingOutlined } from '@ant-design/icons-vue'; | 2 | + import { |
| 3 | + CopyOutlined, | ||
| 4 | + DeleteOutlined, | ||
| 5 | + SettingOutlined, | ||
| 6 | + SwapOutlined, | ||
| 7 | + } from '@ant-design/icons-vue'; | ||
| 3 | import { Tooltip, Button } from 'ant-design-vue'; | 8 | import { Tooltip, Button } from 'ant-design-vue'; |
| 4 | import { FormActionType, useForm } from '/@/components/Form'; | 9 | import { FormActionType, useForm } from '/@/components/Form'; |
| 5 | - import { basicSchema } from '../config/basicConfiguration'; | 10 | + import { basicSchema, DataSourceField } from '../config/basicConfiguration'; |
| 6 | import BasicForm from '/@/components/Form/src/BasicForm.vue'; | 11 | import BasicForm from '/@/components/Form/src/BasicForm.vue'; |
| 7 | - import { ref, shallowReactive, unref, nextTick, watch, computed } from 'vue'; | 12 | + import { ref, shallowReactive, unref, nextTick, watch, computed, onMounted } from 'vue'; |
| 8 | import VisualOptionsModal from './VisualOptionsModal.vue'; | 13 | import VisualOptionsModal from './VisualOptionsModal.vue'; |
| 9 | import { useModal } from '/@/components/Modal'; | 14 | import { useModal } from '/@/components/Modal'; |
| 10 | import { buildUUID } from '/@/utils/uuid'; | 15 | import { buildUUID } from '/@/utils/uuid'; |
| @@ -13,6 +18,10 @@ | @@ -13,6 +18,10 @@ | ||
| 13 | import { DataBoardLayoutInfo } from '../../types/type'; | 18 | import { DataBoardLayoutInfo } from '../../types/type'; |
| 14 | import { getDataSourceComponent } from './DataSourceForm/help'; | 19 | import { getDataSourceComponent } from './DataSourceForm/help'; |
| 15 | import { FrontComponent } from '../../const/const'; | 20 | import { FrontComponent } from '../../const/const'; |
| 21 | + import { isNullAndUnDef } from '/@/utils/is'; | ||
| 22 | + import { useSortable } from '/@/hooks/web/useSortable'; | ||
| 23 | + import { cloneDeep } from 'lodash-es'; | ||
| 24 | + import { frontComponentMap } from '../../components/help'; | ||
| 16 | 25 | ||
| 17 | type DataSourceFormEL = { [key: string]: Nullable<FormActionType> }; | 26 | type DataSourceFormEL = { [key: string]: Nullable<FormActionType> }; |
| 18 | 27 | ||
| @@ -61,27 +70,55 @@ | @@ -61,27 +70,55 @@ | ||
| 61 | 70 | ||
| 62 | const validateDataSourceField = async () => { | 71 | const validateDataSourceField = async () => { |
| 63 | const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]); | 72 | const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]); |
| 64 | - const _dataSource: boolean[] = []; | 73 | + const _dataSource: Record<DataSourceField, string>[] = []; |
| 65 | for (const id of hasExistEl) { | 74 | for (const id of hasExistEl) { |
| 66 | - const flag = (await (dataSourceEl[id] as FormActionType).validate()) as boolean; | ||
| 67 | - _dataSource.push(flag); | 75 | + const flag = (await (dataSourceEl[id] as FormActionType).validate()) as Record< |
| 76 | + DataSourceField, | ||
| 77 | + string | ||
| 78 | + >; | ||
| 79 | + flag && _dataSource.push(flag); | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + if ( | ||
| 83 | + [ | ||
| 84 | + FrontComponent.MAP_COMPONENT_TRACK_HISTORY, | ||
| 85 | + FrontComponent.MAP_COMPONENT_TRACK_REAL, | ||
| 86 | + ].includes(props.frontId!) | ||
| 87 | + ) { | ||
| 88 | + await validateMapComponent(_dataSource); | ||
| 68 | } | 89 | } |
| 69 | return _dataSource; | 90 | return _dataSource; |
| 70 | }; | 91 | }; |
| 71 | 92 | ||
| 93 | + const validateMapComponent = async (dataSource: Record<DataSourceField, string>[]) => { | ||
| 94 | + if (dataSource.length) { | ||
| 95 | + const firstRecord = dataSource.at(0)!; | ||
| 96 | + const { deviceId, slaveDeviceId } = firstRecord; | ||
| 97 | + const flag = dataSource.every( | ||
| 98 | + (item) => item.deviceId === deviceId && item.slaveDeviceId === slaveDeviceId | ||
| 99 | + ); | ||
| 100 | + if (!flag) { | ||
| 101 | + createMessage.warning('地图组件绑定的数据源应该一致'); | ||
| 102 | + return Promise.reject(false); | ||
| 103 | + } | ||
| 104 | + } | ||
| 105 | + }; | ||
| 106 | + | ||
| 72 | const getDataSourceField = () => { | 107 | const getDataSourceField = () => { |
| 73 | const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]); | 108 | const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]); |
| 74 | const _dataSource: DataSource[] = []; | 109 | const _dataSource: DataSource[] = []; |
| 110 | + | ||
| 75 | for (const id of hasExistEl) { | 111 | for (const id of hasExistEl) { |
| 76 | const index = unref(dataSource).findIndex((item) => item.id === id); | 112 | const index = unref(dataSource).findIndex((item) => item.id === id); |
| 77 | const value = (dataSourceEl[id] as FormActionType).getFieldsValue() as DataSource; | 113 | const value = (dataSourceEl[id] as FormActionType).getFieldsValue() as DataSource; |
| 78 | if (!~index) continue; | 114 | if (!~index) continue; |
| 79 | const componentInfo = unref(dataSource)[index].componentInfo || {}; | 115 | const componentInfo = unref(dataSource)[index].componentInfo || {}; |
| 80 | - _dataSource.unshift({ | 116 | + _dataSource[index] = { |
| 81 | ...value, | 117 | ...value, |
| 82 | componentInfo: { ...(props.defaultConfig || {}), ...componentInfo }, | 118 | componentInfo: { ...(props.defaultConfig || {}), ...componentInfo }, |
| 83 | - }); | 119 | + }; |
| 84 | } | 120 | } |
| 121 | + | ||
| 85 | return _dataSource; | 122 | return _dataSource; |
| 86 | }; | 123 | }; |
| 87 | 124 | ||
| @@ -160,7 +197,8 @@ | @@ -160,7 +197,8 @@ | ||
| 160 | }; | 197 | }; |
| 161 | 198 | ||
| 162 | const showSettingButton = computed(() => { | 199 | const showSettingButton = computed(() => { |
| 163 | - return props.frontId !== FrontComponent.PICTURE_COMPONENT_1; | 200 | + const flag = frontComponentMap.get(props.frontId!)?.hasSetting; |
| 201 | + return flag; | ||
| 164 | }); | 202 | }); |
| 165 | 203 | ||
| 166 | watch( | 204 | watch( |
| @@ -179,6 +217,41 @@ | @@ -179,6 +217,41 @@ | ||
| 179 | return getDataSourceComponent(props.frontId as FrontComponent); | 217 | return getDataSourceComponent(props.frontId as FrontComponent); |
| 180 | }); | 218 | }); |
| 181 | 219 | ||
| 220 | + let inited = false; | ||
| 221 | + const formListEl = ref<HTMLElement>(); | ||
| 222 | + async function handleSort() { | ||
| 223 | + if (inited) return; | ||
| 224 | + await nextTick(); | ||
| 225 | + const formList = unref(formListEl); | ||
| 226 | + if (!formList) return; | ||
| 227 | + | ||
| 228 | + const { initSortable } = useSortable(unref(formList), { | ||
| 229 | + handle: '.sort-icon', | ||
| 230 | + onEnd: (evt) => { | ||
| 231 | + const { oldIndex, newIndex } = evt; | ||
| 232 | + if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) { | ||
| 233 | + return; | ||
| 234 | + } | ||
| 235 | + | ||
| 236 | + const _dataSource = cloneDeep(unref(dataSource)); | ||
| 237 | + | ||
| 238 | + if (oldIndex > newIndex) { | ||
| 239 | + _dataSource.splice(newIndex, 0, _dataSource[oldIndex]); | ||
| 240 | + _dataSource.splice(oldIndex + 1, 1); | ||
| 241 | + } else { | ||
| 242 | + _dataSource.splice(newIndex + 1, 0, _dataSource[oldIndex]); | ||
| 243 | + _dataSource.splice(oldIndex, 1); | ||
| 244 | + } | ||
| 245 | + | ||
| 246 | + dataSource.value = _dataSource; | ||
| 247 | + }, | ||
| 248 | + }); | ||
| 249 | + initSortable(); | ||
| 250 | + inited = true; | ||
| 251 | + } | ||
| 252 | + | ||
| 253 | + onMounted(() => handleSort()); | ||
| 254 | + | ||
| 182 | defineExpose({ | 255 | defineExpose({ |
| 183 | getAllDataSourceFieldValue, | 256 | getAllDataSourceFieldValue, |
| 184 | validate, | 257 | validate, |
| @@ -192,36 +265,43 @@ | @@ -192,36 +265,43 @@ | ||
| 192 | <BasicForm @register="basicRegister" class="w-full" /> | 265 | <BasicForm @register="basicRegister" class="w-full" /> |
| 193 | </div> | 266 | </div> |
| 194 | <h3 class="w-24 flex-shrink-0 text-right pr-2 my-4">选择数据源</h3> | 267 | <h3 class="w-24 flex-shrink-0 text-right pr-2 my-4">选择数据源</h3> |
| 195 | - <div v-for="item in dataSource" :key="item.id" class="flex"> | ||
| 196 | - <div | ||
| 197 | - class="w-24 text-right leading-30px pr-8px flex right" | ||
| 198 | - style="flex: 0 0 96px; justify-content: right" | ||
| 199 | - > | ||
| 200 | - 选择设备 | 268 | + <section ref="formListEl"> |
| 269 | + <div v-for="item in dataSource" :data-id="item.id" :key="item.id" class="flex bg-light-50"> | ||
| 270 | + <div | ||
| 271 | + class="w-24 text-right leading-30px pr-8px flex right" | ||
| 272 | + style="flex: 0 0 96px; justify-content: right" | ||
| 273 | + > | ||
| 274 | + 选择设备 | ||
| 275 | + </div> | ||
| 276 | + <div class="pl-2 flex-auto"> | ||
| 277 | + <component :is="dataSourceComponent" :ref="(el) => setFormEl(el, item.id)" /> | ||
| 278 | + </div> | ||
| 279 | + <div class="flex justify-center gap-3 w-28"> | ||
| 280 | + <Tooltip title="复制"> | ||
| 281 | + <CopyOutlined @click="handleCopy(item)" class="cursor-pointer text-lg !leading-32px" /> | ||
| 282 | + </Tooltip> | ||
| 283 | + <Tooltip title="设置"> | ||
| 284 | + <SettingOutlined | ||
| 285 | + v-show="showSettingButton" | ||
| 286 | + @click="handleSetting(item)" | ||
| 287 | + class="cursor-pointer text-lg !leading-32px" | ||
| 288 | + /> | ||
| 289 | + </Tooltip> | ||
| 290 | + <Tooltip title="拖拽排序"> | ||
| 291 | + <SwapOutlined | ||
| 292 | + class="cursor-pointer text-lg !leading-32px svg:transform svg:rotate-90 sort-icon" | ||
| 293 | + /> | ||
| 294 | + </Tooltip> | ||
| 295 | + <Tooltip title="删除"> | ||
| 296 | + <DeleteOutlined | ||
| 297 | + @click="handleDelete(item)" | ||
| 298 | + class="cursor-pointer text-lg !leading-32px" | ||
| 299 | + /> | ||
| 300 | + </Tooltip> | ||
| 301 | + </div> | ||
| 201 | </div> | 302 | </div> |
| 202 | - <div class="pl-2 flex-auto"> | ||
| 203 | - <!-- <BasicDataSourceForm /> --> | ||
| 204 | - <component :is="dataSourceComponent" :ref="(el) => setFormEl(el, item.id)" /> | ||
| 205 | - </div> | ||
| 206 | - <div class="flex justify-center gap-3 w-24"> | ||
| 207 | - <Tooltip title="复制"> | ||
| 208 | - <CopyOutlined @click="handleCopy(item)" class="cursor-pointer text-lg !leading-32px" /> | ||
| 209 | - </Tooltip> | ||
| 210 | - <Tooltip title="设置"> | ||
| 211 | - <SettingOutlined | ||
| 212 | - v-show="showSettingButton" | ||
| 213 | - @click="handleSetting(item)" | ||
| 214 | - class="cursor-pointer text-lg !leading-32px" | ||
| 215 | - /> | ||
| 216 | - </Tooltip> | ||
| 217 | - <Tooltip title="删除"> | ||
| 218 | - <DeleteOutlined | ||
| 219 | - @click="handleDelete(item)" | ||
| 220 | - class="cursor-pointer text-lg !leading-32px" | ||
| 221 | - /> | ||
| 222 | - </Tooltip> | ||
| 223 | - </div> | ||
| 224 | - </div> | 303 | + </section> |
| 304 | + | ||
| 225 | <div class="text-center"> | 305 | <div class="text-center"> |
| 226 | <Button type="primary" @click="handleAdd">添加数据源</Button> | 306 | <Button type="primary" @click="handleAdd">添加数据源</Button> |
| 227 | </div> | 307 | </div> |
| @@ -66,6 +66,7 @@ | @@ -66,6 +66,7 @@ | ||
| 66 | const { getAllDataSourceFieldValue, validate } = unref(basicConfigurationEl)!; | 66 | const { getAllDataSourceFieldValue, validate } = unref(basicConfigurationEl)!; |
| 67 | await validate(); | 67 | await validate(); |
| 68 | const value = getAllDataSourceFieldValue(); | 68 | const value = getAllDataSourceFieldValue(); |
| 69 | + | ||
| 69 | unref(isEdit) ? handleUpdateComponent(value) : handleAddComponent(value); | 70 | unref(isEdit) ? handleUpdateComponent(value) : handleAddComponent(value); |
| 70 | resetForm(); | 71 | resetForm(); |
| 71 | } catch (error: unknown) { | 72 | } catch (error: unknown) { |
| @@ -2,14 +2,10 @@ import { Component } from 'vue'; | @@ -2,14 +2,10 @@ import { Component } from 'vue'; | ||
| 2 | import { FrontComponent } from '../../../const/const'; | 2 | import { FrontComponent } from '../../../const/const'; |
| 3 | import BasicDataSourceForm from './BasicDataSourceForm.vue'; | 3 | import BasicDataSourceForm from './BasicDataSourceForm.vue'; |
| 4 | import ControlDataSourceForm from './ControlDataSourceForm.vue'; | 4 | import ControlDataSourceForm from './ControlDataSourceForm.vue'; |
| 5 | -import MapDataSourceForm from './MapDataSourceForm.vue'; | ||
| 6 | 5 | ||
| 7 | const dataSourceComponentMap = new Map<FrontComponent, Component>(); | 6 | const dataSourceComponentMap = new Map<FrontComponent, Component>(); |
| 8 | 7 | ||
| 9 | dataSourceComponentMap.set(FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, ControlDataSourceForm); | 8 | dataSourceComponentMap.set(FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, ControlDataSourceForm); |
| 10 | -dataSourceComponentMap.set(FrontComponent.MAP_COMPONENT_TRACK_REAL, MapDataSourceForm); | ||
| 11 | -dataSourceComponentMap.set(FrontComponent.MAP_COMPONENT_TRACK_HISTORY, MapDataSourceForm); | ||
| 12 | - | ||
| 13 | export const getDataSourceComponent = (frontId: FrontComponent) => { | 9 | export const getDataSourceComponent = (frontId: FrontComponent) => { |
| 14 | if (dataSourceComponentMap.has(frontId)) return dataSourceComponentMap.get(frontId)!; | 10 | if (dataSourceComponentMap.has(frontId)) return dataSourceComponentMap.get(frontId)!; |
| 15 | return BasicDataSourceForm; | 11 | return BasicDataSourceForm; |
| @@ -8,20 +8,24 @@ | @@ -8,20 +8,24 @@ | ||
| 8 | import { AggregateDataEnum, eChartOptions } from '/@/views/device/localtion/config.data'; | 8 | import { AggregateDataEnum, eChartOptions } from '/@/views/device/localtion/config.data'; |
| 9 | import { useGridLayout } from '/@/hooks/component/useGridLayout'; | 9 | import { useGridLayout } from '/@/hooks/component/useGridLayout'; |
| 10 | import { ColEx } from '/@/components/Form/src/types'; | 10 | import { ColEx } from '/@/components/Form/src/types'; |
| 11 | - import { DataSource } from '/@/api/dataBoard/model'; | 11 | + import { DataSource, DeviceAttributeRecord } from '/@/api/dataBoard/model'; |
| 12 | import { useForm, BasicForm } from '/@/components/Form'; | 12 | import { useForm, BasicForm } from '/@/components/Form'; |
| 13 | import { formSchema, QueryWay, SchemaFiled } from '../config/historyTrend.config'; | 13 | import { formSchema, QueryWay, SchemaFiled } from '../config/historyTrend.config'; |
| 14 | import { DEFAULT_DATE_FORMAT } from '../config/util'; | 14 | import { DEFAULT_DATE_FORMAT } from '../config/util'; |
| 15 | import { Loading } from '/@/components/Loading'; | 15 | import { Loading } from '/@/components/Loading'; |
| 16 | import BasicModal from '/@/components/Modal/src/BasicModal.vue'; | 16 | import BasicModal from '/@/components/Modal/src/BasicModal.vue'; |
| 17 | import { useModalInner } from '/@/components/Modal'; | 17 | import { useModalInner } from '/@/components/Modal'; |
| 18 | - import { getDeviceAttributes } from '/@/api/dataBoard'; | 18 | + import { getAllDeviceByOrg, getDeviceAttributes } from '/@/api/dataBoard'; |
| 19 | + import { HistoryData } from '/@/api/alarm/position/model'; | ||
| 20 | + import { EChartsOption } from 'echarts'; | ||
| 21 | + | ||
| 22 | + type DeviceOption = Record<'label' | 'value' | 'organizationId', string>; | ||
| 19 | 23 | ||
| 20 | defineEmits(['register']); | 24 | defineEmits(['register']); |
| 21 | 25 | ||
| 22 | const chartRef = ref(); | 26 | const chartRef = ref(); |
| 23 | 27 | ||
| 24 | - const deviceAttrs = ref<string[]>([]); | 28 | + const deviceAttrs = ref<DeviceAttributeRecord[]>([]); |
| 25 | 29 | ||
| 26 | const loading = ref(false); | 30 | const loading = ref(false); |
| 27 | 31 | ||
| @@ -29,26 +33,27 @@ | @@ -29,26 +33,27 @@ | ||
| 29 | 33 | ||
| 30 | function getSearchParams(value: Recordable) { | 34 | function getSearchParams(value: Recordable) { |
| 31 | const { startTs, endTs, interval, agg, limit, keys, way, deviceId } = value; | 35 | const { startTs, endTs, interval, agg, limit, keys, way, deviceId } = value; |
| 36 | + const basicRecord = { | ||
| 37 | + entityId: deviceId, | ||
| 38 | + keys: keys | ||
| 39 | + ? keys | ||
| 40 | + : unref(deviceAttrs) | ||
| 41 | + .map((item) => item.identifier) | ||
| 42 | + .join(), | ||
| 43 | + interval, | ||
| 44 | + agg, | ||
| 45 | + limit, | ||
| 46 | + }; | ||
| 32 | if (way === QueryWay.LATEST) { | 47 | if (way === QueryWay.LATEST) { |
| 33 | - return { | ||
| 34 | - entityId: deviceId, | ||
| 35 | - keys: keys ? keys : unref(deviceAttrs).join(), | 48 | + return Object.assign(basicRecord, { |
| 36 | startTs: moment().subtract(startTs, 'ms').valueOf(), | 49 | startTs: moment().subtract(startTs, 'ms').valueOf(), |
| 37 | endTs: Date.now(), | 50 | endTs: Date.now(), |
| 38 | - interval, | ||
| 39 | - agg, | ||
| 40 | - limit, | ||
| 41 | - }; | 51 | + }); |
| 42 | } else { | 52 | } else { |
| 43 | - return { | ||
| 44 | - entityId: deviceId, | ||
| 45 | - keys: keys ? keys : unref(deviceAttrs).join(), | 53 | + return Object.assign(basicRecord, { |
| 46 | startTs: moment(startTs).valueOf(), | 54 | startTs: moment(startTs).valueOf(), |
| 47 | endTs: moment(endTs).valueOf(), | 55 | endTs: moment(endTs).valueOf(), |
| 48 | - interval, | ||
| 49 | - agg, | ||
| 50 | - limit, | ||
| 51 | - }; | 56 | + }); |
| 52 | } | 57 | } |
| 53 | } | 58 | } |
| 54 | 59 | ||
| @@ -60,24 +65,24 @@ | @@ -60,24 +65,24 @@ | ||
| 60 | } | 65 | } |
| 61 | } | 66 | } |
| 62 | 67 | ||
| 63 | - function setChartOptions(data, keys?) { | 68 | + function setChartOptions(data: HistoryData, keys?: string | string[]) { |
| 64 | const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); | 69 | const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
| 65 | 70 | ||
| 66 | - const dataArray: any[] = []; | 71 | + const dataArray: [string, string, string][] = []; |
| 67 | for (const key in data) { | 72 | for (const key in data) { |
| 68 | for (const item1 of data[key]) { | 73 | for (const item1 of data[key]) { |
| 69 | let { ts, value } = item1; | 74 | let { ts, value } = item1; |
| 70 | const time = dateUtil(ts).format(DEFAULT_DATE_FORMAT); | 75 | const time = dateUtil(ts).format(DEFAULT_DATE_FORMAT); |
| 71 | value = Number(value).toFixed(2); | 76 | value = Number(value).toFixed(2); |
| 72 | - dataArray.push([time, value, key]); | 77 | + dataArray.push([time, value, key as string]); |
| 73 | } | 78 | } |
| 74 | } | 79 | } |
| 75 | - keys = keys ? [keys] : unref(deviceAttrs); | ||
| 76 | - const series: any = keys.map((item) => { | 80 | + keys = keys ? [keys as string] : unref(deviceAttrs).map((item) => item.identifier); |
| 81 | + const series: EChartsOption['series'] = (keys as string[]).map((item) => { | ||
| 77 | return { | 82 | return { |
| 78 | name: item, | 83 | name: item, |
| 79 | type: 'line', | 84 | type: 'line', |
| 80 | - data: dataArray.filter((item1) => item1[2] === item), | 85 | + data: dataArray.filter((temp) => temp[2] === item), |
| 81 | }; | 86 | }; |
| 82 | }); | 87 | }); |
| 83 | // 设置数据 | 88 | // 设置数据 |
| @@ -118,18 +123,23 @@ | @@ -118,18 +123,23 @@ | ||
| 118 | }, | 123 | }, |
| 119 | }); | 124 | }); |
| 120 | 125 | ||
| 121 | - const getDeviceDataKey = async (deviceId: string) => { | ||
| 122 | - if (!deviceId) return; | 126 | + const getDeviceDataKey = async (record: DeviceOption) => { |
| 127 | + const { organizationId, value } = record; | ||
| 123 | try { | 128 | try { |
| 124 | - deviceAttrs.value = (await getDeviceAttributes({ deviceId })) || []; | 129 | + const options = await getAllDeviceByOrg(organizationId); |
| 130 | + const record = options.find((item) => item.id === value); | ||
| 131 | + const { deviceProfileId } = record!; | ||
| 132 | + deviceAttrs.value = (await getDeviceAttributes({ deviceProfileId })) || []; | ||
| 125 | await nextTick(); | 133 | await nextTick(); |
| 126 | method.updateSchema({ | 134 | method.updateSchema({ |
| 127 | field: SchemaFiled.KEYS, | 135 | field: SchemaFiled.KEYS, |
| 128 | componentProps: { | 136 | componentProps: { |
| 129 | - options: unref(deviceAttrs).map((item) => ({ label: item, value: item })), | 137 | + options: unref(deviceAttrs).map((item) => ({ label: item.name, value: item.identifier })), |
| 130 | }, | 138 | }, |
| 131 | }); | 139 | }); |
| 132 | - } catch (error) {} | 140 | + } catch (error) { |
| 141 | + throw error; | ||
| 142 | + } | ||
| 133 | }; | 143 | }; |
| 134 | 144 | ||
| 135 | const handleModalOpen = async () => { | 145 | const handleModalOpen = async () => { |
| @@ -147,7 +157,9 @@ | @@ -147,7 +157,9 @@ | ||
| 147 | 157 | ||
| 148 | const res = await getDeviceHistoryInfo({ | 158 | const res = await getDeviceHistoryInfo({ |
| 149 | entityId: deviceId, | 159 | entityId: deviceId, |
| 150 | - keys: unref(deviceAttrs).join(), | 160 | + keys: unref(deviceAttrs) |
| 161 | + .map((item) => item.identifier) | ||
| 162 | + .join(), | ||
| 151 | startTs: Date.now() - 1 * 24 * 60 * 60 * 1000, | 163 | startTs: Date.now() - 1 * 24 * 60 * 60 * 1000, |
| 152 | endTs: Date.now(), | 164 | endTs: Date.now(), |
| 153 | agg: AggregateDataEnum.NONE, | 165 | agg: AggregateDataEnum.NONE, |
| @@ -166,9 +178,10 @@ | @@ -166,9 +178,10 @@ | ||
| 166 | 178 | ||
| 167 | const generateDeviceOptions = (dataSource: DataSource[]) => { | 179 | const generateDeviceOptions = (dataSource: DataSource[]) => { |
| 168 | const record: { [key: string]: boolean } = {}; | 180 | const record: { [key: string]: boolean } = {}; |
| 169 | - const options: Record<'label' | 'value', string>[] = []; | 181 | + |
| 182 | + const options: DeviceOption[] = []; | ||
| 170 | for (const item of dataSource) { | 183 | for (const item of dataSource) { |
| 171 | - const { deviceName, gatewayDevice, slaveDeviceId } = item; | 184 | + const { deviceName, gatewayDevice, slaveDeviceId, organizationId } = item; |
| 172 | let { deviceId } = item; | 185 | let { deviceId } = item; |
| 173 | if (gatewayDevice) { | 186 | if (gatewayDevice) { |
| 174 | deviceId = slaveDeviceId; | 187 | deviceId = slaveDeviceId; |
| @@ -177,6 +190,7 @@ | @@ -177,6 +190,7 @@ | ||
| 177 | options.push({ | 190 | options.push({ |
| 178 | label: deviceName, | 191 | label: deviceName, |
| 179 | value: deviceId, | 192 | value: deviceId, |
| 193 | + organizationId, | ||
| 180 | }); | 194 | }); |
| 181 | record[deviceId] = true; | 195 | record[deviceId] = true; |
| 182 | } | 196 | } |
| @@ -195,8 +209,8 @@ | @@ -195,8 +209,8 @@ | ||
| 195 | const { setFieldsValue } = formActionType; | 209 | const { setFieldsValue } = formActionType; |
| 196 | return { | 210 | return { |
| 197 | options, | 211 | options, |
| 198 | - onChange(value: string) { | ||
| 199 | - getDeviceDataKey(value); | 212 | + onChange(_, record: DeviceOption) { |
| 213 | + getDeviceDataKey(record); | ||
| 200 | setFieldsValue({ [SchemaFiled.KEYS]: null }); | 214 | setFieldsValue({ [SchemaFiled.KEYS]: null }); |
| 201 | }, | 215 | }, |
| 202 | }; | 216 | }; |
| @@ -204,17 +218,10 @@ | @@ -204,17 +218,10 @@ | ||
| 204 | }); | 218 | }); |
| 205 | 219 | ||
| 206 | if (options.length && options.at(0)?.value) { | 220 | if (options.length && options.at(0)?.value) { |
| 207 | - const value = options.at(0)!.value; | ||
| 208 | - getDeviceDataKey(value); | 221 | + const record = options.at(0)!; |
| 222 | + await getDeviceDataKey(record); | ||
| 209 | try { | 223 | try { |
| 210 | - deviceAttrs.value = (await getDeviceAttributes({ deviceId: value })) || []; | ||
| 211 | - method.updateSchema({ | ||
| 212 | - field: SchemaFiled.KEYS, | ||
| 213 | - componentProps: { | ||
| 214 | - options: unref(deviceAttrs).map((item) => ({ label: item, value: item })), | ||
| 215 | - }, | ||
| 216 | - }); | ||
| 217 | - await method.setFieldsValue({ [SchemaFiled.DEVICE_ID]: value }); | 224 | + await method.setFieldsValue({ [SchemaFiled.DEVICE_ID]: record.value }); |
| 218 | } catch (error) {} | 225 | } catch (error) {} |
| 219 | } | 226 | } |
| 220 | 227 | ||
| @@ -231,7 +238,7 @@ | @@ -231,7 +238,7 @@ | ||
| 231 | <section class="bg-white my-3 p-2"> | 238 | <section class="bg-white my-3 p-2"> |
| 232 | <BasicForm @register="register" /> | 239 | <BasicForm @register="register" /> |
| 233 | </section> | 240 | </section> |
| 234 | - <section class="bg-white p-3" style="min-hight: 350px"> | 241 | + <section class="bg-white p-3" style="min-height: 350px"> |
| 235 | <div v-show="isNull" ref="chartRef" :style="{ height: '350px', width: '100%' }"> | 242 | <div v-show="isNull" ref="chartRef" :style="{ height: '350px', width: '100%' }"> |
| 236 | <Loading :loading="loading" :absolute="true" /> | 243 | <Loading :loading="loading" :absolute="true" /> |
| 237 | </div> | 244 | </div> |
| @@ -4,16 +4,17 @@ import { FormSchema } from '/@/components/Form'; | @@ -4,16 +4,17 @@ import { FormSchema } from '/@/components/Form'; | ||
| 4 | import { copyTransFun } from '/@/utils/fnUtils'; | 4 | import { copyTransFun } from '/@/utils/fnUtils'; |
| 5 | import { OnChangeHookParams } from '/@/components/Form/src/components/ApiSearchSelect.vue'; | 5 | import { OnChangeHookParams } from '/@/components/Form/src/components/ApiSearchSelect.vue'; |
| 6 | import { unref } from 'vue'; | 6 | import { unref } from 'vue'; |
| 7 | +import { MasterDeviceList } from '/@/api/dataBoard/model'; | ||
| 7 | 8 | ||
| 8 | export enum BasicConfigField { | 9 | export enum BasicConfigField { |
| 9 | NAME = 'name', | 10 | NAME = 'name', |
| 10 | REMARK = 'remark', | 11 | REMARK = 'remark', |
| 11 | } | 12 | } |
| 12 | 13 | ||
| 13 | -const getDeviceAttribute = async (deviceId: string) => { | 14 | +const getDeviceAttribute = async (deviceProfileId: string) => { |
| 14 | try { | 15 | try { |
| 15 | - const data = await getDeviceAttributes({ deviceId }); | ||
| 16 | - if (data) return data.map((item) => ({ label: item, value: item })); | 16 | + const data = await getDeviceAttributes({ deviceProfileId }); |
| 17 | + if (data) return data.map((item) => ({ label: item.name, value: item.identifier })); | ||
| 17 | } catch (error) {} | 18 | } catch (error) {} |
| 18 | return []; | 19 | return []; |
| 19 | }; | 20 | }; |
| @@ -23,6 +24,8 @@ export enum DataSourceField { | @@ -23,6 +24,8 @@ export enum DataSourceField { | ||
| 23 | ORIGINATION_ID = 'organizationId', | 24 | ORIGINATION_ID = 'organizationId', |
| 24 | DEVICE_ID = 'deviceId', | 25 | DEVICE_ID = 'deviceId', |
| 25 | SLAVE_DEVICE_ID = 'slaveDeviceId', | 26 | SLAVE_DEVICE_ID = 'slaveDeviceId', |
| 27 | + DEVICE_PROFILE_ID = 'deviceProfileId', | ||
| 28 | + SLAVE_DEVICE_PROFILE_ID = 'slaveDeviceProfileId', | ||
| 26 | ATTRIBUTE = 'attribute', | 29 | ATTRIBUTE = 'attribute', |
| 27 | ATTRIBUTE_RENAME = 'attributeRename', | 30 | ATTRIBUTE_RENAME = 'attributeRename', |
| 28 | DEVICE_NAME = 'deviceName', | 31 | DEVICE_NAME = 'deviceName', |
| @@ -86,6 +89,8 @@ export const dataSourceSchema: FormSchema[] = [ | @@ -86,6 +89,8 @@ export const dataSourceSchema: FormSchema[] = [ | ||
| 86 | [DataSourceField.ATTRIBUTE]: null, | 89 | [DataSourceField.ATTRIBUTE]: null, |
| 87 | [DataSourceField.SLAVE_DEVICE_ID]: null, | 90 | [DataSourceField.SLAVE_DEVICE_ID]: null, |
| 88 | [DataSourceField.IS_GATEWAY_DEVICE]: false, | 91 | [DataSourceField.IS_GATEWAY_DEVICE]: false, |
| 92 | + [DataSourceField.DEVICE_PROFILE_ID]: null, | ||
| 93 | + [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: null, | ||
| 89 | }); | 94 | }); |
| 90 | }, | 95 | }, |
| 91 | getPopupContainer: () => document.body, | 96 | getPopupContainer: () => document.body, |
| @@ -93,6 +98,12 @@ export const dataSourceSchema: FormSchema[] = [ | @@ -93,6 +98,12 @@ export const dataSourceSchema: FormSchema[] = [ | ||
| 93 | }, | 98 | }, |
| 94 | }, | 99 | }, |
| 95 | { | 100 | { |
| 101 | + field: DataSourceField.DEVICE_PROFILE_ID, | ||
| 102 | + component: 'Input', | ||
| 103 | + label: '', | ||
| 104 | + show: false, | ||
| 105 | + }, | ||
| 106 | + { | ||
| 96 | field: DataSourceField.DEVICE_ID, | 107 | field: DataSourceField.DEVICE_ID, |
| 97 | component: 'ApiSelect', | 108 | component: 'ApiSelect', |
| 98 | label: '设备', | 109 | label: '设备', |
| @@ -101,13 +112,19 @@ export const dataSourceSchema: FormSchema[] = [ | @@ -101,13 +112,19 @@ export const dataSourceSchema: FormSchema[] = [ | ||
| 101 | componentProps({ formModel, formActionType }) { | 112 | componentProps({ formModel, formActionType }) { |
| 102 | const { setFieldsValue } = formActionType; | 113 | const { setFieldsValue } = formActionType; |
| 103 | const organizationId = formModel[DataSourceField.ORIGINATION_ID]; | 114 | const organizationId = formModel[DataSourceField.ORIGINATION_ID]; |
| 115 | + const deviceId = formModel[DataSourceField.DEVICE_ID]; | ||
| 104 | return { | 116 | return { |
| 105 | api: async () => { | 117 | api: async () => { |
| 106 | if (organizationId) { | 118 | if (organizationId) { |
| 107 | try { | 119 | try { |
| 108 | const data = await getAllDeviceByOrg(organizationId); | 120 | const data = await getAllDeviceByOrg(organizationId); |
| 121 | + if (deviceId) { | ||
| 122 | + const record = data.find((item) => item.id === deviceId); | ||
| 123 | + setFieldsValue({ [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId }); | ||
| 124 | + } | ||
| 109 | if (data) | 125 | if (data) |
| 110 | return data.map((item) => ({ | 126 | return data.map((item) => ({ |
| 127 | + ...item, | ||
| 111 | label: item.name, | 128 | label: item.name, |
| 112 | value: item.id, | 129 | value: item.id, |
| 113 | deviceType: item.deviceType, | 130 | deviceType: item.deviceType, |
| @@ -116,11 +133,14 @@ export const dataSourceSchema: FormSchema[] = [ | @@ -116,11 +133,14 @@ export const dataSourceSchema: FormSchema[] = [ | ||
| 116 | } | 133 | } |
| 117 | return []; | 134 | return []; |
| 118 | }, | 135 | }, |
| 119 | - onChange(_value, record: Record<'value' | 'label' | 'deviceType', string>) { | 136 | + |
| 137 | + onChange(_value, record: MasterDeviceList) { | ||
| 120 | setFieldsValue({ | 138 | setFieldsValue({ |
| 121 | [DataSourceField.ATTRIBUTE]: null, | 139 | [DataSourceField.ATTRIBUTE]: null, |
| 122 | [DataSourceField.IS_GATEWAY_DEVICE]: record?.deviceType === 'GATEWAY', | 140 | [DataSourceField.IS_GATEWAY_DEVICE]: record?.deviceType === 'GATEWAY', |
| 141 | + [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId, | ||
| 123 | [DataSourceField.SLAVE_DEVICE_ID]: null, | 142 | [DataSourceField.SLAVE_DEVICE_ID]: null, |
| 143 | + [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: null, | ||
| 124 | [DataSourceField.DEVICE_NAME]: record?.label, | 144 | [DataSourceField.DEVICE_NAME]: record?.label, |
| 125 | }); | 145 | }); |
| 126 | }, | 146 | }, |
| @@ -130,6 +150,12 @@ export const dataSourceSchema: FormSchema[] = [ | @@ -130,6 +150,12 @@ export const dataSourceSchema: FormSchema[] = [ | ||
| 130 | }, | 150 | }, |
| 131 | }, | 151 | }, |
| 132 | { | 152 | { |
| 153 | + field: DataSourceField.SLAVE_DEVICE_PROFILE_ID, | ||
| 154 | + component: 'Input', | ||
| 155 | + label: '', | ||
| 156 | + show: false, | ||
| 157 | + }, | ||
| 158 | + { | ||
| 133 | field: DataSourceField.SLAVE_DEVICE_ID, | 159 | field: DataSourceField.SLAVE_DEVICE_ID, |
| 134 | label: '网关子设备', | 160 | label: '网关子设备', |
| 135 | component: 'ApiSelect', | 161 | component: 'ApiSelect', |
| @@ -146,13 +172,19 @@ export const dataSourceSchema: FormSchema[] = [ | @@ -146,13 +172,19 @@ export const dataSourceSchema: FormSchema[] = [ | ||
| 146 | const organizationId = formModel[DataSourceField.ORIGINATION_ID]; | 172 | const organizationId = formModel[DataSourceField.ORIGINATION_ID]; |
| 147 | const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE]; | 173 | const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE]; |
| 148 | const deviceId = formModel[DataSourceField.DEVICE_ID]; | 174 | const deviceId = formModel[DataSourceField.DEVICE_ID]; |
| 175 | + const slaveDeviceId = formModel[DataSourceField.SLAVE_DEVICE_ID]; | ||
| 149 | return { | 176 | return { |
| 150 | api: async () => { | 177 | api: async () => { |
| 151 | if (organizationId && isGatewayDevice) { | 178 | if (organizationId && isGatewayDevice) { |
| 152 | try { | 179 | try { |
| 153 | const data = await getGatewaySlaveDevice({ organizationId, masterId: deviceId }); | 180 | const data = await getGatewaySlaveDevice({ organizationId, masterId: deviceId }); |
| 181 | + if (slaveDeviceId) { | ||
| 182 | + const record = data.find((item) => item.id === slaveDeviceId); | ||
| 183 | + setFieldsValue({ [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId }); | ||
| 184 | + } | ||
| 154 | if (data) | 185 | if (data) |
| 155 | return data.map((item) => ({ | 186 | return data.map((item) => ({ |
| 187 | + ...item, | ||
| 156 | label: item.name, | 188 | label: item.name, |
| 157 | value: item.id, | 189 | value: item.id, |
| 158 | deviceType: item.deviceType, | 190 | deviceType: item.deviceType, |
| @@ -161,9 +193,10 @@ export const dataSourceSchema: FormSchema[] = [ | @@ -161,9 +193,10 @@ export const dataSourceSchema: FormSchema[] = [ | ||
| 161 | } | 193 | } |
| 162 | return []; | 194 | return []; |
| 163 | }, | 195 | }, |
| 164 | - onChange(_value, record: Record<'value' | 'label' | 'deviceType', string>) { | 196 | + onChange(_value, record: MasterDeviceList) { |
| 165 | setFieldsValue({ | 197 | setFieldsValue({ |
| 166 | [DataSourceField.ATTRIBUTE]: null, | 198 | [DataSourceField.ATTRIBUTE]: null, |
| 199 | + [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: record.deviceProfileId, | ||
| 167 | [DataSourceField.DEVICE_NAME]: record?.label, | 200 | [DataSourceField.DEVICE_NAME]: record?.label, |
| 168 | }); | 201 | }); |
| 169 | }, | 202 | }, |
| @@ -179,19 +212,21 @@ export const dataSourceSchema: FormSchema[] = [ | @@ -179,19 +212,21 @@ export const dataSourceSchema: FormSchema[] = [ | ||
| 179 | colProps: { span: 8 }, | 212 | colProps: { span: 8 }, |
| 180 | rules: [{ required: true, message: '属性为必填项' }], | 213 | rules: [{ required: true, message: '属性为必填项' }], |
| 181 | componentProps({ formModel }) { | 214 | componentProps({ formModel }) { |
| 182 | - const organizationId = formModel[DataSourceField.ORIGINATION_ID]; | ||
| 183 | const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE]; | 215 | const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE]; |
| 184 | const deviceId = formModel[DataSourceField.DEVICE_ID]; | 216 | const deviceId = formModel[DataSourceField.DEVICE_ID]; |
| 217 | + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | ||
| 185 | const slaveDeviceId = formModel[DataSourceField.SLAVE_DEVICE_ID]; | 218 | const slaveDeviceId = formModel[DataSourceField.SLAVE_DEVICE_ID]; |
| 219 | + const slaveDeviceProfileId = formModel[DataSourceField.SLAVE_DEVICE_PROFILE_ID]; | ||
| 220 | + | ||
| 186 | return { | 221 | return { |
| 187 | api: async () => { | 222 | api: async () => { |
| 188 | - if (organizationId && deviceId) { | 223 | + if (deviceId) { |
| 189 | try { | 224 | try { |
| 190 | - if (isGatewayDevice && slaveDeviceId) { | ||
| 191 | - return await getDeviceAttribute(slaveDeviceId); | 225 | + if (isGatewayDevice && slaveDeviceId && slaveDeviceProfileId) { |
| 226 | + return await getDeviceAttribute(slaveDeviceProfileId); | ||
| 192 | } | 227 | } |
| 193 | - if (!isGatewayDevice) { | ||
| 194 | - return await getDeviceAttribute(deviceId); | 228 | + if (!isGatewayDevice && deviceProfileId) { |
| 229 | + return await getDeviceAttribute(deviceProfileId); | ||
| 195 | } | 230 | } |
| 196 | } catch (error) {} | 231 | } catch (error) {} |
| 197 | } | 232 | } |
| 1 | import moment from 'moment'; | 1 | import moment from 'moment'; |
| 2 | import { Moment } from 'moment'; | 2 | import { Moment } from 'moment'; |
| 3 | -import { getDeviceAttributes } from '/@/api/dataBoard'; | ||
| 4 | import { FormSchema } from '/@/components/Form'; | 3 | import { FormSchema } from '/@/components/Form'; |
| 5 | import { ColEx } from '/@/components/Form/src/types'; | 4 | import { ColEx } from '/@/components/Form/src/types'; |
| 6 | import { useGridLayout } from '/@/hooks/component/useGridLayout'; | 5 | import { useGridLayout } from '/@/hooks/component/useGridLayout'; |
| @@ -134,7 +133,6 @@ export const formSchema: FormSchema[] = [ | @@ -134,7 +133,6 @@ export const formSchema: FormSchema[] = [ | ||
| 134 | field: SchemaFiled.AGG, | 133 | field: SchemaFiled.AGG, |
| 135 | label: '数据聚合功能', | 134 | label: '数据聚合功能', |
| 136 | component: 'Select', | 135 | component: 'Select', |
| 137 | - // defaultValue: AggregateDataEnum.NONE, | ||
| 138 | componentProps: { | 136 | componentProps: { |
| 139 | getPopupContainer: () => document.body, | 137 | getPopupContainer: () => document.body, |
| 140 | options: [ | 138 | options: [ |
| @@ -181,7 +179,6 @@ export const formSchema: FormSchema[] = [ | @@ -181,7 +179,6 @@ export const formSchema: FormSchema[] = [ | ||
| 181 | field: SchemaFiled.LIMIT, | 179 | field: SchemaFiled.LIMIT, |
| 182 | label: '最大条数', | 180 | label: '最大条数', |
| 183 | component: 'InputNumber', | 181 | component: 'InputNumber', |
| 184 | - // defaultValue: 7, | ||
| 185 | ifShow({ values }) { | 182 | ifShow({ values }) { |
| 186 | return values[SchemaFiled.AGG] === AggregateDataEnum.NONE; | 183 | return values[SchemaFiled.AGG] === AggregateDataEnum.NONE; |
| 187 | }, | 184 | }, |
| @@ -201,34 +198,30 @@ export const formSchema: FormSchema[] = [ | @@ -201,34 +198,30 @@ export const formSchema: FormSchema[] = [ | ||
| 201 | componentProps: { | 198 | componentProps: { |
| 202 | getPopupContainer: () => document.body, | 199 | getPopupContainer: () => document.body, |
| 203 | }, | 200 | }, |
| 204 | - // componentProps({ formModel }) { | ||
| 205 | - // const deviceId = formModel[SchemaFiled.DEVICE_ID]; | ||
| 206 | - // return { | ||
| 207 | - // api: async () => { | ||
| 208 | - // if (deviceId) { | ||
| 209 | - // try { | ||
| 210 | - // try { | ||
| 211 | - // const data = await getDeviceAttributes({ deviceId }); | ||
| 212 | - // if (data) return data.map((item) => ({ label: item, value: item })); | ||
| 213 | - // } catch (error) {} | ||
| 214 | - // return []; | ||
| 215 | - // } catch (error) {} | ||
| 216 | - // } | ||
| 217 | - // return []; | ||
| 218 | - // }, | ||
| 219 | - // getPopupContainer: () => document.body, | ||
| 220 | - // }; | ||
| 221 | - // }, | ||
| 222 | }, | 201 | }, |
| 223 | ]; | 202 | ]; |
| 224 | 203 | ||
| 225 | -// export const selectDeviceAttrSchema: FormSchema[] = [ | ||
| 226 | -// { | ||
| 227 | -// field: 'keys', | ||
| 228 | -// label: '设备属性', | ||
| 229 | -// component: 'Select', | ||
| 230 | -// componentProps: { | ||
| 231 | -// getPopupContainer: () => document.body, | ||
| 232 | -// }, | ||
| 233 | -// }, | ||
| 234 | -// ]; | 204 | +export function getHistorySearchParams(value: Recordable) { |
| 205 | + const { startTs, endTs, interval, agg, limit, way, keys, deviceId } = value; | ||
| 206 | + if (way === QueryWay.LATEST) { | ||
| 207 | + return { | ||
| 208 | + keys, | ||
| 209 | + entityId: deviceId, | ||
| 210 | + startTs: moment().subtract(startTs, 'ms').valueOf(), | ||
| 211 | + endTs: Date.now(), | ||
| 212 | + interval, | ||
| 213 | + agg, | ||
| 214 | + limit, | ||
| 215 | + }; | ||
| 216 | + } else { | ||
| 217 | + return { | ||
| 218 | + keys, | ||
| 219 | + entityId: deviceId, | ||
| 220 | + startTs: moment(startTs).valueOf(), | ||
| 221 | + endTs: moment(endTs).valueOf(), | ||
| 222 | + interval, | ||
| 223 | + agg, | ||
| 224 | + limit, | ||
| 225 | + }; | ||
| 226 | + } | ||
| 227 | +} |
| @@ -119,7 +119,7 @@ | @@ -119,7 +119,7 @@ | ||
| 119 | data.width = newWPx; | 119 | data.width = newWPx; |
| 120 | data.height = newHPx; | 120 | data.height = newHPx; |
| 121 | 121 | ||
| 122 | - data.record.dataSource = data?.record.dataSource.map((item) => { | 122 | + data.record.dataSource = data.record.dataSource.map((item) => { |
| 123 | if (!item.uuid) item.uuid = buildUUID(); | 123 | if (!item.uuid) item.uuid = buildUUID(); |
| 124 | return { | 124 | return { |
| 125 | ...item, | 125 | ...item, |
| @@ -265,9 +265,9 @@ | @@ -265,9 +265,9 @@ | ||
| 265 | record: item, | 265 | record: item, |
| 266 | }; | 266 | }; |
| 267 | }); | 267 | }); |
| 268 | - | ||
| 269 | beginSendMessage(); | 268 | beginSendMessage(); |
| 270 | } catch (error) { | 269 | } catch (error) { |
| 270 | + throw error; | ||
| 271 | } finally { | 271 | } finally { |
| 272 | loading.value = false; | 272 | loading.value = false; |
| 273 | } | 273 | } |
| @@ -310,7 +310,7 @@ | @@ -310,7 +310,7 @@ | ||
| 310 | 310 | ||
| 311 | const getComponentConfig = ( | 311 | const getComponentConfig = ( |
| 312 | record: DataBoardLayoutInfo['record'], | 312 | record: DataBoardLayoutInfo['record'], |
| 313 | - dataSourceRecord: DataSource | 313 | + dataSourceRecord: DataSource | DataSource[] |
| 314 | ) => { | 314 | ) => { |
| 315 | const frontComponent = record.frontId; | 315 | const frontComponent = record.frontId; |
| 316 | const component = frontComponentMap.get(frontComponent as FrontComponent); | 316 | const component = frontComponentMap.get(frontComponent as FrontComponent); |
| @@ -375,6 +375,10 @@ | @@ -375,6 +375,10 @@ | ||
| 375 | historyDataModalMethod.openModal(true, record); | 375 | historyDataModalMethod.openModal(true, record); |
| 376 | }; | 376 | }; |
| 377 | 377 | ||
| 378 | + const hasHistoryTrend = (item: DataBoardLayoutInfo) => { | ||
| 379 | + return frontComponentMap.get(item.record.frontId as FrontComponent)?.hasHistoryTrend; | ||
| 380 | + }; | ||
| 381 | + | ||
| 378 | onMounted(async () => { | 382 | onMounted(async () => { |
| 379 | await injectBaiDuMapLib(); | 383 | await injectBaiDuMapLib(); |
| 380 | await injectBaiDuMapTrackAniMationLib(); | 384 | await injectBaiDuMapTrackAniMationLib(); |
| @@ -442,6 +446,7 @@ | @@ -442,6 +446,7 @@ | ||
| 442 | <WidgetWrapper | 446 | <WidgetWrapper |
| 443 | :key="item.i" | 447 | :key="item.i" |
| 444 | :ref="(el: Element) => setComponentRef(el, item)" | 448 | :ref="(el: Element) => setComponentRef(el, item)" |
| 449 | + :record="item.record" | ||
| 445 | :data-source="item.record.dataSource" | 450 | :data-source="item.record.dataSource" |
| 446 | > | 451 | > |
| 447 | <template #header> | 452 | <template #header> |
| @@ -455,7 +460,7 @@ | @@ -455,7 +460,7 @@ | ||
| 455 | <Tooltip title="趋势"> | 460 | <Tooltip title="趋势"> |
| 456 | <img | 461 | <img |
| 457 | :src="trendIcon" | 462 | :src="trendIcon" |
| 458 | - v-if="!getIsSharePage" | 463 | + v-if="!getIsSharePage && hasHistoryTrend(item)" |
| 459 | class="cursor-pointer w-4.5 h-4.5" | 464 | class="cursor-pointer w-4.5 h-4.5" |
| 460 | @click="handleOpenHistroyDataModal(item.record.dataSource)" | 465 | @click="handleOpenHistroyDataModal(item.record.dataSource)" |
| 461 | /> | 466 | /> |
| @@ -18,10 +18,13 @@ export interface ComponentConfig { | @@ -18,10 +18,13 @@ export interface ComponentConfig { | ||
| 18 | ComponentKey: FrontComponent; | 18 | ComponentKey: FrontComponent; |
| 19 | ComponentConfig?: Recordable; | 19 | ComponentConfig?: Recordable; |
| 20 | ComponentCategory: FrontComponentCategory; | 20 | ComponentCategory: FrontComponentCategory; |
| 21 | + isMultipleDataSource?: boolean; | ||
| 22 | + hasHistoryTrend?: boolean; | ||
| 23 | + hasSetting?: boolean; | ||
| 21 | transformConfig: ( | 24 | transformConfig: ( |
| 22 | componentConfig: Recordable, | 25 | componentConfig: Recordable, |
| 23 | record: DataComponentRecord, | 26 | record: DataComponentRecord, |
| 24 | - dataSourceRecord: DataSource | 27 | + dataSourceRecord: DataSource | DataSource[] |
| 25 | ) => Recordable; | 28 | ) => Recordable; |
| 26 | } | 29 | } |
| 27 | 30 |