Files
xianyan/preview/poetry_chat_preview.html
Developer 6f5400ec4b feat: 5.4.0版本大更新,新增多端桌面小组件与多项功能优化
- 重构「灵感」模块为「发现」模块,统一页面命名与文案
- 新增flutter_tts语音朗读依赖与鸿蒙Nearby配对方式
- 添加Android/iOS/鸿蒙全平台桌面小组件支持(7种类型)
- 完善文件传输模块,新增画布邀请消息与删除会话功能
- 优化协作画布光标广播节流逻辑,修复已知bug
- 更新应用英文名与隐私政策入口,新增翻译API抽象层
- 移除用户中心多余的加号按钮,完善空状态组件类型
2026-05-19 05:39:50 +08:00

1017 lines
27 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>闲言APP — 今日诗词聊天界面预览</title>
<style>
:root {
--primary: #007AFF;
--primary-dark: #0056CC;
--primary-light: rgba(0,122,255,0.12);
--bg: #F2F2F7;
--bg-card: #FFFFFF;
--bg-grouped: #F2F2F7;
--text-primary: #1C1C1E;
--text-secondary: #8E8E93;
--text-hint: #AEAEB2;
--separator: rgba(60,60,67,0.12);
--red: #FF3B30;
--orange: #FF9500;
--green: #34C759;
--purple: #AF52DE;
--yellow: #FFCC00;
--bubble-left: #E9E9EB;
--bubble-left-text: #1C1C1E;
--bubble-right: #007AFF;
--bubble-right-text: #FFFFFF;
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 20px;
--radius-bubble: 18px;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.06);
--shadow-md: 0 4px 12px rgba(0,0,0,0.08);
--shadow-lg: 0 8px 24px rgba(0,0,0,0.12);
--font: -apple-system, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', sans-serif;
--font-poetry: 'STKaiti', 'Kaiti SC', 'Kaiti', 'STSong', 'Songti SC', serif;
}
[data-theme="dark"] {
--bg: #000000;
--bg-card: #1C1C1E;
--bg-grouped: #000000;
--text-primary: #FFFFFF;
--text-secondary: #98989D;
--text-hint: #636366;
--separator: rgba(84,84,88,0.65);
--bubble-left: #383838;
--bubble-left-text: #FFFFFF;
--bubble-right: #007AFF;
--bubble-right-text: #FFFFFF;
--primary-light: rgba(0,122,255,0.2);
--shadow-sm: 0 1px 3px rgba(0,0,0,0.2);
--shadow-md: 0 4px 12px rgba(0,0,0,0.3);
--shadow-lg: 0 8px 24px rgba(0,0,0,0.4);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: var(--font);
background: #1a1a1a;
color: var(--text-primary);
-webkit-font-smoothing: antialiased;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.phone-frame {
width: 393px;
height: 852px;
border-radius: 44px;
overflow: hidden;
background: var(--bg);
box-shadow: 0 20px 60px rgba(0,0,0,0.3), 0 0 0 1px rgba(0,0,0,0.08);
position: relative;
display: flex;
flex-direction: column;
}
.status-bar {
height: 54px;
padding: 14px 24px 0;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 15px;
font-weight: 600;
flex-shrink: 0;
background: rgba(242,242,247,0.85);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
z-index: 10;
}
[data-theme="dark"] .status-bar {
background: rgba(0,0,0,0.85);
}
.status-bar .time { font-weight: 700; }
.status-bar .icons { display: flex; gap: 5px; align-items: center; font-size: 12px; }
.nav-bar {
height: 52px;
padding: 0 16px;
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
background: rgba(242,242,247,0.85);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 0.5px solid var(--separator);
z-index: 10;
}
[data-theme="dark"] .nav-bar {
background: rgba(0,0,0,0.85);
}
.nav-left {
display: flex;
align-items: center;
gap: 4px;
color: var(--primary);
font-size: 17px;
cursor: pointer;
}
.nav-left .back-arrow { font-size: 20px; }
.nav-center {
display: flex;
flex-direction: column;
align-items: center;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
.nav-title {
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.nav-subtitle {
font-size: 11px;
color: var(--text-secondary);
margin-top: -1px;
}
.nav-right {
display: flex;
align-items: center;
gap: 16px;
}
.nav-btn {
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: var(--primary);
padding: 4px;
transition: transform 0.2s ease, opacity 0.2s ease;
line-height: 1;
}
.nav-btn:active { transform: scale(0.88); opacity: 0.6; }
.chat-area {
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
padding: 8px 0 12px;
background: var(--bg);
}
.chat-area::-webkit-scrollbar { display: none; }
.date-separator {
display: flex;
justify-content: center;
padding: 16px 0 8px;
}
.date-separator span {
font-size: 11px;
font-weight: 500;
color: var(--text-secondary);
background: rgba(120,120,128,0.12);
padding: 4px 12px;
border-radius: 12px;
letter-spacing: 0.3px;
}
.message-row {
display: flex;
align-items: flex-end;
padding: 2px 16px;
gap: 6px;
animation: msgFadeIn 0.35s ease-out both;
}
.message-row.left { justify-content: flex-start; }
.message-row.right { justify-content: flex-end; }
@keyframes msgFadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.avatar {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
flex-shrink: 0;
background: linear-gradient(135deg, #FFD60A, #FF9F0A);
box-shadow: 0 2px 6px rgba(255,159,10,0.3);
}
.avatar-spacer {
width: 32px;
flex-shrink: 0;
}
.bubble {
max-width: 260px;
padding: 9px 14px;
border-radius: var(--radius-bubble);
font-size: 15px;
line-height: 1.45;
position: relative;
word-break: break-word;
}
.bubble-left {
background: var(--bubble-left);
color: var(--bubble-left-text);
border-bottom-left-radius: 6px;
}
.bubble-right {
background: var(--bubble-right);
color: var(--bubble-right-text);
border-bottom-right-radius: 6px;
}
.message-time {
font-size: 10px;
color: var(--text-hint);
margin-top: 2px;
padding: 0 4px;
}
.message-row.left .message-time { text-align: left; padding-left: 38px; }
.message-row.right .message-time { text-align: right; padding-right: 4px; }
.poetry-card {
max-width: 270px;
background: var(--bg-card);
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow: var(--shadow-md);
border: 0.5px solid var(--separator);
}
[data-theme="dark"] .poetry-card {
background: #2C2C2E;
border-color: rgba(84,84,88,0.65);
}
.poetry-card-header {
padding: 12px 14px 8px;
display: flex;
align-items: center;
gap: 8px;
border-bottom: 0.5px solid var(--separator);
}
.poetry-card-icon {
width: 28px;
height: 28px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
background: linear-gradient(135deg, #FF9F0A, #FFD60A);
}
.poetry-card-label {
font-size: 12px;
font-weight: 600;
color: var(--text-secondary);
letter-spacing: 0.5px;
}
.poetry-card-body {
padding: 14px 16px;
}
.poetry-text {
font-family: var(--font-poetry);
font-size: 17px;
line-height: 1.8;
color: var(--text-primary);
letter-spacing: 1px;
text-align: center;
}
.poetry-card-footer {
padding: 10px 16px 12px;
display: flex;
align-items: center;
justify-content: space-between;
border-top: 0.5px solid var(--separator);
}
.poetry-author {
font-size: 13px;
color: var(--text-secondary);
font-weight: 500;
}
.poetry-source {
font-size: 12px;
color: var(--text-hint);
font-style: italic;
}
.poetry-card-actions {
padding: 6px 16px 10px;
display: flex;
gap: 20px;
justify-content: center;
}
.poetry-action {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
cursor: pointer;
transition: transform 0.15s ease;
border: none;
background: none;
}
.poetry-action:active { transform: scale(0.9); }
.poetry-action .icon { font-size: 18px; }
.poetry-action .label { font-size: 10px; color: var(--text-secondary); font-weight: 500; }
.tag-bubble {
display: flex;
flex-wrap: wrap;
gap: 6px;
padding: 2px 0;
}
.tag {
display: inline-flex;
align-items: center;
gap: 3px;
padding: 4px 10px;
border-radius: 14px;
font-size: 12px;
font-weight: 500;
background: var(--primary-light);
color: var(--primary);
transition: transform 0.15s ease;
cursor: pointer;
}
.tag:active { transform: scale(0.95); }
.reason-bubble {
background: var(--bubble-left);
color: var(--bubble-left-text);
border-radius: var(--radius-bubble);
border-bottom-left-radius: 6px;
padding: 9px 14px;
max-width: 260px;
font-size: 15px;
line-height: 1.45;
}
.reason-bubble .emoji { font-size: 16px; }
.translation-bubble {
background: var(--bubble-left);
color: var(--bubble-left-text);
border-radius: var(--radius-bubble);
border-bottom-left-radius: 6px;
padding: 10px 14px;
max-width: 260px;
font-size: 14px;
line-height: 1.55;
}
.translation-bubble .prefix {
font-size: 13px;
font-weight: 600;
color: var(--orange);
margin-bottom: 4px;
display: block;
}
.quick-replies {
display: flex;
gap: 8px;
padding: 4px 16px 8px 54px;
flex-wrap: wrap;
}
.quick-reply {
padding: 6px 14px;
border-radius: 16px;
font-size: 13px;
font-weight: 500;
background: var(--bg-card);
color: var(--primary);
border: 1px solid rgba(0,122,255,0.25);
cursor: pointer;
transition: all 0.2s ease;
box-shadow: var(--shadow-sm);
}
[data-theme="dark"] .quick-reply {
background: #2C2C2E;
border-color: rgba(0,122,255,0.35);
}
.quick-reply:active {
transform: scale(0.95);
background: var(--primary-light);
}
.input-bar {
padding: 8px 12px;
padding-bottom: max(8px, env(safe-area-inset-bottom));
background: rgba(242,242,247,0.85);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-top: 0.5px solid var(--separator);
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
[data-theme="dark"] .input-bar {
background: rgba(0,0,0,0.85);
}
.input-bar .input-wrap {
flex: 1;
display: flex;
align-items: center;
background: var(--bg-card);
border-radius: 20px;
padding: 0 12px;
height: 36px;
border: 0.5px solid var(--separator);
}
[data-theme="dark"] .input-bar .input-wrap {
background: #1C1C1E;
border-color: rgba(84,84,88,0.65);
}
.input-bar input {
flex: 1;
border: none;
outline: none;
font-size: 15px;
font-family: var(--font);
background: transparent;
color: var(--text-primary);
}
.input-bar input::placeholder { color: var(--text-hint); }
.input-bar .send-btn {
width: 32px;
height: 32px;
border-radius: 50%;
background: var(--primary);
border: none;
color: #fff;
font-size: 15px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.15s ease, opacity 0.15s ease;
flex-shrink: 0;
}
.input-bar .send-btn:active { transform: scale(0.88); }
.input-bar .send-btn.disabled { background: var(--text-hint); opacity: 0.5; }
.input-bar .tool-btn {
width: 32px;
height: 32px;
border-radius: 50%;
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: var(--text-secondary);
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.15s ease;
flex-shrink: 0;
}
.input-bar .tool-btn:active { transform: scale(0.88); }
.typing-indicator {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 0 4px 54px;
}
.typing-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--text-hint);
animation: typingBounce 1.4s ease-in-out infinite;
}
.typing-dot:nth-child(2) { animation-delay: 0.2s; }
.typing-dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes typingBounce {
0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
30% { transform: translateY(-5px); opacity: 1; }
}
.theme-toggle {
position: fixed;
top: 20px;
right: 20px;
width: 44px;
height: 44px;
border-radius: 50%;
background: rgba(255,255,255,0.15);
backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.2);
color: #fff;
font-size: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.2s ease;
z-index: 100;
}
.theme-toggle:active { transform: scale(0.9); }
.new-poetry-hint {
display: flex;
justify-content: center;
padding: 8px 0;
}
.new-poetry-btn {
padding: 6px 16px;
border-radius: 16px;
font-size: 12px;
font-weight: 600;
background: var(--primary);
color: #fff;
border: none;
cursor: pointer;
box-shadow: 0 2px 8px rgba(0,122,255,0.3);
transition: transform 0.15s ease;
display: flex;
align-items: center;
gap: 4px;
}
.new-poetry-btn:active { transform: scale(0.95); }
.liked .poetry-action .icon { animation: likeHeart 0.4s ease; }
@keyframes likeHeart {
0% { transform: scale(1); }
50% { transform: scale(1.4); }
100% { transform: scale(1); }
}
.home-indicator {
height: 34px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
background: var(--bg);
}
.home-indicator .bar {
width: 134px;
height: 5px;
border-radius: 3px;
background: var(--text-primary);
opacity: 0.18;
}
@media (max-width: 440px) {
body { padding: 0; }
.phone-frame {
width: 100vw;
height: 100vh;
border-radius: 0;
box-shadow: none;
}
}
</style>
</head>
<body>
<button class="theme-toggle" onclick="toggleTheme()" title="切换深色模式">🌙</button>
<div class="phone-frame" id="phoneFrame">
<div class="status-bar">
<span class="time">9:41</span>
<span class="icons">📶 📡 🔋</span>
</div>
<div class="nav-bar">
<div class="nav-left">
<span class="back-arrow"></span>
<span>闲言</span>
</div>
<div class="nav-center">
<span class="nav-title">📜 今日诗词</span>
<span class="nav-subtitle">每日推荐 · 第128天</span>
</div>
<div class="nav-right">
<button class="nav-btn" onclick="refreshPoetry()" title="刷新">🔄</button>
<button class="nav-btn" onclick="toggleTheme()" title="主题">⚙️</button>
</div>
</div>
<div class="chat-area" id="chatArea">
</div>
<div class="input-bar">
<button class="tool-btn" title="更多"></button>
<div class="input-wrap">
<input type="text" id="msgInput" placeholder="说点什么..." onkeydown="handleInput(event)">
</div>
<button class="send-btn disabled" id="sendBtn" onclick="sendMessage()"></button>
</div>
<div class="home-indicator"><div class="bar"></div></div>
</div>
<script>
const chatData = [
{ type: 'date', text: '今天' },
{ type: 'system', time: '09:00', text: '早安!今日为你推荐一首诗 🌅' },
{ type: 'poetry', time: '09:00', poem: '床前明月光,疑是地上霜。\n举头望明月低头思故乡。', author: '李白', source: '静夜思', tags: ['思乡', '月夜', '唐诗'] },
{ type: 'reason', time: '09:01', text: '💡 推荐理由:今夜月色正好,与这首诗意境相合' },
{ type: 'tags', time: '09:01', tags: ['思乡', '月夜', '唐诗'] },
{ type: 'user', time: '09:02', text: '换一首' },
{ type: 'system', time: '09:02', text: '好的,为你换一首 ✨' },
{ type: 'poetry', time: '09:02', poem: '春眠不觉晓,处处闻啼鸟。\n夜来风雨声花落知多少。', author: '孟浩然', source: '春晓', tags: ['春天', '写景', '唐诗'] },
{ type: 'translation', time: '09:03', text: '春日酣眠不知不觉天已破晓,到处都是鸟儿的啼鸣声。昨夜风雨交加,不知花落了多少。' },
{ type: 'tags', time: '09:03', tags: ['春天', '写景', '唐诗'] },
{ type: 'quick-replies', replies: ['再来一首', '收藏', '分享', '赏析'] }
];
const extraPoems = [
{
poem: '白日依山尽,黄河入海流。\n欲穷千里目更上一层楼。',
author: '王之涣', source: '登鹳雀楼', tags: ['壮志', '登高', '唐诗'],
translation: '太阳依傍山峦渐渐下落,黄河向着大海滔滔东流。如果要想遍览千里风光,那就请再登上一层高楼。',
reason: '💡 推荐理由:登高望远,心胸开阔,适合新的一天'
},
{
poem: '千山鸟飞绝,万径人踪灭。\n孤舟蓑笠翁独钓寒江雪。',
author: '柳宗元', source: '江雪', tags: ['孤独', '写景', '唐诗'],
translation: '千山万岭不见飞鸟的踪影,千路万径不见行人的足迹。一叶孤舟上,一位身披蓑衣头戴斗笠的渔翁,独自在漫天风雪中垂钓。',
reason: '💡 推荐理由:独处也是一种境界,静心品味'
},
{
poem: '空山新雨后,天气晚来秋。\n明月松间照清泉石上流。',
author: '王维', source: '山居秋暝', tags: ['山水', '秋天', '唐诗'],
translation: '空旷的群山沐浴了一场新雨,傍晚的天气显得格外凉爽秋意。皎皎明月从松隙间洒下清光,清清泉水在石上淙淙流淌。',
reason: '💡 推荐理由:山间清幽,洗涤心灵'
}
];
let poemIndex = 0;
let isDark = false;
function toggleTheme() {
isDark = !isDark;
document.getElementById('phoneFrame').setAttribute('data-theme', isDark ? 'dark' : '');
document.querySelector('.theme-toggle').textContent = isDark ? '☀️' : '🌙';
}
function createMessageRow(msg, index) {
const delay = index * 0.08;
let html = '';
if (msg.type === 'date') {
html = `<div class="date-separator"><span>${msg.text}</span></div>`;
} else if (msg.type === 'system') {
html = `
<div class="message-row left" style="animation-delay:${delay}s">
<div class="avatar">📜</div>
<div>
<div class="bubble bubble-left">${msg.text}</div>
<div class="message-time">${msg.time}</div>
</div>
</div>`;
} else if (msg.type === 'poetry') {
html = `
<div class="message-row left" style="animation-delay:${delay}s">
<div class="avatar">📜</div>
<div>
<div class="poetry-card" id="poemCard${index}">
<div class="poetry-card-header">
<div class="poetry-card-icon">📖</div>
<span class="poetry-card-label">今日诗词</span>
</div>
<div class="poetry-card-body">
<div class="poetry-text">${msg.poem.replace(/\n/g, '<br>')}</div>
</div>
<div class="poetry-card-footer">
<span class="poetry-author">${msg.author}</span>
<span class="poetry-source">《${msg.source}》</span>
</div>
<div class="poetry-card-actions">
<button class="poetry-action" onclick="toggleLike(this)">
<span class="icon">🤍</span>
<span class="label">收藏</span>
</button>
<button class="poetry-action" onclick="sharePoem()">
<span class="icon">📤</span>
<span class="label">分享</span>
</button>
<button class="poetry-action" onclick="copyPoem(${index})">
<span class="icon">📋</span>
<span class="label">复制</span>
</button>
<button class="poetry-action" onclick="showAnnotation()">
<span class="icon">📝</span>
<span class="label">注释</span>
</button>
</div>
</div>
<div class="message-time">${msg.time}</div>
</div>
</div>`;
} else if (msg.type === 'reason') {
html = `
<div class="message-row left" style="animation-delay:${delay}s">
<div class="avatar-spacer"></div>
<div>
<div class="reason-bubble">${msg.text}</div>
<div class="message-time">${msg.time}</div>
</div>
</div>`;
} else if (msg.type === 'tags') {
const tagHtml = msg.tags.map(t => `<span class="tag">#${t}</span>`).join('');
html = `
<div class="message-row left" style="animation-delay:${delay}s">
<div class="avatar-spacer"></div>
<div>
<div class="tag-bubble">${tagHtml}</div>
<div class="message-time">${msg.time}</div>
</div>
</div>`;
} else if (msg.type === 'user') {
html = `
<div class="message-row right" style="animation-delay:${delay}s">
<div>
<div class="bubble bubble-right">${msg.text}</div>
<div class="message-time">${msg.time}</div>
</div>
</div>`;
} else if (msg.type === 'translation') {
html = `
<div class="message-row left" style="animation-delay:${delay}s">
<div class="avatar-spacer"></div>
<div>
<div class="translation-bubble">
<span class="prefix">📝 译文</span>
${msg.text}
</div>
<div class="message-time">${msg.time}</div>
</div>
</div>`;
} else if (msg.type === 'quick-replies') {
const replyHtml = msg.replies.map(r => `<button class="quick-reply" onclick="quickReply('${r}')">${r}</button>`).join('');
html = `<div class="quick-replies">${replyHtml}</div>`;
}
return html;
}
function renderChat() {
const area = document.getElementById('chatArea');
area.innerHTML = chatData.map((msg, i) => createMessageRow(msg, i)).join('');
area.scrollTop = area.scrollHeight;
}
function appendMessage(msg) {
const area = document.getElementById('chatArea');
const div = document.createElement('div');
div.innerHTML = createMessageRow(msg, chatData.length);
const quickReplies = area.querySelector('.quick-replies');
if (quickReplies) quickReplies.remove();
area.appendChild(div.firstElementChild || div);
chatData.push(msg);
area.scrollTop = area.scrollHeight;
}
function appendMessages(msgs) {
const area = document.getElementById('chatArea');
const quickReplies = area.querySelector('.quick-replies');
if (quickReplies) quickReplies.remove();
msgs.forEach((msg, i) => {
setTimeout(() => {
const div = document.createElement('div');
div.innerHTML = createMessageRow(msg, chatData.length);
area.appendChild(div.firstElementChild || div);
chatData.push(msg);
area.scrollTop = area.scrollHeight;
}, i * 400);
});
}
function showTyping() {
const area = document.getElementById('chatArea');
const typing = document.createElement('div');
typing.className = 'typing-indicator';
typing.id = 'typingIndicator';
typing.innerHTML = '<div class="typing-dot"></div><div class="typing-dot"></div><div class="typing-dot"></div>';
area.appendChild(typing);
area.scrollTop = area.scrollHeight;
}
function hideTyping() {
const typing = document.getElementById('typingIndicator');
if (typing) typing.remove();
}
function getNow() {
const d = new Date();
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
}
function refreshPoetry() {
const btn = document.querySelector('.nav-btn');
btn.style.transform = 'rotate(360deg)';
btn.style.transition = 'transform 0.6s ease';
setTimeout(() => { btn.style.transform = ''; }, 600);
showTyping();
setTimeout(() => {
hideTyping();
const poem = extraPoems[poemIndex % extraPoems.length];
poemIndex++;
const now = getNow();
appendMessages([
{ type: 'system', time: now, text: '为你推荐新的诗词 🎲' },
{ type: 'poetry', time: now, poem: poem.poem, author: poem.author, source: poem.source, tags: poem.tags },
{ type: 'reason', time: now, text: poem.reason },
{ type: 'translation', time: now, text: poem.translation },
{ type: 'tags', time: now, tags: poem.tags },
{ type: 'quick-replies', replies: ['再来一首', '收藏', '分享', '赏析'] }
]);
}, 1200);
}
function quickReply(text) {
const now = getNow();
appendMessage({ type: 'user', time: now, text });
if (text === '再来一首' || text === '换一首') {
showTyping();
setTimeout(() => {
hideTyping();
const poem = extraPoems[poemIndex % extraPoems.length];
poemIndex++;
const t = getNow();
appendMessages([
{ type: 'system', time: t, text: '好的,为你换一首 ✨' },
{ type: 'poetry', time: t, poem: poem.poem, author: poem.author, source: poem.source, tags: poem.tags },
{ type: 'reason', time: t, text: poem.reason },
{ type: 'translation', time: t, text: poem.translation },
{ type: 'tags', time: t, tags: poem.tags },
{ type: 'quick-replies', replies: ['再来一首', '收藏', '分享', '赏析'] }
]);
}, 1000);
} else if (text === '收藏') {
showTyping();
setTimeout(() => {
hideTyping();
appendMessage({ type: 'system', time: getNow(), text: '已收藏到你的诗词本 ⭐' });
}, 600);
} else if (text === '分享') {
showTyping();
setTimeout(() => {
hideTyping();
appendMessage({ type: 'system', time: getNow(), text: '已生成分享卡片,可分享给好友 🎴' });
}, 600);
} else if (text === '赏析') {
showTyping();
setTimeout(() => {
hideTyping();
appendMessages([
{ type: 'system', time: getNow(), text: '📖 诗词赏析' },
{ type: 'translation', time: getNow(), text: '此诗以清新自然的笔触,描绘了一幅生动的画面。诗人用简洁的语言,传达出深远的意境,令人回味无穷。全诗对仗工整,音韵和谐,堪称千古佳作。' },
{ type: 'quick-replies', replies: ['再来一首', '收藏', '分享'] }
]);
}, 800);
}
}
function sendMessage() {
const input = document.getElementById('msgInput');
const text = input.value.trim();
if (!text) return;
input.value = '';
updateSendBtn();
const now = getNow();
appendMessage({ type: 'user', time: now, text });
showTyping();
setTimeout(() => {
hideTyping();
const responses = [
'诗词之美,在于意境 🌸',
'每一首诗都是一段故事 📖',
'诗中有画,画中有诗 🎨',
'读诗如品茶,越品越有味 🍵',
'古人的智慧,尽在字里行间 ✨'
];
appendMessage({ type: 'system', time: getNow(), text: responses[Math.floor(Math.random() * responses.length)] });
}, 1000);
}
function handleInput(e) {
updateSendBtn();
if (e.key === 'Enter') sendMessage();
}
function updateSendBtn() {
const input = document.getElementById('msgInput');
const btn = document.getElementById('sendBtn');
if (input.value.trim()) {
btn.classList.remove('disabled');
} else {
btn.classList.add('disabled');
}
}
function toggleLike(el) {
const icon = el.querySelector('.icon');
const label = el.querySelector('.label');
if (icon.textContent === '🤍') {
icon.textContent = '❤️';
label.textContent = '已收藏';
el.closest('.poetry-card').classList.add('liked');
} else {
icon.textContent = '🤍';
label.textContent = '收藏';
el.closest('.poetry-card').classList.remove('liked');
}
}
function sharePoem() {
const resp = confirm('分享到朋友圈?');
if (resp) appendMessage({ type: 'system', time: getNow(), text: '已生成分享卡片 🎴' });
}
function copyPoem(index) {
appendMessage({ type: 'system', time: getNow(), text: '诗词已复制到剪贴板 📋' });
}
function showAnnotation() {
showTyping();
setTimeout(() => {
hideTyping();
appendMessages([
{ type: 'system', time: getNow(), text: '📝 注释' },
{ type: 'translation', time: getNow(), text: '此诗语言质朴无华,意境深远。诗人以白描手法,勾勒出一幅动人的画面,将个人情感与自然景物融为一体,情景交融,浑然天成。' },
]);
}, 800);
}
renderChat();
</script>
</body>
</html>