爱库录 目录即站点,文件即文章

AiKdex主题开发规范-v1

2026-09-16 · AiKdex板块 · 返回列表

AiKdex 主题开发规范 v1

版本:v1.0(dataContract: "1.0")· 编制日期:2026-09-16 · 面向读者:第三方主题开发团队 本规范是第三方开发 AiKdex 主题的唯一权威依据与完整交付说明,需方不再另行口头/转述补充;规范内含开工前必读、易错点警示、验收清单与返工机制,请逐章阅读后再动工。规范描述的接口均已在当前代码中实现并核验,参考实现见 web/src/themes/(qiuzhi 采集型 / default 博客型)。


0. 致开发团队(开工前必读)

  1. 动工前必须先完成两项确认并向需方书面提交:① 主题形态(通用博客型 blog / 采集频道型 collect,见第 1 节);② 需要接管的页面清单(对照第 3 节 pages 声明与第 9.3 节路由)。未确认前不写代码,避免做偏返工。
  2. 先读懂两个参考实现再写第一行代码web/src/themes/index.js 头部注释(协议原始定义)与 web/src/themes/qiuzhi/(采集型完整范例:manifest/入口/model/style 四件套怎么组织)。通用博客型另参考 views/BlogView.vue 的 themeContext 注入路径。
  3. 你只交付主题本身,不改平台文件。允许创建/修改的范围:web/src/themes/<你的id>/ 目录内全部文件;平台侧接入(注册、路由、import、构建、部署)由需方执行(第 9.2 节),交付说明中写清需要平台做的接入步骤即可。
  4. 数据一律走契约,不走猜路径:所有取数、标签、状态、字段解析按第 4 章契约来;发现契约未覆盖的需求,先提变更申请,不得自行绕开(如硬编码标签路径、直连内部 API)。
  5. SEO 是本站的命脉,不是可选项:第 6 节的 SEO 钩子与插件槽是强制验收项,历史上已有主题因漏接 SEO 整体重做,请引以为戒。
  6. 开发与自测环境:仓库根目录 cd web && npm install && npm run dev(开发模式代理 /api);真实数据自测用 npm run build 后走平台完整构建(build.ps1)。开发期间可用 themes/examples/procurement.js 与采集真实数据对照。
  7. 易错点警示(历次踩坑总结,务必逐条自查)
    • 缺失字段渲染出 null/undefined/NaN(验收 T3 必测,降级规则见 4.6)
    • 硬编码标签路径导致标签调整即坏(用 tagBindings,验收 T4)
    • 标题/正文渲染未转义导致 XSS(验收 T14,用 utils/markdown.js 封装)
    • 漏调 seo.js 导致页面搜索引擎不可见(验收 T8)
    • 全局选择器/忘加命名空间污染宿主样式(验收 T10)
    • 硬编码色值不跟 data-theme,深色模式穿帮(验收 T11)
    • 依赖 window 挂载时序的写法,无法兼容平台后续静态化(第 6.3 条)
    • 列表一次性渲染大数据不加分页/虚拟化导致卡顿(验收 T15)
  8. 验收与返工机制:交付后由需方按第 9.3 节 16 条用例逐条测试并出具验收报告(通过/不通过+证据);不通过项附具体修复建议返回,修复后仅重测相关项;两次返工后仍不通过的项,需方有权终止合作。交付即视为接受本机制。

1. 适用范围与主题形态

本规范适用于为 AiKdex(信息采集发布系统)开发对外公开页主题。系统当前支持两种主题形态,第三方按需求选择:

| 形态 | dataSources | 数据来源 | 适用场景 | |---|---|---|---| | 通用博客主题 | ['blog'] | 宿主注入 themeContext(公开文章/标签),主题零对接 | 内容型站点:博客、资讯、公告 | | 采集频道主题 | ['collect'] | 主题经 themes/lib/model.js 装载采集数据 | 数据型频道:岗位、培训、招标、情报 |

