Commit 7cdf8ee02fb2f456f2c4ba9d52fe3e03965fe746
1 parent
9b283601
feat: implement TextComponent && DigitalDashboardComponent echo real data
Showing
11 changed files
with
179 additions
and
24 deletions
| ... | ... | @@ -14,12 +14,14 @@ VITE_PUBLIC_PATH = / |
| 14 | 14 | # VITE_PROXY = [["/api","http://101.133.234.90:8080/api"]] |
| 15 | 15 | # 线上测试环境 |
| 16 | 16 | # VITE_PROXY = [["/api","http://localhost:8080/api"],["/thingskit-drawio","http://localhost:3000/"]] |
| 17 | -VITE_PROXY = [["/api","https://dev.thingskit.com/api"],["/thingskit-drawio","http://localhost:3000/"]] | |
| 17 | +# VITE_PROXY = [["/api","https://dev.thingskit.com/api"],["/thingskit-drawio","http://localhost:3000/"]] | |
| 18 | +VITE_PROXY = [["/api","http://121.37.251.8:8080/api"],["/thingskit-drawio","http://localhost:3000/"]] | |
| 18 | 19 | # VITE_PROXY = [["/api","http://192.168.10.106:8080/api"],["/thingskit-drawio","http://192.168.10.106:8080/api"]] |
| 19 | 20 | |
| 20 | 21 | # 实时数据的ws地址 |
| 21 | 22 | # VITE_WEB_SOCKET = ws://localhost:8080/api/ws/plugins/telemetry?token= |
| 22 | -VITE_WEB_SOCKET = ws://44.99.141.212:8080/api/ws/plugins/telemetry?token= | |
| 23 | +# VITE_WEB_SOCKET = ws://44.99.141.212:8080/api/ws/plugins/telemetry?token= | |
| 24 | +VITE_WEB_SOCKET = ws://121.37.251.8:8080/api/ws/plugins/telemetry?token= | |
| 23 | 25 | |
| 24 | 26 | # Delete console |
| 25 | 27 | VITE_DROP_CONSOLE = true |
| ... | ... | @@ -37,5 +39,5 @@ VITE_GLOB_API_URL_PREFIX=/yt |
| 37 | 39 | VITE_GLOB_CONFIGURATION = /thingskit-drawio |
| 38 | 40 | |
| 39 | 41 | # Content Security Policy |
| 40 | -VITE_CONTENT_SECURITY_POLICY = true | |
| 42 | +VITE_CONTENT_SECURITY_POLICY = false | |
| 41 | 43 | ... | ... |
| ... | ... | @@ -51,6 +51,7 @@ |
| 51 | 51 | option && unref(chartRef)?.setOption(option); |
| 52 | 52 | }); |
| 53 | 53 | } |
| 54 | + | |
| 54 | 55 | const getRadio = computed(() => { |
| 55 | 56 | const { radio } = props.radio; |
| 56 | 57 | return radio; |
| ... | ... | @@ -93,10 +94,12 @@ |
| 93 | 94 | > |
| 94 | 95 | <Tooltip |
| 95 | 96 | placement="topLeft" |
| 96 | - :title="props.value.updateTime || dateUtil().format(DEFAULT_DATE_FORMAT)" | |
| 97 | + :title="dateUtil(props?.value?.updateTime || new Date()).format(DEFAULT_DATE_FORMAT)" | |
| 97 | 98 | > |
| 98 | 99 | <span class="mr-2">更新时间:</span> |
| 99 | - <span> {{ props.value.updateTime || dateUtil().format(DEFAULT_DATE_FORMAT) }}</span> | |
| 100 | + <span> | |
| 101 | + {{ dateUtil(props?.value?.updateTime || new Date()).format(DEFAULT_DATE_FORMAT) }} | |
| 102 | + </span> | |
| 100 | 103 | </Tooltip> |
| 101 | 104 | </div> |
| 102 | 105 | </div> | ... | ... |
| ... | ... | @@ -17,20 +17,21 @@ |
| 17 | 17 | }>(); |
| 18 | 18 | |
| 19 | 19 | const integerPart = computed(() => { |
| 20 | - const { value = 0 } = props.value; | |
| 20 | + let { value = 0 } = props.value; | |
| 21 | 21 | const { max = 5 } = props.layout; |
| 22 | - let _value = value?.toFixed(2).split('.')[0]; | |
| 22 | + if (isNaN(value)) value = 0; | |
| 23 | + let _value = Number(value).toFixed(2).split('.')[0]; | |
| 23 | 24 | if (_value.length < max) _value = _value.padStart(5, '0'); |
| 24 | - | |
| 25 | 25 | if (_value.length > max) _value = ''.padStart(5, '9'); |
| 26 | 26 | |
| 27 | 27 | return _value; |
| 28 | 28 | }); |
| 29 | 29 | |
| 30 | 30 | const decimalPart = computed(() => { |
| 31 | - const { value = 0 } = props.value; | |
| 31 | + let { value = 0 } = props.value; | |
| 32 | 32 | const { keepNumber = 2 } = props.layout; |
| 33 | - let _value = value?.toFixed(2).split('.')[1]; | |
| 33 | + if (isNaN(value)) value = 0; | |
| 34 | + let _value = Number(value)?.toFixed(2).split('.')[1]; | |
| 34 | 35 | if (_value.length < keepNumber) _value = _value.padStart(5, '0'); |
| 35 | 36 | |
| 36 | 37 | if (_value.length > keepNumber) _value = ''.padStart(5, '0'); |
| ... | ... | @@ -91,10 +92,12 @@ |
| 91 | 92 | > |
| 92 | 93 | <Tooltip |
| 93 | 94 | placement="topLeft" |
| 94 | - :title="props.value.updateTime || dateUtil().format(DEFAULT_DATE_FORMAT)" | |
| 95 | + :title="dateUtil(props?.value?.updateTime || new Date()).format(DEFAULT_DATE_FORMAT)" | |
| 95 | 96 | > |
| 96 | 97 | <span class="mr-1">更新时间:</span> |
| 97 | - <span>{{ props.value.updateTime || dateUtil().format(DEFAULT_DATE_FORMAT) }}</span> | |
| 98 | + <span> | |
| 99 | + {{ dateUtil(props?.value?.updateTime || new Date()).format(DEFAULT_DATE_FORMAT) }} | |
| 100 | + </span> | |
| 98 | 101 | </Tooltip> |
| 99 | 102 | </div> |
| 100 | 103 | </div> | ... | ... |
| ... | ... | @@ -288,9 +288,9 @@ export const transformDashboardComponentConfig = ( |
| 288 | 288 | value: { |
| 289 | 289 | id: buildUUID(), |
| 290 | 290 | name: dataSourceRecord.attributeRename || dataSourceRecord.attribute, |
| 291 | - // value: record.va | |
| 291 | + value: dataSourceRecord.componentInfo.value, | |
| 292 | 292 | unit: dataSourceRecord.componentInfo.unit, |
| 293 | - updateTime: record.updateTime || record.createTime, | |
| 293 | + updateTime: dataSourceRecord.componentInfo.updateTime, | |
| 294 | 294 | fontColor: dataSourceRecord.componentInfo.fontColor, |
| 295 | 295 | gradientInfo: dataSourceRecord.componentInfo.gradientInfo, |
| 296 | 296 | }, | ... | ... |
| ... | ... | @@ -90,14 +90,14 @@ |
| 90 | 90 | <div v-if="getShowUpdate" class="text-center text-xs text-gray-400 truncate"> |
| 91 | 91 | <Tooltip |
| 92 | 92 | placement="topLeft" |
| 93 | - :title="props.value.updateTime || dateUtil().format(DEFAULT_DATE_FORMAT)" | |
| 93 | + :title="dateUtil(props?.value?.updateTime || new Date()).format(DEFAULT_DATE_FORMAT)" | |
| 94 | 94 | > |
| 95 | 95 | <span>更新时间:</span> |
| 96 | 96 | <span |
| 97 | 97 | :style="{ fontSize: fontSize({ radio: getRadio, basic: 12, max: 16 }) }" |
| 98 | 98 | class="truncate" |
| 99 | 99 | > |
| 100 | - {{ props.value.updateTime || dateUtil().format(DEFAULT_DATE_FORMAT) }} | |
| 100 | + {{ dateUtil(props?.value?.updateTime || new Date()).format(DEFAULT_DATE_FORMAT) }} | |
| 101 | 101 | </span> |
| 102 | 102 | </Tooltip> |
| 103 | 103 | </div> | ... | ... |
| ... | ... | @@ -88,10 +88,10 @@ export const transformTextComponentConfig = ( |
| 88 | 88 | } as TextComponentLayout, |
| 89 | 89 | value: { |
| 90 | 90 | name: dataSourceRecord.attributeRename || dataSourceRecord.attribute, |
| 91 | - // value: record.va | |
| 91 | + value: dataSourceRecord.componentInfo.value, | |
| 92 | 92 | icon: dataSourceRecord.componentInfo.icon, |
| 93 | 93 | unit: dataSourceRecord.componentInfo.unit, |
| 94 | - updateTime: record.updateTime || record.createTime, | |
| 94 | + updateTime: dataSourceRecord.componentInfo.updateTime, | |
| 95 | 95 | fontColor: dataSourceRecord.componentInfo.fontColor, |
| 96 | 96 | iconColor: dataSourceRecord.componentInfo.iconColor, |
| 97 | 97 | } as TextComponentValue, | ... | ... |
| ... | ... | @@ -23,6 +23,7 @@ |
| 23 | 23 | import { DataBoardLayoutInfo } from '../types/type'; |
| 24 | 24 | import { WidgetComponentType } from './config/visualOptions'; |
| 25 | 25 | import Authority from '/@/components/Authority/src/Authority.vue'; |
| 26 | + import { useSocketConnect } from '../hook/useSocketConnect'; | |
| 26 | 27 | |
| 27 | 28 | const ROUTE = useRoute(); |
| 28 | 29 | |
| ... | ... | @@ -43,9 +44,6 @@ |
| 43 | 44 | const GirdLayoutColNum = 24; |
| 44 | 45 | const GridLayoutMargin = 10; |
| 45 | 46 | |
| 46 | - const defaultWidth = 6; | |
| 47 | - const defaultHeight = 6; | |
| 48 | - | |
| 49 | 47 | const handleBack = () => { |
| 50 | 48 | ROUTER.go(-1); |
| 51 | 49 | }; |
| ... | ... | @@ -164,6 +162,8 @@ |
| 164 | 162 | } catch (error) {} |
| 165 | 163 | }; |
| 166 | 164 | |
| 165 | + const { beginSendMessage } = useSocketConnect(dataBoardList); | |
| 166 | + | |
| 167 | 167 | const getDataBoardComponent = async () => { |
| 168 | 168 | try { |
| 169 | 169 | // dataBoardList.value = []; |
| ... | ... | @@ -178,8 +178,8 @@ |
| 178 | 178 | } |
| 179 | 179 | return { |
| 180 | 180 | i: item.id, |
| 181 | - w: layout.w || defaultWidth, | |
| 182 | - h: layout.h || defaultHeight, | |
| 181 | + w: layout.w || DEFAULT_WIDGET_WIDTH, | |
| 182 | + h: layout.h || DEFAULT_WIDGET_HEIGHT, | |
| 183 | 183 | x: layout.x || 0, |
| 184 | 184 | y: layout.y || 0, |
| 185 | 185 | record: { |
| ... | ... | @@ -189,6 +189,7 @@ |
| 189 | 189 | }, |
| 190 | 190 | }; |
| 191 | 191 | }); |
| 192 | + beginSendMessage(); | |
| 192 | 193 | } catch (error) {} |
| 193 | 194 | }; |
| 194 | 195 | ... | ... |
| 1 | +import { useWebSocket } from '@vueuse/core'; | |
| 2 | +import { Ref, unref } from 'vue'; | |
| 3 | +import { DataBoardLayoutInfo } from '../types/type'; | |
| 4 | +import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum'; | |
| 5 | +import { getAuthCache } from '/@/utils/auth'; | |
| 6 | + | |
| 7 | +interface SocketMessage { | |
| 8 | + tsSubCmds: SocketMessageItem[]; | |
| 9 | +} | |
| 10 | + | |
| 11 | +interface SocketMessageItem { | |
| 12 | + entityType: string; | |
| 13 | + entityId: string; | |
| 14 | + scope: string; | |
| 15 | + cmdId: number; | |
| 16 | + keys: string; | |
| 17 | +} | |
| 18 | + | |
| 19 | +interface CmdMapping { | |
| 20 | + componentId: string; | |
| 21 | + deviceId: string; | |
| 22 | + recordIndex: number; | |
| 23 | + dataSourceIndex: number; | |
| 24 | + attribute: string; | |
| 25 | +} | |
| 26 | + | |
| 27 | +interface ResponseMessage { | |
| 28 | + subscriptionId: number; | |
| 29 | + errorCode: number; | |
| 30 | + errorMsg: Nullable<string>; | |
| 31 | + data: { | |
| 32 | + [key: string]: [[number, string]]; | |
| 33 | + }; | |
| 34 | + latestValues: { | |
| 35 | + [key: string]: number; | |
| 36 | + }; | |
| 37 | +} | |
| 38 | + | |
| 39 | +const generateMessage = (deviceId: string, cmdId: number, attr: string): SocketMessageItem => { | |
| 40 | + return { | |
| 41 | + entityType: 'DEVICE', | |
| 42 | + entityId: deviceId, | |
| 43 | + scope: 'LATEST_TELEMETRY', | |
| 44 | + cmdId, | |
| 45 | + keys: attr, | |
| 46 | + }; | |
| 47 | +}; | |
| 48 | + | |
| 49 | +export function useSocketConnect(dataSourceRef: Ref<DataBoardLayoutInfo[]>) { | |
| 50 | + const token = getAuthCache(JWT_TOKEN_KEY); | |
| 51 | + | |
| 52 | + const cmdIdMapping = new Map<number, CmdMapping>(); | |
| 53 | + | |
| 54 | + const waitSendQueue: string[] = []; | |
| 55 | + | |
| 56 | + const config = { | |
| 57 | + server: `${import.meta.env.VITE_WEB_SOCKET}${token}`, | |
| 58 | + }; | |
| 59 | + | |
| 60 | + const getNeedUpdateValueById = (componentId: string, deviceId: string) => {}; | |
| 61 | + | |
| 62 | + const getNeedUpdateValueByIndex = (recordIndex: number, dataSourceIndex: number) => { | |
| 63 | + return unref(dataSourceRef)[recordIndex].record.dataSource[dataSourceIndex]; | |
| 64 | + }; | |
| 65 | + | |
| 66 | + const { close, send, open, status } = useWebSocket(config.server, { | |
| 67 | + onConnected() { | |
| 68 | + if (waitSendQueue.length) { | |
| 69 | + waitSendQueue.forEach((string) => { | |
| 70 | + send(string); | |
| 71 | + }); | |
| 72 | + waitSendQueue.length = 0; | |
| 73 | + } | |
| 74 | + }, | |
| 75 | + onMessage(ws, message) { | |
| 76 | + try { | |
| 77 | + const res: ResponseMessage = JSON.parse(message.data); | |
| 78 | + const { subscriptionId, data = {} } = res; | |
| 79 | + const mappingRecord = cmdIdMapping.get(subscriptionId); | |
| 80 | + if (!mappingRecord) return; | |
| 81 | + const { attribute, recordIndex, dataSourceIndex } = mappingRecord; | |
| 82 | + const [[timespan, value]] = data[attribute]; | |
| 83 | + const record = getNeedUpdateValueByIndex(recordIndex, dataSourceIndex); | |
| 84 | + record.componentInfo.value = value; | |
| 85 | + record.componentInfo.updateTime = timespan; | |
| 86 | + } catch (error) { | |
| 87 | + throw Error(error as string); | |
| 88 | + } | |
| 89 | + }, | |
| 90 | + onDisconnected() { | |
| 91 | + close(); | |
| 92 | + }, | |
| 93 | + }); | |
| 94 | + | |
| 95 | + const setCmdId = (cmdId: number, record: CmdMapping) => { | |
| 96 | + cmdIdMapping.set(cmdId, record); | |
| 97 | + }; | |
| 98 | + | |
| 99 | + const transformSocketMessageItem = () => { | |
| 100 | + const messageList: SocketMessageItem[] = []; | |
| 101 | + console.log(dataSourceRef); | |
| 102 | + unref(dataSourceRef).forEach((record, recordIndex) => { | |
| 103 | + const componentId = record.record.id; | |
| 104 | + record.record.dataSource.forEach((dataSource, dataSourceIndex) => { | |
| 105 | + const { deviceId, attribute } = dataSource; | |
| 106 | + const cmdId = recordIndex * dataSourceIndex + recordIndex; | |
| 107 | + setCmdId(cmdId, { | |
| 108 | + componentId, | |
| 109 | + deviceId, | |
| 110 | + recordIndex, | |
| 111 | + dataSourceIndex, | |
| 112 | + attribute, | |
| 113 | + }); | |
| 114 | + | |
| 115 | + messageList.push(generateMessage(deviceId, cmdId, attribute)); | |
| 116 | + }); | |
| 117 | + }); | |
| 118 | + return { | |
| 119 | + tsSubCmds: messageList, | |
| 120 | + } as SocketMessage; | |
| 121 | + }; | |
| 122 | + | |
| 123 | + const beginSendMessage = () => { | |
| 124 | + // close(); | |
| 125 | + cmdIdMapping.clear(); | |
| 126 | + | |
| 127 | + // open(); | |
| 128 | + const messageList = transformSocketMessageItem(); | |
| 129 | + | |
| 130 | + if (unref(status) !== 'OPEN') { | |
| 131 | + waitSendQueue.push(JSON.stringify(messageList)); | |
| 132 | + return; | |
| 133 | + } | |
| 134 | + send(JSON.stringify(messageList)); | |
| 135 | + }; | |
| 136 | + | |
| 137 | + return { | |
| 138 | + close, | |
| 139 | + send, | |
| 140 | + open, | |
| 141 | + beginSendMessage, | |
| 142 | + }; | |
| 143 | +} | ... | ... |