xaznapp声卡

This commit is contained in:
1 2025-06-18 10:18:15 +08:00
parent a875eeba11
commit 179dce665a

View File

@ -18,7 +18,7 @@
</u--form>
</view>
</view>
<button @click="print()">测试打印</button>
<!-- <button @click="print()">测试打印</button -->
<!-- 基础信息 -->
<view class="card basic-info">
<view class="section-title">基础信息</view>
@ -80,7 +80,7 @@
<!-- 默认音频列表 -->
<view class="card default-list">
<view class="section-title">
<text>默认列表</text>
<text>播放列表</text>
<image src="https://iot-xcwl.cn/doc/photo/add.svg" mode="aspectFit" class="add-icon"
@click="showAddDefaultModal"></image>
</view>
@ -91,11 +91,20 @@
</view>
<view class="audio-item" v-for="(item, index) in defaultList" :key="index">
<view class="audio-info">
<u-icon name="star-fill" size="18" color="#ff9900"></u-icon>
<u-icon :name="item.status === '启用' ? 'star-fill' : 'star'" size="18"
:color="item.status === '启用' ? '#ff9900' : '#c0c4cc'"></u-icon>
<view class="audio-details">
<text class="audio-name">{{ item.name }}</text>
<view class="audio-meta">
<text class="time-info">{{ item.playTime }}</text>
<text class="week-info">{{ item.weekdays }}</text>
<text v-if="item.radarEnabled" class="radar-info">{{ item.radarSpeed }}</text>
</view>
</view>
</view>
<view class="audio-actions">
<text class="audio-duration">{{ item.duration }}</text>
<u-switch v-model="item.status" :active-value="'启用'" :inactive-value="'禁用'"
@change="(value) => handleStatusChange(index, value)" size="22"></u-switch>
<u-icon name="trash" size="18" color="#ff4d4f" @click="deleteDefault(index)"></u-icon>
</view>
</view>
@ -104,7 +113,6 @@
<!-- 远程喊话 -->
<view class="card remote-talk">
<view class="section-title">远程喊话</view>
<view class="talk-container">
<view class="talk-tip">按住按钮开始录音松开结束录音</view>
<view class="talk-button" :class="{ recording: isRecording }" @touchstart="startRecording"
@ -135,21 +143,25 @@
</u-form-item>
<u-form-item label="合成语速" prop="spd" borderBottom>
<view class="slider-with-value">
<u-slider v-model="newAudio.spd" :min="0" :max="15" :step="1" :showValue="true" class="custom-slider"></u-slider>
<u-slider v-model="newAudio.spd" :min="0" :max="15" :step="1" :showValue="true"
class="custom-slider"></u-slider>
</view>
</u-form-item>
<u-form-item label="合成音调" prop="pit" borderBottom>
<view class="slider-with-value">
<u-slider v-model="newAudio.pit" :min="0" :max="15" :step="1" :showValue="true" class="custom-slider"></u-slider>
<u-slider v-model="newAudio.pit" :min="0" :max="15" :step="1" :showValue="true"
class="custom-slider"></u-slider>
</view>
</u-form-item>
<u-form-item label="合成音量" prop="vol" borderBottom>
<view class="slider-with-value">
<u-slider v-model="newAudio.vol" :min="0" :max="15" :step="1" :showValue="true" class="custom-slider"></u-slider>
<u-slider v-model="newAudio.vol" :min="0" :max="15" :step="1" :showValue="true"
class="custom-slider"></u-slider>
</view>
</u-form-item>
<u-form-item label="合成文本" prop="text" borderBottom>
<u-input v-model="newAudio.text" type="textarea" placeholder="请输入合成文本" height="100"></u-input>
<u-input v-model="newAudio.text" type="textarea" placeholder="请输入合成文本"
height="100"></u-input>
</u-form-item>
</u-form>
</view>
@ -163,7 +175,7 @@
<!-- 添加默认音频弹窗 -->
<u-popup :show="showAddDefault" mode="center" @close="showAddDefault = false" round="8">
<view class="add-audio-modal">
<view class="modal-title">添加默认音频</view>
<view class="modal-title">添加播放列表</view>
<view class="modal-content">
<u-form :model="newDefault" ref="defaultForm">
<u-form-item label="开始时间" prop="startTime" borderBottom>
@ -757,13 +769,127 @@
});
}
},
confirmAddDefault() {
async confirmAddDefault() {
if (!this.checkOnline()) return;
//
if (!this.newDefault.startTime || !this.newDefault.endTime || !this.newDefault.audioFile) {
uni.showToast({
title: '默认音频添加成功',
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 === 'play_list');
if (playListModel) {
try {
const jsonStr = playListModel.shadow.replace('JSON=', '');
const data = JSON.parse(jsonStr);
// sound_card play_list
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));
}
// HH:MM
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);
// shadow
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;
@ -777,7 +903,8 @@
success: async (res) => {
if (res.confirm) {
// mp3_list
const mp3ListModel = this.deviceInfo.thingsModels.find(model => model.id === 'mp3_list');
const mp3ListModel = this.deviceInfo.thingsModels.find(model => model
.id === 'mp3_list');
if (mp3ListModel && mp3ListModel.shadow) {
try {
// JSON
@ -824,12 +951,53 @@
deleteDefault(index) {
uni.showModal({
title: '提示',
content: '确定要删除该默认音频吗?',
content: '确认删除该播放项吗?',
cancelText: '取消',
confirmText: '确定',
success: (res) => {
success: async (res) => {
if (res.confirm) {
this.defaultList.splice(index, 1);
const playListModel = this.deviceInfo.thingsModels.find(model => model.id ===
'play_list');
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;
});
// shadow
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'
});
}
}
}
}
});
@ -960,6 +1128,99 @@
} else {
console.log('no mp3ListModel or shadow is empty');
}
//
const playListModel = this.deviceInfo.thingsModels.find(model => model.id === 'play_list');
if (playListModel && playListModel.shadow) {
try {
const jsonStr = playListModel.shadow.replace('JSON=', '');
const data = JSON.parse(jsonStr);
if (data.sound_card && data.sound_card.play_list) {
this.defaultList = data.sound_card.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);
}
}
},
// (HH:MM)
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 === 'play_list');
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;
// shadow
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 === '启用' ? '禁用' : '启用';
}
}
}
},
computed: {
@ -1129,17 +1390,37 @@
.audio-info {
display: flex;
align-items: center;
align-items: flex-start;
gap: 16rpx;
flex: 1;
overflow: hidden;
.audio-details {
flex: 1;
overflow: hidden;
.audio-name {
font-size: 26rpx;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-bottom: 8rpx;
display: block;
}
.audio-meta {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
font-size: 22rpx;
color: #666;
.time-info,
.week-info,
.radar-info {
background-color: #f5f5f5;
padding: 4rpx 12rpx;
border-radius: 4rpx;
}
}
}
}
@ -1148,13 +1429,6 @@
align-items: center;
gap: 24rpx;
margin-left: 16rpx;
.audio-duration {
font-size: 24rpx;
color: #999;
min-width: 72rpx;
text-align: right;
}
}
}
}