288 lines
7.5 KiB
Vue
288 lines
7.5 KiB
Vue
![]() |
<template>
|
||
|
<view class="gateway-container">
|
||
|
<!-- 设备状态卡片 -->
|
||
|
<view class="card">
|
||
|
<view class="status-titletop">{{ title }}</view>
|
||
|
<view style="padding:20rpx;">
|
||
|
<u--form labelPosition="left" labelWidth="100"
|
||
|
:labelStyle="{ marginRight: '16px', lineHeight: '32px', width: '50px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', color: '#000000' }">
|
||
|
<view class="version-wrap">
|
||
|
<u-form-item :label="$tt('status.deviceVersion') || '设备版本'">
|
||
|
<u-row>
|
||
|
<u-col span="8">
|
||
|
<u--text :text="'Version' + (device.firmwareVersion || '')"></u--text>
|
||
|
</u-col>
|
||
|
</u-row>
|
||
|
</u-form-item>
|
||
|
</view>
|
||
|
</u--form>
|
||
|
</view>
|
||
|
</view>
|
||
|
|
||
|
<!-- Webview容器 -->
|
||
|
<view class="webview-container">
|
||
|
<!-- #ifdef APP-PLUS -->
|
||
|
<web-view :src="fullUrl" @message="handleWebviewMessage" ref="webview"></web-view>
|
||
|
<!-- #endif -->
|
||
|
|
||
|
<!-- #ifdef H5 -->
|
||
|
<iframe :src="fullUrl" frameborder="0" class="h5-iframe" @load="iframeLoaded"></iframe>
|
||
|
<!-- #endif -->
|
||
|
|
||
|
<!-- #ifdef MP-WEIXIN -->
|
||
|
<web-view id="wxWebview" :src="fullUrl" @message="handleWebviewMessage" @load="wxWebviewLoaded"
|
||
|
@error="wxWebviewError"></web-view>
|
||
|
<!-- #endif -->
|
||
|
</view>
|
||
|
</view>
|
||
|
</template>
|
||
|
|
||
|
<script>
|
||
|
export default {
|
||
|
name: 'gateway-control',
|
||
|
props: {
|
||
|
device: {
|
||
|
type: Object,
|
||
|
default: () => ({
|
||
|
deviceId: '',
|
||
|
serialNumber: '',
|
||
|
firmwareVersion: '',
|
||
|
status: 0,
|
||
|
isShadow: 0,
|
||
|
thingsModels: []
|
||
|
}),
|
||
|
required: true
|
||
|
}
|
||
|
},
|
||
|
data() {
|
||
|
return {
|
||
|
title: '设备离线',
|
||
|
baseUrl: 'https://iot-xcwl.cn/h5/index.html',
|
||
|
wxWebviewReady: false
|
||
|
};
|
||
|
},
|
||
|
computed: {
|
||
|
fullUrl() {
|
||
|
if (!this.device?.deviceId || !this.device?.serialNumber) {
|
||
|
console.error('设备ID或序列号未定义', this.device);
|
||
|
return this.baseUrl;
|
||
|
}
|
||
|
|
||
|
const productParamModel = this.device.thingsModels?.find(model => model.id === 'productpram');
|
||
|
let paramData = productParamModel?.shadow || '';
|
||
|
if (typeof paramData === 'string' && paramData.startsWith('JSON=')) {
|
||
|
paramData = paramData.substring(5);
|
||
|
}
|
||
|
const paramDataString = typeof paramData === 'string' ? paramData : JSON.stringify(paramData || {});
|
||
|
|
||
|
return `${this.baseUrl}?deviceId=${encodeURIComponent(this.device.deviceId)}&serialNumber=${encodeURIComponent(this.device.serialNumber)}&initialData=${encodeURIComponent(paramDataString)}`;
|
||
|
}
|
||
|
},
|
||
|
mounted() {
|
||
|
this.updateDeviceStatus(this.device);
|
||
|
this.mqttCallback();
|
||
|
|
||
|
// 监听来自 H5 的消息
|
||
|
uni.$on('h5Message', this.handleH5Message);
|
||
|
},
|
||
|
beforeDestroy() {
|
||
|
// 移除监听器,防止内存泄漏
|
||
|
uni.$off('h5Message', this.handleH5Message);
|
||
|
|
||
|
if (this.messageListener) {
|
||
|
window.removeEventListener('message', this.messageListener);
|
||
|
}
|
||
|
},
|
||
|
methods: {
|
||
|
// 微信 webview 加载完成
|
||
|
wxWebviewLoaded() {
|
||
|
console.log('微信webview加载完成');
|
||
|
this.wxWebviewReady = true;
|
||
|
this.sendInitialDataToWebview();
|
||
|
},
|
||
|
|
||
|
// 获取 webview 组件
|
||
|
getWebviewComponent() {
|
||
|
const pages = getCurrentPages();
|
||
|
const currentPage = pages[pages.length - 1];
|
||
|
return currentPage.selectComponent('#wxWebview');
|
||
|
},
|
||
|
|
||
|
// 发送初始数据到 webview
|
||
|
sendInitialDataToWebview() {
|
||
|
const productParamModel = this.device.thingsModels?.find(model => model.id === 'productpram');
|
||
|
if (!productParamModel) {
|
||
|
console.warn('未找到 productpram 模型');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let paramData = productParamModel.shadow || '';
|
||
|
if (typeof paramData === 'string' && paramData.startsWith('JSON=')) {
|
||
|
paramData = paramData.substring(5);
|
||
|
}
|
||
|
const paramDataString = typeof paramData === 'string' ? paramData : JSON.stringify(paramData || {});
|
||
|
|
||
|
// #ifdef MP-WEIXIN
|
||
|
const component = this.getWebviewComponent();
|
||
|
if (component) {
|
||
|
component.postMessage({
|
||
|
type: 'initialData',
|
||
|
data: paramDataString,
|
||
|
timestamp: Date.now()
|
||
|
});
|
||
|
console.log('【微信小程序】发送初始数据成功:', paramDataString);
|
||
|
} else {
|
||
|
console.error('获取 webview 组件失败');
|
||
|
}
|
||
|
// #endif
|
||
|
|
||
|
// #ifdef APP-PLUS
|
||
|
if (this.$refs.webview) {
|
||
|
this.$refs.webview.postMessage({
|
||
|
type: 'initialData',
|
||
|
data: paramDataString,
|
||
|
timestamp: Date.now()
|
||
|
});
|
||
|
console.log('【APP】发送初始数据成功:', paramDataString);
|
||
|
}
|
||
|
// #endif
|
||
|
|
||
|
// #ifdef H5
|
||
|
const iframe = document.querySelector('.h5-iframe');
|
||
|
if (iframe && iframe.contentWindow) {
|
||
|
iframe.contentWindow.postMessage({
|
||
|
type: 'initialData',
|
||
|
data: paramDataString,
|
||
|
timestamp: Date.now()
|
||
|
}, '*');
|
||
|
console.log('【H5】发送初始数据成功:', paramDataString);
|
||
|
}
|
||
|
// #endif
|
||
|
},
|
||
|
|
||
|
// 不加任何判断,收到啥就打印啥
|
||
|
handleWebviewMessage(e) {
|
||
|
console.log('【小程序】收到 H5 消息:', e);
|
||
|
|
||
|
let messageData = null;
|
||
|
|
||
|
// 处理不同平台的消息格式
|
||
|
// #ifdef MP-WEIXIN
|
||
|
messageData = e.detail.data; // 直接取整个对象
|
||
|
// #endif
|
||
|
|
||
|
// #ifdef APP-PLUS || H5
|
||
|
messageData = e.data || e.detail?.data?.[0] || e.detail.data;
|
||
|
// #endif
|
||
|
|
||
|
if (!messageData) {
|
||
|
console.warn('无法解析的消息格式', e);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// 不管是什么类型,都先打印出来
|
||
|
console.log('【小程序】原始消息内容:', messageData);
|
||
|
|
||
|
// 使用 uni.$emit 广播消息
|
||
|
uni.$emit('h5Message', messageData);
|
||
|
},
|
||
|
|
||
|
// 接收 H5 消息的回调
|
||
|
handleH5Message(message) {
|
||
|
console.log('【uni.$on】接收到 H5 消息:', message);
|
||
|
|
||
|
if (message.type === 'mqtt_data') {
|
||
|
console.log('处理 mqtt_data 消息:', message.payload);
|
||
|
// 这里可以执行业务逻辑,例如 MQTT 发布等
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// 更新设备状态
|
||
|
updateDeviceStatus(device) {
|
||
|
if (!device) {
|
||
|
this.title = '设备未连接';
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (device.status === 3) {
|
||
|
this.title = this.$tt('status.online') || '在线';
|
||
|
} else {
|
||
|
this.title = device.isShadow === 1 ?
|
||
|
(this.$tt('status.shadow') || '影子模式') :
|
||
|
(this.$tt('status.deviceOffline') || '离线');
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// MQTT 状态监听
|
||
|
mqttCallback() {
|
||
|
if (!this.$mqttTool?.client) {
|
||
|
console.warn('MQTT客户端未初始化');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.$mqttTool.client.removeAllListeners('message');
|
||
|
|
||
|
this.$mqttTool.client.on('message', (topic, message, buffer) => {
|
||
|
const topics = topic.split('/');
|
||
|
if (this.device.serialNumber !== topics[2]) return;
|
||
|
|
||
|
const msg = JSON.parse(message.toString());
|
||
|
if (topics[3] === 'status') {
|
||
|
this.device.status = msg.status;
|
||
|
this.device.isShadow = msg.isShadow;
|
||
|
this.device.rssi = msg.rssi;
|
||
|
this.updateDeviceStatus(this.device);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
</script>
|
||
|
|
||
|
<style lang="scss" scoped>
|
||
|
/* 保持原有样式不变 */
|
||
|
.gateway-container {
|
||
|
padding: 20rpx;
|
||
|
height: 100%;
|
||
|
display: flex;
|
||
|
flex-direction: column;
|
||
|
|
||
|
.card {
|
||
|
background-color: #fff;
|
||
|
border-radius: 20rpx;
|
||
|
margin-bottom: 20rpx;
|
||
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||
|
}
|
||
|
|
||
|
.webview-container {
|
||
|
flex: 1;
|
||
|
height: 0;
|
||
|
overflow: hidden;
|
||
|
border-radius: 20rpx;
|
||
|
background-color: #fff;
|
||
|
|
||
|
.h5-iframe {
|
||
|
width: 100%;
|
||
|
height: 600px;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
.status-titletop {
|
||
|
font-weight: bold;
|
||
|
font-size: 32rpx;
|
||
|
color: #333333;
|
||
|
line-height: 42rpx;
|
||
|
text-align: left;
|
||
|
padding: 15rpx 28rpx;
|
||
|
}
|
||
|
|
||
|
.version-wrap {
|
||
|
background-color: #F7F7F7;
|
||
|
border-radius: 10rpx;
|
||
|
padding: 0 42rpx;
|
||
|
font-size: 28rpx;
|
||
|
color: #000000;
|
||
|
line-height: 42rpx;
|
||
|
}
|
||
|
}
|
||
|
</style>
|