import axios from 'axios' import { getToken } from '../../../../src/utils/auth' // 替换文本中的特殊提示标签 export function replaceAiTipTags(text: string): string { if (!text) { return '' } // 对于字符串 (##3$$、##2$$、##1$$) 匹配 ##数字$$,并替换为 $1 是捕获到的 数字 // 还有字符串 [asdaID:3、 ID:32vccca、****ID:****] 匹配 ID:数字,并替换为 const updatedText = text .replace(/ID:(\d+)/g, '') .replace(/##(\d+)\$\$/g, '') return updatedText } const PLACEHOLDER = '\u0000' const HTML_ENTITIES: Record = { nbsp: ' ', amp: '&', lt: '<', gt: '>', quot: '"', apos: "'", '#39': "'" } /** * 把 AI 回答的 markdown 源码转成纯文本:去掉 ** _ # ` 等标记和 、 等 HTML 标签, * 保留换行、缩进、列表符号等段落结构。围栏代码块与行内代码的内容原样保留,不参与标记剥离。 */ export function markdownToPlainText(markdown: string): string { if (!markdown || typeof markdown !== 'string') { return '' } const preserved: string[] = [] // 代码块、行内代码与转义字符先换成占位符,避免后续规则改写它们的字面内容 const keep = (chunk: string) => { preserved.push(chunk) return `${PLACEHOLDER}${preserved.length - 1}${PLACEHOLDER}` } let text = markdown.replace(/[\s\S]*?<\/think>/gi, '') text = text.replace(/```[^\n]*\n([\s\S]*?)```/g, (_, code) => keep(code)) text = text.replace(/`([^`\n]+)`/g, (_, code) => keep(code)) text = text.replace(/\\([\\`*_{}[\]()#+\-.!>~|])/g, (_, escaped) => keep(escaped)) text = text.replace(//g, '') text = text .replace(/<((?:https?|ftp|mailto):[^>\n]*)>/gi, '$1') .replace(//gi, '\n') .replace(/<\/(p|div|h[1-6]|li|ul|ol|tr|table|thead|tbody|blockquote)>/gi, '\n') // 引用标记常被方括号包住:[],连同方括号一起删,避免残留 [] .replace(/\[\s*(?:]*>(?:\s*<\/amb-[a-z-]+>)?\s*)+\]/gi, '') .replace(/<\/?[a-zA-Z][a-zA-Z0-9-]*(?:\s[^<>]*)?\/?>/g, '') .replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1') .replace(/\[([^\]]*)\]\([^)]*\)/g, '$1') .replace(/&(nbsp|amp|lt|gt|quot|apos|#39);/gi, (match, entity) => HTML_ENTITIES[entity.toLowerCase()] || match) text = text .replace(/~~(?=\S)([\s\S]*?\S)~~/g, '$1') .replace(/\*\*\*(?=\S)([\s\S]*?\S)\*\*\*/g, '$1') .replace(/\*\*(?=\S)([\s\S]*?\S)\*\*/g, '$1') .replace(/\*(\S(?:[^*\n]*\S)?)\*/g, '$1') .replace(/__(\S(?:[^_\n]*\S)?)__/g, '$1') .replace(/(^|[\s((【])_(\S(?:[^_\n]*\S)?)_(?=[\s),.,。、;;::!!??】]|$)/g, '$1$2') text = text .replace(/^\s{0,3}#{1,6}\s+/gm, '') .replace(/^\s{0,3}#{1,6}\s*$/gm, '') .replace(/^\s{0,3}>+\s?/gm, '') .replace(/^\s*([-*_])(?:\s*\1){2,}\s*$/gm, '') .replace(/\r?\n^[ \t]*\|?[ \t:|-]*--+[ \t:|-]*\|?[ \t]*$/gm, '') text = text.replace(/[ \t]+$/gm, '').replace(/ +(?=[。,、;:!?)】”])/g, '').replace(/\n{3,}/g, '\n\n') return text .replace(new RegExp(`${PLACEHOLDER}(\\d+)${PLACEHOLDER}`, 'g'), (_, index) => preserved[Number(index)] ?? '') .trim() } // 解析回答内容,分离思考内容和主要内容 export function parseAnswerContent(answer: string): { thinkingContent: string; mainContent: string } { if (!answer) { return { thinkingContent: '', mainContent: '' } } if (answer.startsWith('')) { const thinkEndIndex = answer.indexOf('') return { thinkingContent: answer.substring(7, thinkEndIndex), mainContent: answer.substring(thinkEndIndex + 8) } } return { thinkingContent: '', mainContent: answer } } export function extractFilenameFromUrl(fileUrl: string): string | null { if (!fileUrl) return null try { const u = new URL(fileUrl) const raw = u.searchParams.get('response-content-disposition') || u.searchParams.get('content-disposition') || '' if (!raw) return null let dispo = raw try { dispo = decodeURIComponent(dispo) } catch (e) { // ignore } const rfc5987 = dispo.match(/filename\*\s*=\s*([^'']*)''([^;]+)/i) if (rfc5987 && rfc5987[2]) { try { return decodeURIComponent(rfc5987[2].trim()).replace(/^"|"$/g, '') } catch (e) { return rfc5987[2].trim().replace(/^"|"$/g, '') } } const quoted = dispo.match(/filename\s*=\s*"([^"]+)"/i) if (quoted && quoted[1]) { try { return decodeURIComponent(quoted[1]) } catch (e) { return quoted[1] } } const unquoted = dispo.match(/filename\s*=\s*([^;]+)/i) if (unquoted && unquoted[1]) { const name = unquoted[1].trim().replace(/^"|"$/g, '') try { return decodeURIComponent(name) } catch (e) { return name } } return null } catch (err) { return null } } export function sendExportChartRequest(requestParams: any): Promise { axios.defaults.headers.common.Authorization = getToken() return axios .post(window.$vueApp.config.globalProperties.baseAPI + '/component/ai/chart/export-excel', requestParams, { responseType: 'blob' }) .then((res) => { const link = document.createElement('a') link.style.display = 'none' link.href = URL.createObjectURL(res.data) link.setAttribute('download', '质量报告.xlsx') document.body.appendChild(link) link.click() document.body.removeChild(link) }) }