craft-designer
v0.2.8
Published
基于 Craft.js 的可视化页面设计器 React 组件库,拖拽编排、属性编辑、Schema 导入导出,开箱即用
Maintainers
Readme
craft-designer
一个基于 Craft.js 的可视化、低代码页面设计器 React 组件库,支持拖拽编排、属性编辑、事件绑定、数据源管理、Schema 导入导出。
你可以用配置驱动的方式,从组件库拖拽到画布、编辑属性、绑定事件、接入数据源、导出 Schema,完成「设计 → 编辑 → 预览 → 保存」的完整闭环。
目录
为什么选择 craft-designer
开箱即用:画布缩放、平移、吸附、撤销重做、组件树、属性面板、事件绑定、数据源、Schema 导入导出……这些都已经内置,你不用从零实现。
可配置:通过「组件配置」注册业务组件,就能自动出现在组件库和属性区。工具栏、面板宽度、根画布属性等都可以配置。
可替换:属性面板和顶栏支持完全自定义 UI,同时复用设计器的数据和工具栏等能力。
可集成:onSchemaChange 对接保存、designerRef 编程式读写、locale 与 词条表 对接宿主 i18n。
依赖清晰:peerDependencies 声明 React 与 AntD,产物不重复打包,宿主完全控制版本。
体验细节:缩放画布时控制框 1:1 视觉;拖拽超出画布自动拉回;大量节点下仍保持流畅。
快速上手
1. 安装
在 React 项目中执行:
npm install craft-designer需要已有 react、react-dom(^18 或 ^19)和 antd(>=5.0.0)。zustand、@craftjs/core 等内部依赖已打入产物,无需单独安装。
Craft.js API 透传:为避免 Context 双实例问题,
useNode、useEditor、Element等 Craft.js API 应从craft-designer导入,而非直接依赖@craftjs/core。
2. 准备组件配置
为每个可编排组件写一份配置,其中 preview 是纯展示用的 React 组件:
{
type: 'Button',
name: '按钮',
preview: () => <button>按钮</button>,
craftConfig: { props: {} },
propertyConfig: { layout: true, style: true },
}容器类组件若需在画布内编辑,再提供 editorComponent。
3. 挂载设计器
在页面中渲染 <Designer />,传入 componentConfigs 即可:
import { Designer } from 'craft-designer';
import 'craft-designer/dist/craft-designer.css';
const configs = [{
type: 'Button',
name: '按钮',
preview: () => <button>按钮</button>,
craftConfig: { props: {} },
propertyConfig: { layout: true, style: true },
}];
export default function Page() {
return <Designer componentConfigs={configs} />;
}4. 持久化
- 设计变更时:在
onSchemaChange回调里拿到最新 Schema,存到后端。 - 保存/导出:通过
designerRef.current?.serialize()取 JSON。 - 打开编辑页:用
initialSchema或deserialize()还原。
5. 进阶
- 自定义属性面板:传
renderPropertyPanel。 - 自定义顶栏:传
renderHeader,可内嵌DesignerTool。 - 业务属性:用
propertyConfig.business或createBusinessProperties。 - 事件绑定:通过
EventSystem为组件配置交互行为。 - 数据源:通过
DataSource模块注册和消费远程/静态数据。
完整示例:playground/configs/DesignerPage/index.tsx,运行 npm run dev 打开 playground 体验。
安装
npm install craft-designer antd需要在 React 项目(^18 或 ^19)中使用。antd(>=5.0.0)为 peer 依赖,需要单独安装。
功能清单
面向产品层面的能力说明,便于判断「能做什么、边界在哪」。技术实现见 接入与扩展。
画布操作
- 尺寸预设:桌面端(1920×1080)、平板(768×1024)、手机(375×667)和自定义。
- 缩放:10%–500%,支持适应窗口、100% 重置、自由缩放。
- 平移:Alt + 左键或中键拖拽。
- 自适应:打开时自动适应窗口,面板展开/收起时重新计算。
- 居中/滚动:画布小于可视区时居中显示,大于可视区时左上角定位 + 滚动。
- 背景色:可自定义。
组件库
- 按分组展示;支持图标+名称、预览卡片两种布局。
- 从左侧面板拖拽到画布即可创建实例。
- 面板可折叠。
组件交互
- 选中、拖拽、缩放、旋转:点击选中、八方向控制点缩放、顶部手柄旋转。
- 吸附:拖拽时吸附相邻组件和画布中心参考线。
- 跨容器拖拽:可在不同容器间移动,自动换算坐标。
- 删除:选中时显示删除按钮,支持
onBeforeDelete拦截确认。
多选操作
- Shift 多选;组拖拽;左/右/上/下/水平居中/垂直居中对齐;置顶、上移、下移、置底。
组件树
- 右侧树形展示层级;点击选中;树内拖拽排序;同类型自动编号;容器可折叠;悬停显示删除按钮;可拖拽分割线调整高度。
- 支持组件图标注册(
registerComponentIcon/registerComponentIcons)。
属性面板
- 布局:X、Y、宽、高、旋转。
- 样式:背景色、文字色、边框、圆角、透明度、字号、字重。
- 业务属性:由
propertyConfig配置驱动;支持dependsOn/dependsValue条件显隐。 - 事件:为组件绑定交互动作(页面跳转、弹窗、数据请求等)。
- 多选:切换为对齐 + 层级操作。
- 画布:选中根画布可改尺寸预设、背景色、布局模式。
- 自定义:
renderPropertyPanel完全替换。
工具栏
- 缩放控制、撤销/重做(含快捷键)、层级排序、对齐、Schema 预览、导出、紧凑模式;
sections按需显隐。
数据管理
- 加载(
initialSchema)、实时通知(onSchemaChange,防抖 300ms)、预览、复制、下载、designerRef编程式serialize/deserialize/getHistory。
布局模式
- 自由布局(absolute):任意位置、拖拽/旋转/吸附/跨容器。
- 流式布局(flex):顺序排列,仅缩放,不支持拖拽和旋转。
数据源
- 支持静态数据(
static)和远程请求(fetch)两种类型。 - 远程数据源支持
requestMode: 'auto'(注册时自动拉取)和'manual'。 - 实例隔离 — 每个 Designer 拥有独立的数据源 store。
- 提供 Hooks(
useDataSource、useDataSourceOptions、useInlineDataSource)直接在组件内消费。 - 支持
transformToOptions:将接口返回数据转换为下拉框选项。 - 可通过
configureDataSourceApi配置全局请求基址和拦截器。
事件系统
- 支持为组件配置事件(点击、加载等)及对应动作(跳转、弹窗、请求等)。
EventContextProvider+useEventContext提供运行时事件分发。PreviewRenderer支持预览模式下的事件执行。executeAction/createEventHandler供自定义场景调用。- 事件日志可通过
createLogger(debug)控制输出级别。
多语言
- 内置中文、英文;
locale支持三种形态(快捷字符串 / 底稿+覆盖 / 宿主全权接管);完整词条见 docs/i18n-keys.md。
组件控制项
每个组件可通过 craftConfig.controls 单独配置:可缩放、可旋转、可删除、保持比例。
性能与体验
- 拖拽/缩放/旋转流畅;控制框 1:1 视觉;超出画布自动拉回;面板过渡平滑;大量组件下按需计算。
接入与扩展
组件注册:componentConfigs
interface IComponentConfig {
type: string;
name: string;
group?: string;
icon?: React.ReactNode;
preview: React.ComponentType; // 纯展示组件
editorComponent?: React.ComponentType; // 容器类画布内编辑用
craftConfig?: {
props?: INodeProps;
rules?: { canDrag?, canMoveIn?, canMoveOut? };
controls?: IDesignerControls; // resizable, rotatable, deletable...
};
propertyConfig?: IComponentPropertyConfig;
wrapperOptions?: { wrapper?, wrapperProps? };
isRootConfig?: boolean;
}属性面板:propertyConfig
interface IComponentPropertyConfig {
layout?: boolean | string[] | IPropertyConfig[]; // true=全部, false=不展示
style?: boolean | string[] | IPropertyConfig[];
business?: IPropertyConfig[];
}IPropertyConfig 常用字段:type(text/number/color/select/boolean/object)、category、options、default、dependsOn + dependsValue。
常用 Props 速查
| Prop | 作用 |
|------|------|
| componentConfigs | 注册业务组件(必填) |
| rootCanvasProps | 根画布:layoutMode、sizePreset、width/height、background |
| initialSchema / onSchemaChange | 初始 Schema、变更回调(防抖 300ms) |
| designerRef | serialize() / deserialize() / getHistory() |
| leftPanel / rightPanel | 左:layout、title、width;右:width |
| keyboardShortcuts | false 全关,或 { undoRedo: false } 等 |
| locale | 'zh-CN' | 'en-US' | Partial |
示例:rootCanvasProps
<Designer
componentConfigs={myConfigs}
rootCanvasProps={{
layoutMode: 'absolute',
sizePreset: 'custom',
width: 800,
height: 600,
background: '#f5f5f5',
}}
/>示例:Schema 与 Ref
<Designer
componentConfigs={myConfigs}
initialSchema={savedJson}
onSchemaChange={(schema) => saveToServer(schema)}
designerRef={ref}
/>
// ref.current?.serialize() | deserialize(json) | getHistory()自定义渲染
- 属性面板:
renderPropertyPanel完全接管右侧属性区;参数含selectedNode、selectedNodes、setProp、setStyle等。 - 顶栏:
renderHeader替换整个 Header;可内嵌<DesignerTool sections={['zoom','undoRedo','zoomControl']} compact />。
典型场景
| 场景 | 参考 |
|------|------|
| 自由布局页面编排 | playground/configs/DesignerPage |
| 对接已有 Schema 的编辑页 | initialSchema + onSchemaChange + designerRef.serialize(),见 DesignerPage |
数据源
数据源模块提供注册、管理和消费远程/静态数据的能力,每个 Designer 实例拥有独立的数据源 store。
数据源类型
// 静态数据源
interface IStaticDataSource {
id: string;
type: 'static';
data: unknown;
}
// 远程数据源
interface IFetchDataSource {
id: string;
type: 'fetch';
fetchConfig: IFetchConfig;
requestMode?: 'auto' | 'manual';
}在组件中消费
import { useDataSource, useDataSourceOptions } from 'craft-designer';
// 获取原始数据
const { data, loading, error } = useDataSource('myDataSource');
// 直接转为 Select 选项
const options = useDataSourceOptions('myDataSource', {
labelField: 'name',
valueField: 'id',
});API 配置
import { configureDataSourceApi } from 'craft-designer';
configureDataSourceApi({
baseURL: '/api',
headers: { Authorization: `Bearer ${token}` },
});事件系统
事件系统为组件提供交互能力,支持在设计态绑定事件,在预览/运行态执行。
设计态
通过属性面板的「事件」区域为组件绑定事件和动作。每个事件可配置多个动作链。
预览/运行态
import { EventContextProvider, PreviewRenderer } from 'craft-designer';
function PreviewPage({ schema }) {
return (
<EventContextProvider actions={actionMap}>
<PreviewRenderer schema={schema} />
</EventContextProvider>
);
}编程式调用
import { executeAction, createEventHandler } from 'craft-designer';
// 直接执行单个动作
await executeAction(actionConfig, context);
// 创建事件处理函数(可绑定到 DOM 事件)
const handler = createEventHandler(eventDefinition, context);
element.addEventListener('click', handler);多语言
locale prop 支持三种形态:
1. 快捷字符串 — 使用内置整包
<Designer locale="en-US" />
<Designer locale="zh-CN" />2. 底稿 + 覆盖 — 以内置包为底,覆盖部分 key
<Designer locale={{ preset: 'en-US', overrides: { 'Designer.leftPanel.title': 'My Library' } }} />overrides 中存在的 key 覆盖 preset 对应的整包;未写的 key 用 preset 的文案补齐。
3. 宿主全权接管 — 传完整 ILocale 对象
const jaBundle: ILocale = { /* 日语完整 key-value */ };
<Designer locale={jaBundle} />库不做任何合并,缺 key 时 t(key) 返回 key 字符串本身。
覆盖对象的 Key 须与 词条表 一致。部分文案含插值 {type}、{count}。高级用法可用导出的 resolveLocale(locale) 在非 React 处解析文案。
包内导出:zhCN、enUS、DesignerLocaleEnum、useLocale、LocaleProvider、resolveLocale;可用 t(DesignerLocaleEnum.xxx)。
API 导出
组件
| 符号 | 用途 |
|------|------|
| Designer | 设计器主组件 |
| DesignerTool | 可配置工具栏(供 renderHeader 内嵌) |
| LayoutPanel / StylePanel | 布局/样式编辑面板块 |
| MultiSelectLayoutSection | 多选时的对齐/层级操作区 |
| PreviewRenderer | 预览渲染器 |
| EventContextProvider | 事件运行时 Context Provider |
Hooks
| 符号 | 用途 |
|------|------|
| useDesignerConfig | 读取设计器配置上下文 |
| useSelectedNode | 获取当前选中节点信息 |
| useLayoutValues | 读取节点布局值与更新方法 |
| useChildPositionInit | 自由布局下子节点位置初始化 |
| useLocale | 获取多语言 t(key) 函数 |
| useEventContext / useEventContextOptional | 事件运行时 Context |
| useDataSource | 消费数据源原始数据 |
| useDataSourceOptions | 数据源转下拉选项 |
| useInlineDataSource | 行内数据源(组件内独立使用) |
| useDataSourceStore / useDataSourceState / useDataSourceData / useDataSourceActions | 数据源 store 底层访问 |
工具函数
| 符号 | 用途 |
|------|------|
| createBusinessProperties | 业务属性配置工厂 |
| getDefaultPropsFromConfig | 从配置提取默认 props |
| extractPropertyKeys | 提取属性 key 列表 |
| setLastDropPosition / subscribeDropPosition | 自由布局拖放位置管理 |
| flatToTree / treeToFlat | 扁平 ↔ 嵌套树数据转换 |
| registerComponentIcon / registerComponentIcons | 组件图标注册 |
| getComponentIcon / hasComponentIcon / clearComponentIcons | 组件图标查询/清理 |
| executeAction / createEventHandler | 事件执行 |
| createLogger | 创建事件日志器 |
| resolveLocale | 非 React 环境解析多语言 |
| configureDataSourceApi / DataSourceApi / getApiConfig | 数据源 API 配置 |
| fetchData / transformToOptions / executeDataSource / executeDataSourceAsOptions | 数据源执行器 |
| DataSourceStoreProvider / createDataSourceStore | 数据源 store 创建 |
Craft.js 透传
| 符号 | 用途 |
|------|------|
| useNode | Craft 节点 Hook |
| useEditor | Craft 编辑器 Hook |
| Element | Craft 画布元素 |
| Node / SerializedNodes | Craft 类型 |
类型导出(节选)
IDesignerProps、IDesignerRef、DesignerComponentType、IComponentConfig、IComponentPropertyConfig、IPropertyConfig、ICustomPropertyPanelProps、ISelectedNodeInfo、INodeLayout、INodeStyle、StylePropertyKey、LayoutPropertyKey、IDesignerToolProps、DesignerToolSection、RenderPropertyPanel、LeftPanelLayout、TreeNode、ITreeNodeFilterInfo、ITreeNodeFilterResult、FilterTreeNode、IComponentIconConfig、ILocale、ILocaleKeys、DesignerLocale、BuiltinLocalePreset、ILocalePresetWithOverrides、IEventContext、IEventDefinition、IActionConfig、ActionType、ExecuteOptions、IEventSecurityOptions、PreviewRendererProps、NodeWrapper、IMultiSelectLayoutSectionProps、IDataSourceApiConfig。
完整列表见 src/index.ts。
开发与目录
npm run dev # playground 开发服务器
npm run build # 输出 dist/
npm run lint
npm run test # 单元测试craft-designer/
├── src/
│ ├── components/Designer/
│ │ ├── index.tsx # 入口:Provider 嵌套 + Editor + 三栏布局
│ │ ├── DesignerConfig.tsx # 配置 Context
│ │ ├── DesignerEffects.tsx # 副作用聚合(schema 监听、快捷键、生命周期)
│ │ ├── DesignerRefBridge.tsx # designerRef → serialize/deserialize/history
│ │ ├── Canvas/ # 画布区(Frame + RootCanvas)
│ │ ├── common/
│ │ │ ├── RootCanvas/ # 根画布:尺寸/拖拽/缩放/自适应/居中
│ │ │ ├── RenderWrapper/ # 单节点 Moveable 控制框
│ │ │ └── GroupMoveable/ # 多选 Moveable
│ │ ├── Header/ # 顶栏 + 工具条
│ │ ├── LeftPanel/ # 组件库(分组 + 拖拽创建)
│ │ ├── RightPanel/ # 组件树 + 属性面板 + 事件面板
│ │ ├── DataSource/ # 数据源:store/executor/api/hooks
│ │ ├── EventSystem/ # 事件:Context/Executor/PreviewRenderer
│ │ ├── hooks/ # 画布交互、选中、schema、快捷键等
│ │ ├── store/ # UI 状态(zoom/offset/panels)
│ │ ├── locale/ # 多语言(zh-CN / en-US)
│ │ ├── types/ # 类型定义
│ │ ├── utils/ # 工具函数
│ │ └── icons/ # 内置图标
│ └── index.ts # 统一导出入口
├── docs/
├── playground/
└── package.json技术说明
样式隔离
组件样式通过 postcss-prefix-selector 自动添加 .craft-designer-scope 前缀,避免与宿主应用冲突。使用时需确保 Designer 组件被包裹在带有该 class 的容器中(已内置处理)。
状态管理
- UI 状态(缩放、面板显隐等):zustand + Context,每个 Designer 实例独立隔离。
- 数据源状态:独立 zustand store,同样按实例隔离。
- 组件树/选中/历史:由 Craft.js Editor 管理。
日志
开发调试日志通过 IEventLogger 抽象,createLogger(debug) 可控制是否输出 debug 级别日志。生产环境建议传入 debug: false。
License
MIT
