fix(cms): 页面文案可编辑性修复(结构压平)+ 样式旋钮退役 + favicon 直传
用户实测「瞬间/书架 说明文字」在后台无法编辑。根因:Sveltia 对「可选对象 嵌套可选对象」的子编辑器渲染缺陷线(sveltia-cms #446/#654,双层嵌套正踩 雷区)——压平为单层可选对象 + markdown 直挂,与已验证可编辑的主页/关于 页同构。顺应用户第二问:样式旋钮(字号/颜色/字体三档)整体退役,排版 并入内联类(Insert▸排版,预览直接可见),视觉基线由 .page-note scoped 样式承载(13px/ink-4,与原默认一致零回归);favicon 从贴路径 string 改为 image 直传(落 public/icons/,yaml 存 /icons/…)。 验证: pnpm astro build exit 0;config-check 226 字段全过;content-lint 53 页 0/0/0;E2E 61/61 Spec: none (用户实测报告的后台编辑 bug 紧急修复) Co-authored-by: GLM-5.3
This commit is contained in:
1 parent
cf331ccf3d
commit
eb5e3f851e
6 files changed
+37
-132
No files matched your search
+7
-17
@@ -1,21 +1,11 @@
|
||||
# 页面标题下的说明文案(瞬间 / 书架):文字、字号、颜色、字体都在这里改。
|
||||
# 缺省即回落内置默认,删字段不会破版。
|
||||
# 说明:color 取 tokens 里的灰阶层级(ink-1..ink-4);font 取排版类白名单里的字体类。
|
||||
# 页面标题下的说明文案(瞬间 / 书架):纯 markdown,后台「页面文案」直接改;
|
||||
# 排版(字体/颜色)用内联类,与文章正文同一套(编辑器 Insert ▸ 排版 可视化选)。
|
||||
# 删字段即回落内置默认,不破版。
|
||||
|
||||
moments:
|
||||
note:
|
||||
zh: 零碎的记录:随手拍、一句话感想、开源动态。
|
||||
en: 'Fragmented notes: casual shots, one-liners, and open-source updates.'
|
||||
style:
|
||||
size: 13
|
||||
color: ink-4
|
||||
font: font-sans
|
||||
note_zh: 零碎的记录:随手拍、一句话感想、开源动态。
|
||||
note_en: 'Fragmented notes: casual shots, one-liners, and open-source updates.'
|
||||
|
||||
books:
|
||||
note:
|
||||
zh: 点一行展开封面与简介 · 置顶的书默认展开
|
||||
en: 'Tap a row for the cover and blurb · pinned books start open'
|
||||
style:
|
||||
size: 13
|
||||
color: ink-4
|
||||
font: font-sans # 与改造前一致;想用楷体改成 font-kai
|
||||
note_zh: 点一行展开封面与简介 · 置顶的书默认展开
|
||||
note_en: 'Tap a row for the cover and blurb · pinned books start open'
|
||||
+12
-56
@@ -154,69 +154,22 @@ collections:
|
||||
label: 瞬间 / 书架 标题下的说明
|
||||
file: content/site/pages.yaml
|
||||
format: yaml
|
||||
hint: 这两页大标题下面那一行字,文字与样式都在这里改
|
||||
hint: 这两页大标题下面那一行字的文字;字体/颜色用编辑器 Insert ▸ 排版 内联标注(预览直接可见)
|
||||
fields:
|
||||
- label: 瞬间页
|
||||
- label: 瞬间页说明
|
||||
name: moments
|
||||
widget: object
|
||||
required: false
|
||||
fields:
|
||||
- label: 说明文字
|
||||
name: note
|
||||
widget: object
|
||||
required: false
|
||||
hint: 支持 markdown 与排版类(字体/颜色)
|
||||
fields:
|
||||
- { label: 中文, name: zh, widget: markdown, required: false }
|
||||
- { label: English, name: en, widget: markdown, required: false }
|
||||
- label: 样式
|
||||
name: style
|
||||
widget: object
|
||||
required: false
|
||||
hint: 留空用全站默认;越界值会被自动夹取(不会破版)
|
||||
fields:
|
||||
- { label: 字号(px,11–16), name: size, widget: number, required: false, value_type: int, min: 11, max: 16 }
|
||||
- label: 颜色
|
||||
name: color
|
||||
widget: select
|
||||
required: false
|
||||
hint: 只开放灰阶层级,避免正文被染色
|
||||
options: [ink-1, ink-2, ink-3, ink-4]
|
||||
- label: 字体
|
||||
name: font
|
||||
widget: select
|
||||
required: false
|
||||
options: [font-sans, font-serif, font-kai, font-huiwen, font-kuaile, font-xiaowei, font-huangyou, font-mashanzheng, font-liujianmaocao, font-smiley, font-neoxihei]
|
||||
- label: 书架页
|
||||
- { label: 中文, name: note_zh, widget: markdown, required: false, hint: '一行小字;排版(字体/颜色)选中文字 → Insert ▸ 排版' }
|
||||
- { label: English, name: note_en, widget: markdown, required: false }
|
||||
- label: 书架页说明
|
||||
name: books
|
||||
widget: object
|
||||
required: false
|
||||
fields:
|
||||
- label: 说明文字
|
||||
name: note
|
||||
widget: object
|
||||
required: false
|
||||
hint: 支持 markdown 与排版类(字体/颜色)
|
||||
fields:
|
||||
- { label: 中文, name: zh, widget: markdown, required: false }
|
||||
- { label: English, name: en, widget: markdown, required: false }
|
||||
- label: 样式
|
||||
name: style
|
||||
widget: object
|
||||
required: false
|
||||
hint: 留空用全站默认;越界值会被自动夹取(不会破版)
|
||||
fields:
|
||||
- { label: 字号(px,11–16), name: size, widget: number, required: false, value_type: int, min: 11, max: 16 }
|
||||
- label: 颜色
|
||||
name: color
|
||||
widget: select
|
||||
required: false
|
||||
options: [ink-1, ink-2, ink-3, ink-4]
|
||||
- label: 字体
|
||||
name: font
|
||||
widget: select
|
||||
required: false
|
||||
options: [font-sans, font-serif, font-kai, font-huiwen, font-kuaile, font-xiaowei, font-huangyou, font-mashanzheng, font-liujianmaocao, font-smiley, font-neoxihei]
|
||||
- { label: 中文, name: note_zh, widget: markdown, required: false, hint: '一行小字;排版(字体/颜色)选中文字 → Insert ▸ 排版' }
|
||||
- { label: English, name: note_en, widget: markdown, required: false }
|
||||
- name: home
|
||||
label: 主页设置
|
||||
file: content/site/home.yaml
|
||||
@@ -309,9 +262,12 @@ collections:
|
||||
hint: '留空 = 内置渐变占位;上传后自动优化为 60px WebP'
|
||||
- label: 站点图标 favicon
|
||||
name: favicon
|
||||
widget: string
|
||||
widget: image
|
||||
required: false
|
||||
hint: 'public 下的路径,如 /icons/my.ico 或 /icons/my.svg;留空回退 /favicon.svg。经「图标」媒体库上传'
|
||||
# media_folder 按 file 集合语义相对 content/site/ 解析(写 content/site/assets 会嵌套翻倍——5l 实测教训)
|
||||
media_folder: ../../public/icons
|
||||
public_folder: /icons
|
||||
hint: '直接上传 .svg/.ico/.png(建议 64–512px 方图);留空回退 /favicon.svg'
|
||||
- label: 社交链接(顺序即展示顺序)
|
||||
name: socials
|
||||
widget: list
|
||||
|
||||
@@ -78,7 +78,7 @@ const L = {
|
||||
{pinnedCount > 0 && <span class="pinned-count mono">{pinnedCount} {L.pinned}</span>}
|
||||
</h1>
|
||||
<!-- 标题下说明:由 content/site/pages.yaml 的 books.note/style 驱动 -->
|
||||
<p class:list={['page-note', note.className]} style={note.style} set:html={note.html} />
|
||||
<p class="page-note" set:html={note.html} />
|
||||
|
||||
<ul class="books">
|
||||
{items.map((b) => (
|
||||
|
||||
@@ -41,7 +41,7 @@ const iso = (d: Date) => d.toISOString();
|
||||
<BaseLayout title={`${t('moments_title', locale)} · wait4x`}>
|
||||
<h1 class="page-title">{t('moments_title', locale)}</h1>
|
||||
<!-- 标题下说明:文案/字号/颜色/字体均由后台「页面文案 → 页面说明」控制(site-content.pageNote) -->
|
||||
<p class:list={['page-note', note.className]} style={note.style} set:html={note.html} />
|
||||
<p class="page-note" set:html={note.html} />
|
||||
<!-- 整页计数(Phase 5h T4):这是本页唯一的计数点(page 模式);
|
||||
每条瞬间卡片底部的徽标是 readonly,读者开评论不会虚增浏览数 -->
|
||||
<ViewMeta class="mom-page-meta" pageKey={locale === 'en' ? '/en/moments/' : '/moments/'} {locale} variant="chip" />
|
||||
|
||||
@@ -163,16 +163,14 @@ const _exifShapeOk: _PhotoExifOut extends PhotoExif
|
||||
: never
|
||||
: never = true;
|
||||
|
||||
/** 页面说明文案的字段形状(瞬间 / 书架共用):文本双语 + 三档样式 */
|
||||
/**
|
||||
* 页面说明文案的字段形状(瞬间 / 书架共用):扁平双语。
|
||||
* 样式旋钮(size/color/font)已退役——排版统一走内联类(Insert▸排版),
|
||||
* 也避开了 Sveltia「可选对象嵌可选对象不渲染子编辑器」的缺陷结构(#446/#654)。
|
||||
*/
|
||||
const pageNoteSchema = z.object({
|
||||
note: bilingual.optional(),
|
||||
style: z
|
||||
.object({
|
||||
size: z.number().optional(),
|
||||
color: z.string().optional(),
|
||||
font: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
note_zh: z.string().optional(),
|
||||
note_en: z.string().optional(),
|
||||
});
|
||||
|
||||
export const collections = {
|
||||
|
||||
+9
-48
@@ -164,23 +164,11 @@ export async function renderRich(md: string, opts: { inline?: boolean } = {}): P
|
||||
return html.replace(/^\s*<p>/i, '').replace(/<\/p>\s*$/i, '').trim();
|
||||
}
|
||||
|
||||
export interface PageNoteStyle {
|
||||
/** 像素字号(已夹取到 11–16) */
|
||||
size: number;
|
||||
/** 已过白名单的 ink 档位(ink-1..ink-4) */
|
||||
color: string;
|
||||
/** 已过白名单的排版字体类(font-sans 等) */
|
||||
font: string;
|
||||
}
|
||||
|
||||
export interface PageNote {
|
||||
/** 已渲染的行内 HTML(note 走 renderRich inline,避免 <p> 套 <p>) */
|
||||
html: string;
|
||||
/** 直接可挂的行内样式串(字号 + 颜色) */
|
||||
style: string;
|
||||
/** 字体类名(白名单内),与 style 一起挂到元素上 */
|
||||
/** 挂到元素上的类名(基础 .page-note;字体/颜色由文案内联排版类表达) */
|
||||
className: string;
|
||||
resolved: PageNoteStyle;
|
||||
}
|
||||
|
||||
/** 标题下说明文案的默认值(= 改造前硬编码的两句;删 yaml 字段即回到这里) */
|
||||
@@ -195,20 +183,12 @@ const PAGE_NOTE_DEFAULTS = {
|
||||
},
|
||||
} as const;
|
||||
|
||||
/** 颜色白名单:只认灰阶 ink 档位(后台可填任意值,但绝不能把正文染成任意颜色/破对比度) */
|
||||
const NOTE_COLORS = ['ink-1', 'ink-2', 'ink-3', 'ink-4'] as const;
|
||||
/** 字号夹取:<11px 读不清、>16px 会盖过正文层级 */
|
||||
const NOTE_SIZE_MIN = 11;
|
||||
const NOTE_SIZE_MAX = 16;
|
||||
const NOTE_SIZE_DEFAULT = 13;
|
||||
|
||||
/**
|
||||
* 页面标题下说明文案(瞬间 / 书架)+ 可选样式。
|
||||
* 后台可改文字与字号/颜色/字体;样式全部经白名单校验后回落,非法值不会破版:
|
||||
* · size 夹取 11–16(缺省 13)
|
||||
* · color 只认 ink-1..ink-4(其它回落 ink-4)
|
||||
* · font 只认排版类白名单(其它回落 font-sans)
|
||||
* 无 yaml 字段时文字回落内置默认,与改造前逐字一致(零回归)。
|
||||
* 页面标题下说明文案(瞬间 / 书架):纯 markdown,排版用内联类(与正文同一套白名单)。
|
||||
* 样式旋钮(size/color/font)已退役——视觉基线(13px / ink-4 / sans)由两页
|
||||
* .page-note 的 scoped 样式承载;要变字体/颜色就在文案里写内联排版类(Insert▸排版),
|
||||
* 后台预览直接可见。结构同时压平为 note_zh/note_en,避开 Sveltia
|
||||
* 「可选对象嵌可选对象不渲染子编辑器」的缺陷结构(#446/#654)。
|
||||
*/
|
||||
export async function pageNote(
|
||||
page: 'moments' | 'books',
|
||||
@@ -216,29 +196,10 @@ export async function pageNote(
|
||||
): Promise<PageNote> {
|
||||
const entry = await getEntry('sitePages', 'pages');
|
||||
const data = entry?.data?.[page];
|
||||
const text = pickBi(data?.note, PAGE_NOTE_DEFAULTS[page], locale);
|
||||
const fallback = PAGE_NOTE_DEFAULTS[page];
|
||||
const text = (locale === 'en' ? data?.note_en : data?.note_zh) || fallback[locale];
|
||||
const html = await renderRich(text, { inline: true });
|
||||
|
||||
const rawSize = data?.style?.size;
|
||||
const size =
|
||||
typeof rawSize === 'number' && Number.isFinite(rawSize)
|
||||
? Math.min(NOTE_SIZE_MAX, Math.max(NOTE_SIZE_MIN, Math.trunc(rawSize)))
|
||||
: NOTE_SIZE_DEFAULT;
|
||||
|
||||
const rawColor = data?.style?.color?.trim() ?? '';
|
||||
const color = (NOTE_COLORS as readonly string[]).includes(rawColor) ? rawColor : 'ink-4';
|
||||
|
||||
const rawFont = data?.style?.font?.trim() ?? '';
|
||||
const font = ALLOWED_PAGE_FONTS.has(rawFont) ? rawFont : 'font-sans';
|
||||
|
||||
// 字体必须写成 inline font-family:global.css 的 .font-* 类都挂在富文本容器作用域内
|
||||
// (.prose-*/.rich/.mom-text/.prose-body),裸 .page-note 拿不到,加类无效(实测)
|
||||
return {
|
||||
html,
|
||||
style: `font-size:${size}px; color: var(--${color}); font-family: ${fontVarFor(font)};`,
|
||||
className: font,
|
||||
resolved: { size, color, font },
|
||||
};
|
||||
return { html, className: 'page-note' };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user