fix(cms): 图库分组自由输入 + 前端筛选动态化;速通坞折叠防误触
用户实测三处:①照片分组写死 street/landscape 两选不能加——Sveltia select 无 creatable(文档核实),改 string 自由输入;前端筛选同步动态化(分组 从数据反向派生,[a-z0-9-] 命名的分组零配置自动出现筛选片),顺带排查全 后台其余 select:分轨/颜色/字体/来源均为语义契约(写死是安全设计)唯分 组该开放;②速通坞与编辑区抢点击(手机端尤甚)——改默认收起成一枚 26px 圆钮,点开才展开五入口,状态记忆+点外部收起;③翻译无按钮答疑: 英文版走构建链自动机翻(保存中文即出,无按钮是设计),仪表盘有覆盖数。 (同轮收编站长经 CMS 新增的图库照片 夜色金茂湾) 验证: pnpm astro build exit 0;config-check 通过;E2E 65/65(筛选断言适配动态 id);产物断言 f-g-landscape 数据派生 Spec: 2026-11-05_blog-5n-admin-deep-optimization Co-authored-by: GLM-5.3
This commit is contained in:
1 parent
5d187e1240
commit
ef2bd97267
4 files changed
+63
-25
No files matched your search
@@ -132,7 +132,7 @@ collections:
|
||||
# title 必填:Astro schema z.string() 无 .optional(),omit 纪律下必需且有值才会落键(空 title 会让 main 不可构建)
|
||||
- { label: 标题, name: title, widget: string, required: true }
|
||||
- { label: 图片, name: image, widget: image }
|
||||
- { label: 分组, name: group, widget: select, options: [street, landscape] }
|
||||
- { label: 分组, name: group, widget: string, hint: '小写英文短词(如 landscape / street / portrait);新分组直接输入新词,图库筛选会自动出现' }
|
||||
- { label: 替代文本, name: alt, widget: string, required: false }
|
||||
- { label: 人为描述, name: caption, widget: text, required: false, hint: 展示在灯箱里的人为描述(一句话策展文案) }
|
||||
- label: EXIF 覆盖(一般留空,构建期自动从图提取)
|
||||
|
||||
+43
-13
@@ -9,23 +9,53 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="manifest" href="./manifest.webmanifest" />
|
||||
<style>
|
||||
/* 速通坞(5n D1):日常要跳的五个入口常驻右上,不挡 Sveltia 主 UI(对话层 z 更高) */
|
||||
#quick-dock{position:fixed;top:10px;right:12px;z-index:9000;display:flex;gap:4px;
|
||||
font:12px/1 system-ui,sans-serif;opacity:.78;transition:opacity .2s}
|
||||
#quick-dock:hover{opacity:1}
|
||||
#quick-dock a{color:#5b6472;text-decoration:none;padding:4px 8px;border-radius:6px;background:rgba(127,131,140,.12)}
|
||||
#quick-dock a:hover{color:#3454d1;background:rgba(52,84,209,.1)}
|
||||
@media (max-width:720px){#quick-dock a:not(.qd-key){display:none}}
|
||||
</style>
|
||||
/* 速通坞(5n D1 + 用户实测防误触改版):默认收起成一枚小圆钮,
|
||||
点开才展开五入口;开关状态存 localStorage(手机端默认必收起)。
|
||||
收起态只有一个 26px 圆钮,不再与编辑区抢点击面。 */
|
||||
#qd-toggle{position:fixed;top:10px;right:12px;z-index:9000;width:26px;height:26px;
|
||||
border-radius:50%;border:1px solid rgba(127,131,140,.4);background:rgba(255,255,255,.85);
|
||||
color:#5b6472;font:14px/1 system-ui,sans-serif;cursor:pointer;opacity:.6;transition:opacity .2s}
|
||||
#qd-toggle:hover{opacity:1}
|
||||
body[data-qd=open] #qd-toggle{background:rgba(52,84,209,.12);color:#3454d1;border-color:rgba(52,84,209,.4)}
|
||||
#qd-links{position:fixed;top:44px;right:12px;z-index:9000;display:none;flex-direction:column;gap:4px;
|
||||
font:12px/1 system-ui,sans-serif;padding:6px;border-radius:10px;background:rgba(255,255,255,.95);
|
||||
box-shadow:0 2px 10px rgba(0,0,0,.12)}
|
||||
body[data-qd=open] #qd-links{display:flex}
|
||||
#qd-links a{color:#5b6472;text-decoration:none;padding:5px 10px;border-radius:6px;white-space:nowrap}
|
||||
#qd-links a:hover{color:#3454d1;background:rgba(52,84,209,.08)}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav id="quick-dock" aria-label="快捷入口">
|
||||
<a class="qd-key" href="https://wait4x.online/artalk/" target="_blank" rel="noopener">评论</a>
|
||||
<a class="qd-key" href="./stats.html" target="_blank" rel="noopener">仪表盘</a>
|
||||
<a href="https://git.wait4x.online/wait4x/blog" target="_blank" rel="noopener">仓库</a>
|
||||
<a href="https://git.wait4x.online/wait4x/blog/src/branch/main/docs/%E5%90%8E%E5%8F%B0%E5%9B%9E%E6%BB%9A%E6%8C%87%E5%BC%95.md" target="_blank" rel="noopener">回滚</a>
|
||||
<a href="https://git.wait4x.online/wait4x/blog/src/branch/main/docs/%E7%AE%A1%E7%90%86%E5%8F%B0.md" target="_blank" rel="noopener">手册</a>
|
||||
<button id="qd-toggle" type="button" aria-expanded="false" title="快捷入口">≡</button>
|
||||
<div id="qd-links">
|
||||
<a href="https://wait4x.online/artalk/" target="_blank" rel="noopener">评论管理</a>
|
||||
<a href="./stats.html" target="_blank" rel="noopener">内容仪表盘</a>
|
||||
<a href="https://git.wait4x.online/wait4x/blog" target="_blank" rel="noopener">仓库</a>
|
||||
<a href="https://git.wait4x.online/wait4x/blog/src/branch/main/docs/%E5%90%8E%E5%8F%B0%E5%9B%9E%E6%BB%9A%E6%8C%87%E5%BC%95.md" target="_blank" rel="noopener">回滚指引</a>
|
||||
<a href="https://git.wait4x.online/wait4x/blog/src/branch/main/docs/%E7%AE%A1%E7%90%86%E5%8F%B0.md" target="_blank" rel="noopener">管理台手册</a>
|
||||
</div>
|
||||
</nav>
|
||||
<script>
|
||||
// 速通坞开合:默认收起(误触根除);记住用户选择;点外部自动收起
|
||||
(function () {
|
||||
var t = document.getElementById('qd-toggle');
|
||||
var set = function (open) {
|
||||
document.body.dataset.qd = open ? 'open' : 'closed';
|
||||
t.setAttribute('aria-expanded', String(open));
|
||||
try { localStorage.setItem('qd-open', open ? '1' : '0'); } catch (e) {}
|
||||
};
|
||||
var saved;
|
||||
try { saved = localStorage.getItem('qd-open'); } catch (e) {}
|
||||
set(saved === '1'); // 缺省收起
|
||||
t.addEventListener('click', function () {
|
||||
set(document.body.dataset.qd !== 'open');
|
||||
});
|
||||
document.addEventListener('click', function (e) {
|
||||
if (document.body.dataset.qd === 'open' && !e.target.closest('#quick-dock')) set(false);
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<!-- pinned 本地 bundle(0.259.0,5n T1 升级):官方模板仅此一个 script,无 CSS link、无 type=module -->
|
||||
<script src="./sveltia-cms.js"></script>
|
||||
<!-- 艺术字体切片(unicode-range 按需取片):选择面板的示例字形用真实字体渲染 -->
|
||||
|
||||
@@ -73,6 +73,14 @@ const cards = await Promise.all(
|
||||
}),
|
||||
);
|
||||
const first = cards[0];
|
||||
// 分组筛选动态化(5n 后续):后台分组已开放自由输入(select 无 creatable),
|
||||
// 这里从数据反向派生筛选片——新分组零配置自动出现。
|
||||
// 仅 [a-z0-9-] 命名的分组生成 CSS 规则(选择器安全);其余分组仍正常展示在「全部」里
|
||||
const groups = [...new Set(cards.map((c) => c.group).filter(Boolean))]
|
||||
.filter((g) => /^[a-z0-9-]+$/.test(g));
|
||||
const filterRules = groups
|
||||
.map((g) => `.g-main:has(#f-g-${g}:checked) .shot:not([data-group='${g}']) { display: none; }`)
|
||||
.join('\n');
|
||||
---
|
||||
|
||||
<BaseLayout title={`${t('gallery_title', locale)} · wait4x`}>
|
||||
@@ -82,10 +90,12 @@ const first = cards[0];
|
||||
<legend class="sr-only">{t('gallery_filter_legend', locale)}</legend>
|
||||
<input class="f-input" type="radio" name="gallery-group" id="f-all" checked />
|
||||
<label class="tap chip" for="f-all">{t('gallery_filter_all', locale)}</label>
|
||||
<input class="f-input" type="radio" name="gallery-group" id="f-street" />
|
||||
<label class="tap chip" for="f-street">{t('gallery_filter_street', locale)}</label>
|
||||
<input class="f-input" type="radio" name="gallery-group" id="f-landscape" />
|
||||
<label class="tap chip" for="f-landscape">{t('gallery_filter_landscape', locale)}</label>
|
||||
{groups.map((g) => (
|
||||
<Fragment>
|
||||
<input class="f-input" type="radio" name="gallery-group" id={`f-g-${g}`} />
|
||||
<label class="tap chip" for={`f-g-${g}`}>{g.charAt(0).toUpperCase() + g.slice(1)}</label>
|
||||
</Fragment>
|
||||
))}
|
||||
</fieldset>
|
||||
|
||||
<div class="stage">
|
||||
@@ -618,8 +628,7 @@ const first = cards[0];
|
||||
.chip:hover { color: var(--ink-1); border-color: var(--ink-4); }
|
||||
.f-input:focus-visible + .chip { outline: 2px solid var(--accent); outline-offset: 2px; }
|
||||
.f-input:checked + .chip { color: var(--ink-1); border-color: var(--ink-3); font-weight: 500; }
|
||||
.g-main:has(#f-street:checked) .shot:not([data-group='street']) { display: none; }
|
||||
.g-main:has(#f-landscape:checked) .shot:not([data-group='landscape']) { display: none; }
|
||||
/* 动态分组筛选规则见下方 is:global 块(分组来自数据,构建期生成) */
|
||||
|
||||
/* masonry:stage 是容器查询锚,行高/列距以 stage 宽为基(cqw) */
|
||||
.stage { container-type: inline-size; }
|
||||
@@ -1035,4 +1044,5 @@ const first = cards[0];
|
||||
border-radius: 6px;
|
||||
}
|
||||
.shot img, .thumb img { border-radius: var(--radius-img); }
|
||||
<style is:global set:html={filterRules} />
|
||||
</style>
|
||||
+3
-5
@@ -295,14 +295,12 @@ await page.keyboard.press('Escape');
|
||||
ok('灯箱·Esc 关闭', await page.locator('#lb[open]').count() === 0);
|
||||
|
||||
// 7) 分组筛选(当前库存单组:21 张全 landscape;验证筛选逻辑用街拍=0 反证)
|
||||
await page.click('label[for=f-landscape]');
|
||||
await page.click('label[for=f-g-landscape]'); // 5n:分组筛选动态化(数据派生 id)
|
||||
await page.waitForTimeout(200);
|
||||
const landscapeShown = await page.locator('.shot:visible').count();
|
||||
ok('图库·风景筛选', landscapeShown === 21, `${landscapeShown} 张`);
|
||||
await page.click('label[for=f-street]');
|
||||
await page.waitForTimeout(200);
|
||||
const streetShown = await page.locator('.shot:visible').count();
|
||||
ok('图库·街拍筛选(应为0)', streetShown === 0, `${streetShown} 张`);
|
||||
// 5n:分组筛选动态化——筛选片由数据派生(当前库存仅 landscape 一组,不再有 street 档)
|
||||
ok('图库·筛选片数据派生', (await page.locator('.filters .chip').count()) === 2);
|
||||
await page.click('label[for=f-all]');
|
||||
|
||||
// 8) 书评字体(思源宋)与技术页 TOC
|
||||
|
||||
Reference in new issue
Block a user