安全红线:dataScope 必须为 'shared'(只读已公开数据)。声明 'library'(直读文件库)仅限单机/内网自用主题,不得对外公开交付

2. 主题注册与接入(平台侧机制,第三方需了解)

注册表位于 web/src/themes/index.js,核心 API:registerTheme(theme) / listThemes() / getActiveTheme() / setActiveTheme(id)。激活状态持久化于 localStorage(key aikmap.blog.theme)。

主题对象必须字段(缺一拒绝注册,见 index.js:58-70):id(全局唯一,kebab-case)、titleversion(semver)、entry(Vue 入口组件)。可选:descpages(主题页面标识数组)、tokens(设计令牌说明)、dataSourcesdataScopetagBindings(见第 4.3 节)。

接入点约定(平台侧执行,第三方在交付说明中注明需接入的路由):/blog 形态由 views/BlogView.vue 动态渲染,无需新路由;独立频道形态(如 /qiuzhi)需在 router/index.js 增加路由并在某入口 import 触发注册。

3. manifest 规范

建议主题根目录提供 manifest.js(default export,参考 themes/qiuzhi/manifest.js),字段如下:

export default {
  id: 'my-theme',                  // 必填,kebab-case,全局唯一
  title: '主题展示名',
  desc: '一句话描述',
  version: '1.0.0',                // semver
  pages: ['posts'],                // 声明接管的页面标识
  tokens: { '--th-ink': '主文字色', /* … */ },  // 设计令牌说明(见第 7 节)
  dataSources: ['blog'],           // 'blog' | 'collect'
  dataScope: 'shared',             // 对外交付必须 shared
  dataContract: '1.0',             // 遵循的本规范数据契约版本
  tagBindings: { /* … */ },        // 采集型主题必填,见 4.3
  seo: true                        // 是否已集成 SEO 钩子(第 6 节),对外主题必须 true
}

4. 数据契约(dataContract 1.0)——核心章节

4.1 通用博客主题:themeContext 契约

dataSources: ['blog'] 的主题由 views/BlogView.vue 通过 provide('themeContext') 注入(themes/index.js:30-41),主题用 inject('themeContext') 接收:

| 字段 | 类型 | 说明 | |---|---|---| | siteName | string | 站点名 | | siteDesc | string | 站点描述 | | posts | Array | 公开文章列表:{ token, title, created_at, size, kind, preview } | | tags | Array | 公开标签:{ name, count } | | postUrl(token) | function | 单篇文章地址生成器,主题必须用它生成链接(不得自行拼接) | | loading / error | boolean / string | 加载态/错误态,主题必须处理(空态、错误提示) |

4.2 采集产物溯源字段(front matter)

采集入库的每个 md 产物头部固定携带以下元数据,主题可直接消费:

| 字段 | 类型 | 说明 | |---|---|---| | source_url | string | 原文链接 | | source_name | string | 采集源名称 | | city | string | 归一化城市名(已过白名单/县→市映射,主题禁止再做地理推断) | | kind | string | 产物类型:job / training / procurement / intel… | | status | string | 采集侧状态:ongoing / upcoming / expired(映射见 4.5) | | collected_at | string | 采集时间 | | org | string | 发布机构 | | publish_date | string | 原文发布日期 |

解析优先级:front matter 优先,正文兜底(lib/resolvers.js 已实现)。主题不得重复实现解析。

4.3 自动标签体系与 tagBindings(重点)

系统在采集入库时自动打标,标签为统一树状标签(父子级联):

  • 情报/城市/{city} —— 城市维度(如 情报/城市/合肥)
  • 情报/就业 —— 岗位数据集合标签
  • 情报/培训 —— 培训补贴数据集合标签
  • 后续扩展遵循 情报/{频道} 模式

采集型主题不得硬编码标签路径,必须在 manifest 中声明 tagBindings

