AiKdex主题开发规范-v1
AiKdex 主题开发规范 v1
版本:v1.0(dataContract: "1.0")· 编制日期:2026-09-16 · 面向读者:第三方主题开发团队 本规范是第三方开发 AiKdex 主题的唯一权威依据与完整交付说明,需方不再另行口头/转述补充;规范内含开工前必读、易错点警示、验收清单与返工机制,请逐章阅读后再动工。规范描述的接口均已在当前代码中实现并核验,参考实现见
web/src/themes/(qiuzhi 采集型 / default 博客型)。
0. 致开发团队(开工前必读)
- 动工前必须先完成两项确认并向需方书面提交:① 主题形态(通用博客型
blog/ 采集频道型collect,见第 1 节);② 需要接管的页面清单(对照第 3 节 pages 声明与第 9.3 节路由)。未确认前不写代码,避免做偏返工。 - 先读懂两个参考实现再写第一行代码:
web/src/themes/index.js头部注释(协议原始定义)与web/src/themes/qiuzhi/(采集型完整范例:manifest/入口/model/style 四件套怎么组织)。通用博客型另参考 views/BlogView.vue 的 themeContext 注入路径。 - 你只交付主题本身,不改平台文件。允许创建/修改的范围:
web/src/themes/<你的id>/目录内全部文件;平台侧接入(注册、路由、import、构建、部署)由需方执行(第 9.2 节),交付说明中写清需要平台做的接入步骤即可。 - 数据一律走契约,不走猜路径:所有取数、标签、状态、字段解析按第 4 章契约来;发现契约未覆盖的需求,先提变更申请,不得自行绕开(如硬编码标签路径、直连内部 API)。
- SEO 是本站的命脉,不是可选项:第 6 节的 SEO 钩子与插件槽是强制验收项,历史上已有主题因漏接 SEO 整体重做,请引以为戒。
- 开发与自测环境:仓库根目录
cd web && npm install && npm run dev(开发模式代理 /api);真实数据自测用npm run build后走平台完整构建(build.ps1)。开发期间可用themes/examples/procurement.js与采集真实数据对照。 - 易错点警示(历次踩坑总结,务必逐条自查):
- 缺失字段渲染出 null/undefined/NaN(验收 T3 必测,降级规则见 4.6)
- 硬编码标签路径导致标签调整即坏(用 tagBindings,验收 T4)
- 标题/正文渲染未转义导致 XSS(验收 T14,用 utils/markdown.js 封装)
- 漏调 seo.js 导致页面搜索引擎不可见(验收 T8)
- 全局选择器/忘加命名空间污染宿主样式(验收 T10)
- 硬编码色值不跟 data-theme,深色模式穿帮(验收 T11)
- 依赖 window 挂载时序的写法,无法兼容平台后续静态化(第 6.3 条)
- 列表一次性渲染大数据不加分页/虚拟化导致卡顿(验收 T15)
- 验收与返工机制:交付后由需方按第 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)、title、version(semver)、entry(Vue 入口组件)。可选:desc、pages(主题页面标识数组)、tokens(设计令牌说明)、dataSources、dataScope、tagBindings(见第 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.js 的 loadModel(内部依次调用公开 API:publicListTags → publicListFilesByTag(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 与插件槽(对外主题必选)
- SEO 钩子:主题必须在路由切换时调用
utils/seo.js的setChannelSeo(列表页)/setJobSeo(详情页,输出 JobPosting JSON-LD),传入标题/描述/canonical。未集成 SEO 的主题验收不通过——本站流量依赖搜索引擎。 - 插件挂载点:主题应在对应位置渲染
components/BlogPluginSlot(mount:head/list_item/post_bottom/sidebar),缺失挂载点影响平台功能扩展能力(验收扣分项)。 - 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、岗位字段集、状态枚举映射、降级规则。
- 变更原则:新增字段不升版本;语义变更/删除升大版本并提前通知已接入主题方。