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)
})
}