diff --git a/CHANGELOG.md b/CHANGELOG.md index 5db8f5a..81957ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.12.0](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.11.0...v1.12.0) (2025-05-06) + + +### Features + +* 增加倍速控制 ([99690d9](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/99690d97673423ba99e28b772962e300ce450dea)) + + +### Bug Fixes + +* fix doc ([72c6a53](https://github.com/TencentCloudBase/cloudbase-agent-ui/commit/72c6a53d226cf6426da4569be21d4bc77bba0827)) + ## [1.11.0](https://github.com/TencentCloudBase/cloudbase-agent-ui/compare/v1.10.1...v1.11.0) (2025-04-27) diff --git a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/imgs/playing.svg b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/imgs/playing.svg new file mode 100644 index 0000000..c5b2c36 --- /dev/null +++ b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/imgs/playing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/imgs/sound.svg b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/imgs/sound.svg new file mode 100644 index 0000000..5cb4144 --- /dev/null +++ b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/imgs/sound.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.js b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.js index 0a148d3..04b1193 100644 --- a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.js +++ b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.js @@ -125,6 +125,7 @@ Component({ frameSize: 50, }, voiceRecognizing: false, + speedList: [2, 1.5, 1.25, 1, 0.75], }, attached: async function () { const chatMode = this.data.chatMode; @@ -1912,15 +1913,20 @@ Component({ if (audioContext.recordId === botRecordId) { // 是则直接播放 audioContext.playStatus = 2; + audioContext.showSpeedList = false; + // audioContext.currentSpeed = 1.25; this.setData({ audioContext: audioContext, }); + audioContext.context.playbackRate = audioContext.currentSpeed; audioContext.context.play(); } else { // 需销毁当前的 audioContext TODO:, 先测试复用content, 直接更换src audioContext.context.stop(); // 旧的停止 audioContext.recordId = botRecordId; audioContext.playStatus = 1; + audioContext.showSpeedList = false; + audioContext.currentSpeed = 1.25; this.setData({ audioContext: { ...audioContext, @@ -1930,6 +1936,7 @@ Component({ if (audioUrl) { audioContext.context.src = audioUrl; audioContext.context.seek(0); // 播放进度拉回到0 + audioContext.context.playbackRate = audioContext.currentSpeed; audioContext.context.play(); this.setData({ audioContext: { @@ -1952,11 +1959,20 @@ Component({ const audioContext = { recordId: botRecordId, playStatus: 1, + showSpeedList: false, + currentSpeed: 1.25, }; - const innerAudioContent = wx.createInnerAudioContext({ + const innerAudioContext = wx.createInnerAudioContext({ useWebAudioImplement: false, // 是否使用 WebAudio 作为底层音频驱动,默认关闭。对于短音频、播放频繁的音频建议开启此选项,开启后将获得更优的性能表现。由于开启此选项后也会带来一定的内存增长,因此对于长音频建议关闭此选项 }); - innerAudioContent.onEnded(() => { + try { + await wx.setInnerAudioOption({ + obeyMuteSwitch: false, // 是否遵循系统静音开关,默认遵循 + }); + } catch (e) { + console.log("不遵循静音模式控制", e); + } + innerAudioContext.onEnded(() => { // 音频自然播放至结束触发 this.setData({ audioContext: { @@ -1965,13 +1981,14 @@ Component({ }, }); }); - audioContext.context = innerAudioContent; + audioContext.context = innerAudioContext; this.setData({ audioContext: audioContext, }); const audioUrl = await this.fetchAudioUrlByContent(botRecordId, content); if (audioUrl) { audioContext.context.src = audioUrl; + audioContext.context.playbackRate = audioContext.currentSpeed; // 播放速率,范围 0.5~2.0,默认 1.0 audioContext.context.play(); this.setData({ audioContext: { @@ -2006,14 +2023,38 @@ Component({ console.log("暂停异常"); } }, + toggleSpeedList(e) { + this.setData({ + audioContext: { + ...this.data.audioContext, + showSpeedList: !this.data.audioContext.showSpeedList, + }, + }); + }, + chooseSpeed(e) { + const speed = e.currentTarget.dataset.speed; + console.log("choose speed", speed); + const audioContext = this.data.audioContext; + audioContext.showSpeedList = !this.data.audioContext.showSpeedList; + audioContext.currentSpeed = Number(speed); + audioContext.context.pause(); + audioContext.context.playbackRate = audioContext.currentSpeed; + audioContext.context.play(); + this.setData({ + audioContext: { + ...this.data.audioContext, + ...audioContext, + }, + }); + }, // 触摸开始 handleTouchStart(e) { - if(this.data.chatStatus !== 0 || this.data.voiceRecognizing === true) { + if (this.data.chatStatus !== 0 || this.data.voiceRecognizing === true) { wx.showToast({ title: "请等待对话完成", icon: "error", }); - return + return; } console.log("touchstart e", e); const { clientY } = e.touches[0]; @@ -2033,12 +2074,12 @@ Component({ }, // 触摸移动 handleTouchMove(e) { - if(this.data.chatStatus !== 0 || this.data.voiceRecognizing === true) { + if (this.data.chatStatus !== 0 || this.data.voiceRecognizing === true) { wx.showToast({ title: "请等待对话完成", icon: "error", }); - return + return; } console.log("touchMove"); if (!this.data.longPressTriggered) return; @@ -2061,12 +2102,12 @@ Component({ }, // 触摸结束 handleTouchEnd(e) { - if(this.data.chatStatus !== 0 || this.data.voiceRecognizing === true) { + if (this.data.chatStatus !== 0 || this.data.voiceRecognizing === true) { wx.showToast({ title: "请等待对话完成", icon: "error", }); - return + return; } console.log("touchEnd e", e); clearTimeout(this.data.longPressTimer); diff --git a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxml b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxml index 34298cf..a92187f 100644 --- a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxml +++ b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxml @@ -148,18 +148,33 @@ - - + + - - - - - + + + + + + + + + + + {{audioContext.currentSpeed || '1'}}X + + + + + {{item}}X + + + + diff --git a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxss b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxss index b003d93..9549f10 100644 --- a/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxss +++ b/apps/miniprogram-agent-ui/miniprogram/components/agent-ui/index.wxss @@ -104,9 +104,9 @@ .share_btn { background-color: #fff; margin: 0px !important; - padding: 0px !important; - width: 36rpx !important; - height: 36rpx; + padding: 0rpx !important; + width: 64rpx !important; + height: 64rpx; } .avatar { @@ -671,4 +671,65 @@ background-color: rgb(249, 251, 255); filter: brightness(95%); transition: filter 0.4s; +} + +.tool_btn { + width: 36rpx; + height: 36rpx; + padding: 10rpx; + border: 1rpx solid #cfcdcd; + border-radius: 14rpx; + /* box-sizing: content-box; */ +} + +.playing_btn { + height: 36rpx; + padding: 10rpx; + border: 1rpx solid #cfcdcd; + border-radius: 14rpx; + display: flex; + align-items: center; + gap: 10rpx; + position: relative; +} + +.speed-switch { + display: flex; + align-items: center; + margin-left: 0rpx; + padding: 4rpx 12rpx; + border-radius: 20rpx; + /* background: #f5f5f7; */ + font-size: 26rpx; + color: #222; + cursor: pointer; +} + +.speed-label { + margin-left: 6rpx; +} + +.speed-popup { + position: absolute; + bottom: 48rpx; + right: 0; + background: #fff; + border-radius: 12rpx; + box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.08); + z-index: 99; + padding: 8rpx 0; + min-width: 80rpx; +} + +.speed-option { + padding: 16rpx 32rpx; + font-size: 28rpx; + color: #222; + display: flex; + align-items: center; + justify-content: flex-start; +} + +.speed-option:active { + background: #f0f0f0; } \ No newline at end of file diff --git a/components/agent-ui/imgs/playing.svg b/components/agent-ui/imgs/playing.svg new file mode 100644 index 0000000..c5b2c36 --- /dev/null +++ b/components/agent-ui/imgs/playing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/agent-ui/imgs/sound.svg b/components/agent-ui/imgs/sound.svg new file mode 100644 index 0000000..5cb4144 --- /dev/null +++ b/components/agent-ui/imgs/sound.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/components/agent-ui/index.js b/components/agent-ui/index.js index 0a148d3..04b1193 100644 --- a/components/agent-ui/index.js +++ b/components/agent-ui/index.js @@ -125,6 +125,7 @@ Component({ frameSize: 50, }, voiceRecognizing: false, + speedList: [2, 1.5, 1.25, 1, 0.75], }, attached: async function () { const chatMode = this.data.chatMode; @@ -1912,15 +1913,20 @@ Component({ if (audioContext.recordId === botRecordId) { // 是则直接播放 audioContext.playStatus = 2; + audioContext.showSpeedList = false; + // audioContext.currentSpeed = 1.25; this.setData({ audioContext: audioContext, }); + audioContext.context.playbackRate = audioContext.currentSpeed; audioContext.context.play(); } else { // 需销毁当前的 audioContext TODO:, 先测试复用content, 直接更换src audioContext.context.stop(); // 旧的停止 audioContext.recordId = botRecordId; audioContext.playStatus = 1; + audioContext.showSpeedList = false; + audioContext.currentSpeed = 1.25; this.setData({ audioContext: { ...audioContext, @@ -1930,6 +1936,7 @@ Component({ if (audioUrl) { audioContext.context.src = audioUrl; audioContext.context.seek(0); // 播放进度拉回到0 + audioContext.context.playbackRate = audioContext.currentSpeed; audioContext.context.play(); this.setData({ audioContext: { @@ -1952,11 +1959,20 @@ Component({ const audioContext = { recordId: botRecordId, playStatus: 1, + showSpeedList: false, + currentSpeed: 1.25, }; - const innerAudioContent = wx.createInnerAudioContext({ + const innerAudioContext = wx.createInnerAudioContext({ useWebAudioImplement: false, // 是否使用 WebAudio 作为底层音频驱动,默认关闭。对于短音频、播放频繁的音频建议开启此选项,开启后将获得更优的性能表现。由于开启此选项后也会带来一定的内存增长,因此对于长音频建议关闭此选项 }); - innerAudioContent.onEnded(() => { + try { + await wx.setInnerAudioOption({ + obeyMuteSwitch: false, // 是否遵循系统静音开关,默认遵循 + }); + } catch (e) { + console.log("不遵循静音模式控制", e); + } + innerAudioContext.onEnded(() => { // 音频自然播放至结束触发 this.setData({ audioContext: { @@ -1965,13 +1981,14 @@ Component({ }, }); }); - audioContext.context = innerAudioContent; + audioContext.context = innerAudioContext; this.setData({ audioContext: audioContext, }); const audioUrl = await this.fetchAudioUrlByContent(botRecordId, content); if (audioUrl) { audioContext.context.src = audioUrl; + audioContext.context.playbackRate = audioContext.currentSpeed; // 播放速率,范围 0.5~2.0,默认 1.0 audioContext.context.play(); this.setData({ audioContext: { @@ -2006,14 +2023,38 @@ Component({ console.log("暂停异常"); } }, + toggleSpeedList(e) { + this.setData({ + audioContext: { + ...this.data.audioContext, + showSpeedList: !this.data.audioContext.showSpeedList, + }, + }); + }, + chooseSpeed(e) { + const speed = e.currentTarget.dataset.speed; + console.log("choose speed", speed); + const audioContext = this.data.audioContext; + audioContext.showSpeedList = !this.data.audioContext.showSpeedList; + audioContext.currentSpeed = Number(speed); + audioContext.context.pause(); + audioContext.context.playbackRate = audioContext.currentSpeed; + audioContext.context.play(); + this.setData({ + audioContext: { + ...this.data.audioContext, + ...audioContext, + }, + }); + }, // 触摸开始 handleTouchStart(e) { - if(this.data.chatStatus !== 0 || this.data.voiceRecognizing === true) { + if (this.data.chatStatus !== 0 || this.data.voiceRecognizing === true) { wx.showToast({ title: "请等待对话完成", icon: "error", }); - return + return; } console.log("touchstart e", e); const { clientY } = e.touches[0]; @@ -2033,12 +2074,12 @@ Component({ }, // 触摸移动 handleTouchMove(e) { - if(this.data.chatStatus !== 0 || this.data.voiceRecognizing === true) { + if (this.data.chatStatus !== 0 || this.data.voiceRecognizing === true) { wx.showToast({ title: "请等待对话完成", icon: "error", }); - return + return; } console.log("touchMove"); if (!this.data.longPressTriggered) return; @@ -2061,12 +2102,12 @@ Component({ }, // 触摸结束 handleTouchEnd(e) { - if(this.data.chatStatus !== 0 || this.data.voiceRecognizing === true) { + if (this.data.chatStatus !== 0 || this.data.voiceRecognizing === true) { wx.showToast({ title: "请等待对话完成", icon: "error", }); - return + return; } console.log("touchEnd e", e); clearTimeout(this.data.longPressTimer); diff --git a/components/agent-ui/index.wxml b/components/agent-ui/index.wxml index 34298cf..a92187f 100644 --- a/components/agent-ui/index.wxml +++ b/components/agent-ui/index.wxml @@ -148,18 +148,33 @@ - - + + - - - - - + + + + + + + + + + + {{audioContext.currentSpeed || '1'}}X + + + + + {{item}}X + + + + diff --git a/components/agent-ui/index.wxss b/components/agent-ui/index.wxss index b003d93..9549f10 100644 --- a/components/agent-ui/index.wxss +++ b/components/agent-ui/index.wxss @@ -104,9 +104,9 @@ .share_btn { background-color: #fff; margin: 0px !important; - padding: 0px !important; - width: 36rpx !important; - height: 36rpx; + padding: 0rpx !important; + width: 64rpx !important; + height: 64rpx; } .avatar { @@ -671,4 +671,65 @@ background-color: rgb(249, 251, 255); filter: brightness(95%); transition: filter 0.4s; +} + +.tool_btn { + width: 36rpx; + height: 36rpx; + padding: 10rpx; + border: 1rpx solid #cfcdcd; + border-radius: 14rpx; + /* box-sizing: content-box; */ +} + +.playing_btn { + height: 36rpx; + padding: 10rpx; + border: 1rpx solid #cfcdcd; + border-radius: 14rpx; + display: flex; + align-items: center; + gap: 10rpx; + position: relative; +} + +.speed-switch { + display: flex; + align-items: center; + margin-left: 0rpx; + padding: 4rpx 12rpx; + border-radius: 20rpx; + /* background: #f5f5f7; */ + font-size: 26rpx; + color: #222; + cursor: pointer; +} + +.speed-label { + margin-left: 6rpx; +} + +.speed-popup { + position: absolute; + bottom: 48rpx; + right: 0; + background: #fff; + border-radius: 12rpx; + box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.08); + z-index: 99; + padding: 8rpx 0; + min-width: 80rpx; +} + +.speed-option { + padding: 16rpx 32rpx; + font-size: 28rpx; + color: #222; + display: flex; + align-items: center; + justify-content: flex-start; +} + +.speed-option:active { + background: #f0f0f0; } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f26db3d..28f59c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cloudbase-agent-ui", - "version": "1.11.0", + "version": "1.12.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cloudbase-agent-ui", - "version": "1.11.0", + "version": "1.12.0", "license": "MIT", "dependencies": { "standard-version": "^9.5.0" diff --git a/package.json b/package.json index 0853b9e..6b12845 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cloudbase-agent-ui", - "version": "1.11.0", + "version": "1.12.0", "description": "微信小程序 Agent UI组件", "main": "index.js", "directories": {