tagBindings: {
  listing: '情报/就业',       // 列表数据源标签(必填)
  cityPrefix: '情报/城市/',   // 城市筛选维度(前缀匹配,必填)
  extra: ['情报/培训']        // 可选关联维度
}

标签路径调整时只改 manifest,主题组件零改动。主题取数统一走 themes/lib/model.jsloadModel(内部依次调用公开 API:publicListTagspublicListFilesByTag(withContent)parseFrontMatter → 字段组装 → 关联富化),主题声明 collections/fields 即可,禁止绕开 model.js 直连内部 API

4.4 内容字段契约(岗位为例)

采集型主题消费的结构化字段(已由 lib/resolvers.js + qiuzhi/model.js 沉淀):

| 字段 | 说明 | |---|---| | id | 文件 id(详情页路由参数) | | company / position / area / type | 公司 / 职位 / 地区 / 用工性质 | | salary | 薪资(k 区间解析结果,如 "8k-15k") | | status | live / soon / ended(见 4.5) | | source / sourceUrl | 来源名 / 原文链接 | | match | 与用户条件的匹配度(如主题提供匹配功能) | | detail.edu / detail.exp | 学历 / 经验要求 | | detail.tags | 正文提取的关键词标签 | | detail.relatedTraining | 关联的培训补贴数组(后端已富化,主题只消费不计算) |

版本化:字段新增向后兼容(旧主题忽略新字段);字段语义变更必须升 dataContract 大版本并在本规范记录变更日志(第 10 节)。

4.5 状态枚举映射

| 采集侧 status_rules | 主题侧 status | 展示语义 | |---|---|---| | ongoing | live | 进行中(绿色系) | | upcoming | soon | 即将开启(琥珀色系) | | expired | ended | 已结束(灰色系/过期触动) |

4.6 缺失字段降级规则

任何字段可能缺失(正文兜底失败、源数据不全)。主题必须:缺失字段不渲染该元素(不得显示 "null"/"undefined"/"NaN");列表项至少保证 title 可渲染;数字字段缺失时不参与排序比较(排最后)。

5. 公开数据接口(平台提供,主题唯一数据入口)

  • GET /api/v1/public/tags —— 公开标签及计数
  • GET /api/v1/public/files?tag=…&withContent=1 —— 按标签取文件列表(withContent 含正文/元数据)
  • GET /api/v1/public/files/{id}/content —— 单篇正文(文本)
  • 详情页路由:/job/:id(岗位)、/p/:token(分享文章)
  • CORS:默认关闭,由平台配置白名单(AIKMAP_SERVER_CORS_ORIGINS)
  • 完整协议见《采集对接API协议.md》。主题不得调用需鉴权的接口。

6. SEO 与插件槽(对外主题必选)

  1. SEO 钩子:主题必须在路由切换时调用 utils/seo.jssetChannelSeo(列表页)/ setJobSeo(详情页,输出 JobPosting JSON-LD),传入标题/描述/canonical。未集成 SEO 的主题验收不通过——本站流量依赖搜索引擎。
  2. 插件挂载点:主题应在对应位置渲染 components/BlogPluginSlot(mount:head / list_item / post_bottom / sidebar),缺失挂载点影响平台功能扩展能力(验收扣分项)。
  3. SSG 兼容预留:主题组件须为纯声明式渲染(不依赖 window 挂载时序),以兼容平台后续静态化。

