...
|
...
|
@@ -27,6 +27,7 @@ import { getDeviceActiveTime } from '@/api/external/common/index' |
27
|
27
|
import dayjs from 'dayjs'
|
28
|
28
|
import DeviceLatestTable from './components/DeviceLatestTable.vue'
|
29
|
29
|
import { getDeviceLatest, getProfileAttrs } from '@/api/external/common'
|
|
30
|
+import { isObject } from '@/utils/external/is'
|
30
|
31
|
|
31
|
32
|
const props = defineProps({
|
32
|
33
|
chartConfig: {
|
...
|
...
|
@@ -51,11 +52,12 @@ const { |
51
|
52
|
pitch,
|
52
|
53
|
skyColor,
|
53
|
54
|
marker,
|
54
|
|
- iconMarker,
|
55
|
55
|
mpBorderConfig,
|
56
|
56
|
bgColor
|
57
|
57
|
} = toRefs(props.chartConfig.option.mapOptions)
|
58
|
58
|
|
|
59
|
+const { dialogCustomField, dialogConfigField } = toRefs(props.chartConfig.option)
|
|
60
|
+
|
59
|
61
|
//官方没有高德地图api的ts,所以类型全用的any
|
60
|
62
|
let mapIns: any = null
|
61
|
63
|
let markers: any = []
|
...
|
...
|
@@ -116,21 +118,48 @@ const devicePartInfo = reactive<devicePartInfoInterface>({ |
116
|
118
|
//创建信息弹窗
|
117
|
119
|
const createInfoWindow = async (extraInfo: dataExtraInfoType) => {
|
118
|
120
|
try {
|
119
|
|
- const { name, alias, organizationDTO, deviceState, deviceProfile, deviceInfo, tbDeviceId, deviceProfileId } =
|
120
|
|
- extraInfo
|
|
121
|
+ const {
|
|
122
|
+ name,
|
|
123
|
+ alias,
|
|
124
|
+ organizationDTO,
|
|
125
|
+ deviceState,
|
|
126
|
+ deviceProfile,
|
|
127
|
+ deviceInfo,
|
|
128
|
+ tbDeviceId,
|
|
129
|
+ deviceProfileId,
|
|
130
|
+ customField
|
|
131
|
+ } = extraInfo
|
|
132
|
+ // customField 用于自定义服务端返回的数据
|
|
133
|
+ let realDialogConfigFieldValue = []
|
|
134
|
+ if ('customField' in extraInfo && isObject(customField)) {
|
|
135
|
+ realDialogConfigFieldValue = dialogConfigField.value?.reduce((acc: Recordable[], curr: Recordable) => {
|
|
136
|
+ const realValue = Object.entries(customField)?.map(([label, value]) => ({
|
|
137
|
+ label,
|
|
138
|
+ value
|
|
139
|
+ }))
|
|
140
|
+ const findRealValue = realValue?.find(realItem => realItem.label === curr.value)?.value
|
|
141
|
+ curr['realValue'] = findRealValue
|
|
142
|
+ acc.push(curr)
|
|
143
|
+ return [...acc]
|
|
144
|
+ }, [])
|
|
145
|
+ }
|
|
146
|
+ //
|
121
|
147
|
devicePartInfo.tbDeviceId = tbDeviceId
|
122
|
148
|
devicePartInfo.alias = alias
|
123
|
149
|
devicePartInfo.name = name
|
124
|
150
|
devicePartInfo.deviceProfileId = deviceProfileId
|
125
|
|
- if (tbDeviceId.startsWith('@')) return //假的模拟数据则终止
|
126
|
151
|
let deviceLastUpdateTs = null
|
127
|
|
- const res = await getDeviceActiveTime(tbDeviceId) //查询设备最后离线时间
|
128
|
|
- if (!res) {
|
129
|
|
- deviceLastUpdateTs = null
|
130
|
|
- } else {
|
131
|
|
- deviceLastUpdateTs = res[0]['lastUpdateTs']
|
|
152
|
+ try {
|
|
153
|
+ const res = !dialogCustomField.value ? await getDeviceActiveTime(tbDeviceId) : [{ lastUpdateTs: '' }] //查询设备最后离线时间
|
|
154
|
+ if (!res) {
|
|
155
|
+ deviceLastUpdateTs = null
|
|
156
|
+ } else {
|
|
157
|
+ deviceLastUpdateTs = res[0]['lastUpdateTs']
|
|
158
|
+ }
|
|
159
|
+ } catch (e) {
|
|
160
|
+ console.error(`${e}`, '查询设备最后离线时间(tbDeviceId错误)')
|
132
|
161
|
}
|
133
|
|
- const lastUpdateFormatTs = deviceLastUpdateTs ? dayjs(deviceLastUpdateTs).format('YYYY-MM-DD HH:mm:ss'): ''
|
|
162
|
+ const lastUpdateFormatTs = deviceLastUpdateTs ? dayjs(deviceLastUpdateTs).format('YYYY-MM-DD HH:mm:ss') : ''
|
134
|
163
|
//以render方式渲染小组件里的边框组件
|
135
|
164
|
const BorderInstance = await import(`../../../../Decorates/Borders/${mpBorderConfig.value.value}/index.vue`)
|
136
|
165
|
const config = await import(`../../../../Decorates/Borders/${mpBorderConfig.value.value}/config.ts`)
|
...
|
...
|
@@ -141,48 +170,66 @@ const createInfoWindow = async (extraInfo: dataExtraInfoType) => { |
141
|
170
|
render(h(BorderInstance.default, { chartConfig: BorderConfigInstance }), document.getElementById(id)!)
|
142
|
171
|
}, 100)
|
143
|
172
|
//
|
144
|
|
- const textOverflow = `width:16rem;text-overflow:ellipsis;overflow:hidden;word-break:break-all;white-space:nowrap;`
|
145
|
|
- const textOverflowFontBold = `width:12rem;text-overflow:ellipsis;overflow:hidden;word-break:break-all;white-space:nowrap;font-size:15px;font-weight:bold;`
|
146
|
|
- const deviceStateContainer = `display:flex;justify-content:space-between;align-items:center;`
|
147
|
|
- const deviceStateImg = `width:1.2rem;height:1.2rem;`
|
148
|
|
- const deviceStateText = `margin-left:0.6rem;font-weight:bold;`
|
|
173
|
+
|
|
174
|
+ const dialogStyles = {
|
|
175
|
+ textOverflowStyle: `width:15rem;text-overflow:ellipsis;overflow:hidden;word-break:break-all;white-space:nowrap;`,
|
|
176
|
+ customFieldBox: `display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;color:white;`,
|
|
177
|
+ defaultFieldHeaderBox: `display:flex;justify-content:space-between;align-items:center;color:white;`,
|
|
178
|
+ defaultFieldHeaderBoldText: `width:12rem;text-overflow:ellipsis;overflow:hidden;word-break:break-all;white-space:nowrap;font-size:15px;font-weight:bold;`,
|
|
179
|
+ defaultFieldBox: `display:flex;flex-direction:column;justify-content:space-between;color:white;margin-top:1rem;gap:0.95rem;`,
|
|
180
|
+ iconSize: `width:1.2rem;height:1.2rem;`,
|
|
181
|
+ textStatus: `margin-left:0.6rem;font-weight:bold;`,
|
|
182
|
+ defaultFieldHeaderRightBox: `display:flex;justify-content:space-between;align-items:center;margin-right:16px`
|
|
183
|
+ }
|
|
184
|
+
|
149
|
185
|
return `
|
150
|
|
- <div id="${id}" style="width:30rem;">
|
151
|
|
- <div style="display:flex;flex-direction:column;margin:3.5rem 5.5rem 2rem 6.5rem;position:relative;">
|
152
|
|
- <div style="display:flex;justify-content:space-between;align-items:center;color:white;">
|
153
|
|
- <span style="${textOverflowFontBold}">${alias || name}</span>
|
154
|
|
- ${
|
155
|
|
- deviceState === 'INACTIVE'
|
156
|
|
- ? `<div style="${deviceStateContainer}">
|
157
|
|
- <img onclick="handleOpenDrawer()" title="最新数据" style="${deviceStateImg};cursor:pointer;" src="${listView}"/>
|
158
|
|
- <img style="${deviceStateImg};margin-left:0.3rem" src="${inactive}"/>
|
159
|
|
- <span style="${deviceStateText}">待激活</span>
|
160
|
|
- </div>`
|
161
|
|
- : deviceState === 'ONLINE'
|
162
|
|
- ? `<div style="${deviceStateContainer}">
|
163
|
|
- <img onclick="handleOpenDrawer()" title="最新数据" style="${deviceStateImg};cursor:pointer;" src="${listView}"/>
|
164
|
|
- <img style="${deviceStateImg};margin-left:0.3rem" src="${online}"/>
|
165
|
|
- <span style="${deviceStateText}">在线</span>
|
166
|
|
- </div>`
|
167
|
|
- : `<div style="${deviceStateContainer}">
|
168
|
|
- <img onclick="handleOpenDrawer()" title="最新数据" style="${deviceStateImg};cursor:pointer;" src="${listView}"/>
|
169
|
|
- <img style="${deviceStateImg};margin-left:0.3rem" src="${offline}"/>
|
170
|
|
- <span style="${deviceStateText}">离线</span>
|
171
|
|
- </div>`
|
172
|
|
- }
|
173
|
|
- </div>
|
174
|
|
- <div style="display:flex;flex-direction:column;justify-content:space-between;color:white;margin-top:1rem;gap:0.95rem;">
|
175
|
|
- <div style="${textOverflow}">所属组织:${organizationDTO.name}</div>
|
176
|
|
- <div style="${textOverflow}">接入协议:${deviceProfile.transportType}</div>
|
177
|
|
- <div style="${textOverflow}">设备位置:${
|
178
|
|
- !deviceInfo.address ? '该设备暂无地理位置' : deviceInfo.address
|
179
|
|
- }</div>
|
180
|
|
- <div style="${textOverflow}">${
|
181
|
|
- deviceState === 'ONLINE' ? '在线' : deviceState === 'INACTIVE' ? '创建' : '离线'
|
182
|
|
- }时间:${lastUpdateFormatTs}</div>
|
|
186
|
+ <div id="${id}" style="position: absolute;top: 70px">
|
|
187
|
+ </div>
|
|
188
|
+ ${
|
|
189
|
+ dialogCustomField.value
|
|
190
|
+ ? `
|
|
191
|
+ <div style="position: absolute;top: 135px;left:110px;width:20rem">
|
|
192
|
+ <div style="${dialogStyles.customFieldBox}">
|
|
193
|
+ ${realDialogConfigFieldValue?.map((item: { label: string; value: string; realValue: any }) => {
|
|
194
|
+ const { label, realValue } = item
|
|
195
|
+ return `
|
|
196
|
+ <div style="${dialogStyles.textOverflowStyle}">
|
|
197
|
+ <span >${label}:${realValue}</span>
|
|
198
|
+ </div>
|
|
199
|
+ `
|
|
200
|
+ })}
|
183
|
201
|
</div>
|
184
|
202
|
</div>
|
185
|
|
- </div>
|
|
203
|
+ `
|
|
204
|
+ : `
|
|
205
|
+ <div style="position: absolute;top: 135px;left:110px;width:20rem">
|
|
206
|
+ <div style="${dialogStyles.defaultFieldHeaderBox}">
|
|
207
|
+ <span style="${dialogStyles.defaultFieldHeaderBoldText}">${alias || name}</span>
|
|
208
|
+ <div style="${dialogStyles.defaultFieldHeaderRightBox}">
|
|
209
|
+ <img onclick="handleOpenDrawer()" style="${
|
|
210
|
+ dialogStyles.iconSize
|
|
211
|
+ };cursor:pointer;margin-right:0.5rem" src="${listView}"/>
|
|
212
|
+ <img style="${dialogStyles.iconSize};margin-left:0.5rem" src="${
|
|
213
|
+ deviceState === 'INACTIVE' ? inactive : deviceState === 'ONLINE' ? online : offline
|
|
214
|
+ }"/>
|
|
215
|
+ <span style="${dialogStyles.textStatus}">${
|
|
216
|
+ deviceState === 'INACTIVE' ? '待激活' : deviceState === 'ONLINE' ? '在线' : '离线'
|
|
217
|
+ }</span>
|
|
218
|
+ </div>
|
|
219
|
+ </div>
|
|
220
|
+ <div style="${dialogStyles.defaultFieldBox}">
|
|
221
|
+ <div style="${dialogStyles.textOverflowStyle}">所属组织:${organizationDTO?.name}</div>
|
|
222
|
+ <div style="${dialogStyles.textOverflowStyle}">接入协议:${deviceProfile?.transportType}</div>
|
|
223
|
+ <div style="${dialogStyles.textOverflowStyle}">设备位置:${
|
|
224
|
+ !deviceInfo?.address ? '该设备暂无地理位置' : deviceInfo?.address
|
|
225
|
+ }</div>
|
|
226
|
+ <div style="${dialogStyles.textOverflowStyle}">${
|
|
227
|
+ deviceState === 'ONLINE' ? '在线' : deviceState === 'INACTIVE' ? '创建' : '离线'
|
|
228
|
+ }时间:${lastUpdateFormatTs}</div>
|
|
229
|
+ </div>
|
|
230
|
+ `
|
|
231
|
+ }
|
|
232
|
+
|
186
|
233
|
`
|
187
|
234
|
} catch (e) {
|
188
|
235
|
console.error(e)
|
...
|
...
|
@@ -199,11 +246,11 @@ const handleOpenDrawer = async () => { |
199
|
246
|
}
|
200
|
247
|
|
201
|
248
|
onMounted(() => {
|
202
|
|
- (window as any).handleOpenDrawer = handleOpenDrawer
|
|
249
|
+ ;(window as any).handleOpenDrawer = handleOpenDrawer
|
203
|
250
|
})
|
204
|
251
|
|
205
|
252
|
onUnmounted(() => {
|
206
|
|
- (window as any).handleOpenDrawer = null
|
|
253
|
+ ;(window as any).handleOpenDrawer = null
|
207
|
254
|
})
|
208
|
255
|
|
209
|
256
|
//地图鼠标hover
|
...
|
...
|
@@ -214,10 +261,9 @@ const mapClick = (markerInstance: any, markerItem: dataJsonMarkersType) => { |
214
|
261
|
// mouseover
|
215
|
262
|
markerInstance.on('click', async (e: any) => {
|
216
|
263
|
const { extraInfo } = e.target.getExtData()
|
217
|
|
- if (extraInfo.tbDeviceId.startsWith('@')) return //假的模拟数据则终止弹窗
|
218
|
264
|
let infoWindow = new AMapIns.InfoWindow({
|
219
|
|
- content: await createInfoWindow(extraInfo),
|
220
|
|
- offset: new AMapIns.Pixel(3, -30)
|
|
265
|
+ content: await createInfoWindow(extraInfo)
|
|
266
|
+ // offset: new AMapIns.Pixel(3, -30)
|
221
|
267
|
})
|
222
|
268
|
infoWindow.open(mapIns, e.target.getPosition())
|
223
|
269
|
})
|
...
|
...
|
@@ -238,13 +284,17 @@ const dataHandle = (newData: dataJsonType) => { |
238
|
284
|
// 记录新标记
|
239
|
285
|
if (mapMarkerType.value === MarkerEnum.MARKER) {
|
240
|
286
|
newData.markers.forEach((markerItem: dataJsonMarkersType) => {
|
241
|
|
- console.log("🚀 ~ newData.markers.forEach ~ markerItem:", markerItem.extraInfo.deviceState)
|
|
287
|
+ console.log('🚀 ~ newData.markers.forEach ~ markerItem:', markerItem.extraInfo.deviceState)
|
242
|
288
|
const markerInstance = new AMapIns.Marker({
|
243
|
289
|
position: [markerItem.position[0], markerItem.position[1]],
|
244
|
290
|
offset: new AMapIns.Pixel(-13, 5),
|
245
|
291
|
icon: new AMapIns.Icon({
|
246
|
|
- // image: iconMarker.value,
|
247
|
|
- image: markerItem.extraInfo.deviceState === 'ONLINE' ? online : markerItem.extraInfo.deviceState === 'INACTIVE' ? inactive : offline,
|
|
292
|
+ image:
|
|
293
|
+ markerItem.extraInfo.deviceState === 'ONLINE'
|
|
294
|
+ ? online
|
|
295
|
+ : markerItem.extraInfo.deviceState === 'INACTIVE'
|
|
296
|
+ ? inactive
|
|
297
|
+ : offline,
|
248
|
298
|
size: new AMapIns.Size(35, 35), //图标所处区域大小
|
249
|
299
|
imageSize: new AMapIns.Size(35, 35) //图标大小
|
250
|
300
|
})
|
...
|
...
|
@@ -324,7 +374,10 @@ useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => { |
324
|
374
|
}
|
325
|
375
|
|
326
|
376
|
.amap-info-content {
|
|
377
|
+ width: 30rem;
|
|
378
|
+ height: 20rem;
|
327
|
379
|
overflow: hidden !important;
|
|
380
|
+ position: relative;
|
328
|
381
|
}
|
329
|
382
|
|
330
|
383
|
/**去除高德地图原生弹窗*/
|
...
|
...
|
|