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:
@@ -12,14 +12,15 @@
|
|||||||
- **显示简称**:每个网站有「名称(自动)」和「简称(手动)」两个字段,页面展示你自定义的简称。
|
- **显示简称**:每个网站有「名称(自动)」和「简称(手动)」两个字段,页面展示你自定义的简称。
|
||||||
- **自定义排序**:网站 / 板块 / 搜索引擎都可上移下移排序。
|
- **自定义排序**:网站 / 板块 / 搜索引擎都可上移下移排序。
|
||||||
- **一键全部更新**:批量刷新所有网站的图标与名称,**不会改动你自定义的简称,也不会覆盖你自定义的图标**。
|
- **一键全部更新**:批量刷新所有网站的图标与名称,**不会改动你自定义的简称,也不会覆盖你自定义的图标**。
|
||||||
- **合集标签页**:把「合集」做成标签页,点标签平铺该合集下的全部链接。
|
- **合集区可「标签页折叠」或「全部平铺」**:底部「合集」区有两种展示方式,后台「设置 → 合集显示方式」一键切换。条目不多时选**全部平铺**(每个合集各占一块,一眼看全、不用点);合集多或条目杂时选**标签页折叠**(默认只展开当前那一个)。
|
||||||
- **前后台永远一致**:所有动态页面都发 `Cache-Control: no-store, no-cache`,静态资源走「长缓存 + 版本号」(文件名不变、URL 带 `?v=mtime-size`)。
|
- **前后台永远一致**:所有动态页面都发 `Cache-Control: no-store, no-cache`,静态资源走「长缓存 + 版本号」(文件名不变、URL 带 `?v=mtime-size`)。
|
||||||
|
|
||||||
## 页面结构(复刻 n.yangyq.net 风格)
|
## 页面结构(复刻 n.yangyq.net 风格)
|
||||||
|
|
||||||
1. 顶部:搜索引擎切换 + 搜索框(带联想下拉)
|
1. 顶部:搜索引擎切换 + 搜索框(带联想下拉)
|
||||||
2. 中部:网格板块(如「常用」「工具」)
|
2. 中部:网格板块(如「常用」「工具」)——「展示方式」选「普通网格板块」的板块都在这里
|
||||||
3. 下部:「合集」标签页
|
3. 下部:「合集」区 ——「展示方式」选「合集标签页」的板块收集到这里,再按后台的
|
||||||
|
「合集显示方式」决定是**标签页折叠**(默认)还是**全部平铺**(每组各占一块)
|
||||||
|
|
||||||
## 目录结构
|
## 目录结构
|
||||||
|
|
||||||
|
|||||||
@@ -211,6 +211,8 @@ try {
|
|||||||
case 'settings_save': {
|
case 'settings_save': {
|
||||||
$srcRaw = (string)($_POST['suggest_source'] ?? 'baidu');
|
$srcRaw = (string)($_POST['suggest_source'] ?? 'baidu');
|
||||||
$src = in_array($srcRaw, ['auto', 'baidu', 'bing', 'so360'], true) ? $srcRaw : 'baidu';
|
$src = in_array($srcRaw, ['auto', 'baidu', 'bing', 'so360'], true) ? $srcRaw : 'baidu';
|
||||||
|
// 合集区显示方式:flat = 全部平铺;其余一律回到 tabs(标签页折叠)
|
||||||
|
$coll = (string)($_POST['collections_layout'] ?? 'tabs') === 'flat' ? 'flat' : 'tabs';
|
||||||
$map = [
|
$map = [
|
||||||
'site_name' => trim((string)($_POST['site_name'] ?? '')),
|
'site_name' => trim((string)($_POST['site_name'] ?? '')),
|
||||||
'subtitle' => trim((string)($_POST['subtitle'] ?? '')),
|
'subtitle' => trim((string)($_POST['subtitle'] ?? '')),
|
||||||
@@ -219,6 +221,7 @@ try {
|
|||||||
'open_blank' => isset($_POST['open_blank']) ? '1' : '0',
|
'open_blank' => isset($_POST['open_blank']) ? '1' : '0',
|
||||||
'icon_services' => isset($_POST['icon_services']) ? '1' : '0',
|
'icon_services' => isset($_POST['icon_services']) ? '1' : '0',
|
||||||
'suggest_source' => $src,
|
'suggest_source' => $src,
|
||||||
|
'collections_layout' => $coll,
|
||||||
];
|
];
|
||||||
$st = $pdo->prepare('INSERT INTO settings (k,v) VALUES (?,?) ON CONFLICT(k) DO UPDATE SET v=excluded.v');
|
$st = $pdo->prepare('INSERT INTO settings (k,v) VALUES (?,?) ON CONFLICT(k) DO UPDATE SET v=excluded.v');
|
||||||
foreach ($map as $k => $v) { $st->execute([$k, $v]); }
|
foreach ($map as $k => $v) { $st->execute([$k, $v]); }
|
||||||
|
|||||||
@@ -321,6 +321,19 @@ else: // settings ?>
|
|||||||
所以默认「统一用百度」——这样切到任何引擎下拉都有词。选「各搜索引擎自己的接口」则恢复成
|
所以默认「统一用百度」——这样切到任何引擎下拉都有词。选「各搜索引擎自己的接口」则恢复成
|
||||||
各用各的(没恢复好的引擎会自动回退到百度的建议)。
|
各用各的(没恢复好的引擎会自动回退到百度的建议)。
|
||||||
</p>
|
</p>
|
||||||
|
<label>合集显示方式
|
||||||
|
<?php $cl = setting('collections_layout', 'tabs'); ?>
|
||||||
|
<select name="collections_layout">
|
||||||
|
<option value="tabs" <?= $cl === 'flat' ? '' : 'selected' ?>>标签页折叠(合集多时用)</option>
|
||||||
|
<option value="flat" <?= $cl === 'flat' ? 'selected' : '' ?>>全部平铺(每组各占一块)</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<p class="hint span2">
|
||||||
|
只影响底部<b>「合集」</b>那一区,中间那些普通网格板块(常用 / 工具…)不受影响。
|
||||||
|
<b>每个合集里条目不多</b>时选「全部平铺」,一眼看全、不用点;
|
||||||
|
<b>合集很多、条目很杂</b>时选「标签页折叠」,默认只展开当前那一个。
|
||||||
|
某个板块是进「合集」还是做成普通网格板块,由「板块 → 展示方式」决定。
|
||||||
|
</p>
|
||||||
<label>主题色<input type="text" name="primary_color" value="<?= e(setting('primary_color', '#2F6BF7')) ?>"></label>
|
<label>主题色<input type="text" name="primary_color" value="<?= e(setting('primary_color', '#2F6BF7')) ?>"></label>
|
||||||
<label class="chk"><input type="checkbox" name="open_blank" <?= setting('open_blank', '1') === '1' ? 'checked' : '' ?>> 搜索结果新窗口打开</label>
|
<label class="chk"><input type="checkbox" name="open_blank" <?= setting('open_blank', '1') === '1' ? 'checked' : '' ?>> 搜索结果新窗口打开</label>
|
||||||
<label class="chk"><input type="checkbox" name="icon_services" <?= setting('icon_services', '1') === '1' ? 'checked' : '' ?>> 联网搜索高清图标(第三方聚合服务)</label>
|
<label class="chk"><input type="checkbox" name="icon_services" <?= setting('icon_services', '1') === '1' ? 'checked' : '' ?>> 联网搜索高清图标(第三方聚合服务)</label>
|
||||||
|
|||||||
@@ -162,6 +162,10 @@ a { text-decoration: none; color: inherit; }
|
|||||||
}
|
}
|
||||||
.tab-panel { display: none; }
|
.tab-panel { display: none; }
|
||||||
.tab-panel.active { display: flex; animation: fadeUp .28s ease both; }
|
.tab-panel.active { display: flex; animation: fadeUp .28s ease both; }
|
||||||
|
/* 「全部平铺」模式:每个合集各占一块,与上面的网格板块一致(此时没有 .tabs) */
|
||||||
|
.collections.flat .collections-title { margin-bottom: 16px; }
|
||||||
|
.collections.flat .mylist { margin: 0 0 16px; }
|
||||||
|
.collections.flat .mylist:last-child { margin-bottom: 0; }
|
||||||
@keyframes fadeUp {
|
@keyframes fadeUp {
|
||||||
from { opacity: 0; transform: translateY(8px); }
|
from { opacity: 0; transform: translateY(8px); }
|
||||||
to { opacity: 1; transform: none; }
|
to { opacity: 1; transform: none; }
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* 简航 SimpleNav —— 前台首页
|
* 简航 SimpleNav —— 前台首页
|
||||||
* 顶部:品牌 + 搜索引擎切换 + 搜索框(带联想下拉)
|
* 顶部:品牌 + 搜索引擎切换 + 搜索框(带联想下拉)
|
||||||
* 中部:常用 / 工具等网格板块
|
* 中部:常用 / 工具等网格板块
|
||||||
* 下部:合集(标签页形式,点标签平铺该合集全部链接)
|
* 下部:合集(标签页折叠 或 全部平铺,后台可切换)
|
||||||
*/
|
*/
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
require_once __DIR__ . '/functions.php';
|
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();
|
$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();
|
$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');
|
$defSlug = setting('default_engine', 'bing');
|
||||||
$default = null;
|
$default = null;
|
||||||
@@ -125,24 +130,41 @@ function site_tile(array $s): void
|
|||||||
</ul>
|
</ul>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
||||||
<!-- 合集:标签页 -->
|
<!-- 合集:标签页折叠 或 全部平铺(后台「设置 → 合集显示方式」切换) -->
|
||||||
<?php if ($tabGroups): ?>
|
<?php
|
||||||
<div class="collections">
|
// 先收一遍「有内容的合集」,空合集不占位(标签页模式下也不会多出一个空标签)
|
||||||
|
$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="collections-title">合集</div>
|
||||||
|
<?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; ?>
|
||||||
|
<?php else: ?>
|
||||||
<div class="tabs" id="coll-tabs" role="tablist">
|
<div class="tabs" id="coll-tabs" role="tablist">
|
||||||
<?php foreach ($tabGroups as $i => $g): ?>
|
<?php foreach ($collGroups as $i => $c): ?>
|
||||||
<button type="button" class="tab<?= $i === 0 ? ' active' : '' ?>" role="tab"
|
<button type="button" class="tab<?= $i === 0 ? ' active' : '' ?>" role="tab"
|
||||||
aria-selected="<?= $i === 0 ? 'true' : 'false' ?>"
|
aria-selected="<?= $i === 0 ? 'true' : 'false' ?>"
|
||||||
data-target="coll-<?= (int)$g['id'] ?>"><?= e($g['name']) ?></button>
|
data-target="coll-<?= (int)$c['g']['id'] ?>"><?= e($c['g']['name']) ?></button>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-panels">
|
<div class="tab-panels">
|
||||||
<?php foreach ($tabGroups as $i => $g): $items = sites_of((int)$g['id']); ?>
|
<?php foreach ($collGroups as $i => $c): ?>
|
||||||
<ul class="mylist tab-panel<?= $i === 0 ? ' active' : '' ?>" id="coll-<?= (int)$g['id'] ?>" role="tabpanel">
|
<ul class="mylist tab-panel<?= $i === 0 ? ' active' : '' ?>" id="coll-<?= (int)$c['g']['id'] ?>" role="tabpanel">
|
||||||
<?php foreach ($items as $s) { site_tile($s); } ?>
|
<?php foreach ($c['items'] as $s) { site_tile($s); } ?>
|
||||||
</ul>
|
</ul>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user