7. 样式规范

  • 所有主题 CSS 变量统一 --th-* 前缀,并在 manifest.tokens 中登记说明
  • 类名命名空间:主题根容器使用唯一类(如 .qz-root.myt-root),所有选择器置于该命名空间内;Vue SFC 推荐 scoped
  • 禁止修改全局样式(styles/main.css、tokens.css)、禁止 !important 覆盖宿主组件、禁止全局选择器(* {}、裸 body {}
  • 响应式:至少适配 375px(手机)/ 768px(平板)/ 1200px(桌面,主栏 1200px 为平台约定)
  • 深浅色:跟随宿主 data-theme 属性(4 套全局 tokens),主题用 CSS 变量取值而非硬编码色值

8. 安全要求

  • 禁止 eval / new Function / 动态远程脚本 / 外链脚本(CDN 白名单需在交付说明中声明并审核)
  • 禁止硬编码密钥、令牌、个人信息、真实服务器地址
  • 用户输入渲染必须经转义或 DOMPurify(项目已有 utils/markdown.js 封装)
  • dataScope: 'shared' 强制;任何尝试读取私有数据的实现即拒收

9. 交付格式与验收

9.1 交付物(zip 包)

aikdex-theme-<id>-<version>.zip
├── manifest.js            # 必填(第 3 节)
├── src/                   # 主题源码(入口 Vue + 组件 + model.js 声明 + style.css)
├── README.md              # 安装说明、页面清单、CDN 白名单、截图
└── LICENSE

9.2 平台侧接入流程(第三方在 README.md 中照此写接入说明)

审核源码(安全/规范逐条)→ 放入 web/src/themes/<id>/ → index.js 注册接入 → 加路由(如需)→ 构建 → 验收测试(9.3)→ 上线切换。

9.3 验收测试清单(需方执行,第三方自测同样以本表为准)

| # | 类别 | 测试项 | 通过标准 | |---|---|---|---| | T1 | 注册 | manifest 完整性 | registerTheme 成功,listThemes 可见,字段齐全 | | T2 | 数据 | 列表装载 | 真实采集数据渲染,字段与 4.4 契约一致 | | T3 | 数据 | 缺失字段 | 删字段测试文件,页面无 null/undefined/NaN、不白屏 | | T4 | 数据 | tagBindings | 改标签路径仅改 manifest,功能正常 | | T5 | 数据 | 空态/错误态 | 断网或空标签时展示空态,无未捕获异常 | | T6 | 功能 | 详情页跳转 | postUrl/job 路由正确,返回列表状态保留 | | T7 | 功能 | 筛选/排序 | 城市/状态/薪资筛选与 4.5 枚举一致 | | T8 | SEO | 元数据注入 | view-source 可见 title/description/canonical/JSON-LD | | T9 | SEO | 插件槽 | 4 个 mount 点按位渲染 | | T10 | 样式 | 隔离性 | 切换主题后宿主样式无污染(对照 AppShell/后台) | | T11 | 样式 | 深浅色 | 4 套 data-theme 下无硬编码色残留 | | T12 | 样式 | 响应式 | 375/768/1200px 三档无横向滚动、无布局破碎 | | T13 | 安全 | 静态扫描 | 无 eval/远程脚本/硬编码密钥/越权 API 调用 | | T14 | 安全 | XSS | 标题含 <script> 等载荷时不执行 | | T15 | 性能 | 首屏 | 250 条数据列表首屏交互 < 3s,无阻塞渲染 | | T16 | 兼容 | 主题切换 | 与 default/qiuzhi 互切无状态残留、无 console 报错 |

10. 里程碑与沟通约定

  • 建议分两期交付:一期先交「可运行骨架」(manifest + 入口 + 列表/详情 + 契约数据贯通),需方跑 T1-T7/T10/T13 给出中期反馈;二期交完整样式、SEO、插件槽、响应式,跑全套 16 条。
  • 问题沟通:规范未覆盖的疑问,以书面形式(文档批注/问题清单)提交,需方答复后以附录形式补录进本规范,保持单一事实源。
  • 工期与报价由双方商务约定,不在本规范范围内。

11. 契约版本与变更日志

  • dataContract 1.0(2026-09-16):首版。定义 themeContext、front matter 溯源字段、tagBindings、岗位字段集、状态枚举映射、降级规则。
  • 变更原则:新增字段不升版本;语义变更/删除升大版本并提前通知已接入主题方。