Files
FeedLite/index.php
T

316 lines
14 KiB
PHP
Raw Normal View History

<?php
/**
* 首页 / 当前软件的问题列表
*
* 两种形态(用户 2026-11-03 定的产品逻辑):
* ① **不带 sw**:首页 = 轻反馈系统的介绍页。
* 不再列「全部问题」—— 多个软件的反馈混在一起,对访客毫无意义:
* 他不是所有软件的使用者,看别人的反馈没价值。
* ② **带 sw**:当前软件的问题列表。
* 检索/筛选/分类全部只在本软件范围内,弱化其他软件。
*/
require __DIR__ . '/lib/bootstrap.php';
$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; }
}
/* ==================================================================
* ① 没有指定软件 → 首页介绍页
* ------------------------------------------------------------------
* 用户原话:「首页应该是对我们这个轻量级反馈系统的介绍,
* 说明它的特点是什么,然后告诉用户,如果现在想提交反馈,
* 可以点右上角的设置按钮,选择不同的系统,进去就可以了。」
*
* 🔴 不要在这里列问题。以前首页默认落到第一个软件并列出全部问题,
* 等于把 A 软件的反馈摊给 B 软件的用户看。
* ================================================================== */
if ($sw === null) {
fb_render_home($swList);
exit;
}
$swId = (int)$sw['id'];
$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';
// 分类按 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'; }
$where = [];
$args = [];
// 审核(先审后显):访客只看已通过审核的;管理员登录审核后台后可看全部
if (fb_moderation_on() && !fb_is_admin()) { $where[] = 'visible = 1'; }
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 '\\')";
$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;
$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);
$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) : [];
$siteName = (string)fb_cfg('site_name');
/* 页面标题(浏览器标签 / 页面大标题):
* 选中某个软件时用「XX 反馈系统」,让用户一眼确认自己在这个软件的反馈区;
* 没选软件时退回站点名。 */
$headTitle = $sw ? fb_software_site_title($sw)
: ($siteName !== '' ? $siteName . ' · 问题反馈' : '问题反馈');
fb_layout_head($sw ? fb_software_site_title($sw) : $headTitle, ['software' => $sw]);
?>
<?php /* 这一整行(软件图标+名字 / 简介 / 「我要反馈」按钮)已删除。
*
* 用户 2026-11-03:「上面已经有标题和『我要反馈』了,下面又出现一次,
* 完全重复。下面这一行不要了…其他内容开始往前提,往上提,这样更简洁。」
*
* 现在:
* - 「同步日历反馈系统」+ 灰色简介 → 只在顶栏出现一次
* - 「我要反馈」→ 顶栏右侧那个 btn-mini 就够了(列表页不再放第二个)
* - 列表内容直接从下面开始,省掉一整行高度
*/
if (!$sw):
?>
<div class="page-head">
<div><h1>全部问题</h1></div>
</div>
<?php endif; ?>
<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 $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>
<?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>
<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']; ?>">
<?php /* 标题单独占第一行、从最左边顶头开始 ——
用户 2026-11-03:标签放在标题前面会把标题挤到右边,很难看。
顺序改为「标题 → 标签」:标题在上,分类/状态另起一行。 */ ?>
<div class="item-head">
<span class="item-title"><?php echo e($t['title']); ?></span>
<span class="item-id" title="问题编号">#<?php echo (int)$t['id']; ?></span>
</div>
<div class="item-tags">
<?php
/* 分类标签必须有名字才渲染 —— 否则会输出一个空胶囊(什么都看不见),
* 用户会以为「反馈类型丢了」。老数据 category_id 可能为空,
* 这时按 slug 兜底;两者都取不到才不显示。 */
$lCat = !empty($t['category_id'])
? fb_category_label((int)$t['category_id'])
: fb_category_label_by_slug((int)$t['software_id'], (string)$t['category']);
?>
<?php if ($lCat !== ''): ?><span class="badge cat"><?php echo e($lCat); ?></span><?php endif; ?>
<span class="badge st st-<?php echo e($t['status']); ?>"><?php echo e(fb_status_label($t['status'])); ?></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();
/* ==================================================================
* 首页介绍页
* ------------------------------------------------------------------
* 用户 2026-11-03 定的产品逻辑:首页不讲"有哪些问题",只讲"这是什么"。
*
* 场景说明(决定了下面文案的取舍):
* 这个系统一般是给某个软件配一个反馈入口,把地址贴进那个软件的
* 「意见反馈」里。所以访客**总是从某个软件过来的**,他心里已经有一个
* 明确的目标软件;首页要做的是「介绍 + 指路」,不是「陈列别人的问题」。
* ================================================================== */
function fb_render_home(array $swList) {
$siteName = (string)fb_cfg('site_name');
$title = $siteName !== '' ? $siteName : '轻反馈';
fb_layout_head($title . ' · 轻量级反馈系统');
$n = count($swList);
?>
<div class="home">
<section class="home-hero">
<h1>轻量级反馈系统</h1>
<p class="home-lead">给每个软件一个独立的反馈入口:用户不用注册,填个昵称和邮箱就能提问题;
所有进展都会自动发邮件通知他。</p>
</section>
<section class="home-feats">
<div class="home-feat">
<div class="home-feat-ic">📮</div>
<h3>不用注册</h3>
<p>填昵称和常用邮箱即可提交。后续每一条进展都会发邮件通知,不用反复回来刷。</p>
</div>
<div class="home-feat">
<div class="home-feat-ic">🔗</div>
<h3>私人追踪链接</h3>
<p>提交后会收到一条专属链接,凭它随时查看进度——哪怕这条反馈还没审核通过。</p>
</div>
<div class="home-feat">
<div class="home-feat-ic">🗂</div>
<h3>按软件分开</h3>
<p>每个软件一套独立的问题列表和分类,互不干扰。同一软件的其他人也能看到已解决的问题。</p>
</div>
<div class="home-feat">
<div class="home-feat-ic">💬</div>
<h3>公开讨论</h3>
<p>已公开的问题任何人都能查看和回复。提交前先搜一下,避免重复提同样的问题。</p>
</div>
</section>
<section class="home-enter">
<h2>进入某个软件的反馈系统</h2>
<?php if ($n === 0): ?>
<p class="muted">管理员还没有配置软件。</p>
<?php else: ?>
<p class="home-enter-tip">
点击右上角的<span class="home-menu-demo" aria-hidden="true"><i></i><i></i><i></i></span>
<b>设置按钮</b>,从列表里选择您要反馈的软件即可。
</p>
<div class="home-sw-list">
<?php foreach ($swList as $s): ?>
<?php
list($c) = fb_software_colors($s);
if ($c === '') { $c = '#0057B8'; }
?>
<a class="home-sw" href="?sw=<?php echo e(urlencode((string)$s['slug'])); ?>"
style="--swc:<?php echo e($c); ?>">
<span class="home-sw-ic">
<?php
$ik = trim((string)(isset($s['icon_key']) ? $s['icon_key'] : ''));
$iu = $ik !== '' ? fb_icon_url($ik, 72) : '';
?>
<?php if ($iu !== ''): ?>
<img src="<?php echo e($iu); ?>" alt="" width="36" height="36">
<?php else: ?>
<span><?php echo e(fb_software_icon($s)); ?></span>
<?php endif; ?>
</span>
<span class="home-sw-nm"><?php echo e((string)$s['name']); ?></span>
<span class="home-sw-go">进入 →</span>
</a>
<?php endforeach; ?>
</div>
<p class="home-enter-alt muted small">
也可以直接点上方的软件卡片进入。
</p>
<?php endif; ?>
</section>
</div>
<?php
fb_layout_foot();
}