Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

770 lines
34 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>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>玩Android - 体系</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Roboto', 'Noto Sans SC', sans-serif; background: #F5F5F5; }
/* 容器 */
.container { max-width: 750px; margin: 0 auto; background: white; min-height: 100vh; }
/* 状态栏 */
.status-bar { height: 40px; background: #6200EE; }
/* 顶部导航 */
.top-nav { background: #6200EE; padding: 12px 16px; display: flex; align-items: center; justify-content: space-between; color: white; }
.nav-title { font-size: 20px; font-weight: 500; }
.nav-actions { display: flex; gap: 16px; }
.nav-icon { font-size: 24px; cursor: pointer; }
/* 搜索栏 */
.search-bar { background: #6200EE; padding: 12px 16px; }
.search-container { background: rgba(255,255,255,0.9); border-radius: 24px; padding: 12px 16px; display: flex; align-items: center; }
.search-icon { font-size: 20px; margin-right: 12px; color: #6200EE; }
.search-input { font-size: 16px; color: #212121; flex: 1; border: none; outline: none; background: transparent; }
.search-input::placeholder { color: #757575; }
/* 双栏布局 */
.tree-layout { display: flex; height: calc(100vh - 160px); overflow: hidden; }
/* 左侧分类树 */
.tree-sidebar { width: 320rpx; background: white; border-right: 1px solid #EEEEEE; overflow-y: auto; }
/* 分类列表 */
.category-list { padding: 16px; }
.category-item { margin-bottom: 8px; border-radius: 12px; overflow: hidden; transition: all 0.3s; }
.category-item.selected { background: #F0F9FF; border-left: 4px solid #6200EE; }
.category-header { display: flex; justify-content: space-between; align-items: center; padding: 16px; cursor: pointer; }
.category-header:hover { background: #F8F8F8; }
.category-info { display: flex; align-items: center; flex: 1; }
.category-icon { font-size: 20px; margin-right: 12px; color: #6200EE; }
.category-name { font-size: 16px; font-weight: 500; color: #212121; flex: 1; }
.category-count { font-size: 12px; color: #757575; margin-left: 8px; }
.category-actions { display: flex; align-items: center; gap: 12px; }
.expand-icon { font-size: 12px; color: #757575; padding: 4px; cursor: pointer; }
.selected-icon { font-size: 14px; color: #6200EE; font-weight: bold; }
/* 子分类列表 */
.subcategory-list { background: #FAFAFA; border-top: 1px solid #F0F0F0; padding: 0 16px; }
.subcategory-item { display: flex; justify-content: space-between; align-items: center; padding: 12px 0; border-bottom: 1px solid #F0F0F0; cursor: pointer; }
.subcategory-item:last-child { border-bottom: none; }
.subcategory-item:hover { background: #F5F5F5; }
.subcategory-item.selected { background: #F0F9FF; }
.subcategory-info { display: flex; align-items: center; flex: 1; }
.subcategory-icon { font-size: 16px; margin-right: 12px; color: #666; }
.subcategory-name { font-size: 14px; color: #666; flex: 1; }
/* 右侧内容区域 */
.tree-content { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
/* 当前分类信息 */
.current-category { padding: 20px; background: white; border-bottom: 1px solid #EEEEEE; }
.category-header { display: flex; align-items: center; margin-bottom: 12px; }
.category-title { font-size: 20px; font-weight: 500; color: #212121; flex: 1; }
.category-count { font-size: 14px; color: #757575; margin-left: 8px; }
.category-path { margin-bottom: 16px; }
.path-text { font-size: 14px; color: #666; background: #F8F8F8; padding: 6px 12px; border-radius: 6px; }
.category-actions { display: flex; gap: 12px; }
.action-item { display: flex; align-items: center; padding: 8px 16px; background: #F8F8F8; border-radius: 20px; cursor: pointer; transition: all 0.3s; }
.action-item:hover { background: #F0F0F0; }
.action-icon { font-size: 16px; margin-right: 6px; }
.action-text { font-size: 14px; color: #666; }
/* 文章滚动区域 */
.article-scroll { flex: 1; overflow-y: auto; }
/* 文章列表 */
.article-list { padding: 20px; }
/* 文章卡片 */
.article-card { background: white; border-radius: 16px; padding: 20px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); cursor: pointer; transition: all 0.3s; }
.article-card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.12); transform: translateY(-2px); }
.article-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px; }
.article-title { font-size: 18px; font-weight: 500; color: #212121; line-height: 1.4; flex: 1; }
.article-tags { display: flex; gap: 8px; margin-left: 12px; }
.tag { padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: 500; }
.tag.fresh { background: #FF4444; color: white; }
.tag.top { background: #FF9900; color: white; }
.article-summary { font-size: 14px; color: #666; line-height: 1.5; margin-bottom: 16px; }
.article-meta { display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 16px; padding-bottom: 16px; border-bottom: 1px solid #F0F0F0; }
.meta-item { display: flex; align-items: center; font-size: 12px; color: #999; }
.meta-icon { margin-right: 4px; font-size: 12px; }
.article-actions { display: flex; justify-content: space-around; align-items: center; }
.action-item { display: flex; flex-direction: column; align-items: center; padding: 8px 16px; border-radius: 8px; cursor: pointer; }
.action-item:hover { background: #F8F8F8; }
.action-icon { font-size: 20px; margin-bottom: 4px; }
.action-icon.collected { color: #FF9900; }
.action-text { font-size: 12px; color: #666; }
.action-text.collected { color: #FF9900; }
/* 加载更多 */
.load-more { text-align: center; padding: 32px; }
.load-btn { background: #6200EE; color: white; border: none; padding: 12px 32px; border-radius: 24px; font-size: 16px; cursor: pointer; }
.load-btn:hover { background: #3700B3; }
/* 空状态 */
.empty-state { text-align: center; padding: 60px 20px; color: #757575; }
.empty-icon { font-size: 48px; margin-bottom: 16px; }
.empty-title { font-size: 18px; font-weight: 500; margin-bottom: 8px; color: #212121; }
.empty-desc { font-size: 14px; margin-bottom: 24px; }
.empty-action { background: #6200EE; color: white; border: none; padding: 10px 24px; border-radius: 20px; font-size: 14px; cursor: pointer; }
/* 底部导航 */
.tab-bar { position: fixed; bottom: 0; left: 0; right: 0; background: white; border-top: 1px solid #EEEEEE; display: flex; justify-content: space-around; padding: 12px 0; max-width: 750px; margin: 0 auto; z-index: 100; }
.tab-item { display: flex; flex-direction: column; align-items: center; padding: 8px 16px; border-radius: 12px; transition: all 0.3s; cursor: pointer; }
.tab-item.active { background: rgba(98, 0, 238, 0.08); }
.tab-icon { font-size: 24px; margin-bottom: 4px; }
.tab-text { font-size: 12px; }
.tab-item.active .tab-icon, .tab-item.active .tab-text { color: #6200EE; }
.tab-item:not(.active) .tab-icon, .tab-item:not(.active) .tab-text { color: #757575; }
/* 安全区域 */
.safe-area { height: 80px; }
/* 加载动画 */
.loading { text-align: center; padding: 40px; color: #757575; }
.loading-spinner { width: 40px; height: 40px; border: 4px solid #E0E0E0; border-top: 4px solid #6200EE; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 16px; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
/* 响应式设计 */
@media (max-width: 600px) {
.tree-layout { flex-direction: column; }
.tree-sidebar { width: 100%; height: 200px; border-right: none; border-bottom: 1px solid #EEEEEE; }
}
</style>
</head>
<body>
<div class="container">
<!-- 状态栏 -->
<div class="status-bar"></div>
<!-- 顶部导航 -->
<div class="top-nav">
<div class="nav-title">玩Android - 体系</div>
<div class="nav-actions">
<div class="nav-icon" onclick="showMenu()">⋮</div>
</div>
</div>
<!-- 搜索栏 -->
<div class="search-bar">
<div class="search-container">
<div class="search-icon">🔍</div>
<input type="text" class="search-input" placeholder="搜索体系分类..." id="searchInput" onkeypress="handleSearchKeypress(event)">
</div>
</div>
<!-- 双栏布局 -->
<div class="tree-layout">
<!-- 左侧分类树 -->
<div class="tree-sidebar">
<div class="category-list" id="categoryList">
<div class="loading" id="loadingCategories">
<div class="loading-spinner"></div>
<div>正在加载分类...</div>
</div>
</div>
</div>
<!-- 右侧内容区域 -->
<div class="tree-content">
<!-- 当前分类信息 -->
<div class="current-category" id="currentCategory" style="display: none;">
<div class="category-header">
<div class="category-title" id="categoryTitle">请选择分类</div>
<div class="category-count" id="categoryCount"></div>
</div>
<div class="category-path" id="categoryPath"></div>
<div class="category-actions">
<div class="action-item" onclick="refreshArticles()">
<div class="action-icon">🔄</div>
<div class="action-text">刷新</div>
</div>
<div class="action-item" onclick="shareCategory()">
<div class="action-icon">↗️</div>
<div class="action-text">分享</div>
</div>
<div class="action-item" onclick="collectCategory()" id="collectBtn">
<div class="action-icon">⭐</div>
<div class="action-text">收藏</div>
</div>
</div>
</div>
<!-- 文章滚动区域 -->
<div class="article-scroll" id="articleScroll">
<!-- 空状态 -->
<div class="empty-state" id="emptyState">
<div class="empty-icon">📚</div>
<div class="empty-title">请选择分类</div>
<div class="empty-desc">点击左侧分类查看对应文章</div>
</div>
<!-- 文章列表 -->
<div class="article-list" id="articleList" style="display: none;">
<!-- 文章卡片会通过JavaScript动态生成 -->
</div>
<!-- 加载更多 -->
<div class="load-more" id="loadMoreContainer" style="display: none;">
<button class="load-btn" onclick="loadMoreArticles()">加载更多</button>
</div>
</div>
</div>
</div>
<!-- 安全区域 -->
<div class="safe-area"></div>
<!-- 底部导航 -->
<div class="tab-bar">
<div class="tab-item" onclick="switchTab('home')">
<div class="tab-icon">🏠</div>
<div class="tab-text">首页</div>
</div>
<div class="tab-item" onclick="switchTab('project')">
<div class="tab-icon">📦</div>
<div class="tab-text">项目</div>
</div>
<div class="tab-item" onclick="switchTab('wechat')">
<div class="tab-icon">📱</div>
<div class="tab-text">公众号</div>
</div>
<div class="tab-item active" onclick="switchTab('system')">
<div class="tab-icon">🌳</div>
<div class="tab-text">体系</div>
</div>
<div class="tab-item" onclick="switchTab('profile')">
<div class="tab-icon">👤</div>
<div class="tab-text">我的</div>
</div>
</div>
</div>
<script>
// 当前选中的分类
let selectedCategory = null;
let currentPage = 0;
let isLoading = false;
let hasMore = true;
let categories = [];
// 初始化页面
document.addEventListener('DOMContentLoaded', function() {
console.log('玩Android体系页面已加载');
console.log('公网访问: http://129.211.173.87:3000/tree.html');
// 加载体系分类
loadCategories();
});
// 加载体系分类
async function loadCategories() {
try {
const response = await fetch('/api/tree/json');
const data = await response.json();
if (data.errorCode === 0 && data.data && data.data.length > 0) {
categories = data.data;
renderCategories(categories);
} else {
// 使用默认分类数据
renderDefaultCategories();
}
} catch (error) {
console.error('加载分类失败:', error);
renderDefaultCategories();
}
}
// 渲染分类树
function renderCategories(categories) {
const container = document.getElementById('categoryList');
container.innerHTML = '';
categories.forEach(category => {
const categoryItem = document.createElement('div');
categoryItem.className = 'category-item';
categoryItem.setAttribute('data-id', category.id);
// 计算子分类数量
const childCount = category.children ? category.children.length : 0;
categoryItem.innerHTML = `
<div class="category-header" onclick="selectCategory(${category.id})">
<div class="category-info">
<div class="category-icon">📚</div>
<div class="category-name">${category.name}</div>
${childCount > 0 ? `<div class="category-count">(${childCount})</div>` : ''}
</div>
<div class="category-actions">
${childCount > 0 ? `<div class="expand-icon" onclick="event.stopPropagation(); toggleCategory(${category.id})">▶</div>` : ''}
<div class="selected-icon" id="selectedIcon${category.id}" style="display: none;">✓</div>
</div>
</div>
<div class="subcategory-list" id="subcategory${category.id}" style="display: none;">
${category.children ? category.children.map(child => `
<div class="subcategory-item" onclick="selectSubcategory(${child.id}, ${category.id})">
<div class="subcategory-info">
<div class="subcategory-icon">📖</div>
<div class="subcategory-name">${child.name}</div>
</div>
<div class="selected-icon" id="selectedIcon${child.id}" style="display: none;">✓</div>
</div>
`).join('') : ''}
</div>
`;
container.appendChild(categoryItem);
});
}
// 渲染默认分类
function renderDefaultCategories() {
const defaultCategories = [
{
id: 1,
name: "开发环境",
children: [
{ id: 11, name: "Android Studio相关" },
{ id: 12, name: "gradle" },
{ id: 13, name: "官方发布" },
{ id: 14, name: "90-120hz" }
]
},
{
id: 2,
name: "基础知识",
children: [
{ id: 21, name: "Activity" },
{ id: 22, name: "Service" },
{ id: 23, name: "BroadcastReceiver" },
{ id: 24, name: "ContentProvider" }
]
},
{
id: 3,
name: "四大组件",
children: [
{ id: 31, name: "Activity详解" },
{ id: 32, name: "Service详解" },
{ id: 33, name: "Broadcast详解" },
{ id: 34, name: "ContentProvider详解" }
]
},
{
id: 4,
name: "常用控件",
children: [
{ id: 41, name: "TextView" },
{ id: 42, name: "Button" },
{ id: 43, name: "ImageView" },
{ id: 44, name: "RecyclerView" }
]
}
];
categories = defaultCategories;
renderCategories(defaultCategories);
}
// 选择分类
function selectCategory(categoryId) {
// 清除之前的选中状态
document.querySelectorAll('.category-item.selected').forEach(item => {
item.classList.remove('selected');
});
document.querySelectorAll('.subcategory-item.selected').forEach(item => {
item.classList.remove('selected');
});
document.querySelectorAll('.selected-icon').forEach(icon => {
icon.style.display = 'none';
});
// 设置新的选中状态
const categoryItem = document.querySelector(`.category-item[data-id="${categoryId}"]`);
if (categoryItem) {
categoryItem.classList.add('selected');
const selectedIcon = document.getElementById(`selectedIcon${categoryId}`);
if (selectedIcon) {
selectedIcon.style.display = 'block';
}
}
// 查找分类
const category = categories.find(c => c.id === categoryId);
if (category) {
selectedCategory = {
id: category.id,
name: category.name,
level: 1
};
// 更新当前分类信息
updateCurrentCategory();
// 加载文章
loadArticles(category.id);
}
}
// 选择子分类
function selectSubcategory(subcategoryId, parentCategoryId) {
// 清除之前的选中状态
document.querySelectorAll('.category-item.selected').forEach(item => {
item.classList.remove('selected');
});
document.querySelectorAll('.subcategory-item.selected').forEach(item => {
item.classList.remove('selected');
});
document.querySelectorAll('.selected-icon').forEach(icon => {
icon.style.display = 'none';
});
// 设置新的选中状态
const subcategoryItem = document.querySelector(`.subcategory-item[onclick*="${subcategoryId}"]`);
if (subcategoryItem) {
subcategoryItem.classList.add('selected');
const selectedIcon = document.getElementById(`selectedIcon${subcategoryId}`);
if (selectedIcon) {
selectedIcon.style.display = 'block';
}
}
// 查找父分类
const parentCategory = categories.find(c => c.id === parentCategoryId);
if (parentCategory) {
// 查找子分类
const subcategory = parentCategory.children.find(c => c.id === subcategoryId);
if (subcategory) {
selectedCategory = {
id: subcategory.id,
name: subcategory.name,
level: 2,
parentCategory: {
id: parentCategory.id,
name: parentCategory.name
}
};
// 更新当前分类信息
updateCurrentCategory();
// 加载文章
loadArticles(subcategory.id);
}
}
}
// 切换分类展开状态
function toggleCategory(categoryId) {
const subcategoryList = document.getElementById(`subcategory${categoryId}`);
const expandIcon = event.target;
if (subcategoryList.style.display === 'none') {
subcategoryList.style.display = 'block';
expandIcon.textContent = '▼';
} else {
subcategoryList.style.display = 'none';
expandIcon.textContent = '▶';
}
}
// 更新当前分类信息
function updateCurrentCategory() {
const currentCategoryDiv = document.getElementById('currentCategory');
const emptyState = document.getElementById('emptyState');
const articleList = document.getElementById('articleList');
if (selectedCategory) {
currentCategoryDiv.style.display = 'block';
emptyState.style.display = 'none';
articleList.style.display = 'block';
// 更新分类标题
document.getElementById('categoryTitle').textContent = selectedCategory.name;
// 更新分类路径
const categoryPath = document.getElementById('categoryPath');
if (selectedCategory.level === 2 && selectedCategory.parentCategory) {
categoryPath.innerHTML = `<div class="path-text">父分类: ${selectedCategory.parentCategory.name}</div>`;
} else {
categoryPath.innerHTML = '';
}
} else {
currentCategoryDiv.style.display = 'none';
emptyState.style.display = 'block';
articleList.style.display = 'none';
}
}
// 加载文章
async function loadArticles(categoryId, page = 0) {
if (isLoading) return;
isLoading = true;
try {
const response = await fetch(`/api/article/list/${page}/json?cid=${categoryId}`);
const data = await response.json();
if (data.errorCode === 0 && data.data && data.data.datas) {
renderArticles(data.data.datas);
hasMore = !data.data.over;
// 更新文章数量
const total = data.data.total || data.data.datas.length;
document.getElementById('categoryCount').textContent = `(${total}篇文章)`;
// 显示或隐藏加载更多按钮
const loadMoreContainer = document.getElementById('loadMoreContainer');
loadMoreContainer.style.display = hasMore ? 'block' : 'none';
} else {
// 使用默认文章数据
renderDefaultArticles();
}
} catch (error) {
console.error('加载文章失败:', error);
renderDefaultArticles();
} finally {
isLoading = false;
currentPage = page;
}
}
// 渲染文章列表
function renderArticles(articles) {
const container = document.getElementById('articleList');
// 如果是第一页,清空容器
if (currentPage === 0) {
container.innerHTML = '';
}
articles.forEach(article => {
const articleElement = document.createElement('div');
articleElement.className = 'article-card';
articleElement.onclick = () => showArticleDetail(article.id);
// 格式化时间
const publishTime = new Date(article.publishTime);
const timeAgo = getTimeAgo(publishTime);
articleElement.innerHTML = `
<div class="article-header">
<div class="article-title">${article.title || '无标题'}</div>
<div class="article-tags">
${article.fresh ? '<div class="tag fresh">新</div>' : ''}
${article.top ? '<div class="tag top">置顶</div>' : ''}
</div>
</div>
<div class="article-summary">${article.desc || '暂无描述'}</div>
<div class="article-meta">
<div class="meta-item">
<span class="meta-icon">👤</span>
<span>${article.author || '未知作者'}</span>
</div>
<div class="meta-item">
<span class="meta-icon">🏷️</span>
<span>${article.superChapterName || ''}${article.chapterName ? ' · ' + article.chapterName : ''}</span>
</div>
<div class="meta-item">
<span class="meta-icon">🕒</span>
<span>${timeAgo}</span>
</div>
</div>
<div class="article-actions">
<div class="action-item" onclick="event.stopPropagation(); likeArticle(${article.id})">
<div class="action-icon">❤️</div>
<div class="action-text">${article.zan || 0}</div>
</div>
<div class="action-item" onclick="event.stopPropagation(); collectArticle(${article.id})">
<div class="action-icon ${article.collect ? 'collected' : ''}">⭐</div>
<div class="action-text ${article.collect ? 'collected' : ''}">${article.collect ? '已收藏' : '收藏'}</div>
</div>
<div class="action-item" onclick="event.stopPropagation(); shareArticle(${article.id})">
<div class="action-icon">↗️</div>
<div class="action-text">分享</div>
</div>
</div>
`;
container.appendChild(articleElement);
});
}
// 渲染默认文章
function renderDefaultArticles() {
const defaultArticles = [
{
id: 101,
title: "Android Studio优化技巧",
author: "Android开发者",
publishTime: Date.now() - 3600000,
superChapterName: "开发环境",
chapterName: "Android Studio相关",
desc: "本文介绍Android Studio的各种优化技巧,包括内存优化、编译加速、插件推荐等,帮助提升开发效率。",
zan: 42,
collect: false,
fresh: true
},
{
id: 102,
title: "Gradle配置详解",
author: "Gradle专家",
publishTime: Date.now() - 7200000,
superChapterName: "开发环境",
chapterName: "gradle",
desc: "深入解析Gradle配置文件,包括依赖管理、构建变体、自定义任务等高级功能。",
zan: 28,
collect: true,
fresh: false
},
{
id: 103,
title: "最新Android官方发布说明",
author: "Google",
publishTime: Date.now() - 10800000,
superChapterName: "开发环境",
chapterName: "官方发布",
desc: "Android最新版本的发布说明,包括新特性、API变更、兼容性说明等重要信息。",
zan: 56,
collect: false,
fresh: false,
top: true
}
];
renderArticles(defaultArticles);
}
// 加载更多文章
function loadMoreArticles() {
if (!selectedCategory || isLoading || !hasMore) return;
currentPage++;
loadArticles(selectedCategory.id, currentPage);
}
// 刷新文章
function refreshArticles() {
if (!selectedCategory) return;
currentPage = 0;
const articleList = document.getElementById('articleList');
articleList.innerHTML = '<div class="loading"><div class="loading-spinner"></div><div>正在刷新文章...</div></div>';
loadArticles(selectedCategory.id, 0);
setTimeout(() => {
alert('文章刷新成功!');
}, 1000);
}
// 分享分类
function shareCategory() {
if (!selectedCategory) return;
const shareText = `分享体系分类: ${selectedCategory.name}\n来自玩Android客户端`;
alert(`已复制分享内容:\n\n${shareText}`);
// 模拟复制到剪贴板
navigator.clipboard.writeText(shareText).then(() => {
console.log('分享内容已复制');
});
}
// 收藏分类
function collectCategory() {
if (!selectedCategory) return;
const collectBtn = document.getElementById('collectBtn');
const isCollected = collectBtn.querySelector('.action-text').textContent === '已收藏';
if (isCollected) {
collectBtn.querySelector('.action-text').textContent = '收藏';
alert(`已取消收藏分类: ${selectedCategory.name}`);
} else {
collectBtn.querySelector('.action-text').textContent = '已收藏';
alert(`已收藏分类: ${selectedCategory.name}`);
}
}
// 显示文章详情
function showArticleDetail(articleId) {
alert(`查看文章详情: ${articleId}\n\n文章详情功能开发中...`);
}
// 处理搜索按键
function handleSearchKeypress(event) {
if (event.key === 'Enter') {
const query = document.getElementById('searchInput').value.trim();
if (query) {
alert(`搜索体系分类: ${query}\n\n搜索功能开发中...`);
}
}
}
// 点赞文章
function likeArticle(articleId) {
alert(`已点赞文章 ${articleId}!`);
}
// 收藏文章
function collectArticle(articleId) {
alert(`已收藏文章 ${articleId}!`);
}
// 分享文章
function shareArticle(articleId) {
alert(`分享文章 ${articleId}!`);
}
// 显示菜单
function showMenu() {
alert('菜单功能开发中...\n\n包括设置、关于、反馈等功能');
}
// 切换Tab
function switchTab(tab) {
switch(tab) {
case 'home':
window.location.href = '/index.html';
break;
case 'project':
window.location.href = '/projects_fixed.html';
break;
case 'wechat':
window.location.href = '/wechat.html';
break;
case 'system':
// 当前页面,不需要跳转
break;
case 'profile':
window.location.href = '/profile.html';
break;
}
}
// 获取相对时间
function getTimeAgo(date) {
const now = new Date();
const diff = now - date;
const minutes = Math.floor(diff / 60000);
const hours = Math.floor(diff / 3600000);
const days = Math.floor(diff / 86400000);
if (minutes < 1) return '刚刚';
if (minutes < 60) return `${minutes}分钟前`;
if (hours < 24) return `${hours}小时前`;
if (days < 30) return `${days}天前`;
return date.toLocaleDateString();
}
</script>
</body>
</html>