- 重构「灵感」模块为「发现」模块,统一页面命名与文案 - 新增flutter_tts语音朗读依赖与鸿蒙Nearby配对方式 - 添加Android/iOS/鸿蒙全平台桌面小组件支持(7种类型) - 完善文件传输模块,新增画布邀请消息与删除会话功能 - 优化协作画布光标广播节流逻辑,修复已知bug - 更新应用英文名与隐私政策入口,新增翻译API抽象层 - 移除用户中心多余的加号按钮,完善空状态组件类型
1017 lines
27 KiB
HTML
1017 lines
27 KiB
HTML
<!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>
|