按钮组件,链接显示修复
This commit is contained in:
@@ -467,6 +467,35 @@ import { openFile as openFileNative, downloadAndOpen } from '@/utils/fileOpener.
|
||||
return text
|
||||
}
|
||||
|
||||
// 判断链接是否为可下载文件
|
||||
const isDownloadLink = (url) => {
|
||||
const supportedExtensions = ['html', 'htm', 'xlsx', 'xls', 'pdf', 'zip', 'rar', '7z', 'md', 'markdown',
|
||||
'docx', 'doc', 'pptx', 'ppt', 'txt', 'csv', 'json', 'xml', 'yaml', 'yml',
|
||||
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp', 'ico', 'tiff', 'tif', 'dxf'
|
||||
]
|
||||
const extPattern = supportedExtensions.join('|')
|
||||
const extRegex = new RegExp(`\\.(${extPattern})(?:\\?|$)`, 'i')
|
||||
return extRegex.test(url)
|
||||
}
|
||||
|
||||
// 渲染下载按钮 HTML
|
||||
const renderDownloadBtn = (href, displayName) => {
|
||||
const ext = ((href.split('.').pop() || '').split('?')[0] || '').toLowerCase()
|
||||
const fileInfo = getFileTypeInfo(ext)
|
||||
const name = displayName || extractFileNameFromUrl(href)
|
||||
return '<span class="chat-inline-link chat-download-btn" data-url="' + href + '">'
|
||||
+ '<span class="chat-dl-icon">' + fileInfo.emoji + '</span>'
|
||||
+ '<span class="chat-dl-name">' + name + '</span>'
|
||||
+ '</span>'
|
||||
}
|
||||
|
||||
// 支持的文件扩展名(用于原始 URL 检测)
|
||||
const supportedExtensions = ['html', 'htm', 'xlsx', 'xls', 'pdf', 'zip', 'rar', '7z', 'md', 'markdown',
|
||||
'docx', 'doc', 'pptx', 'ppt', 'txt', 'csv', 'json', 'xml', 'yaml', 'yml',
|
||||
'jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp', 'ico', 'tiff', 'tif', 'dxf'
|
||||
]
|
||||
const extPattern = supportedExtensions.join('|')
|
||||
|
||||
// Markdown转HTML节点
|
||||
const pareseMarkdown = (content) => {
|
||||
if (!content) return ''
|
||||
@@ -476,14 +505,60 @@ import { openFile as openFileNative, downloadAndOpen } from '@/utils/fileOpener.
|
||||
try {
|
||||
// 转义单词中的单下划线,防止 snarkdown 误解析为斜体
|
||||
content = escapeLoneUnderscores(content)
|
||||
|
||||
// 修复1: URL 中的中文括号编码,防止 snarkdown 截断URL
|
||||
// [text](http://xxx/高二(1)班.xlsx) → [text](http://xxx/高二%281%29班.xlsx)
|
||||
content = content.replace(
|
||||
/\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g,
|
||||
(m, name, url) => '[' + name + '](' + url.replace(/\(/g, '%28').replace(/\)/g, '%29') + ')'
|
||||
)
|
||||
|
||||
// 修复2: 误用括号 [高二](1)班成绩表 → 高二(1)班成绩表
|
||||
// 当 (数字/短文本) 后紧跟中文时,说明不是真实URL,还原为纯文本
|
||||
content = content.replace(
|
||||
/\[([^\]]+)\]\((\d{1,2}|[^\s)]{1,3})\)(?=[\u4e00-\u9fff])/g,
|
||||
'$1($2)'
|
||||
)
|
||||
|
||||
// 先转换表格,再用 snarkdown 处理其他 Markdown
|
||||
content = convertMarkdownTable(content)
|
||||
let html = snarkdown(content)
|
||||
// 将 <a href="..."> 替换为自定义 span,避免 WebView 触发默认下载
|
||||
|
||||
// 步骤1:暂存 <a> 标签,避免 raw URL 替换误伤 href 内的链接
|
||||
const linkPlaceholders = []
|
||||
html = html.replace(
|
||||
/<a\s+href="([^"]*)"[^>]*>(.*?)<\/a>/gi,
|
||||
'<span class="chat-inline-link" data-url="$1">$2</span>'
|
||||
(match, href, innerText) => {
|
||||
const idx = linkPlaceholders.length
|
||||
linkPlaceholders.push({ href, innerText })
|
||||
return '\x00LINK_' + idx + '\x00'
|
||||
}
|
||||
)
|
||||
|
||||
// 步骤2:将正文中的原始文件 URL 替换为下载按钮
|
||||
const rawUrlRegex = new RegExp(
|
||||
`https?://[^\\s<>"'']+\\.(${extPattern})(?:[?#][^\\s<>"'']*)?`,
|
||||
'gi'
|
||||
)
|
||||
html = html.replace(rawUrlRegex, (url) => {
|
||||
return renderDownloadBtn(url)
|
||||
})
|
||||
|
||||
// 步骤3:恢复 <a> 标签并分类处理
|
||||
html = html.replace(/\x00LINK_(\d+)\x00/g, (_, idx) => {
|
||||
const { href, innerText } = linkPlaceholders[idx]
|
||||
if (isDownloadLink(href)) {
|
||||
const plainText = innerText
|
||||
.replace(/<[^>]*>/g, ' ')
|
||||
.replace(/ /gi, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
return renderDownloadBtn(href, plainText)
|
||||
}
|
||||
// 普通链接保持原有样式
|
||||
return '<span class="chat-inline-link" data-url="' + href + '">' + innerText + '</span>'
|
||||
})
|
||||
|
||||
return html
|
||||
} catch (e) {
|
||||
console.error('解析失败', e)
|
||||
@@ -839,6 +914,50 @@ import { openFile as openFileNative, downloadAndOpen } from '@/utils/fileOpener.
|
||||
}
|
||||
return urlStr
|
||||
}
|
||||
|
||||
// 文件类型 → 图标和标签映射
|
||||
const getFileTypeInfo = (ext) => {
|
||||
const map = {
|
||||
// 文档类
|
||||
pdf: { emoji: '📕', label: 'PDF', color: '#e74c3c' },
|
||||
doc: { emoji: '📘', label: 'Word', color: '#2b579a' },
|
||||
docx: { emoji: '📘', label: 'Word', color: '#2b579a' },
|
||||
xls: { emoji: '📗', label: 'Excel', color: '#217346' },
|
||||
xlsx: { emoji: '📗', label: 'Excel', color: '#217346' },
|
||||
ppt: { emoji: '📙', label: 'PPT', color: '#d24726' },
|
||||
pptx: { emoji: '📙', label: 'PPT', color: '#d24726' },
|
||||
txt: { emoji: '📄', label: '文本', color: '#666' },
|
||||
md: { emoji: '📝', label: 'Markdown', color: '#333' },
|
||||
markdown: { emoji: '📝', label: 'Markdown', color: '#333' },
|
||||
csv: { emoji: '📊', label: 'CSV', color: '#217346' },
|
||||
json: { emoji: '📋', label: 'JSON', color: '#f0a500' },
|
||||
xml: { emoji: '📋', label: 'XML', color: '#e67e22' },
|
||||
yaml: { emoji: '📋', label: 'YAML', color: '#e67e22' },
|
||||
yml: { emoji: '📋', label: 'YAML', color: '#e67e22' },
|
||||
// 压缩包
|
||||
zip: { emoji: '📦', label: 'ZIP', color: '#f39c12' },
|
||||
rar: { emoji: '📦', label: 'RAR', color: '#f39c12' },
|
||||
'7z': { emoji: '📦', label: '7Z', color: '#f39c12' },
|
||||
// 网页
|
||||
html: { emoji: '🌐', label: '网页', color: '#e44d26' },
|
||||
htm: { emoji: '🌐', label: '网页', color: '#e44d26' },
|
||||
// 图片
|
||||
jpg: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
|
||||
jpeg: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
|
||||
png: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
|
||||
gif: { emoji: '🖼️', label: 'GIF', color: '#9b59b6' },
|
||||
bmp: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
|
||||
svg: { emoji: '🖼️', label: 'SVG', color: '#9b59b6' },
|
||||
webp: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
|
||||
ico: { emoji: '🖼️', label: '图标', color: '#9b59b6' },
|
||||
tiff: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
|
||||
tif: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
|
||||
// CAD/设计类
|
||||
dxf: { emoji: '📐', label: 'CAD/DXF', color: '#2c3e50' },
|
||||
}
|
||||
return map[ext] || { emoji: '📎', label: ext.toUpperCase(), color: '#888' }
|
||||
}
|
||||
|
||||
const showMessageDetail = (message) => {
|
||||
detailLinks.value = extractLinks(message.content || '')
|
||||
showMessageModal.value = true
|
||||
@@ -1381,7 +1500,8 @@ import { openFile as openFileNative, downloadAndOpen } from '@/utils/fileOpener.
|
||||
const takeConversationMessages = async () => {
|
||||
try {
|
||||
allmessages.value = await getConversationMessages(userToken.value, currentSessionId.value) || [];
|
||||
// console.log("获取到的所有消息:", JSON.stringify(allmessages.value) );
|
||||
console.log("获取到的所有消息:", JSON.stringify(allmessages.value) );
|
||||
console.log("获取到的所有消息:", allmessages.value);
|
||||
// currentMessages.value = allmessages.value.slice(-pageInfoNumber);
|
||||
// 数据获取后执行滚动
|
||||
scrollToBottom();
|
||||
@@ -1654,6 +1774,43 @@ import { openFile as openFileNative, downloadAndOpen } from '@/utils/fileOpener.
|
||||
text-decoration: underline;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* 可下载文件链接 → 按钮卡片样式 */
|
||||
.chat-download-btn {
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 8rpx 14rpx;
|
||||
margin: 4rpx 0;
|
||||
background: #f5f7fb;
|
||||
border: 2rpx solid #e4e8f0;
|
||||
border-radius: 8rpx;
|
||||
text-decoration: none !important;
|
||||
color: #333 !important;
|
||||
word-break: break-all;
|
||||
transition: all 0.15s ease;
|
||||
|
||||
&:active {
|
||||
background: #eef1f7;
|
||||
border-color: #c8cde0;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.chat-dl-icon {
|
||||
font-size: 26rpx !important;
|
||||
line-height: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.chat-dl-name {
|
||||
font-size: 16px !important;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 流式气泡中的加载动画 */
|
||||
|
||||
Reference in New Issue
Block a user