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

540 lines
20 KiB
JavaScript

#!/usr/bin/env node
/**
* Uniapp Material Design项目开发服务器
* 绑定到所有IP地址,支持公网访问
*/
const express = require('express');
const cors = require('cors');
const path = require('path');
const fs = require('fs');
// 创建Express应用
const app = express();
const PORT = process.env.PORT || 8000;
const HOST = '0.0.0.0'; // 绑定到所有IP
// 启用CORS
app.use(cors());
// 解析JSON请求体
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 静态文件服务
app.use(express.static(path.join(__dirname)));
// 模拟Uniapp的页面路由
app.get('/pages/:page/:page', (req, res) => {
const pagePath = path.join(__dirname, 'pages', req.params.page, `${req.params.page}.vue`);
if (fs.existsSync(pagePath)) {
// 如果是Vue文件,返回一个简单的HTML页面
res.send(`
<!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 - ${req.params.page}</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Roboto', sans-serif; background: #F5F5F5; }
.container { max-width: 750px; margin: 0 auto; background: white; min-height: 100vh; }
.header { background: #6200EE; color: white; padding: 40px 20px; text-align: center; }
.content { padding: 40px 20px; text-align: center; }
.icon { font-size: 80px; color: #6200EE; margin-bottom: 24px; }
.title { font-size: 28px; font-weight: 500; margin-bottom: 12px; color: #212121; }
.desc { font-size: 16px; color: #757575; margin-bottom: 32px; line-height: 1.6; }
.info { background: #F5F5F5; padding: 20px; border-radius: 12px; margin-top: 32px; text-align: left; }
.info-title { font-size: 16px; font-weight: 500; margin-bottom: 12px; color: #6200EE; }
.info-item { margin-bottom: 8px; font-size: 14px; color: #616161; }
.btn { display: inline-block; background: #6200EE; color: white; padding: 12px 24px; border-radius: 24px; text-decoration: none; margin-top: 20px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>玩Android Material Design</h1>
<p>开发服务器预览</p>
</div>
<div class="content">
<div class="icon">📱</div>
<div class="title">${req.params.page}页面</div>
<div class="desc">这是Uniapp Material Design项目的${req.params.page}页面预览</div>
<div class="info">
<div class="info-title">服务器信息</div>
<div class="info-item">页面路径: /pages/${req.params.page}/${req.params.page}.vue</div>
<div class="info-item">服务器地址: ${req.protocol}://${req.get('host')}</div>
<div class="info-item">公网IP: 129.211.173.87:${PORT}</div>
<div class="info-item">访问时间: ${new Date().toLocaleString()}</div>
</div>
<a href="/" class="btn">返回首页</a>
</div>
</div>
</body>
</html>
`);
} else {
res.status(404).send('页面不存在');
}
});
// 首页路由
app.get('/', (req, res) => {
res.send(`
<!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 Material Design - 首页</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Roboto', 'Noto Sans SC', sans-serif; background: #F5F5F5; }
/* Material Design样式 */
.container { max-width: 750px; margin: 0 auto; background: white; min-height: 100vh; box-shadow: 0 0 20px rgba(0,0,0,0.1); }
/* 状态栏 */
.status-bar { height: 40px; background: #6200EE; }
/* 搜索栏 */
.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-placeholder { font-size: 16px; color: #757575; flex: 1; }
/* 内容区域 */
.content { padding: 24px 16px; }
/* Banner */
.banner { background: linear-gradient(135deg, #6200EE, #3700B3); height: 200px; border-radius: 16px; margin-bottom: 24px; display: flex; flex-direction: column; justify-content: center; align-items: center; color: white; }
.banner-icon { font-size: 60px; margin-bottom: 16px; }
.banner-title { font-size: 24px; font-weight: 500; margin-bottom: 8px; }
.banner-desc { font-size: 16px; opacity: 0.9; }
/* 功能卡片 */
.feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 32px 0; }
.feature-card { background: white; border-radius: 16px; padding: 24px; text-align: center; box-shadow: 0 2px 8px rgba(0,0,0,0.08); transition: all 0.3s; }
.feature-card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.12); transform: translateY(-4px); }
.feature-icon { font-size: 40px; margin-bottom: 16px; color: #6200EE; }
.feature-title { font-size: 18px; font-weight: 500; margin-bottom: 8px; color: #212121; }
.feature-desc { font-size: 14px; color: #757575; line-height: 1.5; }
/* 底部导航模拟 */
.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; }
.tab-item { display: flex; flex-direction: column; align-items: center; padding: 8px 16px; border-radius: 12px; transition: all 0.3s; }
.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; }
/* 服务器信息 */
.server-info { background: #F5F5F5; border-radius: 16px; padding: 24px; margin-top: 32px; }
.server-title { font-size: 18px; font-weight: 500; margin-bottom: 16px; color: #6200EE; }
.server-item { margin-bottom: 12px; font-size: 14px; color: #616161; display: flex; }
.server-label { font-weight: 500; min-width: 100px; }
.server-value { flex: 1; font-family: 'Courier New', monospace; }
/* 操作按钮 */
.action-buttons { display: flex; gap: 16px; margin-top: 24px; }
.btn { flex: 1; height: 48px; border-radius: 24px; display: flex; align-items: center; justify-content: center; font-size: 16px; font-weight: 500; text-decoration: none; transition: all 0.3s; }
.btn-primary { background: #6200EE; color: white; }
.btn-secondary { background: #03DAC6; color: white; }
.btn-outline { background: transparent; color: #6200EE; border: 2px solid #6200EE; }
.btn:hover { transform: scale(0.98); }
/* 安全区域 */
.safe-area { height: 80px; }
@media (max-width: 600px) {
.feature-grid { grid-template-columns: 1fr; }
.action-buttons { flex-direction: column; }
}
</style>
</head>
<body>
<div class="container">
<!-- 状态栏 -->
<div class="status-bar"></div>
<!-- 搜索栏 -->
<div class="search-bar">
<div class="search-container">
<div class="search-icon">🔍</div>
<div class="search-placeholder">搜索文章、项目、公众号...</div>
</div>
</div>
<!-- 内容区域 -->
<div class="content">
<!-- Banner -->
<div class="banner">
<div class="banner-icon">📱</div>
<div class="banner-title">玩Android Material Design</div>
<div class="banner-desc">Uniapp开发服务器预览</div>
</div>
<!-- 功能预览 -->
<div class="feature-grid">
<div class="feature-card">
<div class="feature-icon">🏠</div>
<div class="feature-title">首页功能</div>
<div class="feature-desc">Banner轮播、文章列表、搜索热词、下拉刷新、上拉加载</div>
</div>
<div class="feature-card">
<div class="feature-icon">🎨</div>
<div class="feature-title">Material Design</div>
<div class="feature-desc">遵循Material Design 3规范,紫色主色系统,完整设计语言</div>
</div>
<div class="feature-card">
<div class="feature-icon">📱</div>
<div class="feature-title">底部导航</div>
<div class="feature-desc">5个Tab导航,轮廓/填充双状态图标,平滑切换动画</div>
</div>
<div class="feature-card">
<div class="feature-icon">🔌</div>
<div class="feature-title">API集成</div>
<div class="feature-desc">集成玩Android开放API,统一网络请求和错误处理</div>
</div>
</div>
<!-- 服务器信息 -->
<div class="server-info">
<div class="server-title">服务器信息</div>
<div class="server-item">
<div class="server-label">服务器地址:</div>
<div class="server-value">${req.protocol}://${req.get('host')}</div>
</div>
<div class="server-item">
<div class="server-label">公网IP:</div>
<div class="server-value">129.211.173.87:${PORT}</div>
</div>
<div class="server-item">
<div class="server-label">项目路径:</div>
<div class="server-value">/home/ubuntu/wanandroid/uniapp_android</div>
</div>
<div class="server-item">
<div class="server-label">启动时间:</div>
<div class="server-value">${new Date().toLocaleString()}</div>
</div>
<div class="server-item">
<div class="server-label">Node版本:</div>
<div class="server-value">${process.version}</div>
</div>
<div class="server-item">
<div class="server-label">平台:</div>
<div class="server-value">${process.platform} ${process.arch}</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="action-buttons">
<a href="/material_preview.html" class="btn btn-primary">查看Material预览</a>
<a href="http://129.211.173.87:${PORT}/material_preview.html" class="btn btn-secondary">公网访问预览</a>
<a href="/quick_test.html" class="btn btn-outline">快速测试</a>
</div>
<!-- 安全区域 -->
<div class="safe-area"></div>
</div>
<!-- 底部导航 -->
<div class="tab-bar">
<div class="tab-item active">
<div class="tab-icon">🏠</div>
<div class="tab-text">首页</div>
</div>
<div class="tab-item">
<div class="tab-icon">📦</div>
<div class="tab-text">项目</div>
</div>
<div class="tab-item">
<div class="tab-icon">📱</div>
<div class="tab-text">公众号</div>
</div>
<div class="tab-item">
<div class="tab-icon">🌳</div>
<div class="tab-text">体系</div>
</div>
<div class="tab-item">
<div class="tab-icon">👤</div>
<div class="tab-text">我的</div>
</div>
</div>
</div>
<script>
// 简单的Tab切换
document.querySelectorAll('.tab-item').forEach(tab => {
tab.addEventListener('click', function() {
document.querySelectorAll('.tab-item').forEach(t => t.classList.remove('active'));
this.classList.add('active');
const tabText = this.querySelector('.tab-text').textContent;
alert('切换到: ' + tabText + ' 页面 (开发中)');
});
});
// 搜索栏点击
document.querySelector('.search-container').addEventListener('click', function() {
alert('搜索功能 (开发中)');
});
// 显示访问信息
console.log('玩Android Material Design开发服务器');
console.log('公网访问: http://129.211.173.87:${PORT}');
console.log('本地访问: http://localhost:${PORT}');
console.log('项目路径: /home/ubuntu/wanandroid/uniapp_android');
</script>
</body>
</html>
`);
});
// API模拟 - 玩Android API
app.get('/api/banner/json', (req, res) => {
res.json({
data: [
{
desc: "Material Design Banner 1",
id: 1,
imagePath: "https://via.placeholder.com/300x150/6200EE/ffffff?text=Banner1",
isVisible: 1,
order: 0,
title: "玩Android Material Design",
type: 0,
url: "https://www.wanandroid.com"
},
{
desc: "Material Design Banner 2",
id: 2,
imagePath: "https://via.placeholder.com/300x150/03DAC6/ffffff?text=Banner2",
isVisible: 1,
order: 1,
title: "Uniapp开发框架",
type: 0,
url: "https://uniapp.dcloud.io"
},
{
desc: "Material Design Banner 3",
id: 3,
imagePath: "https://via.placeholder.com/300x150/FF9800/ffffff?text=Banner3",
isVisible: 1,
order: 2,
title: "Android应用开发",
type: 0,
url: "https://developer.android.com"
}
],
errorCode: 0,
errorMsg: ""
});
});
app.get('/api/hotkey/json', (req, res) => {
res.json({
data: [
{ id: 1, link: "", name: "Material Design", order: 1, visible: 1 },
{ id: 2, link: "", name: "Uniapp", order: 2, visible: 1 },
{ id: 3, link: "", name: "Android", order: 3, visible: 1 },
{ id: 4, link: "", name: "Vue 3", order: 4, visible: 1 },
{ id: 5, link: "", name: "玩Android", order: 5, visible: 1 },
{ id: 6, link: "", name: "API开发", order: 6, visible: 1 }
],
errorCode: 0,
errorMsg: ""
});
});
app.get('/api/article/list/0/json', (req, res) => {
res.json({
data: {
curPage: 0,
datas: [
{
id: 1,
title: "玩Android Material Design客户端发布",
link: "https://www.wanandroid.com",
author: "Material Design",
shareUser: "",
chapterName: "前端开发",
superChapterName: "移动开发",
niceDate: "刚刚",
publishTime: Date.now(),
collect: false,
zan: 42,
fresh: true,
tags: []
},
{
id: 2,
title: "Uniapp框架Material Design实践指南",
link: "https://uniapp.dcloud.io",
author: "Uniapp团队",
shareUser: "",
chapterName: "Uniapp",
superChapterName: "跨端开发",
niceDate: "1小时前",
publishTime: Date.now() - 3600000,
collect: true,
zan: 28,
fresh: false,
tags: []
},
{
id: 3,
title: "Android Material Design 3新特性详解",
link: "https://developer.android.com",
author: "Google",
shareUser: "",
chapterName: "Android",
superChapterName: "移动开发",
niceDate: "3小时前",
publishTime: Date.now() - 10800000,
collect: false,
zan: 56,
fresh: false,
tags: []
},
{
id: 4,
title: "玩Android API接口完整文档",
link: "https://www.wanandroid.com/blog/show/2",
author: "玩Android",
shareUser: "",
chapterName: "API文档",
superChapterName: "开发资源",
niceDate: "1天前",
publishTime: Date.now() - 86400000,
collect: true,
zan: 89,
fresh: false,
tags: []
}
],
offset: 0,
over: false,
pageCount: 5,
size: 20,
total: 20
},
errorCode: 0,
errorMsg: ""
});
});
// 健康检查
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
service: 'wanandroid-material-server',
version: '1.0.0',
uptime: process.uptime()
});
});
// 404处理
app.use((req, res) => {
res.status(404).send(`
<!DOCTYPE html>
<html>
<head>
<title>404 - 页面未找到</title>
<style>
body { font-family: 'Roboto', sans-serif; background: #F5F5F5; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
.error-container { text-align: center; padding: 40px; background: white; border-radius: 20px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); }
.error-icon { font-size: 80px; color: #6200EE; margin-bottom: 24px; }
.error-title { font-size: 32px; font-weight: 500; color: #212121; margin-bottom: 16px; }
.error-message { font-size: 18px; color: #757575; margin-bottom: 32px; }
.error-path { font-family: 'Courier New', monospace; background: #F5F5F5; padding: 12px; border-radius: 8px; margin: 20px 0; }
.btn { display: inline-block; background: #6200EE; color: white; padding: 12px 24px; border-radius: 24px; text-decoration: none; margin-top: 20px; }
</style>
</head>
<body>
<div class="error-container">
<div class="error-icon">🔍</div>
<div class="error-title">404 - 页面未找到</div>
<div class="error-message">请求的页面不存在</div>
<div class="error-path">${req.originalUrl}</div>
<a href="/" class="btn">返回首页</a>
</div>
</body>
</html>
`);
});
// 启动服务器
app.listen(PORT, HOST, () => {
console.log(`
==============================================
玩Android Material Design开发服务器
==============================================
✅ 服务器启动成功!
📱 本地访问:
http://localhost:${PORT}
http://127.0.0.1:${PORT}
🌐 公网访问:
http://129.211.173.87:${PORT}
📁 项目信息:
项目路径: /home/ubuntu/wanandroid/uniapp_android
服务器IP: ${HOST}
服务端口: ${PORT}
Node版本: ${process.version}
🚀 可用路由:
/ - 首页预览
/material_preview.html - Material Design预览
/api/banner/json - Banner数据API
/api/hotkey/json - 搜索热词API
/api/article/list/0/json - 文章列表API
/health - 健康检查
⏰ 启动时间: ${new Date().toLocaleString()}
==============================================
`);
// 打印网络信息
const os = require('os');
const networkInterfaces = os.networkInterfaces();
console.log('📡 网络接口信息:');
Object.keys(networkInterfaces).forEach(interfaceName => {
networkInterfaces[interfaceName].forEach(interface => {
if (interface.family === 'IPv4' && !interface.internal) {
console.log(` ${interfaceName}: ${interface.address}`);
}
});
});
console.log('==============================================\n');
});
// 优雅关闭
process.on('SIGINT', () => {
console.log('\n🛑 收到关闭信号,正在停止服务器...');
process.exit(0);
});
process.on('SIGTERM', () => {
console.log('\n🛑 收到终止信号,正在停止服务器...');
process.exit(0);
});