feat: 实现完整的应用商店系统
- 添加前端页面包括首页、应用详情页和版本历史页 - 实现管理员后台功能,包括应用管理、版本管理和登录验证 - 添加API接口用于获取应用列表和详情 - 实现文件上传和下载功能 - 添加Windows控制台和GUI客户端 - 完善数据库结构和初始化脚本 - 添加样式表和图片资源
This commit is contained in:
313
admin/addapp.php
Normal file
313
admin/addapp.php
Normal file
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
require_once '../config.php';
|
||||
|
||||
session_start();
|
||||
// 检查管理员登录状态
|
||||
if (!isset($_SESSION['admin'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$success = '';
|
||||
$error = '';
|
||||
// 处理添加App请求
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_app'])) {
|
||||
$name = $_POST['name'];
|
||||
$description = $_POST['description'];
|
||||
$ageRating = $_POST['age_rating'];
|
||||
$platforms = isset($_POST['platforms']) ? json_encode($_POST['platforms']) : json_encode([]);
|
||||
|
||||
// 处理表单提交
|
||||
// 验证必填字段
|
||||
$required = ['name', 'description', 'age_rating', 'platforms'];
|
||||
$errors = [];
|
||||
foreach ($required as $field) {
|
||||
if (empty($_POST[$field])) {
|
||||
$errors[] = ucfirst($field) . ' 不能为空';
|
||||
}
|
||||
}
|
||||
|
||||
// 年龄分级说明验证
|
||||
if (($_POST['age_rating'] === '12+' || $_POST['age_rating'] === '17+') && empty($_POST['age_rating_description'])) {
|
||||
$errors[] = '年龄分级为12+或以上时,年龄分级说明不能为空';
|
||||
}
|
||||
|
||||
|
||||
// 处理应用图标上传
|
||||
|
||||
// 处理平台数据
|
||||
$platforms = json_encode($_POST['platforms']);
|
||||
// 插入应用数据
|
||||
$stmt = $conn->prepare("INSERT INTO apps (name, description, age_rating, age_rating_description, platforms) VALUES (?, ?, ?, ?, ?)");
|
||||
if (!$stmt) {
|
||||
$error = "Database error: " . $conn->error;
|
||||
}
|
||||
if ($stmt) {
|
||||
$stmt->bind_param("sssss", $name, $description, $ageRating, $_POST['age_rating_description'], $platforms);
|
||||
if ($stmt->execute() === TRUE) {
|
||||
$appId = $stmt->insert_id;
|
||||
|
||||
// 处理上传的预览图片
|
||||
if (!empty($_FILES['images']['name'][0])) {
|
||||
$uploadDir = '../images/';
|
||||
foreach ($_FILES['images']['tmp_name'] as $key => $tmpName) {
|
||||
$fileName = basename($_FILES['images']['name'][$key]);
|
||||
$targetPath = $uploadDir . $fileName;
|
||||
if (move_uploaded_file($tmpName, $targetPath)) {
|
||||
$insertImageSql = "INSERT INTO app_images (app_id, image_path) VALUES (?, ?)";
|
||||
$imgStmt = $conn->prepare($insertImageSql);
|
||||
$imgStmt->bind_param("is", $appId, $targetPath);
|
||||
$imgStmt->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理上传的App文件
|
||||
if (!empty($_FILES['app_file']['name'])) {
|
||||
$uploadDir = '../files/';
|
||||
$fileName = basename($_FILES['app_file']['name']);
|
||||
$targetPath = $uploadDir . $fileName;
|
||||
if (move_uploaded_file($_FILES['app_file']['tmp_name'], $targetPath)) {
|
||||
$version = $_POST['version'];
|
||||
$changelog = $_POST['changelog'];
|
||||
$insertVersionSql = "INSERT INTO app_versions (app_id, version, changelog, file_path) VALUES (?, ?, ?, ?)";
|
||||
$verStmt = $conn->prepare($insertVersionSql);
|
||||
$verStmt->bind_param("isss", $appId, $version, $changelog, $targetPath);
|
||||
$verStmt->execute();
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: index.php?success=App 添加成功');
|
||||
exit;
|
||||
} else {
|
||||
$error = 'App 添加失败: '. $conn->error;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>添加App - <?php echo APP_STORE_NAME; ?></title>
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- 自定义CSS -->
|
||||
<link rel="stylesheet" href="../styles.css">
|
||||
<!-- Fluent Design 模糊效果 -->
|
||||
<style>
|
||||
.blur-bg {
|
||||
backdrop-filter: blur(10px);
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<?php if (isset($error)): ?>
|
||||
<div style='color: red; padding: 10px; background-color: #ffeeee; border-radius: 5px; margin-bottom: 20px;'>
|
||||
<?php echo htmlspecialchars($error); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<!-- 导航栏 -->
|
||||
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="index.php">App列表</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="addapp.php">添加App</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="?logout=true">退出登录</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container mt-4">
|
||||
<?php if (!empty($success)): ?>
|
||||
<div class="alert alert-success"><?php echo $success; ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($error)): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<h2>添加App</h2>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">App名称</label>
|
||||
<input type="text" class="form-control" id="name" name="name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">描述</label>
|
||||
<textarea class="form-control" id="description" name="description" rows="3" required></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="age_rating" class="form-label">年龄分级</label>
|
||||
<select class="form-select" id="age_rating" name="age_rating" required>
|
||||
<option value="3+">3+</option>
|
||||
<option value="7+">7+</option>
|
||||
<option value="12+">12+</option>
|
||||
<option value="17+">17+</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3" id="ageRatingDescriptionGroup" style="display: none;">
|
||||
<label for="age_rating_description" class="form-label">年龄分级说明</label>
|
||||
<textarea class="form-control" id="age_rating_description" name="age_rating_description" rows="3" placeholder="请说明为何需要此年龄分级"></textarea>
|
||||
<div class="form-text">当年龄分级为12+或以上时,此项为必填</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">适用平台</label>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="android" id="android" name="platforms[]">
|
||||
<label class="form-check-label" for="android">Android</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="ios" id="ios" name="platforms[]">
|
||||
<label class="form-check-label" for="ios">iOS</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="windows" id="windows" name="platforms[]">
|
||||
<label class="form-check-label" for="windows">Windows</label>
|
||||
</div>
|
||||
<div id="windows_suboptions" class="ms-4 mt-2" style="display: none;">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="windows_version" id="windows_xp" value="windows_xp">
|
||||
<label class="form-check-label" for="windows_xp">XP以前</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="windows_version" id="windows_win7" value="windows_win7">
|
||||
<label class="form-check-label" for="windows_win7">Win7以后</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="macos" id="macos" name="platforms[]">
|
||||
<label class="form-check-label" for="macos">macOS</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="linux" id="linux" name="platforms[]">
|
||||
<label class="form-check-label" for="linux">Linux</label>
|
||||
</div>
|
||||
<div id="linux_suboptions" class="ms-4 mt-2" style="display: none;">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_ubuntu" value="linux_ubuntu">
|
||||
<label class="form-check-label" for="linux_ubuntu">Ubuntu</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_arch" value="linux_arch">
|
||||
<label class="form-check-label" for="linux_arch">Arch Linux</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_centos" value="linux_centos">
|
||||
<label class="form-check-label" for="linux_centos">CentOS</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="version" class="form-label">版本号</label>
|
||||
<input type="text" class="form-control" id="version" name="version" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="changelog" class="form-label">更新日志</label>
|
||||
<textarea class="form-control" id="changelog" name="changelog" rows="3" required></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="app_file" class="form-label">App文件</label>
|
||||
<input class="form-control" type="file" id="app_file" name="app_file" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="images" class="form-label">预览图片 (可多选)</label>
|
||||
<input class="form-control" type="file" id="images" name="images[]" multiple>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" name="add_app">添加App</button>
|
||||
<a href="index.php" class="btn btn-secondary ms-2">取消</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS Bundle with Popper -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// 年龄分级说明显示控制
|
||||
const ageRatingSelect = document.getElementById('age_rating');
|
||||
const descriptionGroup = document.getElementById('ageRatingDescriptionGroup');
|
||||
const descriptionInput = document.getElementById('age_rating_description');
|
||||
|
||||
function toggleAgeDescription() {
|
||||
const selectedRating = ageRatingSelect.value;
|
||||
if (selectedRating === '12+' || selectedRating === '17+') {
|
||||
descriptionGroup.style.display = 'block';
|
||||
descriptionInput.required = true;
|
||||
} else {
|
||||
descriptionGroup.style.display = 'none';
|
||||
descriptionInput.required = false;
|
||||
}
|
||||
}
|
||||
|
||||
ageRatingSelect.addEventListener('change', toggleAgeDescription);
|
||||
// 初始加载时检查
|
||||
toggleAgeDescription();
|
||||
|
||||
|
||||
// 导航栏滚动效果
|
||||
window.addEventListener('scroll', function() {
|
||||
const navbar = document.querySelector('.navbar');
|
||||
if (window.scrollY > 10) {
|
||||
navbar.classList.add('scrolled');
|
||||
} else {
|
||||
navbar.classList.remove('scrolled');
|
||||
}
|
||||
});
|
||||
|
||||
// 平台子选项显示控制
|
||||
document.getElementById('windows').addEventListener('change', function() {
|
||||
const suboptions = document.getElementById('windows_suboptions');
|
||||
suboptions.style.display = this.checked ? 'block' : 'none';
|
||||
if (!this.checked) {
|
||||
document.querySelectorAll('input[name="windows_version"]').forEach(radio => radio.checked = false);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('linux').addEventListener('change', function() {
|
||||
const suboptions = document.getElementById('linux_suboptions');
|
||||
suboptions.style.display = this.checked ? 'block' : 'none';
|
||||
if (!this.checked) {
|
||||
document.querySelectorAll('input[name="linux_distribution"]').forEach(radio => radio.checked = false);
|
||||
}
|
||||
});
|
||||
|
||||
// 表单提交验证
|
||||
document.querySelector('form').addEventListener('submit', function(e) {
|
||||
// 验证Windows子选项
|
||||
if (document.getElementById('windows').checked && !document.querySelector('input[name="windows_version"]:checked')) {
|
||||
e.preventDefault();
|
||||
alert('请选择Windows版本(XP以前或Win7以后)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证Linux子选项
|
||||
if (document.getElementById('linux').checked && !document.querySelector('input[name="linux_distribution"]:checked')) {
|
||||
e.preventDefault();
|
||||
alert('请选择Linux发行版(Ubuntu、Arch Linux或CentOS)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新平台值包含子选项信息
|
||||
if (document.getElementById('windows').checked) {
|
||||
document.getElementById('windows').value = document.querySelector('input[name="windows_version"]:checked').value;
|
||||
}
|
||||
if (document.getElementById('linux').checked) {
|
||||
document.getElementById('linux').value = document.querySelector('input[name="linux_distribution"]:checked').value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
41
admin/deleteapp.php
Normal file
41
admin/deleteapp.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
require_once '../config.php';
|
||||
|
||||
session_start();
|
||||
// 检查管理员登录状态
|
||||
if (!isset($_SESSION['admin'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// 验证App ID
|
||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||
header('Location: index.php?error=无效的App ID');
|
||||
exit;
|
||||
}
|
||||
$appId = $_GET['id'];
|
||||
|
||||
// 删除App
|
||||
$deleteAppSql = "DELETE FROM apps WHERE id = ?";
|
||||
$stmt = $conn->prepare($deleteAppSql);
|
||||
$stmt->bind_param("i", $appId);
|
||||
|
||||
if ($stmt->execute() === TRUE) {
|
||||
// 删除关联的图片
|
||||
$deleteImagesSql = "DELETE FROM app_images WHERE app_id = ?";
|
||||
$imgStmt = $conn->prepare($deleteImagesSql);
|
||||
$imgStmt->bind_param("i", $appId);
|
||||
$imgStmt->execute();
|
||||
|
||||
// 删除关联的版本
|
||||
$deleteVersionsSql = "DELETE FROM app_versions WHERE app_id = ?";
|
||||
$verStmt = $conn->prepare($deleteVersionsSql);
|
||||
$verStmt->bind_param("i", $appId);
|
||||
$verStmt->execute();
|
||||
|
||||
header('Location: index.php?success=App 删除成功');
|
||||
} else {
|
||||
header('Location: index.php?error=App 删除失败: '. $conn->error);
|
||||
}
|
||||
exit;
|
||||
?>
|
||||
347
admin/editapp.php
Normal file
347
admin/editapp.php
Normal file
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
require_once '../config.php';
|
||||
|
||||
session_start();
|
||||
// 检查管理员登录状态
|
||||
if (!isset($_SESSION['admin'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// 验证App ID
|
||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||
header('Location: index.php?error=无效的App ID');
|
||||
exit;
|
||||
}
|
||||
$appId = $_GET['id'];
|
||||
|
||||
// 获取App信息
|
||||
$app = null;
|
||||
$getAppSql = "SELECT * FROM apps WHERE id = ?";
|
||||
$stmt = $conn->prepare($getAppSql);
|
||||
$stmt->bind_param("i", $appId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result->num_rows === 0) {
|
||||
header('Location: index.php?error=App不存在');
|
||||
exit;
|
||||
}
|
||||
$app = $result->fetch_assoc();
|
||||
$platforms = json_decode($app['platforms'], true);
|
||||
|
||||
$success = '';
|
||||
$error = '';
|
||||
// 处理编辑App请求
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edit_app'])) {
|
||||
$name = $_POST['name'];
|
||||
$description = $_POST['description'];
|
||||
$ageRating = $_POST['age_rating'];
|
||||
$newPlatforms = json_encode($_POST['platforms'] ?? []);
|
||||
|
||||
// 处理表单提交
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// 验证必填字段
|
||||
$required = ['name', 'description', 'age_rating', 'platforms'];
|
||||
$errors = [];
|
||||
foreach ($required as $field) {
|
||||
if (empty($_POST[$field])) {
|
||||
$errors[] = ucfirst($field) . ' 不能为空';
|
||||
}
|
||||
}
|
||||
|
||||
// 年龄分级验证
|
||||
if (($_POST['age_rating'] === '12+' || $_POST['age_rating'] === '17+') && empty($_POST['age_rating_description'])) {
|
||||
$errors[] = '年龄分级为12+或以上时,年龄分级说明不能为空';
|
||||
}
|
||||
|
||||
// 处理应用图标上传(如果有新上传)
|
||||
if (!empty($_FILES['images']['name'][0])) {
|
||||
$uploadDir = '../images/';
|
||||
foreach ($_FILES['images']['tmp_name'] as $key => $tmpName) {
|
||||
$fileName = basename($_FILES['images']['name'][$key]);
|
||||
$targetPath = $uploadDir . $fileName;
|
||||
if (move_uploaded_file($tmpName, $targetPath)) {
|
||||
$insertImageSql = "INSERT INTO app_images (app_id, image_path) VALUES (?, ?)";
|
||||
$imgStmt = $conn->prepare($insertImageSql);
|
||||
$imgStmt->bind_param("is", $appId, $targetPath);
|
||||
$imgStmt->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理新上传的App文件
|
||||
if (!empty($_FILES['app_file']['name'])) {
|
||||
$uploadDir = '../files/';
|
||||
$fileName = basename($_FILES['app_file']['name']);
|
||||
$targetPath = $uploadDir . $fileName;
|
||||
if (move_uploaded_file($_FILES['app_file']['tmp_name'], $targetPath)) {
|
||||
$version = $_POST['version'];
|
||||
$changelog = $_POST['changelog'];
|
||||
$insertVersionSql = "INSERT INTO app_versions (app_id, version, changelog, file_path) VALUES (?, ?, ?, ?)";
|
||||
$verStmt = $conn->prepare($insertVersionSql);
|
||||
$verStmt->bind_param("isss", $appId, $version, $changelog, $targetPath);
|
||||
$verStmt->execute();
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: index.php?success=App 更新成功');
|
||||
exit;
|
||||
} else {
|
||||
$error = 'App 更新失败: '. $conn->error;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>编辑App - <?php echo APP_STORE_NAME; ?></title>
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- 自定义CSS -->
|
||||
<link rel="stylesheet" href="../styles.css">
|
||||
<!-- Fluent Design 模糊效果 -->
|
||||
<style>
|
||||
.blur-bg {
|
||||
backdrop-filter: blur(10px);
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 导航栏 -->
|
||||
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="index.php">App列表</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="addapp.php">添加App</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="editapp.php?id=<?php echo $appId; ?>">编辑App</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="?logout=true">退出登录</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container mt-4">
|
||||
<?php if (!empty($success)): ?>
|
||||
<div class="alert alert-success"><?php echo $success; ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($error)): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<h2>编辑App: <?php echo htmlspecialchars($app['name']); ?></h2>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">App名称</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($app['name']); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">描述</label>
|
||||
<textarea class="form-control" id="description" name="description" rows="3" required><?php echo htmlspecialchars($app['description']); ?></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="age_rating" class="form-label">年龄分级</label>
|
||||
<select class="form-select" id="age_rating" name="age_rating" required>
|
||||
<option value="3+" <?php echo $app['age_rating'] === '3+' ? 'selected' : ''; ?>>3+</option>
|
||||
<option value="7+" <?php echo $app['age_rating'] === '7+' ? 'selected' : ''; ?>>7+</option>
|
||||
<option value="12+" <?php echo $app['age_rating'] === '12+' ? 'selected' : ''; ?>>12+</option>
|
||||
<option value="17+" <?php echo $app['age_rating'] === '17+' ? 'selected' : ''; ?>>17+</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3" id="ageRatingDescriptionGroup" style="display: none;">
|
||||
<label for="age_rating_description" class="form-label">年龄分级说明</label>
|
||||
<textarea class="form-control" id="age_rating_description" name="age_rating_description" rows="3" placeholder="请说明为何需要此年龄分级"><?php echo htmlspecialchars($app['age_rating_description'] ?? ''); ?></textarea>
|
||||
<div class="form-text">当年龄分级为12+或以上时,此项为必填</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">适用平台</label>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="android" id="android" name="platforms[]" <?php echo in_array('android', $platforms) ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="android">Android</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="ios" id="ios" name="platforms[]" <?php echo in_array('ios', $platforms) ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="ios">iOS</label>
|
||||
</div>
|
||||
<?php
|
||||
$windowsChecked = false;
|
||||
$windowsVersion = '';
|
||||
foreach ($platforms as $p) {
|
||||
if (strpos($p, 'windows_') === 0) {
|
||||
$windowsChecked = true;
|
||||
$windowsVersion = $p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="windows" id="windows" name="platforms[]" <?php echo $windowsChecked ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="windows">Windows</label>
|
||||
</div>
|
||||
<div id="windows_suboptions" class="ms-4 mt-2" style="display: <?php echo $windowsChecked ? 'block' : 'none'; ?>">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="windows_version" id="windows_xp" value="windows_xp" <?php echo $windowsVersion === 'windows_xp' ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="windows_xp">XP以前</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="windows_version" id="windows_win7" value="windows_win7" <?php echo $windowsVersion === 'windows_win7' ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="windows_win7">Win7以后</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="macos" id="macos" name="platforms[]" <?php echo in_array('macos', $platforms) ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="macos">macOS</label>
|
||||
</div>
|
||||
<?php
|
||||
$linuxChecked = false;
|
||||
$linuxVersion = '';
|
||||
foreach ($platforms as $p) {
|
||||
if (strpos($p, 'linux_') === 0) {
|
||||
$linuxChecked = true;
|
||||
$linuxVersion = $p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="linux" id="linux" name="platforms[]" <?php echo $linuxChecked ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="linux">Linux</label>
|
||||
</div>
|
||||
<div id="linux_suboptions" class="ms-4 mt-2" style="display: <?php echo $linuxChecked ? 'block' : 'none'; ?>">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_ubuntu" value="linux_ubuntu" <?php echo $linuxVersion === 'linux_ubuntu' ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="linux_ubuntu">Ubuntu</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_arch" value="linux_arch" <?php echo $linuxVersion === 'linux_arch' ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="linux_arch">Arch Linux</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="linux_distribution" id="linux_centos" value="linux_centos" <?php echo $linuxVersion === 'linux_centos' ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="linux_centos">CentOS</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="version" class="form-label">新版本号</label>
|
||||
<input type="text" class="form-control" id="version" name="version" placeholder="如: 1.0.1">
|
||||
<div class="form-text">仅在上传新安装包时填写</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="changelog" class="form-label">更新日志</label>
|
||||
<textarea class="form-control" id="changelog" name="changelog" rows="3" placeholder="描述本次更新内容"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="app_file" class="form-label">新App文件 (可选)</label>
|
||||
<input class="form-control" type="file" id="app_file" name="app_file">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="images" class="form-label">新增预览图片 (可选, 可多选)</label>
|
||||
<input class="form-control" type="file" id="images" name="images[]" multiple>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" name="edit_app">更新App</button>
|
||||
<a href="index.php" class="btn btn-secondary ms-2">取消</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS Bundle with Popper -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// 年龄分级说明显示控制
|
||||
// 年龄分级说明显示控制
|
||||
const ageRatingSelect = document.getElementById('age_rating');
|
||||
const descriptionGroup = document.getElementById('ageRatingDescriptionGroup');
|
||||
const descriptionInput = document.getElementById('age_rating_description');
|
||||
|
||||
function toggleAgeDescription() {
|
||||
const selectedRating = ageRatingSelect.value;
|
||||
if (selectedRating === '12+' || selectedRating === '17+') {
|
||||
descriptionGroup.style.display = 'block';
|
||||
descriptionInput.required = true;
|
||||
} else {
|
||||
descriptionGroup.style.display = 'none';
|
||||
descriptionInput.required = false;
|
||||
}
|
||||
}
|
||||
|
||||
ageRatingSelect.addEventListener('change', toggleAgeDescription);
|
||||
// 初始加载时检查
|
||||
toggleAgeDescription();
|
||||
|
||||
// 导航栏滚动效果
|
||||
window.addEventListener('scroll', function() {
|
||||
const navbar = document.querySelector('.navbar');
|
||||
if (window.scrollY > 10) {
|
||||
navbar.classList.add('scrolled');
|
||||
} else {
|
||||
navbar.classList.remove('scrolled');
|
||||
}
|
||||
});
|
||||
|
||||
// 平台子选项显示控制
|
||||
document.getElementById('windows').addEventListener('change', function() {
|
||||
const suboptions = document.getElementById('windows_suboptions');
|
||||
suboptions.style.display = this.checked ? 'block' : 'none';
|
||||
if (!this.checked) {
|
||||
document.querySelectorAll('input[name="windows_version"]').forEach(radio => radio.checked = false);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('linux').addEventListener('change', function() {
|
||||
const suboptions = document.getElementById('linux_suboptions');
|
||||
suboptions.style.display = this.checked ? 'block' : 'none';
|
||||
if (!this.checked) {
|
||||
document.querySelectorAll('input[name="linux_distribution"]').forEach(radio => radio.checked = false);
|
||||
}
|
||||
});
|
||||
|
||||
// 表单提交验证
|
||||
document.querySelector('form').addEventListener('submit', function(e) {
|
||||
// 验证Windows子选项
|
||||
if (document.getElementById('windows').checked && !document.querySelector('input[name="windows_version"]:checked')) {
|
||||
e.preventDefault();
|
||||
alert('请选择Windows版本(XP以前或Win7以后)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证Linux子选项
|
||||
if (document.getElementById('linux').checked && !document.querySelector('input[name="linux_distribution"]:checked')) {
|
||||
e.preventDefault();
|
||||
alert('请选择Linux发行版(Ubuntu、Arch Linux或CentOS)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新平台值包含子选项信息
|
||||
if (document.getElementById('windows').checked) {
|
||||
document.getElementById('windows').value = document.querySelector('input[name="windows_version"]:checked').value;
|
||||
}
|
||||
if (document.getElementById('linux').checked) {
|
||||
document.getElementById('linux').value = document.querySelector('input[name="linux_distribution"]:checked').value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<?php
|
||||
// 更新应用数据
|
||||
$stmt = $conn->prepare("UPDATE apps SET name=?, description=?, age_rating=?, age_rating_description=?, platforms=?, updated_at=NOW() WHERE id=?");
|
||||
$stmt->bind_param("sssssi", $name, $description, $age_rating, $_POST['age_rating_description'], $platformsJson, $appId);
|
||||
|
||||
// ... existing code ...
|
||||
?>
|
||||
123
admin/index.php
Normal file
123
admin/index.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
require_once '../config.php';
|
||||
|
||||
session_start();
|
||||
// 检查管理员登录状态
|
||||
if (!isset($_SESSION['admin'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// 处理退出登录
|
||||
if (isset($_GET['logout'])) {
|
||||
unset($_SESSION['admin']);
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// 获取App列表
|
||||
$sqlApps = "SELECT * FROM apps ORDER BY created_at DESC";
|
||||
$resultApps = $conn->query($sqlApps);
|
||||
|
||||
if (!$resultApps) {
|
||||
error_log("Database query failed: " . $conn->error);
|
||||
echo '<div class="alert alert-danger">获取App列表失败,请联系管理员。</div>';
|
||||
} else {
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>App管理 - <?php echo APP_STORE_NAME; ?></title>
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- 自定义CSS -->
|
||||
<link rel="stylesheet" href="../styles.css">
|
||||
<!-- Fluent Design 模糊效果 -->
|
||||
<style>
|
||||
.blur-bg {
|
||||
backdrop-filter: blur(10px);
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 导航栏 -->
|
||||
<nav class="navbar navbar-expand-lg navbar-light blur-bg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="../index.php"><?php echo APP_STORE_NAME; ?></a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="index.php">App列表</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="addapp.php">添加App</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="?logout=true">退出登录</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container mt-4">
|
||||
<?php if (isset($_GET['success'])): ?>
|
||||
<div class="alert alert-success"><?php echo $_GET['success']; ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($_GET['error'])): ?>
|
||||
<div class="alert alert-danger"><?php echo $_GET['error']; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<h2>App列表</h2>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>名称</th>
|
||||
<th>年龄分级</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php while ($app = $resultApps->fetch_assoc()): ?>
|
||||
<tr>
|
||||
<td><?php echo $app['id']; ?></td>
|
||||
<td><?php echo htmlspecialchars($app['name']); ?></td>
|
||||
<td><?php echo $app['age_rating']; ?></td>
|
||||
<td><?php echo $app['created_at']; ?></td>
|
||||
<td>
|
||||
<a href="editapp.php?id=<?php echo $app['id']; ?>" class="btn btn-sm btn-outline-primary">编辑</a>
|
||||
<a href="manage_versions.php?app_id=<?php echo $app['id']; ?>" class="btn btn-sm btn-outline-secondary">版本管理</a>
|
||||
<a href="deleteapp.php?id=<?php echo $app['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('确定要删除吗?');">删除</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endwhile; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS Bundle with Popper -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// 导航栏滚动效果
|
||||
window.addEventListener('scroll', function() {
|
||||
const navbar = document.querySelector('.navbar');
|
||||
if (window.scrollY > 10) {
|
||||
navbar.classList.add('scrolled');
|
||||
} else {
|
||||
navbar.classList.remove('scrolled');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
70
admin/login.php
Normal file
70
admin/login.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
require_once '../config.php';
|
||||
|
||||
// 检查管理员登录状态
|
||||
session_start();
|
||||
if (!isset($_SESSION['admin'])) {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['username']) && isset($_POST['password'])) {
|
||||
$username = $_POST['username'];
|
||||
$password = $_POST['password'];
|
||||
|
||||
if ($username === ADMIN_USERNAME && $password === ADMIN_PASSWORD) {
|
||||
$_SESSION['admin'] = true;
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
} else {
|
||||
$error = '用户名或密码错误';
|
||||
}
|
||||
}
|
||||
|
||||
// 显示登录表单
|
||||
echo '<!DOCTYPE html>';
|
||||
echo '<html lang="zh-CN">';
|
||||
echo '<head>';
|
||||
echo ' <meta charset="UTF-8">';
|
||||
echo ' <meta name="viewport" content="width=device-width, initial-scale=1.0">';
|
||||
echo ' <title>管理员登录 - '. APP_STORE_NAME . '</title>';
|
||||
echo ' <!-- Bootstrap CSS -->';
|
||||
echo ' <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">';
|
||||
echo ' <!-- 自定义CSS -->';
|
||||
echo ' <link rel="stylesheet" href="../styles.css">';
|
||||
echo ' <!-- Fluent Design 模糊效果 -->';
|
||||
echo ' <style>';
|
||||
echo ' .blur-bg {';
|
||||
echo ' backdrop-filter: blur(10px);';
|
||||
echo ' background-color: rgba(255, 255, 255, 0.5);';
|
||||
echo ' }';
|
||||
echo ' </style>';
|
||||
echo '</head>';
|
||||
echo '<body>';
|
||||
echo ' <div class="container mt-5">';
|
||||
echo ' <div class="row justify-content-center">';
|
||||
echo ' <div class="col-md-6">';
|
||||
echo ' <div class="card blur-bg">';
|
||||
echo ' <div class="card-header">管理员登录</div>';
|
||||
echo ' <div class="card-body">';
|
||||
if (isset($error)) {
|
||||
echo ' <div class="alert alert-danger">'. $error . '</div>';
|
||||
}
|
||||
echo ' <form method="post">';
|
||||
echo ' <div class="mb-3">';
|
||||
echo ' <label for="username" class="form-label">用户名</label>';
|
||||
echo ' <input type="text" class="form-control" id="username" name="username" required>';
|
||||
echo ' </div>';
|
||||
echo ' <div class="mb-3">';
|
||||
echo ' <label for="password" class="form-label">密码</label>';
|
||||
echo ' <input type="password" class="form-control" id="password" name="password" required>';
|
||||
echo ' </div>';
|
||||
echo ' <button type="submit" class="btn btn-primary">登录</button>';
|
||||
echo ' </form>';
|
||||
echo ' </div>';
|
||||
echo ' </div>';
|
||||
echo ' </div>';
|
||||
echo ' </div>';
|
||||
echo ' </div>';
|
||||
echo ' <!-- Bootstrap JS Bundle with Popper -->';
|
||||
echo ' <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>';
|
||||
echo '</body>';
|
||||
echo '</html>';
|
||||
exit;
|
||||
}
|
||||
362
admin/manage_versions.php
Normal file
362
admin/manage_versions.php
Normal file
@@ -0,0 +1,362 @@
|
||||
<?php
|
||||
require_once '../config.php';
|
||||
|
||||
session_start();
|
||||
// 检查管理员登录状态
|
||||
if (!isset($_SESSION['admin'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// 验证App ID
|
||||
if (!isset($_GET['app_id']) || !is_numeric($_GET['app_id'])) {
|
||||
header('Location: index.php?error=无效的App ID');
|
||||
exit;
|
||||
}
|
||||
$appId = $_GET['app_id'];
|
||||
|
||||
// 获取App信息
|
||||
$app = null;
|
||||
$getAppSql = "SELECT * FROM apps WHERE id = ?";
|
||||
$stmt = $conn->prepare($getAppSql);
|
||||
$stmt->bind_param("i", $appId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result->num_rows === 0) {
|
||||
header('Location: index.php?error=App不存在');
|
||||
exit;
|
||||
}
|
||||
$app = $result->fetch_assoc();
|
||||
|
||||
// 获取所有版本
|
||||
$versions = [];
|
||||
$getVersionsSql = "SELECT * FROM app_versions WHERE app_id = ? ORDER BY created_at DESC";
|
||||
$stmt = $conn->prepare($getVersionsSql);
|
||||
$stmt->bind_param("i", $appId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$versions[] = $row;
|
||||
}
|
||||
|
||||
$success = '';
|
||||
$error = '';
|
||||
|
||||
// 处理添加版本
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_version'])) {
|
||||
$version = $_POST['version'];
|
||||
$changelog = $_POST['changelog'];
|
||||
|
||||
if (empty($version)) {
|
||||
$error = '版本号不能为空';
|
||||
} elseif (empty($_FILES['app_file']['name'])) {
|
||||
$error = '请上传App文件';
|
||||
} else {
|
||||
$uploadDir = '../files/';
|
||||
$fileName = basename($_FILES['app_file']['name']);
|
||||
$targetPath = $uploadDir . $fileName;
|
||||
|
||||
if (move_uploaded_file($_FILES['app_file']['tmp_name'], $targetPath)) {
|
||||
$insertVersionSql = "INSERT INTO app_versions (app_id, version, changelog, file_path, created_at) VALUES (?, ?, ?, ?, NOW())";
|
||||
$stmt = $conn->prepare($insertVersionSql);
|
||||
$stmt->bind_param("isss", $appId, $version, $changelog, $targetPath);
|
||||
|
||||
if ($stmt->execute() === TRUE) {
|
||||
header('Location: manage_versions.php?app_id=' . $appId . '&success=版本添加成功');
|
||||
exit;
|
||||
} else {
|
||||
$error = '版本添加失败: ' . $conn->error;
|
||||
unlink($targetPath); // 删除已上传的文件
|
||||
}
|
||||
} else {
|
||||
$error = '文件上传失败';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理删除版本
|
||||
if (isset($_GET['delete_id']) && is_numeric($_GET['delete_id'])) {
|
||||
$versionId = $_GET['delete_id'];
|
||||
|
||||
// 获取版本信息
|
||||
$getVersionSql = "SELECT file_path FROM app_versions WHERE id = ? AND app_id = ?";
|
||||
$stmt = $conn->prepare($getVersionSql);
|
||||
$stmt->bind_param("ii", $versionId, $appId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows === 1) {
|
||||
$version = $result->fetch_assoc();
|
||||
|
||||
// 删除文件
|
||||
if (file_exists($version['file_path'])) {
|
||||
unlink($version['file_path']);
|
||||
}
|
||||
|
||||
// 删除数据库记录
|
||||
$deleteVersionSql = "DELETE FROM app_versions WHERE id = ? AND app_id = ?";
|
||||
$stmt = $conn->prepare($deleteVersionSql);
|
||||
$stmt->bind_param("ii", $versionId, $appId);
|
||||
|
||||
if ($stmt->execute() === TRUE) {
|
||||
header('Location: manage_versions.php?app_id=' . $appId . '&success=版本删除成功');
|
||||
exit;
|
||||
} else {
|
||||
$error = '版本删除失败: ' . $conn->error;
|
||||
}
|
||||
} else {
|
||||
$error = '版本不存在';
|
||||
}
|
||||
}
|
||||
|
||||
// 处理编辑版本
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edit_version'])) {
|
||||
$versionId = $_POST['version_id'];
|
||||
$version = $_POST['version'];
|
||||
$changelog = $_POST['changelog'];
|
||||
|
||||
if (empty($version)) {
|
||||
$error = '版本号不能为空';
|
||||
} else {
|
||||
// 检查是否上传了新文件
|
||||
$fileUpdate = '';
|
||||
$params = ['ss', $version, $changelog, $versionId, $appId];
|
||||
|
||||
if (!empty($_FILES['new_app_file']['name'])) {
|
||||
$uploadDir = '../files/';
|
||||
$fileName = basename($_FILES['new_app_file']['name']);
|
||||
$targetPath = $uploadDir . $fileName;
|
||||
|
||||
if (move_uploaded_file($_FILES['new_app_file']['tmp_name'], $targetPath)) {
|
||||
// 获取旧文件路径
|
||||
$getOldFileSql = "SELECT file_path FROM app_versions WHERE id = ? AND app_id = ?";
|
||||
$stmt = $conn->prepare($getOldFileSql);
|
||||
$stmt->bind_param("ii", $versionId, $appId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$oldVersion = $result->fetch_assoc();
|
||||
|
||||
// 删除旧文件
|
||||
if (file_exists($oldVersion['file_path'])) {
|
||||
unlink($oldVersion['file_path']);
|
||||
}
|
||||
|
||||
$fileUpdate = ", file_path = ?";
|
||||
$params[0] = 'sss';
|
||||
$params[] = $targetPath;
|
||||
} else {
|
||||
$error = '文件上传失败';
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($error)) {
|
||||
$updateVersionSql = "UPDATE app_versions SET version = ?, changelog = ?" . $fileUpdate . " WHERE id = ? AND app_id = ?";
|
||||
$stmt = $conn->prepare($updateVersionSql);
|
||||
|
||||
// 动态绑定参数
|
||||
$stmt->bind_param(...$params);
|
||||
|
||||
if ($stmt->execute() === TRUE) {
|
||||
header('Location: manage_versions.php?app_id=' . $appId . '&success=版本更新成功');
|
||||
exit;
|
||||
} else {
|
||||
$error = '版本更新失败: ' . $conn->error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取URL参数中的成功/错误消息
|
||||
if (isset($_GET['success'])) {
|
||||
$success = $_GET['success'];
|
||||
} elseif (isset($_GET['error'])) {
|
||||
$error = $_GET['error'];
|
||||
}
|
||||
?>
|
||||
<!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 htmlspecialchars($app['name']); ?></title>
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- 自定义CSS -->
|
||||
<link rel="stylesheet" href="../styles.css">
|
||||
<style>
|
||||
.version-card {
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
.version-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
|
||||
}
|
||||
.action-btn {
|
||||
margin: 0 2px;
|
||||
}
|
||||
.modal-backdrop {
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 导航栏 -->
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="index.php"><?php echo APP_STORE_NAME; ?></a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="index.php">App列表</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="editapp.php?id=<?php echo $appId; ?>">返回编辑App</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="manage_versions.php?app_id=<?php echo $appId; ?>">管理版本</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="?logout=true">退出登录</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h1>管理版本: <?php echo htmlspecialchars($app['name']); ?></h1>
|
||||
<p class="text-muted">管理该应用的所有版本</p>
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addVersionModal">
|
||||
添加新版本
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($success)): ?>
|
||||
<div class="alert alert-success"><?php echo $success; ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($error)): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (empty($versions)): ?>
|
||||
<div class="alert alert-info">
|
||||
暂无版本记录
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="row">
|
||||
<?php foreach ($versions as $version): ?>
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<div class="card version-card h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">版本 <?php echo htmlspecialchars($version['version']); ?></h5>
|
||||
<h6 class="card-subtitle mb-2 text-muted">发布日期: <?php echo date('Y-m-d H:i', strtotime($version['created_at'])); ?></h6>
|
||||
<p class="card-text"><?php echo nl2br(htmlspecialchars($version['changelog'])); ?></p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">文件大小: <?php
|
||||
$filePath = $version['file_path'];
|
||||
if (file_exists($filePath)) {
|
||||
echo filesize($filePath) > 0 ? number_format(filesize($filePath) / 1024 / 1024, 2) . ' MB' : '未知';
|
||||
} else {
|
||||
echo '文件不存在';
|
||||
}
|
||||
?></small>
|
||||
<div> <button type="button" class="btn btn-sm btn-outline-secondary action-btn" data-bs-toggle="modal" data-bs-target="#editVersionModal_<?php echo $version['id']; ?>"> 编辑 </button> <a href="../<?php echo htmlspecialchars($version['file_path']); ?>" class="btn btn-sm btn-primary action-btn" download>下载</a> <a href="?app_id=<?php echo $appId; ?>&delete_id=<?php echo $version['id']; ?>" class="btn btn-sm btn-outline-danger action-btn" onclick="return confirm('确定要删除此版本吗?');"> 删除 </a> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 编辑版本模态框 -->
|
||||
<div class="modal fade" id="editVersionModal_<?php echo $version['id']; ?>" tabindex="-1" aria-labelledby="editVersionModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="editVersionModalLabel">编辑版本 <?php echo htmlspecialchars($version['version']); ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="version_id" value="<?php echo $version['id']; ?>">
|
||||
<div class="mb-3">
|
||||
<label for="version_<?php echo $version['id']; ?>" class="form-label">版本号</label>
|
||||
<input type="text" class="form-control" id="version_<?php echo $version['id']; ?>" name="version" value="<?php echo htmlspecialchars($version['version']); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="changelog_<?php echo $version['id']; ?>" class="form-label">更新日志</label>
|
||||
<textarea class="form-control" id="changelog_<?php echo $version['id']; ?>" name="changelog" rows="3" required><?php echo htmlspecialchars($version['changelog']); ?></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="new_app_file_<?php echo $version['id']; ?>" class="form-label">更新App文件 (可选)</label>
|
||||
<input class="form-control" type="file" id="new_app_file_<?php echo $version['id']; ?>" name="new_app_file">
|
||||
<div class="form-text">当前文件: <?php echo basename($version['file_path']); ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="submit" class="btn btn-primary" name="edit_version">保存更改</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- 添加版本模态框 -->
|
||||
<div class="modal fade" id="addVersionModal" tabindex="-1" aria-labelledby="addVersionModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addVersionModalLabel">添加新版本</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="version" class="form-label">版本号</label>
|
||||
<input type="text" class="form-control" id="version" name="version" placeholder="如: 1.0.0" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="changelog" class="form-label">更新日志</label>
|
||||
<textarea class="form-control" id="changelog" name="changelog" rows="3" placeholder="描述本次更新内容" required></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="app_file" class="form-label">App文件</label>
|
||||
<input class="form-control" type="file" id="app_file" name="app_file" required>
|
||||
<a href="<?php echo htmlspecialchars($version['file_path']); ?>" class="btn btn-sm btn-primary" download>下载</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="submit" class="btn btn-primary" name="add_version">添加版本</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS Bundle with Popper -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// 导航栏滚动效果
|
||||
window.addEventListener('scroll', function() {
|
||||
const navbar = document.querySelector('.navbar');
|
||||
if (window.scrollY > 10) {
|
||||
navbar.classList.add('scrolled');
|
||||
} else {
|
||||
navbar.classList.remove('scrolled');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user