From af799e8ee37443cd68581e53f648f328aade1fb9 Mon Sep 17 00:00:00 2001 From: aaron <> Date: Tue, 3 Feb 2026 16:55:29 +0800 Subject: [PATCH] update --- frontend/css/style.css | 153 +++++++++++++++++++++++++++++++++++++++++ frontend/index.html | 46 +++++++++++-- frontend/js/app.js | 105 +++++++++++++++++++++++++++- 3 files changed, 299 insertions(+), 5 deletions(-) diff --git a/frontend/css/style.css b/frontend/css/style.css index 264a81c..14c98c3 100644 --- a/frontend/css/style.css +++ b/frontend/css/style.css @@ -201,6 +201,83 @@ html, body { color: var(--text-primary); } +/* Message Actions */ +.message-actions { + display: flex; + gap: 8px; + margin-top: 12px; + padding-top: 12px; + border-top: 1px solid var(--border); +} + +.action-btn { + display: flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + background: transparent; + border: 1px solid var(--border-bright); + border-radius: 2px; + color: var(--text-secondary); + font-size: 12px; + cursor: pointer; + transition: all 0.2s; +} + +.action-btn:hover { + background: var(--accent-dim); + border-color: var(--accent); + color: var(--accent); +} + +.action-btn svg { + flex-shrink: 0; +} + +/* Share Image Container (hidden, used for rendering) */ +.share-image-container { + position: fixed; + left: -9999px; + top: 0; + width: 800px; + background: var(--bg-secondary); + padding: 40px; + border: 1px solid var(--border); +} + +.share-image-header { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 24px; + padding-bottom: 16px; + border-bottom: 1px solid var(--border); +} + +.share-image-logo { + font-size: 18px; + font-weight: 500; + color: var(--text-primary); + letter-spacing: 0.5px; +} + +.share-image-content { + color: var(--text-primary); + font-size: 14px; + line-height: 1.8; + border-left: 2px solid var(--accent); + padding-left: 20px; +} + +.share-image-footer { + margin-top: 24px; + padding-top: 16px; + border-top: 1px solid var(--border); + text-align: center; + font-size: 12px; + color: var(--text-tertiary); +} + /* Markdown Styles */ .markdown h1, .markdown h2, @@ -549,3 +626,79 @@ html, body { transform: translateX(-50%) translateY(-10px); } } + +/* Image Modal */ +.image-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.95); + display: flex; + align-items: center; + justify-content: center; + z-index: 10000; + padding: 20px; + animation: fadeIn 0.2s ease; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.image-modal-content { + position: relative; + max-width: 90%; + max-height: 90%; + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; +} + +.modal-close-btn { + position: absolute; + top: -50px; + right: 0; + width: 40px; + height: 40px; + background: transparent; + border: 1px solid var(--border-bright); + border-radius: 2px; + color: var(--text-primary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; +} + +.modal-close-btn:hover { + background: var(--accent-dim); + border-color: var(--accent); + color: var(--accent); +} + +.modal-image { + max-width: 100%; + max-height: calc(90vh - 80px); + border: 1px solid var(--border); + border-radius: 2px; + box-shadow: 0 8px 32px rgba(0, 255, 65, 0.1); +} + +.modal-hint { + color: var(--text-secondary); + font-size: 13px; + text-align: center; + padding: 8px 16px; + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 2px; +} diff --git a/frontend/index.html b/frontend/index.html index ebc36a7..5e74776 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -53,11 +53,32 @@ :class="['message', msg.role]">
{{ msg.content }}
-
+
+
- -
-
+ +
+ + +
+ + +
+
+
@@ -107,6 +128,20 @@ + + +
+
+ + 分享图 + +
+
@@ -115,6 +150,9 @@ + + + diff --git a/frontend/js/app.js b/frontend/js/app.js index aada4b1..20c3f40 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -8,7 +8,9 @@ createApp({ userInput: '', loading: false, sessionId: null, - charts: {} + charts: {}, + showImageModal: false, + modalImageUrl: '' }; }, mounted() { @@ -256,6 +258,107 @@ createApp({ setTimeout(() => { document.body.removeChild(notification); }, 2000); + }, + + copyMessage(content) { + // 移除HTML标签,只保留纯文本 + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = marked.parse(content); + const plainText = tempDiv.textContent || tempDiv.innerText; + + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(plainText).then(() => { + this.showNotification('已复制内容'); + }).catch(err => { + console.error('复制失败:', err); + this.fallbackCopy(plainText); + }); + } else { + this.fallbackCopy(plainText); + } + }, + + async generateShareImage(content, index) { + try { + this.showNotification('正在生成分享图...'); + + // 创建临时容器 + const container = document.createElement('div'); + container.className = 'share-image-container'; + container.style.left = '-9999px'; + + // 构建分享图内容 + container.innerHTML = ` +
+ + + + +
+
${marked.parse(content)}
+ + `; + + document.body.appendChild(container); + + // 等待渲染 + await new Promise(resolve => setTimeout(resolve, 100)); + + // 生成图片 + const canvas = await html2canvas(container, { + backgroundColor: '#0a0a0a', + scale: 2, + logging: false, + useCORS: true + }); + + // 移除临时容器 + document.body.removeChild(container); + + // 显示图片在模态框中 + this.modalImageUrl = canvas.toDataURL('image/png'); + this.showImageModal = true; + + this.showNotification('长按图片可保存'); + + } catch (error) { + console.error('生成分享图失败:', error); + this.showNotification('生成失败,请重试'); + } + }, + + closeImageModal() { + this.showImageModal = false; + this.modalImageUrl = ''; + }, + + showNotification(text) { + const notification = document.createElement('div'); + notification.textContent = text; + notification.style.cssText = ` + position: fixed; + bottom: 80px; + left: 50%; + transform: translateX(-50%); + background: #00ff41; + color: #000000; + padding: 8px 16px; + border-radius: 2px; + font-size: 13px; + font-weight: 500; + z-index: 10000; + animation: fadeInOut 2s ease; + `; + + document.body.appendChild(notification); + + setTimeout(() => { + if (document.body.contains(notification)) { + document.body.removeChild(notification); + } + }, 2000); } },