文档内容上传操作手册

内容上传操作手册

往站点里添加内容的完整流程:五种内容类型各自怎么放、蓝图怎么从 UE 复制出来、图片批处理、大文件规则、发布流程与排错对照表。

Markdown可在线阅读,也允许下载2026/9/16 更新站点使用操作手册

下载这份文档

这份手册讲的是:怎么往站点里加东西。 覆盖五种内容(项目 / 模块 / 蓝图 / 图集 / 文档)、图片处理、大文件规则、发布流程与排错。

站点是纯静态的,没有在线后台 —— 但有一个只在你本机跑的内容编写器 (admin.bat,监听 127.0.0.1:4322)。图片可以直接在编写器里多选上传, 文件落到 public/media/ 下,它同时会替你量好宽高。 除此之外的内容仍然是磁盘上的文件:「上传」= 把文件放到 content/ 里,然后构建。


目录

  1. 先开服务
  2. 通用规则(先读这几条)
  3. 内容都放在哪
  4. 项目
  5. 功能模块
  6. 蓝图(含 UE 里怎么复制)
  7. 图集
  8. 文档
  9. 3D 模型
  10. 图片处理流水线
  11. 大文件:什么时候必须走对象存储
  12. 发布
  13. 排错对照表
  14. 上传前检查清单

1. 先开服务

双击根目录的批处理文件即可,不用敲命令:

双击这个 干什么 地址
admin.bat 写内容(本文的主角) http://127.0.0.1:4322
dev.bat 看成品(自动重建 + 起服务) http://127.0.0.1:4321
preview.bat 只构建一次再起服务(不自动重建) http://127.0.0.1:4321

三点注意:

  • 两个窗口都要开着,关掉窗口服务就停了。
  • dev / preview 占同一个端口(4321)不能同时开;admin 是 4322,可以和任一个并存。
  • dev.bat 会自动重建:你在编辑器里保存,它 1~2 秒后重新构建一次, 刷新浏览器就能看到新内容(窗口里会打一行 ✓ 重建完成)。 搜索索引也是每次重建都更新的,所以看搜索效果也用 dev.bat 就行。

写作时推荐组合:dev.bat(4321 窗口)+ admin.bat(4322 窗口)。

⚠️ 别用 pnpm dev:hmr(那是 Astro 自带的开发服务器)。它对 content/ 的改动 不可靠 —— 改完正文刷新,页面可能还是旧的,而且不报任何错。 dev.bat 走的是“改了就重新构建”,慢 1 秒但看到的一定是当前内容。


2. 通用规则(先读这几条)

2.1 slug 只用英文小写 + 连字符

slug 是网址的一部分,content/projects/tomato/ 就对应 /projects/tomato/。

✅ tomato   marathon-volt   dev-doc   bp-farmer
❌ 番茄     Marathon_Volt   文档1     中文名

一旦发布不要改 slug —— 它就是这篇内容的身份,改了等于换了个人,外链和搜索记录全断。

例外:蓝图图表的文件名可以是 UE 那种大小写下划线风格 (BP_Farmer_Look、Blueprint_WallSconce_UserConstructionScript),因为它不进网址。

2.2 order 决定排序

数字小的排前面,同层级内比较。习惯上按 10、20、30 留间隔,方便以后往中间插。

2.3 draft: true = 存草稿

写一半的东西勾上草稿,构建时整篇不上线,不会在列表里出现,也不会被搜索索引。 写完了把勾去掉再构建。

2.4 图片必须写 w / h

<img> 不写宽高,图片加载完会把下面的内容顶下去(页面抖动,也就是 CLS 掉分)。 用第 10 节的脚本会自动量好给你。

2.5 单个文件不超过 25 MiB

这是托管平台的硬上限。超了必须走对象存储,见第 11 节。

2.6 编辑器里 Ctrl/Cmd + S 就是保存

有未保存改动时切走其它条目会拦你一下。覆盖已有文件前会自动备份到 tmp/admin-backup/。


3. 内容都放在哪

content/
  projects/<项目slug>/
    project.md                       项目概述
    modules/<模块slug>.md            功能模块
    blueprints/<图表名>.t3d          蓝图原文(UE 里复制的)
    blueprints/<图表名>.json         蓝图元数据(可选)
    code/<模块slug>/<文件名>         该模块的源码(会显示在模块页「源码」区块)
    mindmaps/<名称>.md               思维导图源(规划中,现在还不会显示)
  gallery/<图集slug>/
    album.md                         图集元数据 + 图片清单
  docs/<slug>.md                     文档
  models/<slug>.md                   3D 模型元数据
