上传文件至 /
This commit is contained in:
122
sou_api.php
Normal file
122
sou_api.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
// sou_api.php
|
||||
|
||||
// 设置跨域请求头
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
|
||||
// 初始化响应数组
|
||||
$response = [
|
||||
'status' => 'error',
|
||||
'message' => 'Unknown error',
|
||||
'count' => 0,
|
||||
'results' => []
|
||||
];
|
||||
|
||||
try {
|
||||
// 1. 验证请求方法
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
http_response_code(405);
|
||||
$response['message'] = 'Method Not Allowed';
|
||||
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 2. 验证并获取必要的参数
|
||||
if (!isset($_GET['action']) || $_GET['action'] !== 'getSou') {
|
||||
http_response_code(400);
|
||||
$response['message'] = 'Invalid action. Use ?action=getSou';
|
||||
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 检查搜索词是否存在
|
||||
if (!isset($_GET['s'])) {
|
||||
http_response_code(400);
|
||||
$response['message'] = 'Missing search term. Use &s=your_keyword';
|
||||
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
$searchTerm = trim($_GET['s']);
|
||||
|
||||
// 如果搜索词为空,则返回空结果
|
||||
if (empty($searchTerm)) {
|
||||
$response['status'] = 'success';
|
||||
$response['message'] = 'Search term is empty, returning no results.';
|
||||
$response['count'] = 0;
|
||||
$response['results'] = [];
|
||||
http_response_code(200);
|
||||
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 3. 引入音乐数据
|
||||
$musicFile = __DIR__ . '/data/music.php';
|
||||
if (!file_exists($musicFile)) {
|
||||
http_response_code(500);
|
||||
$response['message'] = 'Music data file not found at ' . $musicFile;
|
||||
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 使用require确保每次都能获取最新数据
|
||||
$musicList = require $musicFile;
|
||||
|
||||
// 验证音乐数据格式
|
||||
if (!is_array($musicList)) {
|
||||
http_response_code(500);
|
||||
$response['message'] = 'Invalid music data format in ' . $musicFile;
|
||||
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 4. 定义音乐分类(用于返回更丰富的信息)
|
||||
$categories = [
|
||||
'all' => ['name' => '全部音乐', 'color' => '#b89e81', 'text_color' => '#5d4037'],
|
||||
'cantonese' => ['name' => '粤语歌曲', 'color' => '#c8e6c9', 'text_color' => '#2e7d32'],
|
||||
'mandarin' => ['name' => '国语歌曲', 'color' => '#fff3e0', 'text_color' => '#e65100'],
|
||||
'waiyu' => ['name' => '外语歌曲', 'color' => '#e3f2fd', 'text_color' => '#0d47a1'],
|
||||
'classic' => ['name' => '经典老歌', 'color' => '#efebe9', 'text_color' => '#3e2723'],
|
||||
'other' => ['name' => '其他音乐', 'color' => '#f3e5f5', 'text_color' => '#6a1b9a']
|
||||
];
|
||||
|
||||
// 5. 执行搜索
|
||||
$searchResults = [];
|
||||
$lowerTerm = strtolower($searchTerm);
|
||||
|
||||
foreach ($musicList as $music) {
|
||||
// 检查音乐条目是否有效
|
||||
if (!is_array($music) || !isset($music['title'], $music['artist'])) {
|
||||
continue; // 跳过无效条目
|
||||
}
|
||||
|
||||
if (strpos(strtolower($music['title']), $lowerTerm) !== false ||
|
||||
strpos(strtolower($music['artist']), $lowerTerm) !== false) {
|
||||
// 为结果添加分类信息,如果分类不存在则使用'other'
|
||||
$music['category_info'] = $categories[$music['category']] ?? $categories['other'];
|
||||
$searchResults[] = $music;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 构建成功响应
|
||||
$response = [
|
||||
'status' => 'success',
|
||||
'action' => 'getSou',
|
||||
'search_term' => $searchTerm,
|
||||
'count' => count($searchResults),
|
||||
'results' => $searchResults
|
||||
];
|
||||
|
||||
http_response_code(200);
|
||||
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
|
||||
} catch (Exception $e) {
|
||||
// 捕获所有未预见的异常
|
||||
http_response_code(500);
|
||||
$response['message'] = 'Server error: ' . $e->getMessage();
|
||||
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
53
submit_recommendation.php
Normal file
53
submit_recommendation.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
// 处理推荐提交的后端接口
|
||||
require 'db_connect.php';
|
||||
|
||||
// 仅处理POST请求
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$songName = trim($_POST['songName'] ?? '');
|
||||
$artistName = trim($_POST['artistName'] ?? '');
|
||||
$reason = trim($_POST['reason'] ?? '');
|
||||
|
||||
// 获取用户标识
|
||||
function getUserIdentifier() {
|
||||
if (isset($_COOKIE['user_identifier'])) {
|
||||
return $_COOKIE['user_identifier'];
|
||||
}
|
||||
$identifier = uniqid('user_', true);
|
||||
setcookie('user_identifier', $identifier, time() + 365 * 24 * 3600, '/');
|
||||
return $identifier;
|
||||
}
|
||||
$userIdentifier = getUserIdentifier();
|
||||
|
||||
// 验证必填字段
|
||||
if (!empty($songName) && !empty($artistName)) {
|
||||
try {
|
||||
// 插入数据,包含用户标识
|
||||
$sql = "INSERT INTO recommendations (song_name, artist_name, reason, user_identifier, status)
|
||||
VALUES (:song_name, :artist_name, :reason, :user_id, 0)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->bindParam(':song_name', $songName);
|
||||
$stmt->bindParam(':artist_name', $artistName);
|
||||
$stmt->bindParam(':reason', $reason);
|
||||
$stmt->bindParam(':user_id', $userIdentifier);
|
||||
$stmt->execute();
|
||||
|
||||
// 跳转回主页并显示成功消息
|
||||
header('Location: index.html?status=success');
|
||||
exit;
|
||||
} catch(PDOException $e) {
|
||||
// 数据库错误
|
||||
header('Location: index.html?status=error&msg=' . urlencode($e->getMessage()));
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
// 字段验证失败
|
||||
header('Location: index.html?status=error&msg=歌曲名称和歌手名称为必填项');
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
// 非POST请求直接跳转回主页
|
||||
header('Location: index.html');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
50
update_recommendation_status.php
Normal file
50
update_recommendation_status.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
// 引入数据库连接
|
||||
require_once 'db_connect.php';
|
||||
|
||||
// 初始化变量
|
||||
$id = $_POST['id'] ?? 0;
|
||||
$status = $_POST['status'] ?? 0;
|
||||
// 管理员首页地址,确保与实际管理员首页文件名一致
|
||||
$adminHome = 'admin_recommendations.php';
|
||||
|
||||
// 验证参数
|
||||
if (empty($id) || !in_array($status, [1, 2])) {
|
||||
header("Location: $adminHome?delete_status=error&msg=参数错误");
|
||||
exit;
|
||||
}
|
||||
|
||||
// 处理数据库更新
|
||||
if ($pdo) {
|
||||
try {
|
||||
// 准备更新语句
|
||||
$sql = "UPDATE recommendations
|
||||
SET status = :status, reviewed_at = CURRENT_TIMESTAMP
|
||||
WHERE id = :id";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->bindParam(':status', $status, PDO::PARAM_INT);
|
||||
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
|
||||
// 执行更新
|
||||
$result = $stmt->execute();
|
||||
|
||||
if ($result) {
|
||||
$message = $status == 1 ? "推荐已同意" : "推荐已驳回";
|
||||
header("Location: $adminHome?delete_status=success&msg=$message");
|
||||
} else {
|
||||
header("Location: $adminHome?delete_status=error&msg=更新失败,请重试");
|
||||
}
|
||||
} catch(PDOException $e) {
|
||||
// 记录错误日志
|
||||
$logMsg = date('[Y-m-d H:i:s] ') . "状态更新错误: " . $e->getMessage() . "\n";
|
||||
file_put_contents('db_error.log', $logMsg, FILE_APPEND);
|
||||
|
||||
header("Location: $adminHome?delete_status=error&msg=数据库错误,请稍后再试");
|
||||
}
|
||||
} else {
|
||||
header("Location: $adminHome?delete_status=error&msg=数据库连接失败");
|
||||
}
|
||||
|
||||
exit;
|
||||
?>
|
||||
442
upload.php
Normal file
442
upload.php
Normal file
@@ -0,0 +1,442 @@
|
||||
<?php
|
||||
// 音乐上传功能(已登录用户自动识别)
|
||||
require_once 'db_config.php';
|
||||
|
||||
$message = '';
|
||||
$message_type = '';
|
||||
|
||||
// 启动会话以检查登录状态
|
||||
session_start();
|
||||
|
||||
// 音乐分类映射
|
||||
$categories = [
|
||||
'cantonese' => '粤语歌曲',
|
||||
'mandarin' => '国语歌曲',
|
||||
'waiyu' => '外语歌曲',
|
||||
'classic' => '经典老歌',
|
||||
'other' => '其他音乐'
|
||||
];
|
||||
|
||||
// 检查用户是否登录
|
||||
$isLoggedIn = isset($_SESSION['user_logged_in']) && $_SESSION['user_logged_in'] === true;
|
||||
$uploaderName = $isLoggedIn ? $_SESSION['user_info']['username'] : '匿名用户';
|
||||
|
||||
// 处理上传
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['upload_music'])) {
|
||||
// 验证表单数据
|
||||
$title = trim($_POST['title'] ?? '');
|
||||
$artist = trim($_POST['artist'] ?? '');
|
||||
// 如果用户已登录,强制使用会话中的用户名,忽略表单提交的值
|
||||
$uploader = $isLoggedIn ? $_SESSION['user_info']['username'] : trim($_POST['uploader'] ?? '匿名用户');
|
||||
$category = $_POST['category'] ?? 'other';
|
||||
$description = trim($_POST['description'] ?? '');
|
||||
$bvid = trim($_POST['bvid'] ?? '');
|
||||
$duration = trim($_POST['duration'] ?? '');
|
||||
|
||||
// 验证时长格式
|
||||
if (!empty($duration) && !preg_match('/^\d+:\d{2}$/', $duration)) {
|
||||
$message = "时长格式不正确,请使用 MM:SS 格式(例如 3:45 表示3分45秒)";
|
||||
$message_type = "error";
|
||||
} elseif (empty($title) || empty($artist)) {
|
||||
$message = "歌曲名和歌手名不能为空";
|
||||
$message_type = "error";
|
||||
} elseif (!isset($_FILES['audio_file']) || $_FILES['audio_file']['error'] !== UPLOAD_ERR_OK) {
|
||||
$message = "请选择要上传的音频文件";
|
||||
$message_type = "error";
|
||||
} else {
|
||||
// 验证BV号格式
|
||||
if (!empty($bvid) && !preg_match('/^BV[0-9A-Za-z]+$/', $bvid)) {
|
||||
$message = "BV号格式不正确,应为以BV开头的字符串(如BV1Va4y1n7HN)";
|
||||
$message_type = "error";
|
||||
} else {
|
||||
// 处理文件上传
|
||||
$upload_dir = 'uploads/';
|
||||
if (!is_dir($upload_dir)) {
|
||||
mkdir($upload_dir, 0755, true);
|
||||
}
|
||||
|
||||
$file_ext = pathinfo($_FILES['audio_file']['name'], PATHINFO_EXTENSION);
|
||||
$allowed_ext = ['mp3', 'wav', 'flac', 'm4a'];
|
||||
|
||||
if (!in_array(strtolower($file_ext), $allowed_ext)) {
|
||||
$message = "不支持的文件格式,仅支持mp3, wav, flac, m4a";
|
||||
$message_type = "error";
|
||||
} else {
|
||||
// 检查文件大小(限制30MB)
|
||||
$max_file_size = 30 * 1024 * 1024; // 30MB
|
||||
if ($_FILES['audio_file']['size'] > $max_file_size) {
|
||||
$message = "文件大小超过限制(30MB)";
|
||||
$message_type = "error";
|
||||
} else {
|
||||
// 生成唯一文件名
|
||||
$file_name = uniqid('music_') . '.' . $file_ext;
|
||||
$target_path = $upload_dir . $file_name;
|
||||
|
||||
if (move_uploaded_file($_FILES['audio_file']['tmp_name'], $target_path)) {
|
||||
// 使用用户输入的时长,如果为空则使用默认值
|
||||
$final_duration = !empty($duration) ? $duration : "0:00";
|
||||
|
||||
// 保存到待审核表
|
||||
try {
|
||||
$stmt = $conn->prepare("INSERT INTO pending_music
|
||||
(uploader_name, title, artist, category, description, file_path, duration, upload_time, bvid)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), ?)");
|
||||
$stmt->execute([$uploader, $title, $artist, $category, $description, $target_path, $final_duration, $bvid]);
|
||||
|
||||
$message = "上传成功,已进入待审核队列,感谢您的分享!";
|
||||
$message_type = "success";
|
||||
// 清空表单
|
||||
$_POST = [];
|
||||
} catch (PDOException $e) {
|
||||
$message = "数据库错误: " . $e->getMessage();
|
||||
$message_type = "error";
|
||||
unlink($target_path); // 数据库错误时删除已上传文件
|
||||
}
|
||||
} else {
|
||||
$message = "文件上传失败";
|
||||
$message_type = "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>
|
||||
<link rel="icon" href="./static/icon/icon.png" type="image/png">
|
||||
<link rel="alternate icon" href="./static/icon/icon.ico" type="image/x-icon">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "MyCustomFont";
|
||||
src: url("./static/font/bbc.ttf") format("truetype");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
/* === 全局变量定义 === */
|
||||
:root {
|
||||
--primary-color: #2c3e50;
|
||||
--primary-hover: #34495e;
|
||||
--accent-color: #e74c3c;
|
||||
--bg-color: #f7f9fc;
|
||||
--text-color: #333;
|
||||
--muted-color: #7f8c8d;
|
||||
--card-bg: #ffffff;
|
||||
--card-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||
--border-color: #dcdfe6;
|
||||
--main-font: "MyCustomFont", sans-serif; /* 自定义字体变量 */
|
||||
}
|
||||
body {
|
||||
font-family: var(--main-font);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
h1, h2, h3 { color: var(--primary-color); }
|
||||
h1 { font-size: 1.8rem; text-align: center; margin: 1rem 0; }
|
||||
h2 { font-size: 1.4rem; margin-bottom: 0.5rem; }
|
||||
.desktop .app-container { display: flex; min-height: 100vh; }
|
||||
.desktop .main-wrapper { flex: 1; display: flex; flex-direction: column; margin-left: 280px; }
|
||||
.desktop .main-content { flex: 1; max-width: 1200px; width: 100%; margin: 0 auto; padding: 1rem; box-sizing: border-box; }
|
||||
.mobile .app-container { display: block; min-height: 100vh; }
|
||||
.mobile .main-wrapper { margin-left: 0; }
|
||||
.mobile .main-content { width: 100%; padding: 0.5rem; box-sizing: border-box; }
|
||||
.top-bar {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 1rem 1.5rem; background-color: var(--card-bg);
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1); position: sticky; top: 0; z-index: 100;
|
||||
}
|
||||
.site-title { margin: 0; font-size: 1.4rem; color: var(--primary-color); }
|
||||
.theme-toggle, .user-menu-btn {
|
||||
background: none; border: 1px solid var(--border-color); color: var(--text-color);
|
||||
padding: 0.5rem 0.8rem; border-radius: 20px; cursor: pointer;
|
||||
display: flex; align-items: center; gap: 0.5rem; transition: all 0.3s ease; text-decoration: none;
|
||||
}
|
||||
.theme-toggle:hover, .user-menu-btn:hover { background-color: var(--primary-color); color: white; border-color: var(--primary-color); }
|
||||
.form-group { margin-bottom: 1.2rem; text-align: left; }
|
||||
.form-group label { display: block; margin-bottom: 0.5rem; color: var(--primary-color); font-weight: 500; }
|
||||
.form-group.required label::after { content: " *"; color: var(--accent-color); }
|
||||
.form-control {
|
||||
width: 100%; padding: 0.8rem; border: 1px solid var(--border-color);
|
||||
border-radius: 8px; font-size: 1rem; box-sizing: border-box;
|
||||
background-color: var(--bg-color); color: var(--text-color);
|
||||
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
.form-control:read-only { background-color: #e9ecef; cursor: not-allowed; color: #495057; }
|
||||
.form-control:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 2px rgba(44, 62, 80, 0.1); }
|
||||
.error-input { border-color: var(--accent-color) !important; box-shadow: 0 0 0 2px rgba(231, 76, 60, 0.1) !important; }
|
||||
.submit-btn {
|
||||
background-color: var(--primary-color); color: white; border: none;
|
||||
padding: 0.8rem 2rem; border-radius: 25px; font-size: 1rem; cursor: pointer;
|
||||
transition: background-color 0.3s ease, transform 0.1s ease; width: 100%;
|
||||
box-sizing: border-box; display: flex; align-items: center; justify-content: center; gap: 0.5rem;
|
||||
}
|
||||
.submit-btn:hover { background-color: var(--primary-hover); }
|
||||
.submit-btn:active { transform: translateY(2px); }
|
||||
.file-info { margin-top: 0.5rem; font-size: 0.85rem; color: var(--muted-color); }
|
||||
.field-hint { margin-top: 0.3rem; font-size: 0.8rem; color: var(--muted-color); font-style: italic; }
|
||||
.message { padding: 1rem; border-radius: 8px; margin-bottom: 1rem; text-align: center; font-size: 0.95rem; transition: opacity 0.3s ease, transform 0.3s ease; }
|
||||
.success { background-color: #d4edda; color: #155724; }
|
||||
.error { background-color: #f8d7da; color: #721c24; }
|
||||
.music-item { background-color: var(--card-bg); border-radius: 12px; padding: 1.2rem; box-shadow: var(--card-shadow); margin-bottom: 1.2rem; }
|
||||
.artist-info { color: var(--muted-color); font-size: 0.9rem; margin-bottom: 1rem; }
|
||||
.back-to-top {
|
||||
position: fixed; bottom: 30px; right: 30px; width: 50px; height: 50px;
|
||||
background-color: var(--primary-color); color: white; border-radius: 50%;
|
||||
display: flex; align-items: center; justify-content: center; font-size: 1.5rem;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.2); cursor: pointer;
|
||||
opacity: 0; visibility: hidden; transition: all 0.3s ease; z-index: 999;
|
||||
}
|
||||
.back-to-top.show { opacity: 1; visibility: visible; }
|
||||
.back-to-top:hover { background-color: var(--primary-hover); transform: translateY(-5px); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-container">
|
||||
<div class="main-wrapper">
|
||||
<header class="top-bar">
|
||||
<h1 class="site-title">
|
||||
<img src="./static/icon/icon.png" alt="音乐图标" style="width: 24px; height: 24px; vertical-align: middle; margin-right: 8px;">
|
||||
<a href="./index.php" style="text-decoration: none; color: inherit;">音乐分享平台</a>
|
||||
</h1>
|
||||
<?php if ($isLoggedIn): ?>
|
||||
<a href="user_center.php" class="user-menu-btn"><i class="fas fa-user"></i> <?php echo htmlspecialchars($uploaderName); ?></a>
|
||||
<?php else: ?>
|
||||
<a href="login.php" class="user-menu-btn"><i class="fas fa-sign-in-alt"></i> 登录</a>
|
||||
<?php endif; ?>
|
||||
</header>
|
||||
<main class="main-content">
|
||||
<div class="music-item">
|
||||
<h2><i class="fas fa-cloud-upload-alt"></i> 上传音乐</h2>
|
||||
<p class="artist-info">
|
||||
<?php if ($isLoggedIn): ?>
|
||||
您好,<strong><?php echo htmlspecialchars($uploaderName); ?></strong>。您已登录,上传将自动记录您的昵称。
|
||||
<?php else: ?>
|
||||
您当前未登录,上传后将以“匿名用户”身份显示。<a href="login.php" style="color: var(--primary-color);">登录</a>后可获得更完整的服务。
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="message <?php echo $message_type; ?>"><?php echo htmlspecialchars($message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form id="upload-form" method="post" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label for="uploader">您的昵称</label>
|
||||
<input type="text" id="uploader" name="uploader" class="form-control"
|
||||
value="<?php echo htmlspecialchars($isLoggedIn ? $uploaderName : ($_POST['uploader'] ?? '匿名用户')); ?>"
|
||||
<?php echo $isLoggedIn ? 'readonly' : ''; ?>>
|
||||
<?php if ($isLoggedIn): ?>
|
||||
<div class="field-hint">已为您自动填充昵称,该字段不可修改。</div>
|
||||
<?php else: ?>
|
||||
<div class="field-hint">未登录状态下,默认为“匿名用户”。</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="form-group required">
|
||||
<label for="title">歌曲名称</label>
|
||||
<input type="text" id="title" name="title" class="form-control" value="<?php echo htmlspecialchars($_POST['title'] ?? ''); ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group required">
|
||||
<label for="artist">歌手/乐队</label>
|
||||
<input type="text" id="artist" name="artist" class="form-control" value="<?php echo htmlspecialchars($_POST['artist'] ?? ''); ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group required">
|
||||
<label for="category">音乐分类</label>
|
||||
<select id="category" name="category" class="form-control" required>
|
||||
<?php foreach ($categories as $key => $name): ?>
|
||||
<option value="<?php echo $key; ?>" <?php echo (($_POST['category'] ?? '') == $key) ? 'selected' : ''; ?>>
|
||||
<?php echo $name; ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group required">
|
||||
<label for="duration">音频时长</label>
|
||||
<input type="text" id="duration" name="duration" class="form-control"
|
||||
value="<?php echo htmlspecialchars($_POST['duration'] ?? ''); ?>"
|
||||
placeholder="请输入时长,格式为 MM:SS(例如 3:45 表示3分45秒)" required>
|
||||
<div class="field-hint">请按照实际时长填写,格式为 分钟:秒数(例如 4:30 表示4分30秒)</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="bvid">B站视频BV号 (可选)</label>
|
||||
<input type="text" id="bvid" name="bvid" class="form-control"
|
||||
value="<?php echo htmlspecialchars($_POST['bvid'] ?? ''); ?>"
|
||||
placeholder="例如: BV1Va4y1n7HN">
|
||||
<div class="field-hint">若该音乐来自B站视频,可填写对应的BV号(以BV开头),方便用户查看视频来源</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">歌曲描述 (可选)</label>
|
||||
<textarea id="description" name="description" class="form-control" rows="3"><?php echo htmlspecialchars($_POST['description'] ?? ''); ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group required">
|
||||
<label for="audio_file">音频文件</label>
|
||||
<input type="file" id="audio_file" name="audio_file" class="form-control" accept="audio/*" required>
|
||||
<small class="file-info">支持格式:mp3, wav, flac, m4a,文件大小不超过30MB</small>
|
||||
</div>
|
||||
|
||||
<button type="submit" name="upload_music" class="submit-btn">
|
||||
<i class="fas fa-upload"></i> 上传音乐
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 检测设备类型并添加相应类名
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const isMobile = window.innerWidth < 768;
|
||||
document.body.classList.add(isMobile ? 'mobile' : 'desktop');
|
||||
|
||||
// 表单验证增强
|
||||
const uploadForm = document.getElementById('upload-form');
|
||||
if (uploadForm) {
|
||||
uploadForm.addEventListener('submit', function(e) {
|
||||
let isValid = true;
|
||||
const titleInput = document.getElementById('title');
|
||||
const artistInput = document.getElementById('artist');
|
||||
const durationInput = document.getElementById('duration');
|
||||
const bvidInput = document.getElementById('bvid');
|
||||
|
||||
// 清除之前的错误状态
|
||||
titleInput.classList.remove('error-input');
|
||||
artistInput.classList.remove('error-input');
|
||||
durationInput.classList.remove('error-input');
|
||||
bvidInput.classList.remove('error-input');
|
||||
|
||||
// 验证必填字段
|
||||
if (!titleInput.value.trim()) {
|
||||
titleInput.classList.add('error-input');
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (!artistInput.value.trim()) {
|
||||
artistInput.classList.add('error-input');
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// 验证时长格式
|
||||
if (durationInput.value.trim() && !/^\d+:\d{2}$/.test(durationInput.value.trim())) {
|
||||
durationInput.classList.add('error-input');
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// 验证BV号格式(如果填写了)
|
||||
if (bvidInput.value.trim() && !/^BV[0-9A-Za-z]+$/.test(bvidInput.value.trim())) {
|
||||
bvidInput.classList.add('error-input');
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
e.preventDefault();
|
||||
|
||||
// 清除已有的错误消息
|
||||
const existingErrors = document.querySelectorAll('.message.error');
|
||||
existingErrors.forEach(el => el.remove());
|
||||
|
||||
// 显示错误消息
|
||||
const errorMsg = document.createElement('div');
|
||||
errorMsg.className = 'message error';
|
||||
|
||||
if (durationInput.classList.contains('error-input')) {
|
||||
errorMsg.textContent = '时长格式不正确,请使用 MM:SS 格式(例如 3:45 表示3分45秒)';
|
||||
} else if (bvidInput.classList.contains('error-input')) {
|
||||
errorMsg.textContent = 'BV号格式不正确,应为以BV开头的字符串(如BV1Va4y1n7HN)';
|
||||
} else {
|
||||
errorMsg.textContent = '请填写所有必填字段';
|
||||
}
|
||||
|
||||
errorMsg.style.marginBottom = '1rem';
|
||||
uploadForm.parentNode.insertBefore(errorMsg, uploadForm);
|
||||
|
||||
// 3秒后自动移除错误消息
|
||||
setTimeout(() => {
|
||||
errorMsg.style.opacity = '0';
|
||||
setTimeout(() => errorMsg.remove(), 300);
|
||||
}, 3000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 文件选择预览(大小和名称)
|
||||
const fileInput = document.getElementById('audio_file');
|
||||
if (fileInput) {
|
||||
fileInput.addEventListener('change', function() {
|
||||
if (this.files && this.files[0]) {
|
||||
const fileInfo = document.querySelector('.file-info') || document.createElement('div');
|
||||
fileInfo.className = 'file-info';
|
||||
|
||||
// 格式化文件大小
|
||||
const fileSize = this.files[0].size;
|
||||
let sizeText;
|
||||
if (fileSize < 1024 * 1024) {
|
||||
sizeText = (fileSize / 1024).toFixed(2) + ' KB';
|
||||
} else {
|
||||
sizeText = (fileSize / (1024 * 1024)).toFixed(2) + ' MB';
|
||||
}
|
||||
|
||||
fileInfo.innerHTML = `已选择: ${this.files[0].name} (${sizeText})`;
|
||||
|
||||
if (!document.querySelector('.file-info')) {
|
||||
this.parentNode.appendChild(fileInfo);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 回到顶部按钮功能
|
||||
const backToTopBtn = document.createElement('button');
|
||||
backToTopBtn.className = 'back-to-top';
|
||||
backToTopBtn.innerHTML = '<i class="fas fa-chevron-up"></i>';
|
||||
backToTopBtn.setAttribute('aria-label', '回到顶部');
|
||||
document.body.appendChild(backToTopBtn);
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
if (window.scrollY > 300) {
|
||||
backToTopBtn.classList.add('show');
|
||||
} else {
|
||||
backToTopBtn.classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
backToTopBtn.addEventListener('click', () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
});
|
||||
|
||||
// 显示消息动画
|
||||
const messageEl = document.querySelector('.message');
|
||||
if (messageEl) {
|
||||
setTimeout(() => {
|
||||
messageEl.style.opacity = 1;
|
||||
}, 100);
|
||||
|
||||
// 成功消息5秒后自动消失
|
||||
if (messageEl.classList.contains('success')) {
|
||||
setTimeout(() => {
|
||||
messageEl.style.opacity = 0;
|
||||
setTimeout(() => messageEl.remove(), 300);
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
132
upload_gg.php
Normal file
132
upload_gg.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
// gg.php - 公告管理页面
|
||||
|
||||
// 数据库连接信息
|
||||
$servername = "localhost";
|
||||
$dbUsername = "a1sax1m9i";
|
||||
$dbPassword = "a1sax1m9i";
|
||||
$dbName = "a1sax1m9i";
|
||||
|
||||
// 创建连接
|
||||
$conn = new mysqli($servername, $dbUsername, $dbPassword, $dbName);
|
||||
if ($conn->connect_error) {
|
||||
die("数据库连接失败: " . $conn->connect_error);
|
||||
}
|
||||
$conn->set_charset("utf8mb4");
|
||||
|
||||
$message = "";
|
||||
$messageType = "";
|
||||
|
||||
// 处理表单提交
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['action']) && $_POST['action'] == 'add_announcement') {
|
||||
$nr = trim($_POST["nr"] ?? "");
|
||||
|
||||
if (empty($nr)) {
|
||||
$message = "公告内容不能为空!";
|
||||
$messageType = "error";
|
||||
} else {
|
||||
$stmt = $conn->prepare("INSERT INTO announcements (nr) VALUES (?)");
|
||||
$stmt->bind_param("s", $nr);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$message = "公告发布成功!";
|
||||
$messageType = "success";
|
||||
header("Location: " . $_SERVER['PHP_SELF']);
|
||||
exit;
|
||||
} else {
|
||||
$message = "发布失败: " . $conn->error;
|
||||
$messageType = "error";
|
||||
}
|
||||
$stmt->close();
|
||||
}
|
||||
}
|
||||
|
||||
// 查询所有公告
|
||||
$announcements = [];
|
||||
$ggResult = $conn->query("SELECT id, nr, time FROM announcements ORDER BY id DESC");
|
||||
while($row = $ggResult->fetch_assoc()) {
|
||||
$announcements[] = $row;
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
?>
|
||||
<!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="icon" href="./static/icon/icon.png" type="image/png">
|
||||
<link rel="alternate icon" href="./static/icon/icon.ico" type="image/x-icon">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
body { font-family: 'Arial', sans-serif; background-color: #f5f5f7; color: #333; margin: 0; padding: 20px; }
|
||||
.container { max-width: 800px; margin: 0 auto; background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
|
||||
h1, h2 { color: #2c3e50; }
|
||||
.form-group { margin-bottom: 15px; }
|
||||
label { display: block; margin-bottom: 8px; font-weight: bold; }
|
||||
textarea.form-control { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; min-height: 120px; }
|
||||
.submit-btn { background-color: #2c3e50; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; font-size: 16px; }
|
||||
.submit-btn:hover { background-color: #34495e; }
|
||||
.announcement-list { margin-top: 30px; }
|
||||
.announcement-item { padding: 15px; border-bottom: 1px solid #eee; }
|
||||
.announcement-item:last-child { border-bottom: none; }
|
||||
.announcement-meta { color: #777; font-size: 14px; margin-bottom: 8px; }
|
||||
.announcement-content { line-height: 1.6; }
|
||||
.back-link { display: inline-block; margin-top: 20px; color: #2c3e50; text-decoration: none; }
|
||||
.back-link:hover { text-decoration: underline; }
|
||||
.message { padding: 10px; margin-bottom: 15px; border-radius: 4px; }
|
||||
.success { background-color: #d4edda; color: #155724; }
|
||||
.error { background-color: #f8d7da; color: #721c24; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>公告管理</h1>
|
||||
|
||||
<!-- 发布公告表单 -->
|
||||
<div class="recommend-container">
|
||||
<h2>发布新公告</h2>
|
||||
<?php if (!empty($message)): ?>
|
||||
<div class="message <?php echo $messageType; ?>">
|
||||
<?php echo $message; // 提示信息仍用纯文本 ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
|
||||
<input type="hidden" name="action" value="add_announcement">
|
||||
<div class="form-group">
|
||||
<label for="nr">公告内容 (支持HTML)</label>
|
||||
<textarea id="nr" name="nr" class="form-control" placeholder="请输入公告内容,例如:<b>加粗</b>、<a href='https://shanwogou.cn'>链接</a>..." required></textarea>
|
||||
</div>
|
||||
<button type="submit" class="submit-btn">发布公告</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- 历史公告列表 -->
|
||||
<div class="announcement-list">
|
||||
<h2>历史公告</h2>
|
||||
<?php if (empty($announcements)): ?>
|
||||
<p>暂无历史公告。</p>
|
||||
<?php else: ?>
|
||||
<?php foreach($announcements as $ann): ?>
|
||||
<div class="announcement-item">
|
||||
<div class="announcement-meta">
|
||||
<span>ID: <?php echo $ann['id']; ?></span> |
|
||||
<span>发布时间: <?php echo $ann['time']; ?></span>
|
||||
</div>
|
||||
<div class="announcement-content">
|
||||
<?php
|
||||
// 核心修改:移除 htmlspecialchars,让HTML标签生效
|
||||
// 保留 nl2br 以正确处理换行符
|
||||
echo nl2br($ann['nr']);
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<a href="index.php" class="back-link"><i class="fas fa-arrow-left"></i> 返回首页</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user