feat: 合集区支持「全部平铺 / 标签页折叠」两种展示方式

后台「设置 → 合集显示方式」一键切换,只影响底部「合集」区,
中间那些普通网格板块不受影响:

- tabs(默认,标签页折叠):一次只平铺当前这一个合集(原行为)。
- flat(全部平铺):每个合集各占一块,和网格板块同构,一眼看全、不用点。
  适合「每个合集里条目都不多」的情况;合集一多就切回标签页折叠。

实现:
- index.php 先收集「有内容的合集」,空合集不再占位(标签页模式下也不会
  多出一个空标签),再按设置渲染成标签页或平铺区块;
  输出 data-layout="tabs|flat" 便于断言与排查。
- admin/index.php 新增选择框 + 说明;admin/actions.php 白名单校验,
  非法值一律回退成 tabs。
- style.css 新增 3 条 .collections.flat 规则(平铺区块间距、最后一块不留底边距)。
- README 更新「页面结构」与特点说明。

验证:
- e2e 新增场景 6(93/93 通过):两种模式各自的结构断言、非法值回退、
  保存该项不会弄丢其它设置、空合集不占位。
- 真实几何检查(flat / tabs 各 5 个视口,30/30 通过):无横向溢出、正文列宽度正常。
- 部署诊断段在线上真机切 flat → 抓首页 → 断言 → 切回 tabs 并断言已复原。
This commit is contained in:
2026-09-28 17:51:10 +08:00
parent 4ba512b147
commit c3ba71c4ac
5 changed files with 62 additions and 19 deletions
+38 -16
View File
@@ -3,7 +3,7 @@
* 简航 SimpleNav —— 前台首页
* 顶部:品牌 + 搜索引擎切换 + 搜索框(带联想下拉)
* 中部:常用 / 工具等网格板块
* 下部:合集(标签页形式,点标签平铺该合集全部链接)
* 下部:合集(标签页折叠 或 全部平铺,后台可切换)
*/
declare(strict_types=1);
require_once __DIR__ . '/functions.php';
@@ -16,6 +16,11 @@ $gridGroups = $pdo->query("SELECT * FROM groups WHERE is_visible=1 AND display='
$tabGroups = $pdo->query("SELECT * FROM groups WHERE is_visible=1 AND display='tabs' ORDER BY sort_order, id")->fetchAll();
$engines = $pdo->query("SELECT * FROM engines WHERE is_active=1 ORDER BY sort_order, id")->fetchAll();
// 合集区的显示方式(后台「设置 → 合集显示方式」):
// tabs = 标签页折叠(默认)—— 一次只平铺当前这一个合集
// flat = 全部平铺 —— 每个合集各占一块,一眼看全、不用点
$collLayout = setting('collections_layout', 'tabs') === 'flat' ? 'flat' : 'tabs';
// 默认引擎
$defSlug = setting('default_engine', 'bing');
$default = null;
@@ -125,24 +130,41 @@ function site_tile(array $s): void
</ul>
<?php endforeach; ?>
<!-- 合集:标签页 -->
<?php if ($tabGroups): ?>
<div class="collections">
<!-- 合集:标签页折叠 或 全部平铺(后台「设置 → 合集显示方式」切换) -->
<?php
// 先收一遍「有内容的合集」,空合集不占位(标签页模式下也不会多出一个空标签)
$collGroups = [];
foreach ($tabGroups as $g) {
$items = sites_of((int)$g['id']);
if ($items) { $collGroups[] = ['g' => $g, 'items' => $items]; }
}
?>
<?php if ($collGroups): ?>
<div class="collections<?= $collLayout === 'flat' ? ' flat' : '' ?>" data-layout="<?= e($collLayout) ?>">
<div class="collections-title">合集</div>
<div class="tabs" id="coll-tabs" role="tablist">
<?php foreach ($tabGroups as $i => $g): ?>
<button type="button" class="tab<?= $i === 0 ? ' active' : '' ?>" role="tab"
aria-selected="<?= $i === 0 ? 'true' : 'false' ?>"
data-target="coll-<?= (int)$g['id'] ?>"><?= e($g['name']) ?></button>
<?php endforeach; ?>
</div>
<div class="tab-panels">
<?php foreach ($tabGroups as $i => $g): $items = sites_of((int)$g['id']); ?>
<ul class="mylist tab-panel<?= $i === 0 ? ' active' : '' ?>" id="coll-<?= (int)$g['id'] ?>" role="tabpanel">
<?php foreach ($items as $s) { site_tile($s); } ?>
<?php if ($collLayout === 'flat'): ?>
<?php foreach ($collGroups as $c): ?>
<ul class="mylist">
<li class="title"><?= e($c['g']['name']) ?></li>
<?php foreach ($c['items'] as $s) { site_tile($s); } ?>
</ul>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="tabs" id="coll-tabs" role="tablist">
<?php foreach ($collGroups as $i => $c): ?>
<button type="button" class="tab<?= $i === 0 ? ' active' : '' ?>" role="tab"
aria-selected="<?= $i === 0 ? 'true' : 'false' ?>"
data-target="coll-<?= (int)$c['g']['id'] ?>"><?= e($c['g']['name']) ?></button>
<?php endforeach; ?>
</div>
<div class="tab-panels">
<?php foreach ($collGroups as $i => $c): ?>
<ul class="mylist tab-panel<?= $i === 0 ? ' active' : '' ?>" id="coll-<?= (int)$c['g']['id'] ?>" role="tabpanel">
<?php foreach ($c['items'] as $s) { site_tile($s); } ?>
</ul>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php endif; ?>