上传文件至 /

This commit is contained in:
2025-11-30 13:06:45 +00:00
parent 9600be072a
commit e7884f2436
3 changed files with 979 additions and 0 deletions

400
upload.php Normal file
View File

@@ -0,0 +1,400 @@
<?php
require_once 'config.php';
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
try {
$stmt = $pdo->query("SELECT * FROM tags ORDER BY name");
$allTags = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch(PDOException $e) {
$allTags = [];
}
$error = '';
$success = '';
$uploadResults = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_FILES['images'])) {
$is_public = isset($_POST['is_public']) ? 1 : 0;
$uploadedFiles = $_FILES['images'];
$fileCount = count($uploadedFiles['name']);
$successCount = 0;
$errorCount = 0;
for ($i = 0; $i < $fileCount; $i++) {
if ($uploadedFiles['error'][$i] === UPLOAD_ERR_NO_FILE) continue;
$title = trim($_POST['titles'][$i] ?? '');
$file_tags = $_POST['tags'][$i] ?? [];
$file = [
'name' => $uploadedFiles['name'][$i],
'type' => $uploadedFiles['type'][$i],
'tmp_name' => $uploadedFiles['tmp_name'][$i],
'error' => $uploadedFiles['error'][$i],
'size' => $uploadedFiles['size'][$i]
];
if (empty($title)) $title = pathinfo($file['name'], PATHINFO_FILENAME);
if ($file['error'] !== UPLOAD_ERR_OK) {
$uploadResults[] = ['success' => false, 'filename' => $file['name'], 'message' => t('upload_failed')];
$errorCount++;
continue;
}
if ($file['size'] > MAX_FILE_SIZE) {
$uploadResults[] = ['success' => false, 'filename' => $file['name'], 'message' => t('max_size') . ': 5MB'];
$errorCount++;
continue;
}
$file_extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($file_extension, ALLOWED_TYPES)) {
$uploadResults[] = ['success' => false, 'filename' => $file['name'], 'message' => t('supported_formats') . ': JPG, PNG, GIF, WebP'];
$errorCount++;
continue;
}
$filename = uniqid() . '_' . time() . '.' . $file_extension;
$upload_path = 'uploads/' . $filename;
if (move_uploaded_file($file['tmp_name'], $upload_path)) {
try {
$stmt = $pdo->prepare("INSERT INTO images (user_id, title, filename, is_public, file_size, mime_type) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$_SESSION['user_id'], $title, $filename, $is_public, $file['size'], $file['type']]);
$image_id = $pdo->lastInsertId();
$tagNames = [];
if (!empty($file_tags)) {
foreach ($file_tags as $tag_id) {
$stmt = $pdo->prepare("INSERT INTO image_tags (image_id, tag_id) VALUES (?, ?)");
$stmt->execute([$image_id, $tag_id]);
}
$tagIds = implode(',', array_map('intval', $file_tags));
$stmt = $pdo->query("SELECT name FROM tags WHERE id IN ($tagIds)");
$tagNames = $stmt->fetchAll(PDO::FETCH_COLUMN);
}
$uploadResults[] = [
'success' => true,
'filename' => $file['name'],
'title' => $title,
'tags' => $tagNames,
'url' => SITE_URL . '/uploads/' . $filename,
'view_url' => SITE_URL . '/view-image.php?id=' . $image_id
];
$successCount++;
} catch(PDOException $e) {
unlink($upload_path);
$uploadResults[] = ['success' => false, 'filename' => $file['name'], 'message' => t('error') . ': ' . $e->getMessage()];
$errorCount++;
}
} else {
$uploadResults[] = ['success' => false, 'filename' => $file['name'], 'message' => t('upload_failed')];
$errorCount++;
}
}
if ($successCount > 0) {
$success = t('upload_success') . " {$successCount} " . t('images') . ($errorCount > 0 ? "{$errorCount} " . t('upload_failed') : "");
}
if ($errorCount > 0 && $successCount === 0) {
$error = t('upload_failed');
}
}
?>
<!DOCTYPE html>
<html lang="<?php echo $lang; ?>" data-theme="<?php echo $currentUserSettings['dark_mode'] ? 'dark' : 'light'; ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo t('upload_title'); ?> - <?php echo SITE_NAME; ?></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<?php include 'components/navbar.php'; ?>
<div class="container">
<div class="upload-container">
<div class="upload-card card">
<h2><i class="fas fa-cloud-upload-alt"></i> <?php echo t('batch_upload'); ?></h2>
<?php if($error): ?>
<div class="alert alert-error">
<i class="fas fa-exclamation-triangle"></i> <?php echo $error; ?>
</div>
<?php endif; ?>
<?php if($success): ?>
<div class="alert alert-success">
<i class="fas fa-check-circle"></i> <?php echo $success; ?>
</div>
<?php endif; ?>
<form method="POST" action="" enctype="multipart/form-data" id="uploadForm">
<div class="upload-area" id="uploadArea">
<div>
<div class="feature-icon">
<i class="fas fa-cloud-upload-alt"></i>
</div>
<h3><?php echo t('drag_drop'); ?></h3>
<p><?php echo t('supported_formats'); ?>: JPG, PNG, GIF, WebP | <?php echo t('max_size'); ?>: 5MB</p>
<p class="text-muted"><?php echo t('multiple_files'); ?></p>
</div>
<input type="file" id="fileInput" name="images[]" multiple accept="image/*" style="display: none;">
</div>
<div class="batch-actions mt-2" id="batchActions" style="display: none;">
<button type="button" class="btn btn-secondary" onclick="setAllTitles()">
<i class="fas fa-heading"></i> <?php echo t('set_all_titles'); ?>
</button>
<button type="button" class="btn btn-secondary" onclick="setAllTags()">
<i class="fas fa-tags"></i> <?php echo t('set_all_tags'); ?>
</button>
<button type="button" class="btn btn-danger" onclick="clearAllFiles()">
<i class="fas fa-trash"></i> <?php echo t('clear_all'); ?>
</button>
<span id="fileCount" class="text-muted">0 <?php echo t('files_selected'); ?></span>
</div>
<div class="file-list" id="fileList"></div>
<div class="form-group mt-2">
<label class="checkbox-label">
<input type="checkbox" name="is_public" checked>
<span class="checkmark"></span>
<i class="fas fa-globe"></i> <?php echo t('set_public'); ?>
</label>
</div>
<button type="submit" class="btn btn-primary btn-full mt-2" id="uploadButton" style="display: none;">
<i class="fas fa-upload"></i> <?php echo t('start_upload'); ?>
</button>
</form>
<?php if(!empty($uploadResults)): ?>
<div class="upload-results mt-3">
<h3><i class="fas fa-list"></i> <?php echo t('upload_results'); ?></h3>
<?php foreach($uploadResults as $result): ?>
<div class="result-item <?php echo $result['success'] ? 'alert alert-success' : 'alert alert-error'; ?>">
<?php if($result['success']): ?>
<i class="fas fa-check-circle"></i> <strong><?php echo htmlspecialchars($result['filename']); ?></strong> - <?php echo t('upload_success'); ?>
<br>
<small>
<i class="fas fa-heading"></i> <?php echo t('title'); ?>: <?php echo htmlspecialchars($result['title']); ?> |
<?php if(!empty($result['tags'])): ?>
<i class="fas fa-tags"></i> <?php echo t('tags'); ?>: <?php echo implode(', ', $result['tags']); ?> |
<?php endif; ?>
<a href="<?php echo $result['view_url']; ?>" target="_blank"><i class="fas fa-eye"></i> <?php echo t('view'); ?></a> |
<a href="#" onclick="copyToClipboard('<?php echo $result['url']; ?>'); return false;"><i class="fas fa-copy"></i> <?php echo t('copy'); ?></a>
</small>
<?php else: ?>
<i class="fas fa-times-circle"></i> <strong><?php echo htmlspecialchars($result['filename']); ?></strong> - <?php echo t('upload_failed'); ?>: <?php echo $result['message']; ?>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
<script>
const allTags = <?php echo json_encode($allTags); ?>;
let fileCounter = 0;
const fileList = document.getElementById('fileList');
const fileInput = document.getElementById('fileInput');
const uploadArea = document.getElementById('uploadArea');
const batchActions = document.getElementById('batchActions');
const uploadButton = document.getElementById('uploadButton');
const fileCount = document.getElementById('fileCount');
uploadArea.addEventListener('click', () => fileInput.click());
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
handleFiles(e.dataTransfer.files);
});
fileInput.addEventListener('change', (e) => {
handleFiles(e.target.files);
});
function handleFiles(files) {
for (let file of files) {
if (file.type.startsWith('image/')) {
addFileToList(file);
} else {
alert(`文件 "${file.name}" 不是图片格式,已跳过。`);
}
}
updateUI();
}
function addFileToList(file) {
const fileId = 'file_' + fileCounter++;
const reader = new FileReader();
reader.onload = function(e) {
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
fileItem.id = fileId;
fileItem.innerHTML = `
<div class="file-preview">
<img src="${e.target.result}" alt="预览">
</div>
<div class="file-info">
<div class="file-name">${file.name}</div>
<div class="file-size">${formatFileSize(file.size)}</div>
</div>
<div class="file-metadata">
<input type="text" class="file-title-input" name="titles[]"
placeholder="<?php echo t('title'); ?><?php echo t('optional'); ?>"
value="${file.name.replace(/\.[^/.]+$/, "")}">
<div class="tags-selector">
<select name="tags[]" multiple class="tags-select" style="width: 100%; height: 80px; padding: 5px;">
<option value=""><?php echo t('select_tags'); ?></option>
${allTags.map(tag =>
`<option value="${tag.id}">${tag.name}</option>`
).join('')}
</select>
<small><?php echo t('hold_ctrl_multiselect'); ?></small>
</div>
</div>
<button type="button" class="remove-file" onclick="removeFile('${fileId}')">
<i class="fas fa-times"></i> <?php echo t('delete'); ?>
</button>
`;
fileList.appendChild(fileItem);
updateUI();
};
reader.readAsDataURL(file);
}
function removeFile(fileId) {
const fileItem = document.getElementById(fileId);
if (fileItem) {
fileItem.remove();
updateUI();
}
}
function clearAllFiles() {
if (confirm('<?php echo t('confirm_clear_all'); ?>')) {
fileList.innerHTML = '';
fileInput.value = '';
updateUI();
}
}
function setAllTitles() {
const title = prompt('<?php echo t('enter_title_for_all'); ?>:');
if (title !== null) {
const inputs = document.querySelectorAll('.file-title-input');
inputs.forEach(input => {
input.value = title || input.defaultValue;
});
}
}
function setAllTags() {
const selectedTags = prompt('<?php echo t('enter_tag_ids'); ?>:');
if (selectedTags) {
const tagInputs = selectedTags.split(',').map(t => t.trim());
const selects = document.querySelectorAll('.tags-select');
selects.forEach(select => {
Array.from(select.options).forEach(option => {
option.selected = false;
});
tagInputs.forEach(tagInput => {
let option = Array.from(select.options).find(opt => opt.value === tagInput);
if (!option) {
option = Array.from(select.options).find(opt =>
opt.text.toLowerCase() === tagInput.toLowerCase()
);
}
if (option) {
option.selected = true;
}
});
});
}
}
function updateUI() {
const fileItems = fileList.querySelectorAll('.file-item');
const hasFiles = fileItems.length > 0;
batchActions.style.display = hasFiles ? 'flex' : 'none';
uploadButton.style.display = hasFiles ? 'block' : 'none';
fileCount.textContent = `${fileItems.length} <?php echo t('files_selected'); ?>`;
}
function formatFileSize(bytes) {
if (bytes >= 1073741824) return (bytes / 1073741824).toFixed(2) + ' GB';
else if (bytes >= 1048576) return (bytes / 1048576).toFixed(2) + ' MB';
else if (bytes >= 1024) return (bytes / 1024).toFixed(2) + ' KB';
else return bytes + ' bytes';
}
function copyToClipboard(text) {
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(() => {
alert('<?php echo t('copied_to_clipboard'); ?>');
});
} else {
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
alert('<?php echo t('copied_to_clipboard'); ?>');
}
}
document.getElementById('uploadForm').addEventListener('submit', function() {
const uploadButton = document.getElementById('uploadButton');
uploadButton.disabled = true;
uploadButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> <?php echo t('uploading'); ?>...';
});
</script>
</body>
</html>

