a
This commit is contained in:
@@ -0,0 +1,770 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user