P1 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: 落地 6 个 P1 子系统:双模设计器、跨运行时协议+React 子集、数据中台合同、套打、运行时 AI、移动端覆盖。每个子系统可独立发版。
Architecture: 全部以 src/modules/<name> 纯逻辑 + 可选 UI 接入。双模最先做(只改配置)。React 渲染器放独立目录 designer3.0/packages/react-renderer/,不进主包,避免 Vue 客户买单体积。
Tech Stack: 现有 Config.show*、Rule JSON、Vant 移动端、html2canvas 打印(套打会替换主路径)。
Spec: docs/superpowers/specs/2026-08-23-innovation-requirements.md §4 P1
P1-5 双模设计器(先做)
File map
- Modify:
designer3.0/src/modules/shared/modePreset.js(已在 foundation) - Modify:
designer3.0/src/designer/FcDesigner.vue— 启动时resolveDesignerConfig - Create:
designer3.0/src/designer/ModeSwitch.vue - Modify:
designer3.0/types/index.d.ts—Config.mode、allowModeSwitch
Task 1: 接入 mode
- [ ] Step 1: 在 getConfig 源头合并预设
找到 getConfig(FcDesigner.vue)。改为:
getConfig(key) {
const merged = resolveDesignerConfig(props.config || {});
if (key) return merged[key];
return merged;
}确认所有 getConfig('showXxx') 都走这一处,不要只改部分调用。
- [ ] Step 2: ModeSwitch
工具栏最右侧。config.allowModeSwitch === true 时显示。切换只改本地 data.modeOverride,再与 props.config 合并,不写回宿主除非 emit update:mode。
- [ ] Step 3: 验收
config.mode='business' 打开设计器:左侧无 JSON,右侧无事件,预览无 SFC/SQL。allowModeSwitch: true 切回 developer 后这些入口回来。
git add src/designer/FcDesigner.vue src/designer/ModeSwitch.vue types/index.d.ts src/locale
git commit -m "feat: business and developer designer modes"P1-1 跨运行时协议 + React 子集
File map
- Create:
designer3.0/src/modules/protocol/whitelist.js - Create:
designer3.0/src/modules/protocol/lintRule.js - Create:
designer3.0/tests/modules/protocol/lintRule.spec.js - Create:
designer3.0/packages/react-renderer/package.json - Create:
designer3.0/packages/react-renderer/src/FcForm.jsx - Create:
designer3.0/packages/react-renderer/src/widgets/index.js - Create:
fc-design-pro/src/protocol.md
白名单第一期:input textarea number password select radio checkbox switch date dateRange time upload rate slider cascader treeSelect group。
Task 1: 协议检查
- [ ] Step 1: 写失败测试
import { lintRule } from '../../../src/modules/protocol/lintRule.js';
it('flags unknown types', () => {
const report = lintRule([{ type: 'fcVxeDataTable', field: 't' }]);
expect(report.ok).toBe(false);
expect(report.issues[0].code).toBe('type_not_portable');
});
it('accepts core inputs', () => {
expect(lintRule([{ type: 'input', field: 'n' }]).ok).toBe(true);
});- [ ] Step 2: 实现
lintRule 递归 children。未知 type → type_not_portable。含函数的 on / control.handle 标 runtime_fn warning,不判失败(React 一期可以忽略事件)。
预览增加「跨端检查」小报告,config.showProtocolLint === true 时显示。
- [ ] Step 3: React 渲染器最小集
packages/react-renderer 使用 antd Input/Select。FcForm props:rule option value onChange。只实现白名单组件;遇到其它 type 渲染一个只读占位「不支持的组件:type」。
不把该包打进 dist/index.es.js。单独 package.json name @form-create/react。
git add src/modules/protocol packages/react-renderer
git commit -m "feat: portable rule lint and react subset renderer"P1-2 数据中台合同 + 参考服务
File map
- Modify:
designer3.0/src/modules/shared/adapter.js—shareLink/listStats - Create:
designer3.0/examples/platform/data-service/README.md - Create:
designer3.0/examples/platform/data-service/server.mjs - Modify:
fc-design-pro/src/database.md— 声明演示云 ≠ 产品后端 - Modify:
designer3.0/examples/components/SaveFormModel.vue— 顶部注释 + UI 提示「演示」
Task 1: 扩展 adapter
- [ ] Step 1: 测试
it('creates a share token for published form', async () => {
const adapter = createMemoryAdapter();
const { id } = await adapter.saveForm({ snapshot: { format: '1.0.0', formId: 'f1', name: 'n', rule: [], options: {}, schema: [], createdAt: '' }, rule: '[]', options: '{}' });
await adapter.publishVersion(id, 1);
const share = await adapter.createShare({ formId: id, version: 1 });
expect(share.token).toBeTruthy();
const open = await adapter.getShare(share.token);
expect(open.formId).toBe(id);
});- [ ] Step 2: 参考服务
server.mjs 用 Node 内置 http,内存存储,路由:
POST /forms→ saveFormGET /forms/:id/versionsPOST /forms/:id/recordsGET /forms/:id/recordsPOST /shareGET /s/:token
不要引入 Nest/Express 全家桶。README 写清「参考实现,生产请换」。
git add src/modules/shared/adapter.js examples/platform
git commit -m "feat: share adapter and reference data service"P1-3 套打设计器
File map
- Create:
designer3.0/src/modules/print/types.js - Create:
designer3.0/src/modules/print/bindTemplate.js - Create:
designer3.0/tests/modules/print/bindTemplate.spec.js - Create:
designer3.0/src/designer/print/PrintDesigner.vue - Modify:
designer3.0/src/utils/extendPrintApi.js—printTemplate - Modify:
designer3.0/src/designer/PrintForm.vue
模板结构:
{
id: 'print_default',
page: { width: 210, height: 297, unit: 'mm' },
background: '',
items: [{ id, x, y, w, h, text: '{{name}}' }],
repeat: { field: 'items', y: 120, height: 8, items: [{ text: '{{name}}', x: 20 }] }
}Task 1: 绑定纯函数
- [ ] Step 1: 测试
import { bindTemplate } from '../../../src/modules/print/bindTemplate.js';
it('replaces mustache fields', () => {
const html = bindTemplate(
{ page: { width: 210, height: 297, unit: 'mm' }, items: [{ x: 10, y: 10, w: 80, h: 8, text: '姓名:{{name}}' }], repeat: null },
{ name: '张三' }
);
expect(html).toContain('姓名:张三');
expect(html).toContain('210mm');
});- [ ] Step 2: PrintDesigner
画布按 mm 换算 px(1mm = 3.78px)。从当前表单 field 列表拖「数据域」到画布。背景图 input。存 options.printTemplates[]。
- [ ] Step 3: 运行时
api.printTemplate(id) 用 bindTemplate 生成 HTML,window.print()。默认模板仍可走旧 html2canvas,避免行为突变。
git add src/modules/print src/designer/print src/utils/extendPrintApi.js
git commit -m "feat: print template designer with field binding"P1-4 运行时 AI Agent
File map
- Create:
designer3.0/src/modules/runtime-ai/tools.js - Create:
designer3.0/src/modules/runtime-ai/runAgent.js - Create:
designer3.0/tests/modules/runtime-ai/tools.spec.js - Create:
designer3.0/src/form/FcAiAgent.js - Create:
fc-design-pro/src/help/feature/runtime-ai.md
允许的工具:setValue {field,value}、getValue {field}、validate {}、explainField {field}。
Task 1: 工具白名单
- [ ] Step 1: 测试
import { executeTool } from '../../../src/modules/runtime-ai/tools.js';
it('sets a value via api', () => {
const api = { setValue: (f, v) => (store[f] = v), getValue: (f) => store[f] };
const store = { name: '' };
executeTool(api, { name: 'setValue', args: { field: 'name', value: 'A' } });
expect(store.name).toBe('A');
});
it('rejects unknown tools', () => {
expect(() => executeTool({}, { name: 'eval', args: {} })).toThrow('tool_not_allowed');
});- [ ] Step 2: runAgent
循环最多 4 步:把 schema 摘要 + formData + 用户话发给 adapter.chat 或 config.ai。模型必须返回 {reply, tool?} JSON。执行 tool 后再问一次。禁止把 on 函数源码送给模型去改。
- [ ] Step 3: 插件
formCreate.use(FcAiAgent, { ai, enabled: false });默认 enabled false。设计器预览用同一插件。
git add src/modules/runtime-ai src/form/FcAiAgent.js
git commit -m "feat: runtime AI agent with allowlisted form tools"P1-6 移动端覆盖 + 渠道
File map
- Create:
designer3.0/src/modules/mobile/coverage.js - Create:
designer3.0/tests/modules/mobile/coverage.spec.js - Modify: 每个复杂 DragRule(至少
tableFormfcVxeDataTablenestedTableFormdataTable)增加mobile: 'fallback' | 'hidden' - Create:
designer3.0/src/components/mobile/TableFormFallback.vue - Create:
fc-design-pro/src/help/feature/mobile-coverage.md
Task 1: 覆盖表
- [ ] Step 1: 从 menu 收集 name,对照
src/form/mobile.js已注册组件,生成 coverage 报告函数
export function mobileCoverage(dragRules, mobileTypes) {
return dragRules.map((r) => ({
name: r.name,
status: mobileTypes.includes(r.name) ? 'native' : r.mobile || 'hidden',
}));
}测试:native / fallback / hidden 三态。
- [ ] Step 2: TableFormFallback
移动端把每一行渲染成 Card,字段纵向排列。注册到 formCreateMobile。
- [ ] Step 3: 微信渠道文档
fc-design-pro/src/help/feature/miniprogram.md:只输出白名单 JSON,小程序端用官方示例仓库(本计划不写完整小程序项目,避免范围爆炸)。设计器增加 getPortableJson() = lintRule 通过后的 rule。
git add src/modules/mobile src/components/mobile
git commit -m "feat: mobile coverage report and table fallback"

