jeecgBootUniapp/src/pages-home/device-control/office/ALLleader.vue
2025-07-24 09:39:14 +08:00

756 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>