+
语音播报
@@ -81,19 +81,19 @@
-
+
上传语音
-
+
文字转语音
-
+
所有语音
@@ -114,19 +114,24 @@
📍
-
经纬度 {{ deviceDetail && deviceDetail.longitude ?
- Number(deviceDetail.longitude).toFixed(4) : '无' }}
- {{ deviceDetail && deviceDetail.latitude ? Number(deviceDetail.latitude).toFixed(4)
- : '无' }}
+
经纬度
+
{{ deviceDetail && deviceDetail.longitude ?
+ Number(deviceDetail.longitude).toFixed(4) : '0.00' }}
+ {{ deviceDetail && deviceDetail.latitude ?
+ Number(deviceDetail.latitude).toFixed(4)
+ : '0.00' }}
+
+
-
-
地址 {{ deviceDetail.address || "未获取到地址" }}
+
+
{{ deviceDetail.address || "未获取到地址" }}
-
+
调节
@@ -135,7 +140,7 @@
%
-
保存
@@ -145,7 +150,7 @@
%
-
保存
+ 保存
@@ -155,7 +160,7 @@
%
-
保存
+ 保存
@@ -322,22 +327,16 @@ import { useRoute, useRouter } from 'vue-router';
import { useMqtt } from '@/utils/mqtt';
import api from '@/api/controlCenter/controlPanel/100J';
import { DeviceDetail, LightMode } from '@/api/controlCenter/controlPanel/types';
-import { getDeviceStatus } from '@/utils/function';
// 路由和实例
const route = useRoute();
const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-import request from '@/utils/request';
// 导入图片资源
-import closeDefault from '@/assets/images/close.png';
-import closeActive from '@/assets/images/close_HL.png';
-import rb from '@/assets/images/rb.png';
-import rbAc from '@/assets/images/rbAc.png';
-import sg from '@/assets/images/sg.png';
-import sgAc from '@/assets/images/sgAc.png';
-import { object } from 'vue-types';
-
+import rb from '@/assets/images/rg1.png';
+import rbAc from '@/assets/images/rg1Ac.png';
+import sg from '@/assets/images/hb.png';
+import sgAc from '@/assets/images/hbAc.png';
// 基础状态
const forceAlarmLoading = ref(false);
const lightModesLoading = ref(false);
@@ -345,26 +344,17 @@ const centerDialogVisible = ref(false);
const currentVoiceId = ref(''); // 当前选中的语音ID
const voiceList = ref
([]); // 语音列表
-// MQTT相关
-const {
- connected,
- connect,
- subscribe,
- onConnect,
- onError,
- onMessage,
- disconnect
-} = useMqtt();
-
// ====================== 录制语音 ======================
+import Recorder from 'recorder-core';
+// 引入 MP3 编码器(核心:生成标准 MP3)
+import 'recorder-core/src/engine/mp3';
+import 'recorder-core/src/engine/mp3-engine';
const recordVoiceDialog = ref(false);
const isRecording = ref(false);
const recordDuration = ref(0);
-const recordedBlob = ref(null);
-const audioChunks = ref([]);
-const mediaRecorder = ref(null);
+const recorderIns = ref(null); // Recorder 实例
let recordTimer: NodeJS.Timeout | null = null;
-let audioStream: MediaStream | null = null;
+const recordedBlob = ref()
const formatTime = (seconds: number) => {
const min = String(Math.floor(seconds / 60)).padStart(2, '0');
@@ -372,80 +362,118 @@ const formatTime = (seconds: number) => {
return `${min}:${sec}`;
};
+// ------------- 开始录制语音上传 -------------
const startRecordVoice = async () => {
try {
- if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
- proxy?.$modal.msgError('当前浏览器不支持麦克风录制,请使用Chrome/Edge/Firefox');
- return;
- }
- audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });
- mediaRecorder.value = new MediaRecorder(audioStream);
- audioChunks.value = [];
- mediaRecorder.value.ondataavailable = (e) => audioChunks.value.push(e.data);
- mediaRecorder.value.onstop = () => {
- recordedBlob.value = new Blob(audioChunks.value, { type: 'audio/mp3' });
- if (audioStream) {
- audioStream.getTracks().forEach(track => track.stop());
- audioStream = null;
+ // 1. 重置状态
+ resetRecord();
+ recorderIns.value = Recorder({
+ type: 'mp3', // 直接录制为 MP3
+ sampleRate: 44100, // 标准采样率
+ bitRate: 128, // 128kbps(微信兼容)
+ onProcess: (buffers: any, power: number, bufferDuration: number, bufferSampleRate: number) => {
}
- };
- mediaRecorder.value.start();
- isRecording.value = true;
- recordDuration.value = 0;
- recordTimer = setInterval(() => recordDuration.value++, 1000);
+ });
+ recorderIns.value.open(() => {
+ // 权限申请成功,开始录制
+ recorderIns.value.start();
+ isRecording.value = true;
+ recordDuration.value = 0;
+ recordTimer = setInterval(() => recordDuration.value++, 1000);
+ // proxy?.$modal.msgSuccess('开始录制语音...');
+ }, (err: any) => {
+ // 权限申请失败
+ proxy?.$modal.msgError(`麦克风权限申请失败`);
+ resetRecord();
+ });
} catch (err) {
- proxy?.$modal.msgError('麦克风权限申请失败');
- if (audioStream) {
- audioStream.getTracks().forEach(track => track.stop());
- audioStream = null;
- }
+ proxy?.$modal.msgError('录制异常,请重试');
+ resetRecord();
}
};
+// ------------- 停止录制 -------------
const stopRecordVoice = () => {
- if (!mediaRecorder.value || !isRecording.value) return;
- mediaRecorder.value.stop();
- isRecording.value = false;
- if (recordTimer) {
- clearInterval(recordTimer);
- recordTimer = null;
+ if (!recorderIns.value || !isRecording.value) {
+ proxy?.$modal.msgWarning('未在录制状态');
+ return;
+ }
+
+ try {
+ // 停止录制并获取 MP3 Blob
+ recorderIns.value.stop((blob: Blob, duration: number) => {
+ // blob 是标准 MP3 格式(微信100%兼容)
+ recordedBlob.value = blob;
+ console.log('标准 MP3 生成成功:', blob);
+
+ isRecording.value = false;
+ if (recordTimer) clearInterval(recordTimer);
+ proxy?.$modal.msgSuccess(`录制完成,时长:${Math.round(duration)}秒`);
+
+ // 释放资源
+ recorderIns.value.close();
+ }, (err: any) => {
+ // proxy?.$modal.msgError(`停止录制失败:${err.msg}`);
+ resetRecord();
+ });
+ } catch (err) {
+ proxy?.$modal.msgError('停止录制异常');
+ resetRecord();
}
};
+// ------------- 重置录制状态 -------------
const resetRecord = () => {
isRecording.value = false;
recordDuration.value = 0;
recordedBlob.value = null;
- audioChunks.value = [];
if (recordTimer) {
clearInterval(recordTimer);
recordTimer = null;
}
+ // 释放 Recorder 资源
+ if (recorderIns.value) {
+ recorderIns.value.close();
+ recorderIns.value = null;
+ }
};
-
+// ------------- 保存/上传标准 MP3 文件 -------------
const saveRecordVoice = () => {
- if (!recordedBlob.value) {
- proxy?.$modal.msgWarning('暂无录制的语音文件');
+ // 1. 校验 MP3 Blob 是否有效
+ if (!recordedBlob.value || recordedBlob.value.size === 0) {
+ proxy?.$modal.msgWarning('暂无有效录制的 MP3 文件');
return;
}
- const formData = new FormData();
- formData.append('file', recordedBlob.value, `record_${new Date().getTime()}.mp3`);
- formData.append('deviceId', route.params.deviceId as string);
- proxy?.$modal.loading('保存中...');
- api.uploadAudioToOss(formData).then(res => {
- proxy?.$modal.closeLoading();
- if (res.code === 200) {
- proxy?.$modal.msgSuccess('保存成功');
- recordVoiceDialog.value = false;
- queryAudioFileInfo();
- resetRecord();
- } else {
- proxy?.$modal.msgError('保存语音失败:' + res.msg);
- }
- }).catch(err => {
- proxy?.$modal.closeLoading();
- proxy?.$modal.msgError('保存语音失败');
- });
+ // 2. 校验 deviceId
+ const deviceId = route?.params?.deviceId;
+ if (!deviceId || typeof deviceId !== 'string') {
+ return;
+ }
+ try {
+ // 3. 构建 FormData(上传标准 MP3)
+ const formData = new FormData();
+ formData.append('deviceId', deviceId);
+ const fileName = `${new Date().getTime()}.mp3`;
+ formData.append('file', recordedBlob.value, fileName);
+ // 4. 上传接口
+ proxy?.$modal.loading('保存中...');
+ api.uploadAudioToOss(formData).then(res => {
+ proxy?.$modal.closeLoading();
+ if (res.code === 200) {
+ proxy?.$modal.msgSuccess('保存成功');
+ recordVoiceDialog.value = false;
+ queryAudioFileInfo();
+ resetRecord();
+ } else {
+ proxy?.$modal.msgError('保存语音失败');
+ }
+ }).catch(err => {
+ proxy?.$modal.closeLoading();
+ proxy?.$modal.msgError('保存语音失败');
+ });
+ } catch (err) {
+
+ }
};
const handleRecordVoice = () => {
@@ -638,7 +666,7 @@ const deleteVoiceById = async (fileId: string) => {
const renameVoice = async (item: any) => {
const { value } = await ElMessageBox.prompt('请输入新名称', '重命名', { inputPlaceholder: '请输入新名称' });
if (value) {
- const res = await api.videRenameAudioFile({ deviceId: route.params.deviceId, fileId:item.fileId, fileName: value });
+ const res = await api.videRenameAudioFile({ deviceId: route.params.deviceId, fileId: item.fileId, fileName: value });
if (res.code === 200) {
proxy?.$modal.msgSuccess(res.msg);
queryAudioFileInfo();
@@ -884,7 +912,7 @@ const showClose = async () => {
if (res.code === 200) {
deviceDetail.value.voiceStrobeAlarm = 0;
proxy?.$modal.msgSuccess(res.msg);
- await getList();
+ //await getList();
}
} catch (error: any) {
proxy?.$modal.msgError(error.msg);
@@ -908,7 +936,7 @@ const forceAlarm = async () => {
deviceDetail.value.voiceStrobeAlarm = 1;
proxy?.$modal.msgSuccess(res.msg);
- await getList();
+ // await getList();
}
} catch (error: any) {
proxy?.$modal.msgError(error.msg);
@@ -939,7 +967,7 @@ const playCurrentVoice = async () => {
proxy?.$modal.msgError(res.msg);
}
} catch (err: any) {
- proxy?.$modal.msgError(err.msg);
+ // proxy?.$modal.msgError(err.msg);
}
}
// 2. 非报警中场景:单纯播放/暂停语音
@@ -958,7 +986,7 @@ const playCurrentVoice = async () => {
proxy?.$modal.msgError(res.msg);
}
} catch (err: any) {
- proxy?.$modal.msgError(err.msg);
+ // proxy?.$modal.msgError(err.msg);
}
}
};
@@ -1018,18 +1046,27 @@ onUnmounted(() => {
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 34, 96, 0.1);
background: white;
- padding: 20px;
+ padding: 1px 20px;
border: 1px solid #ebeef5;
- min-height: 730px;
+ min-height: 700px;
}
.content-card1 {
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 34, 96, 0.1);
background: white;
- padding: 20px;
+ padding: 1px 20px;
border: 1px solid #ebeef5;
- min-height: 250px;
+ height: 440px;
+ }
+
+ .content-card2 {
+ border-radius: 8px;
+ box-shadow: 0 2px 12px rgba(0, 34, 96, 0.1);
+ background: white;
+ padding: 1px 20px;
+ border: 1px solid #ebeef5;
+ height: 250px;
}
.content-card_gps {
@@ -1056,11 +1093,12 @@ onUnmounted(() => {
.lacatin_gps {
height: 70px;
- border-radius: 4px;
- background: rgba(247, 248, 252, 1);
+ background: #F7F8FC;
+ border-radius: 4px 4px 4px 4px;
width: 100%;
padding: 10px;
word-break: break-all;
+ margin-top: 15px;
}
.mode_2 {
@@ -1069,8 +1107,17 @@ onUnmounted(() => {
border-radius: 8px;
width: 105px;
text-align: center;
+
+
}
+ .active {
+ border-radius: 4px 4px 4px 4px;
+ border: 1px solid #027CFB;
+ background: rgba(2, 124, 251, 0.06);
+ }
+
+
.mode-card {
display: flex;
flex-direction: column;
@@ -1142,7 +1189,7 @@ onUnmounted(() => {
.save-btn {
padding: 6px 20px;
border-radius: 29px;
- background: rgba(2, 124, 251, 1);
+ // background: rgba(2, 124, 251, 1);
border: none;
}
@@ -1168,6 +1215,12 @@ onUnmounted(() => {
}
.location-info {
+ .location-item1 {
+ display: flex;
+ justify-content: space-between;
+ color: #38404F;
+ }
+
.location-item {
display: flex;
align-items: center;
@@ -1235,7 +1288,7 @@ onUnmounted(() => {
flex-direction: column;
align-items: center;
padding: 10px;
- border: 1px solid #dcdfe6;
+ // border: 1px solid #dcdfe6;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
@@ -1246,14 +1299,19 @@ onUnmounted(() => {
}
.el-icon {
- font-size: 20px;
+ font-size: 24px;
margin-bottom: 5px;
- color: #409eff;
+ color: #027CFB;
+ border: 1px solid rgba(2, 124, 251, 0.2);
+ border-radius: 6px 6px 6px 6px;
+ width: 37px;
+ height: 37px;
}
span {
- font-size: 12px;
- color: #606266;
+ font-size: 14px;
+ color: #027CFB;
+ margin-top: 5px;
}
}
}