init
This commit is contained in:
+231
@@ -0,0 +1,231 @@
|
||||
# 软件发布平台完善计划
|
||||
|
||||
## 项目目标
|
||||
将现有的简单下载页面升级为功能完整的软件发布平台,包含后端管理、版本管理和配置系统。
|
||||
|
||||
## 技术方案
|
||||
|
||||
### 数据库选择
|
||||
- 使用 **SQLite** 作为文件数据库(轻量、无需额外配置)
|
||||
- 数据库文件路径:`data/database.db`
|
||||
|
||||
### 数据库设计
|
||||
|
||||
**apps 表** - 存储应用版本信息
|
||||
```sql
|
||||
CREATE TABLE apps (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
filename TEXT NOT NULL,
|
||||
original_name TEXT NOT NULL,
|
||||
version TEXT,
|
||||
description TEXT,
|
||||
upload_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
file_size INTEGER
|
||||
);
|
||||
```
|
||||
|
||||
**config 表** - 存储网站配置
|
||||
```sql
|
||||
CREATE TABLE config (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT
|
||||
);
|
||||
```
|
||||
|
||||
预设配置项:
|
||||
- `site_title` - 网站标题(默认:"OPS 运营管理系统")
|
||||
- `site_description` - 软件说明(默认:"点击下方按钮下载移动端 APP")
|
||||
- `logo_path` - Logo 路径(默认:"logo.png")
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
php_app_downloader/
|
||||
├── index.php # 主页(显示所有版本)
|
||||
├── download.php # 下载接口(支持按ID下载)
|
||||
├── logo.png # 默认 Logo
|
||||
├── admin/ # 管理后端目录
|
||||
│ ├── index.php # 登录页面
|
||||
│ ├── dashboard.php # 管理面板主页
|
||||
│ ├── upload.php # 文件上传处理
|
||||
│ ├── config.php # 配置管理页面
|
||||
│ ├── delete.php # 删除应用接口
|
||||
│ ├── logout.php # 登出
|
||||
│ └── auth.php # 认证中间件
|
||||
├── includes/ # 共享代码
|
||||
│ ├── db.php # 数据库连接和初始化
|
||||
│ └── functions.php # 通用函数
|
||||
├── data/ # 数据存储目录
|
||||
│ ├── apps/ # 上传的 APK 文件
|
||||
│ ├── uploads/ # 上传的 Logo
|
||||
│ └── database.db # SQLite 数据库
|
||||
└── assets/ # 静态资源
|
||||
└── admin.css # 管理后台样式
|
||||
```
|
||||
|
||||
## 实现步骤
|
||||
|
||||
### 1. 创建基础设施(includes/)
|
||||
|
||||
#### 1.1 数据库连接(includes/db.php)
|
||||
- 创建 SQLite 连接
|
||||
- 自动初始化数据库结构(如果不存在)
|
||||
- 插入默认配置数据
|
||||
|
||||
#### 1.2 通用函数(includes/functions.php)
|
||||
- `getConfig($key)` - 获取配置
|
||||
- `updateConfig($key, $value)` - 更新配置
|
||||
- `formatFileSize($bytes)` - 格式化文件大小
|
||||
- `isLoggedIn()` - 检查登录状态
|
||||
|
||||
### 2. 创建目录结构
|
||||
- 创建 `data/`, `data/apps/`, `data/uploads/`, `admin/`, `includes/`, `assets/`
|
||||
- 设置适当的权限(777 for data/ 及子目录)
|
||||
|
||||
### 3. 实现管理后端(admin/)
|
||||
|
||||
#### 3.1 登录页面(admin/index.php)
|
||||
- 简洁的登录表单
|
||||
- 默认用户名/密码:admin/admin
|
||||
- 使用 Session 管理登录状态
|
||||
- 成功后跳转到 dashboard.php
|
||||
|
||||
#### 3.2 认证中间件(admin/auth.php)
|
||||
- 检查 Session 中的登录状态
|
||||
- 未登录则重定向到登录页面
|
||||
- 被其他后台页面 include
|
||||
|
||||
#### 3.3 管理面板(admin/dashboard.php)
|
||||
- 显示所有已上传的应用版本(表格形式)
|
||||
- 上传新应用的表单(文件 + 版本号 + 描述)
|
||||
- 删除应用的功能
|
||||
- 链接到配置页面
|
||||
|
||||
#### 3.4 文件上传处理(admin/upload.php)
|
||||
- 接收 POST 请求
|
||||
- 验证文件类型(仅允许 .apk)
|
||||
- 生成唯一文件名(时间戳_原文件名)
|
||||
- 移动文件到 data/apps/
|
||||
- 插入数据库记录
|
||||
- 返回 JSON 响应或重定向
|
||||
|
||||
#### 3.5 配置管理(admin/config.php)
|
||||
- 表单显示当前配置
|
||||
- 网站标题输入框
|
||||
- 软件说明文本框
|
||||
- Logo 上传(支持 jpg/png/gif)
|
||||
- 保存到数据库
|
||||
- Logo 上传到 data/uploads/ 并更新配置
|
||||
|
||||
#### 3.6 删除接口(admin/delete.php)
|
||||
- 接收 app ID
|
||||
- 从数据库删除记录
|
||||
- 删除物理文件
|
||||
- 重定向回 dashboard
|
||||
|
||||
#### 3.7 登出(admin/logout.php)
|
||||
- 销毁 Session
|
||||
- 重定向到登录页面
|
||||
|
||||
### 4. 更新前端
|
||||
|
||||
#### 4.1 主页(index.php)
|
||||
**功能改进:**
|
||||
- 从数据库读取配置(标题、说明、Logo)
|
||||
- 显示所有已发布的应用版本列表(不只是最新的一个)
|
||||
- 每个版本显示:版本号、描述、上传时间、文件大小
|
||||
- 最新版本在最前面(ORDER BY upload_time DESC)
|
||||
|
||||
**样式重写:**
|
||||
- 现代化的卡片布局
|
||||
- 响应式设计(移动端友好)
|
||||
- 更好的视觉层次
|
||||
- 使用渐变背景和阴影效果
|
||||
- 每个版本一个下载按钮
|
||||
|
||||
#### 4.2 下载接口(download.php)
|
||||
**功能改进:**
|
||||
- 接收 `id` 参数(GET 请求)
|
||||
- 从数据库查询应用信息
|
||||
- 返回文件路径用于下载
|
||||
- 保留向后兼容(如果没有 id,返回最新版本)
|
||||
|
||||
### 5. 管理后台样式(assets/admin.css)
|
||||
- 简洁的管理界面样式
|
||||
- 表格样式
|
||||
- 表单样式
|
||||
- 按钮和交互元素
|
||||
- 侧边栏导航(可选)
|
||||
|
||||
## 安全考虑
|
||||
|
||||
1. **文件上传安全:**
|
||||
- 验证文件扩展名
|
||||
- 限制文件大小
|
||||
- 使用随机文件名防止覆盖
|
||||
|
||||
2. **认证安全:**
|
||||
- 使用 Session 管理
|
||||
- 密码硬编码(后续可改为数据库存储的加密密码)
|
||||
|
||||
3. **SQL 注入防护:**
|
||||
- 使用 PDO 预处理语句
|
||||
|
||||
4. **XSS 防护:**
|
||||
- 输出时使用 htmlspecialchars()
|
||||
|
||||
## 实现顺序
|
||||
|
||||
1. ✅ 创建目录结构
|
||||
2. ✅ 实现 includes/db.php(数据库初始化)
|
||||
3. ✅ 实现 includes/functions.php
|
||||
4. ✅ 实现 admin/index.php(登录页面)
|
||||
5. ✅ 实现 admin/auth.php
|
||||
6. ✅ 实现 admin/dashboard.php
|
||||
7. ✅ 实现 admin/upload.php
|
||||
8. ✅ 实现 admin/config.php
|
||||
9. ✅ 实现 admin/delete.php
|
||||
10. ✅ 实现 admin/logout.php
|
||||
11. ✅ 更新 index.php(前端)
|
||||
12. ✅ 更新 download.php
|
||||
13. ✅ 创建 assets/admin.css
|
||||
14. ✅ 测试所有功能
|
||||
|
||||
## 验证清单
|
||||
|
||||
- [ ] 可以登录管理后台(admin/admin)
|
||||
- [ ] 可以上传 APK 文件
|
||||
- [ ] 上传的文件保存在 data/apps/
|
||||
- [ ] 主页显示所有版本,最新的在前
|
||||
- [ ] 可以下载各个版本
|
||||
- [ ] 可以在后台删除版本
|
||||
- [ ] 可以配置网站标题
|
||||
- [ ] 可以配置软件说明
|
||||
- [ ] 可以上传和更换 Logo
|
||||
- [ ] 前端样式美观且响应式
|
||||
|
||||
## 预计文件清单
|
||||
|
||||
**新建文件:**
|
||||
- admin/index.php
|
||||
- admin/dashboard.php
|
||||
- admin/upload.php
|
||||
- admin/config.php
|
||||
- admin/delete.php
|
||||
- admin/logout.php
|
||||
- admin/auth.php
|
||||
- includes/db.php
|
||||
- includes/functions.php
|
||||
- assets/admin.css
|
||||
|
||||
**修改文件:**
|
||||
- index.php
|
||||
- download.php
|
||||
|
||||
**创建目录:**
|
||||
- data/
|
||||
- data/apps/
|
||||
- data/uploads/
|
||||
- admin/
|
||||
- includes/
|
||||
- assets/
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
# 数据目录 - 包含上传的文件和数据库
|
||||
data/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.bak
|
||||
*.backup
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
@@ -0,0 +1,190 @@
|
||||
# 软件发布平台
|
||||
|
||||
一个简单易用的软件发布管理系统,支持多版本管理和在线下载。
|
||||
|
||||
## 功能特性
|
||||
|
||||
✅ **后台管理系统**
|
||||
- 用户登录验证(默认用户名/密码:admin/admin)
|
||||
- 上传 APK 文件到服务器
|
||||
- 版本管理(查看、删除)
|
||||
- 网站配置(标题、说明、Logo)
|
||||
|
||||
✅ **前端展示**
|
||||
- 显示所有已发布的应用版本
|
||||
- 最新版本优先显示
|
||||
- 版本详情(版本号、描述、上传时间、文件大小)
|
||||
- 一键下载功能
|
||||
|
||||
✅ **数据存储**
|
||||
- 使用 SQLite 文件数据库
|
||||
- APK 文件存储在 `data/apps/` 目录
|
||||
- Logo 文件存储在 `data/uploads/` 目录
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
php_app_downloader/
|
||||
├── index.php # 主页(显示所有版本)
|
||||
├── download.php # 下载接口
|
||||
├── logo.png # 默认 Logo
|
||||
├── admin/ # 管理后台
|
||||
│ ├── index.php # 登录页面
|
||||
│ ├── dashboard.php # 管理面板
|
||||
│ ├── upload.php # 文件上传处理
|
||||
│ ├── config.php # 配置管理
|
||||
│ ├── delete.php # 删除应用
|
||||
│ ├── logout.php # 退出登录
|
||||
│ └── auth.php # 认证中间件
|
||||
├── includes/ # 共享代码
|
||||
│ ├── db.php # 数据库连接
|
||||
│ └── functions.php # 通用函数
|
||||
├── data/ # 数据存储(需要写权限)
|
||||
│ ├── apps/ # APK 文件
|
||||
│ ├── uploads/ # 上传的 Logo
|
||||
│ └── database.db # SQLite 数据库
|
||||
└── assets/
|
||||
└── admin.css # 管理后台样式
|
||||
```
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 1. 环境要求
|
||||
- PHP 7.0+
|
||||
- SQLite 支持(PHP 默认包含)
|
||||
- Web 服务器(Apache/Nginx)
|
||||
|
||||
### 2. 安装步骤
|
||||
|
||||
1. 将项目文件上传到 Web 服务器
|
||||
2. 确保 `data/` 目录有写权限(已设置为 777)
|
||||
3. 访问网站首页,数据库会自动初始化
|
||||
|
||||
### 3. 管理后台
|
||||
|
||||
访问 `/admin/` 进入管理后台
|
||||
|
||||
**默认登录信息:**
|
||||
- 用户名:`admin`
|
||||
- 密码:`admin`
|
||||
|
||||
**功能说明:**
|
||||
|
||||
#### 上传应用
|
||||
1. 在管理面板点击"上传新应用"
|
||||
2. 选择 APK 文件
|
||||
3. 填写版本号(可选)
|
||||
4. 填写版本描述(可选)
|
||||
5. 点击"上传应用"
|
||||
|
||||
#### 管理版本
|
||||
- 查看所有已发布的版本
|
||||
- 下载任意版本
|
||||
- 删除不需要的版本
|
||||
|
||||
#### 网站配置
|
||||
1. 点击"网站配置"
|
||||
2. 修改网站标题
|
||||
3. 修改软件说明
|
||||
4. 上传新的 Logo(支持 JPG/PNG/GIF)
|
||||
5. 点击"保存配置"
|
||||
|
||||
### 4. 前端使用
|
||||
|
||||
- 访问网站首页查看所有版本
|
||||
- 点击"立即下载"按钮下载对应版本
|
||||
- 右下角有"管理后台"快捷入口
|
||||
|
||||
## 数据库结构
|
||||
|
||||
### apps 表
|
||||
存储应用版本信息
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | INTEGER | 主键,自增 |
|
||||
| filename | TEXT | 存储的文件名 |
|
||||
| original_name | TEXT | 原始文件名 |
|
||||
| version | TEXT | 版本号 |
|
||||
| description | TEXT | 版本描述 |
|
||||
| upload_time | DATETIME | 上传时间 |
|
||||
| file_size | INTEGER | 文件大小(字节)|
|
||||
|
||||
### config 表
|
||||
存储网站配置
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| key | TEXT | 配置键(主键)|
|
||||
| value | TEXT | 配置值 |
|
||||
|
||||
**预设配置项:**
|
||||
- `site_title` - 网站标题
|
||||
- `site_description` - 软件说明
|
||||
- `logo_path` - Logo 路径
|
||||
|
||||
## API 接口
|
||||
|
||||
### 下载接口
|
||||
`GET download.php?id={app_id}`
|
||||
|
||||
**参数:**
|
||||
- `id` (可选) - 应用 ID,不传则返回最新版本
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"file": "data/apps/1719223456_app.apk",
|
||||
"filename": "app.apk",
|
||||
"version": "1.0.0",
|
||||
"date": "2024-06-24 17:30:56"
|
||||
}
|
||||
```
|
||||
|
||||
## 安全建议
|
||||
|
||||
1. **修改默认密码**
|
||||
- 当前密码硬编码在 `admin/index.php`
|
||||
- 建议修改为复杂密码
|
||||
|
||||
2. **限制文件上传**
|
||||
- 当前仅允许 `.apk` 文件
|
||||
- 可根据需要调整文件类型限制
|
||||
|
||||
3. **数据备份**
|
||||
- 定期备份 `data/` 目录
|
||||
- 包含数据库和所有上传的文件
|
||||
|
||||
4. **访问控制**
|
||||
- 建议通过 Web 服务器配置限制 `/admin/` 访问
|
||||
- 可添加 IP 白名单
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **后端:** PHP + SQLite
|
||||
- **前端:** 原生 HTML/CSS/JavaScript
|
||||
- **样式:** 现代化渐变设计,响应式布局
|
||||
|
||||
## 更新日志
|
||||
|
||||
### v2.0 (2024-06-24)
|
||||
- ✅ 新增管理后台系统
|
||||
- ✅ 新增用户登录功能
|
||||
- ✅ 支持多版本管理
|
||||
- ✅ 支持在线上传 APK
|
||||
- ✅ 支持网站配置管理
|
||||
- ✅ 重写前端样式
|
||||
- ✅ 使用 SQLite 数据库
|
||||
|
||||
### v1.0
|
||||
- 基础下载功能
|
||||
- 显示最新版本
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
||||
|
||||
---
|
||||
|
||||
如有问题或建议,欢迎反馈!
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
// 认证中间件 - 检查登录状态
|
||||
|
||||
session_start();
|
||||
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_once '../includes/db.php';
|
||||
require_once '../includes/functions.php';
|
||||
|
||||
$success = '';
|
||||
$error = '';
|
||||
|
||||
// 处理表单提交
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$site_title = $_POST['site_title'] ?? '';
|
||||
$site_description = $_POST['site_description'] ?? '';
|
||||
|
||||
// 更新基本配置
|
||||
updateConfig('site_title', $site_title);
|
||||
updateConfig('site_description', $site_description);
|
||||
|
||||
// 处理 Logo 上传
|
||||
if (isset($_FILES['logo']) && $_FILES['logo']['error'] === UPLOAD_ERR_OK) {
|
||||
$logo = $_FILES['logo'];
|
||||
$ext = strtolower(pathinfo($logo['name'], PATHINFO_EXTENSION));
|
||||
|
||||
// 验证文件类型
|
||||
if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif'])) {
|
||||
$logo_filename = 'logo_' . time() . '.' . $ext;
|
||||
$logo_path = '../data/uploads/' . $logo_filename;
|
||||
|
||||
if (move_uploaded_file($logo['tmp_name'], $logo_path)) {
|
||||
// 删除旧 logo(如果在 data/uploads/ 目录)
|
||||
$old_logo = getConfig('logo_path');
|
||||
if (strpos($old_logo, 'data/uploads/') === 0) {
|
||||
$old_logo_path = '../' . $old_logo;
|
||||
if (file_exists($old_logo_path)) {
|
||||
unlink($old_logo_path);
|
||||
}
|
||||
}
|
||||
|
||||
updateConfig('logo_path', 'data/uploads/' . $logo_filename);
|
||||
$success = '配置保存成功!';
|
||||
} else {
|
||||
$error = 'Logo 上传失败';
|
||||
}
|
||||
} else {
|
||||
$error = 'Logo 只支持 JPG、PNG、GIF 格式';
|
||||
}
|
||||
} else {
|
||||
$success = '配置保存成功!';
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前配置
|
||||
$site_title = getConfig('site_title', 'OPS 运营管理系统');
|
||||
$site_description = getConfig('site_description', '点击下方按钮下载移动端 APP');
|
||||
$logo_path = getConfig('logo_path', 'logo.png');
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>网站配置</title>
|
||||
<link rel="stylesheet" href="../assets/admin.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="admin-container">
|
||||
<header class="admin-header">
|
||||
<h1>网站配置</h1>
|
||||
<div class="header-actions">
|
||||
<a href="dashboard.php" class="btn btn-secondary">返回管理面板</a>
|
||||
<a href="logout.php" class="btn btn-secondary">退出</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="admin-main">
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success"><?php echo e($success); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-error"><?php echo e($error); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<section class="config-section">
|
||||
<h2>基本设置</h2>
|
||||
<form method="POST" enctype="multipart/form-data" class="config-form">
|
||||
<div class="form-group">
|
||||
<label>网站标题 *</label>
|
||||
<input type="text" name="site_title" value="<?php echo e($site_title); ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>软件说明</label>
|
||||
<textarea name="site_description" rows="4"><?php echo e($site_description); ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>网站 Logo</label>
|
||||
<div class="logo-preview">
|
||||
<img src="../<?php echo e($logo_path); ?>" alt="Logo" style="max-width: 200px; max-height: 200px;">
|
||||
</div>
|
||||
<input type="file" name="logo" accept="image/jpeg,image/png,image/gif">
|
||||
<small class="form-hint">支持 JPG、PNG、GIF 格式</small>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">保存配置</button>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_once '../includes/db.php';
|
||||
require_once '../includes/functions.php';
|
||||
|
||||
// 获取所有应用列表
|
||||
$stmt = $db->query("SELECT * FROM apps ORDER BY upload_time DESC");
|
||||
$apps = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>管理面板</title>
|
||||
<link rel="stylesheet" href="../assets/admin.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="admin-container">
|
||||
<header class="admin-header">
|
||||
<h1>管理面板</h1>
|
||||
<div class="header-actions">
|
||||
<span>欢迎,<?php echo e($_SESSION['username']); ?></span>
|
||||
<a href="config.php" class="btn btn-secondary">网站配置</a>
|
||||
<a href="logout.php" class="btn btn-secondary">退出</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="admin-main">
|
||||
<section class="upload-section">
|
||||
<h2>上传新应用</h2>
|
||||
<form action="upload.php" method="POST" enctype="multipart/form-data" class="upload-form">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>APK 文件 *</label>
|
||||
<input type="file" name="apk_file" accept=".apk" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>版本号</label>
|
||||
<input type="text" name="version" placeholder="例如:1.0.0">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>版本描述</label>
|
||||
<textarea name="description" rows="3" placeholder="描述此版本的更新内容..."></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">上传应用</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="apps-section">
|
||||
<h2>已发布应用 (<?php echo count($apps); ?>)</h2>
|
||||
|
||||
<?php if (empty($apps)): ?>
|
||||
<p class="empty-state">暂无已发布的应用</p>
|
||||
<?php else: ?>
|
||||
<table class="apps-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>文件名</th>
|
||||
<th>版本</th>
|
||||
<th>描述</th>
|
||||
<th>大小</th>
|
||||
<th>上传时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($apps as $app): ?>
|
||||
<tr>
|
||||
<td><?php echo e($app['original_name']); ?></td>
|
||||
<td><?php echo e($app['version'] ?: '-'); ?></td>
|
||||
<td class="description"><?php echo e($app['description'] ?: '-'); ?></td>
|
||||
<td><?php echo formatFileSize($app['file_size']); ?></td>
|
||||
<td><?php echo date('Y-m-d H:i', strtotime($app['upload_time'])); ?></td>
|
||||
<td>
|
||||
<a href="../data/apps/<?php echo e($app['filename']); ?>" class="btn-small btn-info" download>下载</a>
|
||||
<a href="delete.php?id=<?php echo $app['id']; ?>"
|
||||
class="btn-small btn-danger"
|
||||
onclick="return confirm('确定要删除此应用吗?')">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_once '../includes/db.php';
|
||||
|
||||
$id = $_GET['id'] ?? 0;
|
||||
|
||||
if ($id > 0) {
|
||||
// 获取应用信息
|
||||
$stmt = $db->prepare("SELECT * FROM apps WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$app = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($app) {
|
||||
// 删除物理文件
|
||||
$file_path = '../data/apps/' . $app['filename'];
|
||||
if (file_exists($file_path)) {
|
||||
unlink($file_path);
|
||||
}
|
||||
|
||||
// 从数据库删除
|
||||
$stmt = $db->prepare("DELETE FROM apps WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// 如果已登录,跳转到管理面板
|
||||
if (isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true) {
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = '';
|
||||
|
||||
// 处理登录请求
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
// 简单的硬编码验证(默认 admin/admin)
|
||||
if ($username === 'admin' && $password === 'admin') {
|
||||
$_SESSION['admin_logged_in'] = true;
|
||||
$_SESSION['username'] = $username;
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
} else {
|
||||
$error = '用户名或密码错误';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>管理后台登录</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
.login-container {
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
input[type="text"],
|
||||
input[type="password"] {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
input[type="text"]:focus,
|
||||
input[type="password"]:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
button:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.error {
|
||||
background: #fee;
|
||||
color: #c33;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<h1>管理后台登录</h1>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="error"><?php echo htmlspecialchars($error); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST">
|
||||
<div class="form-group">
|
||||
<label>用户名</label>
|
||||
<input type="text" name="username" required autofocus>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>密码</label>
|
||||
<input type="password" name="password" required>
|
||||
</div>
|
||||
|
||||
<button type="submit">登录</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
// 登出
|
||||
|
||||
session_start();
|
||||
session_destroy();
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
require_once '../includes/db.php';
|
||||
|
||||
// 处理文件上传
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (!isset($_FILES['apk_file']) || $_FILES['apk_file']['error'] !== UPLOAD_ERR_OK) {
|
||||
die('文件上传失败');
|
||||
}
|
||||
|
||||
$file = $_FILES['apk_file'];
|
||||
$version = $_POST['version'] ?? '';
|
||||
$description = $_POST['description'] ?? '';
|
||||
|
||||
// 验证文件类型
|
||||
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
if ($ext !== 'apk') {
|
||||
die('只允许上传 APK 文件');
|
||||
}
|
||||
|
||||
// 生成唯一文件名
|
||||
$filename = time() . '_' . $file['name'];
|
||||
$target_path = '../data/apps/' . $filename;
|
||||
|
||||
// 移动文件
|
||||
if (!move_uploaded_file($file['tmp_name'], $target_path)) {
|
||||
die('文件保存失败');
|
||||
}
|
||||
|
||||
// 插入数据库
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO apps (filename, original_name, version, description, file_size)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
");
|
||||
$stmt->execute([
|
||||
$filename,
|
||||
$file['name'],
|
||||
$version,
|
||||
$description,
|
||||
$file['size']
|
||||
]);
|
||||
|
||||
// 重定向回管理面板
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// 如果不是 POST 请求,重定向回管理面板
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
@@ -0,0 +1,280 @@
|
||||
/* 管理后台样式 */
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #f5f7fa;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.admin-container {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 头部 */
|
||||
.admin-header {
|
||||
background: #fff;
|
||||
padding: 20px 40px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.admin-header h1 {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-actions span {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 主内容区 */
|
||||
.admin-main {
|
||||
max-width: 1200px;
|
||||
margin: 40px auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
/* 区块 */
|
||||
section {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
section h2 {
|
||||
font-size: 20px;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 表单 */
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
color: #555;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="file"],
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 10px 14px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
input[type="text"]:focus,
|
||||
textarea:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
display: block;
|
||||
margin-top: 6px;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 按钮 */
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #f0f0f0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #e5e5e5;
|
||||
}
|
||||
|
||||
.btn-small {
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.btn-info {
|
||||
background: #3b82f6;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-info:hover {
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #ef4444;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #dc2626;
|
||||
}
|
||||
|
||||
/* 表格 */
|
||||
.apps-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.apps-table thead {
|
||||
background: #f9fafb;
|
||||
}
|
||||
|
||||
.apps-table th,
|
||||
.apps-table td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.apps-table th {
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.apps-table td {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.apps-table td.description {
|
||||
max-width: 250px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.apps-table tbody tr:hover {
|
||||
background: #f9fafb;
|
||||
}
|
||||
|
||||
.apps-table td:last-child {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.apps-table .btn-small {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
/* 提示信息 */
|
||||
.alert {
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
border: 1px solid #6ee7b7;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background: #fee;
|
||||
color: #c33;
|
||||
border: 1px solid #fca5a5;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 40px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Logo 预览 */
|
||||
.logo-preview {
|
||||
margin: 10px 0;
|
||||
padding: 20px;
|
||||
background: #f9fafb;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo-preview img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 768px) {
|
||||
.admin-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.apps-table {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.apps-table th,
|
||||
.apps-table td {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
require_once 'includes/db.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
try {
|
||||
if ($id) {
|
||||
// 按 ID 下载指定版本
|
||||
$stmt = $db->prepare("SELECT * FROM apps WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$app = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($app && file_exists('data/apps/' . $app['filename'])) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'file' => 'data/apps/' . rawurlencode($app['filename']),
|
||||
'filename' => $app['original_name'],
|
||||
'version' => $app['version'],
|
||||
'date' => date('Y-m-d H:i:s', strtotime($app['upload_time']))
|
||||
]);
|
||||
} else {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'APK 文件不存在'
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
// 兼容旧版:返回最新版本
|
||||
$stmt = $db->query("SELECT * FROM apps ORDER BY upload_time DESC LIMIT 1");
|
||||
$app = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($app && file_exists('data/apps/' . $app['filename'])) {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'file' => 'data/apps/' . rawurlencode($app['filename']),
|
||||
'filename' => $app['original_name'],
|
||||
'version' => $app['version'],
|
||||
'date' => date('Y-m-d H:i:s', strtotime($app['upload_time']))
|
||||
]);
|
||||
} else {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => '未找到 APK 文件'
|
||||
]);
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => '查询失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
// 数据库连接和初始化
|
||||
|
||||
$db_path = __DIR__ . '/../data/database.db';
|
||||
$is_new_db = !file_exists($db_path);
|
||||
|
||||
try {
|
||||
$db = new PDO('sqlite:' . $db_path);
|
||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
// 如果是新数据库,初始化表结构
|
||||
if ($is_new_db) {
|
||||
// 创建 apps 表
|
||||
$db->exec("
|
||||
CREATE TABLE IF NOT EXISTS apps (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
filename TEXT NOT NULL,
|
||||
original_name TEXT NOT NULL,
|
||||
version TEXT,
|
||||
description TEXT,
|
||||
upload_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
file_size INTEGER
|
||||
)
|
||||
");
|
||||
|
||||
// 创建 config 表
|
||||
$db->exec("
|
||||
CREATE TABLE IF NOT EXISTS config (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT
|
||||
)
|
||||
");
|
||||
|
||||
// 插入默认配置
|
||||
$defaults = [
|
||||
['site_title', 'OPS 运营管理系统'],
|
||||
['site_description', '点击下方按钮下载移动端 APP'],
|
||||
['logo_path', 'logo.png']
|
||||
];
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO config (key, value) VALUES (?, ?)");
|
||||
foreach ($defaults as $config) {
|
||||
$stmt->execute($config);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
die("数据库连接失败: " . $e->getMessage());
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
// 通用函数
|
||||
|
||||
/**
|
||||
* 获取配置值
|
||||
*/
|
||||
function getConfig($key, $default = '') {
|
||||
global $db;
|
||||
$stmt = $db->prepare("SELECT value FROM config WHERE key = ?");
|
||||
$stmt->execute([$key]);
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return $result ? $result['value'] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新配置值
|
||||
*/
|
||||
function updateConfig($key, $value) {
|
||||
global $db;
|
||||
$stmt = $db->prepare("INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)");
|
||||
return $stmt->execute([$key, $value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文件大小
|
||||
*/
|
||||
function formatFileSize($bytes) {
|
||||
if ($bytes >= 1073741824) {
|
||||
return number_format($bytes / 1073741824, 2) . ' GB';
|
||||
} elseif ($bytes >= 1048576) {
|
||||
return number_format($bytes / 1048576, 2) . ' MB';
|
||||
} elseif ($bytes >= 1024) {
|
||||
return number_format($bytes / 1024, 2) . ' KB';
|
||||
} else {
|
||||
return $bytes . ' B';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否已登录
|
||||
*/
|
||||
function isLoggedIn() {
|
||||
return isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全输出 HTML
|
||||
*/
|
||||
function e($string) {
|
||||
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
require_once 'includes/db.php';
|
||||
require_once 'includes/functions.php';
|
||||
|
||||
// 获取网站配置
|
||||
$site_title = getConfig('site_title', 'OPS 运营管理系统');
|
||||
$site_description = getConfig('site_description', '点击下方按钮下载移动端 APP');
|
||||
$logo_path = getConfig('logo_path', 'logo.png');
|
||||
|
||||
// 获取所有应用版本
|
||||
$stmt = $db->query("SELECT * FROM apps ORDER BY upload_time DESC");
|
||||
$apps = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo e($site_title); ?></title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.header {
|
||||
background: #fff;
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.logo {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin-bottom: 20px;
|
||||
object-fit: contain;
|
||||
}
|
||||
.title {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.description {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.versions {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
}
|
||||
.version-card {
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.version-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.version-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.version-info h3 {
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.version-number {
|
||||
display: inline-block;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #fff;
|
||||
padding: 4px 12px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.version-meta {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.version-description {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.download-btn {
|
||||
display: inline-block;
|
||||
padding: 12px 32px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
border-radius: 50px;
|
||||
text-decoration: none;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
.download-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
|
||||
}
|
||||
.download-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.empty-state {
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
padding: 60px 40px;
|
||||
text-align: center;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
color: #999;
|
||||
}
|
||||
.admin-link {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
color: #667eea;
|
||||
padding: 10px 20px;
|
||||
border-radius: 20px;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.admin-link:hover {
|
||||
transform: translateY(-2px);
|
||||
background: #fff;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
body {
|
||||
padding: 20px 10px;
|
||||
}
|
||||
.header {
|
||||
padding: 30px 20px;
|
||||
}
|
||||
.title {
|
||||
font-size: 22px;
|
||||
}
|
||||
.version-card {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<img src="<?php echo e($logo_path); ?>" alt="Logo" class="logo">
|
||||
<h1 class="title"><?php echo e($site_title); ?></h1>
|
||||
<p class="description"><?php echo nl2br(e($site_description)); ?></p>
|
||||
</div>
|
||||
|
||||
<div class="versions">
|
||||
<?php if (empty($apps)): ?>
|
||||
<div class="empty-state">
|
||||
<p>暂无可用版本</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($apps as $app): ?>
|
||||
<div class="version-card">
|
||||
<div class="version-header">
|
||||
<div class="version-info">
|
||||
<h3><?php echo e($app['original_name']); ?></h3>
|
||||
<?php if ($app['version']): ?>
|
||||
<span class="version-number">v<?php echo e($app['version']); ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="version-meta">
|
||||
<span>📅 <?php echo date('Y-m-d H:i', strtotime($app['upload_time'])); ?></span>
|
||||
<span>📦 <?php echo formatFileSize($app['file_size']); ?></span>
|
||||
</div>
|
||||
|
||||
<?php if ($app['description']): ?>
|
||||
<div class="version-description">
|
||||
<?php echo nl2br(e($app['description'])); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<a href="download.php?id=<?php echo $app['id']; ?>" class="download-btn">立即下载</a>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="admin/" class="admin-link">管理后台</a>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user