1278 lines
36 KiB
Vue
Raw Normal View History

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>
<!-- 音频开关 -->
<view class="audio-switch">
<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>
<u-cell @click="showPicker('template')" title="屏幕模板"
:value="screenParams.templateLabel || '请选择屏幕模板'" isLink></u-cell>
<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>
</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">
<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">
<!-- 这里内容暂时空着如需表单可补充 -->
</view>
</view>
<!-- 测试按钮 -->
<!-- Pickers -->
<u-picker :show="pickerShow.template" :columns="[templateColumns]" keyName="label"
@confirm="onPickerConfirm('template')" @cancel="closePicker('template')" safe-area-inset-bottom></u-picker>
<u-picker :show="pickerShow.screenRotate" :columns="[screenRotateOptions]" keyName="label"
@confirm="onPickerConfirm('screenRotate')" @cancel="closePicker('screenRotate')"
safe-area-inset-bottom></u-picker>
</view>
</template>
<script>
2025-07-03 08:57:13 +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
2025-06-26 14:55:08 +08:00
},
2025-07-03 08:57:13 +08:00
deviceInfo: {
chartList: [],
2025-06-26 14:55:08 +08:00
},
2025-07-03 08:57:13 +08:00
newDefault: {
startTime: '',
endTime: '',
repeatDays: [],
radarEnabled: false,
minSpeed: '',
maxSpeed: '',
audioFile: null
2025-06-26 14:55:08 +08:00
},
2025-07-03 08:57:13 +08:00
audioFiles: [],
audioList: [],
defaultList: [],
audioUrl: '',
uploadFailed: false,
screenParams: {
template: '',
templateLabel: '',
oePolarity: '0',
oePolarityLabel: '0',
dataPolarity: '0',
dataPolarityLabel: '0',
screenRotate: '',
screenRotateLabel: '',
width: '',
height: ''
2025-06-26 14:55:08 +08:00
},
2025-07-03 08:57:13 +08:00
pickerShow: {
template: false,
screenRotate: false
2025-06-26 14:55:08 +08:00
},
2025-07-03 08:57:13 +08:00
templateColumns: [{
label: '自定义',
value: '1'
},
{
label: '伸缩屏',
value: '2'
},
{
label: '大屏',
value: '3'
},
{
label: '小屏',
value: '4'
}
],
screenRotateOptions: [{
label: '0°',
value: '0'
},
{
label: '90°',
value: '90'
},
{
label: '180°',
value: '180'
},
{
label: '270°',
value: '270'
}
],
};
2025-06-26 14:55:08 +08:00
},
2025-07-03 08:57:13 +08:00
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);
2025-06-26 14:55:08 +08:00
},
2025-07-03 08:57:13 +08:00
beforeDestroy() {
if (this.recordingTimer) {
clearInterval(this.recordingTimer);
this.recordingTimer = null;
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +08:00
// 移除事件监听
uni.$off('programDataReady', this.handleProgramData);
2025-06-26 14:55:08 +08:00
},
2025-07-03 08:57:13 +08:00
methods: {
showPicker(type) {
this.pickerShow[type] = true
},
closePicker(type) {
this.pickerShow[type] = false
},
onPickerConfirm(type, e) {
console.log('Picker confirm:', type, e);
const value = e.value[0];
this.screenParams[type] = value.value;
this.screenParams[type + 'Label'] = value.label;
this.closePicker(type);
},
clearScreen() {
2025-06-26 14:55:08 +08:00
uni.showToast({
2025-07-03 08:57:13 +08:00
title: '已清除屏幕',
icon: 'success'
2025-06-26 14:55:08 +08:00
});
2025-07-03 08:57:13 +08:00
},
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-03 08:57:13 +08:00
return true;
},
async volumeChange(value) {
if (!this.checkOnline()) return;
try {
const volumeModel = this.device.thingsModels.find(item => item.id === 'volume');
if (volumeModel) {
volumeModel.shadow = value.toString();
await this.mqttPublish(this.device, volumeModel);
}
} catch (error) {
console.error('调整音量失败:', error);
2025-06-26 14:55:08 +08:00
uni.showToast({
2025-07-03 08:57:13 +08:00
title: '操作失败: ' + error.message,
icon: 'none'
});
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +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();
}
}
if (topics[4] == 'reply') {
uni.showToast({
icon: 'none',
title: message,
})
}
if (topics[3] == 'property' || topics[3] == 'function' || topic.endsWith('ws/service')) {
if (this.deviceInfo.serialNumber == deviceNum) {
for (let j = 0; j < message.message.length; j++) {
let isComplete = false;
for (let k = 0; k < this.deviceInfo.thingsModels.length && !isComplete; k++) {
if (this.deviceInfo.thingsModels[k].id == message.message[j].id) {
this.deviceInfo.thingsModels[k].shadow = message.message[j].value;
isComplete = true;
break;
} else 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 == message
.message[j].id) {
this.deviceInfo.thingsModels[k].datatype.params[n].shadow = message
.message[j].value;
isComplete = true;
break;
}
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +08:00
} else if (this.deviceInfo.thingsModels[k].datatype.type == "array") {
if (this.deviceInfo.thingsModels[k].datatype.arrayType == "object") {
if (String(message.message[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 == message.message[j].id) {
this.deviceInfo.thingsModels[k].datatype.arrayParams[n]
[m].shadow = message.message[j].value;
isComplete = true;
break;
}
}
if (isComplete) {
2025-06-26 14:55:08 +08:00
break;
}
}
2025-07-03 08:57:13 +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 + message.message[j].id) {
this.deviceInfo.thingsModels[k].datatype.arrayParams[n]
[m].shadow = message.message[j].value;
isComplete = true;
}
}
if (isComplete) {
break;
}
2025-06-26 14:55:08 +08:00
}
}
} else {
2025-07-03 08:57:13 +08:00
for (let n = 0; n < this.deviceInfo.thingsModels[k].datatype.arrayModel
.length; n++) {
if (this.deviceInfo.thingsModels[k].datatype.arrayModel[n].id ==
message.message[j].id) {
this.deviceInfo.thingsModels[k].datatype.arrayModel[n].shadow =
message.message[j].value;
isComplete = true;
2025-06-26 14:55:08 +08:00
break;
}
}
}
2025-07-03 08:57:13 +08:00
}
};
for (let k = 0; k < this.deviceInfo.chartList.length && !isComplete; k++) {
if (this.deviceInfo.chartList[k].id.indexOf("array_") == 0) {
if (this.deviceInfo.chartList[k].id == message.message[j].id) {
this.deviceInfo.chartList[k].shadow = message.message[j].value;
isComplete = true;
break;
}
2025-06-26 14:55:08 +08:00
} else {
2025-07-03 08:57:13 +08:00
if (this.deviceInfo.chartList[k].id == message.message[j].id) {
this.deviceInfo.chartList[k].shadow = message.message[j].value;
isComplete = true;
break;
2025-06-26 14:55:08 +08:00
}
}
2025-07-03 08:57:13 +08:00
if (isComplete) {
2025-06-26 14:55:08 +08:00
break;
}
2025-07-03 08:57:13 +08:00
};
}
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +08:00
this.updateBasicSettings();
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +08:00
});
},
async mqttPublish(device, model) {
const command = {};
command[model.id] = model.shadow;
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) {
uni.showToast({
icon: 'none',
title: this.$tt('status.service')
});
}
});
},
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');
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +08:00
},
showAddAudioModal() {
// 将设备信息存储到本地,供 addProgram 页面使用
uni.setStorageSync('currentDevice', this.device);
uni.setStorageSync('currentDeviceInfo', this.deviceInfo);
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
uni.navigateTo({
url: '/pagesA/home/device/status/addProgram'
});
console.log('页面跳转');
},
showAddDefaultModal() {
this.showAddDefault = true;
},
async confirmAddAudio() {
if (!this.checkOnline()) return;
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
try {
const mp3ListModel = this.deviceInfo.thingsModels.find(model => model.id ===
'103#mp3List');
if (!mp3ListModel) {
throw new Error('未找到 mp3_list 模型');
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +08:00
const jsonStr = mp3ListModel.shadow.replace('JSON=', '');
const data = JSON.parse(jsonStr);
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
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;
}
});
}
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
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}`
}
}
};
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
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
};
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
this.showAddAudio = false;
2025-06-26 14:55:08 +08:00
uni.showToast({
2025-07-03 08:57:13 +08:00
title: '添加成功',
icon: 'success'
});
} catch (error) {
console.error('添加音频失败:', error);
uni.showToast({
title: '添加失败: ' + error.message,
2025-06-26 14:55:08 +08:00
icon: 'none'
});
}
2025-07-03 08:57:13 +08:00
},
async confirmAddDefault() {
if (!this.checkOnline()) return;
if (!this.newDefault.startTime || !this.newDefault.endTime || !this.newDefault.audioFile) {
2025-06-26 14:55:08 +08:00
uni.showToast({
2025-07-03 08:57:13 +08:00
title: '请填写完整信息',
2025-06-26 14:55:08 +08:00
icon: 'none'
});
return;
}
2025-07-03 08:57:13 +08:00
if (this.newDefault.radarEnabled) {
if (!this.newDefault.minSpeed || !this.newDefault.maxSpeed) {
uni.showToast({
title: '请填写速度范围',
icon: 'none'
});
return;
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +08:00
if (parseInt(this.newDefault.minSpeed) >= parseInt(this.newDefault.maxSpeed)) {
uni.showToast({
title: '最小速度必须小于最大速度',
icon: 'none'
});
return;
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-03 08:57:13 +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);
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
if (!data.sound_card) {
data.sound_card = {};
}
if (!data.sound_card.play_list) {
data.sound_card.play_list = [];
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +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-03 08:57:13 +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;
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
let weekValue = 0;
this.newDefault.repeatDays.forEach(day => {
weekValue |= (1 << day);
2025-06-26 14:55:08 +08:00
});
2025-07-03 08:57:13 +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-06-26 14:55:08 +08:00
};
2025-07-03 08:57:13 +08:00
data.sound_card.play_list.push(newPlayItem);
playListModel.shadow = 'JSON=' + JSON.stringify(data);
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
};
} catch (error) {
console.error('发送添加命令失败:', error);
uni.showToast({
title: '添加失败',
icon: 'none'
});
}
2025-06-26 14:55:08 +08:00
} catch (error) {
2025-07-03 08:57:13 +08:00
console.error('解析或更新播放列表失败:', error);
2025-06-26 14:55:08 +08:00
uni.showToast({
title: '添加失败',
icon: 'none'
});
}
2025-07-03 08:57:13 +08:00
}
},
async deleteAudio(index) {
if (!this.checkOnline()) return;
try {
uni.showModal({
title: '提示',
content: '确定要删除该音频吗?',
cancelText: '取消',
confirmText: '确定',
success: async (res) => {
if (res.confirm) {
const mp3ListModel = this.deviceInfo.thingsModels.find(
model =>
model
.id === '103#mp3List');
if (mp3ListModel && mp3ListModel.shadow) {
try {
const jsonStr = mp3ListModel.shadow.replace(
'JSON=',
'');
const data = JSON.parse(jsonStr);
if (data.sound_card && data.sound_card.mp3_list) {
data.sound_card.mp3_list.splice(index, 1);
mp3ListModel.shadow = 'JSON=' + JSON.stringify(
data);
await this.mqttPublish(this.device,
mp3ListModel);
this.audioList.splice(index, 1);
uni.showToast({
title: '删除成功',
icon: 'success'
});
}
} catch (error) {
console.error('删除音频失败:', error);
uni.showToast({
title: '删除失败: ' + error.message,
icon: 'none'
});
}
}
}
}
});
2025-06-26 14:55:08 +08:00
} catch (error) {
2025-07-03 08:57:13 +08:00
console.error('删除音频失败:', error);
2025-06-26 14:55:08 +08:00
uni.showToast({
2025-07-03 08:57:13 +08:00
title: '删除失败: ' + error.message,
2025-06-26 14:55:08 +08:00
icon: 'none'
});
}
2025-07-03 08:57:13 +08:00
},
deleteDefault(index) {
2025-06-26 14:55:08 +08:00
uni.showModal({
title: '提示',
2025-07-03 08:57:13 +08:00
content: '确认删除该播放项吗?',
2025-06-26 14:55:08 +08:00
cancelText: '取消',
confirmText: '确定',
success: async (res) => {
if (res.confirm) {
2025-07-03 08:57:13 +08:00
const playListModel = this.deviceInfo.thingsModels.find(
2025-06-26 14:55:08 +08:00
model =>
2025-07-03 08:57:13 +08:00
model.id ===
'103#playList');
if (playListModel) {
2025-06-26 14:55:08 +08:00
try {
2025-07-03 08:57:13 +08:00
const jsonStr = playListModel.shadow.replace('JSON=',
2025-06-26 14:55:08 +08:00
'');
const data = JSON.parse(jsonStr);
2025-07-03 08:57:13 +08:00
if (data.sound_card && data.sound_card.play_list) {
data.sound_card.play_list.splice(index, 1);
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
data.sound_card.play_list.forEach((item,
index) => {
item.play.num = index + 1;
});
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
playListModel.shadow = 'JSON=' + JSON.stringify(
data);
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
try {
await this.mqttPublish(this.device,
playListModel);
uni.showToast({
title: '删除成功',
icon: 'success'
});
} catch (error) {
console.error('发送删除命令失败:', error);
uni.showToast({
title: '删除失败',
icon: 'none'
});
}
2025-06-26 14:55:08 +08:00
}
} catch (error) {
2025-07-03 08:57:13 +08:00
console.error('解析或更新播放列表失败:', error);
2025-06-26 14:55:08 +08:00
uni.showToast({
2025-07-03 08:57:13 +08:00
title: '删除失败',
2025-06-26 14:55:08 +08:00
icon: 'none'
});
}
}
}
}
});
2025-07-03 08:57:13 +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;
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
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 playEnModel = this.device.thingsModels.find(item => item.id ===
'play_en');
if (playEnModel) {
playEnModel.shadow = this.audioEnabled ? '1' : '0';
await this.mqttPublish(this.device, playEnModel);
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +08:00
} catch (error) {
console.error('切换音频开关失败:', error);
uni.showToast({
title: '操作失败: ' + error.message,
icon: 'none'
});
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +08:00
},
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];
},
updateBasicSettings() {
if (!this.deviceInfo.thingsModels) return;
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
const playEnModel = this.deviceInfo.thingsModels.find(model => model.id ===
'103#playEn');
2025-06-26 14:55:08 +08:00
if (playEnModel) {
2025-07-03 08:57:13 +08:00
this.audioEnabled = playEnModel.shadow === '1';
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +08:00
const volumeModel = this.deviceInfo.thingsModels.find(model => model.id ===
'103#volume');
if (volumeModel) {
this.volume = parseInt(volumeModel.shadow) || 50;
}
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
const mp3ListModel = this.deviceInfo.thingsModels.find(model => model.id ===
'103#mp3List');
if (mp3ListModel && mp3ListModel.shadow) {
try {
const jsonStr = mp3ListModel.shadow.replace('JSON=', '');
const data = JSON.parse(jsonStr);
if (data && data.mp3_list) {
this.audioList = data.mp3_list.map((item, index) => {
const name = item.split('_')[1] || item;
return {
id: index + 1,
name: name
};
});
}
} catch (error) {
console.error('解析音频列表失败:', error);
2025-06-26 14:55:08 +08:00
}
}
2025-07-03 08:57:13 +08:00
const playListModel = this.deviceInfo.thingsModels.find(model => model.id ===
'103#playList');
if (playListModel && playListModel.shadow) {
try {
const jsonStr = playListModel.shadow.replace('JSON=', '');
const data = JSON.parse(jsonStr);
if (data && data.play_list) {
this.defaultList = data.play_list.map((item, index) => {
const beginTime = this.formatSecondsToTime(item.time
.begin);
const endTime = this.formatSecondsToTime(item.time.end);
const weekdays = this.convertWeekToArray(item.time.week);
return {
id: index + 1,
name: item.play.filename,
playTime: `${beginTime} - ${endTime}`,
weekdays: weekdays.join(', '),
radarEnabled: item.speed.en === 1,
status: item.play.en === 1 ? '启用' : '禁用',
radarSpeed: item.speed.en === 1 ?
`${item.speed.min}-${item.speed.max}km/h` : ''
};
});
}
} catch (error) {
console.error('解析播放列表失败:', error);
2025-06-26 14:55:08 +08:00
}
}
2025-07-03 08:57:13 +08:00
},
formatSecondsToTime(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
},
convertWeekToArray(week) {
const weekdays = [];
for (let i = 0; i < 7; i++) {
if (week & (1 << i)) {
weekdays.push(this.weekDays[i]);
}
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +08:00
return weekdays;
},
async handleStatusChange(index, value) {
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-03 08:57:13 +08:00
if (data.sound_card && data.sound_card.play_list) {
data.sound_card.play_list[index].play.en = value === '启用' ? 1 : 0;
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
playListModel.shadow = 'JSON=' + JSON.stringify(data);
2025-06-26 14:55:08 +08:00
2025-07-03 08:57:13 +08:00
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 === '启用' ? '禁用' : '启用';
}
}
} catch (error) {
console.error('解析或更新播放列表失败:', error);
uni.showToast({
title: '更新失败',
icon: 'none'
});
this.defaultList[index].status = value === '启用' ? '禁用' : '启用';
}
}
},
deleteRecording(index) {
uni.showModal({
title: '提示',
content: '确定要删除该录音吗?',
cancelText: '取消',
confirmText: '确定',
success: (res) => {
if (res.confirm) {
this.recordings.splice(index, 1);
2025-06-26 14:55:08 +08:00
uni.showToast({
2025-07-03 08:57:13 +08:00
title: '删除成功',
2025-06-26 14:55:08 +08:00
icon: 'success'
});
}
}
2025-07-03 08:57:13 +08:00
});
},
// testOpenPicker(type) {
// this.pickerShow[type] = true;
// console.log('测试按钮pickerShow.' + type + ' =', this.pickerShow[type]);
// },
// 处理从 addProgram 页面传来的节目数据
async handleProgramData(data) {
try {
console.log('收到节目数据:', data);
console.log('设备模型列表:', this.device.thingsModels);
// 检查设备是否在线
if (!this.checkOnline()) return;
// 根据实际的设备模型ID来处理数据
// 这里需要根据你的设备实际模型来调整
// 示例1更新显示参数模型
const displayParamsModel = this.device.thingsModels.find(model =>
model.id === 'displayParams' || model.id === '103#displayParams'
);
if (displayParamsModel) {
const displayData = {
mode: data.form.mode,
duration: parseInt(data.form.duration) || 10,
zones: data.form.zones.map(zone => ({
playType: zone.playType,
displayText: zone.displayText,
font: zone.font,
fontSize: zone.fontSize,
fontColor: zone.fontColor,
effect: zone.effect,
speed: zone.speed,
stayTime: zone.stayTime,
hAlign: zone.hAlign,
vAlign: zone.vAlign,
x: parseInt(zone.x) || 0,
y: parseInt(zone.y) || 0,
width: parseInt(zone.width) || 32,
height: parseInt(zone.height) || 64
}))
};
displayParamsModel.shadow = JSON.stringify(displayData);
await this.mqttPublish(this.device, displayParamsModel);
}
// 示例2如果有专门的节目模型
const programModel = this.device.thingsModels.find(model =>
model.id === 'program' || model.id === '103#program'
);
if (programModel) {
programModel.shadow = JSON.stringify(data.form);
await this.mqttPublish(this.device, programModel);
}
// 示例3更新屏幕参数
const screenParamsModel = this.device.thingsModels.find(model =>
model.id === 'screenParams' || model.id === '103#screenParams'
);
if (screenParamsModel) {
const screenData = {
template: this.screenParams.template,
screenRotate: this.screenParams.screenRotate,
oePolarity: this.screenParams.oePolarity,
dataPolarity: this.screenParams.dataPolarity,
width: this.screenParams.width,
height: this.screenParams.height,
program: data.form
};
screenParamsModel.shadow = JSON.stringify(screenData);
await this.mqttPublish(this.device, screenParamsModel);
}
uni.showToast({
title: '节目设置成功',
icon: 'success'
});
// 刷新基础设置
this.updateBasicSettings();
2025-06-26 14:55:08 +08:00
} catch (error) {
2025-07-03 08:57:13 +08:00
console.error('处理节目数据失败:', error);
2025-06-26 14:55:08 +08:00
uni.showToast({
2025-07-03 08:57:13 +08:00
title: '设置失败: ' + error.message,
2025-06-26 14:55:08 +08:00
icon: 'none'
});
}
2025-07-03 08:57:13 +08:00
},
2025-06-26 14:55:08 +08:00
},
2025-07-03 08:57:13 +08:00
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 : '';
}
2025-06-26 14:55:08 +08:00
}
2025-07-03 08:57:13 +08:00
};
2025-06-26 14:55:08 +08:00
</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; }
&__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;
}
.audio-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx 0;
border-bottom: 1rpx solid #f5f5f5;
&: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;
}
}
.audio-actions {
display: flex;
gap: 32rpx;
}
}
}
}
2025-06-26 14:55:08 +08:00
</style>