/tougao 页面对外字段分析
入口:apps/web/app/tougao/page.tsx → 渲染 <DirectSubmissionEditor/>(apps/web/components/user/submissions-section.tsx),纯客户端、force-dynamic,未登录会跳 /login?next=...。
URL 参数:?edit=<id> 或 ?post_id=<id> 表示编辑已有投稿;否则新建。
保存端点:新建 POST /user-center/submissions,编辑 PATCH /user-center/submissions/:id;随后按意图追加 POST .../submit(审核)或 POST .../publish(发布)。
1. 顶层提交字段(payload,来自 save() L550–575)
| 字段 | 类型 | 来源 | 说明 / 约束 |
|---|---|---|---|
type | string | 表单 name="type" | 内容类型 select,默认 NEWS;PAGE 选项禁用(仅后台可建) |
title | string | 表单 name="title" | 文章标题,maxLength=220,required |
slug | string? | 表单 name="slug" | 链接别名,maxLength=240,留空后端自动生成 |
excerpt | string? | 表单 name="excerpt" | 摘要,maxLength=600,3 行 textarea,用于列表摘要 |
body | string | state body | 正文落库形态:markdown 模式下经 markdownToHtml() 转 HTML;html 模式直接存 HTML(含 plainTextCodeToHtml 兜底) |
coverMediaId | string|null | state | 封面媒体 ID(从素材库或上传后写入);未选传 null |
categoryIds | string[] | 表单 name="categoryIds" | 单选 select,落库时包成 [id];空值为 [] |
offerEnabled | boolean | state | 是否作为付费资源/商城资源发布 |
offerPrice | string? | 表单 name="offerPrice" | 资源价格(金币),number min=0 step=0.01;仅 offerEnabled=true 时必填,否则 undefined |
hiddenEnabled | boolean? | state | 是否启用"购买后可见内容";仅 offerEnabled 时下发 |
hiddenContent | string? | 表单 name="hiddenContent" | 购买后可见正文(文字或 HTML),5 行 textarea;仅 offerEnabled && hiddenEnabled 下发 |
freeDownloadRequiresLogin | boolean? | state | 免费下载是否需登录,默认 true |
downloads | DownloadRow[]? | state | 下载列表;仅 offerEnabled && downloadEnabled 下发,过滤掉空 url |
videos | VideoRow[]? | state | 视频列表;仅 offerEnabled && videoEnabled 下发,过滤掉空 url |
tags | string[] | state tags + tagDraft | 标签数组(空格/逗号分隔后 split) |
metadata | object | 见下 | 自定义元数据,原样透传后端 |
2. metadata 自定义字段(L560–573)
| 字段 | 含义 |
|---|---|
editorMode | 实际保存模式:'markdown' | 'html'(用户手动切过则锁定,否则按正文形态自动判定) |
editorSource | 编辑器原始源码(未转换),下次打开时回填 |
articleFormat / format | 文章形态:'' | 'image' | 'video' | 'audio'(两键同时写,兼容旧字段名) |
tags | 标签以空格拼接的字符串 |
tagList | 标签数组(与顶层 tags 同步) |
showCover | 正文内是否渲染封面大图(显式 boolean,=== true 才显示) |
legacyShop | 兼容 RiPro/WordPress 老主题的资源配置块(见第 4 节) |
...metadata | 保留后端返回的其它元数据(如 editorSource 旧值、legacyShop 旧值等),不整包覆盖 |
3. 表单原生 name 字段清单(FormData 直接读取)
title、excerpt、type、slug、categoryIds、offerPrice、demoUrl、customButton、hiddenContent。
其中demoUrl(预览地址)、customButton(格式"按钮名称|Url地址")不进入顶层 payload,只写进metadata.legacyShop。
4. metadata.legacyShop(新老双写,L509–534)
| 新字段 | 兼容旧字段(RiPro) | 含义 |
|---|---|---|
permanentVipFree | cao_is_boosvip | 永久 VIP 免费 |
ordinaryPurchaseDisabled | cao_close_novip_pay | 关闭普通(非 VIP)购买 |
freeForMembers | — | 会员免费 |
videoFree | cao_is_video_free | 视频免费 |
vipRate | cao_vip_rate | VIP 折扣率,默认 '1' |
downloadEnabled | cao_status | 启用下载('1'/'') |
videoEnabled | cao_video | 启用视频('1'/'') |
hiddenEnabled | cao_hidden_content | 启用购买后隐藏内容 |
freeDownloadRequiresLogin | — | 免费下载需登录 |
cao_price | — | 价格(来自 offerPrice) |
demoUrl / cao_demourl | — | 预览地址 |
customButtonLabel / customButtonUrl | cao_diy_btn | 自定义按钮文本与链接(前端以 ` |
5. 子表行结构
DownloadRow(L2019):{ name: string, url: string, password?: string }(兼容旧字段 pwd)
VideoRow(L2024):{ name: string, poster: string, url: string }(兼容旧字段 title / img / src)
6. 保存意图分支(publishIntent,不进 payload,影响后续调用)
DRAFT:只保存,不调用后续接口 → 提示"草稿已保存"REVIEW:保存成功后追加POST /:id/submit→ 进待审核PUBLISHED:保存成功后追加POST /:id/publish→ 直接发布(仅PUBLISHED时保存成功后跳转到文章详情页submissionHref(saved))
7. 其它关键行为(影响字段语义)
- 本地草稿:按
loveps-tougao-draft:<id|new>存 localStorage,350ms 防抖自动保存,含 body/mode/tags/全部开关/downloads/videos/cover/showCover/codeLanguage/wrapCode;手动"放弃草稿"时清除。 - 粘贴/上传:图片粘贴/拖拽/上传后以
或<img>形式插入正文,走POST /user-center/media/upload。 - 正文代码兜底:整篇被代码围栏包裹时自动剥壳;HTML 模式下纯文本源码自动包成
<pre><code>。 - 封面:
coverMediaId受控于素材库选择器或直传,showCover独立控制是否在正文渲染。