public/
  media/<图集slug>/*.webp            图片(加工后的)
  media/models/*.glb                 3D 模型本体
  files/*                            允许下载的文档副本

编辑器左侧就是按这六类分组的:项目 / 模块 / 蓝图 / 图集 / 文档 / 模型。 点右上角的「新建 XXX」建新的,点列表里的条目改已有的。


4. 项目

一个项目 = 一个目录,project.md 是它的门面。

用编辑器

  1. 左侧「项目」组右上角点 「+ 新建 项目」
  2. slug 填英文(如 tomato),确定
  3. 右侧表单填字段 → Ctrl/Cmd + S 保存

字段

字段 必填 说明
title ✅ 中文标题,如 钛心番茄
summary 一句话摘要,出现在卡片和搜索结果里
order 排序
cover 封面图路径,如 /media/tomato/cover.webp(每件作品都要有封面)
workspace UE 里的工作区名,如 U_Tomato
engine 默认 Unreal Engine 5
tech 技术形态,一行一个:C++ / 蓝图 / 数据表
tags 标签,复用为主,全站控制在 30 个以内
status 设计中 / 进行中 / 已完成 / 搁置
updated 更新日期
draft 草稿

正文部分(--- 下面)就是普通的 Markdown:标题、表格、代码块、引用都能用。

kind: project 这个字段编辑器会自动填,不用管。


5. 功能模块

模块挂在某个项目下面,对应 /projects/<项目>/modules/<模块>/。

用编辑器

  1. 「模块」组点 「+ 新建 模块」
  2. 先下拉选一个项目,再填模块 slug(如 farming)
  3. 填字段 → 保存

字段

和项目基本一样(title / summary / order / cover / workspace / engine / tech / tags / status / updated / draft),区别是:

  • 必须选所属项目(编辑器会写进 project: 字段)
  • 不需要 kind,编辑器自动填成 module

模块页会自动出现在所属项目的左侧导航里,不用手工维护目录。

附:把 C++ 源码放上去

模块页底部有一个「源码」区块,读的是这个目录:

content/projects/<项目>/code/<模块slug>/

例:

content/projects/tomato/code/farming/
  FarmingComponent.h
  FarmingComponent.cpp
  PlantData.h

目录里有什么就显示什么,全部按 C++ 高亮,不用在正文里写代码块。

几条规矩:

  • 目录名必须和模块 slug 一致 —— modules/farming.md 对应 code/farming/,对不上就看不见。
  • 跳过 _ 开头的文件 —— 不想让某个文件出现就给它加个下划线前缀。
  • 目录不存在不会报错,只是模块页不显示「源码」区块。

把代码直接贴在模块正文里(用围栏代码块)也行,两种方式并存。 code/ 目录的好处是源码能在版本管理里单独跟踪,改代码不用动 md。

思维导图(暂未上线)

mindmaps/<名称>.md 这个位置在目录结构里留好了,计划用 markmap 直接渲染 Markdown 源文件。 但页面还没接(属于后续里程碑)—— 现在放进去不会显示,先别用它。


6. 蓝图(含 UE 里怎么复制)

蓝图是把 UE 里的节点图搬到网页上,能缩放平移、还能复制回 UE 里继续用。 这是这个站最特别的一环,步骤稍微多一点。

6.1 先在 UE 里复制

  1. 打开要存档的蓝图,进入目标图表(Event Graph / 构造脚本 / 函数 / 材质图……)
  2. 框选要存档的节点(想全要就 Ctrl + A)
  3. Ctrl + C —— 这时候剪贴板里已经是 T3D 文本了

复制出来的不是图片,是一段带节点坐标和连线信息的文本。 想确认的话,先粘到记事本里看一眼,应该能看到 Begin Object、NodePosX=、LinkedTo=(...) 这类内容。

6.2 用编辑器粘贴

  1. 「蓝图」组点 「+ 新建 蓝图」
  2. 下拉选所属项目
  3. 填图表名(文件名,可以是 BP_Farmer_Look 这种大小写下划线风格)
  4. 在弹出的文本框里 Ctrl + V 粘贴 T3D 原文
  5. 填字段 → 保存

编辑器会生成两个文件:<图表名>.t3d(原文)和 <图表名>.json(元数据)。

6.3 字段

字段 必填 说明
title ✅ 显示用的中文标题
graph 图表全名,如 BP_Farmer:Look,显示在查看器标题栏
module 归属到哪个功能模块(对应 modules/<文件名>,可留空)
graphType BLUEPRINT / MATERIAL / MATERIAL FUNCTION / ANIM GRAPH —— 影响节点配色,材质图别选成 BLUEPRINT
summary 这张图做了什么(很有用,别空着)
order 排序
height 画布高度 px,默认 640
zoom 初始缩放,负值 = 放大。留空交给库自己算
tags / draft 标签 / 草稿

source(T3D 原文)是构建时自动注入的,不要手写。

6.4 手写格式(不用编辑器时)

content/projects/tomato/blueprints/BP_Farmer_Look.t3d    ← T3D 原文,直接整段贴进去
content/projects/tomato/blueprints/BP_Farmer_Look.json   ← 元数据,可选

.json 长这样:

{
  "title": "农民 · 视线检测",
  "project": "tomato",
  "graph": "BP_Farmer:Look",
  "graphType": "BLUEPRINT",
  "summary": "每帧从摄像机往前打一条射线,命中可交互物就更新准星。",
  "order": 10,
  "height": 720,
  "tags": ["交互", "射线检测"]
}

没有 .json 也能跑 —— 标题会从文件名推断(下划线换成空格)。但建议写,摘要那一栏很有价值。


7. 图集

一个图集 = 一个目录 + 一份 album.md,页面上是「左边图片流、右边说明面板」。

7.1 图片怎么进去(编写器里多选上传)

打开 http://127.0.0.1:4322 → 左边选「图片」→ 选一个图集(或先新建)→ 在「图片清单」那一栏点 「⤒ 上传图片(可多选)」,一次把要用的图全选上。

上传时编写器自动做四件事:

自动做的 说明
落盘 写进 public/media/<图集slug>/,一次一张排队上传,哪张失败只报哪张
量宽高 直接读文件头(PNG / JPEG / WebP / GIF / AVIF),不用你右键看属性
改名 中文名 / 空格会被清成 ASCII 安全名;全中文名退回 img-<时间戳>;撞名自动加 -2
建条目 往 images: 里追加一条,默认「整栏、居中」

想用原始做法(先把图片批量加工成 1600px 的 webp 再入库)也行,见 7.4。

7.2 排版:顺序 + 占宽 + 对齐

编辑器里图片清单是三栏,从左到右各管一件事:

[① 清单(拖动排序)]  [② 页面排布预览]  [③ 这张图的表单(改文字/占宽)]
  1. 顺序 —— 拖动清单里的 ⠿ 手柄(或点 ▲ ▼)改行的先后。 清单里的行序就是页面上的顺序 —— 想让它出现在上面就往上拖。

  2. 占宽 —— 点任意一张,右栏选 layout:

    值 效果
    full 整栏,独占一行(默认)
    two-thirds 占三分之二栏
    half 半栏 —— 连着 2 张半栏图并排
    third 三分之一 —— 连着 3 张并排
    quarter 四分之一 —— 连着 4 张并排
  3. 对齐 —— 比整栏窄的图,选 align:center / left / right。 只在“一行没排满”时有区别(铺满时靠左靠右长得一样)。

“哪几张凑一块”由“顺序 + 占宽”决定,不需要拖坐标:

  • 连着几张同档位的图自动排成一行,档位一变就另起一行;
  • 分组小标题会打断一行 —— 中间插了一条分组标题,两侧的图不会并排 (所以想让两张图并排,它们得在同一个分组里、并且挨着);
  • 中栏那个「页面排布预览」实时按这套规则画出来,它长什么样,页面上就是什么样。

手机(窄于 860px)上一律退回整栏 —— 半宽的图在手机上没法看。

7.3 一次上传多张之后,还要做什么

不用给每张图补文字。alt 与 caption 都留空时,页面会退回用图集标题当替代文字, figcaption 直接不渲染。只有你想给某张图加一句小注时才点开它填。

批量工具(都在「图片清单」那一栏):

按钮 干什么
+ 手动加一条 手工填一条(图已经在 public/ 里、只是没进清单时用)
扫描 public/media/… 把目录里已有的图列成条目(手建目录、或从别处拷图进来时用)
⇅ 按文件名重排 按路径字母序重排(文件名是 01/02/… 时最有用)
⟲ 自动补宽高 把清单里所有缺 w/h 的条目挨个从文件量一次

7.4 批量加工(把原图压成 1600px 的 webp)

要控制输出体积时用脚本(会生成 webp 并量好宽高、打印可直接粘贴的清单):

node scripts/ingest-gallery.mjs <源目录> <图集slug> [文件名前缀] [最大边]

例:

node scripts/ingest-gallery.mjs "C:/Users/KFC50/Desktop/Marathon" marathon-volt alex-tang-volt 1600
  • 源目录只读,脚本绝不改动你的原图
  • 输出到 public/media/<图集slug>/
  • 跑完打印一段可直接粘贴的 images 清单

7.5 PNG 会被自动减重(构建期)

PNG 可以直接传(上传、页面渲染、下载都没问题)。构建时会多跑一步 scripts/optimize-png.mjs:把 public/media/ 下超过 200 KB 的 PNG 转出一份同目录同名 .webp(带透明的走无损,不透明走有损 90),并打印一张对照表。

三点要知道:

  • 它不删原 PNG、也不改你的 content/。转换是可逆的。
  • 想用上小体积那份,就把该条目的 src 从 .png 改成 .webp(编辑器里改一下即可)。 构建日志会把“哪些图有更小的 webp 版本”列出来。
  • 转换是幂等的:源图没变就不会重做,所以每次构建都跑也不会变慢。

7.6 字段

字段 必填 说明
title ✅ 图集标题
titleEn 英文名,标题下方那行小字
summary 简介,显示在右侧面板顶部
cover 列表卡片的封面图路径
order 排序
credit 来源 / 版权说明
tags 标签
images 图片清单(见下)
updated / draft 更新日期 / 草稿

panel(说明面板配色)已废弃:右侧面板现在直接吃站点的设计 token, 跟其它页面一套观感,页面上也不再提供配色调节。老的 album.md 里如果还留着 panel: 字段,构建不会报错、页面也不会读它,编辑器不再显示这个字段。

images(图片清单):

images:
  - src: /media/marathon-volt/xxx.webp    # 路径(相对 public/)
    w: 1600                                # 宽(上传/扫描时自动量好)
    h: 900                                 # 高
    alt: 图片内容的文字描述                 # 可留空 —— 留空则用图集标题
    caption: 图下小注                       # 可留空 —— 留空则不渲染 figcaption
    group: 携行箱                           # 分组名,同组会插一个分组小标题
    layout: half                           # full / two-thirds / half / third
    align: center                          # center / left / right

数组顺序 = 页面上从上到下的顺序。w/h 仍然建议填全:不填的话图一加载完会把 下面的内容顶下去(首屏抖动),这也是上传时自动量高宽的原因。


8. 文档

一篇文档 = content/docs/<slug>.md,slug 即网址(foo.md → /docs/foo/)。

用编辑器

  1. 「文档」组点 「+ 新建 文档」
  2. 填 slug、填字段、写正文 → 保存

字段

字段 必填 说明
title ✅ 标题
summary 摘要,列表卡片和页面描述都用它
order 排序
format md / pdf / html / text —— 决定用哪个查看器(默认 pdf)
docMode 只读强度,见下
file 下载链接,相对 public/,如 /files/foo.md
tags / updated / draft 标签 / 更新日期 / 草稿

docMode 三档:

值 含义
public 在线阅读 + 下载按钮
readonly 能看不能拿(页面不提供任何下载入口)—— 默认
strict 同上,另外禁右键、禁选中

下载按钮的出现条件是 docMode: public 且 file 填了,两个条件缺一不可。 想给下载就把文件放到 public/files/ 下(用英文文件名),再填 file: /files/xxx.md。

侧栏目录是自动的

页面左边的目录从渲染结果算出来,不用手写。 手写一份目录也行(像自带那篇开发文档那样),但不必 —— 手写锚点容易和实际标题 id 对不上。


9. 3D 模型

一个模型 = 一个内容文件 + 一个 glb 文件:

content/models/<slug>.md              ← 条目:标题、摘要、封面、指向 glb
public/media/models/<文件名>.glb      ← 模型本体

两处的名字互相独立,靠 .md 里的 src: 连起来。详情页里模型能拖拽旋转、 滚轮缩放;右侧那些规格数字(面数、顶点数、包围盒)是构建时直接读 glb 算出来的, 不用你手填。

9.0 想改名字 / 加新模型,改哪几个地方

这是最容易问的一个问题,一次说清。<slug> 决定网址,所以改名 = 换网址。

你想改的 要动的地方 会不会换网址
显示标题(页面上那行大字) .md 里的 title: 不会 ✅ 最常改的就是这个
网址 / 文件名 .md 的文件名(crate-01.md)→ 网址 /models/crate-01/ 会
glb 的文件名 public/media/models/ 里的文件 + .md 里的 src: / cover: 不会
封面图 .md 里的 cover: 不会

只改标题不用改名:.md 里写 title: 我的新模型 就够了, 文件名和网址可以一直叫 sample-table 之类的旧名字 —— 只是看起来有点怪。 改文件名(slug)才会换网址,旧网址会 404(站点没有重定向机制)。

要改 slug 时,四步一起做(漏一步就出现“模型打不开”或“封面破图”):

  1. 把 content/models/旧名.md 复制成 content/models/新名.md,再删掉旧的 (新建文件、别原地改名 —— 项目没有版本控制,出事了只有 tmp/admin-backup/ 能救);
  2. 把 public/media/models/旧文件.glb 复制成新名字(glb 的名字跟 slug 一致最省心);
  3. 打开新的 .md,把 src: 与 cover: 改成新路径;
  4. 重新构建。首页第二屏的数据会自动跟着变(见下面 9.7), 不用再手改任何“详情页文案”。

加一个全新模型(crate-01 为例):

# 1) 把模型本体拷进去(英文名、连字符)
copy 我的模型.glb  public/media/models/crate-01.glb
# 2) 编辑器里新建条目
#    「3D 模型」组 → 「+ 新建 3D 模型」→ slug 填 crate-01
#    然后「渲染用模型」那栏从下拉里选 /media/models/crate-01.glb
# 3) 填标题 / 摘要 / 封面 → 保存 → 构建
pnpm build

加完不用再改别的地方 —— 首页卡片、列表页、档案阵列都是从 content/models/ 一份数据长出来的(9.7 讲的就是这条)。

9.1 先准备文件

模型本体放到:

public/media/models/<英文文件名>.glb
  • 文件名用英文 + 连字符(crate-01.glb),理由和图片一样(见 2.1)。
  • 单个文件超过 25 MiB 就必须放 R2(见第 11 节),这时 src 直接写完整 URL。
  • 格式优先用 .glb(二进制 glTF,模型 + 贴图打包成一个文件)。 .gltf + 外挂贴图那种也能用,但要把贴图一起拷进去、路径还得对得上,容易漏 —— 导出时选「glb」最省事。

9.2 用编辑器

  1. 「模型」组点 「+ 新建 3D 模型」
  2. 填 slug(如 crate-01),确定
  3. 右侧表单里「渲染用模型」那栏,从下拉里选刚放进去的 glb (编辑器会自动扫描 public/media/models/,所以先放文件再填这一栏)
  4. 填字段 → Ctrl/Cmd + S 保存

顺序反过来也行(先建条目后放文件),但 src 是必填 —— 空着保存不会立刻报错, 重新构建时才会指出来。

9.3 字段

字段 必填 说明
title ✅ 中文标题
titleEn 英文名,卡片标题下面那行小字
src ✅ 渲染用的模型文件:/media/models/xxx.glb,或 R2 的完整 URL
srcNote 这个文件做了什么处理,如「面数减半、贴图 2K WebP」
original 原始版文件(可选)。填了下载区会多出一行「原始版」
originalNote 原始版说明
summary 一句话摘要,卡片和搜索结果用它
cover 封面图。留空也能用 —— 列表卡片会用内置占位(深色渐变 + 线框立方体 + 面数)
viewer 外观参数,见 9.4
order / tags / updated / draft 排序 / 标签 / 更新日期 / 草稿

为什么没有 stats(规格)字段:面数、顶点数、材质数、贴图数、包围盒、文件体积 全部是构建时读 glb 现算的,和文件永远一致,手填一份反而会写错、会过期。 (schema 里留了个 stats 覆盖位,只在模型放 R2、构建期读不到本地文件时才用得上,日常别碰。)

9.4 viewer(外观参数)

全部留空就用默认值,不影响渲染。 想微调时:

子字段 默认 说明
autoRotate false 自动旋转。一页里几个模型同时转会互相抢注意力
orbit sphere sphere = 上下左右都能转;turntable = 只能绕圈,不会翻到脚底
zoom true 允许滚轮缩放。关掉可以避免「鼠标停在模型上、页面滚不动」
theta 0 初始水平角(度)。0 = 正对相机
phi 初始俯仰角(度)。90 = 平视;小一点 = 从上往下看
radius 初始距离。一般留空,由库按模型尺寸自动取
exposure 1 整体偏暗 / 偏亮时微调
shadow 阴影强度。0 = 关掉地面阴影
environment 环境贴图路径(HDR / EXR 的 public 路径)。留空用内置的中性棚拍光
transparent true 透明背景,模型直接坐在站点底色上

9.5 页面上是什么样

  • 列表页 /models —— 封面卡网格。这一页不加载 3D 库,所以模型再多也不会拖慢列表。
  • 详情页 /models/<slug>/ —— 模型区滚进视口才开始加载 3D 库,加载时先显示一个转圈占位。 左下角工具条有三个按钮:重置视角 / 自动旋转 / 全屏。
  • 规格表 —— 面数、顶点数、材质、贴图、包围盒、文件体积;空的行不显示。
  • 下载区 —— 渲染版必有一行;original 填了才会多出「原始版」那一行。

手机上用手指转模型时,页面纵向滚动优先,不会出现「想滚页面结果在转模型」的尴尬。

3D 库是自托管的(放在 public/vendor/three-wire/),不走任何 CDN。 首次打开详情页会有约 0.9 MB 的下载,之后按 7 天缓存。这是它唯一“重”的地方。

9.5.1 视口画的是「形体 + 布线」

详情页的视口画两层:中性黏土的面 + glb 里真实的三角网特征边。 页面上没有「线框 / 实体」切换按钮 —— 两层同时画着,没有可切的。

为什么不做切换:DCC 里的“线框视图”是显示模式,不会导出进 glb,文件里只有三角面; 而作者要在这里展示的是“布线怎么撑起形体”,面与线缺一不可。 顺带三个好处:文件小、放大不糊、低模高模都能看清结构。

但线不能单独存在:只画线不画面,模型就只剩一团线条、完全看不出形状 (用户 2026-09-20 原话:“线框之间的面还是需要保留的,我只是想展示布线, 不是为了展示线框,这样完全无法看清模型的形状了”)。所以视口画的是两层:

层 是什么 怎么来的
面 中性黏土材质,只吃光照 直接复用 glb 的几何,不加载原始材质/贴图
线 glb 里真实的三角网边 EdgesGeometry,只取夹角超过 22° 的特征边

为什么不把三角剖分线全画出来:高模上那是几万条线,密到糊成一块剪影, 反而看不见形体。特征边阈值只保留“结构上的折线”,这才是布线该有的读法。 想看更密或更疏:改 public/vendor/three-wire/wireframe-runner.js 里的 edgeThreshold(默认 22 度,调小 = 线更多)。

9.6 手写格式(不用编辑器时)

content/models/crate-01.md
public/media/models/crate-01.glb

crate-01.md 长这样 —— 现成可读的例子是 content/models/chibi-model.md:

---
title: 货箱
titleEn: Supply Crate
summary: 一个低面数货箱,用来测贴图和金属度的表现。
order: 10
cover: /media/models/crate-01.webp
src: /media/models/crate-01.glb
srcNote: Draco 压缩后 1.2 MB,贴图 2K WebP
tags: [道具, 硬表面]
viewer:
  autoRotate: true
  orbit: turntable
  phi: 75
updated: 2026-09-16
---

正文就是普通 Markdown —— 建模过程、参考图、遇到的坑,都能写。

9.7 首页那一屏会自动跟着变(不用手改)

首页第二屏(滚轴选页 + 三维档案阵列)里的卡片、右侧说明面板、点进去的详情, 全部来自 content/ —— 你改完内容重新构建,它就跟着变了。

具体是这样接起来的:

环节 谁产出 什么时候
content/ 你写的(唯一真源) 随时
档案数据 spike/three-archive/gen-archives.mjs 从 content/ 收一遍 每次构建时自动跑
首页右栏 / 卡片 / 无 WebGL 回退 读那份档案数据 页面加载时
三维层(1.2 MB 的包,数据是烘进包里的) 上一步数据变了才重出 同上,自动

所以:加了模型 / 删了图集 / 改了标题 → 跑一次 pnpm build 就够, 不需要去别的地方“同步写一遍”。构建日志里会看到这一行:

档案同步:首页数据已是最新(6 条),无需重出

如果它说的是「内容变了,重出首页三维包」,就说明这次真的重新生成了一遍 (数据一样时不会重出,所以日常构建不会因此变慢)。

以前这一步是手动的,而且漏了会很难查:内容删了首页还在讲旧的, 或者两份数据下标错位 —— 点卡片显示别的项目、详情跳错页。 现在它是构建流水线里的一步,并且会自检“两份数据是不是同一代”。


10. 图片处理流水线

图片是唯一需要“加工”一步的内容,单独说清楚。能自动的都自动了, 下面从“最省事”到“最可控”排三种做法。

最省事:在编写器里多选上传(推荐)

admin.bat → 「图片」→ 选中图集 → 「⤒ 上传图片(可多选)」。 落盘、量宽高、清洗文件名、追加条目全部自动,见 7.1。

  • 超过 25 MiB 的图会被拒(Cloudflare Pages 单文件上限),报错里会写清是哪一张。
  • 上传的是原图尺寸。想要小体积,见下面的减重与批量两条。

自动减重:构建期给大 PNG 补一份 webp

构建流水线里的一步,不用你手动跑(也可以单独跑 pnpm images:optimize):

命令 干什么
pnpm images:optimize 超过 200 KB 的 PNG → 同目录同名 .webp(幂等,源图没变就跳过)
pnpm images:optimize --force 不管有没有,全部重转
pnpm images:optimize --min=120 阈值改成 120 KB
pnpm images:optimize --dry 只报告,不写文件

它不改 content/:转换结果只是多出一个同名 .webp 文件,想用就自己把 src 改成 .webp(PNG 原图可以留着供下载)。构建日志会列出所有“已经有更小 webp 版本”的图。

批量(图集用这个)

node scripts/ingest-gallery.mjs <源目录> <图集slug> [文件名前缀] [最大边]
参数 说明
源目录 原图所在目录,只读,不会被改动
图集slug 输出到 public/media/<slug>/,和 content/gallery/<slug>/ 对应
文件名前缀 只挑文件名以它开头的图(留空 = 全部)
最大边 长边缩到多少 px,默认 1600

跑完会打印 JSON 片段,直接贴进 album.md 的 images: 里。

单张(封面图之类)

手工放到 public/media/<图集>/ 也行,但必须自己确认宽高并写进引用它的地方。 编辑器里「封面图」那栏填的是路径(如 /media/tomato/cover.webp), 项目卡片的封面裁切由页面处理,不用你裁。

三条规矩

  1. w / h 一定要写 —— 不写就让页面抖(CLS)。 上传与扫描会自动量;清单里剩下的空值可以用「⟲ 自动补宽高」一键补。
  2. 文件名用英文 + 连字符 —— 中文名进 URL 会变成一长串 %E4%B8%AD%E6%96%87,难看且容易出错。 (上传时会自动清成 ASCII 安全名,但自己命名时也照这条来。)
  3. 原图留在源目录,别往仓库里塞 —— 加工后的 webp 才进 public/。

11. 大文件:什么时候必须走对象存储

单个文件超过 25 MiB 就必须放对象存储(R2),不能进前端仓库 —— 这是托管平台的硬上限, 也是整套架构的分界线。

现实情况(重要):

  • 目前对象存储的流水线还没接(属于 M4)。所以在接上之前,大于 25 MiB 的文件暂时放不进来。
  • 现在能放的:图片(加工后)、中小体积的 PDF、文本类文档、T3D 蓝图文本(通常几十 KB~几 MB)。
  • 视频、大模型文件、大音频要等 M4 完成,或者你先把文件放别处、页面里写外链。

遇到大文件时别硬塞 —— 构建会失败,而且就算塞进去了,访客下载也会烧掉带宽。


12. 发布

内容写完,看一遍,再发布。

本地看

双击 preview.bat(它会先构建再起服务),打开 http://127.0.0.1:4321。 写内容的过程中用 dev.bat 更顺手。

用编辑器改完,预览里为什么没变?

这是踩得最多的一脚,先说清楚:

编辑器保存的只是磁盘上的源文件;预览服务读的是 dist/(构建产物)。 两者之间隔着一道「构建」。所以改完保存后:

  • 预览页还是旧的 —— 正常,不是保存失败;
  • 刷新也没用 —— 因为 dist/ 里那份文件从头到尾没被改过,刷新多少次读到的都是同一份旧产物。

正确的看法是这三步:

  1. 编辑器右上角会出现黄色提示 「有改动未构建」(看到它 = 保存成功了,但站点还没更新);
  2. 点它旁边的 「重新构建」,等 8~15 秒(按钮转圈时别重复点,服务端同一时刻只跑一个构建);
  3. 提示消失后,再去点「预览这一页」,这时才是你改完的版本。

想让保存即时生效(不用点构建),就用 dev.bat 起开发服务器——它边写边热更新。 代价是 dev 模式下搜索是哑的(Pagefind 索引只在构建期生成), 而且要另开一个端口。日常改排版用 dev,验收成品用 preview。

上线的流程

pnpm build      # 构建到 dist/(含 Pagefind 搜索索引)
pnpm run deploy # 推到 Cloudflare Pages(⚠️ 必须带 run,裸的 pnpm deploy 是 pnpm 内置命令)

或者双击 deploy.bat(两步连做)。

首次部署需要先做一次授权:npx wrangler login(会弹浏览器)。 这个只做一次,之后 deploy.bat 就能直接用。

构建失败怎么办

构建是校验关卡,字段写错会直接失败并指出是哪个文件哪一行 —— 这是好事, 总比上线后页面空白强。根因几乎都是下面几条。


13. 排错对照表

现象 原因 怎么修
构建报 title 相关错误 必填字段没填 补上 title
构建报枚举值不匹配 status / graphType / docMode / format 写成了别的值 对照第 4、6、8 节的取值表改
新建条目被拒 slug 不是英文小写+连字符 改名,或用编辑器建(它会校验)
内容保存了但页面没变 preview 是静态服务,读的是 dist/;源文件改了但没重新构建 编辑器点「重新构建」,或重新 pnpm build;想边写边看用 dev.bat
改完保存、刷新也还是旧版 同上 —— 刷新读的还是那份没变的 dist/,不是缓存问题 别刷了,去点「重新构建」
搜索搜不到新内容 同上,索引是构建期产物 重新构建后才有
图片裂开 路径写错,或图不在 public/ 下 路径要以 /media/... 开头,且文件真实存在
页面加载时跳动 图片没写 w / h 用第 10 节的脚本重新生成清单
蓝图页空白 / 提示没有蓝图 .t3d 没放对位置 必须在 content/projects/<项目>/blueprints/ 下
蓝图节点位置乱 粘贴时 T3D 被截断 重新完整复制粘贴
模型页一直转圈 / 显示不出来 src 指向的 glb 不存在,或路径没以 /media/... 开头 确认文件真在 public/media/models/ 下、文件名大小写一致
构建报「找不到模型文件」 src 填了本地路径但文件不在 要么把文件放上去,要么把 src 改成 R2 完整 URL
手机上看模型翻不了 / 页面滚不动 手指都用在转模型了 这是刻意设计(纵向滚动优先);想翻到脚底把 viewer.orbit 设为 sphere
模型太暗 / 发白 环境光与模型材质不搭 调 viewer.exposure(默认 1,往大调更亮)
详情页第一次打开有点慢 3D 库约 1 MB,只在这一页加载 正常,之后按 7 天缓存;列表页不受影响
构建报找不到模块(Cannot find module) 依赖装了一半 跑 pnpm check:deps 看,再 pnpm install --force
未知栏目 / 404 slug 和目录名对不上 检查 content/ 下的目录名

一个万能自检

pnpm check:deps     # 依赖是否完整
pnpm build          # 内容是否合法(会逐个文件报错)

14. 上传前检查清单

每加一件东西,过一遍这个:

  • slug 是英文小写 + 连字符的,且没有改过已发布内容的 slug
  • title 填了
  • summary 填了(它会进搜索结果,别浪费)
  • order 给了个合适的值
  • 有封面(项目 / 模块 / 图集;模型篇可以没有,会自动用内置占位)
  • 图片写了 w / h,文件名是英文
  • 模型篇:src 指向的 glb 真的在 public/media/models/ 下、文件名大小写也对
  • 单文件没超过 25 MiB(模型同理)
  • 标签是复用已有的,没在造第 31 个
  • 写完了就把 draft 取消掉
  • pnpm build 过了
  • 在 preview 里实际点开看过(别只看构建成功)

附:速查

# 服务
admin.bat                    # 写内容  → 127.0.0.1:4322
dev.bat                      # 边写边看 → 127.0.0.1:4321(无搜索)
preview.bat                  # 看成品  → 127.0.0.1:4321(有搜索)

# 命令
node scripts/ingest-gallery.mjs <源目录> <图集slug> [前缀] [最大边]   # 图片批处理
pnpm docs:sync               # 开发文档 → content/docs/
pnpm check:deps              # 依赖体检
pnpm build                   # 构建(含内容校验)
pnpm run deploy              # 部署(⚠️ 必须带 run)