123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- <template>
- <view>
- <selectTypesVue activeSelect="3"></selectTypesVue>
- <view class="words-xuan-box">
- <!-- 单词区 -->
- <view class="show-words-box"> {{data.name}} </view>
- <!-- 音标区 -->
- <view class="yb-play-box">
- <text>{{data.yinbiao}}</text>
- <!-- active -->
- <icon class="yb-play-btn"></icon>
- <icon class="yb-playing-btn" v-if="false"></icon>
- </view>
- <view style="text-align: center;">
- <text v-if="data.jianyi&&data.jianyi.length>0" v-for="(item,index) in data.jianyi" :key="index">
- {{item}}
- </text>
- </view>
- <view>
- <view class="audio-play-btn"></view>
- <!-- 播放中 -->
- <view class="audio-playing-btn" v-if="false"></view>
- </view>
- <view class="container">
- <view class="status">{{ recordingStatus }}</view>
- <view class="duration" v-if="isRecording">录音时长: {{ duration.toFixed(1) }}秒</view>
- <button class="record-btn" :class="{ recording: isRecording }" @touchstart="startRecord"
- @touchend="endRecord" @mousedown="startRecord" @mouseup="endRecord" @mouseleave="handleMouseLeave">
- {{ isRecording ? '松开结束' : '按住录音' }}
- </button>
- <button class="play-btn" :disabled="!voicePath || isPlaying" @click="playVoice">
- {{ isPlaying ? '播放中...' : '播放录音' }}
- </button>
- <view class="tips">提示:按住按钮录音,松开结束(需3秒以上)</view>
- </view>
- </view>
- </view>
- </template>
- <script setup>
- import selectWordsVue from './selectWords.vue';
- import selectTypesVue from './selectTypes.vue';
- import {
- onLoad
- } from "@dcloudio/uni-app"
- import {
- reactive,
- ref,
- onMounted,
- onUnmounted
- } from 'vue';
- const props = defineProps({
- activeWord: {
- type: Object,
- },
- pageData: {
- type: Object,
- },
- activeWords: {
- type: Array
- },
- })
- let tabFlag = ref(1)
- const audioInfo = ref(null)
- const data = reactive({
- name: '',
- yinbiao: '',
- jianyi: []
- })
- // 状态定义
- const isRecording = ref(false)
- const isPlaying = ref(false)
- const voicePath = ref('')
- const duration = ref(0)
- const timer = ref(null)
- const recordingStatus = ref('准备就绪')
- const startTime = ref(0)
-
- // 获取录音和音频播放管理器
- const recorderManager = uni.getRecorderManager()
- const innerAudioContext = uni.createInnerAudioContext()
-
- // 初始化录音器
- const initRecorder = () => {
- // 录音开始回调
- recorderManager.onStart(() => {
- console.log('recorder start')
- isRecording.value = true
- recordingStatus.value = '录音中...'
- startTime.value = Date.now()
- startTimer()
- })
-
- // 录音停止回调
- recorderManager.onStop((res) => {
- console.log('recorder stop', res)
- const recordTime = (Date.now() - startTime.value) / 1000
-
- stopTimer()
- isRecording.value = false
-
- if (recordTime < 3) {
- recordingStatus.value = '录音时间太短(需3秒以上)'
- uni.showToast({
- title: '录音时间太短,需3秒以上',
- icon: 'none'
- })
- return
- }
-
- if (res.tempFilePath) {
- voicePath.value = res.tempFilePath
- recordingStatus.value = `录音完成,时长: ${recordTime.toFixed(1)}秒`
- uni.showToast({
- title: '录音成功',
- icon: 'success'
- })
- } else {
- recordingStatus.value = '录音失败'
- uni.showToast({
- title: '录音失败',
- icon: 'none'
- })
- }
- })
-
- // 录音错误回调
- recorderManager.onError((res) => {
- console.error('recorder error', res)
- stopTimer()
- isRecording.value = false
- recordingStatus.value = `录音错误: ${res.errMsg}`
- uni.showToast({
- title: `录音出错: ${res.errMsg}`,
- icon: 'none'
- })
- })
-
- // 音频播放相关回调
- innerAudioContext.onPlay(() => {
- isPlaying.value = true
- recordingStatus.value = '播放中...'
- })
-
- innerAudioContext.onEnded(() => {
- isPlaying.value = false
- recordingStatus.value = '播放完成'
- })
-
- innerAudioContext.onError((res) => {
- isPlaying.value = false
- console.error('play error', res)
- uni.showToast({
- title: '播放失败',
- icon: 'none'
- })
- })
- }
-
- // 检查权限
- const checkPermission = () => {
- uni.authorize({
- scope: 'scope.record',
- success: () => {
- console.log('已授权录音权限')
- },
- fail: (err) => {
- console.log('未授权录音权限', err)
- uni.showModal({
- title: '提示',
- content: '需要录音权限才能使用此功能',
- confirmText: '去设置',
- success: (res) => {
- if (res.confirm) {
- uni.openSetting()
- }
- }
- })
- }
- })
- }
-
- // 开始录音
- const startRecord = (e) => {
- // 阻止默认行为,防止触摸事件触发其他行为
- e.preventDefault()
-
- if (isRecording.value) return
-
- console.log('开始录音')
- recordingStatus.value = '准备录音...'
-
- // 适配不同平台的录音参数
- const options = {
- duration: 60000, // 最长60秒
- sampleRate: 44100,
- numberOfChannels: 1,
- encodeBitRate: 192000,
- format: 'mp3',
- frameSize: 50 // iOS特定配置
- }
-
- // Android可能需要不同的配置
- if (uni.getSystemInfoSync().platform === 'android') {
- options.format = 'amr' // Android上amr格式兼容性更好
- }
-
- recorderManager.start(options)
- }
-
- // 结束录音
- const endRecord = (e) => {
- // 阻止默认行为
- e.preventDefault()
-
- if (!isRecording.value) return
-
- console.log('停止录音')
- recorderManager.stop()
- }
-
- // 处理鼠标移出按钮区域的情况
- const handleMouseLeave = (e) => {
- if (isRecording.value) {
- endRecord(e)
- }
- }
-
- // 计时器相关
- const startTimer = () => {
- duration.value = 0
- timer.value = setInterval(() => {
- duration.value += 0.1
- }, 100)
- }
-
- const stopTimer = () => {
- if (timer.value) {
- clearInterval(timer.value)
- timer.value = null
- }
- }
-
- // 播放录音
- const playVoice = () => {
- if (!voicePath.value) {
- uni.showToast({
- title: '没有可播放的录音',
- icon: 'none'
- })
- return
- }
-
- console.log('播放录音:', voicePath.value)
- innerAudioContext.src = voicePath.value
- innerAudioContext.play()
- }
-
- // 生命周期钩子
- onMounted(() => {
- initRecorder()
- checkPermission()
- initItem()
- })
-
- onUnmounted(() => {
- // 组件卸载时清理资源
- stopTimer()
- recorderManager.stop()
- innerAudioContext.stop()
- })
- function initItem() {
- console.log('data', data);
- data.name = props.activeWord.name;
- data.yinbiao = props.activeWord.yinbiao;
- data.jianyi = props.activeWord.jianyi;
- }
- </script>
- <style>
- </style>
|