From 852e097e0df25e65487d54a5afd18709dd45aefc Mon Sep 17 00:00:00 2001 From: zhuoqinglian <1035449612@qq.com> Date: Fri, 20 Mar 2026 16:57:02 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20mobile=20=E4=BF=AE=E5=A4=8D=E5=A3=B0?= =?UTF-8?q?=E7=BA=B9=E7=AE=A1=E7=90=86-=E7=BC=96=E8=BE=91=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E4=B8=8B=E5=A3=B0=E7=BA=B9=E5=90=91=E9=87=8F=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E5=BC=82=E5=B8=B8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/manager-mobile/src/pages/voiceprint/index.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/manager-mobile/src/pages/voiceprint/index.vue b/main/manager-mobile/src/pages/voiceprint/index.vue index a00711a5..e6ba2ccc 100644 --- a/main/manager-mobile/src/pages/voiceprint/index.vue +++ b/main/manager-mobile/src/pages/voiceprint/index.vue @@ -137,7 +137,6 @@ async function loadChatHistory() { audioId: item.audioId, index, })) - showChatHistoryDialog.value = true } catch (error) { console.error('获取对话记录失败:', error) @@ -368,6 +367,7 @@ onMounted(async () => { // 智能体已简化为默认 loadVoicePrintList() + loadChatHistory() }) // 暴露方法给父组件 @@ -468,7 +468,7 @@ defineExpose({ Date: Mon, 23 Mar 2026 14:18:53 +0800 Subject: [PATCH 2/2] =?UTF-8?q?update:=20mobile=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E8=AE=B0=E5=BD=95=E9=9F=B3=E9=A2=91=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/chat-history/detail.vue | 19 +-- main/manager-mobile/src/utils/index.ts | 111 +++++++++++++----- 2 files changed, 91 insertions(+), 39 deletions(-) diff --git a/main/manager-mobile/src/pages/chat-history/detail.vue b/main/manager-mobile/src/pages/chat-history/detail.vue index 4d8f91e7..c3d399d5 100644 --- a/main/manager-mobile/src/pages/chat-history/detail.vue +++ b/main/manager-mobile/src/pages/chat-history/detail.vue @@ -13,9 +13,9 @@ import type { ChatMessage, UserMessageContent } from '@/api/chat-history/types' import { onLoad, onUnload } from '@dcloudio/uni-app' import { computed, ref } from 'vue' import { getAudioId, getChatHistory } from '@/api/chat-history/chat-history' -import { getEnvBaseUrl } from '@/utils' -import { toast } from '@/utils/toast' import { t } from '@/i18n' +import { debounce, getEnvBaseUrl } from '@/utils' +import { toast } from '@/utils/toast' defineOptions({ name: 'ChatDetail', @@ -129,7 +129,7 @@ function formatTime(timeStr: string) { } // 播放音频 -async function playAudio(audioId: string) { +const playAudio = debounce(async (audioId: string) => { if (!audioId) { toast.error(t('chatHistory.invalidAudioId')) return @@ -139,8 +139,11 @@ async function playAudio(audioId: string) { // 如果正在播放其他音频,先停止 if (audioContext.value) { audioContext.value.stop() - audioContext.value.destroy() - audioContext.value = null + } + // 如果当前音频ID与请求ID相同暂停播放 + if (playingAudioId.value === audioId) { + playingAudioId.value = null + return } // 获取音频下载ID @@ -151,7 +154,9 @@ async function playAudio(audioId: string) { const audioUrl = `${baseUrl}/agent/play/${downloadId}` // 创建音频上下文 - audioContext.value = uni.createInnerAudioContext() + if (!audioContext.value) { + audioContext.value = uni.createInnerAudioContext() + } audioContext.value.src = audioUrl // 设置播放状态 @@ -185,7 +190,7 @@ async function playAudio(audioId: string) { toast.error(t('chatHistory.playAudioFailed')) playingAudioId.value = null } -} +}, 400) onLoad((options) => { if (options?.sessionId && options?.agentId) { diff --git a/main/manager-mobile/src/utils/index.ts b/main/manager-mobile/src/utils/index.ts index 0d9070b4..8ac842c8 100644 --- a/main/manager-mobile/src/utils/index.ts +++ b/main/manager-mobile/src/utils/index.ts @@ -1,4 +1,6 @@ +import smCrypto from 'sm-crypto' import { pages, subPackages } from '@/pages.json' + import { isMpWeixin } from './platform' /** @@ -197,23 +199,21 @@ export function getEnvBaseUploadUrl() { return baseUploadUrl } -import smCrypto from 'sm-crypto' - /** * 生成SM2密钥对(十六进制格式) * @returns {Object} 包含公钥和私钥的对象 */ export function generateSm2KeyPairHex() { - // 使用sm-crypto库生成SM2密钥对 - const sm2 = smCrypto.sm2; - const keypair = sm2.generateKeyPairHex(); - - return { - publicKey: keypair.publicKey, - privateKey: keypair.privateKey, - clientPublicKey: keypair.publicKey, // 客户端公钥 - clientPrivateKey: keypair.privateKey // 客户端私钥 - }; + // 使用sm-crypto库生成SM2密钥对 + const sm2 = smCrypto.sm2 + const keypair = sm2.generateKeyPairHex() + + return { + publicKey: keypair.publicKey, + privateKey: keypair.privateKey, + clientPublicKey: keypair.publicKey, // 客户端公钥 + clientPrivateKey: keypair.privateKey, // 客户端私钥 + } } /** @@ -223,21 +223,21 @@ export function generateSm2KeyPairHex() { * @returns {string} 加密后的密文(十六进制格式) */ export function sm2Encrypt(publicKey: string, plainText: string): string { - if (!publicKey) { - throw new Error('公钥不能为null或undefined'); - } - - if (!plainText) { - throw new Error('明文不能为空'); - } - - const sm2 = smCrypto.sm2; - // SM2加密,添加04前缀表示未压缩公钥 - const encrypted = sm2.doEncrypt(plainText, publicKey, 1); - // 转换为十六进制格式(与后端保持一致,添加04前缀) - const result = "04" + encrypted; - - return result; + if (!publicKey) { + throw new Error('公钥不能为null或undefined') + } + + if (!plainText) { + throw new Error('明文不能为空') + } + + const sm2 = smCrypto.sm2 + // SM2加密,添加04前缀表示未压缩公钥 + const encrypted = sm2.doEncrypt(plainText, publicKey, 1) + // 转换为十六进制格式(与后端保持一致,添加04前缀) + const result = `04${encrypted}` + + return result } /** @@ -247,9 +247,56 @@ export function sm2Encrypt(publicKey: string, plainText: string): string { * @returns {string} 解密后的明文 */ export function sm2Decrypt(privateKey: string, cipherText: string): string { - const sm2 = smCrypto.sm2; - // 移除04前缀(与后端保持一致) - const dataWithoutPrefix = cipherText.startsWith("04") ? cipherText.substring(2) : cipherText; - // SM2解密 - return sm2.doDecrypt(dataWithoutPrefix, privateKey, 1); + const sm2 = smCrypto.sm2 + // 移除04前缀(与后端保持一致) + const dataWithoutPrefix = cipherText.startsWith('04') ? cipherText.substring(2) : cipherText + // SM2解密 + return sm2.doDecrypt(dataWithoutPrefix, privateKey, 1) +} + +type AnyFunction = (...args: any[]) => any + +interface DebouncedFunction extends AnyFunction { + cancel: () => void +} + +/** + * 防抖函数 + * @param fn 要防抖的函数 + * @param delay 延迟时间(毫秒),默认500ms + * @param immediate 是否立即执行,默认false + * @returns 防抖处理后的函数 + */ +export function debounce( + fn: T, + delay = 500, + immediate = false, +): DebouncedFunction { + let timer: ReturnType | null = null + + const debounced = function (this: any, ...args: Parameters) { + if (timer) { + clearTimeout(timer) + } + + if (immediate && !timer) { + fn.apply(this, args) + } + + timer = setTimeout(() => { + if (!immediate) { + fn.apply(this, args) + } + timer = null + }, delay) + } as DebouncedFunction + + debounced.cancel = () => { + if (timer) { + clearTimeout(timer) + timer = null + } + } + + return debounced }