多软件归属:软件→分类两级,配置全部入库 (#1)
原先分类写死在 config.example.php,后台改不了;也没有「软件」概念。 现在改为数据库驱动的两级结构,后台可配(管理界面待做)。 ## 数据层(lib/db.php) - 新增 software / categories / statuses 三张表 - categories.software_id 外键:每个软件有自己的分类集合,可同名互不干扰 - threads 加 software_id / category_id;老 category 文本列保留不删(可回退) - fb_db_migrate_taxonomy() 幂等迁移:建表 → 空表灌初始数据 → 回填老帖子 ## 迁移实测 - 老 5 个分类(使用问题/Bug 反馈/功能建议/同步问题/其他)原样落进默认软件 - 老帖子按 category slug 回填 category_id,匹配不到的留 NULL ## 状态(5 个) 待处理 / 处理中 / 已解决 / 已关闭 / 不予考虑(wontfix) 每个状态带 user_note「给用户看的话」,后台可改,将进邮件正文。 待处理 = 刚提交:您提交的反馈已收到,我们正在处理中。 ## 前台 - index.php:软件切换条 + 分类 chip(一级软件、二级分类) - new.php:选软件、分类下拉随软件变、服务端校验分类归属 - URL:?sw=<slug>&cat=<slug>;老链接 ?cat=xxx 解析不到时退回全部,200 不 404 ## 界面一致性 按用户要求去掉「单软件时隐藏软件选择器」的逻辑: 所有界面一致,配一个就列一个,配多个就列多个。 ## 兼容 fb_categories() 现在必须传软件 id;fb_statuses() 返回行数组, 另有 fb_status_keys() 取 key=>name 映射。 验证:WSL 内临时实例实测——单软件显示 1 个软件条;两软件各显示 自己的分类集合与帖子,互不串;无 PHP 警告/致命错误。
This commit is contained in:
@@ -4,16 +4,34 @@
|
||||
*/
|
||||
require __DIR__ . '/lib/bootstrap.php';
|
||||
|
||||
$pdo = fb_db();
|
||||
$cats = fb_categories();
|
||||
$statuses = fb_statuses();
|
||||
$pdo = fb_db();
|
||||
|
||||
/* ---- 软件切换:只配一个软件时自动落到它,不暴露软件选择器 ---- */
|
||||
$swList = fb_softwares(true);
|
||||
$swKey = isset($_GET['sw']) ? (string)$_GET['sw'] : '';
|
||||
$sw = null;
|
||||
if ($swKey !== '') {
|
||||
$sw = fb_software($swKey);
|
||||
// 指定的软件不存在或已停用 → 退回默认,不报错(老链接要能打开)
|
||||
if (!$sw || (int)$sw['enabled'] !== 1) { $sw = null; }
|
||||
}
|
||||
if ($sw === null && $swList) { $sw = $swList[0]; }
|
||||
$swId = $sw ? (int)$sw['id'] : 0;
|
||||
|
||||
$cats = fb_categories($swId);
|
||||
$statuses = fb_status_keys();
|
||||
|
||||
$cat = isset($_GET['cat']) ? (string)$_GET['cat'] : '';
|
||||
$st = isset($_GET['status']) ? (string)$_GET['status'] : '';
|
||||
$q = isset($_GET['q']) ? trim((string)$_GET['q']) : '';
|
||||
$sort = isset($_GET['sort']) ? (string)$_GET['sort'] : 'active';
|
||||
|
||||
if ($cat !== '' && !isset($cats[$cat])) { $cat = ''; }
|
||||
// 分类按 slug 在当前软件内解析;解析不到退回「全部」,不许 404
|
||||
$curCat = null;
|
||||
if ($cat !== '') {
|
||||
$curCat = fb_category_by_slug($swId, $cat);
|
||||
if (!$curCat) { $cat = ''; }
|
||||
}
|
||||
if ($st !== '' && !isset($statuses[$st])) { $st = ''; }
|
||||
if ($sort !== 'new') { $sort = 'active'; }
|
||||
|
||||
@@ -21,8 +39,9 @@ $where = [];
|
||||
$args = [];
|
||||
// 审核(先审后显):访客只看已通过审核的;管理员登录审核后台后可看全部
|
||||
if (fb_moderation_on() && !fb_is_admin()) { $where[] = 'visible = 1'; }
|
||||
if ($cat !== '') { $where[] = 'category = ?'; $args[] = $cat; }
|
||||
if ($st !== '') { $where[] = 'status = ?'; $args[] = $st; }
|
||||
if ($swId > 0) { $where[] = 'software_id = ?'; $args[] = $swId; }
|
||||
if ($curCat) { $where[] = 'category_id = ?'; $args[] = (int)$curCat['id']; }
|
||||
if ($st !== '') { $where[] = 'status = ?'; $args[] = $st; }
|
||||
if ($q !== '') {
|
||||
$like = '%' . addcslashes($q, '%_\\') . '%';
|
||||
$where[] = "(title LIKE ? ESCAPE '\\' OR body LIKE ? ESCAPE '\\')";
|
||||
@@ -43,7 +62,7 @@ if ($page < 1) { $page = 1; }
|
||||
if ($page > $pages){ $page = $pages; }
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
|
||||
$sql = 'SELECT id, title, category, status, author_name, reply_count, created_at, updated_at,'
|
||||
$sql = 'SELECT id, title, category, category_id, software_id, status, author_name, reply_count, created_at, updated_at,'
|
||||
. ' substr(body, 1, 300) AS excerpt FROM threads'
|
||||
. $wsql . ' ORDER BY ' . $order . ' LIMIT ' . $pageSize . ' OFFSET ' . $offset;
|
||||
$sth = $pdo->prepare($sql);
|
||||
@@ -56,25 +75,41 @@ foreach ($threads as $t) { $tids[] = (int)$t['id']; }
|
||||
$attImgs = $tids ? fb_attach_map_images($pdo, $tids, 3) : [];
|
||||
$attCount = $tids ? fb_attach_count_map($pdo, $tids) : [];
|
||||
|
||||
fb_layout_head('全部问题');
|
||||
$siteName = (string)fb_cfg('site_name');
|
||||
$headTitle = $siteName !== '' ? $siteName . ' · 问题反馈' : '问题反馈';
|
||||
fb_layout_head($sw ? ((string)$sw['name']) : $headTitle);
|
||||
?>
|
||||
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>全部问题</h1>
|
||||
<p class="muted"><?php echo e((string)fb_cfg('site_desc')); ?></p>
|
||||
<h1><?php echo e($sw ? (string)$sw['name'] : '全部问题'); ?></h1>
|
||||
<p class="muted"><?php
|
||||
$desc = $sw && !empty($sw['note']) ? (string)$sw['note'] : (string)fb_cfg('site_desc');
|
||||
echo e($desc);
|
||||
?></p>
|
||||
</div>
|
||||
<a class="btn primary" href="new.php<?php echo $sw ? '?sw=' . e(urlencode((string)$sw['slug'])) : ''; ?>">我要反馈</a>
|
||||
</div>
|
||||
|
||||
<div class="software-bar">
|
||||
<span class="sw-label">选择软件:</span>
|
||||
<div class="chips">
|
||||
<?php foreach ($swList as $s): ?>
|
||||
<a class="chip <?php echo ($sw && (int)$s['id'] === (int)$sw['id']) ? 'on' : ''; ?>"
|
||||
href="<?php echo e(fb_qs(['sw' => (string)$s['slug'], 'cat' => null, 'page' => null]) ?: ('?sw=' . e(urlencode((string)$s['slug'])))); ?>"><?php echo e((string)$s['name']); ?></a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<a class="btn primary" href="new.php">我要反馈</a>
|
||||
</div>
|
||||
|
||||
<div class="filters">
|
||||
<div class="chips">
|
||||
<a class="chip <?php echo $cat === '' ? 'on' : ''; ?>" href="<?php echo e(fb_qs(['cat' => null, 'page' => null]) ?: '?'); ?>">全部</a>
|
||||
<?php foreach ($cats as $k => $v): ?>
|
||||
<a class="chip <?php echo $cat === $k ? 'on' : ''; ?>" href="<?php echo e(fb_qs(['cat' => $k, 'page' => null])); ?>"><?php echo e($v); ?></a>
|
||||
<?php foreach ($cats as $c): ?>
|
||||
<a class="chip <?php echo $cat === (string)$c['slug'] ? 'on' : ''; ?>" href="<?php echo e(fb_qs(['cat' => (string)$c['slug'], 'page' => null])); ?>"><?php echo e((string)$c['name']); ?></a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<form class="filter-row" method="get" action="">
|
||||
<?php if ($sw): ?><input type="hidden" name="sw" value="<?php echo e((string)$sw['slug']); ?>"><?php endif; ?>
|
||||
<input type="hidden" name="cat" value="<?php echo e($cat); ?>">
|
||||
<select name="status" onchange="this.form.submit()">
|
||||
<option value="">全部状态</option>
|
||||
@@ -95,13 +130,13 @@ fb_layout_head('全部问题');
|
||||
<?php if (!$threads): ?>
|
||||
<div class="empty">
|
||||
<p>还没有符合条件的问题。</p>
|
||||
<p><a class="btn primary" href="new.php">来提第一个吧</a></p>
|
||||
<p><a class="btn primary" href="new.php<?php echo $sw ? '?sw=' . e(urlencode((string)$sw['slug'])) : ''; ?>">来提第一个吧</a></p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($threads as $t): ?>
|
||||
<a class="item" href="thread.php?id=<?php echo (int)$t['id']; ?>">
|
||||
<div class="item-top">
|
||||
<span class="badge cat"><?php echo e(fb_category_label($t['category'])); ?></span>
|
||||
<span class="badge cat"><?php echo e(fb_category_label($t['category_id'])); ?></span>
|
||||
<span class="badge st st-<?php echo e($t['status']); ?>"><?php echo e(fb_status_label($t['status'])); ?></span>
|
||||
<span class="item-title"><?php echo e($t['title']); ?></span>
|
||||
<span class="item-id" title="问题编号">#<?php echo (int)$t['id']; ?></span>
|
||||
|
||||
Reference in New Issue
Block a user