| | |
| | | <template> |
| | | <u-popup :show="show" mode="bottom" :closeOnClickOverlay="false" @close="closePopup" zIndex="10071"> |
| | | <u-popup :show="show" mode="bottom" :closeOnClickOverlay="false" @close="closePopup" :zIndex="9999" :overlay="true" |
| | | :safeAreaInsetBottom="true"> |
| | | <view class="voice-popup"> |
| | | <view class="header"> |
| | | <text class="pl-30">语音输入</text> |
| | | <image src="/static/Appeal/close.png" class="w-34 h-34 mr-30" mode="" @click="closePopup"></image> |
| | | </view> |
| | | <view class="record-anim"> |
| | | <image src="/static/Appeal/step.png" class="w-153 h-119" mode="" @click="onPlay"></image> |
| | | <image src="/static/Appeal/step.png" class="w-153 h-119" mode=""></image> |
| | | </view> |
| | | <view class="timer">{{ time }}</view> |
| | | <view class="btns"> |
| | | <image src="/static/Appeal/start.png" class="w-153 h-153 mr-14" mode="" @click="onPlay"></image> |
| | | <image src="/static/Appeal/stop.png" class="w-153 h-153 mr-14" mode="" @click="onPause"></image> |
| | | <image :src="getRecordButtonSrc" class="w-153 h-153 mr-14" mode="" @click="handlerOnCahnger"></image> |
| | | <image src="/static/Appeal/cancel.png" class="w-153 h-153 mr-14" mode="" @click="onStop"></image> |
| | | </view> |
| | | </view> |
| | | <mumu-recorder ref="mumuRecorder" @success="handlerSuccess" @error="handleError"></mumu-recorder> |
| | | </u-popup> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getSignature } from '../pages/index/service' |
| | | import MumuRecorder from '@/uni_modules/mumu-recorder/components/mumu-recorder/mumu-recorder.vue' |
| | | export default { |
| | | name: 'VoiceInputPopup', |
| | | components: { |
| | | MumuRecorder |
| | | }, |
| | | props: { |
| | | show: { |
| | | type: Boolean, |
| | |
| | | data() { |
| | | return { |
| | | time: '00:00:00', |
| | | barHeights: [20, 30, 40, 30, 20], // 可做动画 |
| | | isRecording: false, |
| | | isPaused: false, |
| | | tempFilePath: '', |
| | | timer: null, |
| | | seconds: 0, |
| | | minutes: 0, |
| | | hours: 0, |
| | | recordSegments: [], // 存储录音片段 |
| | | currentSegment: null, // 当前录音片段 |
| | | status: false, |
| | | recorder: null |
| | | } |
| | | }, |
| | | computed: { |
| | | getRecordButtonSrc() { |
| | | if (this.isPaused) { |
| | | return '/static/Appeal/start.png' |
| | | } |
| | | return this.isRecording ? '/static/Appeal/stop.png' : '/static/Appeal/start.png' |
| | | } |
| | | }, |
| | | mounted() { |
| | | }, |
| | | methods: { |
| | | closePopup() { |
| | | this.$emit('update:show', false) |
| | | handlerSave() { |
| | | let tag = document.createElement('a') |
| | | tag.href = this.recorder.localUrl |
| | | tag.download = '录音' |
| | | tag.click() |
| | | }, |
| | | onPlay() {}, |
| | | onPause() {}, |
| | | onStop() {}, |
| | | handlerOnCahnger() { |
| | | if (!this.isRecording && !this.isPaused) { |
| | | // 开始新的录音 |
| | | this.startNewRecording() |
| | | } else if (this.isRecording) { |
| | | // 暂停当前录音 |
| | | this.pauseRecording() |
| | | } else if (this.isPaused) { |
| | | // 继续录音 |
| | | this.resumeRecording() |
| | | } |
| | | }, |
| | | startNewRecording() { |
| | | this.isRecording = true |
| | | this.isPaused = false |
| | | this.startTimer() |
| | | this.$refs.mumuRecorder.start() |
| | | }, |
| | | pauseRecording() { |
| | | this.isRecording = false |
| | | this.isPaused = true |
| | | this.stopTimer() |
| | | this.$refs.mumuRecorder.stop() |
| | | }, |
| | | resumeRecording() { |
| | | this.isRecording = true |
| | | this.isPaused = false |
| | | this.startTimer() |
| | | this.$refs.mumuRecorder.start() |
| | | }, |
| | | handlerSuccess(res) { |
| | | console.log('录音成功:', res) |
| | | this.recorder = res |
| | | // 保存当前录音片段 |
| | | if (res.localUrl) { |
| | | this.recordSegments.push({ |
| | | url: res.localUrl, |
| | | duration: this.seconds + this.minutes * 60 + this.hours * 3600 |
| | | }) |
| | | } |
| | | }, |
| | | handlerError(code) { |
| | | switch (code) { |
| | | case '101': |
| | | uni.showModal({ |
| | | content: '当前浏览器版本较低,请更换浏览器使用,推荐在微信中打开。' |
| | | }) |
| | | break; |
| | | case '201': |
| | | uni.showModal({ |
| | | content: '麦克风权限被拒绝,请刷新页面后授权麦克风权限。' |
| | | }) |
| | | break |
| | | default: |
| | | uni.showModal({ |
| | | content: '未知错误,请刷新页面重试' |
| | | }) |
| | | break |
| | | } |
| | | }, |
| | | closePopup() { |
| | | // 清空录音片段 |
| | | this.recordSegments = [] |
| | | // 重置计时器 |
| | | this.seconds = 0 |
| | | this.minutes = 0 |
| | | this.hours = 0 |
| | | this.updateTimeDisplay() |
| | | // 关闭弹窗 |
| | | this.$emit('close') |
| | | }, |
| | | startTimer() { |
| | | this.stopTimer(); |
| | | this.timer = setInterval(() => { |
| | | this.seconds++; |
| | | if (this.seconds >= 60) { |
| | | this.seconds = 0; |
| | | this.minutes++; |
| | | if (this.minutes >= 60) { |
| | | this.minutes = 0; |
| | | this.hours++; |
| | | } |
| | | } |
| | | this.updateTimeDisplay(); |
| | | }, 1000); |
| | | }, |
| | | stopTimer() { |
| | | if (this.timer) { |
| | | clearInterval(this.timer); |
| | | this.timer = null; |
| | | } |
| | | }, |
| | | updateTimeDisplay() { |
| | | this.time = `${String(this.hours).padStart(2, '0')}:${String(this.minutes).padStart(2, '0')}:${String(this.seconds).padStart(2, '0')}`; |
| | | }, |
| | | onStop() { |
| | | // 如果正在录音,先停止当前录音 |
| | | if (this.isRecording) { |
| | | this.$refs.mumuRecorder.stop() |
| | | } |
| | | |
| | | // 停止计时 |
| | | this.stopTimer() |
| | | |
| | | // 重置状态 |
| | | this.isRecording = false |
| | | this.isPaused = false |
| | | |
| | | // 处理录音片段 |
| | | if (this.recordSegments.length > 0) { |
| | | // 发送第一个录音片段的URL |
| | | this.$emit('submit', this.recordSegments[0].url) |
| | | this.closePopup() |
| | | } else { |
| | | uni.showToast({ |
| | | title: '未检测到录音文件', |
| | | icon: 'none', |
| | | duration: 2000 |
| | | }) |
| | | } |
| | | } |
| | | }, |
| | | beforeDestroy() { |
| | | this.stopTimer(); |
| | | } |
| | | } |
| | | </script> |
| | |
| | | background: #fff; |
| | | border-radius: 24rpx 24rpx 0 0; |
| | | padding: 40rpx 0 30rpx 0; |
| | | position: relative; |
| | | z-index: 9999; |
| | | |
| | | .header { |
| | | display: flex; |
| | | justify-content: center; |
| | |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | margin-bottom: 80rpx; |
| | | |
| | | text { |
| | | flex: 1; |
| | | text-align: center; |
| | | } |
| | | |
| | | .u-icon { |
| | | position: absolute; |
| | | right: 30rpx; |
| | | top: 0; |
| | | } |
| | | } |
| | | |
| | | .record-anim { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | margin-bottom: 57rpx; |
| | | |
| | | .bars { |
| | | display: flex; |
| | | align-items: flex-end; |
| | | height: 60rpx; |
| | | |
| | | .bar { |
| | | width: 10rpx; |
| | | margin: 0 4rpx; |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | .timer { |
| | | text-align: center; |
| | | font-size: 28rpx; |
| | | color: #888; |
| | | // margin-bottom: 30rpx; |
| | | } |
| | | |
| | | .btns { |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | padding: 40rpx 67rpx 76rpx 67rpx; |
| | | // .u-button { |
| | | // width: 100rpx; |
| | | // height: 100rpx; |
| | | // border-radius: 50%; |
| | | // display: flex; |
| | | // justify-content: center; |
| | | // align-items: center; |
| | | // margin: 0 10rpx; |
| | | // } |
| | | padding: 40rpx 67rpx 6rpx 67rpx; |
| | | } |
| | | } |
| | | </style> |
| | | |
| | | ::v-deep .uni-toast { |
| | | z-index: 10099 !important; |
| | | } |
| | | |
| | | .uni-sample-toast { |
| | | z-index: 10099 !important; |
| | | } |
| | | |
| | | ::v-deep .uni-toast .uni-sample-toast { |
| | | z-index: 10099 !important; |
| | | } |
| | | |
| | | /deep/ .u-transition.u-fade-enter-to.u-fade-enter-active { |
| | | z-index: 997 !important; |
| | | } |
| | | </style> |