288 lines
7.5 KiB
Vue
Raw Normal View History

2025-06-06 06:23:49 +08:00
<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>