Files
leonapp/admin/addapp.php
Leonmmcoset e8e4f6c269 feat: 实现应用标签管理功能
- 新增标签管理页面,支持标签的增删改查
- 在应用添加/编辑页面增加标签选择功能
- 在首页和应用详情页展示标签信息
- 新增标签筛选功能,支持按标签搜索应用
- 新增标签展示页面,展示所有标签及相关应用
2025-07-06 21:27:50 +08:00

336 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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($_POST['tags'])) {
$stmt = $conn->prepare("INSERT INTO app_tags (app_id, tag_id) VALUES (?, ?)");
foreach ($_POST['tags'] as $tagId) {
$stmt->bind_param("ii", $appId, $tagId);
$stmt->execute();
}
$stmt->close();
}
// 处理上传的预览图片
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="tags" class="form-label">标签</label>
<select id="tags" name="tags[]" multiple class="form-control">
<?php
$tagResult = $conn->query("SELECT id, name FROM tags");
while ($tag = $tagResult->fetch_assoc()):
?>
<option value="<?php echo $tag['id']; ?>"><?php echo htmlspecialchars($tag['name']); ?></option>
<?php endwhile; ?>
</select>
<small class="form-text text-muted">按住Ctrl键可选择多个标签</small>
</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>