146 lines
6.6 KiB
PHP
146 lines
6.6 KiB
PHP
<?php
|
||
require __DIR__ . '/../includes/init.php';
|
||
requireStaff();
|
||
require '_common.php';
|
||
$isAdmin = isAdminStaff();
|
||
|
||
$db = db();
|
||
$msg = '';
|
||
$err = '';
|
||
// 客服账号仅可查看,禁止写操作
|
||
if (!$isAdmin && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
$err = '客服账号仅可查看,如需操作请联系管理员。';
|
||
}
|
||
if (isset($_POST['delete'])) {
|
||
verifyCsrf();
|
||
$id = (int) ($_POST['delete'] ?? 0);
|
||
$db->prepare('DELETE FROM ' . tn('announcements') . ' WHERE id = ?')->execute([$id]);
|
||
$msg = '已删除这条公告。';
|
||
}
|
||
|
||
if (isset($_POST['save'])) {
|
||
verifyCsrf();
|
||
$id = (int) ($_POST['id'] ?? 0);
|
||
$title = trim($_POST['title'] ?? '');
|
||
$content = trim($_POST['content'] ?? '');
|
||
$pinned = isset($_POST['pinned']) ? 1 : 0;
|
||
$status = isset($_POST['status']) ? 1 : 0;
|
||
|
||
if ($title === '') $err = '请填写公告标题。';
|
||
elseif ($content === '') $err = '请填写公告内容。';
|
||
|
||
if ($err === '') {
|
||
if ($id > 0) {
|
||
$db->prepare('UPDATE ' . tn('announcements') . ' SET title=?, content=?, pinned=?, status=? WHERE id=?')
|
||
->execute([$title, $content, $pinned, $status, $id]);
|
||
$msg = '公告已更新!';
|
||
} else {
|
||
$db->prepare('INSERT INTO ' . tn('announcements') . ' (title, content, pinned, status, created_at) VALUES (?,?,?,?,NOW())')
|
||
->execute([$title, $content, $pinned, $status]);
|
||
$msg = '公告已发布!';
|
||
}
|
||
}
|
||
}
|
||
|
||
$edit = null;
|
||
if (isset($_GET['edit'])) {
|
||
$stmt = $db->prepare('SELECT * FROM ' . tn('announcements') . ' WHERE id = ?');
|
||
$stmt->execute([(int) $_GET['edit']]);
|
||
$edit = $stmt->fetch();
|
||
}
|
||
$showForm = ($edit !== null) || isset($_GET['act']) && $_GET['act'] === 'add';
|
||
|
||
$list = $db->query('SELECT * FROM ' . tn('announcements') . ' ORDER BY pinned DESC, created_at DESC')->fetchAll();
|
||
|
||
adminHeader('公告中心', 'announcements');
|
||
?>
|
||
|
||
<h1 class="page-title"><i class="fas fa-bullhorn"></i> 公告中心</h1>
|
||
|
||
<?php if ($msg): ?><p class="banner-ok"><i class="fas fa-check-circle"></i> <?= h($msg) ?></p><?php endif; ?>
|
||
<?php if ($err): ?><p class="form-err"><?= h($err) ?></p><?php endif; ?>
|
||
|
||
<?php if ($showForm): ?>
|
||
<div class="panel">
|
||
<h3><?= $edit ? '编辑公告 #' . (int)$edit['id'] : '发布新公告' ?></h3>
|
||
<form method="post" action="" class="grid-form">
|
||
<?= csrfField() ?>
|
||
<input type="hidden" name="id" value="<?= $edit ? (int)$edit['id'] : 0 ?>">
|
||
<label class="span2">
|
||
<span>公告标题 <b style="color: red;">*</b></span>
|
||
<input type="text" name="title" value="<?= $edit ? h($edit['title']) : '' ?>" required>
|
||
</label>
|
||
<label class="span2">
|
||
<span>公告内容 <b style="color: red;">*</b>(支持 <a href="https://markdown.com.cn/cheat-sheet.html">Markdown 语法</a>)</span>
|
||
<textarea id="annContent" name="content" rows="8" required><?= $edit ? h($edit['content']) : '' ?></textarea>
|
||
</label>
|
||
<div class="span2 ann-preview-wrap">
|
||
<div class="ann-preview-bar">
|
||
<strong>实时预览</strong>
|
||
<button type="button" class="btn btn-ghost btn-sm" id="annPreviewToggle">显示 / 隐藏预览</button>
|
||
</div>
|
||
<div class="ann-detail-body ann-preview" id="annPreview"></div>
|
||
</div>
|
||
<label class="checkbox"><input type="checkbox" name="pinned" <?= $edit && !empty($edit['pinned']) ? 'checked' : '' ?>> 置顶</label>
|
||
<label class="checkbox"><input type="checkbox" name="status" <?= !$edit || !empty($edit['status']) ? 'checked' : '' ?>> 前台显示</label>
|
||
<div class="form-actions span2">
|
||
<button type="submit" name="save" class="btn btn-primary"><i class="fas fa-save"></i> 保存</button>
|
||
<a href="announcements.php" class="btn btn-ghost">取消</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="panel">
|
||
<div class="panel-head">
|
||
<h3>公告列表(共 <?= count($list) ?> 条)</h3>
|
||
<?php if (!$showForm): ?><a href="announcements.php?act=add" class="btn btn-primary"><i class="fas fa-plus"></i> 发布公告</a><?php endif; ?>
|
||
</div>
|
||
<?php if (empty($list)): ?>
|
||
<p class="empty">还没有公告,点上方「发布公告」。</p>
|
||
<?php else: ?>
|
||
<table class="data-table">
|
||
<thead><tr><th>ID</th><th>标题</th><th>置顶</th><th>显示</th><th>时间</th><th>操作</th></tr></thead>
|
||
<tbody>
|
||
<?php foreach ($list as $a): ?>
|
||
<tr>
|
||
<td><?= (int)$a['id'] ?></td>
|
||
<td><?= h($a['title']) ?></td>
|
||
<td><?= !empty($a['pinned']) ? '<span class="badge-on">置顶</span>' : '<span class="badge-off">否</span>' ?></td>
|
||
<td><?= !empty($a['status']) ? '<span class="badge-on">显示</span>' : '<span class="badge-off">隐藏</span>' ?></td>
|
||
<td><?= date('m-d H:i', strtotime($a['created_at'])) ?></td>
|
||
<td class="row-actions">
|
||
<a href="../announcement.php?id=<?= (int)$a['id'] ?>" class="mini-btn" target="_blank" title="前台预览"><i class="fas fa-eye"></i></a>
|
||
<a href="announcements.php?edit=<?= (int)$a['id'] ?>" class="mini-btn"><i class="fas fa-edit"></i></a>
|
||
<form method="post" action="" style="display:inline" onsubmit="return confirm('确定删除该公告?');">
|
||
<?= csrfField() ?>
|
||
<button type="submit" name="delete" value="<?= (int)$a['id'] ?>" class="mini-btn danger"><i class="fas fa-trash"></i></button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php
|
||
$mv = file_exists(__DIR__ . '/../assets/marked.js') ? filemtime(__DIR__ . '/../assets/marked.js') : time();
|
||
?>
|
||
<script src="../assets/marked.js?v=<?= $mv ?>"></script>
|
||
<script>
|
||
(function () {
|
||
var ta = document.getElementById('annContent');
|
||
var pv = document.getElementById('annPreview');
|
||
if (!ta || !pv || !window.marked) return;
|
||
function render() {
|
||
try { pv.innerHTML = marked.parse(ta.value || ''); }
|
||
catch (e) { pv.textContent = ta.value; }
|
||
}
|
||
ta.addEventListener('input', render);
|
||
render();
|
||
var btn = document.getElementById('annPreviewToggle');
|
||
if (btn) btn.addEventListener('click', function () { pv.hidden = !pv.hidden; });
|
||
})();
|
||
</script>
|
||
<?php adminFooter(); ?>
|