fix audio
This commit is contained in:
@@ -129,7 +129,10 @@ interface IPageData {
|
||||
cachedSentenceIndex: number,
|
||||
isWordEmptyResult: boolean,
|
||||
recordPermissionGranted: boolean
|
||||
loadingMaskVisible: boolean
|
||||
loadingMaskVisible: boolean,
|
||||
bottomLocked: boolean,
|
||||
standardAudioLocalMap: { [key: string]: string }, // 标准语音文件ID映射
|
||||
assessmentAudioLocalMap: { [key: string]: string } // 评估语音文件ID映射
|
||||
}
|
||||
|
||||
type IPageMethods = {
|
||||
@@ -228,6 +231,9 @@ Page<IPageData, IPageInstance>({
|
||||
{ tag: 4, description: '未录入', color: '#f5f5f5' }
|
||||
],
|
||||
standardAudioMap: {}, // 标准语音文件ID映射
|
||||
// 页面级音频本地缓存:避免重复网络获取
|
||||
standardAudioLocalMap: {},
|
||||
assessmentAudioLocalMap: {},
|
||||
isPlaying: false, // 是否正在播放音频
|
||||
audioDuration: 0, // 音频总时长
|
||||
currentTime: 0, // 当前播放时间
|
||||
@@ -267,7 +273,8 @@ Page<IPageData, IPageInstance>({
|
||||
cachedSentenceIndex: -1,
|
||||
isMoreMenuClosing: false,
|
||||
recordPermissionGranted: false,
|
||||
loadingMaskVisible: false
|
||||
loadingMaskVisible: false,
|
||||
bottomLocked: false,
|
||||
},
|
||||
|
||||
onMicHighlight() {
|
||||
@@ -542,6 +549,7 @@ Page<IPageData, IPageInstance>({
|
||||
|
||||
// 长按开始录音
|
||||
handleRecordStart() {
|
||||
if (this.data.bottomLocked) return
|
||||
this.startRecording()
|
||||
try { this.onMicHighlight() } catch (e) {}
|
||||
},
|
||||
@@ -604,7 +612,7 @@ Page<IPageData, IPageInstance>({
|
||||
|
||||
// 播放标准语音
|
||||
playStandardVoice() {
|
||||
if (this.data.isRecording) return
|
||||
if (this.data.bottomLocked || this.data.isRecording) return
|
||||
const { currentSentence, standardAudioMap, isPlaying } = this.data
|
||||
const audioUrl = standardAudioMap[currentSentence.id]
|
||||
|
||||
@@ -622,17 +630,17 @@ Page<IPageData, IPageInstance>({
|
||||
return
|
||||
}
|
||||
|
||||
// 使用文件下载接口获取音频文件路径
|
||||
apiManager.downloadFile(audioUrl).then((filePath) => {
|
||||
// 如果当前正在播放且音源一致,则切换为暂停
|
||||
// 优先使用页面本地缓存
|
||||
const cachedLocal = this.data.standardAudioLocalMap[currentSentence.id]
|
||||
const playWithPath = (filePath: string) => {
|
||||
if (isPlaying && this.audioContext?.src === filePath) {
|
||||
this.audioContext.pause()
|
||||
// 清除播放图标动画并复位
|
||||
try { this.audioContext.pause() } catch (e) {}
|
||||
try { this.audioContext.seek(0) } catch (e) {}
|
||||
if (this.playIconTimer) {
|
||||
clearInterval(this.playIconTimer)
|
||||
this.playIconTimer = undefined
|
||||
}
|
||||
this.setData({ isPlaying: false, playIconName: 'sound-low' })
|
||||
this.setData({ isPlaying: false, playIconName: 'sound-low', currentTime: 0, sliderValue: 0 })
|
||||
return
|
||||
}
|
||||
|
||||
@@ -738,36 +746,39 @@ Page<IPageData, IPageInstance>({
|
||||
try { this.audioContext.seek(0) } catch (e) {}
|
||||
this.setData({ currentTime: 0, sliderValue: 0, audioDuration: 0 })
|
||||
this.audioContext.src = filePath
|
||||
} else {
|
||||
try { this.audioContext.seek(0) } catch (e) {}
|
||||
this.setData({ currentTime: 0, sliderValue: 0 })
|
||||
}
|
||||
|
||||
const canplayHandler = () => {
|
||||
try {
|
||||
this.audioContext?.play?.()
|
||||
this.setData({ isPlaying: true })
|
||||
} catch (error) {
|
||||
console.error('标准语音播放失败:', error)
|
||||
wx.showToast({ title: '音频播放失败', icon: 'none' })
|
||||
}
|
||||
try { (this.audioContext as any).offCanplay && (this.audioContext as any).offCanplay(canplayHandler) } catch (e) {}
|
||||
try {
|
||||
this.audioContext.play()
|
||||
this.setData({ isPlaying: true })
|
||||
} catch (error) {
|
||||
wx.showToast({ title: '音频播放失败', icon: 'none' })
|
||||
}
|
||||
try { (this.audioContext as any).onCanplay(canplayHandler) } catch (e) {
|
||||
try {
|
||||
this.audioContext.play()
|
||||
this.setData({ isPlaying: true })
|
||||
} catch (error) {
|
||||
console.error('标准语音播放失败(无canplay):', error)
|
||||
wx.showToast({ title: '音频播放失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error('下载音频文件失败:', error)
|
||||
wx.showToast({ title: '音频下载失败', icon: 'none' })
|
||||
})
|
||||
}
|
||||
if (cachedLocal) {
|
||||
playWithPath(cachedLocal)
|
||||
} else {
|
||||
apiManager.downloadFile(audioUrl).then((filePath) => {
|
||||
this.setData({
|
||||
standardAudioLocalMap: {
|
||||
...this.data.standardAudioLocalMap,
|
||||
[currentSentence.id]: filePath
|
||||
}
|
||||
})
|
||||
playWithPath(filePath)
|
||||
}).catch((error) => {
|
||||
console.error('下载音频文件失败:', error)
|
||||
wx.showToast({ title: '音频下载失败', icon: 'none' })
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 新增:播放评分结果音频(使用当前句子的 file_id)
|
||||
playAssessmentVoice() {
|
||||
if (this.data.isRecording) return
|
||||
if (this.data.bottomLocked || this.data.isRecording) return
|
||||
const { currentSentence, isPlaying } = this.data
|
||||
const fileId = currentSentence?.file_id
|
||||
|
||||
@@ -776,17 +787,16 @@ Page<IPageData, IPageInstance>({
|
||||
return
|
||||
}
|
||||
|
||||
// 使用文件下载接口获取音频文件路径
|
||||
apiManager.downloadFile(fileId).then((filePath) => {
|
||||
// 如果当前正在播放且音源一致,则切换为暂停
|
||||
const cachedLocal = this.data.assessmentAudioLocalMap[fileId]
|
||||
const playWithPath = (filePath: string) => {
|
||||
if (isPlaying && this.audioContext?.src === filePath) {
|
||||
this.audioContext.pause()
|
||||
// 清除播放图标动画并复位
|
||||
try { this.audioContext.pause() } catch (e) {}
|
||||
try { this.audioContext.seek(0) } catch (e) {}
|
||||
if (this.playIconTimer) {
|
||||
clearInterval(this.playIconTimer)
|
||||
this.playIconTimer = undefined
|
||||
}
|
||||
this.setData({ isPlaying: false, playIconName: 'sound-low' })
|
||||
this.setData({ isPlaying: false, playIconName: 'sound-low', currentTime: 0, sliderValue: 0 })
|
||||
return
|
||||
}
|
||||
|
||||
@@ -876,37 +886,39 @@ Page<IPageData, IPageInstance>({
|
||||
})
|
||||
}
|
||||
|
||||
// 切换音源并播放
|
||||
if (this.audioContext.src !== filePath) {
|
||||
try { this.audioContext.pause() } catch (e) {}
|
||||
try { this.audioContext.seek(0) } catch (e) {}
|
||||
this.setData({ currentTime: 0, sliderValue: 0, audioDuration: 0 })
|
||||
this.audioContext.src = filePath
|
||||
} else {
|
||||
try { this.audioContext.seek(0) } catch (e) {}
|
||||
this.setData({ currentTime: 0, sliderValue: 0 })
|
||||
}
|
||||
|
||||
const canplayHandler = () => {
|
||||
try {
|
||||
this.audioContext?.play()
|
||||
this.setData({ isPlaying: true })
|
||||
} catch (error) {
|
||||
console.error('评分语音播放失败:', error)
|
||||
wx.showToast({ title: '音频播放失败', icon: 'none' })
|
||||
}
|
||||
try { (this.audioContext as any).offCanplay && (this.audioContext as any).offCanplay(canplayHandler) } catch (e) {}
|
||||
try {
|
||||
this.audioContext.play()
|
||||
this.setData({ isPlaying: true })
|
||||
} catch (error) {
|
||||
wx.showToast({ title: '音频播放失败', icon: 'none' })
|
||||
}
|
||||
try { (this.audioContext as any).onCanplay(canplayHandler) } catch (e) {
|
||||
try {
|
||||
this.audioContext.play()
|
||||
this.setData({ isPlaying: true })
|
||||
} catch (error) {
|
||||
console.error('评分语音播放失败(无canplay):', error)
|
||||
wx.showToast({ title: '音频播放失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error('下载音频文件失败:', error)
|
||||
wx.showToast({ title: '音频下载失败', icon: 'none' })
|
||||
})
|
||||
}
|
||||
if (cachedLocal) {
|
||||
playWithPath(cachedLocal)
|
||||
} else {
|
||||
apiManager.downloadFile(fileId).then((filePath) => {
|
||||
this.setData({
|
||||
assessmentAudioLocalMap: {
|
||||
...this.data.assessmentAudioLocalMap,
|
||||
[fileId]: filePath
|
||||
}
|
||||
})
|
||||
playWithPath(filePath)
|
||||
}).catch((error) => {
|
||||
console.error('下载音频文件失败:', error)
|
||||
wx.showToast({ title: '音频下载失败', icon: 'none' })
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async handleWordClick(e: any) {
|
||||
@@ -1363,7 +1375,10 @@ Page<IPageData, IPageInstance>({
|
||||
this.setData({
|
||||
wordAudioPlaying: false,
|
||||
wordAudioIconName: 'sound',
|
||||
activeWordAudioType: ''
|
||||
activeWordAudioType: '',
|
||||
// 清空页面级音频缓存映射
|
||||
standardAudioLocalMap: {},
|
||||
assessmentAudioLocalMap: {}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -1519,7 +1534,7 @@ Page<IPageData, IPageInstance>({
|
||||
},
|
||||
|
||||
onScoreTap() {
|
||||
if (this.data.isRecording) return
|
||||
if (this.data.bottomLocked || this.data.isRecording) return
|
||||
console.log('Score button tapped')
|
||||
// 当当前选中例句无评分信息时,不响应点击
|
||||
if (!this.data.hasScoreInfo) {
|
||||
@@ -1558,7 +1573,7 @@ Page<IPageData, IPageInstance>({
|
||||
})
|
||||
},
|
||||
onTransTap() {
|
||||
if (this.data.isRecording) return
|
||||
if (this.data.bottomLocked || this.data.isRecording) return
|
||||
console.log('User button tapped')
|
||||
// Cycle through translation display modes
|
||||
const currentMode = this.data.transDisplayMode
|
||||
@@ -1693,7 +1708,7 @@ Page<IPageData, IPageInstance>({
|
||||
},
|
||||
|
||||
onMoreTap() {
|
||||
if (this.data.isRecording) return
|
||||
if (this.data.bottomLocked || this.data.isRecording) return
|
||||
const { isMoreMenuOpen } = this.data
|
||||
if (!isMoreMenuOpen) {
|
||||
this.setData({ isMoreMenuOpen: true, isMoreMenuClosing: false })
|
||||
|
||||
@@ -43,20 +43,20 @@
|
||||
<view wx:if="{{isMoreMenuOpen}}" class="more-menu-modal"></view>
|
||||
<view class="button-row">
|
||||
<t-icon name="{{isPlaying ? 'pause' : 'play'}}" class="bottom-button {{isRecording ? 'disabled' : ''}}" size="48rpx" bind:tap="playStandardVoice" />
|
||||
<view class="bottom-button-img-wrap bottom-button {{isRecording ? 'disabled' : ''}}" bind:tap="onTransTap">
|
||||
<view class="bottom-button-img-wrap bottom-button {{isRecording ? 'disabled' : ''}} {{bottomLocked ? 'disabled' : ''}}" bind:tap="onTransTap">
|
||||
<t-icon name="translate" class="trans-button left-half {{transDisplayMode === 'en_ipa' ? 'trans-active' : 'trans-deactive'}}" size="48rpx" />
|
||||
<t-icon name="translate" class="trans-button right-half {{transDisplayMode === 'en_zh' ? 'trans-active' : 'trans-deactive'}}" size="48rpx" />
|
||||
</view>
|
||||
<view class="bottom-button mic-wrap" bind:longpress="handleRecordStart" bind:touchend="handleRecordEnd" bind:touchcancel="handleRecordEnd" >
|
||||
<view class="bottom-button mic-wrap {{bottomLocked ? 'disabled' : ''}}" bind:longpress="handleRecordStart" bind:touchend="handleRecordEnd" bind:touchcancel="handleRecordEnd" >
|
||||
<t-icon name="microphone-1" color="{{isRecording ? '#FFFFFF' : '#333333'}}"
|
||||
class="microphone {{isRecording ? 'recording' : 'bottom-button'}} {{recordPermissionGranted ? '' : 'disabled'}}" size="48rpx" />
|
||||
<view wx:if="{{isRecording}}" class="mic">
|
||||
<view class="mic-shadow"></view>
|
||||
</view>
|
||||
</view>
|
||||
<t-icon name="fact-check" class="bottom-button {{(hasScoreInfo && !isRecording) ? '' : 'disabled'}}" size="48rpx" bind:tap="onScoreTap" />
|
||||
<t-icon name="fact-check" class="bottom-button {{(hasScoreInfo && !isRecording && !bottomLocked) ? '' : 'disabled'}}" size="48rpx" bind:tap="onScoreTap" />
|
||||
<!-- <t-icon name="ellipsis" class="bottom-button {{isMoreMenuOpen ? 'more-open' : ''}}" size="48rpx" bind:tap="onMoreTap" /> -->
|
||||
<view class="bottom-button {{isRecording ? 'disabled' : ''}}" bindtap="onMoreTap">
|
||||
<view class="bottom-button {{isRecording ? 'disabled' : ''}} {{bottomLocked ? 'disabled' : ''}}" bindtap="onMoreTap">
|
||||
<view class="ul {{isMoreMenuOpen ? 'active' : ''}}">
|
||||
<view class="dot1"></view>
|
||||
<view class="dot2"></view>
|
||||
|
||||
@@ -656,15 +656,6 @@
|
||||
border: 4rpx solid #e0e0e0;
|
||||
}
|
||||
|
||||
.bottom-button.disabled {
|
||||
opacity: 0.4;
|
||||
pointer-events: none;
|
||||
}
|
||||
.bottom-button:active {
|
||||
background: #e0e0e0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.bottom-button-img-wrap {
|
||||
width: 46rpx;
|
||||
height: 46rpx;
|
||||
@@ -1525,3 +1516,9 @@
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.bottom-button.disabled,
|
||||
.mic-wrap.disabled,
|
||||
.bottom-button-img-wrap.disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user