756 lines
16 KiB
Vue
756 lines
16 KiB
Vue
<route lang="json5" type="page">
|
||
{
|
||
layout: 'default',
|
||
style: {
|
||
navigationBarTitleText: '办公室设备列表',
|
||
|
||
},
|
||
}
|
||
</route>
|
||
<template>
|
||
<view class="container">
|
||
<!-- 楼层选择器 -->
|
||
<wd-row>
|
||
<wd-col :span="16">
|
||
<wd-col-picker label="选择楼层" prop="type" v-model="typeData" :columns="typeData" @confirm="handleConfirm"
|
||
:column-change="columnChange" :display-format="displayFormat" style="width: 99%;" />
|
||
</wd-col>
|
||
<wd-col :span="8" style="margin-top: 5px;">
|
||
<wd-button @click="cleanreset">重置</wd-button>
|
||
</wd-col>
|
||
</wd-row>
|
||
|
||
|
||
|
||
<!-- 设备状态卡片网格 - 两列布局 -->
|
||
<!-- 设备状态卡片网格 - 两列布局 -->
|
||
<view class="floor-wrapper">
|
||
<view v-for="(floor, floorIndex) in filteredFloors" :key="'floor' + floorIndex" class="floor-section">
|
||
<view class="room-grid">
|
||
<view v-for="(room, roomIndex) in floor.rooms" :key="room.id" class="room-card"
|
||
:class="{ 'room-active': room.homezt === 1 }"
|
||
@click="jump(`./officehomedevice?id=${room.id}&fjh=${room.fjh}`)">
|
||
<text v-if="room.fjh" class="room-number">{{ room.fjh }}室</text>
|
||
<view class="room-info">
|
||
<text class="info-label">所属部门:</text>
|
||
<text class="info-value">{{ room.ssbmname }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!--
|
||
<view class="connection-section">
|
||
<button v-if="!connected" @click="connectWebSocket" class="connect-btn">
|
||
连接WebSocket
|
||
</button>
|
||
|
||
<button @click="disconnectWebSocket" class="disconnect-btn">
|
||
断开连接
|
||
</button>
|
||
|
||
<text :class="['status', connected ? 'connected' : 'disconnected']">
|
||
{{ connectionStatus }}
|
||
</text>
|
||
</view>
|
||
<view class="message-container">
|
||
<text class="subtitle">消息记录</text>
|
||
<scroll-view scroll-y="true" class="message-list">
|
||
<view v-for="(item, index) in messages" :key="index" :class="['message-item', item.type]">
|
||
<text class="message-content">{{ item.content }}</text>
|
||
</view>
|
||
</scroll-view>
|
||
</view> -->
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
ref
|
||
} from 'vue';
|
||
import {
|
||
onLaunch,
|
||
onShow,
|
||
onHide,
|
||
onLoad,
|
||
onReady
|
||
} from '@dcloudio/uni-app'
|
||
import {
|
||
gethomelist,
|
||
treeRootList,
|
||
contiontmqtt,
|
||
selectfjkzbmforfjid,
|
||
createdSwitch,
|
||
getMqttPushClient,
|
||
treeChildList,
|
||
listAllRegions,
|
||
Selecthomelist
|
||
} from '@/api/devicecontrol/officedevice';
|
||
// import socketforleader from '@/common/socketforleader'
|
||
|
||
// WebSocket状态
|
||
const connected = ref(false);
|
||
const message = ref('');
|
||
const messages = ref([]);
|
||
let socketTask = null;
|
||
const filteredFloors = ref([]); //楼层数据
|
||
const treeData = ref([]);
|
||
const homelist = ref([]) //获取房间信息
|
||
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||
// 树节点选择处理
|
||
const selectedKeys = ref([]);
|
||
const lastSelectedNode = ref('');
|
||
const typeData = ref([])
|
||
const data = ref([])
|
||
const selectBydata = ref([]) //查找后的房间信息
|
||
const jump = (url) => {
|
||
uni.navigateTo({
|
||
url: url
|
||
});
|
||
}
|
||
|
||
// 应用初始化
|
||
onLaunch(() => {
|
||
console.log('初始化------')
|
||
})
|
||
|
||
|
||
/***-------------------------------开始数据处理------------------------**/
|
||
const getmqttdata = async () => {
|
||
await getMqttPushClient().then(res => {
|
||
console.log("连接mqtt数据服务器----0000000----", res)
|
||
})
|
||
}
|
||
//获取房间信息
|
||
const getofficehomelist = async () => {
|
||
await gethomelist().then((res) => {
|
||
if (res.success) {
|
||
homelist.value = res.result.map((item, index) => ({
|
||
...item, // 保留原有字段
|
||
homezt: 0, // 添加homezt字段,默认为0
|
||
}));
|
||
//添加楼层情况
|
||
}
|
||
}).catch((err) => {
|
||
console.log(err);
|
||
});
|
||
filteredFloors.value = groupByFloor(homelist)
|
||
|
||
console.log("33333333333333-------", filteredFloors.value)
|
||
getAllswitchByids();
|
||
}
|
||
// 数据分组处理
|
||
const groupByFloor = (data) => {
|
||
console.log("data---", data)
|
||
const groups = data.value.reduce((acc, cur) => {
|
||
const floorKey = cur.wlwbmcode.slice(-2) // 根据实际字段调整
|
||
if (!acc[floorKey]) {
|
||
acc[floorKey] = {
|
||
level: floorKey,
|
||
rooms: []
|
||
}
|
||
}
|
||
acc[floorKey].rooms.push(cur)
|
||
return acc
|
||
}, {})
|
||
|
||
return Object.values(groups).sort((a, b) => a.level - b.level)
|
||
}
|
||
/**
|
||
* 操作开关
|
||
*
|
||
*/
|
||
const getAllswitchByids = async () => {
|
||
//查询开关状态
|
||
for (const item of filteredFloors.value) {
|
||
for (const item2 of item.rooms) {
|
||
let fjkzbm = await getfjkzbm(item2)
|
||
if (fjkzbm) {
|
||
await sleep(1000);
|
||
let device = await foreachhomedata(fjkzbm);
|
||
item2.homezt = device.key2 // 使用Vue.set确保响应式,不需要 $set
|
||
console.log("device----房间号-" + item.homename + "---数据---", device.key2)
|
||
} else {
|
||
item2.homezt = device.key2 // 使用Vue.set确保响应式
|
||
}
|
||
}
|
||
}
|
||
}
|
||
const getfjkzbm = async (item) => {
|
||
// console.log("5555----------",item.id)
|
||
let fjkzbm = {};
|
||
//查询房间控制编码
|
||
await selectfjkzbmforfjid({
|
||
fjbid: item.id
|
||
}).then(res => {
|
||
if (res.result.length == 1) {
|
||
fjkzbm = res.result[0]
|
||
}
|
||
})
|
||
return fjkzbm
|
||
}
|
||
|
||
const foreachhomedata = async (fjkzbm) => {
|
||
let device = {}; // 在方法内部定义 device
|
||
const res = await createdSwitch({ // 直接 await 而不是用 .then
|
||
topic: 's' + fjkzbm.sbkzbm,
|
||
qos: 0,
|
||
pushMessage: fjkzbm.kgzt
|
||
})
|
||
if (res) {
|
||
device = JSON.parse(res.message)
|
||
} else {
|
||
console.log("错误房间号--" + fjkzbm + "----")
|
||
}
|
||
|
||
return device
|
||
}
|
||
const cleanreset = () => {
|
||
filteredFloors.value = []
|
||
loadtree();
|
||
}
|
||
//--------树状表展示方法--------
|
||
// 测试查询
|
||
const loadtree = () => {
|
||
listAllRegions().then((res) => {
|
||
data.value = res.result
|
||
typeData.value = [data.value.map(item => {
|
||
return {
|
||
value: item.id,
|
||
label: item.name
|
||
}
|
||
})]
|
||
})
|
||
}
|
||
|
||
function handleConfirm({
|
||
value
|
||
}) {
|
||
// console.log("点击查询-----",value)
|
||
queryRoomsByDepartments(value);
|
||
}
|
||
const displayFormat = (selectedItems) => {
|
||
if (selectedItems.length > 0) {
|
||
return selectedItems[selectedItems.length - 1].label
|
||
}
|
||
}
|
||
const columnChange = ({
|
||
selectedItem,
|
||
resolve,
|
||
finish
|
||
}) => {
|
||
// console.log("selectedItem---------",selectedItem.value)
|
||
const areaData = findChildren(data.value, selectedItem.value)
|
||
if (areaData && areaData.length) {
|
||
resolve(
|
||
areaData.map((item) => {
|
||
return {
|
||
value: item.id,
|
||
label: item.name
|
||
}
|
||
})
|
||
)
|
||
} else {
|
||
finish()
|
||
}
|
||
}
|
||
const findChildren = (data, code) => {
|
||
if (!code) {
|
||
return data
|
||
}
|
||
for (const item of data) {
|
||
// console.log("item---",item)
|
||
// console.log("item--code-",code)
|
||
if (item.id === code) {
|
||
return item.children || null
|
||
}
|
||
if (item.children) {
|
||
const childrenResult = findChildren(item.children, code)
|
||
if (childrenResult) {
|
||
return childrenResult
|
||
}
|
||
}
|
||
}
|
||
return null
|
||
}
|
||
// 根据部门ID查询房间
|
||
const queryRoomsByDepartments = async (departmentIds) => {
|
||
console.log("departmentIds-----------", departmentIds)
|
||
const lastId = departmentIds[departmentIds.length - 1]
|
||
console.log("Processing last ID:", lastId)
|
||
// //在这块添加查询方法
|
||
const res = await Selecthomelist({
|
||
id: lastId
|
||
})
|
||
if (res.success) {
|
||
selectBydata.value = res.result;
|
||
console.log("selectBydata.value:", selectBydata.value)
|
||
filteredFloors.value = groupByFloor(selectBydata)
|
||
// console.log("数据分组-555---", filteredFloors.value[0].rooms)
|
||
//分组完进行房间状态查询,张祥瑞
|
||
getAllswitchByids()
|
||
|
||
}
|
||
}
|
||
//----------------树状展示end-----------
|
||
|
||
//------------------------以上为数据处理---end-------------
|
||
//处理返回的消息
|
||
const handlesocketMessage = (jsonString) => {
|
||
console.log("1300--666666666666666---", jsonString)
|
||
try {
|
||
const messageObj = JSON.parse(jsonString);
|
||
|
||
// 提取嵌套的content字段
|
||
if (messageObj.content) {
|
||
const device = JSON.parse(messageObj.content);
|
||
updatafjzt(messageObj.fjid, device);
|
||
}
|
||
} catch (e) {
|
||
console.error('解析消息出错:', e);
|
||
}
|
||
}
|
||
|
||
const updatafjzt = (fjid, device) => {
|
||
filteredFloors.value = filteredFloors.value.map(floor => ({
|
||
...floor,
|
||
rooms: floor.rooms.map(room => room.id === fjid ? {
|
||
...room,
|
||
homezt: device.key2
|
||
} :
|
||
room
|
||
)
|
||
}));
|
||
};
|
||
|
||
//------------------------以下为websocket-----------
|
||
const WS_URL = 'wss://szcx.zyyt.sinopec.com/mqttprod/ws/simple';
|
||
// const WS_URL = 'ws://10.75.15.246:8899/mqttprod/ws/simple';
|
||
const connectionStatus = ref('未连接');
|
||
|
||
// STOMP帧构建器
|
||
const buildStompFrame = (command, headers = {}, body = '') => {
|
||
let frame = `${command}\n`;
|
||
for (const [key, value] of Object.entries(headers)) {
|
||
frame += `${key}:${value}\n`;
|
||
}
|
||
frame += `\n${body}`;
|
||
return frame + '\x00';
|
||
};
|
||
|
||
// 连接WebSocket
|
||
const connectWebSocket = () => {
|
||
console.log(123123)
|
||
if (connected.value) return;
|
||
|
||
connectionStatus.value = '连接中...';
|
||
|
||
if (socketTask) {
|
||
socketTask.close({});
|
||
socketTask = null;
|
||
}
|
||
console.log(44444444)
|
||
|
||
socketTask = uni.connectSocket({
|
||
url: WS_URL,
|
||
protocols: ['v12.stomp', 'v11.stomp'],
|
||
success: () => {
|
||
console.log('开始连接WebSocket');
|
||
},
|
||
fail: (err) => {
|
||
console.error('连接失败:', err);
|
||
connectionStatus.value = '连接失败';
|
||
}
|
||
});
|
||
console.log(55555555555)
|
||
uni.onSocketOpen((res) => {
|
||
console.log(66666666)
|
||
console.log('WebSocket连接已打开', res);
|
||
connected.value = true;
|
||
connectionStatus.value = '连接中...';
|
||
|
||
// 发送STOMP CONNECT帧
|
||
const connectFrame = buildStompFrame('CONNECT', {
|
||
'accept-version': '1.2',
|
||
'heart-beat': '10000,10000'
|
||
});
|
||
|
||
uni.sendSocketMessage({
|
||
data: connectFrame
|
||
});
|
||
console.log("77777777777")
|
||
});
|
||
|
||
// 监听消息
|
||
socketTask.onMessage((res) => {
|
||
console.log("8888888888888888888")
|
||
const data = res.data;
|
||
const [commandLine, ...headerLines] = data.split('\n');
|
||
const emptyLineIndex = headerLines.findIndex(line => line === '');
|
||
const headers = {};
|
||
|
||
for (let i = 0; i < emptyLineIndex; i++) {
|
||
const [key, value] = headerLines[i].split(':');
|
||
headers[key] = value;
|
||
}
|
||
|
||
const body = headerLines.slice(emptyLineIndex + 1).join('\n').replace(/\x00$/, '');
|
||
console.log("999999999999999", res)
|
||
console.log("121212121212", commandLine)
|
||
switch (commandLine) {
|
||
case 'CONNECTED':
|
||
connectionStatus.value = 'STOMP已连接';
|
||
// 订阅主题
|
||
const subscribeFrame = buildStompFrame('SUBSCRIBE', {
|
||
id: 'sub-0',
|
||
destination: '/topic/messages'
|
||
});
|
||
|
||
socketTask.send({
|
||
data: subscribeFrame
|
||
});
|
||
break;
|
||
|
||
case 'MESSAGE':
|
||
handlesocketMessage(body);
|
||
break;
|
||
|
||
case 'ERROR':
|
||
console.error('服务器错误:', body);
|
||
break;
|
||
}
|
||
});
|
||
|
||
// 监听错误
|
||
uni.onSocketError((err) => {
|
||
console.error('WebSocket错误:', err);
|
||
connectionStatus.value = '连接错误';
|
||
connected.value = false;
|
||
});
|
||
|
||
// 监听关闭
|
||
uni.onSocketClose((closeRes) => {
|
||
console.log('WebSocket连接已关闭', closeRes);
|
||
connectionStatus.value = '已断开';
|
||
connected.value = false;
|
||
});
|
||
};
|
||
|
||
// 发送消息
|
||
const sendMessage = () => {
|
||
if (!message.value.trim()) {
|
||
uni.showToast({
|
||
title: '消息不能为空',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 构建STOMP SEND帧
|
||
const sendFrame = buildStompFrame('SEND', {
|
||
destination: '/app/chat',
|
||
'content-type': 'text/plain'
|
||
}, message.value);
|
||
|
||
socketTask.send({
|
||
data: sendFrame,
|
||
success: () => {
|
||
message.value = '';
|
||
}
|
||
});
|
||
};
|
||
|
||
// 断开WebSocket
|
||
const disconnectWebSocket = () => {
|
||
if (socketTask) {
|
||
// 发送STOMP DISCONNECT帧
|
||
const disconnectFrame = buildStompFrame('DISCONNECT', {
|
||
receipt: 'disconnect-receipt'
|
||
});
|
||
console.log(socketTask)
|
||
socketTask.send({
|
||
data: disconnectFrame,
|
||
success: () => {
|
||
socketTask.close({
|
||
code: 1000,
|
||
reason: '用户手动关闭',
|
||
success: () => {
|
||
connected.value = false;
|
||
connectionStatus.value = '已断开';
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
// disconnectWebSocket();
|
||
loadtree();
|
||
connectWebSocket();
|
||
getmqttdata();
|
||
// getofficehomelist();
|
||
})
|
||
onUnload(() => {
|
||
disconnectWebSocket();
|
||
})
|
||
</script>
|
||
|
||
|
||
<style>
|
||
/* 基础样式重置 */
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.page-container {
|
||
padding: 20rpx;
|
||
background-color: #f5f5f5;
|
||
min-height: 100vh;
|
||
max-width: 750rpx;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
/* 楼层容器 */
|
||
.floor-wrapper {
|
||
display: flex;
|
||
flex-direction: column;
|
||
/* 移除 flex-direction: column,使用默认的 row,但实际楼层是纵向排列的,这里可能需要恢复 column,否则楼层会横向排列 */
|
||
gap: 30rpx;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
padding: 0 20rpx;
|
||
/* 可选:给整体加边距,避免贴边 */
|
||
}
|
||
|
||
.floor-section {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
/* background-color: #fff; */
|
||
border-radius: 16rpx;
|
||
overflow: hidden;
|
||
/* box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); */
|
||
}
|
||
|
||
/* 移除原有的.room-grid样式,替换为以下 */
|
||
.room-grid {
|
||
display: grid;
|
||
/* 每列占50%宽度,减去间距的一半(避免总宽度超100%) */
|
||
grid-template-columns: repeat(2, calc(50% - 10rpx));
|
||
/* 假设间距20rpx,每列减10rpx */
|
||
gap: 20rpx;
|
||
/* 列之间的间距 */
|
||
padding: 20rpx;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
/* 确保padding不增加总宽度 */
|
||
}
|
||
|
||
/* 房间卡片样式优化 */
|
||
.room-card {
|
||
width: 100%;
|
||
/* 强制卡片占满列宽(50% - 10rpx) */
|
||
box-sizing: border-box;
|
||
background-color: #fff;
|
||
border: 1px solid #eee;
|
||
border-radius: 12rpx;
|
||
padding: 25rpx;
|
||
min-height: 140rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.room-card.room-active {
|
||
background-color: #e6f7ee;
|
||
border-color: #b7eb8f;
|
||
}
|
||
|
||
.room-card:active {
|
||
transform: scale(0.98);
|
||
}
|
||
|
||
/* 房间号样式 */
|
||
.room-number {
|
||
font-size: 34rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-bottom: 15rpx;
|
||
display: block;
|
||
}
|
||
|
||
/* 房间信息样式 */
|
||
.room-info {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.info-label {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.info-value {
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* 响应式布局调整 */
|
||
@media (max-width: 375px) {
|
||
.room-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
/* 以下为websocket连接样式 */
|
||
.connection-section {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 30rpx;
|
||
padding: 20rpx;
|
||
background-color: white;
|
||
border-radius: 16rpx;
|
||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.connect-btn,
|
||
.disconnect-btn {
|
||
width: 300rpx;
|
||
height: 80rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
border-radius: 10rpx;
|
||
font-size: 32rpx;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.connect-btn {
|
||
background-color: #2ecc71;
|
||
color: white;
|
||
}
|
||
|
||
.disconnect-btn {
|
||
background-color: #e74c3c;
|
||
color: white;
|
||
}
|
||
|
||
.status {
|
||
flex: 1;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
text-align: center;
|
||
}
|
||
|
||
.connected {
|
||
color: #27ae60;
|
||
}
|
||
|
||
.disconnected {
|
||
color: #e74c3c;
|
||
}
|
||
|
||
.input-section {
|
||
display: flex;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.input {
|
||
flex: 1;
|
||
height: 90rpx;
|
||
padding: 0 20rpx;
|
||
border: 1rpx solid #ddd;
|
||
border-radius: 10rpx;
|
||
background-color: white;
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
.send-btn {
|
||
width: 160rpx;
|
||
height: 90rpx;
|
||
margin-left: 20rpx;
|
||
background-color: #3498db;
|
||
color: white;
|
||
border-radius: 10rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
.send-btn:disabled {
|
||
background-color: #bdc3c7;
|
||
}
|
||
|
||
.message-container {
|
||
flex: 1;
|
||
background-color: white;
|
||
border-radius: 16rpx;
|
||
padding: 20rpx;
|
||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: 34rpx;
|
||
font-weight: bold;
|
||
margin-bottom: 20rpx;
|
||
color: #2c3e50;
|
||
}
|
||
|
||
.message-list {
|
||
flex: 1;
|
||
padding: 10rpx;
|
||
background-color: #f9f9f9;
|
||
border-radius: 10rpx;
|
||
max-height: 60vh;
|
||
}
|
||
|
||
.message-item {
|
||
padding: 20rpx;
|
||
border-bottom: 1rpx solid #eee;
|
||
display: flex;
|
||
flex-direction: column;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.message-item.sent {
|
||
background-color: #e3f2fd;
|
||
border-radius: 8rpx;
|
||
}
|
||
|
||
.message-item.received {
|
||
background-color: #e8f5e9;
|
||
border-radius: 8rpx;
|
||
}
|
||
|
||
.message-item.system {
|
||
background-color: #fffde7;
|
||
border-radius: 8rpx;
|
||
}
|
||
|
||
.message-item.error {
|
||
background-color: #ffebee;
|
||
border-radius: 8rpx;
|
||
}
|
||
|
||
.message-content {
|
||
font-size: 32rpx;
|
||
margin-bottom: 10rpx;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.message-time {
|
||
font-size: 26rpx;
|
||
color: #7f8c8d;
|
||
align-self: flex-end;
|
||
}
|
||
</style> |