From 80bd1ee181b83ee65f5ec773a51cc00723efa8f9 Mon Sep 17 00:00:00 2001 From: francis_fh <13935151924@163.com> Date: Thu, 22 Jan 2026 14:05:22 +0800 Subject: [PATCH] =?UTF-8?q?AI=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=AB=AF=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/md-render/md-render.vue | 183 ++++++++++++++++----------- config.js | 9 +- hook/useTTSPlayer.js | 21 ++- pages/chat/components/ai-paging.vue | 99 +++++++++++---- pages/index/components/index-one.vue | 4 +- utils/markdownParser.js | 70 ++++++---- 6 files changed, 246 insertions(+), 140 deletions(-) diff --git a/components/md-render/md-render.vue b/components/md-render/md-render.vue index 4cc4e90..1865468 100644 --- a/components/md-render/md-render.vue +++ b/components/md-render/md-render.vue @@ -1,7 +1,12 @@ @@ -267,7 +272,7 @@ ol { diff --git a/config.js b/config.js index 7b4efda..31333da 100644 --- a/config.js +++ b/config.js @@ -6,8 +6,8 @@ */ export default { // baseUrl: 'http://39.98.44.136:8080', // 测试 - baseUrl: 'https://www.xjksly.cn/api/ks', // 正式环境 - // baseUrl: 'http://ks.zhaopinzao8dian.com/api/ks', // 测试 + // baseUrl: 'https://www.xjksly.cn/api/ks', // 正式环境 + baseUrl: 'http://ks.zhaopinzao8dian.com/api/ks', // 测试 // LCBaseUrl:'http://10.110.145.145:9100',//内网端口 // LCBaseUrlInner:'http://10.110.145.145:10100',//招聘、培训、帮扶 @@ -22,10 +22,11 @@ export default { StreamBaseURl: 'https://www.xjksly.cn/api/ks/app/chat', // StreamBaseURl: 'https://qd.zhaopinzao8dian.com/ai/test', // 语音转文字 - vioceBaseURl: 'wss://qd.zhaopinzao8dian.com/api/ks/app/speech/asr', + vioceBaseURl: 'https://www.xjksly.cn/api/ks/app/speech/asr', // vioceBaseURl: 'wss://qd.zhaopinzao8dian.com/api/speech-recognition', // 语音合成 - speechSynthesis: 'wss://qd.zhaopinzao8dian.com/api/speech-synthesis', + speechSynthesis: 'https://www.xjksly.cn/api/ks/app/speech/tts', + // speechSynthesis: 'wss://qd.zhaopinzao8dian.com/api/speech-synthesis', // indexedDB DBversion: 2, // 只使用本地缓寸的数据 diff --git a/hook/useTTSPlayer.js b/hook/useTTSPlayer.js index b11ff3a..24a2689 100644 --- a/hook/useTTSPlayer.js +++ b/hook/useTTSPlayer.js @@ -292,19 +292,14 @@ export function useTTSPlayer(wsUrl) { onHide(cancelAudio) onUnload(cancelAudio) - // 只在支持 AudioContext 的环境中初始化 WebSocket - if (audioContext) { - initWebSocket() - } - - return { - speak, - pause, - resume, - cancelAudio, - isSpeaking, - isPaused, - isComplete + return { + speak, + pause, + resume, + cancelAudio, + isSpeaking, + isPaused, + isComplete } } diff --git a/pages/chat/components/ai-paging.vue b/pages/chat/components/ai-paging.vue index 81d4b09..e67e54b 100644 --- a/pages/chat/components/ai-paging.vue +++ b/pages/chat/components/ai-paging.vue @@ -134,9 +134,16 @@ {{ recognizedText }} {{ lastFinalText }} - - - + +
+ + + + + + AI正在思考中... +
+
@@ -1005,12 +1012,26 @@ image-margin-top = 40rpx .messageNull display: none .msg-loading{ - background: transparent; - font-size: 24rpx; - color: #8f8d8e; + background: #F6F6F6; + border-radius: 20rpx 0 20rpx 20rpx; + padding: 20rpx; + width: fit-content; display: flex; - align-items: flex-end; - justify-content: flex-start; + align-items: center; + justify-content: center; + + .loading-content{ + display: flex; + align-items: center; + justify-content: center; + gap: 16rpx; + } + + .loading-text{ + font-size: 28rpx; + color: #666666; + font-weight: 500; + } } .loaded{ padding-left: 20rpx @@ -1250,25 +1271,55 @@ image-margin-top = 40rpx .file-border width: 160rpx !important; -@keyframes ai-circle { - 0% { - -webkit-transform: rotate(0); - transform: rotate(0); +/* 更美观的loading动画 - 兼容H5和小程序 */ +@keyframes ai-loading-dots { + 0%, 20%, 80%, 100% { + transform: scale(1); + opacity: 0.6; } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); + 40% { + transform: scale(1.2); + opacity: 1; } } -.ai-loading + +/* 重置默认样式 */ +.ai-loading { display: inline-flex; - vertical-align: middle; - width: 28rpx; - height: 28rpx; - background: 0 0; + align-items: center; + justify-content: center; + gap: 8rpx; + width: auto; + height: auto; + background: transparent; + border: none; + border-radius: 0; + padding: 0; + margin: 0; +} + +/* 三个点的样式 - 使用标准CSS语法,不使用嵌套 */ +.ai-loading span { + display: inline-block; + width: 12rpx; + height: 12rpx; border-radius: 50%; - border: 4rpx solid; - border-color: #e5e5e5 #e5e5e5 #e5e5e5 #8f8d8e; - -webkit-animation: ai-circle 1s linear infinite; - animation: ai-circle 1s linear infinite; + background-color: #256BFA; + animation: ai-loading-dots 1.4s ease-in-out infinite both; + margin: 0; + padding: 0; +} + +/* 为每个点设置不同的动画延迟 */ +.ai-loading span:nth-child(1) { + animation-delay: -0.32s; +} + +.ai-loading span:nth-child(2) { + animation-delay: -0.16s; +} + +.ai-loading span:nth-child(3) { + animation-delay: 0s; +} diff --git a/pages/index/components/index-one.vue b/pages/index/components/index-one.vue index e30814e..a7aee53 100644 --- a/pages/index/components/index-one.vue +++ b/pages/index/components/index-one.vue @@ -255,10 +255,10 @@ 添加 - + diff --git a/utils/markdownParser.js b/utils/markdownParser.js index 1611be9..451c4a1 100644 --- a/utils/markdownParser.js +++ b/utils/markdownParser.js @@ -19,38 +19,20 @@ const md = new MarkdownIt({ const result = safeExtractJson(str); if (result) { // json解析成功 const jobId = result.appJobUrl.split('jobId=')[1] - let domContext = ` - -
- ${result.jobTitle} -
${result.salary}
-
-
${result.location}·${result.companyName}
-
-
-
${result.education}
-
${result.experience}
-
-
查看详情
-
-
- ` + let domContext = `
${result.jobTitle}
${result.salary}
${result.location}·${result.companyName}
${result.education}
${result.experience}
查看详情
` if (result.data) { jobMoreMap.set(jobId, result.data) - domContext += - `查看更多岗位
` + domContext += `查看更多岗位
` } return domContext } } - //
${result.location}
- //
${result.salary}
// 代码块 let preCode = "" try { preCode = hljs.highlightAuto(str).value } catch (err) { - preCode = markdownIt.utils.escapeHtml(str); + preCode = md.utils.escapeHtml(str); } // 以换行进行分割 , 按行拆分代码 const lines = preCode.split(/\n/).slice(0, -1); @@ -58,7 +40,7 @@ const md = new MarkdownIt({ .map((line, index) => line ? `
  • ${line}
  • ` : - '' + '
  • ' ) .join(''); @@ -127,9 +109,49 @@ export function clearJobMoreMap() { // 切换对话清空 export function parseMarkdown(content) { if (!content) { - return //处理特殊情况,比如网络异常导致的响应的 content 的值为空 + return [] //处理特殊情况,比如网络异常导致的响应的 content 的值为空 } + + // 过滤掉标签及其内容,这些是AI内部思考过程,不应该显示给用户 + // 1. 处理原始标签(支持多行) + content = content.replace(/<\s*think\s*>[\s\S]*?<\s*\/\s*think\s*>/gi, '') + // 2. 处理HTML编码的标签 + content = content.replace(/<\s*think\s*>[\s\S]*?<\s*\/\s*think\s*>/gi, '') + // 3. 处理部分编码的标签 + content = content.replace(/<\s*think\s*>/gi, '') + content = content.replace(/<\s*\/\s*think\s*>/gi, '') + codeDataList = [] const unsafeHtml = md.render(content || '') - return unsafeHtml + + // 在markdown渲染后再次过滤,确保没有遗漏 + let filteredHtml = unsafeHtml + // 1. 处理原始标签(支持多行) + filteredHtml = filteredHtml.replace(/<\s*think\s*>[\s\S]*?<\s*\/\s*think\s*>/gi, '') + // 2. 处理HTML编码的标签 + filteredHtml = filteredHtml.replace(/<\s*think\s*>[\s\S]*?<\s*\/\s*think\s*>/gi, '') + // 3. 处理部分编码的标签 + filteredHtml = filteredHtml.replace(/<\s*think\s*>/gi, '') + filteredHtml = filteredHtml.replace(/<\s*\/\s*think\s*>/gi, '') + // 4. 单独处理剩余的think标签对 + filteredHtml = filteredHtml.replace(/<think>/gi, '') + filteredHtml = filteredHtml.replace(/<\/think>/gi, '') + filteredHtml = filteredHtml.replace(//gi, '') + filteredHtml = filteredHtml.replace(/<\/think>/gi, '') + + // 根据平台返回不同的内容格式 + // 微信小程序:返回rich-text组件支持的nodes格式 + // H5:直接返回HTML字符串,避免HTML解析错误 + if (process.env.UNI_PLATFORM === 'mp-weixin') { + try { + return parseHtml(filteredHtml) + } catch (error) { + console.error('HTML解析失败:', error) + // 解析失败时返回空数组,避免页面崩溃 + return [] + } + } else { + // H5端直接返回HTML字符串 + return filteredHtml + } } \ No newline at end of file