jeecgBootUniapp/src/pages-home/device-control/office/ALLleader.vue

814 lines
18 KiB
Vue
Raw Normal View History

2025-07-18 09:26:18 +00:00
<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 {
gethomelist,
treeRootList,
contiontmqtt,
selectfjkzbmforfjid,
createdSwitch,
getMqttPushClient,
treeChildList,
listAllRegions,
Selecthomelist
} from '@/api/devicecontrol/officedevice';
// 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
});
}
/***-------------------------------开始数据处理------------------------**/
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) => {
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-----------
// 修改后的WebSocket地址使用新的/simple端点
// const WS_URL = 'http://10.75.15.246:8899/mqttprod/ws/simple';
const WS_URL = 'https://szcx.zyyt.sinopec.com/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 = () => {
if (connected.value) return;
console.log('正在连接WebSocket...');
connectionStatus.value = '连接中...';
// 先关闭可能存在的旧连接
if (socketTask) {
socketTask.close({});
socketTask = null;
}
// 修复2使用正确的uni.connectSocket API
socketTask = uni.connectSocket({
url: WS_URL,
protocols: ['v12.stomp', 'v11.stomp'], // 添加STOMP协议支持
success: () => {
console.log('开始连接WebSocket');
},
fail: (err) => {
console.error('连接失败:', err);
connectionStatus.value = '连接失败';
messages.value.push({
type: 'error',
content: `连接失败: ${err.errMsg || '未知错误'}`,
timestamp: Date.now()
});
}
});
// 修复3使用uni.onSocketOpen替代socketTask.onOpen
uni.onSocketOpen((res) => {
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,
success: () => {
messages.value.push({
type: 'system',
content: '已发送STOMP连接请求',
timestamp: Date.now()
});
}
});
});
// 监听消息
socketTask.onMessage((res) => {
const data = res.data;
console.log('收到原始消息:', data);
// STOMP帧解析
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$/, '');
switch (commandLine) {
case 'CONNECTED':
connectionStatus.value = 'STOMP已连接';
messages.value.push({
type: 'system',
content: `STOMP协议连接成功版本: ${headers.version}`,
timestamp: Date.now()
});
// 订阅主题
const subscribeFrame = buildStompFrame('SUBSCRIBE', {
id: 'sub-0',
destination: '/topic/messages'
});
socketTask.send({
data: subscribeFrame,
success: () => {
messages.value.push({
type: 'system',
content: '已订阅消息主题',
timestamp: Date.now()
});
}
});
break;
case 'MESSAGE':
messages.value.push({
type: 'received',
content: `收到: ${body}`,
timestamp: Date.now()
});
handlesocketMessage(body);
break;
case 'ERROR':
messages.value.push({
type: 'error',
content: `服务器错误: ${body}`,
timestamp: Date.now()
});
break;
default:
messages.value.push({
type: 'system',
content: `未知帧类型: ${commandLine}`,
timestamp: Date.now()
});
}
});
// 监听错误(保持原有实现)
// 监听关闭(保持原有实现)
// 监听错误
uni.onSocketError((err) => {
console.error('WebSocket错误:', err);
connectionStatus.value = '连接错误';
connected.value = false;
messages.value.push({
type: 'error',
content: `连接错误: ${err.errMsg || '未知错误'}`,
timestamp: Date.now()
});
});
// 监听关闭
uni.onSocketClose((closeRes) => {
console.log('WebSocket连接已关闭', closeRes);
connectionStatus.value = '已断开';
connected.value = false;
messages.value.push({
type: 'system',
content: `WebSocket连接已关闭: ${closeRes.code || ''} ${closeRes.reason || ''}`,
timestamp: Date.now()
});
});
};
// 发送消息
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: () => {
messages.value.push({
type: 'sent',
content: `发送: ${message.value}`,
timestamp: Date.now()
});
message.value = '';
},
fail: (err) => {
messages.value.push({
type: 'error',
content: `发送失败: ${err.errMsg}`,
timestamp: Date.now()
});
}
});
};
// 断开WebSocket优化实现
const disconnectWebSocket = () => {
if (socketTask) {
// 发送STOMP DISCONNECT帧
const disconnectFrame = buildStompFrame('DISCONNECT', {
receipt: 'disconnect-receipt'
});
socketTask.send({
data: disconnectFrame,
success: () => {
socketTask.close({
code: 1000,
reason: '用户手动关闭',
success: () => {
console.log('WebSocket已关闭');
connected.value = false;
connectionStatus.value = '已断开';
messages.value.push({
type: 'system',
content: '连接已安全关闭',
timestamp: Date.now()
});
}
});
}
});
}
}
onMounted(() => {
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>