Files
xianyan/docs/design-preview/pc-workspace-layout.html
Developer 83720002e6 feat: 新增工作台模式、系统托盘,修复多平台兼容性问题
1. 新增工作台三栏布局模式,适配宽屏设备
2. 添加跨平台系统托盘支持,新增托盘图标资源
3. 修复工作台模式下导航返回异常问题
4. 统一JSON类型安全解析,替换硬类型转换
5. 增加macOS深度链接支持,统一渠道分发信息
6. 优化部分页面生命周期和状态加载逻辑
7. 移除废弃的nearby_connections依赖
2026-06-19 06:43:55 +08:00

1112 lines
31 KiB
HTML
Raw Permalink 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>闲言 PC 工作台布局原型 — 微信PC式三栏</title>
<style>
/* ============================================================
设计令牌 — 对齐项目 app_colors.dart
============================================================ */
:root {
/* 语义色 */
--primary: #6C63FF;
--primary-light: #8B83FF;
--primary-dark: #4A42E0;
--secondary: #FF6B6B;
--accent: #4ECDC4;
--success: #10B981;
--warning: #F59E0B;
--error: #EF4444;
/* 背景色(浅色) */
--bg-primary: #FAFAFA;
--bg-secondary: #F5F5F5;
--bg-card: #FFFFFF;
--bg-elevated: #FFFFFF;
--bg-nav: rgba(255, 255, 255, 0.72);
/* 文字色 */
--text-primary: #1A1A2E;
--text-secondary: #6B7280;
--text-hint: #9CA3AF;
--text-inverse: #FFFFFF;
/* 图标色 */
--icon-primary: #1A1A2E;
--icon-secondary: #6B7280;
--icon-active: #6C63FF;
/* 边框/分割线 */
--border-subtle: rgba(0, 0, 0, 0.06);
--border-medium: rgba(0, 0, 0, 0.1);
/* 间距4的倍数 */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
/* 圆角 */
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 14px;
--radius-xl: 20px;
/* 阴影 */
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
/* 毛玻璃 */
--blur-nav: 24px;
/* 尺寸 */
--nav-width: 72px;
--middle-min: 320px;
--middle-default: 380px;
--middle-max: 600px;
--divider-width: 6px;
--topbar-height: 48px;
}
/* 深色主题 */
[data-theme="dark"] {
--bg-primary: #1A1A1A;
--bg-secondary: #0A0A0A;
--bg-card: #1E1E1E;
--bg-elevated: #2A2A2A;
--bg-nav: rgba(30, 30, 30, 0.72);
--text-primary: #F5F5F5;
--text-secondary: #AEAEB2;
--text-hint: #8E8E93;
--icon-primary: #FFFFFF;
--icon-secondary: #AEAEB2;
--border-subtle: rgba(255, 255, 255, 0.08);
--border-medium: rgba(255, 255, 255, 0.12);
}
/* ============================================================
全局重置
============================================================ */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'PingFang SC', 'Microsoft YaHei', sans-serif;
font-size: 14px;
color: var(--text-primary);
background: var(--bg-primary);
-webkit-font-smoothing: antialiased;
}
button {
font-family: inherit;
border: none;
background: none;
cursor: pointer;
color: inherit;
}
/* ============================================================
主布局骨架 — 三栏工作台
============================================================ */
.workspace {
display: flex;
height: 100vh;
width: 100vw;
overflow: hidden;
}
/* ---- 左栏:导航栏 ---- */
.nav-rail {
width: var(--nav-width);
flex-shrink: 0;
background: var(--bg-nav);
backdrop-filter: blur(var(--blur-nav));
-webkit-backdrop-filter: blur(var(--blur-nav));
border-right: 1px solid var(--border-subtle);
display: flex;
flex-direction: column;
align-items: center;
padding: var(--space-4) 0;
gap: var(--space-2);
z-index: 10;
}
.nav-logo {
width: 40px;
height: 40px;
border-radius: var(--radius-md);
background: linear-gradient(135deg, var(--primary), var(--primary-light));
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 700;
font-size: 18px;
margin-bottom: var(--space-4);
box-shadow: var(--shadow-md);
}
.nav-items {
flex: 1;
display: flex;
flex-direction: column;
gap: var(--space-2);
align-items: center;
}
.nav-item {
width: 56px;
height: 56px;
border-radius: var(--radius-md);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2px;
color: var(--icon-secondary);
transition: all 0.2s ease;
position: relative;
}
.nav-item:hover {
background: var(--border-subtle);
color: var(--icon-primary);
}
.nav-item.active {
color: var(--icon-active);
background: rgba(108, 99, 255, 0.1);
}
.nav-item.active::before {
content: '';
position: absolute;
left: -16px;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 24px;
background: var(--primary);
border-radius: 0 3px 3px 0;
}
.nav-item .icon {
font-size: 22px;
line-height: 1;
}
.nav-item .label {
font-size: 10px;
font-weight: 500;
}
.nav-badge {
position: absolute;
top: 8px;
right: 8px;
width: 8px;
height: 8px;
background: var(--secondary);
border-radius: 50%;
border: 2px solid var(--bg-nav);
}
.nav-bottom {
display: flex;
flex-direction: column;
gap: var(--space-2);
align-items: center;
}
/* ---- 中栏:内容列表 ---- */
.middle-panel {
width: var(--middle-default);
min-width: var(--middle-min);
max-width: var(--middle-max);
flex-shrink: 0;
background: var(--bg-secondary);
display: flex;
flex-direction: column;
overflow: hidden;
transition: width 0.05s linear;
}
.middle-header {
height: var(--topbar-height);
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 var(--space-5);
border-bottom: 1px solid var(--border-subtle);
background: var(--bg-nav);
backdrop-filter: blur(var(--blur-nav));
-webkit-backdrop-filter: blur(var(--blur-nav));
}
.middle-title {
font-size: 17px;
font-weight: 600;
color: var(--text-primary);
}
.middle-actions {
display: flex;
gap: var(--space-2);
}
.icon-btn {
width: 32px;
height: 32px;
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
color: var(--icon-secondary);
font-size: 16px;
transition: all 0.15s ease;
}
.icon-btn:hover {
background: var(--border-subtle);
color: var(--icon-primary);
}
.middle-list {
flex: 1;
overflow-y: auto;
padding: var(--space-3);
}
.list-item {
display: flex;
gap: var(--space-3);
padding: var(--space-3) var(--space-4);
border-radius: var(--radius-md);
cursor: pointer;
transition: background 0.15s ease;
margin-bottom: var(--space-1);
}
.list-item:hover {
background: var(--border-subtle);
}
.list-item.selected {
background: rgba(108, 99, 255, 0.1);
}
.list-item-avatar {
width: 44px;
height: 44px;
border-radius: var(--radius-md);
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
background: var(--bg-card);
box-shadow: var(--shadow-sm);
}
.list-item-content {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 2px;
}
.list-item-title {
font-size: 14px;
font-weight: 600;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.list-item-desc {
font-size: 12px;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.list-item-meta {
font-size: 11px;
color: var(--text-hint);
flex-shrink: 0;
}
/* ---- 分割条 ---- */
.divider {
width: var(--divider-width);
flex-shrink: 0;
background: transparent;
cursor: col-resize;
position: relative;
z-index: 5;
}
.divider::after {
content: '';
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 1px;
background: var(--border-subtle);
transform: translateX(-50%);
transition: background 0.2s;
}
.divider:hover::after,
.divider.dragging::after {
background: var(--primary);
width: 2px;
}
/* ---- 右栏:内容详情 ---- */
.right-panel {
flex: 1;
min-width: 0;
background: var(--bg-primary);
display: flex;
flex-direction: column;
overflow: hidden;
}
.right-topbar {
height: var(--topbar-height);
flex-shrink: 0;
display: flex;
align-items: center;
gap: var(--space-3);
padding: 0 var(--space-5);
border-bottom: 1px solid var(--border-subtle);
background: var(--bg-nav);
backdrop-filter: blur(var(--blur-nav));
-webkit-backdrop-filter: blur(var(--blur-nav));
}
.back-btn {
width: 32px;
height: 32px;
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
color: var(--icon-secondary);
font-size: 18px;
transition: all 0.15s ease;
}
.back-btn:hover {
background: var(--border-subtle);
color: var(--icon-primary);
}
.right-title {
flex: 1;
font-size: 15px;
font-weight: 600;
color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.right-content {
flex: 1;
overflow-y: auto;
padding: var(--space-6);
}
/* ---- 详情页内容样式 ---- */
.detail-hero {
background: var(--bg-card);
border-radius: var(--radius-lg);
padding: var(--space-8);
margin-bottom: var(--space-6);
box-shadow: var(--shadow-sm);
border: 1px solid var(--border-subtle);
}
.detail-quote {
font-size: 22px;
font-weight: 600;
line-height: 1.5;
color: var(--text-primary);
margin-bottom: var(--space-4);
font-family: 'SF Pro Display', 'PingFang SC', serif;
}
.detail-author {
font-size: 14px;
color: var(--text-secondary);
display: flex;
align-items: center;
gap: var(--space-2);
}
.detail-section {
background: var(--bg-card);
border-radius: var(--radius-lg);
padding: var(--space-6);
margin-bottom: var(--space-4);
box-shadow: var(--shadow-sm);
border: 1px solid var(--border-subtle);
}
.detail-section h3 {
font-size: 16px;
font-weight: 600;
margin-bottom: var(--space-3);
color: var(--text-primary);
}
.detail-section p {
font-size: 14px;
line-height: 1.7;
color: var(--text-secondary);
}
.tag-row {
display: flex;
gap: var(--space-2);
flex-wrap: wrap;
margin-top: var(--space-3);
}
.tag {
padding: 4px 10px;
background: rgba(108, 99, 255, 0.1);
color: var(--primary);
border-radius: 999px;
font-size: 12px;
font-weight: 500;
}
/* ---- 概览仪表盘(右栏默认状态) ---- */
.dashboard {
padding: var(--space-6);
}
.dashboard h2 {
font-size: 20px;
font-weight: 700;
margin-bottom: var(--space-5);
color: var(--text-primary);
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: var(--space-4);
margin-bottom: var(--space-6);
}
.stat-card {
background: var(--bg-card);
border-radius: var(--radius-lg);
padding: var(--space-5);
box-shadow: var(--shadow-sm);
border: 1px solid var(--border-subtle);
text-align: center;
}
.stat-icon {
font-size: 28px;
margin-bottom: var(--space-2);
}
.stat-value {
font-size: 24px;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 2px;
}
.stat-label {
font-size: 12px;
color: var(--text-hint);
}
/* ============================================================
响应式断点 — 对齐设计规则 768/1024/1280
============================================================ */
/* 超宽屏 ≥1280px三栏完整显示 */
@media (min-width: 1280px) {
.workspace.triple-column { display: flex; }
}
/* 中宽屏 1024-1280px三栏中栏略窄 */
@media (min-width: 1024px) and (max-width: 1279px) {
:root {
--middle-default: 340px;
}
}
/* 平板 768-1024px双栏模式隐藏中栏列表右栏直接显示内容 */
@media (min-width: 768px) and (max-width: 1023px) {
.workspace.dual-column .middle-panel {
width: 280px;
}
.workspace.dual-column .right-panel {
display: flex;
}
}
/* 窄屏 <768px单栏仅显示右栏内容中栏覆盖式滑入 */
@media (max-width: 767px) {
.workspace {
flex-direction: column;
}
.nav-rail {
width: 100%;
height: 60px;
flex-direction: row;
justify-content: space-around;
padding: var(--space-2);
border-right: none;
border-bottom: 1px solid var(--border-subtle);
order: 2;
}
.nav-logo {
display: none;
}
.nav-items {
flex-direction: row;
gap: var(--space-4);
}
.nav-item {
width: 48px;
height: 48px;
}
.nav-item.active::before {
left: 50%;
top: auto;
bottom: -8px;
transform: translateX(-50%);
width: 24px;
height: 3px;
border-radius: 3px;
}
.nav-bottom {
display: none;
}
.middle-panel {
width: 100%;
height: calc(100vh - 60px);
order: 1;
}
.middle-panel.hidden-mobile {
display: none;
}
.right-panel {
display: none;
order: 1;
}
.right-panel.visible-mobile {
display: flex;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 60px;
z-index: 100;
}
.divider {
display: none;
}
}
/* ============================================================
主题切换按钮
============================================================ */
.theme-toggle {
position: fixed;
top: var(--space-4);
right: var(--space-4);
z-index: 999;
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--bg-card);
box-shadow: var(--shadow-md);
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
border: 1px solid var(--border-subtle);
}
/* ============================================================
断点指示器(仅原型用)
============================================================ */
.breakpoint-indicator {
position: fixed;
bottom: var(--space-3);
left: 50%;
transform: translateX(-50%);
background: var(--bg-elevated);
color: var(--text-secondary);
padding: 6px 12px;
border-radius: 999px;
font-size: 11px;
font-weight: 500;
box-shadow: var(--shadow-md);
border: 1px solid var(--border-subtle);
z-index: 999;
pointer-events: none;
display: flex;
gap: var(--space-2);
align-items: center;
}
.breakpoint-indicator .dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--success);
}
/* 滚动条美化 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-thumb {
background: var(--border-medium);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-hint);
}
::-webkit-scrollbar-track {
background: transparent;
}
</style>
</head>
<body data-theme="light">
<!-- 主题切换 -->
<button class="theme-toggle" onclick="toggleTheme()" title="切换主题">🌙</button>
<!-- 断点指示器 -->
<div class="breakpoint-indicator">
<span class="dot"></span>
<span id="bp-text">三栏模式 · ≥1280px</span>
</div>
<!-- ============================================================
主工作台布局
============================================================ -->
<div class="workspace triple-column" id="workspace">
<!-- 左栏:导航栏 -->
<nav class="nav-rail">
<div class="nav-logo"></div>
<div class="nav-items">
<button class="nav-item active" data-tab="home" onclick="switchTab('home')">
<span class="icon">🏠</span>
<span class="label">首页</span>
</button>
<button class="nav-item" data-tab="discover" onclick="switchTab('discover')">
<span class="icon">🧭</span>
<span class="label">发现</span>
<span class="nav-badge"></span>
</button>
<button class="nav-item" data-tab="profile" onclick="switchTab('profile')">
<span class="icon">👤</span>
<span class="label">我的</span>
</button>
</div>
<div class="nav-bottom">
<button class="nav-item" onclick="alert('打开设置')">
<span class="icon">⚙️</span>
<span class="label">设置</span>
</button>
</div>
</nav>
<!-- 中栏:内容列表 -->
<aside class="middle-panel" id="middlePanel">
<div class="middle-header">
<span class="middle-title" id="middleTitle">每日拾句</span>
<div class="middle-actions">
<button class="icon-btn" title="搜索">🔍</button>
<button class="icon-btn" title="筛选">⚙️</button>
</div>
</div>
<div class="middle-list" id="middleList">
<!-- 列表项由 JS 注入 -->
</div>
</aside>
<!-- 分割条 -->
<div class="divider" id="divider"></div>
<!-- 右栏:内容详情 -->
<main class="right-panel" id="rightPanel">
<div class="right-topbar" id="rightTopbar">
<button class="back-btn" id="backBtn" onclick="goBack()" style="display:none;"></button>
<span class="right-title" id="rightTitle">概览</span>
<div class="middle-actions">
<button class="icon-btn" title="收藏"></button>
<button class="icon-btn" title="分享"></button>
<button class="icon-btn" title="更多"></button>
</div>
</div>
<div class="right-content" id="rightContent">
<!-- 内容由 JS 注入 -->
</div>
</main>
</div>
<script>
// ============================================================
// 数据 — 模拟各 Tab 的列表数据
// ============================================================
const tabData = {
home: {
title: '每日拾句',
items: [
{ id: 1, avatar: '🌅', title: '晨光熹微', desc: '一日之计在于晨', meta: '06:00', type: 'sentence',
detail: { quote: '晨光熹微,万物初醒。每一个清晨都是生命重新开始的机会。', author: '佚名', tags: ['晨间', '励志', '新生'], content: '这句话提醒我们,无论昨天经历了什么,今天都是全新的开始。清晨的第一缕阳光,不仅照亮了大地,也照亮了我们内心的希望。\n\n研究表明早起的人更容易获得成功因为他们拥有更多的时间来规划一天的生活。当你迎着晨光开始新的一天你会发现生活充满了无限可能。' } },
{ id: 2, avatar: '💫', title: '星辰大海', desc: '心怀远方,脚踏实地', meta: '昨日', type: 'sentence',
detail: { quote: '我们都是星辰的孩子,终将回归大海的怀抱。', author: '卡尔·萨根', tags: ['宇宙', '哲学', '浪漫'], content: '卡尔·萨根在《宇宙》一书中写道,构成我们身体的每一个原子,都来自远古恒星的内部。当我们仰望星空,实际上是在仰望我们的故乡。\n\n这种宇宙视角让我们意识到人类的纷争在浩瀚宇宙面前微不足道而我们的存在本身就是一个奇迹。' } },
{ id: 3, avatar: '🌸', title: '樱花哲学', desc: '短暂即永恒', meta: '2天前', type: 'sentence',
detail: { quote: '樱花之所以美丽,正是因为它的短暂。', author: '川端康成', tags: ['日本美学', '物哀', '生命'], content: '日本美学中的"物哀"概念,强调的是对事物消逝之美的感知。樱花花期仅一周,却因此成为日本文化的象征。\n\n川端康成在《雪国》中多次描绘这种转瞬即逝的美提醒我们珍惜当下因为一切美好都是短暂的。' } },
{ id: 4, avatar: '🏔️', title: '山岳静默', desc: '静水流深,大智若愚', meta: '3天前', type: 'sentence',
detail: { quote: '山不辞土,故能成其高;海不辞水,故能成其深。', author: '《管子》', tags: ['古文', '积累', '智慧'], content: '这句古语出自《管子·形势解》,蕴含着深刻的积累哲学。伟大的成就从来不是一蹴而就的,而是无数微小努力的汇聚。\n\n正如泰山不让土壤故能成其大河海不择细流故能就其深。每一次微小的进步都在构筑未来的高度。' } },
{ id: 5, avatar: '🌙', title: '月下独酌', desc: '举杯邀明月', meta: '1周前', type: 'sentence',
detail: { quote: '花间一壶酒,独酌无相亲。举杯邀明月,对影成三人。', author: '李白', tags: ['唐诗', '孤独', '浪漫'], content: '李白的《月下独酌》是中国诗歌史上最著名的孤独之歌。诗人在花间独饮,却将明月和影子拟人化,构成了"三人"的奇妙意象。\n\n这种将孤独转化为丰盈的能力正是李白诗歌的魅力所在。孤独不是寂寞而是一种与天地万物对话的境界。' } },
]
},
discover: {
title: '发现频道',
items: [
{ id: 1, avatar: '💬', title: 'AI 对话助手', desc: '智能聊天,灵感碰撞', meta: '在线', type: 'chat',
detail: { quote: '与 AI 对话,激发无限灵感', author: '闲言 AI', tags: ['AI', '对话', '创意'], content: 'AI 对话助手可以帮助你激发灵感、整理思绪、生成创意。无论是写作瓶颈还是生活困惑,都可以在这里找到新的视角。\n\n支持多轮对话、上下文记忆、风格切换。' } },
{ id: 2, avatar: '📚', title: '经典文库', desc: '古今中外,名句荟萃', meta: '12万+', type: 'collection',
detail: { quote: '读万卷书,行万里路', author: '刘彝', tags: ['阅读', '经典', '积累'], content: '经典文库收录了从先秦诸子到现代文学的经典名句,按朝代、作者、主题分类,支持全文检索。\n\n目前已收录 12 万+ 条目,是写作和思考的宝贵素材库。' } },
{ id: 3, avatar: '🎨', title: '壁纸工坊', desc: '名句配美图,一键生成', meta: '新', type: 'tool',
detail: { quote: '让每一句话都成为艺术品', author: '闲言工坊', tags: ['创作', '壁纸', '设计'], content: '壁纸工坊提供海量模板和字体,将名句与精美图片结合,一键生成专属壁纸。\n\n支持自定义背景、字体、排版导出高清图片。' } },
]
},
profile: {
title: '我的',
items: [
{ id: 1, avatar: '⭐', title: '我的收藏', desc: '128 条收藏', meta: '', type: 'collection',
detail: { quote: '收藏即热爱', author: '个人空间', tags: ['收藏', '回忆'], content: '这里收藏了你珍视的所有句子,按时间倒序排列。你可以创建文件夹分类管理,或导出为 PDF 永久保存。' } },
{ id: 2, avatar: '📝', title: '我的笔记', desc: '36 篇笔记', meta: '', type: 'note',
detail: { quote: '记录即思考', author: '个人空间', tags: ['笔记', '思考'], content: '你的每一篇笔记都保存在这里,支持 Markdown 格式、标签分类、全文搜索。' } },
{ id: 3, avatar: '📊', title: '数据统计', desc: '使用 128 天', meta: '', type: 'stats',
detail: { quote: '数据见证成长', author: '个人空间', tags: ['统计', '成长'], content: '已阅读 1,024 条句子,收藏 128 条,创作 36 篇笔记,连续打卡 28 天。' } },
{ id: 4, avatar: '⚙️', title: '应用设置', desc: '主题·通知·隐私', meta: '', type: 'settings',
detail: { quote: '定制你的体验', author: '系统', tags: ['设置', '个性化'], content: '外观主题、字体大小、通知偏好、隐私设置、数据管理等。' } },
]
}
};
// ============================================================
// 状态管理
// ============================================================
let currentTab = 'home';
let currentItemId = null;
let rightPanelStack = []; // 右栏页面栈
// ============================================================
// 渲染中栏列表
// ============================================================
function renderMiddleList() {
const data = tabData[currentTab];
document.getElementById('middleTitle').textContent = data.title;
const listEl = document.getElementById('middleList');
listEl.innerHTML = data.items.map(item => `
<div class="list-item ${currentItemId === item.id ? 'selected' : ''}"
onclick="selectItem(${item.id})">
<div class="list-item-avatar">${item.avatar}</div>
<div class="list-item-content">
<div class="list-item-title">${item.title}</div>
<div class="list-item-desc">${item.desc}</div>
</div>
<div class="list-item-meta">${item.meta}</div>
</div>
`).join('');
}
// ============================================================
// 选中列表项 → 右栏显示详情
// ============================================================
function selectItem(id) {
const data = tabData[currentTab];
const item = data.items.find(i => i.id === id);
if (!item) return;
currentItemId = id;
rightPanelStack = [item]; // 重置栈
renderRightPanel();
renderMiddleList();
}
// ============================================================
// 渲染右栏
// ============================================================
function renderRightPanel() {
const topbar = document.getElementById('rightTopbar');
const backBtn = document.getElementById('backBtn');
const titleEl = document.getElementById('rightTitle');
const contentEl = document.getElementById('rightContent');
if (rightPanelStack.length === 0) {
// 默认显示概览仪表盘
backBtn.style.display = 'none';
titleEl.textContent = '概览';
contentEl.innerHTML = renderDashboard();
return;
}
const item = rightPanelStack[rightPanelStack.length - 1];
backBtn.style.display = rightPanelStack.length > 1 ? 'flex' : 'none';
titleEl.textContent = item.title;
contentEl.innerHTML = renderDetail(item.detail);
contentEl.scrollTop = 0;
}
// ============================================================
// 概览仪表盘(右栏默认状态)
// ============================================================
function renderDashboard() {
return `
<div class="dashboard">
<h2>👋 欢迎回来</h2>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon">📖</div>
<div class="stat-value">1,024</div>
<div class="stat-label">已读句子</div>
</div>
<div class="stat-card">
<div class="stat-icon">⭐</div>
<div class="stat-value">128</div>
<div class="stat-label">收藏</div>
</div>
<div class="stat-card">
<div class="stat-icon">📝</div>
<div class="stat-value">36</div>
<div class="stat-label">笔记</div>
</div>
<div class="stat-card">
<div class="stat-icon">🔥</div>
<div class="stat-value">28</div>
<div class="stat-label">连续打卡</div>
</div>
</div>
<div class="detail-section">
<h3>📌 今日推荐</h3>
<p>从左侧列表选择一条句子查看详情,或点击「搜索」探索更多内容。</p>
<div class="tag-row">
<span class="tag">晨间励志</span>
<span class="tag">古典诗词</span>
<span class="tag">哲学思考</span>
</div>
</div>
</div>
`;
}
// ============================================================
// 详情页(完整原始页面模拟)
// ============================================================
function renderDetail(detail) {
return `
<div class="detail-hero">
<div class="detail-quote">"${detail.quote}"</div>
<div class="detail-author">— ${detail.author}</div>
<div class="tag-row">
${detail.tags.map(t => `<span class="tag">#${t}</span>`).join('')}
</div>
</div>
<div class="detail-section">
<h3>📖 解读</h3>
<p style="white-space: pre-line;">${detail.content}</p>
</div>
<div class="detail-section">
<h3>💬 互动</h3>
<p>点赞 42 · 收藏 18 · 分享 7</p>
</div>
<div class="detail-section">
<h3>🔗 相关推荐</h3>
<p>这里会显示与当前句子相关的其他内容,点击可在右栏继续打开(三级页面)。</p>
</div>
`;
}
// ============================================================
// 返回上一级
// ============================================================
function goBack() {
if (rightPanelStack.length > 1) {
rightPanelStack.pop();
renderRightPanel();
} else {
rightPanelStack = [];
currentItemId = null;
renderRightPanel();
renderMiddleList();
}
}
// ============================================================
// 切换 Tab
// ============================================================
function switchTab(tab) {
currentTab = tab;
currentItemId = null;
rightPanelStack = [];
document.querySelectorAll('.nav-item[data-tab]').forEach(el => {
el.classList.toggle('active', el.dataset.tab === tab);
});
renderMiddleList();
renderRightPanel();
}
// ============================================================
// 主题切换
// ============================================================
function toggleTheme() {
const body = document.body;
const current = body.getAttribute('data-theme');
const next = current === 'light' ? 'dark' : 'light';
body.setAttribute('data-theme', next);
document.querySelector('.theme-toggle').textContent = next === 'light' ? '🌙' : '☀️';
}
// ============================================================
// 拖拽分割条调整中栏宽度
// ============================================================
const divider = document.getElementById('divider');
const middlePanel = document.getElementById('middlePanel');
let isDragging = false;
divider.addEventListener('mousedown', (e) => {
isDragging = true;
divider.classList.add('dragging');
document.body.style.cursor = 'col-resize';
document.body.style.userSelect = 'none';
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const navWidth = 72;
const newWidth = e.clientX - navWidth;
const min = 320;
const max = 600;
const clamped = Math.max(min, Math.min(max, newWidth));
middlePanel.style.width = clamped + 'px';
});
document.addEventListener('mouseup', () => {
if (isDragging) {
isDragging = false;
divider.classList.remove('dragging');
document.body.style.cursor = '';
document.body.style.userSelect = '';
localStorage.setItem('middleWidth', middlePanel.style.width);
}
});
// 恢复保存的宽度
const savedWidth = localStorage.getItem('middleWidth');
if (savedWidth) {
middlePanel.style.width = savedWidth;
}
// ============================================================
// 响应式断点检测
// ============================================================
function updateBreakpoint() {
const w = window.innerWidth;
const bpText = document.getElementById('bp-text');
const workspace = document.getElementById('workspace');
if (w >= 1280) {
bpText.textContent = `三栏模式 · ${w}px`;
workspace.className = 'workspace triple-column';
} else if (w >= 1024) {
bpText.textContent = `三栏紧凑 · ${w}px`;
workspace.className = 'workspace triple-column';
} else if (w >= 768) {
bpText.textContent = `双栏模式 · ${w}px`;
workspace.className = 'workspace dual-column';
} else {
bpText.textContent = `单栏模式 · ${w}px`;
workspace.className = 'workspace single-column';
}
}
window.addEventListener('resize', updateBreakpoint);
// ============================================================
// 键盘快捷键
// ============================================================
document.addEventListener('keydown', (e) => {
// Esc 返回
if (e.key === 'Escape' && rightPanelStack.length > 0) {
goBack();
}
// Ctrl+1/2/3 切换 Tab
if (e.ctrlKey || e.metaKey) {
if (e.key === '1') switchTab('home');
if (e.key === '2') switchTab('discover');
if (e.key === '3') switchTab('profile');
}
});
// ============================================================
// 初始化
// ============================================================
renderMiddleList();
renderRightPanel();
updateBreakpoint();
</script>
</body>
</html>