Files
nanocode/wanandroid/uniapp_android/index_correct.html
T
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

664 lines
31 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html>
<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; }
/* Banner轮播图 */
.banner-container { padding: 16px; position: relative; }
.banner-slider { position: relative; height: 160px; border-radius: 16px; overflow: hidden; }
.banner-item { position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; transition: opacity 0.5s; display: flex; flex-direction: column; justify-content: center; align-items: center; color: white; padding: 20px; text-align: center; }
.banner-item.active { opacity: 1; }
.banner-item:nth-child(1) { background: linear-gradient(135deg, #6200EE, #3700B3); }
.banner-item:nth-child(2) { background: linear-gradient(135deg, #03DAC6, #018786); }
.banner-item:nth-child(3) { background: linear-gradient(135deg, #FF5252, #B00020); }
.banner-title { font-size: 20px; font-weight: 500; margin-bottom: 8px; }
.banner-desc { font-size: 14px; opacity: 0.9; margin-bottom: 12px; }
.banner-link { background: rgba(255,255,255,0.2); padding: 6px 16px; border-radius: 20px; font-size: 14px; cursor: pointer; }
.banner-dots { display: flex; justify-content: center; margin-top: 12px; gap: 8px; }
.banner-dot { width: 8px; height: 8px; border-radius: 50%; background: #E0E0E0; cursor: pointer; }
.banner-dot.active { background: #6200EE; }
/* 文章列表 */
.articles-container { padding: 0 16px 80px; }
.section-title { font-size: 18px; font-weight: 500; margin: 24px 0 16px; color: #212121; display: flex; align-items: center; }
.section-title::before { content: "📰"; margin-right: 8px; }
/* 文章卡片 */
.article-card { background: white; border-radius: 16px; padding: 20px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); transition: all 0.3s; cursor: pointer; }
.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-fresh { background: #FF5252; color: white; font-size: 12px; padding: 2px 8px; border-radius: 10px; margin-left: 8px; }
.article-meta { display: flex; align-items: center; margin-bottom: 12px; font-size: 14px; color: #757575; }
.article-author { margin-right: 16px; }
.article-time { margin-right: 16px; }
.article-category { background: #E0E0E0; padding: 2px 8px; border-radius: 10px; font-size: 12px; }
.article-content { font-size: 15px; color: #616161; line-height: 1.6; margin-bottom: 16px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.article-footer { display: flex; justify-content: space-between; align-items: center; }
.article-stats { display: flex; gap: 16px; }
.stat-item { display: flex; align-items: center; font-size: 14px; color: #757575; }
.stat-icon { margin-right: 4px; }
.article-actions { display: flex; gap: 12px; }
.action-btn { background: #F5F5F5; border: none; padding: 6px 12px; border-radius: 16px; font-size: 14px; color: #6200EE; cursor: pointer; transition: all 0.3s; }
.action-btn:hover { background: #6200EE; color: white; }
/* 底部导航 */
.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; }
/* 文章详情页 */
.article-detail { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: white; z-index: 1000; overflow-y: auto; }
.detail-header { background: #6200EE; padding: 16px; display: flex; align-items: center; justify-content: space-between; color: white; }
.detail-back { font-size: 24px; cursor: pointer; }
.detail-title { font-size: 18px; font-weight: 500; }
.detail-content { padding: 24px; }
.detail-title-main { font-size: 24px; font-weight: 500; margin-bottom: 16px; color: #212121; }
.detail-meta { display: flex; align-items: center; margin-bottom: 24px; padding-bottom: 16px; border-bottom: 1px solid #EEEEEE; }
.detail-author { margin-right: 16px; font-size: 16px; color: #6200EE; }
.detail-time { font-size: 14px; color: #757575; }
.detail-body { font-size: 16px; line-height: 1.8; color: #424242; }
.detail-body p { margin-bottom: 16px; }
.detail-body h2 { font-size: 20px; font-weight: 500; margin: 24px 0 16px; color: #212121; }
.detail-body h3 { font-size: 18px; font-weight: 500; margin: 20px 0 12px; color: #212121; }
.detail-body code { background: #F5F5F5; padding: 2px 6px; border-radius: 4px; font-family: 'Courier New', monospace; }
.detail-body pre { background: #F5F5F5; padding: 16px; border-radius: 8px; overflow-x: auto; margin: 16px 0; }
.detail-footer { padding: 24px; border-top: 1px solid #EEEEEE; margin-top: 32px; }
.detail-actions { display: flex; justify-content: space-between; }
/* 加载更多 */
.load-more { text-align: center; padding: 24px; }
.load-btn { background: #6200EE; color: white; border: none; padding: 12px 32px; border-radius: 24px; font-size: 16px; cursor: pointer; transition: all 0.3s; }
.load-btn:hover { background: #3700B3; }
/* 安全区域 */
.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) {
.article-header { flex-direction: column; }
.article-title { margin-bottom: 8px; }
.article-footer { flex-direction: column; align-items: flex-start; gap: 12px; }
.article-actions { align-self: flex-end; }
}
</style>
</head>
<body>
<!-- 文章列表页面 -->
<div class="container" id="listPage">
<!-- 状态栏 -->
<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="搜索wanandroid.com的内容..." id="searchInput" onkeypress="handleSearchKeypress(event)">
</div>
</div>
<!-- Banner轮播图 -->
<div class="banner-container">
<div class="banner-slider" id="bannerSlider">
<!-- Banner内容会通过JavaScript动态生成 -->
</div>
<div class="banner-dots" id="bannerDots">
<!-- Banner指示点会通过JavaScript动态生成 -->
</div>
</div>
<!-- 文章列表 -->
<div class="articles-container">
<div class="section-title">最新文章</div>
<!-- 文章列表容器 -->
<div id="articlesList">
<div class="loading" id="loadingArticles">
<div class="loading-spinner"></div>
<div>正在加载文章...</div>
</div>
</div>
<!-- 加载更多 -->
<div class="load-more" id="loadMoreContainer" style="display: none;">
<button class="load-btn" onclick="loadMoreArticles()">加载更多</button>
</div>
</div>
<!-- 安全区域 -->
<div class="safe-area"></div>
<!-- 底部导航 -->
<div class="tab-bar">
<div class="tab-item active" 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" 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>
<!-- 文章详情页 -->
<div class="article-detail" id="detailPage">
<div class="detail-header">
<div class="detail-back" onclick="closeDetail()">←</div>
<div class="detail-title">文章详情</div>
<div style="width: 24px;"></div>
</div>
<div class="detail-content">
<div id="detailContent">
<div class="loading" id="loadingDetail">
<div class="loading-spinner"></div>
<div>正在加载文章详情...</div>
</div>
</div>
</div>
</div>
<script>
// 当前页码
let currentPage = 0;
let isLoading = false;
let hasMore = true;
// 初始化页面
document.addEventListener('DOMContentLoaded', function() {
console.log('玩Android首页已加载');
console.log('公网访问: http://129.211.173.87:3000/index_correct.html');
// 加载Banner数据
loadBanners();
// 加载文章列表
loadArticles();
});
// 加载Banner数据
async function loadBanners() {
try {
const response = await fetch('/api/banner/json');
const data = await response.json();
if (data.errorCode === 0 && data.data && data.data.length > 0) {
renderBanners(data.data);
} else {
// 使用默认Banner数据
renderDefaultBanners();
}
} catch (error) {
console.error('加载Banner失败:', error);
renderDefaultBanners();
}
}
// 渲染Banner
function renderBanners(banners) {
const slider = document.getElementById('bannerSlider');
const dots = document.getElementById('bannerDots');
slider.innerHTML = '';
dots.innerHTML = '';
banners.forEach((banner, index) => {
// Banner项
const bannerItem = document.createElement('div');
bannerItem.className = `banner-item ${index === 0 ? 'active' : ''}`;
bannerItem.innerHTML = `
<div class="banner-title">${banner.title || '玩Android'}</div>
<div class="banner-desc">${banner.desc || 'Android学习平台'}</div>
<div class="banner-link" onclick="openBannerLink('${banner.url || '#'}')">查看详情</div>
`;
slider.appendChild(bannerItem);
// Banner指示点
const dot = document.createElement('div');
dot.className = `banner-dot ${index === 0 ? 'active' : ''}`;
dot.onclick = () => switchBanner(index);
dots.appendChild(dot);
});
// 自动轮播
startBannerAutoPlay();
}
// 渲染默认Banner
function renderDefaultBanners() {
const defaultBanners = [
{
title: '玩Android',
desc: 'Android学习平台',
url: 'https://www.wanandroid.com'
},
{
title: 'Material Design 3',
desc: '最新设计规范',
url: 'https://m3.material.io'
},
{
title: 'Uniapp开发',
desc: '跨平台应用开发',
url: 'https://uniapp.dcloud.net.cn'
}
];
renderBanners(defaultBanners);
}
// 切换Banner
let currentBannerIndex = 0;
let bannerInterval;
function switchBanner(index) {
const banners = document.querySelectorAll('.banner-item');
const dots = document.querySelectorAll('.banner-dot');
banners[currentBannerIndex].classList.remove('active');
dots[currentBannerIndex].classList.remove('active');
currentBannerIndex = index;
banners[currentBannerIndex].classList.add('active');
dots[currentBannerIndex].classList.add('active');
}
// 开始自动轮播
function startBannerAutoPlay() {
if (bannerInterval) clearInterval(bannerInterval);
bannerInterval = setInterval(() => {
const banners = document.querySelectorAll('.banner-item');
const nextIndex = (currentBannerIndex + 1) % banners.length;
switchBanner(nextIndex);
}, 3000);
}
// 打开Banner链接
function openBannerLink(url) {
if (url && url !== '#') {
window.open(url, '_blank');
}
}
// 加载文章列表
async function loadArticles() {
if (isLoading || !hasMore) return;
isLoading = true;
try {
const response = await fetch(`/api/article/list/${currentPage}/json`);
const data = await response.json();
if (data.errorCode === 0 && data.data && data.data.datas) {
renderArticles(data.data.datas);
hasMore = !data.data.over;
// 显示或隐藏加载更多按钮
const loadMoreContainer = document.getElementById('loadMoreContainer');
loadMoreContainer.style.display = hasMore ? 'block' : 'none';
} else {
// 使用默认文章数据
renderDefaultArticles();
}
} catch (error) {
console.error('加载文章失败:', error);
renderDefaultArticles();
} finally {
isLoading = false;
document.getElementById('loadingArticles').style.display = 'none';
}
}
// 渲染文章列表
function renderArticles(articles) {
const container = document.getElementById('articlesList');
// 如果是第一页,清空容器
if (currentPage === 0) {
container.innerHTML = '';
}
articles.forEach(article => {
const articleElement = document.createElement('div');
articleElement.className = 'article-card';
articleElement.setAttribute('data-id', article.id);
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>
${article.fresh ? '<div class="article-fresh">新</div>' : ''}
</div>
<div class="article-meta">
<div class="article-author">👤 ${article.author || '未知作者'}</div>
<div class="article-time">🕒 ${timeAgo}</div>
${article.superChapterName ? `<div class="article-category">${article.superChapterName}</div>` : ''}
</div>
<div class="article-content">${article.desc || '暂无描述'}</div>
<div class="article-footer">
<div class="article-stats">
<div class="stat-item">
<span class="stat-icon">👍</span>
<span>${article.zan || 0}</span>
</div>
<div class="stat-item">
<span class="stat-icon">💬</span>
<span>${article.comments || 0}</span>
</div>
<div class="stat-item">
<span class="stat-icon">⭐</span>
<span>${article.collect ? '已收藏' : '未收藏'}</span>
</div>
</div>
<div class="article-actions">
<button class="action-btn" onclick="event.stopPropagation(); likeArticle(${article.id})">点赞</button>
<button class="action-btn" onclick="event.stopPropagation(); collectArticle(${article.id})">${article.collect ? '取消收藏' : '收藏'}</button>
</div>
</div>
`;
container.appendChild(articleElement);
});
}
// 渲染默认文章
function renderDefaultArticles() {
const defaultArticles = [
{
id: 1,
title: "玩Android Material Design客户端发布",
author: "Material Design",
publishTime: Date.now() - 3600000, // 1小时前
superChapterName: "前端开发",
desc: "玩Android Material Design客户端正式发布!这是一个基于Uniapp框架开发的Android应用,完全遵循Material Design 3设计规范。",
zan: 42,
collect: false,
comments: 12,
fresh: true
},
{
id: 2,
title: "Uniapp框架Material Design实践指南",
author: "Uniapp团队",
publishTime: Date.now() - 7200000, // 2小时前
superChapterName: "Uniapp",
desc: "本文详细介绍了如何在Uniapp框架中实现Material Design设计规范。从颜色系统、排版、图标到组件实现,提供了完整的实践指南和代码示例。",
zan: 28,
collect: true,
comments: 8,
fresh: false
},
{
id: 3,
title: "Android Material Design 3新特性详解",
author: "Google",
publishTime: Date.now() - 10800000, // 3小时前
superChapterName: "Android",
desc: "Material Design 3带来了动态颜色、设计令牌、组件更新等新特性。本文详细解析了这些新特性,并提供了在Android应用中的实现方法。",
zan: 56,
collect: false,
comments: 24,
fresh: false
}
];
renderArticles(defaultArticles);
}
// 加载更多文章
function loadMoreArticles() {
currentPage++;
loadArticles();
}
// 显示文章详情
async function showArticleDetail(articleId) {
document.getElementById('listPage').style.display = 'none';
document.getElementById('detailPage').style.display = 'block';
const detailContent = document.getElementById('detailContent');
detailContent.innerHTML = `
<div class="loading" id="loadingDetail">
<div class="loading-spinner"></div>
<div>正在加载文章详情...</div>
</div>
`;
try {
// 这里应该调用文章详情API,但为了简化,我们使用模拟数据
await new Promise(resolve => setTimeout(resolve, 500));
// 模拟文章详情数据
const articleDetails = {
1: {
title: "玩Android Material Design客户端发布",
author: "Material Design",
publishTime: Date.now() - 3600000,
content: `
<h1>玩Android Material Design客户端发布</h1>
<p>玩Android Material Design客户端正式发布!这是一个基于Uniapp框架开发的Android应用,完全遵循Material Design 3设计规范。</p>
<h2>主要特性</h2>
<p>1. <strong>Material Design 3规范</strong>:完全遵循Google最新的Material Design 3设计语言</p>
<p>2. <strong>紫色主色系统</strong>:采用#6200EE作为主色,搭配#03DAC6作为强调色</p>
<p>3. <strong>完整组件库</strong>:提供卡片、按钮、导航栏、对话框等完整组件</p>
<p>4. <strong>响应式设计</strong>:完美适配手机、平板和桌面设备</p>
<h2>技术栈</h2>
<ul>
<li>前端框架:Uniapp + Vue 3</li>
<li>UI组件:自定义Material Design组件</li>
<li>状态管理:Pinia</li>
<li>网络请求:Axios + 拦截器</li>
<li>构建工具:Vite</li>
</ul>
`
},
2: {
title: "Uniapp框架Material Design实践指南",
author: "Uniapp团队",
publishTime: Date.now() - 7200000,
content: `
<h1>Uniapp框架Material Design实践指南</h1>
<p>Material Design是Google推出的设计语言,强调材质、动效和层次感。在Uniapp中实现Material Design需要关注以下几个方面:</p>
<h2>1. 颜色系统</h2>
<p>Material Design 3引入了动态颜色系统,可以根据壁纸自动生成调色板。在Uniapp中可以通过CSS变量实现:</p>
<pre><code>:root {
--md-primary: #6200EE;
--md-secondary: #03DAC6;
--md-surface: #FFFFFF;
--md-background: #F5F5F5;
--md-on-primary: #FFFFFF;
--md-on-secondary: #000000;
}</code></pre>
`
},
3: {
title: "Android Material Design 3新特性详解",
author: "Google",
publishTime: Date.now() - 10800000,
content: `
<h1>Android Material Design 3新特性详解</h1>
<p>Material Design 3是Google设计语言的最新版本,带来了许多重要的更新和改进。</p>
<h2>1. 动态颜色(Dynamic Color)</h2>
<p>动态颜色是Material Design 3的核心特性,可以根据用户的壁纸自动生成调色板:</p>
<pre><code>// Kotlin实现
val dynamicColor = DynamicColors.wrapContextIfAvailable(context)
setContentView(dynamicColor)</code></pre>
`
}
};
const article = articleDetails[articleId] || {
title: "文章详情",
author: "未知作者",
publishTime: Date.now(),
content: "<p>文章内容加载失败</p>"
};
const timeAgo = getTimeAgo(new Date(article.publishTime));
detailContent.innerHTML = `
<div class="detail-title-main">${article.title}</div>
<div class="detail-meta">
<div class="detail-author">👤 ${article.author}</div>
<div class="detail-time">🕒 ${timeAgo}</div>
</div>
<div class="detail-body">${article.content}</div>
<div class="detail-footer">
<div class="detail-actions">
<button class="action-btn" onclick="likeArticle(${articleId})">👍 点赞</button>
<button class="action-btn" onclick="collectArticle(${articleId})">⭐ 收藏</button>
<button class="action-btn" onclick="shareArticle(${articleId})">📤 分享</button>
</div>
</div>
`;
} catch (error) {
console.error('加载文章详情失败:', error);
detailContent.innerHTML = `
<div class="detail-title-main">加载失败</div>
<div class="detail-body">
<p>抱歉,加载文章详情时出现错误。</p>
<button class="action-btn" onclick="closeDetail()">返回</button>
</div>
`;
}
}
// 关闭文章详情
function closeDetail() {
document.getElementById('listPage').style.display = 'block';
document.getElementById('detailPage').style.display = 'none';
}
// 处理搜索按键
function handleSearchKeypress(event) {
if (event.key === 'Enter') {
const query = document.getElementById('searchInput').value.trim();
if (query) {
alert(`搜索: ${query}\n\n搜索功能开发中,将搜索wanandroid.com的内容`);
// 这里应该调用搜索API
}
}
}
// 点赞文章
function likeArticle(articleId) {
alert(`已点赞文章 ${articleId}!`);
}
// 收藏文章
function collectArticle(articleId) {
alert(`已收藏文章 ${articleId}!`);
}
// 分享文章
function shareArticle(articleId) {
alert(`分享文章 ${articleId}!`);
}
// 显示菜单
function showMenu() {
alert('菜单功能开发中...\n\n包括设置、关于、反馈等功能');
}
// 切换Tab
function switchTab(tab) {
document.querySelectorAll('.tab-item').forEach(item => {
item.classList.remove('active');
});
event.target.closest('.tab-item').classList.add('active');
const tabNames = {
'home': '首页',
'project': '项目',
'wechat': '公众号',
'system': '体系',
'profile': '我的'
};
alert(`切换到: ${tabNames[tab]} 页面 (开发中)`);
}
// 获取相对时间
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>