2026-09-28 20:34:37 +08:00
|
|
|
<?php
|
|
|
|
|
/**
|
|
|
|
|
* 问题列表
|
|
|
|
|
*/
|
|
|
|
|
require __DIR__ . '/lib/bootstrap.php';
|
|
|
|
|
|
2026-11-01 11:35:55 +08:00
|
|
|
$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();
|
2026-09-28 20:34:37 +08:00
|
|
|
|
|
|
|
|
$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';
|
|
|
|
|
|
2026-11-01 11:35:55 +08:00
|
|
|
// 分类按 slug 在当前软件内解析;解析不到退回「全部」,不许 404
|
|
|
|
|
$curCat = null;
|
|
|
|
|
if ($cat !== '') {
|
|
|
|
|
$curCat = fb_category_by_slug($swId, $cat);
|
|
|
|
|
if (!$curCat) { $cat = ''; }
|
|
|
|
|
}
|
2026-09-28 20:34:37 +08:00
|
|
|
if ($st !== '' && !isset($statuses[$st])) { $st = ''; }
|
|
|
|
|
if ($sort !== 'new') { $sort = 'active'; }
|
|
|
|
|
|
|
|
|
|
$where = [];
|
|
|
|
|
$args = [];
|
|
|
|
|
// 审核(先审后显):访客只看已通过审核的;管理员登录审核后台后可看全部
|
|
|
|
|
if (fb_moderation_on() && !fb_is_admin()) { $where[] = 'visible = 1'; }
|
2026-11-01 11:35:55 +08:00
|
|
|
if ($swId > 0) { $where[] = 'software_id = ?'; $args[] = $swId; }
|
|
|
|
|
if ($curCat) { $where[] = 'category_id = ?'; $args[] = (int)$curCat['id']; }
|
|
|
|
|
if ($st !== '') { $where[] = 'status = ?'; $args[] = $st; }
|
2026-09-28 20:34:37 +08:00
|
|
|
if ($q !== '') {
|
|
|
|
|
$like = '%' . addcslashes($q, '%_\\') . '%';
|
|
|
|
|
$where[] = "(title LIKE ? ESCAPE '\\' OR body LIKE ? ESCAPE '\\')";
|
|
|
|
|
$args[] = $like;
|
|
|
|
|
$args[] = $like;
|
|
|
|
|
}
|
|
|
|
|
$wsql = $where ? (' WHERE ' . implode(' AND ', $where)) : '';
|
|
|
|
|
$order = ($sort === 'new') ? 'created_at DESC' : 'updated_at DESC';
|
|
|
|
|
|
|
|
|
|
$stc = $pdo->prepare('SELECT COUNT(*) FROM threads' . $wsql);
|
|
|
|
|
$stc->execute($args);
|
|
|
|
|
$total = (int)$stc->fetchColumn();
|
|
|
|
|
|
|
|
|
|
$pageSize = max(5, (int)fb_cfg('page_size', 20));
|
|
|
|
|
$pages = max(1, (int)ceil($total / $pageSize));
|
|
|
|
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
|
|
|
|
if ($page < 1) { $page = 1; }
|
|
|
|
|
if ($page > $pages){ $page = $pages; }
|
|
|
|
|
$offset = ($page - 1) * $pageSize;
|
|
|
|
|
|
2026-11-01 11:35:55 +08:00
|
|
|
$sql = 'SELECT id, title, category, category_id, software_id, status, author_name, reply_count, created_at, updated_at,'
|
2026-09-28 20:34:37 +08:00
|
|
|
. ' substr(body, 1, 300) AS excerpt FROM threads'
|
|
|
|
|
. $wsql . ' ORDER BY ' . $order . ' LIMIT ' . $pageSize . ' OFFSET ' . $offset;
|
|
|
|
|
$sth = $pdo->prepare($sql);
|
|
|
|
|
$sth->execute($args);
|
|
|
|
|
$threads = $sth->fetchAll();
|
|
|
|
|
|
|
|
|
|
// 附件缩略图与数量(只在有附件时才有开销)
|
|
|
|
|
$tids = [];
|
|
|
|
|
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) : [];
|
|
|
|
|
|
2026-11-01 11:35:55 +08:00
|
|
|
$siteName = (string)fb_cfg('site_name');
|
2026-11-03 08:27:03 +08:00
|
|
|
/* 页面标题(浏览器标签 / 页面大标题):
|
|
|
|
|
* 选中某个软件时用「XX 反馈系统」,让用户一眼确认自己在这个软件的反馈区;
|
|
|
|
|
* 没选软件时退回站点名。 */
|
|
|
|
|
$headTitle = $sw ? fb_software_site_title($sw)
|
|
|
|
|
: ($siteName !== '' ? $siteName . ' · 问题反馈' : '问题反馈');
|
|
|
|
|
fb_layout_head($sw ? fb_software_site_title($sw) : $headTitle, ['software' => $sw]);
|
2026-09-28 20:34:37 +08:00
|
|
|
?>
|
|
|
|
|
|
|
|
|
|
<div class="page-head">
|
|
|
|
|
<div>
|
2026-11-01 12:39:20 +08:00
|
|
|
<?php
|
2026-11-03 09:20:14 +08:00
|
|
|
/* 站点描述(site_desc)不再显示在页面上 —— 用户 2026-11-03 明确说
|
|
|
|
|
* 「欢迎在这里反馈使用问题、提出功能建议」这类话没有意义,全部删掉。
|
|
|
|
|
* 配置项保留,后台仍可编辑,只是前台不渲染。 */
|
2026-11-01 12:39:20 +08:00
|
|
|
$swNote = ($sw && !empty($sw['note'])) ? (string)$sw['note'] : '';
|
|
|
|
|
?>
|
2026-11-03 08:27:03 +08:00
|
|
|
<?php if ($sw): ?>
|
2026-11-03 08:34:03 +08:00
|
|
|
<?php /* 内容区只放「图标 + 软件名」,不再重复「XX 反馈系统」——
|
|
|
|
|
那个标题已经放到最上面的顶栏里了,两处重复反而啰嗦。 */ ?>
|
2026-11-03 09:20:14 +08:00
|
|
|
<?php
|
|
|
|
|
/* 内容区标识也用软件主题色 */
|
|
|
|
|
list($pageSwColor) = fb_software_colors($sw);
|
|
|
|
|
if ($pageSwColor === '') { $pageSwColor = '#0057B8'; }
|
|
|
|
|
?>
|
|
|
|
|
<div class="page-sw-id" style="--swc:<?php echo e($pageSwColor); ?>"><?php echo fb_software_badge($sw, 'lg'); ?></div>
|
2026-11-03 08:34:03 +08:00
|
|
|
<?php else: ?>
|
|
|
|
|
<h1>全部问题</h1>
|
2026-11-03 08:27:03 +08:00
|
|
|
<?php endif; ?>
|
2026-11-01 12:39:20 +08:00
|
|
|
<?php if ($swNote !== ''): ?><p class="muted small" style="margin:2px 0 0"><?php echo e($swNote); ?></p><?php endif; ?>
|
2026-11-01 11:35:55 +08:00
|
|
|
</div>
|
|
|
|
|
<a class="btn primary" href="new.php<?php echo $sw ? '?sw=' . e(urlencode((string)$sw['slug'])) : ''; ?>">我要反馈</a>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-09-28 20:34:37 +08:00
|
|
|
<div class="filters">
|
|
|
|
|
<div class="chips">
|
|
|
|
|
<a class="chip <?php echo $cat === '' ? 'on' : ''; ?>" href="<?php echo e(fb_qs(['cat' => null, 'page' => null]) ?: '?'); ?>">全部</a>
|
2026-11-01 11:35:55 +08:00
|
|
|
<?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>
|
2026-09-28 20:34:37 +08:00
|
|
|
<?php endforeach; ?>
|
|
|
|
|
</div>
|
|
|
|
|
<form class="filter-row" method="get" action="">
|
2026-11-01 11:35:55 +08:00
|
|
|
<?php if ($sw): ?><input type="hidden" name="sw" value="<?php echo e((string)$sw['slug']); ?>"><?php endif; ?>
|
2026-09-28 20:34:37 +08:00
|
|
|
<input type="hidden" name="cat" value="<?php echo e($cat); ?>">
|
|
|
|
|
<select name="status" onchange="this.form.submit()">
|
|
|
|
|
<option value="">全部状态</option>
|
|
|
|
|
<?php foreach ($statuses as $k => $v): ?>
|
|
|
|
|
<option value="<?php echo e($k); ?>" <?php echo $st === $k ? 'selected' : ''; ?>><?php echo e($v); ?></option>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
</select>
|
|
|
|
|
<select name="sort" onchange="this.form.submit()">
|
|
|
|
|
<option value="active" <?php echo $sort === 'active' ? 'selected' : ''; ?>>按最新回复</option>
|
|
|
|
|
<option value="new" <?php echo $sort === 'new' ? 'selected' : ''; ?>>按最新发布</option>
|
|
|
|
|
</select>
|
|
|
|
|
<input type="search" name="q" value="<?php echo e($q); ?>" placeholder="搜索标题或内容…">
|
|
|
|
|
<button class="btn" type="submit">搜索</button>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="list">
|
|
|
|
|
<?php if (!$threads): ?>
|
|
|
|
|
<div class="empty">
|
|
|
|
|
<p>还没有符合条件的问题。</p>
|
2026-11-01 11:35:55 +08:00
|
|
|
<p><a class="btn primary" href="new.php<?php echo $sw ? '?sw=' . e(urlencode((string)$sw['slug'])) : ''; ?>">来提第一个吧</a></p>
|
2026-09-28 20:34:37 +08:00
|
|
|
</div>
|
|
|
|
|
<?php else: ?>
|
|
|
|
|
<?php foreach ($threads as $t): ?>
|
|
|
|
|
<a class="item" href="thread.php?id=<?php echo (int)$t['id']; ?>">
|
|
|
|
|
<div class="item-top">
|
2026-11-01 11:35:55 +08:00
|
|
|
<span class="badge cat"><?php echo e(fb_category_label($t['category_id'])); ?></span>
|
2026-09-28 20:34:37 +08:00
|
|
|
<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>
|
|
|
|
|
</div>
|
|
|
|
|
<p class="item-ex"><?php echo e(fb_excerpt($t['excerpt'], 120)); ?></p>
|
|
|
|
|
<?php if (!empty($attImgs[(int)$t['id']])): ?>
|
|
|
|
|
<div class="thumbs">
|
|
|
|
|
<?php foreach ($attImgs[(int)$t['id']] as $im): ?>
|
|
|
|
|
<img loading="lazy" alt="" src="<?php echo e(fb_attach_thumb_url($im['oss_key'], 200, isset($im['mime']) ? $im['mime'] : '')); ?>">
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
</div>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<div class="item-meta">
|
|
|
|
|
<span class="who"><?php echo e($t['author_name']); ?></span>
|
|
|
|
|
<span class="dot">·</span>
|
|
|
|
|
<span><?php echo e(fb_fmt_time($t['updated_at'])); ?></span>
|
|
|
|
|
<span class="dot">·</span>
|
|
|
|
|
<span><?php echo (int)$t['reply_count']; ?> 条回复</span>
|
|
|
|
|
<?php if (!empty($attCount[(int)$t['id']])): ?>
|
|
|
|
|
<span class="dot">·</span>
|
|
|
|
|
<span class="att-tag">附件 <?php echo (int)$attCount[(int)$t['id']]; ?></span>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</div>
|
|
|
|
|
</a>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<?php if ($pages > 1): ?>
|
|
|
|
|
<div class="pager">
|
|
|
|
|
<?php
|
|
|
|
|
$mk = function ($p) {
|
|
|
|
|
return e(fb_qs(['page' => $p <= 1 ? null : $p]) ?: '?');
|
|
|
|
|
};
|
|
|
|
|
?>
|
|
|
|
|
<?php if ($page > 1): ?>
|
|
|
|
|
<a class="btn" href="<?php echo $mk($page - 1); ?>">上一页</a>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<span class="pager-info">第 <?php echo (int)$page; ?> / <?php echo (int)$pages; ?> 页 · 共 <?php echo (int)$total; ?> 个问题</span>
|
|
|
|
|
<?php if ($page < $pages): ?>
|
|
|
|
|
<a class="btn" href="<?php echo $mk($page + 1); ?>">下一页</a>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</div>
|
|
|
|
|
<?php elseif ($total > 0): ?>
|
|
|
|
|
<div class="pager"><span class="pager-info">共 <?php echo (int)$total; ?> 个问题</span></div>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
|
|
|
|
<?php
|
|
|
|
|
fb_layout_foot();
|