2025-06-26 14:55:08 +08:00

1406 lines
34 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="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">
<image src="https://iot-xcwl.cn/doc/photo/brightness.png" mode="aspectFit" class="volume-svg">
</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>
<image src="https://iot-xcwl.cn/doc/photo/add.svg" mode="aspectFit" class="add-icon"
@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>
<!-- 虚拟遥控器 -->
<view class="card default-list">
<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>
</view>
<!-- 开机参数 -->
<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>
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: {
template: false,
screenRotate: false
},
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'
}
],
};
},
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();
},
beforeDestroy() {
if (this.recordingTimer) {
clearInterval(this.recordingTimer);
this.recordingTimer = null;
}
},
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() {
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);
}
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);
uni.showToast({
title: '操作失败: ' + error.message,
icon: 'none'
});
}
},
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;
}
}
} 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) {
break;
}
}
} 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;
}
}
}
} else {
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;
break;
}
}
}
}
};
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;
}
} else {
if (this.deviceInfo.chartList[k].id == message.message[j].id) {
this.deviceInfo.chartList[k].shadow = message.message[j].value;
isComplete = true;
break;
}
}
if (isComplete) {
break;
}
};
}
}
this.updateBasicSettings();
}
});
},
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');
}
},
showAddAudioModal() {
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) {
uni.showToast({
title: '请填写速度范围',
icon: 'none'
});
return;
}
if (parseInt(this.newDefault.minSpeed) >= parseInt(this.newDefault.maxSpeed)) {
uni.showToast({
title: '最小速度必须小于最大速度',
icon: 'none'
});
return;
}
}
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 = {};
}
if (!data.sound_card.play_list) {
data.sound_card.play_list = [];
}
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));
}
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);
});
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
}
};
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'
});
}
} catch (error) {
console.error('解析或更新播放列表失败:', error);
uni.showToast({
title: '添加失败',
icon: 'none'
});
}
}
},
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'
});
}
}
}
}
});
} 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);
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'
});
}
}
}
}
});
},
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 playEnModel = this.device.thingsModels.find(item => item.id ===
'play_en');
if (playEnModel) {
playEnModel.shadow = this.audioEnabled ? '1' : '0';
await this.mqttPublish(this.device, playEnModel);
}
} 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];
},
updateBasicSettings() {
if (!this.deviceInfo.thingsModels) return;
const playEnModel = this.deviceInfo.thingsModels.find(model => model.id ===
'103#playEn');
if (playEnModel) {
this.audioEnabled = playEnModel.shadow === '1';
}
const volumeModel = this.deviceInfo.thingsModels.find(model => model.id ===
'103#volume');
if (volumeModel) {
this.volume = parseInt(volumeModel.shadow) || 50;
}
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);
}
}
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);
}
}
},
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]);
}
}
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);
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 === '启用' ? '禁用' : '启用';
}
}
} 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);
uni.showToast({
title: '删除成功',
icon: 'success'
});
}
}
});
},
// testOpenPicker(type) {
// this.pickerShow[type] = true;
// console.log('测试按钮pickerShow.' + type + ' =', this.pickerShow[type]);
// },
},
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>
.status-titletop {
font-weight: bold;
font-size: 15px;
color: #333333;
line-height: 42rpx;
text-align: left;
padding: 15rpx 28rpx;
background-color: #eef6ff;
border-bottom: 1rpx solid #dceaff;
}
.voice-control {
padding: 24rpx;
background-color: #f5f7fa;
min-height: 100vh;
font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
.card {
background-color: #fff;
border-radius: 16rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
overflow: hidden;
transition: all 0.3s ease;
&:hover {
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.1);
}
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
padding: 24rpx;
border-bottom: 1rpx solid #f0f2f5;
display: flex;
align-items: center;
background-color: #f9fbfd;
.add-icon {
width: 40rpx;
height: 40rpx;
margin-left: auto;
cursor: pointer;
transition: transform 0.2s;
&:active {
transform: scale(0.9);
}
}
}
.info-content {
padding: 24rpx;
}
/* 音量控制样式 */
.volume-slider {
display: flex;
align-items: center;
gap: 20rpx;
margin-bottom: 24rpx;
padding: 20rpx;
background-color: #f8fafc;
border-radius: 12rpx;
border: 1rpx solid #eaeef5;
.volume-icon {
width: 48rpx;
height: 48rpx;
display: flex;
justify-content: center;
align-items: center;
.volume-svg {
width: 100%;
height: 100%;
}
}
.slider-container {
flex: 1;
position: relative;
.volume-marks {
display: flex;
justify-content: space-between;
margin-top: 12rpx;
padding: 0 8rpx;
text {
font-size: 24rpx;
color: #909399;
font-weight: 500;
}
}
}
.volume-value {
min-width: 80rpx;
text-align: right;
color: #2979ff;
font-size: 28rpx;
font-weight: 600;
}
}
/* 开关样式 */
.audio-switch {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx;
background-color: #f8fafc;
border-radius: 12rpx;
margin-bottom: 24rpx;
border: 1rpx solid #eaeef5;
text {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
}
/* 屏幕参数表单 */
.screen-params {
.screen-params-form {
padding: 20rpx 24rpx;
::v-deep .u-form-item {
margin-bottom: 20rpx;
&__body {
padding: 20rpx 0;
border-bottom: 1rpx solid #f0f2f5;
&__left {
font-size: 28rpx;
color: #606266;
font-weight: 500;
}
&__right {
.u-input {
font-size: 28rpx;
color: #333;
text-align: right;
}
}
}
}
}
}
/* 列表样式 */
.audio-list,
.default-list {
.list-container {
padding: 0 24rpx 24rpx;
}
.empty-tip {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60rpx 0;
color: #c0c4cc;
font-size: 28rpx;
gap: 20rpx;
opacity: 0.8;
transition: all 0.3s ease;
&:active {
transform: scale(0.98);
opacity: 0.6;
}
}
.audio-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 0;
border-bottom: 1rpx solid #f5f5f5;
transition: all 0.2s ease;
&:last-child {
border-bottom: none;
}
&:active {
background-color: #f8f8f8;
}
.audio-info {
display: flex;
align-items: center;
gap: 16rpx;
flex: 1;
overflow: hidden;
.audio-name {
font-size: 28rpx;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.audio-actions {
display: flex;
align-items: center;
gap: 24rpx;
margin-left: 16rpx;
.audio-duration {
font-size: 26rpx;
color: #909399;
}
}
}
}
/* 清除屏幕按钮 */
.clear-screen-btn-wrap {
display: flex;
justify-content: center;
margin-top: 24rpx;
::v-deep .u-button {
width: 60%;
height: 80rpx;
font-size: 28rpx;
}
}
/* 弹窗样式 */
.add-audio-modal {
width: 80vw;
max-width: 600rpx;
padding: 0;
border-radius: 16rpx;
background-color: #fff;
overflow: hidden;
.modal-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
padding: 28rpx;
text-align: center;
border-bottom: 1rpx solid #f5f5f5;
}
.modal-content {
padding: 0 28rpx;
margin: 20rpx 0;
max-height: 60vh;
overflow-y: auto;
::v-deep .u-form-item {
padding: 20rpx 0;
&__body {
padding: 0;
&__left {
width: 160rpx;
font-size: 28rpx;
color: #606266;
}
&__right {
.u-input {
font-size: 28rpx;
color: #333;
}
}
}
}
.slider-with-value {
display: flex;
align-items: center;
width: 100%;
.custom-slider {
flex: 1;
margin-right: 20rpx;
}
}
::v-deep .u-radio-group {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
}
}
.modal-footer {
display: flex;
justify-content: space-between;
padding: 16rpx 28rpx 28rpx;
border-top: 1rpx solid #f5f5f5;
.u-button {
width: 48%;
height: 80rpx;
font-size: 28rpx;
border-radius: 40rpx;
}
}
}
/* 周选择器 */
.week-picker {
display: flex;
flex-wrap: wrap;
gap: 10rpx;
padding: 10rpx 0;
.week-item {
width: 72rpx;
height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 36rpx;
background-color: #f5f5f5;
font-size: 28rpx;
color: #666;
transition: all 0.3s ease;
cursor: pointer;
&.active {
background-color: #2979ff;
color: #fff;
font-weight: 500;
}
}
}
/* 速度范围 */
.speed-range {
display: flex;
align-items: center;
gap: 10rpx;
.u-input {
flex: 1;
}
.separator {
color: #666;
padding: 0 10rpx;
}
.unit {
color: #666;
font-size: 26rpx;
margin-left: 10rpx;
}
}
/* 选择器值显示 */
.picker-value {
height: 80rpx;
line-height: 80rpx;
font-size: 28rpx;
color: #333;
padding: 0 20rpx;
background-color: #f9f9f9;
border-radius: 8rpx;
}
/* 测试按钮 */
.test-card {
.test-buttons {
display: flex;
justify-content: space-between;
padding: 20rpx;
}
}
}
</style>