/** * 设备详情页:仅监听全局长连接推送并合入展示;不在此页 subscribe / unsubscribe。 * 列表侧(DeviceTab)在设备 Tab 内保持订阅,从列表进详情不会触发列表退订。 */ import { connectIfLoggedIn } from '@/common/utils/appUserWs.js' function normalizeMac(m) { if (m === undefined || m === null) return '' return String(m).replace(/:/g, '').trim().toUpperCase() } function pickDeviceRecord(vm) { if (vm.xqobj && typeof vm.xqobj === 'object' && Object.keys(vm.xqobj).length) { return vm.xqobj } if (vm.sbobj && typeof vm.sbobj === 'object' && Object.keys(vm.sbobj).length) { return vm.sbobj } return null } function mergeIotDataPoint(ov, nv) { if (nv === null) return null if (typeof nv !== 'object' || Array.isArray(nv)) { return nv } if (!ov || typeof ov !== 'object' || Array.isArray(ov)) { return { ...nv } } const merged = { ...ov, ...nv } // 长连接 patch 里若带 value: undefined 或缺省,不要冲掉 HTTP 里已有 value/desc if (Object.prototype.hasOwnProperty.call(nv, 'value') && nv.value === undefined) { if (Object.prototype.hasOwnProperty.call(ov, 'value') && ov.value !== undefined) { merged.value = ov.value } } if (!Object.prototype.hasOwnProperty.call(nv, 'desc') || nv.desc === undefined || nv.desc === '') { if (Object.prototype.hasOwnProperty.call(ov, 'desc') && ov.desc !== undefined && ov.desc !== '') { merged.desc = ov.desc } } return merged } export default { data() { return { deviceUserWsPageActive: false, _deviceWsIotPartials: {}, } }, onLoad() { this._deviceWsIotPartials = {} }, computed: { _deviceMacForWs() { const r = pickDeviceRecord(this) const m = r && r.mac return m ? String(m).trim() : '' }, }, onShow() { this.deviceUserWsPageActive = true connectIfLoggedIn() uni.$off('appWs:deviceData', this._onAppWsDeviceData) uni.$off('appWs:deviceOnlineStatus', this._onAppWsDeviceOnlineStatus) uni.$on('appWs:deviceData', this._onAppWsDeviceData) uni.$on('appWs:deviceOnlineStatus', this._onAppWsDeviceOnlineStatus) }, onHide() { uni.$off('appWs:deviceData', this._onAppWsDeviceData) uni.$off('appWs:deviceOnlineStatus', this._onAppWsDeviceOnlineStatus) this._leaveDeviceUserWs() }, onUnload() { uni.$off('appWs:deviceData', this._onAppWsDeviceData) uni.$off('appWs:deviceOnlineStatus', this._onAppWsDeviceOnlineStatus) this._leaveDeviceUserWs() }, onBeforeUnmount() { uni.$off('appWs:deviceData', this._onAppWsDeviceData) uni.$off('appWs:deviceOnlineStatus', this._onAppWsDeviceOnlineStatus) this._leaveDeviceUserWs() }, methods: { _onAppWsDeviceData(msg) { if (!this.deviceUserWsPageActive) return if (!msg || msg.event !== 'device_data') return const outer = msg.data if (!outer || typeof outer !== 'object') return const myMac = normalizeMac(this._deviceMacForWs) if (!myMac) return const inner = outer.data if (!inner || typeof inner !== 'object') return const payloadMac = normalizeMac(inner.mac != null ? inner.mac : outer.mac) if (payloadMac && payloadMac !== myMac) return const patch = inner.data !== undefined ? inner.data : inner if (!patch || typeof patch !== 'object') return this._applyDeviceDataPatch(patch) }, _onAppWsDeviceOnlineStatus(msg) { if (!this.deviceUserWsPageActive) return if (!msg || msg.event !== 'device_online_status') return const outer = msg.data if (!outer || typeof outer !== 'object') return const myMac = normalizeMac(this._deviceMacForWs) if (!myMac) return const inner = outer.data if (!inner || typeof inner !== 'object') return const payloadMac = normalizeMac(inner.mac != null ? inner.mac : outer.mac) if (payloadMac && payloadMac !== myMac) return const statusPayload = inner.data !== undefined && typeof inner.data === 'object' ? inner.data : inner this._applyDeviceOnlinePatch(statusPayload) }, _applyDeviceDataPatch(patch) { const rec = pickDeviceRecord(this) if (!rec) return if (!rec.iotData) { this.$set(rec, 'iotData', {}) } const keys = Object.keys(patch).filter((k) => k !== 'mac') let any = false for (const k of keys) { const nv = patch[k] const ov = rec.iotData[k] const prevP = this._deviceWsIotPartials[k] const pMerged = typeof nv === 'object' && nv !== null && !Array.isArray(nv) ? mergeIotDataPoint(prevP || null, nv) : mergeIotDataPoint(prevP || null, { value: nv }) this.$set(this._deviceWsIotPartials, k, pMerged) const nextVal = nv !== null && typeof nv === 'object' && !Array.isArray(nv) ? mergeIotDataPoint(ov, nv) : nv if (JSON.stringify(ov) === JSON.stringify(nextVal)) continue this.$set(rec.iotData, k, nextVal) any = true } if (any && typeof this._afterDeviceUserWsData === 'function') { this._afterDeviceUserWsData() } }, _applyDeviceOnlinePatch(inner) { const rec = pickDeviceRecord(this) if (!rec) return const os = inner.onlineStatus const lot = inner.lastOnlineTime let changed = false if (os !== undefined && os !== null && os !== '') { const n = Number(os) if (!Number.isNaN(n) && rec.onlineStatus !== n) { this.$set(rec, 'onlineStatus', n) changed = true } } if (lot !== undefined && lot !== null && rec.lastOnlineTime !== lot) { this.$set(rec, 'lastOnlineTime', lot) changed = true } if (!rec.iotOnlineData) { this.$set(rec, 'iotOnlineData', {}) } if (os !== undefined && os !== null && os !== '') { const s = String(os) if (rec.iotOnlineData.onlineStatus !== s) { this.$set(rec.iotOnlineData, 'onlineStatus', s) changed = true } } if (changed && typeof this._afterDeviceUserWsData === 'function') { this._afterDeviceUserWsData() } }, _leaveDeviceUserWs() { this.deviceUserWsPageActive = false }, reapplyDeviceWsIotToHttpIotData() { const rec = pickDeviceRecord(this) if (!rec || !this._deviceWsIotPartials) return const pkeys = Object.keys(this._deviceWsIotPartials) if (!pkeys.length) return if (!rec.iotData) this.$set(rec, 'iotData', {}) for (const k of pkeys) { const p = this._deviceWsIotPartials[k] if (p === null || p === undefined) continue const base = rec.iotData[k] this.$set(rec.iotData, k, mergeIotDataPoint(base, p)) } }, }, }