This commit is contained in:
JayJiaJun 2025-04-24 16:53:10 +08:00
parent de18827af6
commit 225dc12f77
10 changed files with 1587 additions and 5 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",

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

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

@ -72,6 +72,11 @@ const routes = [
name: 'ConeControl',
component: () => import('../views/ConeControl.vue')
},
{
path: '/flipScreen',
name: 'flipScreen',
component: () => import('../views/flipScreen/flipScreen.vue')
},
];
const router = createRouter({

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

View File

@ -21,6 +21,7 @@
<!-- <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>