2025-06-26 14:55:08 +08:00
|
|
|
|
<template>
|
|
|
|
|
<view class="voice-control">
|
|
|
|
|
<!-- 基础信息 -->
|
|
|
|
|
<view class="card">
|
|
|
|
|
<view class="status-titletop">{{ title }}</view>
|
|
|
|
|
<view style="padding:20rpx;">
|
|
|
|
|
<u--form labelPosition="left" labelWidth="100"
|
|
|
|
|
:labelStyle="{ marginRight: '16px', lineHeight: '32px', width: '50px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', color: '#000000' }">
|
|
|
|
|
<view class="version-wrap">
|
|
|
|
|
<u-form-item :label="$tt('status.deviceVersion')">
|
|
|
|
|
<u-row>
|
|
|
|
|
<u-col span="8">
|
|
|
|
|
<u--text :text="'Version ' + device.firmwareVersion"></u--text>
|
|
|
|
|
</u-col>
|
|
|
|
|
</u-row>
|
|
|
|
|
</u-form-item>
|
|
|
|
|
</view>
|
|
|
|
|
</u--form>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view class="card basic-info">
|
|
|
|
|
<view class="section-title">基础设置</view>
|
|
|
|
|
|
|
|
|
|
<view class="info-content">
|
|
|
|
|
<!-- 音量控制 -->
|
|
|
|
|
<view class="volume-slider">
|
|
|
|
|
<view class="volume-icon">
|
2025-07-03 08:57:13 +08:00
|
|
|
|
<image src="https://xaznkj.cn/doc/photo/brightness.png" mode="aspectFit" class="volume-svg">
|
2025-06-26 14:55:08 +08:00
|
|
|
|
</image>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="slider-container">
|
|
|
|
|
<u-slider v-model="volume" :min="0" :max="100" :step="1" @change="volumeChange"
|
|
|
|
|
:disabled="device.status !== 3" height="4" activeColor="#2979ff" blockSize="18"
|
|
|
|
|
:showValue="false">
|
|
|
|
|
</u-slider>
|
|
|
|
|
<view class="volume-marks">
|
|
|
|
|
<text>0</text>
|
|
|
|
|
<text>50</text>
|
|
|
|
|
<text>100</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="volume-value">{{ volume }}%</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 音频开关 -->
|
2025-07-23 14:34:57 +08:00
|
|
|
|
<view class="audio-switch" style="margin-top: 20rpx;">
|
2025-06-26 14:55:08 +08:00
|
|
|
|
<text>屏幕开关</text>
|
|
|
|
|
<u-switch v-model="audioEnabled" @change="audioSwitchChange" :disabled="device.status !== 3"
|
|
|
|
|
size="22"></u-switch>
|
|
|
|
|
</view>
|
|
|
|
|
<!-- 屏幕参数 -->
|
|
|
|
|
|
|
|
|
|
<view class="clear-screen-btn-wrap">
|
|
|
|
|
<u-button type="error" size="medium" shape="circle" @click="clearScreen"
|
|
|
|
|
customStyle="box-shadow:0 4rpx 12rpx rgba(255,77,79,0.15);font-weight:600;">
|
|
|
|
|
<u-icon name="trash" size="18" color="#fff" style="margin-right:8rpx;" />
|
|
|
|
|
清除屏幕
|
|
|
|
|
</u-button>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 屏幕参数卡片 -->
|
|
|
|
|
<view class="card screen-params">
|
|
|
|
|
<view class="section-title">屏幕参数</view>
|
|
|
|
|
<view class="screen-params-form">
|
|
|
|
|
<u-cell-group>
|
2025-07-23 14:34:57 +08:00
|
|
|
|
<picker mode="selector" :range="templateColumns" range-key="label" @change="onNativePickerChange">
|
|
|
|
|
<view class="picker-cell u-cell u-cell--border-bottom"
|
|
|
|
|
style="display:flex;align-items:center;justify-content:space-between;padding:24rpx 32rpx;margin-bottom:12rpx;">
|
|
|
|
|
<text style="font-size:30rpx;color:#333;">屏幕模板</text>
|
|
|
|
|
<text style="font-size:30rpx;color:#2979ff;">{{ screenParams.templateLabel || '请选择屏幕模板'
|
|
|
|
|
}}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</picker>
|
2025-06-26 14:55:08 +08:00
|
|
|
|
<u-cell @click="showPicker('screenRotate')" title="屏幕旋转"
|
|
|
|
|
:value="screenParams.screenRotateLabel || '请选择屏幕旋转'" isLink></u-cell>
|
|
|
|
|
</u-cell-group>
|
|
|
|
|
|
|
|
|
|
<u-form :model="screenParams" labelPosition="left" labelWidth="120">
|
|
|
|
|
<u-form-item label="OE极性" prop="oePolarity" borderBottom>
|
|
|
|
|
<view style="display:flex;align-items:center;">
|
|
|
|
|
<u-switch v-model="screenParams.oePolarity" :active-value="'1'" :inactive-value="'0'"
|
|
|
|
|
@change="val => screenParams.oePolarityLabel = val === '1' ? '1' : '0'" size="22"
|
|
|
|
|
style="margin-right:12rpx;" />
|
|
|
|
|
<u-input v-model="screenParams.oePolarityLabel" readonly border="none" style="flex:1;" />
|
|
|
|
|
</view>
|
|
|
|
|
</u-form-item>
|
|
|
|
|
<u-form-item label="DATA极性" prop="dataPolarity" borderBottom>
|
|
|
|
|
<view style="display:flex;align-items:center;">
|
|
|
|
|
<u-switch v-model="screenParams.dataPolarity" :active-value="'1'" :inactive-value="'0'"
|
|
|
|
|
@change="val => screenParams.dataPolarityLabel = val === '1' ? '1' : '0'" size="22"
|
|
|
|
|
style="margin-right:12rpx;" />
|
|
|
|
|
<u-input v-model="screenParams.dataPolarityLabel" readonly border="none" style="flex:1;" />
|
|
|
|
|
</view>
|
|
|
|
|
</u-form-item>
|
|
|
|
|
<u-form-item label="屏宽" prop="width" borderBottom>
|
|
|
|
|
<u-input v-model="screenParams.width" type="number" placeholder="请输入屏宽" />
|
|
|
|
|
</u-form-item>
|
|
|
|
|
<u-form-item label="屏高" prop="height" borderBottom>
|
|
|
|
|
<u-input v-model="screenParams.height" type="number" placeholder="请输入屏高" />
|
|
|
|
|
</u-form-item>
|
|
|
|
|
</u-form>
|
2025-07-23 14:34:57 +08:00
|
|
|
|
<view style="text-align:center;margin:20rpx 0;">
|
|
|
|
|
<u-button type="primary" size="normal" @click="handleSendScreenParams">下发参数</u-button>
|
|
|
|
|
</view>
|
2025-06-26 14:55:08 +08:00
|
|
|
|
</view>
|
|
|
|
|
<!-- <view style="text-align:center;margin:20rpx 0;">
|
|
|
|
|
<u-button type="primary" size="mini" @click="testOpenPicker">测试弹窗</u-button>
|
|
|
|
|
</view> -->
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 节目列表 -->
|
|
|
|
|
<view class="card audio-list">
|
|
|
|
|
<view class="section-title">
|
|
|
|
|
<text>节目列表</text>
|
2025-07-03 08:57:13 +08:00
|
|
|
|
<image src="https://xaznkj.cn/doc/photo/add.svg" mode="aspectFit" class="add-icon"
|
2025-06-26 14:55:08 +08:00
|
|
|
|
@click="showAddAudioModal"></image>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="list-container">
|
|
|
|
|
<view class="empty-tip" v-if="audioList.length === 0">
|
|
|
|
|
<u-icon name="qzone" size="50" color="#c0c4cc"></u-icon>
|
|
|
|
|
<text>暂无节目文件</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="audio-item" v-for="(item, index) in audioList" :key="index">
|
|
|
|
|
<view class="audio-info">
|
|
|
|
|
<u-icon name="play-right" size="18" color="#2979ff"></u-icon>
|
|
|
|
|
<text class="audio-name">{{ item.name }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="audio-actions">
|
2025-07-23 14:34:57 +08:00
|
|
|
|
<u-switch v-model="item.enabled" :active-value="1" :inactive-value="0"
|
|
|
|
|
@change="onProgramEnableChange(index, item.enabled)" size="22"
|
|
|
|
|
:disabled="device.status !== 3"></u-switch>
|
2025-06-26 14:55:08 +08:00
|
|
|
|
<u-icon name="trash" size="18" color="#ff4d4f" @click="deleteAudio(index)"></u-icon>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 虚拟遥控器 -->
|
2025-07-03 08:57:13 +08:00
|
|
|
|
<!-- <view class="card default-list">
|
2025-06-26 14:55:08 +08:00
|
|
|
|
<view class="section-title">
|
|
|
|
|
<text>虚拟遥控器</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="list-container">
|
|
|
|
|
<view class="empty-tip" v-if="defaultList.length === 0">
|
|
|
|
|
<u-icon name="star" size="50" color="#c0c4cc"></u-icon>
|
|
|
|
|
<text>暂无节目</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="audio-item" v-for="(item, index) in defaultList" :key="index">
|
|
|
|
|
<view class="audio-info">
|
|
|
|
|
<text class="audio-name">{{ item.name }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="audio-actions">
|
|
|
|
|
<u-switch v-model="item.status" :active-value="'启用'" :inactive-value="'禁用'"
|
|
|
|
|
@change="(value) => handleStatusChange(index, value)" size="22"></u-switch>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
2025-07-03 08:57:13 +08:00
|
|
|
|
</view> -->
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
|
|
|
|
<!-- 开机参数 -->
|
|
|
|
|
<view class="card remote-talk">
|
|
|
|
|
<view class="section-title">开机参数</view>
|
|
|
|
|
<view class="info-content">
|
2025-07-23 14:34:57 +08:00
|
|
|
|
<u-form :model="bootSettings" labelPosition="left" labelWidth="120">
|
|
|
|
|
<u-form-item label="开机界面可选">
|
|
|
|
|
<u-switch v-model="bootSettings.showBootScreen" @change="handleBootScreenSwitchChange"
|
|
|
|
|
size="22"></u-switch>
|
|
|
|
|
</u-form-item>
|
|
|
|
|
<u-form-item label="重置开关">
|
|
|
|
|
<u-switch v-model="bootSettings.resetEnabled" @change="handleResetSwitchChange"
|
|
|
|
|
size="22"></u-switch>
|
|
|
|
|
</u-form-item>
|
|
|
|
|
</u-form>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 传感器阈值卡片 -->
|
|
|
|
|
<view class="card sensor-threshold">
|
|
|
|
|
<view class="section-title">传感器阈值</view>
|
|
|
|
|
<view class="sensor-form">
|
|
|
|
|
<u-form :model="sensorThreshold" labelPosition="left" labelWidth="120">
|
|
|
|
|
<u-form-item label="速度阈值">
|
|
|
|
|
<u-input v-model="sensorThreshold.speed" type="number" placeholder="请输入速度阈值" />
|
|
|
|
|
</u-form-item>
|
|
|
|
|
<u-form-item label="温度阈值">
|
|
|
|
|
<u-input v-model="sensorThreshold.temp" type="number" placeholder="请输入温度阈值" />
|
|
|
|
|
</u-form-item>
|
|
|
|
|
<u-form-item label="湿度阈值">
|
|
|
|
|
<u-input v-model="sensorThreshold.humi" type="number" placeholder="请输入湿度阈值" />
|
|
|
|
|
</u-form-item>
|
|
|
|
|
</u-form>
|
|
|
|
|
<view style="text-align:center;margin:20rpx 0;">
|
|
|
|
|
<u-button type="primary" size="normal" @click="handleSendSensorThreshold">下发阈值</u-button>
|
|
|
|
|
</view>
|
2025-06-26 14:55:08 +08:00
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<!-- 测试按钮 -->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Pickers -->
|
|
|
|
|
<u-picker :show="pickerShow.screenRotate" :columns="[screenRotateOptions]" keyName="label"
|
|
|
|
|
@confirm="onPickerConfirm('screenRotate')" @cancel="closePicker('screenRotate')"
|
|
|
|
|
safe-area-inset-bottom></u-picker>
|
2025-07-23 14:34:57 +08:00
|
|
|
|
|
|
|
|
|
<!-- 编译信息卡片 -->
|
|
|
|
|
<view class="card build-info">
|
|
|
|
|
<view class="section-title" style="display:flex;align-items:center;justify-content:space-between;">
|
|
|
|
|
<text>编译信息</text>
|
|
|
|
|
<u-icon name="reload" size="32" color="#2979ff" @click="refreshBuildInfo"
|
|
|
|
|
style="margin-left:12rpx;cursor:pointer;" />
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-content">
|
|
|
|
|
<view class="info-item">
|
|
|
|
|
<text class="info-label">编译版本:</text>
|
|
|
|
|
<text class="info-value">{{ buildVersion || '加载中...' }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="info-item">
|
|
|
|
|
<text class="info-label">编译时间:</text>
|
|
|
|
|
<text class="info-value">{{ buildTime || '加载中...' }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
2025-06-26 14:55:08 +08:00
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
2025-07-23 14:34:57 +08:00
|
|
|
|
import {
|
|
|
|
|
serviceInvoke
|
|
|
|
|
} from '@/apis/modules/runtime.js';
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: 'VoiceControl',
|
|
|
|
|
props: {
|
|
|
|
|
device: {
|
|
|
|
|
type: Object,
|
|
|
|
|
required: true
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
title: '设备离线',
|
|
|
|
|
volume: 50,
|
|
|
|
|
audioEnabled: true,
|
|
|
|
|
showAddAudio: false,
|
|
|
|
|
showAddDefault: false,
|
|
|
|
|
showStartTimePicker: false,
|
|
|
|
|
showEndTimePicker: false,
|
|
|
|
|
showAudioPicker: false,
|
|
|
|
|
weekDays: ['日', '一', '二', '三', '四', '五', '六'],
|
|
|
|
|
audioIndex: -1,
|
|
|
|
|
newAudio: {
|
|
|
|
|
name: '',
|
|
|
|
|
per: '0',
|
|
|
|
|
spd: '5',
|
|
|
|
|
pit: '5',
|
|
|
|
|
vol: '5',
|
|
|
|
|
text: '',
|
|
|
|
|
file: null
|
|
|
|
|
},
|
|
|
|
|
deviceInfo: {
|
|
|
|
|
chartList: [],
|
|
|
|
|
},
|
|
|
|
|
newDefault: {
|
|
|
|
|
startTime: '',
|
|
|
|
|
endTime: '',
|
|
|
|
|
repeatDays: [],
|
|
|
|
|
radarEnabled: false,
|
|
|
|
|
minSpeed: '',
|
|
|
|
|
maxSpeed: '',
|
|
|
|
|
audioFile: null
|
|
|
|
|
},
|
|
|
|
|
audioFiles: [],
|
|
|
|
|
audioList: [],
|
|
|
|
|
defaultList: [],
|
|
|
|
|
audioUrl: '',
|
|
|
|
|
uploadFailed: false,
|
|
|
|
|
screenParams: {
|
|
|
|
|
template: '',
|
|
|
|
|
templateLabel: '',
|
|
|
|
|
oePolarity: '0',
|
|
|
|
|
oePolarityLabel: '0',
|
|
|
|
|
dataPolarity: '0',
|
|
|
|
|
dataPolarityLabel: '0',
|
|
|
|
|
screenRotate: '',
|
|
|
|
|
screenRotateLabel: '',
|
|
|
|
|
width: '',
|
|
|
|
|
height: ''
|
|
|
|
|
},
|
|
|
|
|
pickerShow: {
|
|
|
|
|
screenRotate: false
|
|
|
|
|
},
|
|
|
|
|
templateColumns: [
|
|
|
|
|
{ label: '自定义', value: '1', params: { oe: '', data: '', w: '', h: '', angle: '' } },
|
|
|
|
|
{ label: '伸缩屏', value: '2', params: { oe: 1, data: 1, w: 64, h: 32, angle: 270 } },
|
|
|
|
|
{ label: '大屏', value: '3', params: { oe: 1, data: 1, w: 64, h: 32, angle: 180 } },
|
|
|
|
|
{ label: '小屏', value: '4', params: { oe: 1, data: 1, w: 64, h: 16, angle: 0 } }
|
|
|
|
|
],
|
|
|
|
|
screenRotateOptions: [{
|
|
|
|
|
label: '0°',
|
|
|
|
|
value: '0'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: '90°',
|
|
|
|
|
value: '90'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: '180°',
|
|
|
|
|
value: '180'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: '270°',
|
|
|
|
|
value: '270'
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
],
|
|
|
|
|
bootSettings: {
|
|
|
|
|
showBootScreen: false,
|
|
|
|
|
resetEnabled: false
|
|
|
|
|
},
|
|
|
|
|
sensorThreshold: {
|
|
|
|
|
speed: 60,
|
|
|
|
|
temp: 30,
|
|
|
|
|
humi: 50
|
|
|
|
|
},
|
|
|
|
|
buildVersion: '',
|
|
|
|
|
buildTime: '',
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
created() {
|
|
|
|
|
if (this.device !== null && Object.keys(this.device).length !== 0) {
|
|
|
|
|
this.deviceInfo = this.device;
|
|
|
|
|
this.updateDeviceStatus(this.deviceInfo);
|
|
|
|
|
};
|
|
|
|
|
this.mqttCallback();
|
|
|
|
|
this.recorderManager = uni.getRecorderManager();
|
|
|
|
|
this.updateBasicSettings();
|
|
|
|
|
|
|
|
|
|
// 监听节目数据准备完成事件
|
|
|
|
|
uni.$on('programDataReady', this.handleProgramData);
|
|
|
|
|
},
|
|
|
|
|
beforeDestroy() {
|
|
|
|
|
if (this.recordingTimer) {
|
|
|
|
|
clearInterval(this.recordingTimer);
|
|
|
|
|
this.recordingTimer = null;
|
|
|
|
|
}
|
|
|
|
|
// 移除事件监听
|
|
|
|
|
uni.$off('programDataReady', this.handleProgramData);
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
showPicker(type) {
|
|
|
|
|
this.pickerShow[type] = true
|
2025-07-03 08:57:13 +08:00
|
|
|
|
},
|
2025-07-23 14:34:57 +08:00
|
|
|
|
closePicker(type) {
|
|
|
|
|
this.pickerShow[type] = false
|
2025-06-26 14:55:08 +08:00
|
|
|
|
},
|
2025-07-23 14:34:57 +08:00
|
|
|
|
onPickerConfirm(type, e) {
|
|
|
|
|
console.log('Picker confirm:', type, e);
|
|
|
|
|
const value = e && e.value && Array.isArray(e.value) && e.value.length > 0 ? e.value[0] : undefined;
|
|
|
|
|
if (!value) {
|
|
|
|
|
uni.showToast({ title: '选择数据异常', icon: 'none' });
|
|
|
|
|
this.closePicker(type);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this.screenParams[type] = value.value;
|
|
|
|
|
this.screenParams[type + 'Label'] = value.label;
|
|
|
|
|
this.closePicker(type);
|
|
|
|
|
if (type === 'screenRotate') {
|
|
|
|
|
this.handleScreenRotateChange(value.value);
|
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
},
|
2025-07-23 14:34:57 +08:00
|
|
|
|
clearScreen() {
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '已清除屏幕',
|
|
|
|
|
icon: 'success'
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
checkOnline(callback, ...args) {
|
|
|
|
|
if (this.device.status !== 3) {
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '设备离线,无法操作',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (typeof callback === 'function') {
|
|
|
|
|
return callback.apply(this, args);
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
return true;
|
2025-06-26 14:55:08 +08:00
|
|
|
|
},
|
2025-07-23 14:34:57 +08:00
|
|
|
|
|
|
|
|
|
async volumeChange(value) {
|
|
|
|
|
if (!this.checkOnline()) return;
|
|
|
|
|
try {
|
|
|
|
|
const luminanceModel = this.device.thingsModels.find(item => item.id === '102#luminance');
|
|
|
|
|
if (luminanceModel) {
|
|
|
|
|
luminanceModel.shadow = value.toString();
|
|
|
|
|
await this.mqttPublish(this.device, luminanceModel);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('调整亮度失败:', error);
|
2025-06-26 14:55:08 +08:00
|
|
|
|
uni.showToast({
|
2025-07-23 14:34:57 +08:00
|
|
|
|
title: '操作失败: ' + error.message,
|
|
|
|
|
icon: 'none'
|
2025-06-26 14:55:08 +08:00
|
|
|
|
});
|
2025-07-23 14:34:57 +08:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
mqttCallback() {
|
|
|
|
|
this.$mqttTool.client.on('message', (topic, message, buffer) => {
|
|
|
|
|
let topics = topic.split('/');
|
|
|
|
|
let productId = topics[1];
|
|
|
|
|
let deviceNum = topics[2];
|
|
|
|
|
message = JSON.parse(message.toString());
|
|
|
|
|
if (topics[3] == 'status') {
|
|
|
|
|
if (this.deviceInfo.serialNumber == deviceNum) {
|
|
|
|
|
this.deviceInfo.status = message.status;
|
|
|
|
|
this.deviceInfo.isShadow = message.isShadow;
|
|
|
|
|
this.deviceInfo.rssi = message.rssi;
|
|
|
|
|
this.updateDeviceStatus(this.deviceInfo);
|
|
|
|
|
this.updateBasicSettings();
|
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
if (topics[4] == 'reply') {
|
|
|
|
|
uni.showToast({
|
|
|
|
|
icon: 'none',
|
|
|
|
|
title: message,
|
|
|
|
|
})
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
if (topics[3] == 'property' || topics[3] == 'function' || topic.endsWith('ws/service')) {
|
|
|
|
|
if (this.deviceInfo.serialNumber == deviceNum) {
|
|
|
|
|
this.updateParam({ serialNumber: this.deviceInfo.serialNumber, productId: this.deviceInfo.productId, data: message.message });
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
// 1. 新增/同步 updateParam 方法,接收设备上报后刷新页面数据
|
|
|
|
|
updateParam(params) {
|
|
|
|
|
let { serialNumber, productId, data } = params;
|
|
|
|
|
let isComplete = false;
|
|
|
|
|
data = data.message;
|
|
|
|
|
if (data) {
|
|
|
|
|
for (let j = 0; j < data.length; j++) {
|
|
|
|
|
for (let k = 0; k < this.deviceInfo.thingsModels.length && !isComplete; k++) {
|
|
|
|
|
if (this.deviceInfo.thingsModels[k].id == data[j].id) {
|
|
|
|
|
const variable = this.deviceInfo.thingsModels[k];
|
|
|
|
|
if (this.deviceInfo.thingsModels[k].datatype.type == 'decimal' || this.deviceInfo.thingsModels[k].datatype.type == 'integer') {
|
|
|
|
|
variable.shadow = Number(data[j].value);
|
|
|
|
|
} else {
|
|
|
|
|
variable.shadow = data[j].value;
|
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
if (this.deviceInfo.thingsModels[k].datatype.type == 'object') {
|
|
|
|
|
for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.params.length; n++) {
|
|
|
|
|
if (this.deviceInfo.thingsModels[k].datatype.params[n].id == data[j].id) {
|
|
|
|
|
this.deviceInfo.thingsModels[k].datatype.params[n].shadow = data[j].value;
|
|
|
|
|
isComplete = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (this.deviceInfo.thingsModels[k].datatype.type == 'array') {
|
|
|
|
|
if (this.deviceInfo.thingsModels[k].datatype.arrayType == 'object') {
|
|
|
|
|
if (String(data[j].id).indexOf('array_') == 0) {
|
|
|
|
|
for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayParams.length; n++) {
|
|
|
|
|
for (let m = 0; m < this.deviceInfo.thingsModels[k].datatype.arrayParams[n].length; m++) {
|
|
|
|
|
if (this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].id == data[j].id) {
|
|
|
|
|
this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].shadow = data[j].value;
|
2025-07-03 08:57:13 +08:00
|
|
|
|
isComplete = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
if (isComplete) {
|
2025-07-03 08:57:13 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayParams.length; n++) {
|
|
|
|
|
for (let m = 0; m < this.deviceInfo.thingsModels[k].datatype.arrayParams[n].length; m++) {
|
|
|
|
|
let index = n > 9 ? String(n) : '0' + k;
|
|
|
|
|
let prefix = 'array_' + index + '_';
|
|
|
|
|
if (this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].id == prefix + data[j].id) {
|
|
|
|
|
this.deviceInfo.thingsModels[k].datatype.arrayParams[n][m].shadow = data[j].value;
|
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayModel.length; n++) {
|
|
|
|
|
if (this.deviceInfo.thingsModels[k].datatype.arrayModel[n].id == data[j].id) {
|
|
|
|
|
this.deviceInfo.thingsModels[k].datatype.arrayModel[n].shadow = data[j].value;
|
2025-06-26 14:55:08 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
for (let k = 0; k < this.deviceInfo.chartList.length; k++) {
|
|
|
|
|
if (this.deviceInfo.chartList[k].id.indexOf('array_') == 0) {
|
|
|
|
|
if (this.deviceInfo.chartList[k].id == data[j].id) {
|
|
|
|
|
this.deviceInfo.chartList[k].shadow = data[j].value;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (this.deviceInfo.chartList[k].id == data[j].id) {
|
|
|
|
|
this.deviceInfo.chartList[k].shadow = data[j].value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (isComplete) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
}
|
|
|
|
|
this.updateBasicSettings();
|
|
|
|
|
},
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
// 2. 同步 updateBasicSettings 方法,解析新版物模型 shadow 字段,字段与电脑版保持一致
|
|
|
|
|
updateBasicSettings() {
|
|
|
|
|
if (!this.deviceInfo.thingsModels) return;
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
// 屏幕开关
|
|
|
|
|
const screenEnModel = this.deviceInfo.thingsModels.find(model => model.id === '102#screenEn');
|
|
|
|
|
if (screenEnModel) {
|
|
|
|
|
this.audioEnabled = screenEnModel.shadow === '1';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 屏幕亮度
|
|
|
|
|
const luminanceModel = this.deviceInfo.thingsModels.find(model => model.id === '102#luminance');
|
|
|
|
|
if (luminanceModel) {
|
|
|
|
|
this.volume = parseInt(luminanceModel.shadow) || 50;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 屏幕参数
|
|
|
|
|
const scrParamModel = this.deviceInfo.thingsModels.find(model => model.id === '102#scrParam');
|
|
|
|
|
if (scrParamModel && scrParamModel.shadow) {
|
2025-07-03 08:57:13 +08:00
|
|
|
|
try {
|
2025-07-23 14:34:57 +08:00
|
|
|
|
const jsonStr = scrParamModel.shadow.replace('JSON=', '');
|
|
|
|
|
const data = JSON.parse(jsonStr);
|
|
|
|
|
if (data.scr_param) {
|
|
|
|
|
this.screenParams.oePolarity = data.scr_param.oe;
|
|
|
|
|
this.screenParams.dataPolarity = data.scr_param.data;
|
|
|
|
|
this.screenParams.width = data.scr_param.w;
|
|
|
|
|
this.screenParams.height = data.scr_param.h;
|
|
|
|
|
this.screenParams.screenRotate = data.scr_param.angle;
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('解析屏幕参数失败:', error);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
// 节目列表
|
|
|
|
|
const progListModel = this.deviceInfo.thingsModels.find(model => model.id === '102#progList');
|
|
|
|
|
if (progListModel && progListModel.shadow) {
|
|
|
|
|
try {
|
|
|
|
|
const jsonStr = progListModel.shadow.replace('JSON=', '');
|
2025-07-03 08:57:13 +08:00
|
|
|
|
const data = JSON.parse(jsonStr);
|
2025-07-23 14:34:57 +08:00
|
|
|
|
if (data.prog_list && data.prog_list.length > 0) {
|
|
|
|
|
this.audioList = data.prog_list.map((program, index) => {
|
|
|
|
|
return {
|
|
|
|
|
id: index + 1,
|
|
|
|
|
name: program.rem ? program.rem.replace(/\0/g, '') : '未命名节目',
|
|
|
|
|
duration: program.dur,
|
|
|
|
|
mode: program.areaM,
|
|
|
|
|
zones: program.aLst ? program.aLst.length : 0,
|
|
|
|
|
content: program.aLst ? JSON.stringify(program.aLst) : '',
|
|
|
|
|
enabled: 1,
|
|
|
|
|
raw: program
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
this.audioList = [];
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('解析节目列表失败:', error);
|
|
|
|
|
this.audioList = [];
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
// 节目使能状态
|
|
|
|
|
const progEnModel = this.deviceInfo.thingsModels.find(model => model.id === '102#progEn');
|
|
|
|
|
let enableArr = [];
|
|
|
|
|
if (progEnModel && progEnModel.shadow) {
|
|
|
|
|
try {
|
|
|
|
|
const jsonStr = progEnModel.shadow.replace('JSON=', '');
|
|
|
|
|
const data = JSON.parse(jsonStr);
|
|
|
|
|
if (data.prog_en && Array.isArray(data.prog_en)) {
|
|
|
|
|
enableArr = data.prog_en;
|
|
|
|
|
}
|
|
|
|
|
} catch (e) { }
|
|
|
|
|
}
|
|
|
|
|
if (progListModel && progListModel.shadow) {
|
|
|
|
|
try {
|
|
|
|
|
const jsonStr = progListModel.shadow.replace('JSON=', '');
|
|
|
|
|
const data = JSON.parse(jsonStr);
|
|
|
|
|
if (data.prog_list && data.prog_list.length > 0) {
|
|
|
|
|
this.audioList = data.prog_list.map((program, index) => {
|
|
|
|
|
return {
|
|
|
|
|
id: index + 1,
|
|
|
|
|
name: program.rem ? program.rem.replace(/\0/g, '') : '未命名节目',
|
|
|
|
|
duration: program.dur,
|
|
|
|
|
mode: program.areaM,
|
|
|
|
|
zones: program.aLst ? program.aLst.length : 0,
|
|
|
|
|
content: program.aLst ? JSON.stringify(program.aLst) : '',
|
|
|
|
|
enabled: enableArr[index] === 1 ? 1 : 0,
|
|
|
|
|
raw: program
|
|
|
|
|
};
|
2025-07-03 08:57:13 +08:00
|
|
|
|
});
|
2025-07-23 14:34:57 +08:00
|
|
|
|
} else {
|
|
|
|
|
this.audioList = [];
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('解析节目列表失败:', error);
|
|
|
|
|
this.audioList = [];
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
// 开机参数
|
|
|
|
|
const bootEnModel = this.deviceInfo.thingsModels.find(model => model.id === '102#bootEn');
|
|
|
|
|
if (bootEnModel) {
|
|
|
|
|
this.bootSettings.showBootScreen = bootEnModel.shadow === '1';
|
|
|
|
|
}
|
|
|
|
|
const factoryEnModel = this.deviceInfo.thingsModels.find(model => model.id === '102#factoryEn');
|
|
|
|
|
if (factoryEnModel) {
|
|
|
|
|
this.bootSettings.resetEnabled = factoryEnModel.shadow === '1';
|
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
// 传感器阈值
|
|
|
|
|
const snsrThrModel = this.deviceInfo.thingsModels.find(model => model.id === '102#snsrThr');
|
|
|
|
|
if (snsrThrModel && snsrThrModel.shadow) {
|
|
|
|
|
try {
|
|
|
|
|
const jsonStr = snsrThrModel.shadow.replace('JSON=', '');
|
|
|
|
|
const data = JSON.parse(jsonStr);
|
|
|
|
|
if (data.snsr_thr) {
|
|
|
|
|
this.sensorThreshold.speed = data.snsr_thr.speed;
|
|
|
|
|
this.sensorThreshold.temp = data.snsr_thr.temp;
|
|
|
|
|
this.sensorThreshold.humi = data.snsr_thr.humi;
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('解析传感器阈值失败:', error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 编译信息
|
|
|
|
|
const infoModel = this.deviceInfo.thingsModels.find(model => model.id === '102#info');
|
|
|
|
|
if (infoModel && infoModel.shadow) {
|
|
|
|
|
try {
|
|
|
|
|
const jsonStr = infoModel.shadow.replace('JSON=', '');
|
|
|
|
|
const data = JSON.parse(jsonStr);
|
|
|
|
|
this.buildVersion = data.versions || '未知版本';
|
|
|
|
|
this.buildTime = data.compile_time || '未知时间';
|
|
|
|
|
} catch (e) {
|
|
|
|
|
this.buildVersion = '未知版本';
|
|
|
|
|
this.buildTime = '未知时间';
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
this.buildVersion = '未知版本';
|
|
|
|
|
this.buildTime = '未知时间';
|
|
|
|
|
}
|
|
|
|
|
},
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
// 3. 同步 mqttPublish 方法,参数结构与电脑版一致,适配移动端
|
|
|
|
|
async mqttPublish(device, model) {
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
const command = {};
|
|
|
|
|
command[model.id] = model.shadow;
|
|
|
|
|
const params = {
|
|
|
|
|
deviceId: device.deviceId,
|
|
|
|
|
modelId: model.modelId
|
|
|
|
|
}
|
|
|
|
|
//判断是否有权限
|
|
|
|
|
// const response = await getOrderControl(params);
|
|
|
|
|
// if (response.code != 200) {
|
|
|
|
|
// uni.$u.toast(response.msg);
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
const data = {
|
|
|
|
|
serialNumber: device.serialNumber,
|
|
|
|
|
productId: device.productId,
|
|
|
|
|
remoteCommand: command,
|
|
|
|
|
identifier: model.id,
|
|
|
|
|
modelName: model.name,
|
|
|
|
|
isShadow: device.status != 3,
|
|
|
|
|
type: model.type,
|
|
|
|
|
}
|
|
|
|
|
serviceInvoke(data).then(response => {
|
|
|
|
|
if (response.code === 200) {
|
2025-06-26 14:55:08 +08:00
|
|
|
|
uni.showToast({
|
2025-07-23 14:34:57 +08:00
|
|
|
|
icon: 'none',
|
|
|
|
|
title: this.$tt('status.service')
|
2025-07-03 08:57:13 +08:00
|
|
|
|
});
|
2025-07-23 14:34:57 +08:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
updateDeviceStatus(device) {
|
|
|
|
|
if (device.status === 3) {
|
|
|
|
|
this.title = this.$tt('status.online');
|
|
|
|
|
} else {
|
|
|
|
|
this.title = device.isShadow === 1 ? this.$tt('status.shadow') : this.$tt('status.deviceOffline');
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
showAddAudioModal() {
|
|
|
|
|
// 将设备信息存储到本地,供 addProgram 页面使用
|
|
|
|
|
uni.setStorageSync('currentDevice', this.device);
|
|
|
|
|
uni.setStorageSync('currentDeviceInfo', this.deviceInfo);
|
|
|
|
|
|
|
|
|
|
uni.navigateTo({
|
|
|
|
|
url: '/pagesA/home/device/status/addProgram'
|
|
|
|
|
});
|
|
|
|
|
console.log('页面跳转');
|
|
|
|
|
},
|
|
|
|
|
showAddDefaultModal() {
|
|
|
|
|
this.showAddDefault = true;
|
|
|
|
|
},
|
|
|
|
|
async confirmAddAudio() {
|
|
|
|
|
if (!this.checkOnline()) return;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const mp3ListModel = this.deviceInfo.thingsModels.find(model => model.id ===
|
|
|
|
|
'103#mp3List');
|
|
|
|
|
if (!mp3ListModel) {
|
|
|
|
|
throw new Error('未找到 mp3_list 模型');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const jsonStr = mp3ListModel.shadow.replace('JSON=', '');
|
|
|
|
|
const data = JSON.parse(jsonStr);
|
|
|
|
|
|
|
|
|
|
let maxId = 0;
|
|
|
|
|
if (data.sound_card && data.sound_card.mp3_list) {
|
|
|
|
|
data.sound_card.mp3_list.forEach(item => {
|
|
|
|
|
const id = parseInt(item.split('_')[0]);
|
|
|
|
|
if (!isNaN(id) && id > maxId) {
|
|
|
|
|
maxId = id;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const newId = maxId + 1;
|
|
|
|
|
|
|
|
|
|
const ttsData = {
|
|
|
|
|
JSON_id: 1,
|
|
|
|
|
sound_card: {
|
|
|
|
|
TTS: {
|
|
|
|
|
per: parseInt(this.newAudio.per),
|
|
|
|
|
spd: parseInt(this.newAudio.spd),
|
|
|
|
|
pit: parseInt(this.newAudio.pit),
|
|
|
|
|
vol: parseInt(this.newAudio.vol),
|
|
|
|
|
tex_utf8: this.newAudio.text,
|
|
|
|
|
filename: `${newId}_${this.newAudio.name}`
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
mp3ListModel.shadow = 'JSON=' + JSON.stringify(ttsData);
|
|
|
|
|
await this.mqttPublish(this.device, mp3ListModel);
|
|
|
|
|
|
|
|
|
|
this.newAudio = {
|
|
|
|
|
name: '',
|
|
|
|
|
per: '0',
|
|
|
|
|
spd: '5',
|
|
|
|
|
pit: '5',
|
|
|
|
|
vol: '5',
|
|
|
|
|
text: '',
|
|
|
|
|
file: null
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
this.showAddAudio = false;
|
|
|
|
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '添加成功',
|
|
|
|
|
icon: 'success'
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('添加音频失败:', error);
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '添加失败: ' + error.message,
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
async confirmAddDefault() {
|
|
|
|
|
if (!this.checkOnline()) return;
|
|
|
|
|
|
|
|
|
|
if (!this.newDefault.startTime || !this.newDefault.endTime || !this.newDefault.audioFile) {
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '请填写完整信息',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.newDefault.radarEnabled) {
|
|
|
|
|
if (!this.newDefault.minSpeed || !this.newDefault.maxSpeed) {
|
2025-07-03 08:57:13 +08:00
|
|
|
|
uni.showToast({
|
2025-07-23 14:34:57 +08:00
|
|
|
|
title: '请填写速度范围',
|
2025-06-26 14:55:08 +08:00
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
2025-07-23 14:34:57 +08:00
|
|
|
|
return;
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
if (parseInt(this.newDefault.minSpeed) >= parseInt(this.newDefault.maxSpeed)) {
|
2025-06-26 14:55:08 +08:00
|
|
|
|
uni.showToast({
|
2025-07-23 14:34:57 +08:00
|
|
|
|
title: '最小速度必须小于最大速度',
|
2025-06-26 14:55:08 +08:00
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
const playListModel = this.deviceInfo.thingsModels.find(model => model.id ===
|
|
|
|
|
'103#playList');
|
|
|
|
|
if (playListModel) {
|
|
|
|
|
try {
|
|
|
|
|
const jsonStr = playListModel.shadow.replace('JSON=', '');
|
|
|
|
|
const data = JSON.parse(jsonStr);
|
|
|
|
|
|
|
|
|
|
if (!data.sound_card) {
|
|
|
|
|
data.sound_card = {};
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
if (!data.sound_card.play_list) {
|
|
|
|
|
data.sound_card.play_list = [];
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
let maxNum = 0;
|
|
|
|
|
if (data.sound_card.play_list.length > 0) {
|
|
|
|
|
maxNum = Math.max(...data.sound_card.play_list.map(item => item.play.num ||
|
|
|
|
|
0));
|
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
const [startHour, startMinute] = this.newDefault.startTime.split(':').map(Number);
|
|
|
|
|
const [endHour, endMinute] = this.newDefault.endTime.split(':').map(Number);
|
|
|
|
|
const startSeconds = startHour * 3600 + startMinute * 60;
|
|
|
|
|
const endSeconds = endHour * 3600 + endMinute * 60;
|
|
|
|
|
|
|
|
|
|
let weekValue = 0;
|
|
|
|
|
this.newDefault.repeatDays.forEach(day => {
|
|
|
|
|
weekValue |= (1 << day);
|
|
|
|
|
});
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
const newPlayItem = {
|
|
|
|
|
play: {
|
|
|
|
|
num: maxNum + 1,
|
|
|
|
|
filename: this.newDefault.audioFile.name,
|
|
|
|
|
en: 1
|
|
|
|
|
},
|
|
|
|
|
time: {
|
|
|
|
|
begin: startSeconds,
|
|
|
|
|
end: endSeconds,
|
|
|
|
|
week: weekValue
|
|
|
|
|
},
|
|
|
|
|
speed: {
|
|
|
|
|
en: this.newDefault.radarEnabled ? 1 : 0,
|
|
|
|
|
min: this.newDefault.radarEnabled ? parseInt(this.newDefault
|
|
|
|
|
.minSpeed) : 0,
|
|
|
|
|
max: this.newDefault.radarEnabled ? parseInt(this.newDefault
|
|
|
|
|
.maxSpeed) : 0
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
};
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
data.sound_card.play_list.push(newPlayItem);
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
playListModel.shadow = 'JSON=' + JSON.stringify(data);
|
2025-07-03 08:57:13 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
try {
|
|
|
|
|
await this.mqttPublish(this.device, playListModel);
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '添加成功',
|
|
|
|
|
icon: 'success'
|
|
|
|
|
});
|
|
|
|
|
this.showAddDefault = false;
|
|
|
|
|
|
|
|
|
|
this.newDefault = {
|
|
|
|
|
startTime: '',
|
|
|
|
|
endTime: '',
|
|
|
|
|
repeatDays: [],
|
|
|
|
|
radarEnabled: false,
|
|
|
|
|
minSpeed: '',
|
|
|
|
|
maxSpeed: '',
|
|
|
|
|
audioFile: null
|
2025-06-26 14:55:08 +08:00
|
|
|
|
};
|
|
|
|
|
} catch (error) {
|
2025-07-23 14:34:57 +08:00
|
|
|
|
console.error('发送添加命令失败:', error);
|
2025-06-26 14:55:08 +08:00
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '添加失败',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
2025-07-23 14:34:57 +08:00
|
|
|
|
console.error('解析或更新播放列表失败:', error);
|
2025-06-26 14:55:08 +08:00
|
|
|
|
uni.showToast({
|
2025-07-23 14:34:57 +08:00
|
|
|
|
title: '添加失败',
|
2025-06-26 14:55:08 +08:00
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
async deleteAudio(index) {
|
|
|
|
|
if (!this.checkOnline()) return;
|
|
|
|
|
|
|
|
|
|
try {
|
2025-06-26 14:55:08 +08:00
|
|
|
|
uni.showModal({
|
|
|
|
|
title: '提示',
|
2025-07-23 14:34:57 +08:00
|
|
|
|
content: '确定要删除该音频吗?',
|
2025-06-26 14:55:08 +08:00
|
|
|
|
cancelText: '取消',
|
|
|
|
|
confirmText: '确定',
|
|
|
|
|
success: async (res) => {
|
|
|
|
|
if (res.confirm) {
|
2025-07-23 14:34:57 +08:00
|
|
|
|
const mp3ListModel = this.deviceInfo.thingsModels.find(
|
2025-06-26 14:55:08 +08:00
|
|
|
|
model =>
|
2025-07-23 14:34:57 +08:00
|
|
|
|
model
|
|
|
|
|
.id === '103#mp3List');
|
|
|
|
|
if (mp3ListModel && mp3ListModel.shadow) {
|
2025-06-26 14:55:08 +08:00
|
|
|
|
try {
|
2025-07-23 14:34:57 +08:00
|
|
|
|
const jsonStr = mp3ListModel.shadow.replace(
|
|
|
|
|
'JSON=',
|
2025-06-26 14:55:08 +08:00
|
|
|
|
'');
|
|
|
|
|
const data = JSON.parse(jsonStr);
|
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
if (data.sound_card && data.sound_card.mp3_list) {
|
|
|
|
|
data.sound_card.mp3_list.splice(index, 1);
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
mp3ListModel.shadow = 'JSON=' + JSON.stringify(
|
2025-07-03 08:57:13 +08:00
|
|
|
|
data);
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
await this.mqttPublish(this.device,
|
|
|
|
|
mp3ListModel);
|
|
|
|
|
|
|
|
|
|
this.audioList.splice(index, 1);
|
|
|
|
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '删除成功',
|
|
|
|
|
icon: 'success'
|
|
|
|
|
});
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
2025-07-23 14:34:57 +08:00
|
|
|
|
console.error('删除音频失败:', error);
|
2025-06-26 14:55:08 +08:00
|
|
|
|
uni.showToast({
|
2025-07-23 14:34:57 +08:00
|
|
|
|
title: '删除失败: ' + error.message,
|
2025-06-26 14:55:08 +08:00
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-07-23 14:34:57 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('删除音频失败:', error);
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '删除失败: ' + error.message,
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
deleteDefault(index) {
|
|
|
|
|
uni.showModal({
|
|
|
|
|
title: '提示',
|
|
|
|
|
content: '确认删除该播放项吗?',
|
|
|
|
|
cancelText: '取消',
|
|
|
|
|
confirmText: '确定',
|
|
|
|
|
success: async (res) => {
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
const playListModel = this.deviceInfo.thingsModels.find(
|
|
|
|
|
model =>
|
|
|
|
|
model.id ===
|
|
|
|
|
'103#playList');
|
|
|
|
|
if (playListModel) {
|
|
|
|
|
try {
|
|
|
|
|
const jsonStr = playListModel.shadow.replace('JSON=',
|
|
|
|
|
'');
|
|
|
|
|
const data = JSON.parse(jsonStr);
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
if (data.sound_card && data.sound_card.play_list) {
|
|
|
|
|
data.sound_card.play_list.splice(index, 1);
|
|
|
|
|
|
|
|
|
|
data.sound_card.play_list.forEach((item,
|
|
|
|
|
index) => {
|
|
|
|
|
item.play.num = index + 1;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
playListModel.shadow = 'JSON=' + JSON.stringify(
|
|
|
|
|
data);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await this.mqttPublish(this.device,
|
|
|
|
|
playListModel);
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '删除成功',
|
|
|
|
|
icon: 'success'
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('发送删除命令失败:', error);
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '删除失败',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('解析或更新播放列表失败:', error);
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '删除失败',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
afterAudioRead(file) {
|
|
|
|
|
this.audioFiles.push(file);
|
|
|
|
|
},
|
|
|
|
|
deleteAudioFile(index) {
|
|
|
|
|
this.audioFiles.splice(index, 1);
|
|
|
|
|
},
|
|
|
|
|
beforeAudioUpload(file) {
|
|
|
|
|
const isValidType = file.name.toLowerCase().endsWith('.mp3');
|
|
|
|
|
const isValidSize = file.size / 1024 / 1024 < 10;
|
|
|
|
|
|
|
|
|
|
if (!isValidType) {
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '只能上传MP3格式',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!isValidSize) {
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '文件大小不能超过10MB',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
async audioSwitchChange() {
|
|
|
|
|
if (!this.checkOnline()) return;
|
|
|
|
|
try {
|
|
|
|
|
const screenEnModel = this.device.thingsModels.find(item => item.id === '102#screenEn');
|
|
|
|
|
if (screenEnModel) {
|
|
|
|
|
screenEnModel.shadow = this.audioEnabled ? '1' : '0';
|
|
|
|
|
await this.mqttPublish(this.device, screenEnModel);
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('切换屏幕开关失败:', error);
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '操作失败: ' + error.message,
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
formatTime(seconds) {
|
|
|
|
|
const minutes = Math.floor(seconds / 60);
|
|
|
|
|
const remainingSeconds = seconds % 60;
|
|
|
|
|
return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
|
|
|
|
|
},
|
|
|
|
|
startTimeChange(e) {
|
|
|
|
|
this.newDefault.startTime = e.detail.value;
|
|
|
|
|
},
|
|
|
|
|
endTimeChange(e) {
|
|
|
|
|
this.newDefault.endTime = e.detail.value;
|
|
|
|
|
},
|
|
|
|
|
toggleWeekDay(index) {
|
|
|
|
|
const position = this.newDefault.repeatDays.indexOf(index);
|
|
|
|
|
if (position === -1) {
|
|
|
|
|
this.newDefault.repeatDays.push(index);
|
|
|
|
|
} else {
|
|
|
|
|
this.newDefault.repeatDays.splice(position, 1);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
radarSwitchChange(value) {
|
|
|
|
|
this.newDefault.radarEnabled = value;
|
|
|
|
|
if (!value) {
|
|
|
|
|
this.newDefault.minSpeed = '';
|
|
|
|
|
this.newDefault.maxSpeed = '';
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
audioChange(e) {
|
|
|
|
|
this.audioIndex = e.detail.value;
|
|
|
|
|
this.newDefault.audioFile = this.audioList[this.audioIndex];
|
|
|
|
|
},
|
|
|
|
|
// 4. 同步 handleStatusChange 方法,更新播放项状态
|
|
|
|
|
async handleStatusChange(index, value) {
|
|
|
|
|
const playListModel = this.deviceInfo.thingsModels.find(model => model.id ===
|
|
|
|
|
'103#playList');
|
|
|
|
|
if (playListModel) {
|
2025-07-03 08:57:13 +08:00
|
|
|
|
try {
|
2025-07-23 14:34:57 +08:00
|
|
|
|
const jsonStr = playListModel.shadow.replace('JSON=', '');
|
|
|
|
|
const data = JSON.parse(jsonStr);
|
|
|
|
|
|
|
|
|
|
if (data.sound_card && data.sound_card.play_list) {
|
|
|
|
|
data.sound_card.play_list[index].play.en = value === '启用' ? 1 : 0;
|
|
|
|
|
|
|
|
|
|
playListModel.shadow = 'JSON=' + JSON.stringify(data);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await this.mqttPublish(this.device, playListModel);
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '更新成功',
|
|
|
|
|
icon: 'success'
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('发送状态更新命令失败:', error);
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '更新失败',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
|
|
|
|
this.defaultList[index].status = value === '启用' ? '禁用' : '启用';
|
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
} catch (error) {
|
2025-07-23 14:34:57 +08:00
|
|
|
|
console.error('解析或更新播放列表失败:', error);
|
2025-07-03 08:57:13 +08:00
|
|
|
|
uni.showToast({
|
2025-07-23 14:34:57 +08:00
|
|
|
|
title: '更新失败',
|
2025-07-03 08:57:13 +08:00
|
|
|
|
icon: 'none'
|
|
|
|
|
});
|
2025-07-23 14:34:57 +08:00
|
|
|
|
this.defaultList[index].status = value === '启用' ? '禁用' : '启用';
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
deleteRecording(index) {
|
|
|
|
|
uni.showModal({
|
|
|
|
|
title: '提示',
|
|
|
|
|
content: '确定要删除该录音吗?',
|
|
|
|
|
cancelText: '取消',
|
|
|
|
|
confirmText: '确定',
|
|
|
|
|
success: (res) => {
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
this.recordings.splice(index, 1);
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '删除成功',
|
|
|
|
|
icon: 'success'
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
// testOpenPicker(type) {
|
|
|
|
|
// this.pickerShow[type] = true;
|
|
|
|
|
// console.log('测试按钮:pickerShow.' + type + ' =', this.pickerShow[type]);
|
|
|
|
|
// },
|
|
|
|
|
// 处理从 addProgram 页面传来的节目数据
|
|
|
|
|
async handleProgramData(data) {
|
|
|
|
|
try {
|
|
|
|
|
if (!this.checkOnline()) return;
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
// 1. 找到 progList 物模型
|
|
|
|
|
const progListModel = this.device.thingsModels.find(model => model.id === '102#progList');
|
|
|
|
|
if (!progListModel) {
|
|
|
|
|
uni.showToast({ title: '未找到节目物模型', icon: 'none' });
|
|
|
|
|
return;
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
// 2. 解析原有节目列表
|
|
|
|
|
let progListArr = [];
|
|
|
|
|
if (progListModel.shadow) {
|
2025-07-03 08:57:13 +08:00
|
|
|
|
try {
|
2025-07-23 14:34:57 +08:00
|
|
|
|
const jsonStr = progListModel.shadow.replace('JSON=', '');
|
|
|
|
|
const dataJson = JSON.parse(jsonStr);
|
|
|
|
|
if (dataJson.prog_list && Array.isArray(dataJson.prog_list)) {
|
|
|
|
|
progListArr = dataJson.prog_list;
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
} catch (e) {}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
// 3. 组装新节目
|
|
|
|
|
const program = data.form;
|
|
|
|
|
const newProgram = {
|
|
|
|
|
order: progListArr.length, // 新增节目order为当前长度
|
|
|
|
|
rem: (program.remark || `自定义节目${Date.now()}`) + '\0',
|
|
|
|
|
dur: parseInt(program.duration) || 10,
|
|
|
|
|
areaM: program.mode,
|
|
|
|
|
aLst: program.zones.map(zone => {
|
|
|
|
|
const area = {
|
|
|
|
|
size: {
|
|
|
|
|
x: parseInt(zone.x) || 0,
|
|
|
|
|
y: parseInt(zone.y) || 0,
|
|
|
|
|
l: parseInt(zone.width) || 32,
|
|
|
|
|
h: parseInt(zone.height) || 64
|
|
|
|
|
},
|
|
|
|
|
pLst: []
|
|
|
|
|
};
|
|
|
|
|
if (zone.playType === 0) {
|
|
|
|
|
area.pLst.push({
|
|
|
|
|
typ: 0,
|
|
|
|
|
txt: {
|
|
|
|
|
str: zone.displayText || '',
|
|
|
|
|
fCn: (zone.font || 0) + 1,
|
|
|
|
|
fS: parseInt((this.fontSizes && this.fontSizes[zone.fontSize]) ? this.fontSizes[zone.fontSize] : 16),
|
|
|
|
|
col: zone.fontColor || 0,
|
|
|
|
|
fW: zone.fontBold || 0,
|
|
|
|
|
stch: zone.fontStretch || 0,
|
|
|
|
|
hPos: zone.hAlign || 1,
|
|
|
|
|
vPos: zone.vAlign || 1
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else if (zone.playType === 1) {
|
|
|
|
|
area.pLst.push({
|
|
|
|
|
typ: 1,
|
|
|
|
|
img: {
|
|
|
|
|
num: 0,
|
|
|
|
|
col: zone.imageColor || 0,
|
|
|
|
|
w: parseInt((this.imageSizes && this.imageSizes[zone.imageSize]) ? this.imageSizes[zone.imageSize] : 32),
|
|
|
|
|
h: parseInt((this.imageSizes && this.imageSizes[zone.imageSize]) ? this.imageSizes[zone.imageSize] : 32),
|
|
|
|
|
data: ''
|
|
|
|
|
},
|
|
|
|
|
anim: {
|
|
|
|
|
typ: (zone.effect || 0) + 1,
|
|
|
|
|
spd: zone.speed || 4,
|
|
|
|
|
pauseT: 1000,
|
|
|
|
|
playT: 2000
|
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
});
|
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
return area;
|
|
|
|
|
})
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 4. 添加到原有列表
|
|
|
|
|
progListArr.push(newProgram);
|
|
|
|
|
|
|
|
|
|
// 5. 下发
|
|
|
|
|
const progListData = { del_prog: 0, prog_list: progListArr };
|
|
|
|
|
progListModel.shadow = 'JSON=' + JSON.stringify(progListData);
|
|
|
|
|
await this.mqttPublish(this.device, progListModel);
|
|
|
|
|
|
|
|
|
|
uni.showToast({ title: '节目设置成功', icon: 'success' });
|
|
|
|
|
this.updateBasicSettings();
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('处理节目数据失败:', error);
|
|
|
|
|
uni.showToast({ title: '设置失败: ' + error.message, icon: 'none' });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 新增:开机参数开关下发
|
|
|
|
|
handleBootScreenSwitchChange(val) {
|
|
|
|
|
const bootEnModel = this.device.thingsModels.find(model => model.id === '102#bootEn');
|
|
|
|
|
if (bootEnModel) {
|
|
|
|
|
bootEnModel.shadow = val ? '1' : '0';
|
|
|
|
|
this.mqttPublish(this.device, bootEnModel);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
handleResetSwitchChange(val) {
|
|
|
|
|
const factoryEnModel = this.device.thingsModels.find(model => model.id === '102#factoryEn');
|
|
|
|
|
if (factoryEnModel) {
|
|
|
|
|
factoryEnModel.shadow = val ? '1' : '0';
|
|
|
|
|
this.mqttPublish(this.device, factoryEnModel);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 屏幕模板选择逻辑
|
|
|
|
|
handleTemplateChange(val) {
|
|
|
|
|
const template = this.templateColumns.find(t => t.value === val);
|
|
|
|
|
if (template && template.params) {
|
|
|
|
|
const params = template.params;
|
|
|
|
|
// 保证为字符串'1'或'0',适配u-switch
|
|
|
|
|
this.screenParams.oePolarity = params.oe === 1 ? '1' : (params.oe === 0 ? '0' : '0');
|
|
|
|
|
this.screenParams.dataPolarity = params.data === 1 ? '1' : (params.data === 0 ? '0' : '0');
|
|
|
|
|
this.screenParams.width = params.w;
|
|
|
|
|
this.screenParams.height = params.h;
|
|
|
|
|
this.screenParams.screenRotate = params.angle;
|
|
|
|
|
// 同步旋转label
|
|
|
|
|
const rotateOption = this.screenRotateOptions.find(opt => opt.value == params.angle);
|
|
|
|
|
this.screenParams.screenRotateLabel = rotateOption ? rotateOption.label : '';
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 下发屏幕参数
|
|
|
|
|
handleSendScreenParams() {
|
|
|
|
|
const model = this.device.thingsModels.find(m => m.id === '102#scrParam');
|
|
|
|
|
if (model) {
|
|
|
|
|
const param = {
|
|
|
|
|
scr_param: {
|
|
|
|
|
oe: this.screenParams.oePolarity,
|
|
|
|
|
data: this.screenParams.dataPolarity,
|
|
|
|
|
w: this.screenParams.width,
|
|
|
|
|
h: this.screenParams.height,
|
|
|
|
|
angle: this.screenParams.screenRotate
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
};
|
|
|
|
|
model.shadow = 'JSON=' + JSON.stringify(param);
|
|
|
|
|
this.mqttPublish(this.device, model);
|
|
|
|
|
uni.showToast({ title: '参数已下发', icon: 'success' });
|
|
|
|
|
} else {
|
|
|
|
|
uni.showToast({ title: '未找到屏幕参数物模型', icon: 'none' });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 新增原生picker回调
|
|
|
|
|
onNativePickerChange(e) {
|
|
|
|
|
const index = e.detail.value;
|
|
|
|
|
const value = this.templateColumns[index];
|
|
|
|
|
this.screenParams.template = value.value;
|
|
|
|
|
this.screenParams.templateLabel = value.label;
|
|
|
|
|
this.handleTemplateChange(value.value);
|
|
|
|
|
},
|
|
|
|
|
handleSendSensorThreshold() {
|
|
|
|
|
const model = this.device.thingsModels.find(m => m.id === '102#snsrThr');
|
|
|
|
|
if (model) {
|
|
|
|
|
const param = {
|
|
|
|
|
snsr_thr: {
|
|
|
|
|
speed: Number(this.sensorThreshold.speed),
|
|
|
|
|
temp: Number(this.sensorThreshold.temp),
|
|
|
|
|
humi: Number(this.sensorThreshold.humi)
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
};
|
|
|
|
|
model.shadow = 'JSON=' + JSON.stringify(param);
|
|
|
|
|
this.mqttPublish(this.device, model);
|
|
|
|
|
uni.showToast({ title: '阈值已下发', icon: 'success' });
|
|
|
|
|
} else {
|
|
|
|
|
uni.showToast({ title: '未找到传感器阈值物模型', icon: 'none' });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
onProgramEnableChange(index, value) {
|
|
|
|
|
// 组装10个元素的使能数组
|
|
|
|
|
const enableArray = new Array(10).fill(0);
|
|
|
|
|
this.audioList.forEach((item, idx) => {
|
|
|
|
|
if (idx < 10) enableArray[idx] = item.enabled ? 1 : 0;
|
|
|
|
|
});
|
|
|
|
|
// 下发到 102#progEn
|
|
|
|
|
const progEnModel = this.device.thingsModels.find(model => model.id === '102#progEn');
|
|
|
|
|
if (progEnModel) {
|
|
|
|
|
const progEnData = { prog_en: enableArray };
|
|
|
|
|
progEnModel.shadow = 'JSON=' + JSON.stringify(progEnData);
|
|
|
|
|
this.mqttPublish(this.device, progEnModel);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
refreshBuildInfo() {
|
|
|
|
|
// 下发102#model=1,设备上报编译信息
|
|
|
|
|
const model = this.device.thingsModels.find(m => m.id === '102#model');
|
|
|
|
|
if (model) {
|
|
|
|
|
model.shadow = '1';
|
|
|
|
|
this.mqttPublish(this.device, model);
|
|
|
|
|
uni.showToast({ title: '已发送刷新指令', icon: 'success' });
|
|
|
|
|
} else {
|
|
|
|
|
uni.showToast({ title: '未找到编译信息物模型', icon: 'none' });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
startTimeLabel() {
|
|
|
|
|
return this.newDefault.startTime ? this.formatTime(this.newDefault.startTime) : '请选择开始时间';
|
|
|
|
|
},
|
|
|
|
|
endTimeLabel() {
|
|
|
|
|
return this.newDefault.endTime ? this.formatTime(this.newDefault.endTime) : '请选择结束时间';
|
|
|
|
|
},
|
|
|
|
|
selectedAudioName() {
|
|
|
|
|
return this.audioIndex >= 0 ? this.audioList[this.audioIndex].name : '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.voice-control {
|
|
|
|
|
padding: 32rpx;
|
|
|
|
|
background: #f7f9fc;
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
|
|
|
|
|
|
|
|
|
|
/* 统一卡片 */
|
|
|
|
|
.card {
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 24rpx;
|
|
|
|
|
margin-bottom: 32rpx;
|
|
|
|
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, .06);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
transition: all .3s ease;
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
transform: scale(.99);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 标题栏 */
|
|
|
|
|
.status-titletop {
|
|
|
|
|
font-size: 34rpx;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #2979ff;
|
|
|
|
|
padding: 28rpx 40rpx;
|
|
|
|
|
background: linear-gradient(135deg, #e8f2ff 0%, #f0f7ff 100%);
|
|
|
|
|
border-bottom: 1rpx solid #dceaff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
font-size: 34rpx;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #333;
|
|
|
|
|
padding: 32rpx 40rpx;
|
|
|
|
|
border-bottom: 1rpx solid #f0f2f5;
|
|
|
|
|
background: #fafbff;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
|
|
.add-icon {
|
|
|
|
|
width: 48rpx;
|
|
|
|
|
height: 48rpx;
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
transition: transform .2s;
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
transform: scale(.85);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 内容区域统一内边距 */
|
|
|
|
|
.info-content {
|
|
|
|
|
padding: 32rpx 40rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 音量滑条 */
|
|
|
|
|
.volume-slider {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 24rpx;
|
|
|
|
|
padding: 32rpx;
|
|
|
|
|
background: #f8fafc;
|
|
|
|
|
border-radius: 20rpx;
|
|
|
|
|
box-shadow: inset 0 0 0 1rpx #eaeef5;
|
|
|
|
|
|
|
|
|
|
.volume-icon image {
|
|
|
|
|
width: 48rpx;
|
|
|
|
|
height: 48rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.slider-container {
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
|
|
|
|
.volume-marks {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
margin-top: 12rpx;
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
color: #909399;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.volume-value {
|
|
|
|
|
min-width: 80rpx;
|
|
|
|
|
text-align: right;
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #2979ff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 开关行 */
|
|
|
|
|
.audio-switch {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 32rpx;
|
|
|
|
|
background: #f8fafc;
|
|
|
|
|
border-radius: 20rpx;
|
|
|
|
|
margin-bottom: 32rpx;
|
|
|
|
|
box-shadow: inset 0 0 0 1rpx #eaeef5;
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
color: #333;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 按钮 */
|
|
|
|
|
.clear-screen-btn-wrap {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
margin-top: 32rpx;
|
|
|
|
|
|
|
|
|
|
::v-deep .u-button {
|
|
|
|
|
width: 70%;
|
|
|
|
|
height: 88rpx;
|
|
|
|
|
font-size: 32rpx;
|
|
|
|
|
border-radius: 44rpx;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 表单 / 列表 */
|
|
|
|
|
.screen-params-form {
|
|
|
|
|
padding: 32rpx 40rpx;
|
|
|
|
|
|
|
|
|
|
::v-deep .u-form-item {
|
|
|
|
|
margin-bottom: 24rpx;
|
|
|
|
|
|
|
|
|
|
&__body {
|
|
|
|
|
padding: 24rpx 0;
|
|
|
|
|
border-bottom: 1rpx solid #f0f2f5;
|
|
|
|
|
|
|
|
|
|
&__left {
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
color: #606266;
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
&__right .u-input {
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
color: #333;
|
|
|
|
|
text-align: right;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.list-container {
|
|
|
|
|
padding: 0 40rpx 32rpx;
|
|
|
|
|
|
|
|
|
|
.empty-tip {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
padding: 80rpx 0;
|
|
|
|
|
color: #c0c4cc;
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
gap: 24rpx;
|
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
.audio-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 32rpx 0;
|
|
|
|
|
border-bottom: 1rpx solid #f5f5f5;
|
2025-06-26 14:55:08 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
&:last-child {
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.audio-info {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 20rpx;
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
|
|
|
|
.audio-name {
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
color: #333;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
.audio-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 32rpx;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
.picker-cell {
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 24rpx;
|
|
|
|
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, .06);
|
|
|
|
|
margin-bottom: 12rpx;
|
|
|
|
|
transition: all .3s ease;
|
2025-07-03 08:57:13 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
&:active {
|
|
|
|
|
transform: scale(.99);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sensor-threshold {
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 24rpx;
|
|
|
|
|
margin-bottom: 32rpx;
|
|
|
|
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, .06);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
transition: all .3s ease;
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
font-size: 34rpx;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #333;
|
|
|
|
|
padding: 32rpx 40rpx;
|
|
|
|
|
border-bottom: 1rpx solid #f0f2f5;
|
|
|
|
|
background: #fafbff;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
.sensor-form {
|
|
|
|
|
padding: 32rpx 40rpx;
|
2025-07-03 08:57:13 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
.u-form-item {
|
|
|
|
|
margin-bottom: 32rpx;
|
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
.u-input {
|
|
|
|
|
background: #f8fafc;
|
|
|
|
|
border-radius: 16rpx;
|
|
|
|
|
padding: 0 20rpx;
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
color: #333;
|
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
.u-button {
|
|
|
|
|
width: 70%;
|
|
|
|
|
height: 88rpx;
|
|
|
|
|
font-size: 32rpx;
|
|
|
|
|
border-radius: 44rpx;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
box-shadow: 0 4rpx 12rpx rgba(41, 121, 255, 0.15);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.build-info {
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 24rpx;
|
|
|
|
|
margin-bottom: 32rpx;
|
|
|
|
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, .06);
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
transition: all .3s ease;
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
font-size: 34rpx;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #333;
|
|
|
|
|
padding: 32rpx 40rpx;
|
|
|
|
|
border-bottom: 1rpx solid #f0f2f5;
|
|
|
|
|
background: #fafbff;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
|
2025-07-23 14:34:57 +08:00
|
|
|
|
.info-content {
|
|
|
|
|
padding: 32rpx 40rpx;
|
|
|
|
|
|
|
|
|
|
.info-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
margin-bottom: 18rpx;
|
|
|
|
|
|
|
|
|
|
.info-label {
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
color: #606266;
|
|
|
|
|
min-width: 120rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info-value {
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
color: #333;
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-03 08:57:13 +08:00
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
}
|
2025-07-23 14:34:57 +08:00
|
|
|
|
}
|
2025-07-20 17:23:49 +08:00
|
|
|
|
}
|
2025-06-26 14:55:08 +08:00
|
|
|
|
</style>
|