feat: 添加复制按钮(复制原文)
- 用户和AI消息都添加复制按钮 - 复制原始文本内容(不包含Markdown格式) - 添加Toast提示显示复制成功 - 复制按钮hover时显示蓝色
This commit is contained in:
@@ -174,6 +174,41 @@ async function regenerate(index) {
|
|||||||
await streamGenerate(userMsgIndex);
|
await streamGenerate(userMsgIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 复制消息(复制原文)
|
||||||
|
function copyMessage(index) {
|
||||||
|
const content = messages[index].content;
|
||||||
|
|
||||||
|
navigator.clipboard.writeText(content).then(() => {
|
||||||
|
showToast('已复制到剪贴板');
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('复制失败:', err);
|
||||||
|
// 备用方案
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = content;
|
||||||
|
textarea.style.position = 'fixed';
|
||||||
|
textarea.style.opacity = '0';
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
showToast('已复制到剪贴板');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示提示
|
||||||
|
function showToast(message) {
|
||||||
|
const toast = document.createElement('div');
|
||||||
|
toast.className = 'toast';
|
||||||
|
toast.textContent = message;
|
||||||
|
document.body.appendChild(toast);
|
||||||
|
|
||||||
|
setTimeout(() => toast.classList.add('show'), 10);
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.classList.remove('show');
|
||||||
|
setTimeout(() => document.body.removeChild(toast), 300);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
// 删除消息
|
// 删除消息
|
||||||
function deleteMessage(index) {
|
function deleteMessage(index) {
|
||||||
if (isLoading) return;
|
if (isLoading) return;
|
||||||
@@ -212,14 +247,18 @@ function renderMessages() {
|
|||||||
const avatar = isUser ? '👤' : '🤖';
|
const avatar = isUser ? '👤' : '🤖';
|
||||||
const content = renderMarkdown(msg.content);
|
const content = renderMarkdown(msg.content);
|
||||||
|
|
||||||
// 操作按钮
|
// 操作按钮(复制按钮放在最前面)
|
||||||
|
const copyIcon = `<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>`;
|
||||||
|
|
||||||
const actions = isUser
|
const actions = isUser
|
||||||
? `<div class="message-actions">
|
? `<div class="message-actions">
|
||||||
|
<button class="action-btn copy-btn" onclick="copyMessage(${index})" title="复制">${copyIcon}</button>
|
||||||
<button class="action-btn delete-btn" onclick="deleteMessage(${index})" title="删除">
|
<button class="action-btn delete-btn" onclick="deleteMessage(${index})" title="删除">
|
||||||
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
|
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
|
||||||
</button>
|
</button>
|
||||||
</div>`
|
</div>`
|
||||||
: `<div class="message-actions">
|
: `<div class="message-actions">
|
||||||
|
<button class="action-btn copy-btn" onclick="copyMessage(${index})" title="复制">${copyIcon}</button>
|
||||||
<button class="action-btn regenerate-btn" onclick="regenerate(${index})" title="重新生成">
|
<button class="action-btn regenerate-btn" onclick="regenerate(${index})" title="重新生成">
|
||||||
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
|
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -299,6 +299,28 @@ body {
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Toast 提示 */
|
||||||
|
.toast {
|
||||||
|
position: fixed;
|
||||||
|
top: 80px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%) translateY(-20px);
|
||||||
|
background: var(--text-color);
|
||||||
|
color: white;
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
z-index: 1000;
|
||||||
|
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.show {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
/* 消息结构 */
|
/* 消息结构 */
|
||||||
.message-body {
|
.message-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -345,6 +367,12 @@ body {
|
|||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.action-btn.copy-btn:hover {
|
||||||
|
background: #dbeafe;
|
||||||
|
border-color: var(--primary);
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
.action-btn.delete-btn:hover {
|
.action-btn.delete-btn:hover {
|
||||||
background: #fee2e2;
|
background: #fee2e2;
|
||||||
border-color: #e53e3e;
|
border-color: #e53e3e;
|
||||||
|
|||||||
Reference in New Issue
Block a user