<template> <view class="answerdetail-page flex-column"> <template v-if="questionItem"> <view class="question-wrap"> <view class="question-item"> <view class="question-info"> <view style="flex: 1" class="question-title"> <text class="item-label">{{ questionItem.chart_permission_name }}</text> {{ questionItem.question_content }} </view> <view class="item-answer" v-if="questionItem.reply_status === 3"> <view class="answer" @click.stop="handleAudio(questionItem)"> <template v-if="!questionItem.loading"> <image class="music-img" :src="questionItem.answer.isplay ? playImgSrc : pauseImgSrc" mode="widthFix" /> <template v-if=" questionItem.answer.isplay || questionItem.answer.ispause " > <text>{{ questionItem.answer.audioTime - currentAudioMsg.audioCurrentTime > 0 ? moment( (questionItem.answer.audioTime - currentAudioMsg.audioCurrentTime) * 1000 ).format("mm:ss") : "00:00" }}</text> </template> <template v-else> <text>{{ moment(questionItem.answer.audioTime * 1000).format( "mm:ss" ) }}</text> </template> </template> <template v-else> <image class="load-img" src="../static/loading.png" mode="aspectFill" /> <text>{{ moment(questionItem.answer.audioTime).format("mm:ss") }}</text> </template> </view> </view> </view> <text class="item-time">提问时间:{{ questionItem.create_time }}</text> </view> </view> <view class="record-wrap flex-column" v-if="questionItem.reply_status === 2" > <view class="record flex-column" v-if="questionItem.recordStatus !== 4"> <view class="no-record" v-if="questionItem.recordStatus === 1"> <image src="./static/record.png" mode="widthFix" style="width: 90rpx; height: 180rpx" /> <view>无录音(录音时长超过三分钟自动结束)</view> </view> <!-- <view class="record-time" v-else>{{ audioTime }}</view> --> <view class="recode-image" v-else> <scroll-view scroll-x style="height: 100rpx" class="scroll-view" :scroll-left="scrollTop" @scrolltolower="handleScrolltolower" :show-scrollbar="false"> <view style="width:100%;height:100rpx;display:inline-block;"></view> <image src="./static/record-img.png" mode="scaleToFill" /> <image src="./static/record-img.png" mode="scaleToFill" /> <image src="./static/record-img.png" mode="scaleToFill" /> </scroll-view> </view> </view> <view class="record-tool flex-column" v-if="questionItem.recordStatus !== 4" > <view class="hint" v-if="questionItem.recordStatus === 1" >点击开始录音</view > <view class="record-time" v-else>{{ audioTime }}</view> <view class="record-btn-wrap center" v-if="questionItem.recordStatus === 1" > <view class="switch" @click="changeRecodeStatus"> </view> </view> <view class="record-btn-wrap" v-else> <text @click="questionItem.recordStatus === 3 && handleRecode('delete')" v-if="questionItem.recordStatus >= 2" :class="{ active: questionItem.recordStatus === 3 }" >删除</text > <view class="switch" @click="changeRecodeStatus"> <image v-if="questionItem.recordStatus >= 2" :src=" questionItem.recordStatus === 2 ? playImgSrc : pauseImgSrc " :key="playIconKey" :style="{ 'margin-left': questionItem.recordStatus !== 2 ? '7rpx' : 0, width: '36rpx', height: '44rpx', }" mode="widthFix" /> </view> <text @click="handleRecode('finish')" v-if="questionItem.recordStatus >= 2" :class="{ active: questionItem.recordStatus >= 2 }" >完成</text > </view> </view> <view class="record-play" v-if="questionItem.recordStatus === 4"> <view class="audio-wrap"> <view v-if="pageLoading">音频生成中...</view> <view class="play" v-else> <van-icon :name="isplay ? 'pause' : 'play'" @click="handleAudioByReplay" color="#E6B77DFF" size="64rpx" style="align-items: flex-start; margin-left: -20rpx" /> <!-- 进度条 --> <view class="slider-box"> <slider :value="currentAudioMsg.audioCurrentTime" :max="currentAudioMsg.audioTime" @change="sliderChange($event)" @changing="sliderChanging" activeColor="#E6B77DFF" backgroundColor="#EBEBEBFF" block-color="#E6B77DFF" block-size="12" /> <view class="slider-time"> <text>{{ moment(currentAudioMsg.audioCurrentTime * 1000).format( "mm:ss.SS" ) }}</text> <text>{{ moment(currentAudioMsg.audioTime * 1000).format("mm:ss.SS") }}</text> </view> </view> </view> </view> <view class="audio-delete" @click="handleRecode('delete')"> <image src="./static/delerecord.png" mode="heightFix" style="width: 32rpx; height: 35rpx" /> <text>删除</text></view > <view class="audio-pub" @click="handleRecode('pub')">发布</view> </view> </view> </template> </view> </template> <script> import mixin from "../mixin/questionMixin"; import { apiReplayAsk, apiGetQuestion, apiSetRead } from "@/api/question"; import { uploadAudioToServer } from "@/utils/upload"; export default { mixins: [mixin], data() { return { questionItem: null /* { recordStatus: 1, //1:未录音;2:正在录音;3:已暂停;4:完成录音 }, */, pauseImgSrc: "../static/question/recordplay.png", playImgSrc: "../static/question/recordpause.png", innerAudio: null, //该页面的音频 audioCount: 0, //录音计时,毫秒 recordStartTime: null, //录音开始时间 recordStopTime: null, //录音停止的时间 audioTime: "00:00", //录音时间(格式化后):string timer: null, audioItem: null, pageLoading: false, playIconKey: 0, isplay: false, isSlider: false, scrollTop:0, isStart:false,//判断录音是否开始 //globalRecorder:uni.getRecorderManager(), }; }, onLoad(options) { this.initAudio(); this.getQuestionItem(options.id); }, onUnload() { this.resetAudio(); this.destroyAudio(); //录音时误操作退出页面的情况 if(this.questionItem.recordStatus!==4||this.questionItem.recordStatus!==1){ this.globalRecorder.stop() } }, methods: { //初始化audio initAudio() { this.innerAudio = uni.createInnerAudioContext(); this.handleAudioFun(); this.handleRecorderFun(); }, resetAudio(){ this.innerAudio.stop(); this.isplay = false; this.changeCurrentAudio({ id: '', answer: { source: '', audioTime: 0 } }) }, //audio事件 handleAudioFun() { this.innerAudio.onPlay(() => { this.isplay = true; console.log("播放录音了"); this.questionItem.loading = false; }); this.innerAudio.onTimeUpdate(() => { //console.log("时间更新", this.innerAudio.currentTime); /* this.currentAudioMsg.audioCurrentTime = parseInt( this.innerAudio.currentTime ); */ this.currentAudioMsg.audioCurrentTime = this.innerAudio.currentTime; }); this.innerAudio.onSeeked(() => { //取this.innerAudio.currentTime为0 console.log("seek完成"); this.isSlider = false; }); this.innerAudio.onPause(() => { this.isplay = false; console.log("暂停"); console.log(this.innerAudio.paused); }); this.innerAudio.onEnded(() => { console.log("音频播放完毕"); this.questionItem.answer.isplay = false; this.questionItem.answer.ispause = false; /* this.changeCurrentAudio({ id: "", answer: { source: "", audioTime: 0, }, }); */ this.currentAudioMsg.audioCurrentTime = 0; this.isplay = false; }); }, //录音事件 handleRecorderFun() { this.globalRecorder.onStart(() => { console.log("开始录音"); uni.hideToast(); this.clockTime(); }); this.globalRecorder.onPause(() => { console.log("暂停录音"); this.cleanTime(); }); this.globalRecorder.onStop((res) => { console.log("录音完成"); //录音自动结束 和 暂停时点击删除 的情况 if (this.questionItem.recordStatus === 2) { this.questionItem.recordStatus = 4; } console.log('status',this.questionItem.recordStatus) console.log("res", JSON.stringify(res)); this.cleanTime(); //初始化音频播放 this.innerAudio.stop(); this.isplay = false; this.innerAudio.src = res.tempFilePath; console.log('秒数',this.audioCount) this.changeCurrentAudio({ id: "", answer: { source: res.tempFilePath, audioTime: this.audioCount/1000, }, }); this.pageLoading = false; }); this.globalRecorder.onError((res) => { console.log('err',res.errMsg) uni.showToast({ title:res.errMsg, icon:'none' }) //开始录音失败 if(res.errMsg.includes('start')){ this.questionItem.recordStatus = 1; } }); /* this.globalRecorder.onFrameRecorded((res) => { console.log("?", res); }); */ }, async getQuestionItem(id) { const res = await apiGetQuestion({ question_id: id, }); if (res.code === 200) { this.questionItem = res.data; const { audio_list } = res.data; let temp = {}; if (audio_list.length > 0) { temp = { source: res.data.audio_list[0].audio_url, audioTime: parseInt(res.data.audio_list[0].audio_play_seconds), isplay: false, ispause: false, }; } else { temp = { source: "", audioTime: 0, isplay: false, ispause: false, }; } let readKey = ""; const { is_inner } = this.userInfo; if (is_inner === 1) { readKey = "replier_is_read"; } else { readKey = "is_read"; } //console.log('readKey',readKey) this.questionItem[readKey] !== 1 && (await apiSetRead({ question_ids: this.questionItem.community_question_id + "", })); this.questionItem.id = res.data.community_question_id; this.questionItem.answer = temp; this.questionItem.loading = false; this.questionItem.recordStatus = res.data.reply_status === 3 ? 4 : 1; }else{ //问题被删除的情况,返回小程序首页 setTimeout(()=>{ uni.switchTab({ url:'/pages/report/report' }); },1000) } }, changeRecodeStatus() { console.log('a',this.questionItem.recordStatus) //根据questionItem.recordStatus if (this.questionItem.recordStatus === 1) { const that = this //检查是否有权限 uni.getSetting({ success(res){ if(res.authSetting['scope.record']){ that.startRecord() }else{ that.getRecordAuth() } } }); } else if (this.questionItem.recordStatus === 2) { //暂停录音 this.globalRecorder.pause(); this.questionItem.recordStatus = 3; } else if (this.questionItem.recordStatus === 3) { //继续录音 this.globalRecorder.resume(); this.clockTime(); this.questionItem.recordStatus = 2; } else { //结束录音 this.globalRecorder.stop(); //this.cleanTime(); } }, //获取录音授权 getRecordAuth(){ const {community_question_id} = this.questionItem uni.openSetting({ success(res){ //刷新页面 if(res.authSetting['scope.record']){ uni.redirectTo({url: `/pages-question/answerDetail?id=${community_question_id}`}) } } }) //this.questionItem.recordStatus = 1 }, //开启录音 startRecord(){ console.log(this.globalRecorder) this.globalRecorder.start({ duration: 180000, format: "mp3" }); uni.showToast({ title:'加载中', icon:'loading' }) this.questionItem.recordStatus = 2; }, //上传音频 async uploadAudio() { const res = await uploadAudioToServer(this.innerAudio.src); if (res.code === 200) { this.audioItem = res.data; } else { //重新录 this.questionItem.recordStatus = 1; this.audioCount = 0; this.audioTime = this.moment(this.audioCount).format("mm:ss"); } }, //录音操作:完成/删除/发布 async handleRecode(type) { if (type==='finish') { this.questionItem.recordStatus = 4; this.changeRecodeStatus(); } if (type === "finish") { //生成音频,更改页面布局 this.pageLoading = true; } else if (type === "delete") { //重新录 if(this.questionItem.recordStatus===3)this.globalRecorder.stop(); this.questionItem.recordStatus = 1; this.innerAudio.stop(); this.isplay = false; this.audioItem = null; this.audioCount = 0; this.scrollTop = 0; this.recordStartTime = null; this.audioTime = this.moment(this.audioCount * 1000).format("mm:ss"); } else { //发布 if (!this.audioItem) { await this.uploadAudio(); } //如果上传音频成功 if (this.questionItem.recordStatus === 4) { //发布回答 const res = await apiReplayAsk({ question_id: this.questionItem.community_question_id, audio_list: [{ ...this.audioItem, sort: 1 }], }); if (res.code === 200) { uni.showToast({ title: "发布成功", icon: "success", duration: 500, }); setTimeout(() => { //关闭当前页面,跳转到我的回答 uni.navigateBack({ delta: 1 }); }, 500); } } } }, //(提问者)问题已被回答,点击回答音频 handleAudio(item) { const { source, isplay, ispause } = item.answer; if (isplay) { //说明是播放->暂停 this.innerAudio.pause(); this.questionItem.answer.isplay = false; this.questionItem.answer.ispause = true; } else if (ispause) { //说明是暂停->播放 this.innerAudio.play(); this.questionItem.answer.isplay = true; this.questionItem.answer.ispause = false; } else { //console.log("aaa", source, this.innerAudio.src); //说明是第一次播放或播放完 this.changeCurrentAudio(item); this.innerAudio.stop(); this.innerAudio.src = source; this.handleAudioPlay(); this.questionItem.answer.isplay = true; } }, //(回答者)问题被回答,点击回答音频 handleAudioByReplay() { this.isplay = !this.isplay; if (this.innerAudio.paused) { this.innerAudio.play(); } else { this.innerAudio.pause(); } this.playIconKey++; //更新音频播放图标 }, //拖动音频进度条 sliderChange(e) { console.log("拖动完成?"); //this.innerAudio.pause(); const value = e.detail.value; this.innerAudio.seek(value); this.currentAudioMsg.audioCurrentTime = value; }, sliderChanging() { this.isSlider = true; }, //切换当前播放音频 changeCurrentAudio(item) { const { id } = item; const { source, audioTime } = item.answer; this.currentAudioMsg = { id: id, audioCurrentTime: 0 * 1000, audioTime: audioTime, audioCurrentUrl: source, }; if (id) { console.log("?"); this.questionItem.loading = true; } }, //录音计时 clockTime() { console.log("开始录音计时"); if (!this.recordStartTime) { this.recordStartTime = Date.now(); } //console.log(this.recordStartTime); this.timer = setInterval(() => { if (this.timer) { this.audioCount += 30; this.audioTime = this.moment(this.audioCount).format("mm:ss.SS"); //this.audioTime = this.moment(Date.now() - this.recordStartTime).format("mm:ss.SS"); this.scrollTop+=1; } }, 30); }, //清除录音计时 cleanTime() { console.log("结束录音计时"); //console.log('aa',this.recordStopTime) clearTimeout(this.timer); this.playIconKey++; //更新录音暂停播放图标 //this.audioTime = this.moment(this.audioCount).format("mm:ss.SS"); }, //scroll-view滑动到最右 handleScrolltolower(){ console.log('a',this.scrollTop) this.scrollTop-=150; } }, }; </script> <style scoped lang="scss"> .flex-column { display: flex; flex-direction: column; } .answerdetail-page { padding: 50rpx 30rpx 0 30rpx; height: calc(100vh - calc(50px + env(safe-area-inset-bottom))); box-sizing: border-box; .question-wrap { .question-item::after { height: 0; } } .record-wrap { margin: 0 -30rpx; flex: 1; .record { flex: 1; justify-content: center; align-items: center; .no-record { text-align: center; color: #999999ff; font-size: 28rpx; height:130rpx; image { width: 94rpx; } } /* .record-time { justify-self: flex-end; font-size: 60rpx; } */ .recode-image { width: 100%; height: 100%; background-color: #fafafaff; display: flex; align-items: center; .scroll-view { width: 100%; white-space: nowrap; ::-webkit-scrollbar{ width:0; height:0; display:none; color:transparent; } image { width: 421rpx; height: 100rpx; margin-right: 6rpx; } } } } .record-tool { justify-content: center; align-items: center; border-top: 1rpx solid #e6e6e6ff; height: 400rpx; position: relative; .hint { color: #ee3636ff; font-size: 28rpx; position: absolute; top: 50rpx; } .record-time{ font-size: 60rpx; } .record-btn-wrap { margin-top: 38rpx; display: flex; width: 100%; justify-content: space-around; align-items: center; &.center { justify-content: center; } text { color: #999999ff; font-size: 32rpx; &.active { color: #ee3636ff; } } .switch { width: 118rpx; height: 118rpx; padding: 12rpx; box-sizing: border-box; border-radius: 50%; border: 1rpx solid #ee3636ff; /* background-color: rgb(68, 46, 46); */ position: relative; display: flex; align-items: center; justify-content: center; image { position: absolute; width: 36rpx; z-index: 5; } &::after { content: ""; display: inline-block; width: 100%; height: 100%; border-radius: 50%; background-color: #ee3636ff; } } } } .record-play { margin: 20rpx 30rpx; .audio-wrap { background-color: #fdf8f2ff; padding: 30rpx; border-radius: 16rpx; } .play { display: flex; justify-content: space-between; .slider-box { margin-left: 15rpx; flex: 1; slider { margin: 15rpx 0 0 0; } .slider-time { display: flex; justify-content: space-between; text { color: #999999ff; font-size: 22rpx; } } } } .audio-delete { margin-top: 20rpx; width: 100%; height: 40rpx; display: flex; justify-content: flex-end; align-items: center; image { height: 40rpx; } text { margin-left: 10rpx; color: #999999ff; font-size: 28rpx; } } .audio-pub { margin-top: 120rpx; background-color: #e6b77dff; color: #fff; height: 80rpx; line-height: 80rpx; border-radius: 40rpx; text-align: center; position: relative; width: 390rpx; left: 50%; margin-left: -170rpx; } } } } </style>