From 07e0f3083b8f206436ec04b9d2b27ce88c615086 Mon Sep 17 00:00:00 2001 From: francis_fh <13935151924@163.com> Date: Sat, 24 Jan 2026 16:47:11 +0800 Subject: [PATCH] =?UTF-8?q?AI=E5=9B=9E=E5=A4=8D=E5=86=85=E5=AE=B9=EF=BC=8C?= =?UTF-8?q?=E5=B2=97=E4=BD=8D=E5=8D=A1=E7=89=87=E7=9A=84=E7=82=B9=E5=87=BB?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E7=BB=91=E5=AE=9A=E9=97=AE=E9=A2=98=E8=A7=A3?= =?UTF-8?q?=E5=86=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/md-render/md-render.vue | 745 ++++++++++++++++++++++++----- utils/markdownParser.js | 351 ++++++++------ 2 files changed, 818 insertions(+), 278 deletions(-) diff --git a/components/md-render/md-render.vue b/components/md-render/md-render.vue index 1865468..6a1bd41 100644 --- a/components/md-render/md-render.vue +++ b/components/md-render/md-render.vue @@ -2,17 +2,44 @@ - + + + + + + + + + + {{ card.jobTitle }} + {{ card.salary }} + + {{ card.location }}·{{ card.companyName }} + + + {{ card.education }} + {{ card.experience }} + + 查看详情 + + + + - + diff --git a/utils/markdownParser.js b/utils/markdownParser.js index 451c4a1..c877742 100644 --- a/utils/markdownParser.js +++ b/utils/markdownParser.js @@ -1,157 +1,194 @@ -import MarkdownIt from '@/lib/markdown-it.min.js'; -import hljs from "@/lib/highlight/highlight-uni.min.js"; -import parseHtml from '@/lib/html-parser.js'; -// import DOMPurify from '@/lib/dompurify@3.2.4es.js'; - -export let codeDataList = [] -export let jobMoreMap = new Map() - -const md = new MarkdownIt({ - html: true, // 允许 HTML 标签 - linkify: true, // 自动解析 URL - typographer: true, // 美化标点符号 - tables: true, - breaks: true, // 让 \n 自动换行 - langPrefix: 'language-', // 代码高亮前缀 - // 如果结果以
${result.jobTitle}
${result.salary}
${result.location}·${result.companyName}
${result.education}
${result.experience}
查看详情
` - if (result.data) { - jobMoreMap.set(jobId, result.data) - domContext += `查看更多岗位
` - } - return domContext - } - } - // 代码块 - let preCode = "" - try { - preCode = hljs.highlightAuto(str).value - } catch (err) { - preCode = md.utils.escapeHtml(str); - } - // 以换行进行分割 , 按行拆分代码 - const lines = preCode.split(/\n/).slice(0, -1); - const html = lines - .map((line, index) => - line ? - `
  • ${line}
  • ` : - '
  • ' - ) - .join(''); - - // 代码复制功能 - const cacheIndex = codeDataList.length; - codeDataList.push(str); - return ` -
    -
    - ${lang || 'plaintext'} - 复制代码 -
    -
      ${html}
    -
    - `; - } -}) - -function extractFirstJson(text) { - let stack = []; - let startIndex = -1; - let endIndex = -1; - - for (let i = 0; i < text.length; i++) { - const char = text[i]; - - if (char === '{') { - if (stack.length === 0) startIndex = i; // 记录第一个 '{' 的位置 - stack.push(char); - } else if (char === '}') { - stack.pop(); - if (stack.length === 0) { - endIndex = i; // 找到配对的 '}' - break; - } - } - } - - if (startIndex !== -1 && endIndex !== -1) { - const jsonString = text.slice(startIndex, endIndex + 1); - try { - const jsonObject = JSON.parse(jsonString); - return jsonObject; - } catch (e) { - return null; // 如果不是有效的 JSON - } - } - - return null; // 如果没有找到有效的 JSON 对象 -} - - -function safeExtractJson(text) { - try { - const jsonObject = extractFirstJson(text); - return jsonObject - } catch (e) { - console.error('JSON 解析失败:', e); - } - return null; -} - -export function clearJobMoreMap() { // 切换对话清空 - jobMoreMap.clear() -} - -export function parseMarkdown(content) { - if (!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 || '') - - // 在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 +import MarkdownIt from '@/lib/markdown-it.min.js'; +import hljs from "@/lib/highlight/highlight-uni.min.js"; +import parseHtml from '@/lib/html-parser.js'; +// import DOMPurify from '@/lib/dompurify@3.2.4es.js'; + +export let codeDataList = [] +export let jobMoreMap = new Map() +export let jobCardsList = [] + +const md = new MarkdownIt({ + html: true, // 允许 HTML 标签 + linkify: true, // 自动解析 URL + typographer: true, // 美化标点符号 + tables: true, + breaks: true, // 让 \n 自动换行 + langPrefix: 'language-', // 代码高亮前缀 + // 如果结果以
    ${result.jobTitle}
    ${result.salary}
    ${result.location}·${result.companyName}
    ${result.education}
    ${result.experience}
    查看详情
    ` + if (result.data) { + jobMoreMap.set(jobId, result.data) + domContext += `查看更多岗位
    ` + } + return domContext + } + } + // 代码块 + let preCode = "" + try { + preCode = hljs.highlightAuto(str).value + } catch (err) { + preCode = md.utils.escapeHtml(str); + } + // 以换行进行分割 , 按行拆分代码 + const lines = preCode.split(/\n/).slice(0, -1); + const html = lines + .map((line, index) => + line ? + `
  • ${line}
  • ` : + '
  • ' + ) + .join(''); + + // 代码复制功能 + const cacheIndex = codeDataList.length; + codeDataList.push(str); + return ` +
    +
    + ${lang || 'plaintext'} + 复制代码 +
    +
      ${html}
    +
    + `; + } +}) + +function extractFirstJson(text) { + let stack = []; + let startIndex = -1; + let endIndex = -1; + + for (let i = 0; i < text.length; i++) { + const char = text[i]; + + if (char === '{') { + if (stack.length === 0) startIndex = i; // 记录第一个 '{' 的位置 + stack.push(char); + } else if (char === '}') { + stack.pop(); + if (stack.length === 0) { + endIndex = i; // 找到配对的 '}' + break; + } + } + } + + if (startIndex !== -1 && endIndex !== -1) { + const jsonString = text.slice(startIndex, endIndex + 1); + try { + const jsonObject = JSON.parse(jsonString); + return jsonObject; + } catch (e) { + return null; // 如果不是有效的 JSON + } + } + + return null; // 如果没有找到有效的 JSON 对象 +} + + +function safeExtractJson(text) { + try { + const jsonObject = extractFirstJson(text); + return jsonObject + } catch (e) { + console.error('JSON 解析失败:', e); + } + return null; +} + +export function clearJobMoreMap() { // 切换对话清空 + jobMoreMap.clear() +} + +export function parseMarkdown(content) { + if (!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 = [] + jobCardsList = [] // 清空岗位卡片列表,避免重复 + const unsafeHtml = md.render(content || '') + + // 在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 + } +}