Files
APP/pages/100J/audioManager/Recording.vue
2026-02-03 18:55:48 +08:00

1458 lines
37 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="maincontent contentBg">
<!-- 文字转语音 -->
<view v-if="Status.pageType==='Txt'">
<view>
<view class="importTxt fright" @click="importDoc">导入文档</view>
<view class="clear"></view>
</view>
<view class="text-content">
<view class="uni-textarea">
<textarea class="textarea" v-model="formData.txt" placeholder-class="placehoderClass"
placeholder="请输入要转换的文本" :auto-height="true" maxlength="100" cursor-color="#BBE600" />
</view>
<view class="line"></view>
<view class="footBtn">
<view class="fright convert" @click.stop="txtToAudio()">转换</view>
<view class="fright audioSett" @click.stop="formData.txt=''">清空</view>
<view class="clear"></view>
</view>
</view>
</view>
<!-- 获取到的语音信息 -->
<view class="cEdit"
:class="{'displayNone':(!AudioData.tempFilePath && Status.pageType!=='Txt')|| (AudioData.hexLength===0 && Status.pageType==='Txt')}">
<view class="audioRemark">
<view>
<view>
<input type="text" class="txtName" v-model="cEdit.name" placeholder="音频名称"
placeholder-class="placehoderClass" />
</view>
<view>
<text>{{cEdit.createTime}}</text>
<text>{{getFormatSeconds(cEdit.time,true)}}</text>
</view>
</view>
<view class="btnSave" v-if="Status.pageType!=='Txt'" @click.stop="uploadLuYin">保存</view>
</view>
<view class="slide" v-if="Status.pageType!=='Txt'">
<view class="circle" :style="{'left':PlayProgress}"></view>
<view class="line"></view>
<view class="fleft">
{{getFormatSeconds(cEdit.currTime)}}
</view>
<view class="fright">
{{getFormatSeconds(cEdit.time)}}
</view>
<view class="clear"></view>
</view>
<view class="control" v-if="Status.pageType!=='Txt'">
<view class="playControl">
<view class="seek" @click.stop="seek(-10)">
<image class="img" src="/static/images/common/prev.png" mode="aspectFit"></image>
</view>
<view class="play" @click="playToggle()">=
<image class="img"
:class="{'displayNone':Status.playState!=='ready' && Status.playState!=='pause'}"
src="/static/images/common/play.png" mode="aspectFit"></image>
<image class="img" :class="{'displayNone':!Status.playState || Status.playState!=='play'}"
src="/static/images/common/pause.png" mode="aspectFit"></image>
</view>
<view class="seek" @click.stop="seek(10)">
<image class="img" src="/static/images/common/next.png" mode="aspectFit"></image>
</view>
</view>
<view class="drop" v-if="Status.pageType!=='Txt'" @click.stop="drop()">
<uni-icons type="trash" size="30" color="#e03434"></uni-icons>
</view>
</view>
</view>
<view class="txtBtns" v-if="Status.pageType==='Txt'" :class="{'displayNone':AudioData.hexLength===0}">
<view class="btnSave fright" @click.stop="SaveTTS()">保存</view>
<view class="audioSett fright" @click.stop="CancelTTS">取消</view>
<view class="clear"></view>
</view>
<!-- 录音的提示 -->
<view v-if="Status.pageType==='Record'">
<view class="tips" :class="{'displayNone':Status.isRecord || AudioData.tempFilePath}">
点击下方红色录制按钮开始录音吐字清晰语句流畅
</view>
</view>
<!-- 录音选择文件操作 -->
<view class="footer" v-if="Status.pageType!=='Txt'">
<view class="opt">
<view class="recording" :class="{'displayNone':Status.pageType!=='Record'}">
<view class="recordTitle">语音录制{{Status.RecordName}}</view>
<view class="recordName" :class="{'visibilityHidden':!formatSecondsToDHMS}">
{{formatSecondsToDHMS?formatSecondsToDHMS:"00:00"}}
</view>
</view>
<view class="btnRecord center" @click.stop="toggleRecord()"
:class="{'displayNone':Status.pageType!=='Record'}">
<view class="start" :class="{'active':Status.isRecord}"></view>
</view>
<view class="btnRemark" :class="{'displayNone':Status.pageType!=='Record'}">
{{Status.isRecord?'录制中':'录制'}}
</view>
<view class="btnCheckFile center" @click.stop="checkFile()"
:class="{'displayNone':Status.pageType!=='File'}">
<view class="polygon center">
<uni-icons type="plusempty" size="45" color="#ffffff99"></uni-icons>
</view>
</view>
</view>
</view>
<!-- 自定义弹出层 -->
<MessagePopup :visible="Status.Pop.showPop" :type="Status.Pop.popType" :bgColor="Status.Pop.bgColor"
:borderColor="Status.Pop.borderColor" :textColor="Status.Pop.textColor"
:buttonBgColor="Status.Pop.buttonBgColor" :buttonTextColor="Status.Pop.buttonTextColor"
:iconUrl="Status.Pop.iconUrl" :message="Status.Pop.message" :buttonText="Status.Pop.buttonText"
@buttonClick="HidePop" :visiblePrompt="Status.Pop.visiblePrompt" :promptTitle="Status.Pop.promptTitle"
v-model="Status.Pop.modelValue" @closePop="closePop" :buttonCancelText="Status.Pop.buttonCancelText"
:showCancel="Status.Pop.showCancel" @cancelPop="closePop" :showSlot="Status.Pop.showSlot">
<view class="popup-prompt">
<input type="text" class="popup-prompt-input" placeholder="请输入名称" v-model="cEdit.name" />
</view>
</MessagePopup>
<global-loading ref="loading" />
<xe-upload ref="XeUpload" :options="uploadOptions" @callback="handleUploadCallback"></xe-upload>
</view>
</template>
<script>
var eventChannel = null;
var these = null;
var innerAudioContext = null;
var AudeoRecoder = null;
var audioTime = null;
var hexs = null;
import request, {
baseURL
} from '@/utils/request';
import {
showLoading,
hideLoading,
updateLoading
} from '@/utils/loading.js';
import Common from '@/utils/Common.js';
export default {
data() {
return {
Status: {
playState: '', //播放状态
pageType: '', //页面类型,用于区分录音、选择文件、文字转语音
pageValid: false, //页面状态,是否已销毁
recoderIntval: null, //录音的定时器
isRecord: false, //是否正在录音
RecordTime: null, //已录间的时长
ID: '',
Pop: { //弹出框的配置
showPop: false, //是否显示弹窗
popType: 'custom',
bgColor: '#383934bd',
borderColor: '#BBE600',
textColor: '#ffffffde',
buttonBgColor: '#BBE600',
buttonTextColor: '#232323DE',
iconUrl: '',
message: '您确定要这样做吗?',
buttonText: '确定',
clickEvt: '',
visiblePrompt: false,
promptTitle: '设备名称',
modelValue: '',
visibleClose: false,
okCallback: null,
buttonCancelText: '',
showCancel: false,
showSlot: false
},
},
cEdit: {
Id: "", //编号
name: "", //名称
createTime: "", //创建时间
localPath: "", //本地地址
webPath: "", //网络地址
statu: "", //状态,是否公开
isApply: "", //是否使用中
time: "", //音频总长度
currTime: '', //当前播放进度
type: ''
},
AudioData: {
tempFilePath: '',
hexLength: 0
},
uploadOptions: {
},
formData: {
txt: '祝我暴富,发财吧'
}
}
},
computed: {
formatSecondsToDHMS() {
let seconds = this.Status.RecordTime;
return this.getFormatSeconds(seconds);
},
PlayProgress() {
return this.cEdit.currTime / (this.cEdit.time === 0 ? 1 : this.cEdit.time) * 100 + '%';
}
},
onLoad(opt) {
eventChannel = this.getOpenerEventChannel();
these = this;
this.Status.pageType = opt.pageType;
console.log('id:', opt.id); // 输出123
these.Status.ID = opt.id
let title = ""
if (opt.pageType === 'Record') {
title = "录制语音";
} else if (opt.pageType === 'Txt') {
title = '文字转语音';
} else {
title = "文件上传";
}
uni.setNavigationBarTitle({
title: title
});
},
onUnload() {
this.destoryAudioPlayer();
clearInterval(audioTime);
eventChannel.emit('RecordOver', {
data: '从音频页面返回,刷新数据'
});
},
onBackPress(e) {
if (this.Status.isRecord) { //如果正在录音,提示是否放弃
this.showMsg("正在录音,是否放弃?", false, true, () => {
these.Status.pageValid = true;
these.stopLuYin();
uni.navigateBack();
});
return true;
} else {
if (this.AudioData.tempFilePath) {
this.showMsg("录音没有保存是否放弃?", false, true, () => {
these.Status.pageValid = true;
this.dropTmpFile();
this.AudioData.tempFilePath = "";
AudeoRecoder = null;
uni.navigateBack();
});
return true;
}
}
},
methods: {
CancelTTS() {
this.cEdit.name = '';
this.cEdit.Id = "";
this.cEdit.createTime = "";
this.cEdit.currTime = "";
this.cEdit.isApply = "";
this.cEdit.localPath = "";
this.cEdit.statu = "";
this.cEdit.time = "";
this.cEdit.webPath = "";
this.AudioData.hexLength = 0;
this.cEdit.type = this.Status.pageType;
hexs = [];
},
SaveTTS() {
if (!this.cEdit.name) {
this.showMsg("请输入语音名称");
return;
}
this.cEdit.localPath = "";
this.cEdit.Id = Common.guid();
this.cEdit.createTime = Common.DateFormat(new Date(), "yyyy年MM月dd日");
this.cEdit.isApply = false;
this.cEdit.statu = false;
this.cEdit.type = this.Status.pageType;
this.cEdit.webPath = "";
let pro1 = new Promise((resolve, reject) => {
uni.setStorage({
key: Common.pcmStorageKey + "_" + this
.cEdit.Id,
data: hexs,
success() {
resolve();
},
fail(ex) {
reject(ex)
}
});
});
let pro2 = new Promise((resolve, reject) => {
let array = uni.getStorageSync(Common
.audioStorageKey);
if (!array) {
array = [this.cEdit];
} else {
array.unshift(this.cEdit);
}
uni.setStorage({
key: Common.audioStorageKey,
data: array,
success() {
resolve();
},
fail(ex) {
reject(ex)
}
});
});
Promise.allSettled([pro1, pro2]).then(res => {
if (res[0].status == 'fulfilled' && res[1]
.status == 'fulfilled') {
updateLoading(these, {
text: "操作成功"
});
this.AudioData.tempFilePath = "";
this.Status.isRecord = false;
uni.navigateBack();
return;
} else {
updateLoading(these, {
text: "操作没有成功"
});
}
this.timeOutCloseLoad();
}).catch(ex => {
updateLoading(these, {
text: "出现异常" + JSON.stringify(ex)
});
this.timeOutCloseLoad();
});
},
importDoc() {
this.checkFile();
},
txtToAudio() {
let msg = "";
if (!this.formData.txt) {
msg = '请输入语音内容';
}
if (this.formData.txt.length > 100) {
msg = "内容限制在100字以内";
}
if (msg) {
this.showMsg(msg);
return;
}
hexs = [];
this.AudioData.hexLength = 0;
showLoading(this, {
text: '正在生成语音'
});
let closeWaiting = () => {
setTimeout(() => {
hideLoading(this);
}, 2000)
}
let task = () => {
return new Promise((succ, err) => {
request({
url: "/app/video/ttsToOss",
method: 'post',
data: {
deviceId: these.Status.ID,
text: this.formData.txt
}
}).then(res => {
if (res && res.code && res.code === 200) {
succ(res.data);
console.log('这是成功了吗');
uni.navigateBack()
} else {
err(res);
}
}).catch(ex => {
console.error("出现异常", ex);
err(res);
});
})
}
task().then(res => {
try {
console.log("文本转语音成功", res);
hexs = res;
this.AudioData.hexLength = hexs.length;
this.AudioData.tempFilePath = "";
this.cEdit.localPath = "";
this.cEdit.time = "";
this.playInit("");
updateLoading(this, {
text: '文本转语音成功'
});
} catch (error) {
console.error("出现错误,", error)
}
}).catch(ex => {
console.error("出现异常", ex);
updateLoading(this, {
text: '生成失败'
});
}).finally(closeWaiting);
},
timeOutCloseLoad() {
setTimeout(() => {
hideLoading(these);
}, 1200);
},
// 保存录音并上传(已修复文件格式问题)
uploadLuYin() {
// 文件类型验证
if (!this.AudioData.tempFilePath) {
this.showMsg("请先录制或选择音频文件");
return;
}
// 检查文件扩展名
const validExtensions = ['.mp3', '.wav', '.aac', '.m4a'];
let uploadFilePath = this.AudioData.tempFilePath;
const hasValidExtension = validExtensions.some(ext =>
uploadFilePath.toLowerCase().endsWith(ext)
);
if (!hasValidExtension) {
// 如果没有有效扩展名,添加.mp3扩展名
uploadFilePath = uploadFilePath + '.mp3';
console.log("自动添加.mp3扩展名新路径:", uploadFilePath);
}
console.log("上传文件路径:", uploadFilePath);
plus.io.resolveLocalFileSystemURL(uploadFilePath, (entry) => {
entry.getMetadata((metadata) => {
console.log("文件大小:", metadata.size, "字节");
console.log("文件类型验证通过");
this.doUpload(uploadFilePath);
}, (err) => {
console.error("获取文件元数据失败:", err);
this.doUpload(this.AudioData.tempFilePath);
});
}, (err) => {
console.error("文件不存在或路径错误:", err);
this.doUpload(this.AudioData.tempFilePath);
});
},
// 执行上传操作
doUpload(filePath) {
const key = `${Common.pcmStorageKey}_${this.cEdit.Id}`;
const store = uni.getStorageInfoSync();
if (store.keys.includes(key)) return;
const token = uni.getStorageSync('token');
const clientid = uni.getStorageSync('clientID');
const these = this;
showLoading(this, {
text: "文件上传中"
});
console.log("最终上传文件路径:", filePath);
uni.uploadFile({
url: baseURL + "/app/video/uploadAudioToOss",
filePath: filePath,
name: 'file',
header: {
"Authorization": `Bearer ${token}`,
"clientid": clientid
},
formData: {
deviceId: these.Status.ID
},
success: (res) => {
try {
if (!res.data) {
throw new Error('res.data is empty');
}
const resData = JSON.parse(res.data);
if (resData.code === 200) {
// 合并两个存储操作
Promise.all([
new Promise((resolve, reject) => {
uni.setStorage({
key,
data: resData.data,
success: resolve,
fail: reject
});
}),
new Promise((resolve, reject) => {
let array = uni.getStorageSync(Common.audioStorageKey) ||
[];
array.unshift(these.cEdit);
uni.setStorage({
key: Common.audioStorageKey,
data: array,
success: resolve,
fail: reject
});
})
]).then(() => {
updateLoading(these, {
text: "操作成功"
});
these.AudioData.tempFilePath = "";
these.Status.isRecord = false;
uni.navigateBack();
}).catch(ex => {
updateLoading(these, {
text: `出现异常${JSON.stringify(ex)}`
});
these.timeOutCloseLoad();
});
return;
} else {
console.log('上传失败:', resData.msg);
uni.showToast({
title: resData.msg,
icon: 'none',
duration: 3000
});
these.timeOutCloseLoad();
}
} catch (error) {
uni.showToast({
title: '服务器响应格式错误',
icon: 'none',
duration: 3000
});
these.timeOutCloseLoad();
}
},
fail: (err) => {
console.error('上传文件失败:', err);
uni.showToast({
title: '上传失败,请检查网络',
icon: 'none',
duration: 3000
});
these.timeOutCloseLoad();
},
complete: () => {
console.log('上传操作完成');
}
});
},
drop() {
this.showMsg("您确定删除该音频吗?", false, true, () => {
these.dropTmpFile();
this.cEdit = {
Id: "", //编号
name: "", //名称
createTime: "", //创建时间
localPath: "", //本地地址
webPath: "", //网络地址
statu: "", //状态,是否公开
isApply: "", //是否使用中
time: "", //音频总长度
currTime: '' //当前播放进度
};
this.Status.playState = "";
})
},
seek(intval) { //快进快退
let tmp = this.cEdit.currTime + intval;
console.log("tmp=" + tmp)
if (tmp < 0) {
this.cEdit.currTime = 0;
innerAudioContext.seekTo(0);
innerAudioContext.play();
} else if (tmp > this.cEdit.time) {
this.cEdit.currTime = this.cEdit.time;
innerAudioContext.seekTo(this.cEdit.time);
innerAudioContext.stop();
clearInterval(audioTime);
} else {
this.cEdit.currTime = tmp;
innerAudioContext.seekTo(tmp);
}
},
playToggle() {
if (this.Status.playState === '') {
this.showMsg("音频未就绪");
return;
}
if (innerAudioContext) {
if (this.Status.playState == 'play') {
innerAudioContext.pause();
} else {
innerAudioContext.play();
}
} else {
this.showMsg("播放器未就绪");
}
},
getFormatSeconds(seconds, Ms) {
if (!seconds) {
return "";
}
let totalSeconds = Math.floor(seconds);
let days = Math.floor(totalSeconds / 86400); // 1天=86400秒
let remainingSeconds = totalSeconds % 86400;
let hours = Math.floor(remainingSeconds / 3600);
let minutes = Math.floor((remainingSeconds % 3600) / 60);
let secs = remainingSeconds % 60;
// 补零处理
let paddedDays = String(days).padStart(2, '0');
let paddedHours = String(hours).padStart(2, '0');
let paddedMinutes = String(minutes).padStart(2, '0');
let paddedSecs = String(secs).padStart(2, '0');
if (Ms) {
return `${paddedMinutes}${paddedSecs}`;
}
return `${paddedMinutes}:${paddedSecs}`;
},
createAudioPlayer() {
try {
console.log("准备创建播放器");
if (innerAudioContext) {
this.destoryAudioPlayer();
clearInterval(audioTime);
this.cEdit.currTime = 0;
}
innerAudioContext = plus.audio.createPlayer({
src: this.cEdit.localPath,
autoplay: false,
backgroundControl: false,
});
console.log('播放器创建成功');
innerAudioContext.addEventListener('canplay', () => {
console.log("准备就绪,可以播放了");
this.Status.playState = 'ready';
this.cEdit.time = innerAudioContext.getDuration();
this.cEdit.time = Math.ceil(this.cEdit.time);
console.log("音频长度:", this.cEdit.time);
});
console.log("canplay事件注册成功")
//开始播放
innerAudioContext.addEventListener("play", (res) => {
if (this.Status.playState === 'ready') {
this.cEdit.currTime = 0;
}
this.Status.playState = 'play';
audioTime = setInterval(() => {
this.cEdit.currTime += 1;
}, 1000);
console.log("播放中", res);
});
console.log("play事件注册成功")
//播放结束
innerAudioContext.addEventListener("ended", (res) => {
clearInterval(audioTime);
this.cEdit.currTime = 0;
this.Status.playState = 'ready';
console.log("播放结束", res);
});
console.log("ended事件注册成功")
//暂停播放
innerAudioContext.addEventListener("pause", (res) => {
clearInterval(audioTime);
audioTime = null;
this.Status.playState = 'pause';
console.log("暂停了", res);
});
console.log("pause事件注册成功")
innerAudioContext.addEventListener("seeked", (res) => {
console.log("seek完成了", res);
})
console.log("seeked事件注册成功")
innerAudioContext.addEventListener('error', ex => {
console.log("出错了", ex)
});
console.log("error事件注册成功");
this.Status.playState = 'ready';
} catch (ex) {
// console.error("出现错误", ex);
}
},
destoryAudioPlayer() { //销毁播放器
if (innerAudioContext) {
innerAudioContext.close();
console.log("播放器已销毁");
}
},
handleUploadCallback(res) {
console.log("选择文件:", res);
if (res && res.data) {
if (this.Status.pageType === 'File') { //文件上传
if (res.data[0].type.indexOf('audio') > -1) {
this.cEdit.name = res.data[0].name;
this.playInit(res.data[0].tempFilePath);
} else {
this.showMsg("只能选择音频文件");
}
return;
}
if (this.Status.pageType === 'Txt') { //文字转语音
let file = res.data[0];
let path = file.tempFilePath;
let type = file.type;
if (type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
this.parseWord(path); //docx
return;
}
if (type === 'text/plain') {
this.parseTxt(path); //txt
return;
}
if (type == "application/msword") { //doc
this.showPop({
showPop: true, //是否显示弹窗
popType: 'custom',
bgColor: '#383934bd',
borderColor: '#BBE600',
textColor: '#ffffffde',
buttonBgColor: '#BBE600',
buttonTextColor: '#232323DE',
iconUrl: '',
message: '打开文档后请手动复制文本内容',
buttonText: '打开',
clickEvt: '',
visiblePrompt: false,
promptTitle: '',
modelValue: '',
visibleClose: false,
okCallback: () => {
this.parseDoc(path);
},
showSlot: false,
buttonCancelText: '取消',
showCancel: true,
});
return;
}
this.showMsg("只能选择.txt、.docx、.doc文档");
return;
}
} else {
console.log("未选择任何文件");
}
},
parseTxt(txtPath) { //读取txt文件
showLoading(this, {
text: "文档读取中"
});
let promise = new Promise((resolve, reject) => {
plus.io.resolveLocalFileSystemURL(
txtPath,
(entry) => {
// 2. 获取文件对象
console.log("111111");
entry.file(
(file) => {
console.log("22222")
try {
let reader = new plus.io.FileReader();
reader.onloadend = function(e) {
// Get data
console.log("读取成功:", e.target.result);
resolve(e.target.result);
};
reader.onerror = function(ex) {
console.error("err=", ex)
reject("reader.onerror");
}
reader.readAsText(file);
} catch (err) {
console.error("err=", err);
reject(err.message)
}
},
(err) => {
console.error('获取文件对象失败:', err);
reject('获取文件对象失败')
}
);
},
(err) => {
console.error('解析文件路径失败:', err);
reject('解析文件路径失败')
}
);
});
promise.then(txt => {
this.formData.txt = txt;
updateLoading(this, {
text: '读取成功'
});
}).catch(ex => {
updateLoading(this, {
text: '无法读取文档'
});
}).finally(() => {
setTimeout(() => {
hideLoading(these);
}, 500)
})
},
parseDoc(filePath) {
uni.openDocument({
filePath: filePath,
fail(err) {
these.showMsg("无法打开文档");
}
});
},
parseWord(filePath) { //读取word文件
const token = uni.getStorageSync('token');
const clientid = uni.getStorageSync('clientID');
showLoading(this, {
text: "文档读取中"
});
let promise = new Promise((resolve, reject) => {
uni.uploadFile({
url: baseURL + "/app/video/extract",
filePath: filePath,
name: 'file',
header: {
"Method": "POST",
"Content-Type": "multipart/form-data",
"Authorization": 'Bearer ' + token,
"clientid": clientid
},
timeout: 600000,
fail: (ex) => {
reject('文件无法读取,请手动复制文本内容');
},
success: (res) => {
if (res.statusCode === 200) {
res = JSON.parse(res.data);
console.log("服务器响应=", res);
if (res && res.data) {
these.formData.txt = res.data;
updateLoading(this, {
text: '读取成功'
});
resolve();
return;
}
}
reject('文件无法读取,请手动复制文本内容');
},
complete() {
setTimeout(() => {
hideLoading(these);
}, 500);
}
});
});
promise.then(res => {}).catch(ex => {
updateLoading(these, {
text: ex
});
uni.openDocument({
filePath: filePath,
fail(err) {
console.error("无法打开文件", err);
}
});
});
},
checkFile() {
this.$refs.XeUpload.upload('file');
},
toggleRecord() {
if (this.Status.recoderIntval) {
this.stopLuYin();
} else {
this.startLuYin();
}
},
playInit(tempFilePath) {
let task = (path) => {
try {
console.log("最终播放路径=", path);
// 确保路径有.mp3扩展名
if (path && !path.toLowerCase().endsWith('.mp3')) {
path = path + '.mp3';
console.log("播放路径已添加.mp3扩展名:", path);
}
this.AudioData.tempFilePath = path;
this.cEdit.localPath = path;
this.cEdit.Id = Common.guid();
this.cEdit.createTime = Common.DateFormat(new Date(), "yyyy年MM月dd日");
this.cEdit.isApply = false;
this.cEdit.statu = false;
this.cEdit.type = this.Status.pageType;
this.cEdit.webPath = "";
if (path) {
this.createAudioPlayer();
} else {
this.cEdit.time = "";
}
} catch (exx) {
console.error("playInit错误:", exx)
}
}
if (tempFilePath) {
Common.moveFileToDownloads(tempFilePath).then(res => {
console.log("文件移动成功", res);
task(res);
}).catch(ex => {
console.error("文件移动失败了", ex);
task(tempFilePath);
});
} else {
task("");
}
},
startLuYin() {
this.destoryAudioPlayer();
if (!AudeoRecoder) {
// #ifdef APP
try {
AudeoRecoder = uni.getRecorderManager();
AudeoRecoder.onStop(function(res) {
try {
console.log('录音停止:' + JSON.stringify(res));
these.AudioData = res;
these.cEdit.time = these.Status.RecordTime;
these.cEdit.name = "语音录制" + these.Status.RecordName;
if (these.Status.pageValid) {
these.dropTmpFile();
these.Status.pageValid = false;
AudeoRecoder = null;
} else {
these.playInit(these.AudioData.tempFilePath);
}
} catch (ex) {
console.error("录音停止回调错误:", ex);
}
});
} catch (ex) {
console.error("获取录音管理器错误:", ex);
}
// #endif
}
let startTask = () => {
clearInterval(this.Status.recoderIntval);
this.Status.recoderIntval = null;
// #ifdef APP
try {
AudeoRecoder.start({
duration: 60000,
sampleRate: 44100,
format: 'mp3', // 确保这里是mp3格式
frameSize: 16,
encodeBitRate: 192000,
numberOfChannels: 1
});
} catch (error) {
console.error("开始录音错误:", error);
}
// #endif
this.Status.RecordTime = 0;
// 确保录音文件名包含.mp3扩展名
this.Status.RecordName = Common.DateFormat(new Date(), "yyyyMMddHHmmss") + ".mp3";
this.AudioData.tempFilePath = "";
this.Status.recoderIntval = setInterval(() => {
this.Status.RecordTime += 1;
if (this.Status.RecordTime >= 60) {
this.toggleRecord();
}
}, 1000);
this.Status.isRecord = !this.Status.isRecord;
}
if (this.AudioData.tempFilePath) {
this.showMsg("当前录音未保存,您要放弃该录音吗?", false, true, () => {
these.dropTmpFile();
startTask();
})
} else {
startTask()
}
},
stopLuYin() {
clearInterval(this.Status.recoderIntval);
this.Status.recoderIntval = null;
try {
AudeoRecoder.stop();
} catch (ex) {
console.error("停止录音错误:", ex);
if (!these.Status.pageValid) {
these.AudioData.tempFilePath = "_doc/" + this.Status.RecordName;
}
} finally {
this.Status.isRecord = !this.Status.isRecord;
}
},
dropTmpFile() {
//删除丢弃的文件
// #ifdef APP
if (!these.AudioData.tempFilePath) {
return;
}
try {
plus.io.resolveLocalFileSystemURL(these.AudioData.tempFilePath, (entry) => {
// 确认是文件类型
if (entry.isFile) {
entry.remove(
() => {
console.log('文件删除成功');
// 删除成功后的逻辑
these.AudioData.tempFilePath = "";
},
(err) => {
console.error('文件删除失败:', err.message);
}
);
} else {
console.error('路径指向的不是文件');
}
}, (err) => {
console.error('路径解析失败:', err.message);
});
} catch (error) {
console.error("删除文件异常:", error);
}
// #endif
},
closePop: function() {
this.Status.Pop.showPop = false;
if (this.Status.Pop.cancelCallback) {
this.Status.Pop.cancelCallback();
}
},
HidePop: function() {
this.Status.Pop.showPop = false;
if (this.Status.Pop.okCallback) {
this.Status.Pop.okCallback();
}
},
showPop: function(option) {
hideLoading(this);
let def = {
showPop: true, //是否显示弹窗
popType: 'custom',
bgColor: '#383934bd',
borderColor: '#BBE600',
textColor: '#ffffffde',
buttonBgColor: '#BBE600',
buttonTextColor: '#232323DE',
iconUrl: '',
message: '',
buttonText: '确定',
clickEvt: '',
visiblePrompt: false,
promptTitle: '',
modelValue: '',
visibleClose: false,
okCallback: null,
showSlot: false,
buttonCancelText: '',
showCancel: false,
}
let keys = Object.keys(def);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
if (key in option) {
continue;
}
this.Status.Pop[key] = def[key];
}
if (option) {
keys = Object.keys(option);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
this.Status.Pop[key] = option[key];
}
}
if (!option.borderColor) {
option.borderColor = '#BBE600';
option.buttonBgColor = '#BBE600';
}
these.Status.Pop.showPop = true;
},
showMsg(msg, isSucc, showCancel, okCallback) {
if (showCancel === undefined) {
showCancel = false;
}
if (okCallback === undefined) {
okCallback = null;
}
let icoUrl = '/static/images/6155/DeviceDetail/uploadErr.png';
let borderColor = "#e034344d";
let buttonBgColor = "#E03434";
if (isSucc) {
icoUrl = '/static/images/common/success.png';
borderColor = "#BBE600";
buttonBgColor = "#BBE600";
}
this.showPop({
message: msg,
iconUrl: icoUrl,
borderColor: borderColor,
buttonBgColor: buttonBgColor,
buttonText: '确定',
okCallback: okCallback,
showCancel: showCancel,
});
}
}
}
</script>
<style>
.footer .opt {
display: flex;
flex-direction: column;
flex-wrap: wrap;
align-content: center;
align-items: center;
justify-content: flex-start;
}
.footer .recordTitle {
color: rgba(255, 255, 255, 0.87);
font-family: PingFang SC;
font-size: 28rpx;
font-weight: 400;
line-height: 40rpx;
letter-spacing: 0.14rpx;
text-align: center;
}
.footer .recordName {
color: rgba(255, 255, 255, 0.6);
font-family: PingFang SC;
font-size: 24rpx;
font-weight: 400;
line-height: 34rpx;
letter-spacing: 0.14px;
text-align: center;
margin-bottom: 40rpx;
}
.footer .btnRecord .start {
background: rgba(224, 52, 52, 1);
width: 80rpx;
height: 80rpx;
border-radius: 50%;
}
.footer .btnRecord .start.active {
border-radius: 8rpx;
background: rgba(224, 52, 52, 1);
width: 40rpx;
height: 40rpx;
}
.footer .btnRecord {
width: 110rpx;
height: 110rpx;
box-sizing: border-box;
border: 4rpx solid rgba(255, 255, 255, 0.6);
border-radius: 50%;
}
.footer .btnRemark {
color: rgba(255, 255, 255, 0.6);
margin-top: 20rpx;
font-family: PingFang SC;
font-size: 28rpx;
font-weight: 400;
line-height: 40rpx;
letter-spacing: 0.14px;
text-align: center;
}
.footer {
padding-top: 60rpx;
padding-bottom: 60rpx;
width: 100%;
height: auto;
position: fixed;
z-index: 99;
left: 0rpx;
bottom: 0rpx;
border-radius: 16rpx 16rpx 0px 0px;
background: rgba(26, 26, 26, 1);
display: flex;
flex-direction: column;
flex-wrap: nowrap;
align-content: center;
align-items: center;
justify-content: flex-start;
}
.popup-prompt {
width: 100%;
margin-top: 40rpx;
}
.popup-prompt-input {
width: 100%;
height: 80rpx;
line-height: 80rpx;
border: 1rpx solid #BBE60096;
border-radius: 10rpx;
padding: 0 20rpx;
margin-bottom: 30rpx;
font-size: 26rpx;
box-sizing: border-box;
text-align: left;
}
.tips {
color: rgba(255, 255, 255, 0.6);
font-family: PingFang SC;
font-size: 28rpx;
font-weight: 400;
line-height: 40rpx;
letter-spacing: 0.14rpx;
text-align: left;
}
.polygon {
width: 300rpx;
height: 150rpx;
background-color: #3A3A3A;
border-radius: 10rpx;
color: rgba(255, 255, 255, 0.6);
;
}
.cEdit {
width: 100%;
height: auto;
border-radius: 8px;
background: rgba(26, 26, 26, 1);
box-sizing: border-box;
padding: 40rpx 30rpx
}
.btnSave {
width: 130rpx;
height: 55rpx;
line-height: 55rpx;
text-align: center;
color: rgba(23, 23, 23, 1);
font-family: PingFang SC;
font-size: 24rpx;
font-weight: 400;
letter-spacing: 12rpx;
background-color: #BBE600;
border-radius: 180rpx;
}
.txtName {
color: rgba(174, 214, 0, 1);
border-bottom: 1rpx solid rgba(174, 214, 0, 1);
font-family: PingFang SC;
font-size: 32rpx;
font-weight: 400;
line-height: 44rpx;
letter-spacing: 0.14px;
text-align: left;
margin-bottom: 10rpx;
}
.audioRemark {
display: flex;
flex-wrap: nowrap;
align-content: center;
align-items: center;
justify-content: space-between;
}
.slide {
padding-top: 60rpx;
width: 100%;
position: relative;
}
.slide .line {
width: 100%;
height: 4rpx;
border-radius: 12rpx;
background: rgba(106, 106, 106, 1);
}
.slide .circle {
position: absolute;
margin-top: -4rpx;
width: 12rpx;
height: 12rpx;
background: rgba(234, 234, 234, 1);
border-radius: 50%;
}
.control {
width: 100%;
padding: 40rpx;
position: relative;
}
.control .playControl {
width: 100%;
position: absolute;
left: 0rpx;
bottom: 0rpx;
z-index: 1;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-content: center;
align-items: center;
justify-content: center;
}
.control .drop {
position: absolute;
z-index: 2;
bottom: 0rpx;
right: 0rpx;
}
.playControl .seek .img {
width: 32rpx;
height: 32rpx;
}
.playControl .play .img {
width: 38rpx;
height: 44rpx;
margin: 0rpx 80rpx;
}
.input {
border-bottom: 1rpx solid #FFFFFF;
}
.placehoderClass {
color: rgba(255, 255, 255, 0.6);
font-family: PingFang SC;
font-size: 32rpx;
font-weight: 400;
line-height: 44rpx;
letter-spacing: 0.14px;
}
.text-content {
border-radius: 16rpx;
background: rgba(26, 26, 26, 1);
width: 100%;
height: auto;
box-sizing: border-box;
padding: 20rpx;
margin-bottom: 20rpx;
}
.importTxt {
color: rgba(174, 214, 0, 1);
font-family: PingFang SC;
font-size: 28rpx;
font-weight: 400;
height: 60rpx;
line-height: 60rpx;
letter-spacing: 0.14px;
}
.textarea {
min-height: 300rpx;
width: 100%;
box-sizing: border-box;
}
.line {
width: 100%;
height: 0rpx;
border-bottom: 1rpx solid rgba(255, 255, 255, 0.36);
margin-bottom: 20rpx;
}
.footBtn {
width: 100%;
}
.footBtn .convert {
/* 组合 99 */
width: 130rpx;
height: 55rpx;
line-height: 55rpx;
border-radius: 180px;
color: #232323;
background-color: #AED600;
text-align: center;
font-family: PingFang SC;
font-size: 24rpx;
font-weight: 400;
letter-spacing: 12rpx;
}
.footBtn .audioSett,
.audioSett {
margin-right: 50rpx;
color: #FFFFFFDE;
color: rgba(255, 255, 255, 0.87);
font-family: PingFang SC;
font-size: 28rpx;
font-weight: 400;
line-height: 55rpx;
letter-spacing: 0px;
text-align: center;
}
.txtBtns {
box-sizing: border-box;
width: 100%;
padding: 20rpx;
}
</style>