totustec-unipp/pages/deviceManage/index.vue
2025-07-15 16:37:09 +08:00

845 lines
21 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" v-for="(field, idx) in statusFields" :key="idx">
<view class="statusItem_modal_warp">
<view class="item_content_warp">
<view class="item_content_left">{{ field.label }}</view>
<view class="item_content_middel">
<!-- 状态特殊处理 -->
<template v-if="field.isStatus">
<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>
</template>
<!-- 子状态特殊处理 -->
<template v-else-if="field.isErrorStatus">
<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>
</template>
<!-- 错误码特殊处理 -->
<template v-else-if="field.isErrorCode">
{{ deviceDetail.errorStatus === -1 ? '断开' : deviceDetail.errorStatus }}
</template>
<!-- 当前时间特殊处理 -->
<template v-else-if="field.isCurrentTime">
{{ getCurrentTime() }}
</template>
<!-- 其他字段 -->
<template v-else>
{{ deviceDetail[field.key] }}
</template>
</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 == 1 || 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>
import {
BASE_STOKECT_URL
} from "@/config.js";
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"
},
},
// 新增:设备状态字段配置
statusFields: [{
label: '设备名称',
key: 'brand'
},
{
label: '状态',
key: 'status',
isStatus: true
},
{
label: '子状态',
key: 'errorStatus',
isErrorStatus: true
},
{
label: '当前打印层数',
key: 'currentLayer'
},
{
label: '打印任务总层数',
key: 'totalLayer'
},
{
label: '已打印时间(s)',
key: 'printedTime'
},
{
label: '总打印时间(s)',
key: 'totalTime'
},
{
label: '打印文件名称',
key: 'fileName'
},
{
label: '错误码',
key: 'errorStatus',
isErrorCode: true
},
{
label: '当前任务数量',
key: 'Task'
},
{
label: '总共任务数量',
key: 'totalTask'
},
{
label: '当前时间',
key: 'currentTime',
isCurrentTime: true
}
],
// 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 = BASE_STOKECT_URL;
// 初始化 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 == 1 || this.deviceDetail.status == 2) && this.deviceDetail.status != -
1) {
uni.showModal({
title: '提示',
content: '确定要暂停打印吗?',
confirmText: '确定',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
this.sendWsCommand(type);
}
}
});
}
}
if (type == 'EXIT') {
if (this.deviceDetail.status != 0 && this.deviceDetail.status != -1) {
uni.showModal({
title: '提示',
content: '确定要退出打印吗?',
confirmText: '确定',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
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>