P2 Modules Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 落地 4 个生态向能力:行业包、协同(锁+评论+分支)、私有化 AI、插件清单加载。全部建立在 P0 的 snapshot / adapter / protocol 之上。
Architecture: 行业包和插件都是「远程清单 + 本地 addDragRule/setBehavior」,不在运行时执行任意字符串。协同不做 CRDT。私有化 AI 只保证 OpenAI 兼容协议和离线依赖清单。
Tech Stack: 现有 list / addDragRule / config.ai / _fc_id。
Spec: docs/superpowers/specs/2026-08-23-innovation-requirements.md §4 P2
P2-1 行业包 + 模板市场
File map
- Create:
designer3.0/src/modules/marketplace/loadPack.js - Create:
designer3.0/tests/modules/marketplace/loadPack.spec.js - Create:
designer3.0/packs/hr/index.js - Create:
designer3.0/packs/expense/index.js - Create:
designer3.0/packs/gov-apply/index.js - Modify:
designer3.0/src/designer/FcDesigner.vue—config.packUrl启动时拉取
包格式:
export const pack = {
name: 'fc-pack-hr',
version: '1.0.0',
list: [{ label: '入职登记', rule: [], options: { formName: '入职登记' } }],
dragRules: [],
};Task 1: loadPack
- [ ] Step 1: 测试
import { applyPack } from '../../../src/modules/marketplace/loadPack.js';
it('merges list items onto designer list', () => {
const designer = { addComponent: () => {}, list: [] };
applyPack(designer, { name: 'hr', version: '1.0.0', list: [{ label: '入职', rule: [] }], dragRules: [] });
expect(designer.list).toHaveLength(1);
});
it('rejects pack without name', () => {
expect(() => applyPack({}, { list: [] })).toThrow('invalid_pack');
});applyPack 只接受普通对象,不 eval。dragRules 若来自远程 URL,必须是已经在宿主 import() 过的模块,URL 清单只提供 {name, module} 映射。
[ ] Step 2: 三个官方包内容
HR:姓名、身份证、部门、入职日、紧急联系人(group)。
费用:金额(用现有 chineseAmount 模板)、事由、发票上传、日期。
政务:申请人、证件号、事项类型、材料上传、声明 checkbox。
规则用手写 JSON,放到 packs/*/rules/*.json,index.js 读取。不要用 AI 生成后不检查。
- [ ] Step 3: 设计器接入
config.packs 为本地模块数组;config.packCatalogUrl 只拉 {name, version, importPath} JSON。examples 演示加载 packs/hr。
git add src/modules/marketplace packs
git commit -m "feat: industry packs and pack loader"P2-2 协同:锁、评论、分支
File map
- Create:
designer3.0/src/modules/collab/lock.js - Create:
designer3.0/src/modules/collab/comments.js - Create:
designer3.0/tests/modules/collab/lock.spec.js - Create:
designer3.0/src/designer/collab/CommentPane.vue - Modify:
designer3.0/src/designer/FcDesigner.vue—beforeActiveRule结合锁 - Modify:
designer3.0/types/index.d.ts—HostAdapter.lockField/addComment
Task 1: 字段锁
- [ ] Step 1: 测试
import { canEdit, acquireLock, releaseLock } from '../../../src/modules/collab/lock.js';
it('blocks another user', () => {
const locks = acquireLock({}, 'id_name', 'u1');
expect(canEdit(locks, 'id_name', 'u1')).toBe(true);
expect(canEdit(locks, 'id_name', 'u2')).toBe(false);
const next = releaseLock(locks, 'id_name', 'u1');
expect(canEdit(next, 'id_name', 'u2')).toBe(true);
});锁 map:{ [fcId]: { userId, at } }。超时不在第一期做,由 adapter 心跳决定。
- [ ] Step 2: 评论
评论对象:{ id, fcId, userId, text, at }。CommentPane 按当前 activeRule._fc_id 过滤。adapter listComments / addComment。无 adapter 时面板显示「需协同适配器」。
- [ ] Step 3: 分支
「另存为分支」= saveForm 一份新 snapshot,options.branchOf = oldFormId。复用版本面板,不新做 git merge。
git add src/modules/collab src/designer/collab
git commit -m "feat: field locks comments and snapshot branches"P2-3 私有化 AI 与信创离线
File map
- Create:
designer3.0/src/modules/ai/openaiCompat.js - Create:
designer3.0/tests/modules/ai/openaiCompat.spec.js - Create:
fc-design-pro/src/help/feature/private-ai.md - Create:
fc-design-pro/src/help/feature/offline.md - Modify:
designer3.0/src/designer/ai/AiPanel.vue— 请求改走openaiCompat
Task 1: OpenAI 兼容客户端
- [ ] Step 1: 测试
import { buildChatRequest } from '../../../src/modules/ai/openaiCompat.js';
it('builds chat completions payload', () => {
const req = buildChatRequest({
model: 'local-model',
messages: [{ role: 'user', content: '做个请假表' }],
thinking: false,
});
expect(req.urlPath).toBe('/v1/chat/completions');
expect(req.body.model).toBe('local-model');
expect(req.body.messages[0].content).toBe('做个请假表');
});config.ai.api 已存在。补 config.ai.model、config.ai.path(默认 /v1/chat/completions)。AiPanel 不要写死官方主机路径以外的假设;空 api 才用内置默认。
- [ ] Step 2: 离线文档
对照 开发环境与依赖说明.md §4.3,列出:echarts、mermaid、xlsx、mammoth、html2canvas、jspdf、pdfjs。offline.md 给出用 formCreateUrl 同类本地托管的配置示例。不把模型文件打进仓库。
git add src/modules/ai src/designer/ai
git commit -m "feat: OpenAI-compatible private AI client"P2-4 插件清单
File map
- Create:
designer3.0/src/modules/plugin/validateManifest.js - Create:
designer3.0/src/modules/plugin/usePlugin.js - Create:
designer3.0/tests/modules/plugin/usePlugin.spec.js - Create:
designer3.0/src/extension/plugin.example.json - Modify:
designer3.0/src/index.js—FcDesigner.usePlugin - Modify:
designer3.0/types/index.d.ts
清单:
{
"name": "fc-plugin-id-card",
"version": "1.0.0",
"menus": [{ "name": "biz", "title": "业务" }],
"dragRuleNames": ["idCard"],
"behaviors": ["exportIdCard"],
"formulas": ["CHECK_ID"]
}Task 1: usePlugin
- [ ] Step 1: 测试
import { usePlugin } from '../../../src/modules/plugin/usePlugin.js';
it('registers menus and rules from module maps', () => {
const calls = { menus: [], rules: [] };
const Fc = {
addMenu: (m) => calls.menus.push(m),
addDragRule: (r) => calls.rules.push(r),
setBehavior: () => {},
setFormula: () => {},
};
usePlugin(
Fc,
{ name: 'p', version: '1', menus: [{ name: 'biz', title: '业务' }], dragRuleNames: ['idCard'] },
{ dragRules: { idCard: { name: 'idCard', menu: 'biz', label: '身份证' } } }
);
expect(calls.menus[0].name).toBe('biz');
expect(calls.rules[0].name).toBe('idCard');
});
it('refuses missing module entry', () => {
expect(() =>
usePlugin({ addMenu() {}, addDragRule() {} }, { name: 'p', version: '1', dragRuleNames: ['x'] }, { dragRules: {} })
).toThrow('plugin_export_missing');
});远程商店只返回清单 URL。真正代码必须由宿主 import 后传入第三参。禁止根据 URL 拼 script 执行。
git add src/modules/plugin src/index.js types/index.d.ts
git commit -m "feat: plugin manifest loader"