175
verify.php Normal file
View File

@@ -0,0 +1,175 @@
<?php
require_once 'config.php';
$error = '';
$success = '';
$verification_code = $_GET['code'] ?? '';
if (empty($verification_code)) {
$error = '无效的验证链接';
} else {
try {
$stmt = $pdo->prepare("SELECT id, username, email FROM users WHERE verification_code = ? AND is_verified = 0");
$stmt->execute([$verification_code]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
$error = '验证链接已过期或无效';
} else {
$stmt = $pdo->prepare("UPDATE users SET is_verified = 1, verification_code = NULL WHERE id = ?");
if ($stmt->execute([$user['id']])) {
$success = '邮箱验证成功!您现在可以登录了。';
sendNotification(
$user['id'],
'announcement',
'邮箱验证成功',
"恭喜您,{$user['username']}您的邮箱验证已成功完成。现在您可以享受PicHost的所有功能。",
'dashboard.php'
);
sendEmailNotification(
$user['id'],
'PicHost - 邮箱验证成功',
"
<h2>邮箱验证成功</h2>
<p>亲爱的 {$user['username']}</p>
<p>您的PicHost账户邮箱验证已成功完成</p>
<p>现在您可以:</p>
<ul>
<li>上传和管理图片</li>
<li>使用API接口</li>
<li>接收重要通知</li>
<li>享受完整的功能体验</li>
</ul>
<p>立即登录开始使用:<a href='" . SITE_URL . "/login.php'>" . SITE_URL . "/login.php</a></p>
"
);
} else {
$error = '验证失败,请稍后重试';
}
}
} catch(PDOException $e) {
$error = '系统错误:' . $e->getMessage();
}
}
?>
<!DOCTYPE html>
<html lang="<?php echo $lang; ?>" data-theme="<?php echo $currentUserSettings['dark_mode'] ? 'dark' : 'light'; ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>邮箱验证 - <?php echo SITE_NAME; ?></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<?php include 'components/navbar.php'; ?>
<div class="container">
<div class="auth-container">
<div class="auth-card card">
<h2><i class="fas fa-envelope-circle-check"></i> 邮箱验证</h2>
<?php if($error): ?>
<div class="alert alert-error">
<i class="fas fa-exclamation-triangle"></i> <?php echo $error; ?>
</div>
<div class="text-center mt-2">
<p>遇到问题?</p>
<a href="contact.php" class="btn btn-secondary">
<i class="fas fa-headset"></i> 联系支持
</a>
<a href="index.php" class="btn">
<i class="fas fa-home"></i> 返回首页
</a>
</div>
<?php endif; ?>
<?php if($success): ?>
<div class="alert alert-success">
<i class="fas fa-check-circle"></i> <?php echo $success; ?>
</div>
<div class="text-center mt-3">
<div class="success-animation">
<i class="fas fa-check-circle" style="font-size: 4rem; color: var(--success-color);"></i>
</div>
<p class="mt-2">您的账户现已完全激活</p>
<div class="auth-links">
<a href="login.php" class="btn btn-primary">
<i class="fas fa-sign-in-alt"></i> 立即登录
</a>
<a href="index.php" class="btn btn-secondary">
<i class="fas fa-home"></i> 返回首页
</a>
</div>
</div>
<div class="features-highlight mt-3">
<h4><i class="fas fa-gift"></i> 现在您可以:</h4>
<ul style="text-align: left; margin: 15px 0; padding-left: 20px;">
<li><i class="fas fa-cloud-upload-alt"></i> 上传和管理图片</li>
<li><i class="fas fa-code"></i> 使用API接口</li>
<li><i class="fas fa-bell"></i> 接收重要通知</li>
<li><i class="fas fa-share-alt"></i> 分享图片链接</li>
<li><i class="fas fa-tags"></i> 使用标签分类</li>
</ul>
</div>
<?php endif; ?>
<?php if(empty($error) && empty($success)): ?>
<div class="text-center">
<div class="loading-spinner">
<i class="fas fa-spinner fa-spin" style="font-size: 3rem; color: var(--primary-color);"></i>
</div>
<p class="mt-2">正在验证您的邮箱...</p>
</div>
<script>
setTimeout(() => {
document.querySelector('.loading-spinner').innerHTML = '<i class="fas fa-exclamation-triangle" style="font-size: 3rem; color: var(--warning-color);"></i>';
document.querySelector('.loading-spinner + p').textContent = '验证时间较长,请耐心等待...';
}, 3000);
</script>
<?php endif; ?>
</div>
<?php if(empty($error) && empty($success)): ?>
<div class="card mt-2">
<h4><i class="fas fa-info-circle"></i> 验证说明</h4>
<ul style="text-align: left; margin: 10px 0; padding-left: 20px;">
<li>邮箱验证确保您的账户安全</li>
<li>验证后可以享受完整功能</li>
<li>如果长时间未完成验证,请检查垃圾邮件</li>
<li>如需帮助,请联系客服支持</li>
</ul>
</div>
<?php endif; ?>
</div>
</div>
<style>
.success-animation {
animation: bounce 0.6s ease-in-out;
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {transform: translateY(0);}
40% {transform: translateY(-10px);}
60% {transform: translateY(-5px);}
}
.features-highlight {
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
padding: 20px;
border-radius: 10px;
border-left: 4px solid var(--success-color);
}
[data-theme="dark"] .features-highlight {
background: linear-gradient(135deg, #2d2d2d, #3d3d3d);
}
</style>
</body>
</html>

404
view-image.php Normal file
View File

@@ -0,0 +1,404 @@
<?php
require_once 'config.php';
$image_id = $_GET['id'] ?? 0;
$feedback_success = '';
$feedback_error = '';
if (!$image_id) {
die('图片ID不能为空');
}
try {
$stmt = $pdo->prepare("
SELECT i.*, u.username,
GROUP_CONCAT(t.name) as tag_names,
GROUP_CONCAT(t.color) as tag_colors,
GROUP_CONCAT(t.id) as tag_ids,
(SELECT COUNT(*) FROM image_feedbacks WHERE image_id = i.id AND type = 'like') as like_count,
(SELECT COUNT(*) FROM image_feedbacks WHERE image_id = i.id AND type = 'report') as report_count,
(SELECT COUNT(*) FROM image_feedbacks WHERE image_id = i.id AND user_id = ? AND type = 'like') as user_liked
FROM images i
LEFT JOIN users u ON i.user_id = u.id
LEFT JOIN image_tags it ON i.id = it.image_id
LEFT JOIN tags t ON it.tag_id = t.id
WHERE i.id = ?
GROUP BY i.id
");
$stmt->execute([$_SESSION['user_id'] ?? 0, $image_id]);
$image = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$image) {
die('图片不存在或已被删除');
}
$tags = [];
if ($image['tag_names']) {
$tagNames = explode(',', $image['tag_names']);
$tagColors = explode(',', $image['tag_colors']);
$tagIds = explode(',', $image['tag_ids']);
for ($i = 0; $i < count($tagNames); $i++) {
if (!empty($tagNames[$i])) {
$tags[] = [
'id' => $tagIds[$i],
'name' => $tagNames[$i],
'color' => $tagColors[$i]
];
}
}
}
if (!isset($_SESSION['viewed_images'])) {
$_SESSION['viewed_images'] = [];
}
if (!in_array($image_id, $_SESSION['viewed_images'])) {
$pdo->prepare("UPDATE images SET views = views + 1 WHERE id = ?")->execute([$image_id]);
$_SESSION['viewed_images'][] = $image_id;
}
} catch(PDOException $e) {
die('数据库错误: ' . $e->getMessage());
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SESSION['user_id'])) {
$type = $_POST['type'];
$comment = trim($_POST['comment'] ?? '');
try {
$stmt = $pdo->prepare("SELECT id FROM image_feedbacks WHERE image_id = ? AND user_id = ? AND type = ?");
$stmt->execute([$image_id, $_SESSION['user_id'], $type]);
if ($stmt->rowCount() > 0) {
$feedback_error = '您已经对此图片进行过此操作';
} else {
$stmt = $pdo->prepare("INSERT INTO image_feedbacks (image_id, user_id, type, comment) VALUES (?, ?, ?, ?)");
if ($stmt->execute([$image_id, $_SESSION['user_id'], $type, $comment])) {
$feedback_success = $type === 'like' ? '感谢您的喜欢!' : '举报已提交,我们会尽快处理。';
if ($type === 'like' && $image['user_id'] != $_SESSION['user_id']) {
sendNotification(
$image['user_id'],
'image_feedback',
'您的图片被喜欢了!',
"用户 {$_SESSION['username']} 喜欢了您的图片「{$image['title']}",
"view-image.php?id={$image_id}"
);
}
header("Location: view-image.php?id={$image_id}");
exit;
}
}
} catch(PDOException $e) {
$feedback_error = '操作失败:' . $e->getMessage();
}
}
?>
<!DOCTYPE html>
<html lang="<?php echo $lang; ?>" data-theme="<?php echo $currentUserSettings['dark_mode'] ? 'dark' : 'light'; ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($image['title'] ?: '图片详情'); ?> - <?php echo SITE_NAME; ?></title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="css/style.css">
<style>
.image-preview {
text-align: center;
margin-bottom: 30px;
background: var(--card-bg);
padding: 20px;
border-radius: var(--radius-lg);
box-shadow: var(--shadow);
}
.image-preview img {
max-width: 90%;
max-height: 70vh;
width: auto;
height: auto;
border-radius: var(--radius);
transition: var(--transition);
cursor: zoom-in;
}
.image-preview img.zoomed {
max-width: 100%;
max-height: 90vh;
cursor: zoom-out;
}
.image-info {
background: var(--card-bg);
padding: 30px;
border-radius: var(--radius-lg);
box-shadow: var(--shadow);
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.info-item {
padding: 15px;
background: var(--bg-color);
border-radius: var(--radius);
border-left: 4px solid var(--primary-color);
}
.info-label {
font-weight: bold;
color: #666;
font-size: 0.9em;
margin-bottom: 5px;
}
.copy-buttons {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin: 20px 0;
}
.copy-btn {
flex: 1;
min-width: 120px;
}
.feedback-buttons {
display: flex;
gap: 10px;
margin: 15px 0;
}
.like-btn, .report-btn {
padding: 10px 20px;
border: none;
border-radius: var(--radius);
cursor: pointer;
font-size: 0.9rem;
transition: var(--transition);
display: flex;
align-items: center;
gap: 8px;
}
.like-btn {
background: #e8f5e9;
color: #27ae60;
border: 2px solid #27ae60;
}
.like-btn:hover {
background: #27ae60;
color: white;
}
.like-btn.liked {
background: #27ae60;
color: white;
}
.report-btn {
background: #ffeaea;
color: #e74c3c;
border: 2px solid #e74c3c;
}
.report-btn:hover {
background: #e74c3c;
color: white;
}
[data-theme="dark"] .like-btn {
background: #1e3a2e;
color: #4ade80;
border-color: #4ade80;
}
[data-theme="dark"] .report-btn {
background: #3a1e1e;
color: #f87171;
border-color: #f87171;
}
</style>
</head>
<body>
<?php include 'components/navbar.php'; ?>
<div class="container">
<div class="image-detail">
<div class="image-preview">
<img src="uploads/<?php echo $image['filename']; ?>"
alt="<?php echo htmlspecialchars($image['title']); ?>"
id="previewImage">
</div>
<div class="image-info">
<h1><?php echo htmlspecialchars($image['title'] ?: '未命名图片'); ?></h1>
<?php if($image['description']): ?>
<p class="image-description"><?php echo nl2br(htmlspecialchars($image['description'])); ?></p>
<?php endif; ?>
<?php if(!empty($tags)): ?>
<div class="tags-container">
<?php foreach($tags as $tag): ?>
<span class="tag" style="background: <?php echo $tag['color']; ?>">
<i class="fas fa-tag"></i> <?php echo htmlspecialchars($tag['name']); ?>
</span>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="info-grid">
<div class="info-item">
<div class="info-label"><i class="fas fa-user"></i> 上传者</div>
<div><?php echo htmlspecialchars($image['username']); ?></div>
</div>
<div class="info-item">
<div class="info-label"><i class="fas fa-calendar"></i> 上传时间</div>
<div><?php echo date('Y-m-d H:i', strtotime($image['uploaded_at'])); ?></div>
</div>
<div class="info-item">
<div class="info-label"><i class="fas fa-eye"></i> 浏览量</div>
<div><?php echo $image['views']; ?></div>
</div>
<div class="info-item">
<div class="info-label"><i class="fas fa-heart"></i> 喜欢数</div>
<div><?php echo $image['like_count']; ?></div>
</div>
<div class="info-item">
<div class="info-label"><i class="fas fa-globe"></i> 状态</div>
<div>
<span class="status-badge <?php echo $image['is_public'] ? 'status-public' : 'status-private'; ?>">
<?php echo $image['is_public'] ? '公开' : '私有'; ?>
</span>
</div>
</div>
</div>
<?php if(isset($_SESSION['user_id'])): ?>
<div class="image-feedback">
<h3><i class="fas fa-comment-dots"></i> 图片反馈</h3>
<?php if($feedback_success): ?>
<div class="alert alert-success">
<i class="fas fa-check-circle"></i> <?php echo $feedback_success; ?>
</div>
<?php endif; ?>
<?php if($feedback_error): ?>
<div class="alert alert-error">
<i class="fas fa-exclamation-triangle"></i> <?php echo $feedback_error; ?>
</div>
<?php endif; ?>
<div class="feedback-buttons">
<form method="POST" style="display: inline;">
<input type="hidden" name="type" value="like">
<button type="submit" class="like-btn <?php echo $image['user_liked'] ? 'liked' : ''; ?>">
<?php if($image['user_liked']): ?>
<i class="fas fa-heart"></i> 已喜欢
<?php else: ?>
<i class="far fa-heart"></i> 喜欢
<?php endif; ?>
</button>
</form>
<button type="button" class="report-btn" onclick="showReportForm()">
<i class="fas fa-flag"></i> 举报
</button>
</div>
<div id="reportForm" style="display: none; margin-top: 15px;">
<form method="POST">
<input type="hidden" name="type" value="report">
<div class="form-group">
<label>举报原因(可选)</label>
<textarea name="comment" rows="3" placeholder="请描述举报原因..."></textarea>
</div>
<button type="submit" class="btn btn-danger">
<i class="fas fa-paper-plane"></i> 提交举报
</button>
<button type="button" class="btn btn-secondary" onclick="hideReportForm()">
<i class="fas fa-times"></i> 取消
</button>
</form>
</div>
</div>
<?php endif; ?>
<h3><i class="fas fa-share-alt"></i> 分享链接</h3>
<div class="copy-buttons">
<?php
$image_url = SITE_URL . '/view-image.php?id=' . $image_id;
$direct_url = SITE_URL . '/uploads/' . $image['filename'];
$bbcode = "[img]{$direct_url}[/img]";
$markdown = "![{$image['title']}]({$direct_url})";
$html = "<img src=\"{$direct_url}\" alt=\"{$image['title']}\">";
?>
<button class="btn copy-btn" data-text="<?php echo $image_url; ?>">
<i class="fas fa-link"></i> 页面链接
</button>
<button class="btn copy-btn" data-text="<?php echo $direct_url; ?>">
<i class="fas fa-image"></i> 图片直链
</button>
<button class="btn copy-btn" data-text="<?php echo htmlspecialchars($bbcode); ?>">
<i class="fas fa-code"></i> BBCode
</button>
<button class="btn copy-btn" data-text="<?php echo htmlspecialchars($markdown); ?>">
<i class="fab fa-markdown"></i> Markdown
</button>
<button class="btn copy-btn" data-text="<?php echo htmlspecialchars($html); ?>">
<i class="fab fa-html5"></i> HTML
</button>
</div>
<?php if(isset($_SESSION['user_id']) && $_SESSION['user_id'] == $image['user_id']): ?>
<div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid var(--border-color);">
<a href="delete-image.php?id=<?php echo $image['id']; ?>"
class="btn btn-danger"
onclick="return confirm('确定要删除这张图片吗?此操作不可恢复!')">
<i class="fas fa-trash"></i> 删除图片
</a>
</div>
<?php endif; ?>
</div>
</div>
</div>
<script>
function showReportForm() {
document.getElementById('reportForm').style.display = 'block';
}
function hideReportForm() {
document.getElementById('reportForm').style.display = 'none';
}
document.getElementById('previewImage').addEventListener('click', function() {
this.classList.toggle('zoomed');
});
document.querySelectorAll('.copy-btn').forEach(button => {
button.addEventListener('click', function() {
const text = this.getAttribute('data-text');
copyToClipboard(text);
const originalText = this.innerHTML;
this.innerHTML = '<i class="fas fa-check"></i> 已复制!';
this.style.background = 'var(--success-color)';
setTimeout(() => {
this.innerHTML = originalText;
this.style.background = '';
}, 2000);
});
});
function copyToClipboard(text) {
if (navigator.clipboard) {
navigator.clipboard.writeText(text);
} else {
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
}
}
</script>
</body>
</html>