Compare commits

..

10 Commits

Author SHA1 Message Date
d1b269570b 5.22 2025-05-22 15:36:59 +08:00
225dc12f77 4.24 2025-04-24 16:53:10 +08:00
de18827af6 3.26 2025-03-26 17:10:39 +08:00
e8da4a76a8 3.18 2025-03-18 17:32:30 +08:00
3c80f4a2f7 3.14 2025-03-14 09:37:40 +08:00
f43f1d57ed 3.13 2025-03-13 09:58:38 +08:00
b752d013bb 3.10 2025-03-10 08:40:55 +08:00
a96bd4e88d 3.6 2025-03-06 15:10:00 +08:00
0b4a8265c2 3,5 2025-03-05 11:41:06 +08:00
35f325a468 3.5 2025-03-05 11:40:40 +08:00
22 changed files with 4300 additions and 371 deletions

82
package-lock.json generated
View File

@ -11,6 +11,11 @@
"@capacitor/android": "^7.0.1",
"@capacitor/cli": "^7.0.1",
"@capacitor/core": "^7.0.1",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-brands-svg-icons": "^6.7.2",
"@fortawesome/free-regular-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@fortawesome/vue-fontawesome": "^3.0.8",
"@vant/touch-emulator": "^1.4.0",
"amfe-flexible": "^2.2.1",
"autoprefixer": "^10.4.19",
@ -18,6 +23,7 @@
"core-js": "^3.8.3",
"element-plus": "^2.9.4",
"express": "^4.21.2",
"font-awesome": "^4.7.0",
"lamejs": "^1.2.1",
"mqtt": "^2.18.8",
"nipplejs": "^0.10.2",
@ -1988,6 +1994,73 @@
"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.8.tgz",
"integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="
},
"node_modules/@fortawesome/fontawesome-common-types": {
"version": "6.7.2",
"resolved": "https://registry.npmmirror.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz",
"integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/fontawesome-svg-core": {
"version": "6.7.2",
"resolved": "https://registry.npmmirror.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz",
"integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==",
"license": "MIT",
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.7.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-brands-svg-icons": {
"version": "6.7.2",
"resolved": "https://registry.npmmirror.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.7.2.tgz",
"integrity": "sha512-zu0evbcRTgjKfrr77/2XX+bU+kuGfjm0LbajJHVIgBWNIDzrhpRxiCPNT8DW5AdmSsq7Mcf9D1bH0aSeSUSM+Q==",
"license": "(CC-BY-4.0 AND MIT)",
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.7.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-regular-svg-icons": {
"version": "6.7.2",
"resolved": "https://registry.npmmirror.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.7.2.tgz",
"integrity": "sha512-7Z/ur0gvCMW8G93dXIQOkQqHo2M5HLhYrRVC0//fakJXxcF1VmMPsxnG6Ee8qEylA8b8Q3peQXWMNZ62lYF28g==",
"license": "(CC-BY-4.0 AND MIT)",
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.7.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-solid-svg-icons": {
"version": "6.7.2",
"resolved": "https://registry.npmmirror.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz",
"integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==",
"license": "(CC-BY-4.0 AND MIT)",
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.7.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/vue-fontawesome": {
"version": "3.0.8",
"resolved": "https://registry.npmmirror.com/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.8.tgz",
"integrity": "sha512-yyHHAj4G8pQIDfaIsMvQpwKMboIZtcHTUvPqXjOHyldh1O1vZfH4W03VDPv5RvI9P6DLTzJQlmVgj9wCf7c2Fw==",
"license": "MIT",
"peerDependencies": {
"@fortawesome/fontawesome-svg-core": "~1 || ~6",
"vue": ">= 3.0.0 < 4"
}
},
"node_modules/@hapi/hoek": {
"version": "9.3.0",
"resolved": "https://registry.npmmirror.com/@hapi/hoek/-/hoek-9.3.0.tgz",
@ -7420,6 +7493,15 @@
}
}
},
"node_modules/font-awesome": {
"version": "4.7.0",
"resolved": "https://registry.npmmirror.com/font-awesome/-/font-awesome-4.7.0.tgz",
"integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==",
"license": "(OFL-1.1 AND MIT)",
"engines": {
"node": ">=0.10.3"
}
},
"node_modules/foreground-child": {
"version": "3.3.0",
"resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.0.tgz",

View File

@ -11,6 +11,11 @@
"@capacitor/android": "^7.0.1",
"@capacitor/cli": "^7.0.1",
"@capacitor/core": "^7.0.1",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-brands-svg-icons": "^6.7.2",
"@fortawesome/free-regular-svg-icons": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@fortawesome/vue-fontawesome": "^3.0.8",
"@vant/touch-emulator": "^1.4.0",
"amfe-flexible": "^2.2.1",
"autoprefixer": "^10.4.19",
@ -18,6 +23,7 @@
"core-js": "^3.8.3",
"element-plus": "^2.9.4",
"express": "^4.21.2",
"font-awesome": "^4.7.0",
"lamejs": "^1.2.1",
"mqtt": "^2.18.8",
"nipplejs": "^0.10.2",

View File

@ -1500,13 +1500,14 @@
screen_start = new Object()
screen_start.get_screen = 1;
var str = JSON.stringify(screen_start);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}";
console.log(str);
result_prompt_display(1, "获取屏幕参数中...");
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('post', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(str);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
@ -1527,13 +1528,14 @@
screen_start = new Object()
screen_start.get_sensor = 1;
var str = JSON.stringify(screen_start);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}";
console.log(str);
result_prompt_display(1, "获取传感器中...");
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('post', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(str);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
@ -1554,13 +1556,14 @@
screen_start = new Object()
screen_start.get_program = parseInt(pro_num);
var str = JSON.stringify(screen_start);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}";
console.log(str);
result_prompt_display(1, "获取屏幕参数中...");
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('post', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(str);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
@ -1589,11 +1592,12 @@
manage.pro_manage.type = type;
manage.pro_manage.data = data;
let string = JSON.stringify(manage);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}";
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('post', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(string);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
@ -1705,13 +1709,14 @@
}
}
let string = JSON.stringify(json_cfg);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}";
console.log(string);
result_prompt_display(1, "设置中...");
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('post', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(string);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {

View File

@ -6,7 +6,7 @@
* @LastEditors: Andy
* @LastEditTime: 2024-03-11 11:07:00
*/
var MQTT_MODE = 1;
var MQTT_MODE = 0;
function pageName() {
var a = location.href;

View File

@ -212,6 +212,8 @@
screen_start.get_cmd = new Object()
screen_start.get_cmd.type = _type
var str = JSON.stringify(screen_start);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}";
// 设置超时逻辑
var timeoutId = setTimeout(function () {
result_prompt_display(2, "数据获取失败");
@ -222,7 +224,7 @@
var xhr = new XMLHttpRequest();
xhr.open('POST', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(str);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
@ -292,13 +294,14 @@
screen_start.cmd.data2 = parseInt($('#power_on2').find('option:selected').val());
screen_start.cmd.data3 = 0;
var str = JSON.stringify(screen_start);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}";
console.log(str);
result_prompt_display(1, "发送中...");
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('POST', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(str);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
@ -332,13 +335,14 @@
screen_start.cmd.data2 = 0;
screen_start.cmd.data3 = 0;
var str = JSON.stringify(screen_start);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}";
console.log(str);
result_prompt_display(1, "发送中...");
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('POST', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(str);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
@ -395,13 +399,14 @@
screen_start.cmd.data2 = 0;
screen_start.cmd.data3 = 0;
var str = JSON.stringify(screen_start);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}";
console.log(str);
result_prompt_display(1, "发送中...");
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('POST', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(str);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
@ -429,13 +434,14 @@
screen_close.cmd.data2 = 0;
screen_close.cmd.data3 = 0;
var str = JSON.stringify(screen_close);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}";
console.log(str);
result_prompt_display(1, "发送中...");
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('POST', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(str);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
@ -467,13 +473,14 @@
screen_clear.cmd.data2 = 0;
screen_clear.cmd.data3 = 0;
var str = JSON.stringify(screen_clear);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}";
console.log(str);
result_prompt_display(1, "发送中...");
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('POST', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(str);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {

View File

@ -150,13 +150,14 @@
screen_start = new Object()
screen_start.get_screen = 1;
var str = JSON.stringify(screen_start);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}";
console.log(str);
result_prompt_display(1, "获取中...");
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('POST', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(str);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
@ -224,11 +225,12 @@
manage.pro_manage.type = 2;
manage.pro_manage.data = -1;
var str = JSON.stringify(manage);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}";
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('post', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(str);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
@ -271,13 +273,14 @@
screen.screen_p.data = parseInt($('#data_polarity').val())
screen.screen_p.screen_angle = screen_angle;
let str = JSON.stringify(screen);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + str + "}";
console.log(str);
result_prompt_display(1, "设置中...");
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('post', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(str);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {

View File

@ -257,11 +257,12 @@
manage.pro_manage.remarks = new Array();
manage.pro_manage.remarks[0] = cont;
let string = JSON.stringify(manage);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}";
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('post', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(string);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 || xhr.status == 200) {
program_list_reply_parse(xhr.responseText);
@ -295,11 +296,12 @@
manage = new Object();
manage.get_pro_manage = 1;
let string = JSON.stringify(manage);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}";
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('post', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(string);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
program_list_reply_parse(xhr.responseText);
@ -329,11 +331,12 @@
manage.pro_manage.data = data;
result_prompt_display(1, "删除中...");
let string = JSON.stringify(manage);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}";
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest()
xhr.open('post', baseHost + '/communication', true)
xhr.setRequestHeader('content-type', 'application/json')
xhr.send(string);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if ((xhr.readyState == 4) && (xhr.status == 200)) {
program_list_reply_parse(xhr.responseText);
@ -375,12 +378,13 @@
manage.pro_manage.type = type;
manage.pro_manage.data = data;
let string = JSON.stringify(manage);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}";
result_prompt_display(1, "发送中...");
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest()
xhr.open('post', baseHost + '/communication', true)
xhr.setRequestHeader('content-type', 'application/json')
xhr.send(string)
xhr.send(send_string)
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {

View File

@ -96,11 +96,12 @@
manage = new Object();
manage.get_pro_manage = 1;
let string = JSON.stringify(manage);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}";
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest();
xhr.open('post', baseHost + '/communication', true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.send(string);
xhr.send(send_string);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
screen_virtual_reply_parse(xhr.responseText);
@ -126,12 +127,13 @@
manage.pro_manage.type = 6;
manage.pro_manage.data = parseInt(data);
let string = JSON.stringify(manage);
let send_string = "{\"JSON_id\":" + json_id + ",\"board_id\":102"+",\"led_protocol\":" + string + "}";
result_prompt_display(1, "打开中");
if (MQTT_MODE == 0) {
var xhr = new XMLHttpRequest()
xhr.open('post', baseHost + '/communication', true)
xhr.setRequestHeader('content-type', 'application/json')
xhr.send(string)
xhr.send(send_string)
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {

212
src/api/ledScreen.js Normal file
View File

@ -0,0 +1,212 @@
import axios from 'axios';
// 基础配置
const baseURL = '/api'; // 根据实际情况配置API基础路径
const boardId = 1; // 默认设备ID
// 创建axios实例
const api = axios.create({
baseURL,
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
});
// 响应拦截器处理响应
api.interceptors.response.use(
response => {
// 检查错误码
if (response.data && response.data.error_code !== undefined) {
if (response.data.error_code !== 0) {
console.error('API错误:', response.data.error_code);
return Promise.reject(response.data);
}
}
return response.data;
},
error => {
console.error('网络错误:', error);
return Promise.reject(error);
}
);
/**
* LED双色屏控制API
*/
export const ledScreenApi = {
/**
* 平台上升控制
* @returns {Promise}
*/
platformUp() {
return api.post('/device/control', {
board_id: boardId,
JSON_id: Date.now(),
gateway: {
platform: {
action: "up"
}
}
});
},
/**
* 平台下降控制
* @returns {Promise}
*/
platformDown() {
return api.post('/device/control', {
board_id: boardId,
JSON_id: Date.now(),
gateway: {
platform: {
action: "down"
}
}
});
},
/**
* 屏幕开关控制
* @param {Boolean} status - true为开false为关
* @returns {Promise}
*/
screenToggle(status) {
return api.post('/device/control', {
board_id: boardId,
JSON_id: Date.now(),
gateway: {
screen: {
power: status ? "on" : "off"
}
}
});
},
/**
* 灯光模式切换
* @param {Number} mode - 灯光模式编号
* @returns {Promise}
*/
lightMode(mode) {
return api.post('/device/control', {
board_id: boardId,
JSON_id: Date.now(),
gateway: {
light: {
mode: mode
}
}
});
},
/**
* 单个灯控制
* @param {Number} lightId - 灯ID (1-6)
* @param {Boolean} status - true为开false为关
* @returns {Promise}
*/
controlSingleLight(lightId, status) {
return api.post('/device/control', {
board_id: boardId,
JSON_id: Date.now(),
gateway: {
light: {
id: lightId,
status: status ? "on" : "off"
}
}
});
},
/**
* 调节亮度
* @param {Number} brightness - 亮度值(0-100)
* @returns {Promise}
*/
setBrightness(brightness) {
return api.post('/device/control', {
board_id: boardId,
JSON_id: Date.now(),
gateway: {
screen: {
brightness: brightness
}
}
});
},
/**
* 切换节目
* @param {Number} programId - 节目ID
* @returns {Promise}
*/
switchProgram(programId) {
return api.post('/device/control', {
board_id: boardId,
JSON_id: Date.now(),
gateway: {
program: {
id: programId
}
}
});
},
/**
* 设备登录验证
* @param {String} password - 登录密码
* @returns {Promise}
*/
login(password) {
return api.post('/device/auth', {
board_id: boardId,
JSON_id: Date.now(),
gateway: {
log_in: {
type: 2,
pass: password
}
}
});
},
/**
* 修改设备密码
* @param {String} oldPassword - 旧密码
* @param {String} newPassword - 新密码
* @returns {Promise}
*/
changePassword(oldPassword, newPassword) {
return api.post('/device/auth', {
board_id: boardId,
JSON_id: Date.now(),
gateway: {
log_in: {
type: 2,
pass: oldPassword,
new_pass: newPassword
}
}
});
},
/**
* 获取设备状态
* @returns {Promise}
*/
getDeviceStatus() {
return api.post('/device/status', {
board_id: boardId,
JSON_id: Date.now(),
gateway: {
status: {
query: "all"
}
}
});
}
};
export default ledScreenApi;

View File

@ -0,0 +1,281 @@
/**
* LED双色屏JSON通信格式定义
* WEB/串口 => 设备为请求格式
* 设备 => WEB/串口为响应格式
*/
// ======================== 平台上升 ========================
const platformUpRequest = {
"board_id": 1,
"JSON_id": 1,
"gateway": {
"platform": {
"action": "up"
}
}
};
const platformUpResponse = {
"JSON_id": 1,
"gateway": {
"platform": {
"return": 0
}
},
"error_code": 0
};
// ======================== 平台下降 ========================
const platformDownRequest = {
"board_id": 1,
"JSON_id": 1,
"gateway": {
"platform": {
"action": "down"
}
}
};
const platformDownResponse = {
"JSON_id": 1,
"gateway": {
"platform": {
"return": 0
}
},
"error_code": 0
};
// ======================== 屏幕开关 ========================
const screenOnRequest = {
"board_id": 1,
"JSON_id": 1,
"gateway": {
"screen": {
"power": "on"
}
}
};
const screenOnResponse = {
"JSON_id": 1,
"gateway": {
"screen": {
"return": 0
}
},
"error_code": 0
};
const screenOffRequest = {
"board_id": 1,
"JSON_id": 1,
"gateway": {
"screen": {
"power": "off"
}
}
};
const screenOffResponse = {
"JSON_id": 1,
"gateway": {
"screen": {
"return": 0
}
},
"error_code": 0
};
// ======================== 灯光模式 ========================
const lightModeRequest = {
"board_id": 1,
"JSON_id": 1,
"gateway": {
"light": {
"mode": 1 // 模式编号1-n
}
}
};
const lightModeResponse = {
"JSON_id": 1,
"gateway": {
"light": {
"return": 0
}
},
"error_code": 0
};
// ======================== 单个灯控制 ========================
// 这里以灯1开启为例其他灯位可以修改id值(1-6)
const light1OnRequest = {
"board_id": 1,
"JSON_id": 1,
"gateway": {
"light": {
"id": 1,
"status": "on"
}
}
};
const light1OnResponse = {
"JSON_id": 1,
"gateway": {
"light": {
"return": 0
}
},
"error_code": 0
};
const light1OffRequest = {
"board_id": 1,
"JSON_id": 1,
"gateway": {
"light": {
"id": 1,
"status": "off"
}
}
};
const light1OffResponse = {
"JSON_id": 1,
"gateway": {
"light": {
"return": 0
}
},
"error_code": 0
};
// ======================== 亮度调节 ========================
const setBrightnessRequest = {
"board_id": 1,
"JSON_id": 1,
"gateway": {
"screen": {
"brightness": 80 // 亮度值0-100
}
}
};
const setBrightnessResponse = {
"JSON_id": 1,
"gateway": {
"screen": {
"return": 0
}
},
"error_code": 0
};
// ======================== 节目切换 ========================
const switchProgramRequest = {
"board_id": 1,
"JSON_id": 1,
"gateway": {
"program": {
"id": 2 // 节目编号
}
}
};
const switchProgramResponse = {
"JSON_id": 1,
"gateway": {
"program": {
"return": 0
}
},
"error_code": 0
};
// ======================== 设备登录 ========================
const loginRequest = {
"board_id": 1,
"JSON_id": 1,
"gateway": {
"log_in": {
"type": 2,
"pass": "#输入密码#"
}
}
};
const loginResponse = {
"JSON_id": 1,
"gateway": {
"log_in": {
"return": 0
}
},
"error_code": 0
};
// ======================== 修改设备密码 ========================
const changePasswordRequest = {
"board_id": 1,
"JSON_id": 1,
"gateway": {
"log_in": {
"type": 2,
"pass": "#输入密码#",
"new_pass": "#新密码#"
}
}
};
const changePasswordResponse = {
"JSON_id": 1,
"gateway": {
"log_in": {
"return": 0
}
},
"error_code": 0
};
// ======================== 获取设备状态 ========================
const getStatusRequest = {
"board_id": 1,
"JSON_id": 1,
"gateway": {
"status": {
"query": "all"
}
}
};
const getStatusResponse = {
"JSON_id": 1,
"gateway": {
"status": {
"platform": "up", // up/down
"screen": {
"power": "on", // on/off
"brightness": 80
},
"light": {
"mode": 1,
"lights": [
{"id": 1, "status": "on"},
{"id": 2, "status": "off"},
{"id": 3, "status": "on"},
{"id": 4, "status": "off"},
{"id": 5, "status": "on"},
{"id": 6, "status": "off"}
]
},
"program": {
"current": 2,
"total": 5
}
}
},
"error_code": 0
};

View File

@ -1,151 +1,151 @@
class GamepadController {
constructor(options = {}) {
this.options = {
this.options = {
...{
debug: false,
deadZone: 0.1,
updateInterval: 50,
buttonsConfig: [
{ name: "Left2", index: 6 },
{ name: "Back", index: 8 },
{ name: "Right Joystick Press", index: 11 }
]
updateInterval: 50
},
...options
...options
};
this.gamepadIndex = null;
this.gamepad = null;
this.interval = null;
this.buttons = [];
this.directionAxis0_1 = "";
this.directionAxis9 = "";
this.gamepadIndex = null;
this.gamepad = null;
this.interval = null;
this.angle = 0;
this.speed = 0;
this.directionAxis9 = 0;
this.rightJoystickPressed = false;
this.speed = 0; // 添加速度属性
// 初始化按钮状态
this.buttons = this.options.buttonsConfig.map(button => ({
...button,
pressed: false
}));
// 注册事件监听器
window.addEventListener("gamepadconnected", this.onGamepadConnected.bind(this));
window.addEventListener("gamepaddisconnected", this.onGamepadDisconnected.bind(this));
if (this.options.debug) {
console.log("GamepadController initialized with options:", this.options);
}
this.isActive = false;
// 立即开始更新循环,不等待连接事件
this.startUpdateLoop();
}
onGamepadConnected(e) {
console.log("Gamepad connected:", e.gamepad);
this.gamepadIndex = e.gamepad.index;
this.gamepad = navigator.getGamepads()[this.gamepadIndex];
this.startGamepad();
}
onGamepadDisconnected() {
clearInterval(this.interval);
this.gamepad = null;
if (this.options.debug) {
console.log("Gamepad disconnected");
}
}
startGamepad() {
this.interval = setInterval(() => {
const gamepads = navigator.getGamepads();
const gamepad = gamepads[this.gamepadIndex];
if (gamepad) {
// 注释掉调试打印
// if (this.options.debug) {
// console.log('Axes data:', {
// axis0: gamepad.axes[0],
// axis1: gamepad.axes[1],
// gamepadIndex: this.gamepadIndex
// });
// }
this.updateDirection(gamepad.axes);
this.updateDirectionAxis9(gamepad.axes);
this.pressKey(gamepad.buttons);
}
}, this.options.updateInterval);
}
updateDirection(axes) {
const axis0 = axes[0];
const axis1 = axes[1];
// 检查是否在死区
if (Math.abs(axis0) < this.options.deadZone && Math.abs(axis1) < this.options.deadZone) {
this.directionAxis0_1 = "未定义";
this.angle = 0;
this.speed = 0; // 在死区时速度为0
update() {
// 获取所有手柄
const gamepads = navigator.getGamepads();
if (!gamepads) {
this.isActive = false;
return;
}
// 计算速度(到原点的距离)
// 使用勾股定理计算距离并将结果限制在0-100之间
const distance = Math.sqrt(axis0 * axis0 + axis1 * axis1);
this.speed = Math.round(Math.min(distance * 100, 100));
// 计算方向角度0-360度
let angle = Math.atan2(axis1, axis0) * (180 / Math.PI);
angle = (angle + 360) % 360;
angle = Math.round(angle);
this.angle = angle;
// 找到第一个连接的手柄
const gamepad = Array.from(gamepads).find(pad => pad && pad.connected);
if (!gamepad) {
this.isActive = false;
return;
}
this.isActive = true;
// 更新方向数据
if (Math.abs(axis0) > this.options.deadZone || Math.abs(axis1) > this.options.deadZone) {
this.directionAxis0_1 = `${angle}°`;
// 注释掉调试打印
// if (this.options.debug) {
// console.log(` 摇杆方向: ${angle}°, X轴: ${axis0.toFixed(2)}, Y轴: ${axis1.toFixed(2)}`);
// }
// 获取左摇杆的值
const x = gamepad.axes[0];
const y = gamepad.axes[1];
// 应用死区
const absX = Math.abs(x);
const absY = Math.abs(y);
if (absX < this.options.deadZone && absY < this.options.deadZone) {
this.angle = 0;
this.speed = 0;
} else {
this.updateDirection(gamepad.axes);
}
// 更新 platform 值
this.updateDirectionAxis9(gamepad.axes);
// 更新右摇杆按钮状态 - 尝试多个可能的按钮索引
this.rightJoystickPressed =
(gamepad.buttons[10] && gamepad.buttons[10].pressed) ||
(gamepad.buttons[9] && gamepad.buttons[9].pressed) ||
(gamepad.buttons[11] && gamepad.buttons[11].pressed);
if (this.options.debug) {
console.log('GamepadController 状态:', {
id: gamepad.id,
index: gamepad.index,
x: x.toFixed(2),
y: y.toFixed(2),
angle: this.angle,
speed: this.speed.toFixed(2),
directionAxis9: this.directionAxis9,
rightJoystickPressed: this.rightJoystickPressed,
buttons: Array.from(gamepad.buttons).map((btn, idx) => btn.pressed ? idx : null).filter(idx => idx !== null)
});
}
}
updateDirection(axes) {
const axis0 = axes[0]; // X轴
const axis1 = axes[1]; // Y轴
// 检查是否在死区
if (Math.abs(axis0) < this.options.deadZone && Math.abs(axis1) < this.options.deadZone) {
this.angle = 0;
this.speed = 0;
return;
}
// 计算角度0-360度从x轴正半轴开始顺时针旋转
let angle = Math.atan2(axis1, axis0) * (180 / Math.PI);
// 将角度转换到 0-360 范围
if (angle < 0) {
angle += 360;
}
this.angle = Math.round(angle);
// 计算速度0-100
const distance = Math.sqrt(axis0 * axis0 + axis1 * axis1);
this.speed = Math.min(Math.round(distance * 100), 100);
}
updateDirectionAxis9(axes) {
const axis9 = axes[9];
const roundedAxis9 = Math.round(axis9 * 100) / 100;
if (roundedAxis9 <= -0.9) {
this.directionAxis9 = 0;
} else if (roundedAxis9 >= 0.0 && roundedAxis9 <= 0.2) {
this.directionAxis9 = 1;
// } else if (roundedAxis9 >= 0.6 && roundedAxis9 <= 0.8) {
// this.directionAxis9 = "左";
// } else if (roundedAxis9 >= -0.5 && roundedAxis9 <= -0.4) {
// this.directionAxis9 = "右";
}
else {
this.directionAxis9 = 0;
// 添加调试输出,查看所有轴的值
if (this.options.debug) {
// console.log('所有轴的值:', axes.map((value, index) => `轴${index}: ${value}`));
}
}
pressKey(buttons) {
this.buttons.forEach(button => {
const buttonData = buttons[button.index];
button.pressed = buttonData ? buttonData.value === 1 : false;
// 检查第9轴是否存在
if (axes[9] === undefined) {
// 可能是第3轴或第4轴
const axis3 = axes[3];
const axis4 = axes[4];
// 特别检查 Right Joystick Press 按钮
if (button.name === "Right Joystick Press") {
this.rightJoystickPressed = button.pressed;
if (axis3 !== undefined && Math.abs(axis3) > this.options.deadZone) {
this.directionAxis9 = axis3 > 0 ? 2 : 1;
} else if (axis4 !== undefined && Math.abs(axis4) > this.options.deadZone) {
this.directionAxis9 = axis4 > 0 ? 2 : 1;
} else {
this.directionAxis9 = 0;
}
});
}
destroy() {
clearInterval(this.interval);
window.removeEventListener("gamepadconnected", this.onGamepadConnected);
window.removeEventListener("gamepaddisconnected", this.onGamepadDisconnected);
if (this.options.debug) {
console.log("GamepadController destroyed");
} else {
const axis9 = axes[9];
const roundedAxis9 = Math.round(axis9 * 100) / 100;
if (roundedAxis9 <= -0.9) {
this.directionAxis9 = 1; // 向上
} else if (roundedAxis9 >= 0.0 && roundedAxis9 <= 0.2) {
this.directionAxis9 = 2; // 向下
} else {
this.directionAxis9 = 0; // 中间位置
}
}
if (this.options.debug) {
// console.log('当前 directionAxis9 值:', this.directionAxis9);
}
}
}
// 导出类以便外部使用
export default GamepadController;
startUpdateLoop() {
this.updateLoop = setInterval(() => this.update(), this.options.updateInterval);
}
destroy() {
if (this.updateLoop) {
clearInterval(this.updateLoop);
}
}
}export default GamepadController;

View File

@ -1,163 +1,204 @@
// audio-transmitter.js
export default {
data() {
return {
ws: null, // WebSocket 连接
mediaRecorder: null, // MediaRecorder 实例
audioChunks: [], // 音频数据
status: '点击按钮开始传输音频',
isRecording: false, // 用于判断是否在录音
audioConfig: {
sampleRate: 44100, // 固定为 44100Hz
bitsPerSample: 16, // 固定为 16 位深度
channels: 2 // 固定为双声道
},
stream: null,
audioContext: null,
worklet: null,
lastSendTime: 0,
sendInterval: 200, // 数据传输间隔(毫秒)
audioWs: null, // 音频 WebSocket 连接
};
},
mounted() {
// 初始化 WebSocket 连接
this.ws = new WebSocket('ws://192.168.4.103/ws'); // 替换为 ESP32 IP 地址
this.ws.onopen = () => {
console.log('WebSocket 连接已打开');
};
this.ws.onmessage = (event) => {
console.log(' 接收到来自 ESP32 的消息:', event.data);
};
this.ws.onclose = () => {
console.log('WebSocket 连接已关闭');
};
},
methods: {
toggleRecording() {
if (this.isRecording) {
this.stopRecording();
} else {
this.startRecording();
}
data() {
return {
ws: null, // WebSocket 连接
mediaRecorder: null, // MediaRecorder 实例
audioChunks: [], // 音频数据
status: '点击按钮开始传输音频',
isRecording: false, // 用于判断是否在录音
audioConfig: {
sampleRate: 44100, // 固定为 44100Hz
bitsPerSample: 16, // 固定为 16 位深度
channels: 2 // 固定为双声道
},
async startRecording() {
console.log('音频传输器: 开始录音');
this.status = '开始录音并传输音频...';
this.isRecording = true;
const constraints = {
audio: {
sampleRate: this.audioConfig.sampleRate,
channelCount: this.audioConfig.channels,
sampleSize: this.audioConfig.bitsPerSample
stream: null,
audioContext: null,
worklet: null,
lastSendTime: 0,
sendInterval: 200, // 数据传输间隔(毫秒)
audioWs: null, // 音频 WebSocket 连接
};
},
beforeDestroy() {
// 组件销毁前清理资源
this.cleanup();
// 关闭主 WebSocket 连接
if (this.ws) {
console.log('关闭主 WebSocket 连接');
this.ws.close();
this.ws = null;
}
},
methods: {
toggleRecording() {
if (this.isRecording) {
this.stopRecording();
} else {
this.startRecording();
}
},
async startRecording() {
if (this.isRecording) return;
console.log('音频传输器: 开始录音');
this.status = '开始录音并传输音频...';
this.isRecording = true;
const constraints = {
audio: {
sampleRate: this.audioConfig.sampleRate,
channelCount: this.audioConfig.channels,
sampleSize: this.audioConfig.bitsPerSample
}
};
console.log('使用的音频配置:', constraints);
try {
// 直接获取麦克风权限,不再重新建立 WebSocket 连接
const stream = await navigator.mediaDevices.getUserMedia(constraints);
console.log('成功获取麦克风权限');
const audioContext = new AudioContext({
sampleRate: this.audioConfig.sampleRate
});
await audioContext.audioWorklet.addModule('audio-processor.js');
const source = audioContext.createMediaStreamSource(stream);
const worklet = new AudioWorkletNode(audioContext, 'audio-processor');
worklet.port.onmessage = (event) => {
if (this.audioWs && this.audioWs.readyState === WebSocket.OPEN) {
const currentTime = Date.now();
if (!this.lastSendTime || currentTime - this.lastSendTime > this.sendInterval) {
const pcmData = new Int16Array(event.data);
console.log('发送的音频数据:', Array.from(pcmData.slice(0, 20)));
this.audioWs.send(event.data);
this.lastSendTime = currentTime;
}
}
};
console.log('使用的音频配置:', constraints);
try {
// 初始化音频 WebSocket 连接
this.audioWs = new WebSocket('ws://192.168.1.60:81');
// 等待 WebSocket 连接成功
await new Promise((resolve, reject) => {
this.audioWs.onopen = () => {
console.log('音频 WebSocket 已连接');
resolve();
};
this.audioWs.onerror = (error) => {
console.error('音频 WebSocket 错误:', error);
reject(error);
};
});
// WebSocket 连接成功后,开始获取麦克风权限
const stream = await navigator.mediaDevices.getUserMedia(constraints);
console.log('成功获取麦克风权限');
// 创建 AudioContext
const audioContext = new AudioContext({
sampleRate: this.audioConfig.sampleRate
});
// 加载 AudioWorklet
await audioContext.audioWorklet.addModule('audio-processor.js');
const source = audioContext.createMediaStreamSource(stream);
const worklet = new AudioWorkletNode(audioContext, 'audio-processor');
// 监听来自 worklet 的消息
worklet.port.onmessage = (event) => {
if (this.audioWs && this.audioWs.readyState === WebSocket.OPEN) {
const currentTime = Date.now();
if (!this.lastSendTime || currentTime - this.lastSendTime > this.sendInterval) {
const pcmData = new Int16Array(event.data);
console.log('发送的音频数据:', Array.from(pcmData.slice(0, 20)));
this.audioWs.send(event.data);
this.lastSendTime = currentTime;
}
}
};
source.connect(worklet);
worklet.connect(audioContext.destination);
// 保存引用以便后续停止
this.stream = stream;
this.audioContext = audioContext;
this.worklet = worklet;
} catch (error) {
console.error('音频初始化失败:', error);
// 清理音频相关资源
if (this.stream) {
this.stream.getTracks().forEach(track => track.stop());
}
if (this.worklet) {
this.worklet.disconnect();
}
if (this.audioContext) {
this.audioContext.close();
}
if (this.audioWs) {
this.audioWs.close();
this.audioWs = null;
}
this.isRecording = false;
// 不抛出错误,让程序继续执行
return;
}
},
stopRecording() {
console.log('音频传输器: 停止录音');
this.status = '停止录音';
this.isRecording = false;
// 关闭音频资源
source.connect(worklet);
worklet.connect(audioContext.destination);
this.stream = stream;
this.audioContext = audioContext;
this.worklet = worklet;
} catch (error) {
console.error('音频初始化失败:', error);
this.cleanup();
return;
}
},
// stopRecording() {
// console.log('音频传输器: 停止录音');
// this.status = '停止录音';
// this.isRecording = false;
// // 关闭音频资源
// if (this.stream) {
// console.log('关闭音频流');
// this.stream.getTracks().forEach(track => track.stop());
// }
// if (this.worklet) {
// console.log('断开 AudioWorklet');
// this.worklet.disconnect();
// }
// if (this.audioContext) {
// console.log('关闭音频上下文');
// this.audioContext.close();
// }
// // 关闭音频 WebSocket 连接
// if (this.audioWs) {
// console.log('关闭音频 WebSocket 连接');
// this.audioWs.close();
// this.audioWs = null;
// }
// }
// 修改停止录音方法
stopRecording() {
console.log('音频传输器: 停止录音');
this.status = '停止录音';
this.isRecording = false;
// 异步关闭防止状态冲突
const cleanup = async () => {
// 优先关闭音频流
if (this.stream) {
console.log('关闭音频流');
this.stream.getTracks().forEach(track => track.stop());
this.stream = null;
}
// 断开音频处理节点
if (this.worklet) {
console.log('断开 AudioWorklet');
this.worklet.disconnect();
this.worklet = null;
}
if (this.audioContext) {
// 安全关闭音频上下文
if (this.audioContext && this.audioContext.state !== 'closed') {
console.log('关闭音频上下文');
this.audioContext.close();
}
// 关闭音频 WebSocket 连接
if (this.audioWs) {
console.log('关闭音频 WebSocket 连接');
this.audioWs.close();
this.audioWs = null;
await this.audioContext.close();
this.audioContext = null;
}
// 移除关闭 WebSocket 的代码
// if (this.audioWs) {
// console.log('关闭音频 WebSocket 连接');
// this.audioWs.close();
// this.audioWs = null;
// }
};
cleanup().catch(error => {
console.error('清理过程中发生错误:', error);
});
},
async cleanup() {
console.log('开始清理音频资源...');
// 停止录音
if (this.isRecording) {
this.isRecording = false;
}
// 优先关闭音频流
if (this.stream) {
console.log('关闭音频流');
this.stream.getTracks().forEach(track => track.stop());
this.stream = null;
}
// 断开音频处理节点
if (this.worklet) {
console.log('断开 AudioWorklet');
this.worklet.disconnect();
this.worklet = null;
}
// 安全关闭音频上下文
if (this.audioContext && this.audioContext.state !== 'closed') {
console.log('关闭音频上下文');
await this.audioContext.close();
this.audioContext = null;
}
// 关闭音频 WebSocket 连接
// if (this.audioWs) {
// console.log('关闭音频 WebSocket 连接');
// this.audioWs.close();
// this.audioWs = null;
// }
}
};
}
};

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,7 @@
<style scoped>
.custom-nav-bar {
height: 90px; /* 你可以根据需要调整高度 */
height: 60px; /* 减小高度从90px到60px */
position: fixed;
top: 0;
left: 0;
@ -26,16 +26,16 @@
z-index: 999; /* 使 Header 在最上层 */
}
/deep/ .van-nav-bar {
:deep(.van-nav-bar) {
height: 100%; /* 确保van-nav-bar占满父元素的高度 */
background-color: #0668fc; /* 示例背景色,可以根据需要调整 */
}
/deep/ .van-nav-bar__title {
font-size: 22px; /* 设置标题字体大小 */
:deep(.van-nav-bar__title) {
font-size: 20px; /* 减小标题字体大小 */
font-weight: bold; /* 设置标题字体加粗 */
color: #fff; /* 设置标题字体颜色 */
line-height: 100%;
margin-top: 40px;
margin-top: 20px; /* 减小上边距从40px到20px */
}
</style>

View File

@ -62,6 +62,21 @@ const routes = [
name: 'TEST',
component: () => import('../views/TEST.vue'),
},
{
path: '/gateway-setting',
name: 'GatewaySetting',
component: () => import('../views/gateway/GatewaySetting.vue')
},
{
path: '/cone-control',
name: 'ConeControl',
component: () => import('../views/ConeControl.vue')
},
{
path: '/flipScreen',
name: 'flipScreen',
component: () => import('../views/flipScreen/flipScreen.vue')
},
];
const router = createRouter({

View File

@ -2,17 +2,23 @@
<div class="control-panel">
<!-- 顶部状态栏 -->
<div class="status-bar">
<div class="back-button" @click="handleBack">
</div>
<div class="placeholder-div"></div>
<div class="title-container">
<div class="title-box">
遥控车当前状态
<!-- 根据方向状态显示不同文本 -->
{{ getDirectionText }}
<img src="../assets/img/shout.png" alt="voice" class="voice-icon-img"
:class="{ 'show-voice': isControlPressed }" style="width: 20px; height: 20px; margin-left: 20px;">
</div>
</div>
<div class="top-right-icons">
<img :src="connectionImage" alt="link" class="top-icon">
<img :src="batteryImage" alt="battery" class="top-icon">
<img src="../assets/img/bat_full.png" class="top-icon" alt="">
<!-- 注释掉电池图标 -->
<!-- <img :src="batteryImage" alt="battery" class="top-icon"> -->
</div>
</div>
@ -22,25 +28,25 @@
<div class="left-controls">
<div class="switch-item">
<span>屏幕</span>
<el-switch v-model="screenStatus" />
<el-switch v-model="screenStatus" @change="handleScreenChange" />
</div>
<div class="switch-item">
<span>警灯</span>
<el-switch v-model="warningLightStatus" />
<el-switch v-model="warningLightStatus" @change="handleWarningLightChange" />
</div>
<div class="switch-item">
<span>跟随</span>
<el-switch v-model="followStatus" />
<el-switch v-model="followStatus" @change="handleFollowChange" />
</div>
<div class="switch-item">
<span>避障</span>
<el-switch v-model="obstacleAvoidEnabled" />
<el-switch v-model="obstacleAvoidEnabled" @change="handleObstacleAvoidChange" />
</div>
</div>
<!-- 中间车辆状态显示区域 -->
<div class="center-display">
<div class="status-text">云台状态</div>
<div class="status-text">{{ getptz }}</div>
<div class="car-display">
<div class="boom-container">
<img src="../assets/img/boom.png" alt="boom" class="boom-image top-boom" v-show="obstacleStatus.top">
@ -66,10 +72,11 @@
</div>
<div class="control-button">
<div class="circle-border" :class="{ 'pressed': isControlPressed }" @mousedown="handleControlPress"
@mouseup="handleControlRelease" @mouseleave="handleControlRelease" @touchstart="handleControlPress"
@touchend="handleControlRelease" @touchcancel="handleControlRelease">
@mouseup="handleControlRelease" @mouseleave="handleControlRelease" @touchstart.prevent="handleControlPress"
@touchend.prevent="handleControlRelease" @touchcancel.prevent="handleControlRelease" @contextmenu.prevent
@copy.prevent @selectstart.prevent style="user-select: none;">
<img src="../assets/img/stop.png" alt="control" class="control-button-img"
style="width: 80px; height: 80px;">
style="width: 80px; height: 80px; pointer-events: none;" draggable="false">
</div>
</div>
</div>
@ -101,7 +108,7 @@ export default {
isControlPressed: false,
ws: null,
wsConnected: false,
batteryLevel: 0,
// batteryLevel: 0, //
lastResponseTime: 0,
connectionCheckInterval: null,
sendInterval: null,
@ -111,13 +118,19 @@ export default {
button: "",
audioConfig: audioTransmitter.data(),
isRecording: false,
audioHandler: null
audioHandler: null,
reconnectTimer: null, //
isComponentUnmounted: false, //
isInitialized: false, //
contenttext: "",
_mousePressed: false, // /
contentptz: "",
}
},
created() {
// GamepadController
this.gamepadController = new GamepadController({
debug: true,
debug: false, //
deadZone: 0.1,
updateInterval: 50
});
@ -126,41 +139,50 @@ export default {
window.addEventListener("gamepadconnected", this.handleGamepadConnected);
window.addEventListener("gamepaddisconnected", this.handleGamepadDisconnected);
this.isComponentUnmounted = false;
this.initWebSocket();
//
this.$nextTick(() => {
this.sendCarInfo();
});
//
this.connectionCheckInterval = setInterval(() => {
this.checkConnectionStatus();
}, 3000); // 3
}, 3000);
//
this.sendInterval = setInterval(() => {
this.sendControlData();
}, 1000); // 100ms
}, 400);
//
this.audioHandler = {
...audioTransmitter.data(),
...audioTransmitter.methods
};
// WebSocket
this.audioHandler.audioWs = new WebSocket('ws://192.168.4.103/ws');
this.audioHandler.audioWs.onopen = () => {
console.log('音频 WebSocket 已连接');
};
this.audioHandler.audioWs.onerror = (error) => {
console.error('音频 WebSocket 错误:', error);
};
this.audioHandler.audioWs.onclose = () => {
console.log('音频 WebSocket 已关闭');
};
},
beforeDestroy() {
//
if (this.gamepadController) {
this.gamepadController.destroy();
}
//
window.removeEventListener("gamepadconnected", this.handleGamepadConnected);
window.removeEventListener("gamepaddisconnected", this.handleGamepadDisconnected);
this.isComponentUnmounted = true; //
if (this.ws) {
this.ws.close();
//
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
//
if (this.connectionCheckInterval) {
clearInterval(this.connectionCheckInterval);
}
@ -169,10 +191,34 @@ export default {
clearInterval(this.sendInterval);
}
//
window.removeEventListener("gamepadconnected", this.handleGamepadConnected);
window.removeEventListener("gamepaddisconnected", this.handleGamepadDisconnected);
//
if (this.gamepadController) {
this.gamepadController.destroy();
}
//
if (this.audioHandler && this.audioHandler.isRecording) {
this.audioHandler.stopRecording();
}
// WebSocket
if (this.audioHandler && this.audioHandler.audioWs) {
console.log('关闭音频 WebSocket 连接');
this.audioHandler.audioWs.close();
this.audioHandler.audioWs = null;
}
// WebSocket
if (this.ws) {
console.log('关闭控制 WebSocket 连接');
this.ws.close();
this.ws = null;
this.wsConnected = false;
}
},
methods: {
goto(path) {
@ -196,17 +242,19 @@ export default {
}
},
handleControlPress() {
console.log('按钮被按下,设置 isControlPressed = true');
this._mousePressed = true;
this.isControlPressed = true;
//
console.log('开始音频传输...');
if (this.audioHandler) {
this.audioHandler.startRecording();
}
},
handleControlRelease() {
console.log('按钮被释放,设置 isControlPressed = false');
this._mousePressed = false;
this.isControlPressed = false;
//
console.log('停止音频传输...');
if (this.audioHandler) {
this.audioHandler.stopRecording();
}
@ -215,6 +263,7 @@ export default {
try {
console.log('正在连接 WebSocket...');
this.ws = new WebSocket('ws://192.168.4.120/ws');
// this.ws = new WebSocket('ws://192.168.1.120/ws');
// this.ws = new WebSocket('ws://192.168.1.60:81');
@ -238,14 +287,14 @@ export default {
}
};
this.ws.onclose = (event) => {
console.log('WebSocket 已断开, code:', event.code, 'reason:', event.reason);
this.wsConnected = false;
//
setTimeout(() => {
console.log('尝试重新连接...');
this.initWebSocket();
}, 3000);
this.ws.onclose = () => {
console.log('WebSocket连接关闭');
//
if (!this.isComponentUnmounted) {
this.reconnectTimer = setTimeout(() => {
this.initWebSocket();
}, 3000);
}
};
this.ws.onerror = (error) => {
@ -267,37 +316,53 @@ export default {
}
},
//
// updateGamepadData
updateGamepadData() {
if (this.gamepadController) {
//
const angle = this.gamepadController.angle;
const directionAxis9 = this.gamepadController.directionAxis9;
const speed = this.gamepadController.speed; //
const speed = this.gamepadController.speed;
//
const wasPressed = this.isControlPressed;
this.isControlPressed = this.gamepadController.rightJoystickPressed;
// 0-4
let direction = 0; //
if (speed > 10) {
if (angle >= 315 || angle < 45) {
direction = 4; //
} else if (angle >= 45 && angle < 135) {
direction = 2; // 退
} else if (angle >= 135 && angle < 225) {
direction = 3; //
} else if (angle >= 225 && angle < 315) {
direction = 1; //
}
}
//
if (!wasPressed && this.isControlPressed) {
//
console.log('手柄按钮按下,开始音频传输...');
// /
const gamepadButtonPressed = this.gamepadController.rightJoystickPressed;
//
if (gamepadButtonPressed && !this.isControlPressed) {
console.log('手柄按钮被按下,开始录音');
this.isControlPressed = true;
if (this.audioHandler) {
this.audioHandler.startRecording();
}
} else if (wasPressed && !this.isControlPressed) {
//
console.log('手柄按钮释放,停止音频传输...');
}
//
else if (!gamepadButtonPressed && this.isControlPressed && !this._mousePressed) {
console.log('手柄按钮被释放,停止录音');
this.isControlPressed = false;
if (this.audioHandler) {
this.audioHandler.stopRecording();
}
}
//
//
this.contenttext = direction;
this.lastDirection = angle;
this.platform_fun = directionAxis9;
this.lastSpeed = speed; //
if (directionAxis9 !== undefined) this.platform_fun = directionAxis9;
if (speed !== undefined) this.lastSpeed = speed;
}
},
sendCarInfo() {
@ -323,27 +388,29 @@ export default {
this.sendCarInfo();
},
//
updateCarStatus(data) {
if (data.attribute) {
//
this.batteryLevel = data.attribute.bat_quantity;
//
this.obstacleStatus = {
top: data.attribute.obstacle_sta === 0,
bottom: data.attribute.obstacle_sta === 1
top: data.attribute.obstacle_sta === 1,
bottom: data.attribute.obstacle_sta === 2
};
//
this.platform_fun = data.attribute.platform;
//
// if (!this.isInitialized) {
this.screenStatus = data.attribute.screen_en === 1;
this.warningLightStatus = data.attribute.warn_light_en === 1;
this.followStatus = data.attribute.follow_en === 1;
this.obstacleAvoidEnabled = data.attribute.obstacle_avoid_en === 1;
this.contentptz = data.attribute.platform;
// this.isInitialized = true;
// }
}
//
this.lastSpeed = data.attribute.car_speed;
this.lastDirection = data.attribute.car_direction;
//
if (data.driver && typeof data.driver.direction !== 'undefined') {
this.lastDirection = data.driver.direction; // 使
}
},
@ -355,9 +422,33 @@ export default {
}
},
//
handleScreenChange(value) {
this.screenStatus = value;
this.sendControlData();
},
handleWarningLightChange(value) {
this.warningLightStatus = value;
this.sendControlData();
},
handleFollowChange(value) {
this.followStatus = value;
this.sendControlData();
},
handleObstacleAvoidChange(value) {
this.obstacleAvoidEnabled = value;
this.sendControlData();
},
// sendControlData
sendControlData() {
//
this.updateGamepadData();
//
if (this.gamepadController && this.gamepadController.isActive) {
this.updateGamepadData();
}
const controlData = {
"JSON_id": 1,
@ -380,21 +471,122 @@ export default {
}
},
handleBack() {
//
this.isComponentUnmounted = true;
//
if (this.connectionCheckInterval) {
clearInterval(this.connectionCheckInterval);
this.connectionCheckInterval = null;
}
if (this.sendInterval) {
clearInterval(this.sendInterval);
this.sendInterval = null;
}
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
//
if (this.audioHandler && this.audioHandler.isRecording) {
this.audioHandler.stopRecording();
}
//
window.removeEventListener("gamepadconnected", this.handleGamepadConnected);
window.removeEventListener("gamepaddisconnected", this.handleGamepadDisconnected);
//
if (this.gamepadController) {
this.gamepadController.destroy();
this.gamepadController = null;
}
// WebSocket
const closePromises = [];
if (this.ws) {
console.log('断开控制 WebSocket 连接');
this.ws.onclose = null; //
this.ws.close();
this.ws = null;
this.wsConnected = false;
}
if (this.audioHandler && this.audioHandler.audioWs) {
console.log('断开音频 WebSocket 连接');
this.audioHandler.audioWs.onclose = null; //
this.audioHandler.audioWs.close();
this.audioHandler.audioWs = null;
}
//
setTimeout(() => {
//
this.$router.push('/');
}, 100);
},
},
computed: {
//
/*
batteryImage() {
// 使 require
if (this.batteryLevel >= 80) return require('../assets/img/bat_full.png');
if (this.batteryLevel >= 60) return require('../assets/img/bat_high.png');
if (this.batteryLevel >= 40) return require('../assets/img/bat_medium.png');
if (this.batteryLevel >= 20) return require('../assets/img/bat_low.png');
return require('../assets/img/bat_empty.png');
},
*/
connectionImage() {
return this.wsConnected ?
require('../assets/img/connect.png') :
require('../assets/img/no_connect.png');
},
getDirectionText() {
// direction
switch (this.contenttext) {
case 0:
return '遥控车已停止';
case 1:
return '遥控车正在前进';
case 2:
return '遥控车正在后退';
case 3:
return '遥控车正在左转';
case 4:
return '遥控车正在右转';
default:
return '遥控车当前状态';
}
},
getptz() {
switch (this.contentptz) {
case 0:
return '云台静止';
case 1:
return '云台上升';
case 2:
return '云台下降';
default:
return '云台静止';
}
}
},
setup() {
const router = useRouter();
//
const onClickLeft = () => {
router.back();
};
return {
onClickLeft,
};
}
}
</script>
@ -441,6 +633,11 @@ export default {
border: 2px solid #00ffff;
padding: 2px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
min-width: 180px;
/* 确保文本变化时盒子大小稳定 */
}
.main-content {
@ -589,6 +786,12 @@ export default {
align-items: center;
transition: border-color 0.3s ease;
cursor: pointer;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.circle-border.pressed {
@ -610,4 +813,28 @@ export default {
.voice-icon-img.show-voice {
opacity: 1;
}
/* 确保标题栏在最上层 */
:deep(.van-nav-bar) {
z-index: 999;
}
.back-button {
cursor: pointer;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: background-color 0.3s;
font-size: 24px;
/* 调整箭头大小 */
color: #00ffff;
/* 使用青色,与其他元素保持一致 */
}
.back-button:hover {
background-color: rgba(255, 255, 255, 0.1);
}
</style>

736
src/views/ConeControl.vue Normal file
View File

@ -0,0 +1,736 @@
<template>
<div class="cone-control">
<van-nav-bar title="路锥控制" left-arrow @click-left="onClickLeft" fixed />
<!-- 添加下拉刷新组件 -->
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<!-- 为模组概况添加边框 -->
<div class="module-container">
<div class="module-header">
<van-icon name="setting-o" size="18" />
<span>模组概况</span>
</div>
<van-cell-group inset>
<van-cell title="模组概况">
<template #label>
<div class="module-info">
<div class="info-row">
<span class="info-label">固件版本</span>
<span class="info-value" ref="firmwareVersionEl">{{ firmwareVersion }}</span>
</div>
<div class="info-row">
<span class="info-label">MAC地址</span>
<span class="info-value" ref="macAddressEl">{{ macAddress }}</span>
</div>
<div class="info-title">网络参数</div>
<div class="info-row">
<span class="info-label">网络ID</span>
<van-field v-model="networkId" readonly input-align="right" />
</div>
<div class="info-row">
<span class="info-label">无线频段</span>
<van-field v-model="freqBand" readonly input-align="right" />
</div>
<div class="info-row">
<span class="info-label">信道</span>
<van-field v-model="channel" readonly input-align="right" />
</div>
<div class="info-row">
<span class="info-label">速率(SF)</span>
<van-field v-model="speedRate" readonly input-align="right" />
</div>
<div class="info-row">
<span class="info-label">功率</span>
<van-field v-model="power" readonly input-align="right" />
</div>
<div class="mode-container">
<div class="info-row mode-row">
<span class="info-label">网络通道</span>
<van-field v-model="currentMode" is-link readonly input-align="right" placeholder="请选择网络通道"
@click="showModePopup" class="mode-field" />
</div>
</div>
<!-- <div class="upload-button-container">
<van-button type="primary" size="small" block @click="uploadLoRaConfig">上传配置</van-button>
</div> -->
</div>
</template>
</van-cell>
</van-cell-group>
</div>
<!-- 状态和控制面板 -->
<div class="status-container">
<div class="module-header">
<van-icon name="info-o" size="18" />
<span>状态与控制</span>
</div>
<van-cell-group inset>
<!-- 报警状态只读 -->
<van-cell title="报警状态">
<template #value>
<span :style="{ color: alarmStatus === '正常' ? '#07c160' : '#ee0a24' }">{{ alarmStatus }}</span>
</template>
<template #right-icon>
<van-button v-if="alarmStatus !== '正常'" size="small" type="danger"
@click="sendCommand('clearAlarm')">清除报警</van-button>
</template>
</van-cell>
<!-- 灯光状态选择 -->
<van-cell title="灯光状态" :value="lightStatus" is-link @click="showLightStatusPicker" />
<!-- 布防开关 -->
<van-cell center title="布防开关">
<template #right-icon>
<van-switch v-model="defenseEnabled" size="24" @change="onDefenseChange" />
</template>
</van-cell>
</van-cell-group>
</div>
<!-- 灯光状态选择器 -->
<van-popup v-model:show="lightStatusPickerVisible" position="bottom">
<van-picker :columns="lightStatusOptions" @confirm="onLightStatusConfirm"
@cancel="lightStatusPickerVisible = false" show-toolbar />
</van-popup>
<!-- 添加模式选择器 -->
<van-popup v-model:show="modePickerVisible" position="bottom">
<van-picker :columns="modeOptions" @confirm="onModeConfirm" @cancel="modePickerVisible = false" show-toolbar />
</van-popup>
</van-pull-refresh>
</div>
</template>
<script>
import { ref, reactive, onMounted, onBeforeUnmount, computed } from 'vue';
import { useRouter } from 'vue-router';
// import { showToast } from 'vant';
import axios from 'axios';
export default {
name: 'ConeControl',
setup() {
const router = useRouter();
const networkId = ref('');
const freqBand = ref('');
const channel = ref('');
const speedRate = ref('');
const power = ref('');
const firmwareVersionEl = ref(null);
const macAddressEl = ref(null);
const jsonId = ref(1);
const isLocal = ref(true);
const freqBandPickerVisible = ref(false);
const channelPickerVisible = ref(false);
const speedRatePickerVisible = ref(false);
const powerPickerVisible = ref(false);
const alarmStatus = ref('报警');
const lightStatus = ref('白灯亮');
const defenseEnabled = ref(false);
const lightStatusPickerVisible = ref(false);
const refreshing = ref(false);
const firmwareVersion = ref('');
const macAddress = ref('');
const modePickerVisible = ref(false);
const currentMode = ref('');
//
const freqBandOptions = [
{ text: '433MHz(430MHz-446MHz)', value: 0 },
{ text: '470MHz(454MHz-469MHz)', value: 1 },
{ text: '480MHz(470MHz-486MHz)', value: 2 },
{ text: '500MHz(494MHz-509MHz)', value: 3 }
];
const channelOptions = Array.from({ length: 32 }, (_, i) => ({
text: `信道${i + 1}`,
value: i
}));
const speedRateOptions = [
{ text: '62.5kbps', value: 0 },
{ text: '37.5kbps', value: 1 },
{ text: '21.8kbps', value: 2 },
{ text: '12.5kbps', value: 3 },
{ text: '7.0kbps', value: 4 },
{ text: '3.9kbps', value: 5 },
{ text: '2.1kbps', value: 6 }
];
const powerOptions = [
{ text: '22dbm', value: 0 },
{ text: '20dbm', value: 1 },
{ text: '17dbm', value: 2 },
{ text: '14dbm', value: 3 },
{ text: '11dbm', value: 4 },
{ text: '8dbm', value: 5 },
{ text: '5dbm', value: 6 },
{ text: '2dbm', value: 7 }
];
//
const lightStatusOptions = [
{ text: '白光亮(高)', value: 0 },
{ text: '白光亮(中)', value: 1 },
{ text: '白光亮(低)', value: 2 },
{ text: '红蓝快闪', value: 3 },
{ text: '红蓝慢闪', value: 4 },
{ text: '投射灯亮', value: 5 },
{ text: '关闭', value: 6 }
];
const modeOptions = [
{ text: '通道一', value: 0, networkId: '6A6A00' },
{ text: '通道二', value: 1, networkId: '6A6A01' },
{ text: '通道三', value: 2, networkId: '6A6A02' },
{ text: '通道四', value: 3, networkId: '6A6A03' }
];
const onClickLeft = () => {
router.back();
};
const showFreqBandPopup = () => {
freqBandPickerVisible.value = true;
};
const showChannelPopup = () => {
channelPickerVisible.value = true;
};
const showSpeedRatePopup = () => {
speedRatePickerVisible.value = true;
};
const showPowerPopup = () => {
powerPickerVisible.value = true;
};
const showLightStatusPicker = () => {
lightStatusPickerVisible.value = true;
};
const showModePopup = () => {
modePickerVisible.value = true;
};
const onFreqBandConfirm = (value) => {
freqBand.value = value.selectedOptions[0].text;
freqBandPickerVisible.value = false;
};
const onChannelConfirm = (value) => {
channel.value = value.selectedOptions[0].text;
channelPickerVisible.value = false;
};
const onSpeedRateConfirm = (value) => {
speedRate.value = value.selectedOptions[0].text;
speedRatePickerVisible.value = false;
};
const onPowerConfirm = (value) => {
power.value = value.selectedOptions[0].text;
powerPickerVisible.value = false;
};
const onLightStatusConfirm = (value) => {
lightStatus.value = value.selectedOptions[0].text;
lightStatusPickerVisible.value = false;
sendLightStatusCommand(value.selectedOptions[0].value);
};
const sendLightStatusCommand = (value) => {
const lightStatusCommand = {
board_id: 50,
JSON_id: 1,
cones: {
traffic_cone: {
lamplight: value
}
},
error_code: 0
};
if (isLocal.value) {
axios.post('/communication', lightStatusCommand, {
headers: {
"content-type": "application/json"
}
})
.then(response => {
console.log('Light status response:', response.data);
})
.catch(error => {
console.error('Light status error:', error);
});
} else {
MQTT_send(lightStatusCommand);
}
};
const onDefenseChange = (value) => {
const defenseCommand = {
board_id: 50,
JSON_id: 1,
cones: {
traffic_cone: {
mode: value ? 1 : 0
}
},
error_code: 0
};
if (isLocal.value) {
axios.post('/communication', defenseCommand, {
headers: {
"content-type": "application/json"
}
})
.then(response => {
console.log('Defense mode response:', response.data);
})
.catch(error => {
console.error('Defense mode error:', error);
});
} else {
MQTT_send(defenseCommand);
}
};
const checkEnvironment = () => {
if (window.location.hostname === '192.168.4.1') {
console.log('in local');
// isLocal true
isLocal.value = true;
} else {
// isLocal false
isLocal.value = false;
}
};
const MQTT_send = (send_string) => {
showToast('设置成功');
jsonId.value++;
console.log("MQTT 发送:" + JSON.stringify(send_string));
if (/xazn/.test(navigator.userAgent) || /uni-app/.test(navigator.userAgent)) {
uni.postMessage({
data: {
str: JSON.stringify(send_string)
}
});
} else {
window.parent.postMessage({ str: JSON.stringify(send_string) }, "*");
}
};
const uploadLoRaConfig = () => {
//
if (!networkId.value || !freqBand.value || !channel.value || !speedRate.value || !power.value) {
showToast('请填写所有必填字段');
return;
}
//
const meshId = networkId.value.match(/.{2}/g)?.map(hex => parseInt(hex, 10)) || [0, 0, 0];
//
const freqBandValue = freqBandOptions.find(option => option.text === freqBand.value)?.value || 0;
const channelValue = parseInt(channel.value.replace('信道', '')) - 1;
const speedRateValue = speedRateOptions.find(option => option.text === speedRate.value)?.value || 0;
const powerValue = powerOptions.find(option => option.text === power.value)?.value || 0;
const configData = {
"board_id": 50,
"JSON_id": jsonId.value,
"gateway": {
"LoRa_cfg": {
"mesh_id": meshId,
"fre_band": freqBandValue,
"channel": channelValue,
"SF": speedRateValue,
"power": powerValue,
"mode": 0
}
}
};
console.log('Uploading config:', configData);
if (isLocal.value) {
axios.post('/communication', configData, {
headers: {
"content-type": "application/json"
}
})
.then(response => {
console.log('Upload response:', response.data);
showToast('上传成功');
jsonId.value++;
})
.catch(error => {
console.error('Upload error:', error);
showToast('上传失败');
jsonId.value++;
});
} else {
MQTT_send(configData);
}
};
const sendCommand = (command) => {
if (command === 'clearAlarm') {
const clearAlarmCommand = {
board_id: 50,
JSON_id: 1,
cones: {
traffic_cone: {
alarm: 0
}
},
error_code: 0
};
if (isLocal.value) {
axios.post('/communication', clearAlarmCommand, {
headers: {
"content-type": "application/json"
}
})
.then(response => {
console.log('Clear alarm response:', response.data);
alarmStatus.value = '正常';
})
.catch(error => {
console.error('Clear alarm error:', error);
});
} else {
MQTT_send(clearAlarmCommand);
}
}
};
const onModeConfirm = (value) => {
currentMode.value = value.selectedOptions[0].text;
networkId.value = value.selectedOptions[0].networkId;
modePickerVisible.value = false;
//
const configData = {
board_id: 50,
JSON_id: 1,
cones: {
LoRa_cfg: {
mesh_id: [
parseInt(networkId.value.slice(0, 2), 16),
parseInt(networkId.value.slice(2, 4), 16),
parseInt(networkId.value.slice(4, 6), 16)
],
fre_band: freqBandOptions.find(option => option.text === freqBand.value)?.value || 0,
channel: channel.value ? (parseInt(channel.value.match(/\d+/)?.[0] || '1') - 1) : 0,
SF: speedRateOptions.find(option => option.text === speedRate.value)?.value || 0,
power: powerOptions.find(option => option.text === power.value)?.value || 0,
mode: value.selectedOptions[0].value
}
},
error_code: 0
};
if (isLocal.value) {
axios.post('/communication', configData, {
headers: {
"content-type": "application/json"
}
})
.then(response => {
console.log('Mode update response:', response.data);
})
.catch(error => {
console.error('Mode update error:', error);
});
} else {
MQTT_send(configData);
}
};
const updateStateFromResponse = (data) => {
//
if (!data || !data.cones) {
console.error('Invalid response data structure');
return;
}
// LoRa
if (data.cones.LoRa_cfg) {
const loraConfig = data.cones.LoRa_cfg;
firmwareVersion.value = loraConfig.versions;
macAddress.value = loraConfig.mac_addr.join(':');
networkId.value = loraConfig.mesh_id.map(num => num.toString(16).padStart(2, '0').toUpperCase()).join('');
freqBand.value = freqBandOptions.find(option => option.value === loraConfig.fre_band)?.text || '';
channel.value = `信道${loraConfig.channel + 1}`;
speedRate.value = speedRateOptions.find(option => option.value === loraConfig.SF)?.text || '';
power.value = powerOptions.find(option => option.value === loraConfig.power)?.text || '';
//
const mode = modeOptions.find(option => option.networkId === networkId.value);
if (mode) {
currentMode.value = mode.text;
}
}
//
if (data.cones.traffic_cone) {
const trafficCone = data.cones.traffic_cone;
//
alarmStatus.value = trafficCone.alarm === 1 ? '报警' : '正常';
//
const lightOption = lightStatusOptions.find(option => option.value === trafficCone.lamplight);
if (lightOption) {
lightStatus.value = lightOption.text;
}
//
defenseEnabled.value = trafficCone.mode === 1;
}
};
const onRefresh = () => {
const refreshCommand = {
board_id: 50,
JSON_id: 1,
cones: {
get_traffic_cone: 1,
get_LoRa_cfg: 1
},
error_code: 0
};
if (isLocal.value) {
axios.post('/communication', refreshCommand, {
headers: {
"content-type": "application/json"
}
})
.then(response => {
console.log('Refresh response:', response.data);
//
updateStateFromResponse(response.data);
refreshing.value = false;
})
.catch(error => {
console.error('Refresh error:', error);
refreshing.value = false;
});
} else {
MQTT_send(refreshCommand);
refreshing.value = false;
}
};
onMounted(() => {
checkEnvironment();
onRefresh();
});
return {
networkId,
freqBand,
channel,
speedRate,
power,
firmwareVersionEl,
macAddressEl,
onClickLeft,
showFreqBandPopup,
showChannelPopup,
showSpeedRatePopup,
showPowerPopup,
uploadLoRaConfig,
sendCommand,
freqBandPickerVisible,
channelPickerVisible,
speedRatePickerVisible,
powerPickerVisible,
onFreqBandConfirm,
onChannelConfirm,
onSpeedRateConfirm,
onPowerConfirm,
freqBandOptions,
channelOptions,
speedRateOptions,
powerOptions,
alarmStatus,
lightStatus,
defenseEnabled,
lightStatusPickerVisible,
lightStatusOptions,
showLightStatusPicker,
onLightStatusConfirm,
onDefenseChange,
refreshing,
onRefresh,
firmwareVersion,
macAddress,
modePickerVisible,
currentMode,
modeOptions,
showModePopup,
onModeConfirm,
};
}
};
</script>
<style scoped>
.cone-control {
padding-top: 46px;
/* 为固定的导航栏留出空间 */
background-color: #f7f8fa;
min-height: 100vh;
}
.module-container {
margin: 16px;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 2px 12px rgba(100, 101, 102, 0.08);
overflow: hidden;
}
.module-header {
padding: 16px;
font-size: 16px;
font-weight: 500;
color: #323233;
border-bottom: 1px solid #f5f5f5;
display: flex;
align-items: center;
gap: 8px;
}
.status-container {
margin: 16px;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 2px 12px rgba(100, 101, 102, 0.08);
overflow: hidden;
}
.info-row {
display: flex;
align-items: center;
margin-bottom: 12px;
justify-content: space-between;
}
.info-label {
min-width: 80px;
color: #646566;
flex-shrink: 0;
}
.info-row :deep(.van-field) {
flex: 1;
padding: 0;
background: transparent;
margin-left: 8px;
}
.upload-button-container {
margin-top: 16px;
padding: 0 8px;
}
/* 添加过渡动画 */
.command-item {
position: relative;
overflow: hidden;
}
.command-item::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(25, 137, 250, 0.1);
border-radius: 8px;
opacity: 0;
transition: opacity 0.3s;
}
.command-item:active::after {
opacity: 1;
}
/* 为清除报警按钮添加样式 */
:deep(.van-button--danger) {
margin-left: 8px;
}
.mode-container {
margin: 12px -16px;
padding: 12px 16px;
background-color: #f2f7ff;
/* 浅蓝色背景 */
border-left: 4px solid #1989fa;
/* 左边添加蓝色边框 */
}
.mode-row {
margin-bottom: 0;
/* 覆盖原来的 margin */
}
.mode-field {
font-weight: 500;
/* 字体加粗 */
color: #1989fa;
/* 使用主题蓝色 */
}
.mode-field :deep(.van-field__value) {
color: #1989fa;
}
.mode-field :deep(.van-field__placeholder) {
color: #1989fa;
opacity: 0.7;
}
/* 让其他网络参数稍微淡一点 */
.info-row:not(.mode-row) {
opacity: 0.85;
}
.car-status {
font-weight: bold;
}
.status-0 {
color: #909399;
/* 灰色 - 停止 */
}
.status-1 {
color: #67C23A;
/* 绿色 - 前进 */
}
.status-2 {
color: #E6A23C;
/* 橙色 - 后退 */
}
.status-3 {
color: #409EFF;
/* 蓝色 - 左转 */
}
.status-4 {
color: #409EFF;
/* 蓝色 - 右转 */
}
</style>

View File

@ -0,0 +1,716 @@
<template>
<div class="page-container">
<Header :title="currentTitle"></Header>
<div class="content">
<!-- Top Section - 4 buttons in 2 rows -->
<div class="control-section">
<div class="button-grid top-grid">
<div class="control-btn" @click="handlePlatformControl('up')">
<div class="btn-icon">
<van-icon name="arrow-up" />
</div>
<div class="btn-label">平台上升</div>
</div>
<div class="control-btn" @click="handlePlatformControl('down')">
<div class="btn-icon">
<van-icon name="arrow-down" />
</div>
<div class="btn-label">平台下降</div>
</div>
<div class="control-btn" @click="handleScreenControl">
<div class="btn-icon">
<van-icon name="tv-o" />
</div>
<div class="btn-label">屏幕开关</div>
</div>
<div class="control-btn" @click="showLaserModeDialog">
<div class="btn-icon">
<van-icon name="bulb-o" />
</div>
<div class="btn-label">激光模式</div>
</div>
</div>
</div>
<!-- Middle Section - 6 buttons in 2 rows -->
<div class="control-section">
<div class="button-grid middle-grid">
<div class="control-btn"
@click="handleLightControl(1)"
:class="{ 'active-light': lightStates[0] }">
<div class="btn-icon">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-deng"></use>
</svg>
</div>
<div class="btn-label">灯1</div>
</div>
<div class="control-btn"
@click="handleLightControl(2)"
:class="{ 'active-light': lightStates[1] }">
<div class="btn-icon">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-deng"></use>
</svg>
</div>
<div class="btn-label">灯2</div>
</div>
<div class="control-btn"
@click="handleLightControl(3)"
:class="{ 'active-light': lightStates[2] }">
<div class="btn-icon">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-deng"></use>
</svg>
</div>
<div class="btn-label">灯3</div>
</div>
<div class="control-btn"
@click="handleLightControl(4)"
:class="{ 'active-light': lightStates[3] }">
<div class="btn-icon">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-deng"></use>
</svg>
</div>
<div class="btn-label">灯4</div>
</div>
<div class="control-btn"
@click="handleLightControl(5)"
:class="{ 'active-light': lightStates[4] }">
<div class="btn-icon">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-deng"></use>
</svg>
</div>
<div class="btn-label">灯5</div>
</div>
<div class="control-btn"
@click="handleLightControl(6)"
:class="{ 'active-light': lightStates[5] }">
<div class="btn-icon">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-deng"></use>
</svg>
</div>
<div class="btn-label">灯6</div>
</div>
</div>
</div>
<!-- Bottom Section - 2 buttons in 1 row -->
<div class="control-section">
<div class="button-row bottom-row">
<div class="action-btn reset-btn" @click="showBrightnessDialog">
<div class="btn-icon">
<van-icon name="setting-o" />
</div>
<div class="btn-label">亮度</div>
</div>
<div class="action-btn stop-btn" @click="showProgramDialog">
<div class="btn-icon">
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-jiemu"></use>
</svg>
</div>
<div class="btn-label">节目</div>
</div>
</div>
</div>
</div>
<Footer></Footer>
<!-- 激光模式选择弹窗 -->
<van-popup v-model:show="showLaserMode" position="center" round>
<div class="popup-content">
<div class="popup-title">选择激光模式</div>
<div class="mode-options">
<div class="mode-option"
v-for="mode in laserModes"
:key="mode.value"
:class="{ 'selected': lightMode === mode.value }"
@click="selectLaserMode(mode.value)">
{{ mode.label }}
</div>
</div>
</div>
</van-popup>
<!-- 亮度调节弹窗 -->
<van-popup v-model:show="showBrightness" position="center" round class="brightness-popup">
<div class="popup-content">
<div class="popup-title">调节亮度</div>
<div class="brightness-slider">
<van-slider v-model="brightness" :min="0" :max="100" @change="handleBrightnessChange" />
<div class="brightness-value">{{ brightness }}%</div>
</div>
<!-- <div class="popup-buttons">
<van-button type="primary" block @click="showBrightness = false">确定</van-button>
</div> -->
</div>
</van-popup>
<!-- 节目选择弹窗 -->
<van-popup v-model:show="showProgram" position="center" round>
<div class="popup-content">
<div class="popup-title">选择节目</div>
<div class="program-options">
<div class="program-option"
v-for="id in availablePrograms"
:key="id"
:class="{ 'selected': currentProgram === id }"
@click="selectProgram(id)">
节目 {{ id }}
</div>
</div>
</div>
</van-popup>
</div>
</template>
<script>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import Header from "../../components/Header.vue";
import Footer from "../../components/Footer.vue";
import { iconfont } from '../../assets/js/iconfont.js';
import axios from 'axios';
export default {
components: {
Header,
Footer
},
setup() {
const currentTitle = ref('翻转屏遥控');
const isLocal = ref(false);
const jsonId = ref(1);
const loading = ref(false);
const screenPower = ref('off');
const lightMode = ref('auto');
const lightStates = ref([true, false, false, false, false, false]);
const brightness = ref(70);
const currentProgram = ref(1);
const availablePrograms = ref([1, 2, 3, 4, 5]);
//
const showLaserMode = ref(false);
const showBrightness = ref(false);
const showProgram = ref(false);
//
const laserModes = [
{ label: '自动模式', value: 'auto' },
{ label: '手动模式', value: 'manual' },
{ label: '关闭模式', value: 'off' }
];
//
const checkEnvironment = () => {
if (window.location.hostname === '192.168.4.1') {
console.log('in local');
// isLocal true
isLocal.value = true;
} else {
// isLocal false
isLocal.value = false;
}
};
//
const MQTT_send = (send_string) => {
console.log("MQTT 发送:" + JSON.stringify(send_string));
jsonId.value++;
if (/xazn/.test(navigator.userAgent) || /uni-app/.test(navigator.userAgent)) {
uni.postMessage({
data: {
str: JSON.stringify(send_string)
}
});
} else {
window.parent.postMessage({ str: JSON.stringify(send_string) }, "*");
}
showToast('发送成功');
};
const MQTT_recv = (string) => {
loading.value = false;
console.log("MQTT 接收的json:" + string);
const data = JSON.parse(string);
parseAndFillData(data);
//
if (data.error_code === 0) {
showToast('操作成功');
} else {
showToast('操作失败: ' + (data.error_msg || '未知错误'));
}
console.log('Received message:', string);
};
//
const parseAndFillData = (data) => {
if (data.Flip_screen && data.Flip_screen.status) {
const status = data.Flip_screen.status;
//
if (status.platform) {
//
}
//
if (status.screen) {
screenPower.value = status.screen.power;
brightness.value = status.screen.brightness;
}
//
if (status.lights) {
lightStates.value = status.lights.map(light => light.state === 'on');
}
//
if (status.program) {
currentProgram.value = status.program.current_id;
if (status.program.available) {
availablePrograms.value = status.program.available;
}
}
//
if (status.light_mode) {
lightMode.value = status.light_mode;
}
}
};
//
const sendMessage = async (message) => {
loading.value = true;
if (isLocal.value) {
// 使 axios
try {
const response = await axios.post('/api/flip-screen', message);
console.log('本地通信响应:', response.data);
parseAndFillData(response.data);
//
if (response.data.error_code === 0) {
showToast('操作成功');
} else {
showToast('操作失败: ' + (response.data.error_msg || '未知错误'));
}
} catch (error) {
console.error('本地通信错误:', error);
showToast('通信错误: ' + error.message);
} finally {
loading.value = false;
}
} else {
// 使 MQTT
MQTT_send(message);
// MQTT MQTT_recv
}
};
//
const handlePlatformControl = (direction) => {
const message = {
board_id: 121,
JSON_id: jsonId.value,
Flip_screen: {
platform_control: {
direction: direction
}
}
};
sendMessage(message);
};
//
const handleScreenControl = () => {
const newPower = screenPower.value === 'on' ? 'off' : 'on';
const message = {
board_id: 121,
JSON_id: jsonId.value,
Flip_screen: {
screen_control: {
power: newPower
}
}
};
sendMessage(message);
};
//
const showLaserModeDialog = () => {
showLaserMode.value = true;
};
//
const selectLaserMode = (mode) => {
lightMode.value = mode;
showLaserMode.value = false;
const message = {
board_id: 121,
JSON_id: jsonId.value,
Flip_screen: {
light_mode: {
mode: mode
}
}
};
sendMessage(message);
};
//
const handleLightControl = (lightId) => {
const currentState = lightStates.value[lightId - 1];
const newState = currentState ? 'off' : 'on';
const message = {
board_id: 121,
JSON_id: jsonId.value,
Flip_screen: {
light_control: {
light_id: lightId,
state: newState
}
}
};
sendMessage(message);
};
//
const showBrightnessDialog = () => {
showBrightness.value = true;
};
//
const handleBrightnessChange = (value) => {
brightness.value = value;
const message = {
board_id: 121,
JSON_id: jsonId.value,
Flip_screen: {
brightness_control: {
value: value
}
}
};
sendMessage(message);
};
//
const showProgramDialog = () => {
showProgram.value = true;
};
//
const selectProgram = (programId) => {
currentProgram.value = programId;
showProgram.value = false;
const message = {
board_id: 121,
JSON_id: jsonId.value,
Flip_screen: {
program_control: {
program_id: programId
}
}
};
sendMessage(message);
};
//
const onRefresh = () => {
const message = {
board_id: 121,
JSON_id: jsonId.value,
Flip_screen: {
status_query: {
type: "all"
}
}
};
sendMessage(message);
};
onMounted(async () => {
await checkEnvironment();
console.log(isLocal.value);
window.MQTT_recv = MQTT_recv;
onRefresh();
});
onBeforeUnmount(() => {
//
});
return {
currentTitle,
isLocal,
checkEnvironment,
MQTT_send,
MQTT_recv,
handlePlatformControl,
handleScreenControl,
showLaserModeDialog,
selectLaserMode,
handleLightControl,
showBrightnessDialog,
handleBrightnessChange,
showProgramDialog,
selectProgram,
onRefresh,
screenPower,
lightMode,
lightStates,
brightness,
currentProgram,
availablePrograms,
loading,
showToast,
showLaserMode,
showBrightness,
showProgram,
laserModes
};
}
};
</script>
<style scoped>
.page-container {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: #f5f6fa;
position: relative;
}
.content {
flex: 1;
padding: 20px;
margin-top: 60px;
margin-bottom: 60px;
display: flex;
flex-direction: column;
gap: 20px;
}
.control-section {
padding: 5px;
}
.button-grid {
display: grid;
gap: 15px;
width: 100%;
}
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.top-grid {
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(2, 1fr);
}
.middle-grid {
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(2, 1fr);
}
.button-row {
display: flex;
gap: 15px;
justify-content: space-between;
}
.control-btn {
position: relative;
background-color: white;
border-radius: 16px;
height: 100px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
transition: all 0.2s ease;
cursor: pointer;
overflow: hidden;
}
.control-btn:hover {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
.control-btn:active {
transform: scale(0.98);
}
.active-light {
background-color: #e6f7ff;
border: 2px solid #1890ff;
}
.action-btn {
flex: 1;
position: relative;
border-radius: 16px;
height: 100px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
transition: all 0.2s ease;
cursor: pointer;
overflow: hidden;
color: white;
}
.reset-btn {
background: linear-gradient(135deg, #3498db, #2980b9);
}
.stop-btn {
background: linear-gradient(135deg, #e74c3c, #c0392b);
}
.action-btn:hover {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.action-btn:active {
transform: scale(0.98);
}
.btn-icon {
font-size: 32px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 12px;
color: #3498db;
transition: all 0.3s ease;
}
.control-btn:hover .btn-icon {
transform: scale(1.1);
}
.action-btn .btn-icon {
color: white;
}
.btn-label {
font-size: 16px;
font-weight: 500;
margin-top: 4px;
color: #2c3e50;
}
.action-btn .btn-label {
color: white;
}
/* 弹窗样式 */
.popup-content {
padding: 20px;
min-width: 280px;
}
.popup-title {
font-size: 18px;
font-weight: 600;
text-align: center;
margin-bottom: 20px;
color: #333;
}
.mode-options, .program-options {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
margin-bottom: 20px;
}
.mode-option, .program-option {
padding: 12px 20px;
background-color: #f5f5f5;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: all 0.2s ease;
}
.mode-option:hover, .program-option:hover {
background-color: #e0e0e0;
}
.mode-option.selected, .program-option.selected {
background-color: #1890ff;
color: white;
}
.brightness-slider {
padding: 0 20px;
margin-bottom: 20px;
}
.brightness-value {
text-align: center;
margin-top: 10px;
font-size: 16px;
color: #333;
}
.popup-buttons {
margin-top: 20px;
}
.brightness-popup {
width: 90%;
max-width: 320px;
}
@media (max-width: 380px) {
.content {
padding: 15px;
gap: 20px;
}
.control-btn,
.action-btn {
height: 90px;
}
.btn-icon {
font-size: 24px;
height: 45px;
width: 45px;
}
.btn-label {
font-size: 14px;
}
}
</style>

View File

@ -0,0 +1,278 @@
# 翻转屏通信协议文档
本文档描述了翻转屏设备的通信协议格式。所有通信采用JSON格式包括从控制端到设备的请求和从设备到控制端的响应。
## 通信基本结构
### 请求格式(控制端 → 设备)
```json
{
"board_id": 121, // 设备ID
"JSON_id": 1, // 消息唯一标识
"Flip-screen": { // 翻转屏指令
"command_type": { // 具体命令类型
// 命令参数
}
}
}
```
### 响应格式(设备 → 控制端)
```json
{
"JSON_id": 1, // 与请求消息相同的标识
"Flip-screen": { // 翻转屏响应
"command_type": { // 与请求相同的命令类型
"status": "success" // 操作状态: "success" 或 "failed"
}
},
"error_code": 0 // 错误码0表示成功
}
```
## 功能指令详解
### 1. 平台控制(上升/下降)
#### 请求:
```json
{
"board_id": 121,
"JSON_id": 2,
"Flip-screen": {
"platform_control": {
"direction": "up" // 可选值: "up" 或 "down"
}
}
}
```
#### 响应:
```json
{
"JSON_id": 2,
"Flip-screen": {
"platform_control": {
"status": "success"
}
},
"error_code": 0
}
```
### 2. 屏幕开关控制
#### 请求:
```json
{
"board_id": 121,
"JSON_id": 3,
"Flip-screen": {
"screen_control": {
"power": "on" // 可选值: "on" 或 "off"
}
}
}
```
#### 响应:
```json
{
"JSON_id": 3,
"Flip-screen": {
"screen_control": {
"status": "success"
}
},
"error_code": 0
}
```
### 3. 灯模式控制
#### 请求:
```json
{
"board_id": 121,
"JSON_id": 4,
"Flip-screen": {
"light_mode": {
"mode": "auto" // 灯光模式: "auto", "manual", 等
}
}
}
```
#### 响应:
```json
{
"JSON_id": 4,
"Flip-screen": {
"light_mode": {
"status": "success"
}
},
"error_code": 0
}
```
### 4. 单个灯控制
#### 请求:
```json
{
"board_id": 121,
"JSON_id": 5,
"Flip-screen": {
"light_control": {
"light_id": 1, // 灯ID编号: 1-6
"state": "on" // 可选值: "on" 或 "off"
}
}
}
```
#### 响应:
```json
{
"JSON_id": 5,
"Flip-screen": {
"light_control": {
"status": "success"
}
},
"error_code": 0
}
```
### 5. 亮度调节
#### 请求:
```json
{
"board_id": 121,
"JSON_id": 6,
"Flip-screen": {
"brightness_control": {
"value": 70 // 亮度值: 0-100
}
}
}
```
#### 响应:
```json
{
"JSON_id": 6,
"Flip-screen": {
"brightness_control": {
"status": "success"
}
},
"error_code": 0
}
```
### 6. 节目控制
#### 请求:
```json
{
"board_id": 121,
"JSON_id": 7,
"Flip-screen": {
"program_control": {
"program_id": 1 // 节目ID
}
}
}
```
#### 响应:
```json
{
"JSON_id": 7,
"Flip-screen": {
"program_control": {
"status": "success"
}
},
"error_code": 0
}
```
## 错误处理
当指令执行失败时,设备会返回非零的错误码和错误信息:
```json
{
"JSON_id": 3,
"Flip-screen": {
"screen_control": {
"status": "failed"
}
},
"error_code": 1001,
"error_msg": "Device not responding"
}
```
## 错误码说明
| 错误码 | 描述 |
|--------|------|
| 0 | 成功 |
| 1001 | 设备无响应 |
| 1002 | 参数错误 |
| 1003 | 设备忙 |
| 1004 | 操作超时 |
| 1005 | 设备未就绪 |
## 设备状态查询
### 请求:
```json
{
"board_id": 121,
"JSON_id": 8,
"Flip-screen": {
"status_query": {
"type": "all" // 查询类型: "all", "platform", "screen", "lights", "program"
}
}
}
```
### 响应:
```json
{
"JSON_id": 8,
"Flip-screen": {
"status": {
"platform": {
"position": "up" // "up" 或 "down"
},
"screen": {
"power": "on", // "on" 或 "off"
"brightness": 70 // 0-100
},
"lights": [
{"id": 1, "state": "on", },
{"id": 2, "state": "off",},
{"id": 3, "state": "on", },
{"id": 4, "state": "on", },
{"id": 5, "state": "off",},
{"id": 6, "state": "on", }
],
"program": {
"current_id": 1,
"available": [1, 2, 3, 4, 5]
},
"light_mode": "auto"
}
},
"error_code": 0
}
```

File diff suppressed because it is too large Load Diff

View File

@ -16,9 +16,12 @@
<van-cell size="large" class="custom-cell" title="车牌识别" icon="search" is-link value="识别" @click="goto('recognition')" />
<van-cell size="large" class="custom-cell" title="预警设置" icon="warning-o" is-link value="预警" @click="goto('warning')" />
<van-cell size="large" class="custom-cell" title="远程喊话" icon="bullhorn-o" is-link value="喊话" @click="navigateTo('web/voice_copy.html')" />
<van-cell size="large" class="custom-cell" title="小车控制" icon="car" is-link value="控制" @click="goto('CarControl')" />
<van-cell size="large" class="custom-cell" title="小车控制" icon="car" is-link value="控制" @click="goto('TEST')" />
<van-cell size="large" class="custom-cell" title="小车控制" icon="car" is-link value="控制" @click="goto('AudioPlay')" />
<van-cell size="large" class="custom-cell" title="小车控制" icon="logistics" is-link value="控制" @click="goto('CarControl')" />
<!-- <van-cell size="large" class="custom-cell" title="小车控制" icon="car" is-link value="控制" @click="goto('TEST')" /> -->
<!-- <van-cell size="large" class="custom-cell" title="小车控制" icon="car" is-link value="控制" @click="goto('AudioPlay')" /> -->
<van-cell size="large" class="custom-cell" title="网关设置" icon="desktop-o" is-link value="控制" @click="goto('GatewaySetting')" />
<van-cell size="large" class="custom-cell" title="路锥控制" icon="warning-o" is-link value="控制" @click="goto('ConeControl')" />
<van-cell size="large" class="custom-cell" title="翻转屏遥控" icon="warning-o" is-link value="控制" @click="goto('flipScreen')" />
</van-cell-group>
</div>
</div>

View File

@ -4,6 +4,7 @@ const AutoImport = require('unplugin-auto-import/webpack');
const Components = require('unplugin-vue-components/webpack');
const { codeInspectorPlugin } = require('code-inspector-plugin');
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
const CompressionWebpackPlugin = require('compression-webpack-plugin');
module.exports = defineConfig({
lintOnSave: false,
@ -25,6 +26,7 @@ module.exports = defineConfig({
devServer: {
open: true,
hot: true,//自动保存
port:80
},
chainWebpack: (config) => {
config.plugin('code-inspector-plugin').use(
@ -44,7 +46,7 @@ module.exports = defineConfig({
}),
Components.default({ resolvers: [VantResolver(),ElementPlusResolver()] }),
// new CompressionPlugin({
// new CompressionWebpackPlugin({
// algorithm: 'gzip', // 使用gzip压缩
// test: /\.js$|\.html$|\.css$/, // 匹配文件名
// filename: '[path][base].gz[query]', // 压缩后的文件名(保持原文件名,后缀加.gz)