# React 19 + TypeScript + BPMN 入门完整教程

这是一份**从零到实战**的教程,基于最新 **React 19** + **TypeScript**,带你快速掌握 BPMN 流程设计器的基础使用、渲染、编辑、保存、导出等核心功能,全程可直接复制运行。

## 一、前置知识 & 环境
1. 基础 React + TypeScript 语法
2. 已创建好 React 19 + TS 项目(`create-react-app` / `vite` 均可)
3. 核心依赖:
   - `bpmn-js`:BPMN 渲染、建模核心库
   - `bpmn-js-properties-panel`:右侧属性配置面板
   - `@bpmn-io/properties-panel`:新版属性面板依赖
   - `diagram-js`:BPMN 底层绘图库(自动依赖)

---

## 二、安装依赖
在项目根目录执行:
```bash
# 核心 BPMN 库
npm install bpmn-js

# 属性面板(用于编辑节点名称、ID、类型等)
npm install bpmn-js-properties-panel @bpmn-io/properties-panel

# 样式文件
npm install inherits-browser
```
完整的组件清单:
![image.png](https://note.youdao.com/yws/res/26276/WEBRESOURCE4845e382d40d6ae69f513851f3acb021)
---

## 三、核心概念(新手必看)
1. **Viewer**:仅查看 BPMN 流程图(不可编辑)
2. **Modeler**:可编辑、拖拽、配置的流程设计器(本教程重点)
3. **XML**:BPMN 的数据格式(所有流程最终都是 XML 字符串)
4. **Canvas**:画布区域
5. **Properties Panel**:右侧属性配置面板

---

## 四、基础版:BPMN 流程图查看器(Viewer)
先实现**只读展示**,理解 BPMN 渲染原理。

### 1. 创建组件 `BpmnViewer.tsx`
```tsx
import { useEffect, useRef } from 'react';
import BpmnViewer from 'bpmn-js';
import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';

// 示例 BPMN XML(默认空流程)
const defaultBpmnXml = `<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="sample-diagram" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100524/BPMN20.xsd">
  <bpmn2:process id="Process_1" isExecutable="false">
    <bpmn2:startEvent id="StartEvent_1" />
  </bpmn2:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="179" y="159" width="36" height="36" />
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>`;

const BpmnViewer = () => {
  const containerRef = useRef<HTMLDivElement>(null);
  const viewerRef = useRef<BpmnViewer | null>(null);

  // 初始化 Viewer
  useEffect(() => {
    if (!containerRef.current) return;

    // 创建实例
    const viewer = new BpmnViewer({
      container: containerRef.current,
      width: '100%',
      height: '600px',
    });

    viewerRef.current = viewer;

    // 渲染 XML
    viewer.importXML(defaultBpmnXml).catch((err) => {
      if (err) console.error('BPMN 渲染失败', err);
    });

    // 销毁
    return () => {
      viewer.destroy();
    };
  }, []);

  return (
    <div style={{ border: '1px solid #eee' }}>
      <h3>BPMN 查看器(只读)</h3>
      <div ref={containerRef} style={{ width: '100%', height: '600px' }} />
    </div>
  );
};

export default BpmnViewer;
```

### 2. 在 App 中使用
```tsx
import BpmnViewer from './BpmnViewer';

function App() {
  return (
    <div style={{ padding: 20 }}>
      <BpmnViewer />
    </div>
  );
}

export default App;
```
运行项目,你会看到一个**基础启动事件**的流程图,**不可编辑**。

显示如下:
![image.png](https://note.youdao.com/yws/res/26278/WEBRESOURCE4704a234e648767510f65300d4477b0c)

---

## 五、进阶版:可编辑 BPMN 设计器(Modeler)+ 属性面板
这是**企业最常用**的功能:
- 左侧工具栏拖拽节点
- 右侧编辑节点属性
- 保存/导出 XML
- 导出 SVG 图片

### 1. 创建组件 `BpmnModeler.tsx`
```tsx
import { useEffect, useRef, useState } from 'react';
import Modeler  from 'bpmn-js/lib/Modeler';

import 'bpmn-js/dist/assets/diagram-js.css'
import 'bpmn-js/dist/assets/bpmn-js.css'
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'
import '@bpmn-io/properties-panel/assets/properties-panel.css'

// 引入属性面板模块
import {
  BpmnPropertiesPanelModule,
  BpmnPropertiesProviderModule,
} from 'bpmn-js-properties-panel';

// 默认空流程 XML
const defaultXml = `<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="sample-diagram" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100524/bpmn0.xsd">
  <bpmn:process id="Process_1" isExecutable="false">
    <bpmn:startEvent id="StartEvent_1" />
  </bpmn:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="179" y="159" width="36" height="36" />
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn:definitions>`;

const BpmnModeler = () => {
  const containerRef = useRef<HTMLDivElement>(null);
  const panelRef = useRef<HTMLDivElement>(null);
  const modelerRef = useRef<Modeler | null>(null);

  // 存储当前 XML
  const [xml, setXml] = useState<string>(defaultXml);

  // 1. 初始化 BPMN 设计器
  useEffect(() => {
    if (!containerRef.current || !panelRef.current) return;

    const modeler = new Modeler({
      container: containerRef.current,
      // 集成属性面板
      additionalModules: [
        BpmnPropertiesPanelModule,
        BpmnPropertiesProviderModule,
      ],
      propertiesPanel: {
        parent: panelRef.current,
      },
    });

    modelerRef.current = modeler;

    // 加载默认 XML
    modeler.importXML(defaultXml).catch((err) => {
      console.error('加载失败', err);
    });

    // 监听修改事件
    modeler.on('commandStack.changed', async () => {
      try {
        const { xml:string } = await modeler.saveXML({ format: true });
        setXml(xml);
      } catch (err) {
        console.error('保存失败', err);
      }
    });

    return () => {
      modeler.destroy();
    };
  }, []);

  // 2. 导出 XML 文件
  const exportXml = async () => {
    if (!modelerRef.current) return;
    const { xml:string } = await modelerRef.current.saveXML({ format: true });
    const blob = new Blob([xml], { type: 'application/xml' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'process.bpmn';
    a.click();
    URL.revokeObjectURL(url);
  };

  // 3. 导出 SVG 图片
  const exportSvg = async () => {
    if (!modelerRef.current) return;
    const { svg } = await modelerRef.current.saveSVG();
    const blob = new Blob([svg], { type: 'image/svg+xml' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'process.svg';
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div>
      <h2>React 19 + TS + BPMN 设计器</h2>

      {/* 操作按钮 */}
      <div style={{ marginBottom: 10, gap: 10, display: 'flex' }}>
        <button onClick={exportXml}>导出 BPMN XML</button>
        <button onClick={exportSvg}>导出 SVG 图片</button>
      </div>

      {/* 画布 + 属性面板 布局 */}
      <div style={{ display: 'flex', border: '1px solid #eee' }} >
        {/* BPMN 画布 */}
        <div ref={containerRef} style={{width:'calc(100% - 300px)'}} />

        {/* 右侧属性面板 */}
        <div
          ref={panelRef}
          style={{
            width: '300px',
            height: '600px',
            borderLeft: '1px solid #eee',
          }}
        />
      </div>
    </div>
  );
};

export default BpmnModeler;
```

### 2. 在 App.tsx 中使用
```tsx
import BpmnViewer from './bpmn/components/demo/BpmnViewer';
import BpmnModeler from './bpmn/components/demo/BpmnModeler'

function App() {
  return (
    <div style={{ padding: 20 }}>
      <BpmnModeler />
    </div>
  );
}

export default App;
```
---

## 六、运行效果
启动项目后,你会得到一个**完整可用的 BPMN 设计器**:
1. 左侧工具栏:可拖拽**开始/结束事件、用户任务、网关、连线**等
2. 中间画布:编辑流程图
3. 右侧面板:选中节点可**修改名称、ID、类型**等属性
4. 按钮:导出 `.bpmn` 文件、导出 `.svg` 图片
显示如下:
![image.png](https://note.youdao.com/yws/res/26285/WEBRESOURCEb17483a46ea2e29761fe3a476c42dbc3)


---

## 七、TS 类型补充(避免类型报错)
如果 TS 提示模块无类型定义,可在项目根目录创建 `types/bpmn.d.ts`:
```ts
declare module 'bpmn-js' {
  export class Modeler {
    constructor(options: any);
    importXML(xml: string): Promise<any>;
    saveXML(options?: any): Promise<{ xml: string }>;
    saveSVG(options?: any): Promise<{ svg: string }>;
    on(event: string, callback: () => void): void;
    destroy(): void;
  }
  export default class Viewer {
    constructor(options: any);
    importXML(xml: string): Promise<any>;
    destroy(): void;
  }
}

declare module 'bpmn-js-properties-panel' {
  export const BpmnPropertiesPanelModule: any;
  export const BpmnPropertiesProviderModule: any;
}
```

然后在 `tsconfig.json` 中加入:
```json
"include": ["src", "types"]
```

---

## 八、常用功能扩展(企业级必备)
### 1. 加载本地 BPMN 文件
```tsx
// 文件上传加载
const loadLocalFile = (e: React.ChangeEvent<HTMLInputElement>) => {
  const file = e.target.files?.[0];
  if (!file) return;
  const reader = new FileReader();
  reader.onload = async (ev) => {
    const xml = ev.target?.result as string;
    await modelerRef.current?.importXML(xml);
  };
  reader.readAsText(file);
};
```

### 2. 撤销 / 重做
```tsx
const undo = () => modelerRef.current?.get('commandStack').undo();
const redo = () => modelerRef.current?.get('commandStack').redo();
```

### 3. 清空画布
```tsx
const clearCanvas = () => modelerRef.current?.clear();
```

---

## 九、常见问题
1. **样式错乱**:必须引入 `bpmn-js` 自带的 CSS 文件
2. **属性面板不显示**:检查容器宽度、模块是否正确注册
3. **TS 类型报错**:使用上面的自定义类型声明
4. **React 19 兼容**:`bpmn-js` 不依赖 React 特性,**100% 兼容 React 19**

---

### 总结
1. 本教程基于 **React 19 + TS** 实现了**可用的 BPMN 设计器**,开箱即用
2. 核心分为 **Viewer(查看)** 和 **Modeler(编辑)**
3. 支持:拖拽节点、属性配置、导出 XML/SVG、实时预览
4. 可直接用于后台管理系统的**流程设计模块**

更多推荐