totustec-unipp/pages/deviceManage/index.vue
2025-07-15 15:49:56 +08:00

786 lines
22 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.

<template>
<view class="contai_warp">
<view class="top_area_warp">
<view class="top_status_area" :style="{ height: statusBarHeight + 'px' }"></view>
<view class="top_nav_warp">
<view>设备管理</view>
<view class="back_button_warp" @click="goBack()">
<image class="back_button_icon" src="https://online.totustec.com/upload/minePage/mine_area_right.png"></image>
</view>
</view>
</view>
<view class="content_area_warp">
<view class="content_warp">
<!-- 视频图片 -->
<view class="select_device_warp">
<view class="modal_warp">
<view class="select_device_item" :class="{ device_active: getType == 'video' }"
@click="getVideoImg('video')">
获取视频
</view>
<view class="select_device_item" :class="{ device_active: getType == 'img' }" @click="getVideoImg('img')">
获取图片</view>
</view>
</view>
<!-- 临时调试 -->
<!-- <video class="img_video_warp" src="http://broadcasting.totustec.com/live/12345678.m3u8" v-if="socketData.videoUrl"></video> -->
<video class="img_video_warp" :src="socketData.videoUrl" v-if="socketData.videoUrl"></video>
<image class="img_video_warp" v-else :src="socketData.imageUrl"></image>
<!-- 设备状态选择 -->
<view class="select_device_warp">
<view class="modal_warp">
<view class="select_device_item device_model" :class="{ device_active: deviceType == 1 }"
@click="deviceType = 1">设备状态</view>
<view class="select_device_item device_model" :class="{ device_active: deviceType == 2 }"
@click="deviceType = 2">设备日志</view>
<view class="select_device_item device_model" :class="{ device_active: deviceType == 3 }"
@click="deviceType = 3">设备控制</view>
</view>
</view>
<!-- 设备状态 -->
<view class="device_status_warp" v-if="deviceType == 1">
<!-- 设备名称 -->
<view class="device_status_item">
<view class="statusItem_modal_warp">
<view class="item_content_warp">
<view class="item_content_left">设备名称</view>
<view class="item_content_middel">{{
deviceDetail.brand
}}</view>
</view>
<view class="itemContent_line_warp">
<view class="item_content_model">
<view class="item_content_line"></view>
</view>
</view>
</view>
</view>
<!-- 打印状态 -->
<view class="device_status_item">
<view class="statusItem_modal_warp">
<view class="item_content_warp">
<view class="item_content_left">状态</view>
<view class="item_content_middel">
<view class="label_warp" v-if="deviceDetail.status == 0">
<view class="label_icon"></view>
<view class="label_text">空闲</view>
</view>
<view class="label_warp" v-if="deviceDetail.status == 2">
<view class="label_icon" style="background: #00abff"></view>
<view class="label_text" style="color: #00abff">准备中</view>
</view>
<view class="label_warp" v-if="deviceDetail.status == 1">
<view class="label_icon" style="background: #00d195"></view>
<view class="label_text" style="color: #00d195">打印中</view>
</view>
<view class="label_warp" v-if="deviceDetail.status == 3">
<view class="label_icon" style="background: #fdcb3b"></view>
<view class="label_text" style="color: #fdcb3b">铲件中</view>
</view>
<view class="label_warp" v-if="deviceDetail.status == -1">
<view class="label_icon" style="background: #999999"></view>
<view class="label_text" style="color: #999999">断开</view>
</view>
</view>
</view>
<view class="itemContent_line_warp">
<view class="item_content_model">
<view class="item_content_line"> </view>
</view>
</view>
</view>
</view>
<!-- 子状态 -->
<view class="device_status_item">
<view class="statusItem_modal_warp">
<view class="item_content_warp">
<view class="item_content_left">子状态</view>
<view class="item_content_middel">
<view class="label_warp2">
<!-- 状态图标颜色映射 -->
<view class="label_icon" :style="{
background: getStatusColor(deviceDetail.errorStatus),
}"></view>
<!-- 状态文字条件渲染 -->
<view class="label_text label_text2" :style="{
color: getStatusColor(deviceDetail.errorStatus),
}">
{{ getStatusText(deviceDetail.errorStatus) }}
</view>
</view>
</view>
</view>
<view class="itemContent_line_warp">
<view class="item_content_model">
<view class="item_content_line"></view>
</view>
</view>
</view>
</view>
<!-- 当前时间 -->
<view class="device_status_item">
<view class="statusItem_modal_warp">
<view class="item_content_warp">
<view class="item_content_left">当前时间</view>
<view class="item_content_middel">{{ getCurrentTime() }}</view>
</view>
<view class="itemContent_line_warp">
<view class="item_content_model">
<view class="item_content_line"></view>
</view>
</view>
</view>
</view>
<!-- 错误码 -->
<view class="device_status_item">
<view class="statusItem_modal_warp">
<view class="item_content_warp">
<view class="item_content_left">错误码</view>
<view class="item_content_middel">
<!-- {{deviceDetail.errorStatus}} -->
{{ deviceDetail.errorStatus === -1 ? '断开' : deviceDetail.errorStatus }}
</view>
</view>
<view class="itemContent_line_warp">
<view class="item_content_model">
<view class="item_content_line"></view>
</view>
</view>
</view>
</view>
</view>
<!-- 设备控制 -->
<view class="device_control_warp" v-if="deviceType == 3">
<view class="deviceN_input_warp">
<view class="device_name_txt"> 请输入设备名称:</view>
<view class="deviceN_input_area">
<input class="deviceN_input" type="text" v-model="deviceName" />
<view class="clear_deviceN_warp">
<image class="clear_deviceN_icon" @click="deviceName = ''" v-if="deviceName"
src="https://online.totustec.com/upload/deviceManage/clear_deviceN_icon.png"></image>
</view>
</view>
</view>
<view class="deviceN_input_tip">文件位置桌面File文件夹内</view>
<view class="device_operation_warp">
<view class="device_operation_btn" @click="operationData('PRINT')" :style="{
backgroundColor:
deviceDetail.status == 0 && deviceDetail.status != -1
? '#ffffff'
: '#cccccc',
}">
<image class="operation_btn_icon" src="https://online.totustec.com/upload/deviceManage/print_status1.png">
</image>
<view class="operation_btn_txt">开始打印</view>
</view>
<view class="device_operation_btn" @click="operationData('CONTINUE')" :style="{
backgroundColor:
deviceDetail.status != 1 &&
deviceDetail.printStatus != 6 &&
deviceDetail.status != -1
? '#ffffff'
: '#cccccc',
}">
<image class="operation_btn_icon" src="https://online.totustec.com/upload/print_status1.png"></image>
<view class="operation_btn_txt">继续打印</view>
</view>
</view>
<view class="device_operation_warp">
<view class="device_operation_btn" @click="operationData('PAUSE')" :style="{
backgroundColor:
deviceDetail.status == 2 && deviceDetail.status != -1
? '#ffffff'
: '#cccccc',
}">
<image class="operation_btn_icon" src="https://online.totustec.com/upload/deviceManage/print_status1.png">
</image>
<view class="operation_btn_txt">暂停打印</view>
</view>
<view class="device_operation_btn" @click="operationData('EXIT')" :style="{
backgroundColor:
deviceDetail.status != 0 && deviceDetail.status != -1
? '#ffffff'
: '#cccccc',
}">
<image class="operation_btn_icon" src="https://online.totustec.com/upload/print_status1.png"></image>
<view class="operation_btn_txt">退出打印</view>
</view>
</view>
<view class="device_operation_warp">
<view class="device_operation_btn" @click="sendWsCommand('ALARM_CONTINUE')">
<image class="operation_btn_icon" src="https://online.totustec.com/upload/deviceManage/print_status2.png">
</image>
<view class="operation_btn_txt">报警继续</view>
</view>
<view class="device_operation_btn" @click="sendWsCommand('ALARM_EXIT')">
<image class="operation_btn_icon" src="https://online.totustec.com/upload/deviceManage/print_status2.png">
</image>
<view class="operation_btn_txt">报警退出</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data () {
return {
deviceName: "",
title: "Hello",
statusBarHeight: 0,
getType: "video",
deviceType: 3,
deviceCode: "", // 存储接收到的设备编码
deviceDetail: {}, // 存储设备详情数据
statusMap: {
0: { text: "正常", color: "#00d195" }, // 绿色
1: { text: "文件校验失败", color: "#ff0000" }, // 红色
2: { text: "设备或加密错误", color: "#ff0000" },
3: { text: "打印准备过程出错", color: "#ff0000" }, // 橙色
4: { text: "打印出错", color: "#ff0000" },
5: { text: "加液失败", color: "#ff9800" },
6: { text: "铲件失败", color: "#ff9800" },
7: { text: "图片异物", color: "#ff0000" },
},
// WebSocket 相关状态
socketTask: null, // uni-app 的 WebSocket 任务实例
isSocketConnected: false, // 连接状态
reconnectTimer: null, // 重连定时器
reconnectInterval: 5000, // 重连间隔5秒
socketData: {},
};
},
onLoad (options) {
const systemInfo = wx.getSystemInfoSync();
const {
statusBarHeight, // 状态栏高度单位px
} = systemInfo;
this.statusBarHeight = statusBarHeight;
this.deviceCode = options.deviceCode || "";
console.log("接收到的设备编码:", this.deviceCode);
// 立即根据设备编码获取详情数据
if (this.deviceCode) {
this.getDeviceDetail();
}
this.connectWebSocket(); // 初始化 WebSocket 连接
},
onUnload () {
this.closeWebSocket(); // 页面卸载时关闭连接
},
methods: {
// ---------------------- WebSocket 核心方法 ----------------------
connectWebSocket () {
const wsUrl = "ws://online.totustec.com/webSocket/vue";
// 初始化 WebSocket 连接(使用 uni-app 接口)
this.socketTask = uni.connectSocket({
url: wsUrl,
success: () => {
console.log("WebSocket 连接成功");
this.isSocketConnected = true;
this.listenSocketMessage(); // 监听消息
},
fail: (err) => {
console.error("连接失败:", err);
this.tryReconnect(); // 尝试重连
},
});
},
listenSocketMessage () {
// 监听 WebSocket 消息
uni.onSocketMessage((event) => {
console.log("event", event);
const message = JSON.parse(event.data);
this.getDeviceDetail();
this.updateDeviceStatus(message); // 更新设备状态
});
},
closeWebSocket () {
// 关闭连接并清理资源
if (this.socketTask) {
uni.closeSocket();
this.socketTask = null;
this.isSocketConnected = false;
}
clearTimeout(this.reconnectTimer);
},
tryReconnect () {
// 自动重连机制
clearTimeout(this.reconnectTimer);
this.reconnectTimer = setTimeout(() => {
console.log("尝试重连 WebSocket...");
this.connectWebSocket();
}, this.reconnectInterval);
},
updateDeviceStatus (message) {
console.log("实时消息", message);
// CLOSE、STATUS 你那就刷新接口就行
if (message.msgType == "CLOSE" || message.msgType == "STATUS") {
this.getDeviceDetail();
}
// ERROR 抛出异常信息
if (message.msgType == "ERROR") {
uni.showToast({ title: message.msg, icon: "none" });
}
// INFO 返回视频/图片地址
if (message.msgType == "INFO") {
this.socketData = message.data;
if (this.socketData.imageUrl) {
this.socketData.imageUrl =
"http://online.totustec.com" + message.data.imageUrl;
}
}
// WRITEIMG 接收图片数据中
if (message.msgType == "WRITEIMG") {
if (message.data.chunkIndex == message.data.totalChunks) {
// 微信图片小区域转圈圈加载
} else {
// 关闭加载
}
}
},
/**
* 发送 WebSocket 指令
* @param {string} command - 指令值(如 PAUSE、CONTINUE 等)
*/
sendWsCommand (command) {
if (!this.isSocketConnected) {
console.warn("WebSocket 未连接,无法发送指令");
return;
}
// 确保 deviceCode 存在(从 URL 参数获取)
if (!this.deviceCode) {
console.error("设备编号未获取,无法发送指令");
return;
}
const message = {
Topic: `sdcp/request/${this.deviceCode}`, // 替换 MainboardID 为 deviceCode
command: command,
};
if (command == "PRINT") {
message.filename = this.deviceName;
}
// 发送消息(使用 uni-app 的 sendSocketMessage
uni
.sendSocketMessage({
data: JSON.stringify(message),
})
.then(() => {
console.log("指令发送成功:", message);
})
.catch((err) => {
console.error("指令发送失败:", err);
});
},
operationData (type) {
if (type == "PRINT") {
if (this.deviceDetail.status == 0 && this.deviceDetail.status != -1) {
this.sendWsCommand(type);
}
}
if (type == "CONTINUE") {
if (
this.deviceDetail.status != 1 &&
this.deviceDetail.printStatus != 6 &&
this.deviceDetail.status != -1
) {
this.sendWsCommand(type);
}
}
if (type == 'PAUSE') {
if (this.deviceDetail.status == 2 && this.deviceDetail.status != -1) {
this.sendWsCommand(type);
}
}
if (type == 'EXIT') {
if (this.deviceDetail.status != 0 && this.deviceDetail.status != -1) {
this.sendWsCommand(type);
}
}
},
getVideoImg (type) {
if (type == "img") {
this.getType = "img";
this.sendWsCommand("IMAGE");
} else {
this.getType = "video";
this.sendWsCommand("OPEN_VIDEO");
}
},
// 获取状态文字
getStatusText (status) {
if (status == -1) {
return "断开";
}
return this.statusMap[status]?.text || "";
},
// 获取状态颜色
getStatusColor (status) {
if (status == -1) {
return "#999999";
}
return this.statusMap[status]?.color; // 默认灰色
},
getCurrentTime () {
const date = new Date();
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
return `${year}.${month}.${day} ${hours}:${minutes}`;
},
goBack () {
uni.navigateBack();
},
async getDeviceDetail () {
try {
const res = await uni.request({
url: `https://online.totustec.com/api/api/front/device/get`,
method: "GET",
data: {
deviceCode: this.deviceCode,
},
header: {
"Content-Type": "application/x-www-form-urlencoded", // 关键配置
Authorization: uni.getStorageSync("userInfo")?.token || "",
},
});
if (res.statusCode === 200) {
this.deviceDetail = res.data;
console.log("设备详情:", this.deviceDetail);
}
} catch (error) {
console.error("获取设备详情失败:", error);
uni.showToast({ title: "获取设备信息失败", icon: "none" });
}
},
},
};
</script>
<!-- 临时样式 -->
<style scoped>
.operation_btn_icon {
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
}
.operation_btn_txt {
font-weight: 600;
font-size: 28rpx;
color: #333333;
}
.device_operation_btn {
width: 320rpx;
height: 68rpx;
background: #ffffff;
border-radius: 20rpx;
display: flex;
justify-content: center;
align-items: center;
}
.device_operation_warp {
width: 100%;
display: flex;
justify-content: space-between;
margin-bottom: 24rpx;
}
.deviceN_input_tip {
margin: 24rpx 0 70rpx 0;
font-weight: 400;
font-size: 26rpx;
color: #666666;
width: 100%;
display: flex;
justify-content: center;
}
.device_control_warp {
width: 710rpx;
margin-top: 32rpx;
display: flex;
flex-direction: column;
}
.deviceN_input_warp {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.device_name_txt {
font-weight: 600;
font-size: 32rpx;
color: #333333;
}
.deviceN_input_area {
position: relative;
}
.clear_deviceN_warp {
position: absolute;
right: 16rpx;
top: 0;
height: 100%;
display: flex;
align-items: center;
z-index: 99;
}
.clear_deviceN_icon {
width: 26rpx;
height: 26rpx;
}
.deviceN_input {
width: 419rpx;
height: 74rpx;
background: #ffffff;
border-radius: 10rpx;
padding-left: 30rpx;
font-weight: 400;
font-size: 28rpx;
color: #666666;
}
.label_text {
margin-left: 8rpx;
}
.label_text2 {
color: #ff0000;
}
.label_warp {
display: flex;
align-items: center;
}
.label_warp2 {
display: flex;
align-items: center;
}
.label_icon {
width: 18rpx;
height: 18rpx;
background: #cccccc;
border-radius: 100%;
}
.item_content_model {
padding: 0 20rpx;
}
.item_content_line {
width: 100%;
height: 1rpx;
background-color: #ededed;
}
.itemContent_line_warp {
width: 100%;
position: absolute;
bottom: 0;
left: 0;
}
.item_content_middel {
font-family: PingFang SC, PingFang SC;
font-weight: 400;
font-size: 26rpx;
color: #666666;
display: flex;
align-items: center;
}
.item_content_left {
font-family: PingFang SC, PingFang SC;
font-weight: 600;
font-size: 28rpx;
color: #333333;
margin-left: 5rpx;
width: 222rpx;
}
.item_content_warp {
display: flex;
align-items: center;
}
.statusItem_modal_warp {
padding: 0 22rpx;
height: 100%;
display: flex;
justify-content: center;
flex-direction: column;
position: relative;
}
.device_status_item {
width: 100%;
height: 81rpx;
}
.device_status_warp {
width: 710rpx;
background: #ffffff;
margin-top: 23rpx;
display: flex;
flex-direction: column;
}
.modal_warp {
padding: 0 16rpx;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
.device_active {
background-color: #ffffff !important;
}
.select_device_item {
width: 329rpx;
height: 56rpx;
background: #e2edff;
border-radius: 60rpx;
font-family: PingFang SC, PingFang SC;
font-weight: 600;
font-size: 28rpx;
color: #333333;
display: flex;
justify-content: center;
align-items: center;
}
.select_device_warp {
width: 710rpx;
height: 68rpx;
background: #e2edff;
border-radius: 60rpx;
}
.content_warp {
padding: 16rpx 20rpx 0 20rpx;
display: flex;
flex-direction: column;
}
.content_area_warp {
width: 100%;
}
</style>
<style scoped>
.device_model {
width: 33%;
}
.img_video_warp {
width: 100%;
height: 281rpx;
background: rgba(0, 0, 0, 0.3);
border-radius: 20rpx;
margin: 22rpx 0 32rpx 0;
}
.back_button_icon {
transform: rotate(180deg);
width: 28rpx;
height: 28rpx;
}
.back_button_warp {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
padding-left: 20rpx;
}
.contai_warp {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
font-family: PingFang SC, PingFang SC !important;
min-height: 100vh;
background-color: #f1f6fc;
overflow-x: hidden !important;
}
.top_area_warp {
width: 100%;
display: flex;
flex-direction: column;
position: relative;
}
.top_status_area {
width: 100%;
}
.top_nav_warp {
width: 100%;
height: 88rpx;
display: flex;
justify-content: center;
align-items: center;
font-family: PingFang SC, PingFang SC;
font-weight: 500;
font-size: 32rpx;
color: #000000;
position: relative;
z-index: 5;
position: relative;
}
</style>