# ra-arch
**Repository Path**: rasir/ra-arch
## Basic Information
- **Project Name**: ra-arch
- **Description**: 这是一款基于`antv/x6`的用于系统架构设计可视化的一款工具。
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: feature/V1.0.1
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2021-12-08
- **Last Updated**: 2022-08-16
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
### 前言
这是一款基于`antv/x6`的用于系统架构设计可视化的一款工具。
#### 效果图

### 快速上手
#### 说明
本组件是在`react`框架下的前端组件,推荐`react@16.8`以上版本。推荐使用`umijs@3.x`脚手架。其他必要依赖见下文。
目前,作品还有很多需要完善的地方,有些兼容没有处理,推荐使用 mac + 谷歌最新浏览器。这样能体验到更好的滚动效果。
#### 安装方式
通过 npm 安装
`npm i ra-arch -S`
#### 必要依赖
```
"dependencies": {
"@antv/x6": "^1.28.1",
"ahooks": "^2.10.12",
"antd": "^4.17.1"
}
```
#### 使用方式
```
import React from 'react';
import RaArch from 'ra-arch';
import { Button } from 'antd';
import 'ra-arch/es/components/assets/font/iconfont.js';
import appIcon from './icon/app.png';
....
import bmsIcon from './icon/bms.png';
const data = {
"lanes": [
{ "id": "user", "name": "用户层", "height": 120 },
{ "id": "access", "name": "接入层", "height": 150 },
{ "id": "app", "name": "应用逻辑层", "height": 200 },
{ "id": "store", "name": "存储层", "height": 150 }
],
"nodes": [
{
"id": "1",
"x": 100,
"y": 180,
"name": "ARCH-VIEW-DUBBO-K8S-SIT",
"moduleType": "bmr",
"systemType": "inner",
"preId": "access"
},
{
"id": "2",
"x": 100,
"y": 380,
"name": "ARCH-VIEW-DUBBO-K8S-SIT",
"moduleType": "tidb",
"systemType": "inner",
"preId": "app"
},
{
"id": "3",
"x": 300,
"y": 380,
"name": "ARCH-VIEW-DUBBO-K8S-SIT",
"moduleType": "browser",
"systemType": "outer",
"preId": "app"
},
{
"id": "4",
"x": 300,
"y": 30,
"name": "ARCH-VIEW-DUBBO-K8S-SIT",
"moduleType": "app",
"systemType": "outer",
"preId": "user"
},
{
"moduleType": "jetty",
"name": "ceshi2222",
"systemType": "inner",
"x": 450,
"y": 380,
"preId": "app",
"id": "1flj7sg14",
"label": "Jetty",
"icon": "jetty",
"value": "jetty",
"type": "node",
"image": "jettyModule",
"bgImage": "innerSystem"
},
{
"moduleType": "jetty",
"name": "ceshi11111",
"systemType": "inner",
"x": 600,
"y": 380,
"preId": "app",
"id": "1flj7sg1422",
"label": "Jetty",
"icon": "jetty",
"value": "jetty",
"type": "node",
"image": "jettyModule",
"bgImage": "innerSystem"
},
],
"links": [{ "source": "1", "target": "2" }]
}
const moduleConfig = {
title: '架构自助设计系统',
search: {
placeholder: '请输入组件名称',
},
options: [
{
label: '用户层组件',
value: 'userLayer',
children: [
{
label: 'USER',
icon: 'user',
moduleType: 'user',
moduleImage: 'user',
value: 'user',
target: ['user'],
moduleProp: [
{
type: 'INPUT',
label: '用户名称',
name: 'userName',
inputProps: {
allowClear: true,
placeholder: '请输入用户名称',
className: 'search-input',
},
rules: [
{
required: true,
message: '请选择使用时长',
},
],
},
{
label: '使用时长',
value: '',
name: 'time',
type: 'SELECT',
inputProps: {
placeholder: '请选择使用时长',
options: [
{ label: '1周', value: '1week' },
{ label: '1月', value: '1month' },
{ label: '1年', value: '1year' },
],
},
rules: [
{
required: true,
message: '请选择使用时长',
},
],
},
{
type: 'REMOTESELECT',
label: '负责人',
name: 'ownerId',
inputProps: {
allowClear: true,
remote: async (val: string) => {
if (!val) return [];
return [
{ label: val, value: val },
{ label: `${val}1`, value: `${val}1` },
{ label: `${val}2`, value: `${val}2` },
{ label: `${val}3`, value: `${val}3` },
];
},
className: 'search-ownerId',
placeholder: '请输入负责人ID',
},
rules: [
{
required: true,
message: '请选择使用时长',
},
],
},
],
},
],
},
{
label: '云主机',
value: 'clound',
children: [
{
label: 'Jetty',
icon: 'jetty',
value: 'jetty',
moduleProp: [],
},
{
label: 'Nginx',
icon: 'nginx',
value: 'nginx',
moduleProp: [],
},
],
},
{
label: 'K8S容器',
value: 'k8s',
children: [
{
label: 'Dubbo',
icon: 'dubbo',
value: 'dubbo',
moduleProp: [],
},
{
label: 'SpringBoot',
icon: 'springboot',
value: 'springboot',
moduleProp: [],
},
{
label: 'NODEJS',
icon: 'nodejs',
value: 'nodejs',
moduleProp: [],
},
{
label: 'PYTHON',
icon: 'python',
value: 'python',
moduleProp: [],
},
{
label: 'Jetty',
icon: 'jetty',
value: 'jetty',
moduleProp: [],
},
{
label: 'Nginx',
icon: 'nginx',
value: 'nginx',
moduleProp: [],
},
],
},
...
],
};
const assets = {
appIcon,
bmsIcon,
....
}
export default function IndexPage() {
return (
);
}
```
#### 注意
需要通过以下方式引入 iconfont
```
import 'ra-arch/es/components/assets/font/iconfont.js';
```
### 功能说明
- 生成节点,拖拽左侧组件到右侧,会有弹窗让用户填写相关信息然后生成节点。
- 调整节点尺寸,单击节点,出现工具端口,拖动端口调整节点尺寸。
- 右键删除节点 右键点击节点,在右键菜单中点击删除。触发 `onRemoveNode`
- 右键编辑节点 右键单击节点,在右键菜单中点击编辑,在弹窗中对数据进行编辑。但是节点层级和组件类型不能编辑,触发 `onUpdateNode`
- 调整层级高度,鼠标移入层级下侧拖拽线,点击鼠标左键进行拖动对层级高度进行调整。
- 生成连线,在画布中从源端节点的端口拖拽连线到目标节点的端口。相同两个节点不能有重复的连线,生成节点前会调用 `onAddLink`
- 删除连线,鼠标移入链接出现删除按钮。点击按钮会触发 `onRemoveLink`
- 组件过滤,在左侧搜索框中输入内容,对下面的组件模版进行过滤
- 撤销重做,画布上的操作都可以通过撤销重做来操作。会触发 `onUndoOrRedo` 回调
- 放大 最多放大到当前画布的 `1.5` 倍
- 缩小 最小缩小到当前画布的 `0.5` 倍
- 全屏 展开整个编辑器到全屏操作
- 取消全屏
- 本地导入数据 在弹窗中拖入 `RaArchData` 结构的 `.json` 文件
- 本地导出数据 生成 `RaArchData` 结果的`.json` 文件
- 本地导出图片 将画布区域生成 `png` 图片
- 上传数据 触发 `onSave`
- 复制节点 左键单击节点,再点击复制节点按钮,节点信息将放入剪贴板中
- 粘贴节点 点击粘贴节点按钮,将触发 `onCreateNode`
- 删除节点 左键单击节点,再点击删除节点按钮。触发 `onRemoveNode`
- 清理缓存 点击清理缓存,将清除缓存数据,并触发 `reload` ,而使用 `data` 传入的数据
### 快捷键
- 复制节点到剪贴板 `ctrl+c/commond+c`
- 粘贴节点 `ctrl+v/commond+v`
- 撤销 `ctrl+z/commond+z`
- 重做 `ctrl+shift+z/commond+shift+z`
- 删除节点 `backspace/backspace`
- 放大 `ctrl+1/commond+1`
- 缩小` ctrl+2/commond+2`
- 清理缓存 `ctrl+e/commond+e`
### API
| 参数 | 说明 | 类型 | 默认值 |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ----------- |
| `assets` | 图片资源键值对 | `Object` | `{}` |
| `data` | 需要加载的数据,如果要更新数据,需要获取数据后,清理掉缓存 | `RaArchData` | `undefined` |
| `moduleConfig` | 画布左侧的组件模版,包含申请组件需要填写的数据 | `ModuleConfig` | `undefined` |
| `saveInterval` | 自动缓存数据,便于刷新页面时恢复数据单位 ms | `number\|undefined` | `10000` |
| `onAddLink` | 在画布中添加链接的回调,返回 `promise`。如果 `resolve(true)` 则建立链接,如果 `resolve(false)`则不会产生链接。 | `(link: Edge) => Promise` | `undefined` |
| `onRemoveLink` | 在画布中移除链接的回调,返回 `promise`。如果 `resolve(true)` 则移除链接,如果 `resolve(false)`则不会移除链接。 | `(links: Edge[]) => Promise` | `undefined` |
| `onRemoveNode` | 在画布中移除节点的回调,返回 `promise`。如果 `resolve(true)` 则移除节点,如果 `resolve(false)`则不会移除节点。 | `(nodes: Node[]) => Promise` | `undefined` |
| `onCreateNode` | 在画布中新建节点的回调,返回 `promise`。如果 `resolve(true)` 则创建新节点,如果 `resolve(false)`则不会创建节点。 | `(nodeData: any) => Promise` | `undefined` |
| `onUpdateNode` | 在画布中编辑节点的回调,返回 `promise`。如果 `resolve(true)` 则更新节点数据,如果 `resolve(false)`则不会更新节点数据。 | `(nodeData: any) => Promise` | `undefined` |
| `onUndoOrRedo` | 在画布中撤销和重做的回调。因为数据是之前已经产生过的,不需要用户再次确认,但可能存在与后端的交互,所以开放给开发者进行处理。节点数据更新不会放入撤销和重做队列中 | `(type: 'undo' \| 'redo',graph: Graph,args: { cmds: any[]; options: any }) => Promise` | `undefined` |
| `onSave` | 将画布中数据保存至后端,返回 `promise`。如果 `resolve(true)` 则保存成功,如果 `resolve(false)`则保存失败。 | `(data: any) => Promise` | `undefined` |
### RaArchData 输入数据结构
| 参数 | 说明 | 类型 |
| -------- | ------------ | ------------ |
| `width` | 画布宽度 | `number` |
| `height` | 画布高度 | `number` |
| `lanes` | 节点层级配置 | `NodeData[]` |
| `nodes` | 组件节点 | `NodeData[]` |
| `links` | 组件连线 | `LinkData[]` |
### NodeData 数据结构
| 参数 | 说明 | 类型 |
| ------------ | --------------------------------------------------------------- | ------------------------------ |
| `id` | 节点 ID (必需) | `string` |
| `name` | 节点名称 (必需) | `string` |
| `type` | 节点类型 | `"lane"\|"node"` |
| `width` | 节点宽度 | `number` |
| `height` | 节点高度 | `number` |
| `zIndex` | 节点层级 默认 1 | `number` |
| `x` | 节点 x 坐标 | `number` |
| `y` | 节点 y 坐标 | `number` |
| `moduleType` | 节点组件类型 (必需) | `string` |
| `systemType` | 节点系统类型 (必需) 内部系统(`inner`) 外部系统(`outer`) | `"inner"\|"outer"` |
| `preId` | 节点所在层级的 ID (必需) | `string` |
| `image` | 节点组件图形,可根据`${moduleType.toLowerCase()}Module`自动生成 | `string` |
| `bgImage` | 节点系统图形,可根据`${systemType.toLowerCase()}System`自动生成 | `"innerSystem"\|"outerSystem"` |
### LinkData 数据结构
| 参数 | 说明 | 类型 |
| -------- | ---------------------- | ------------------- |
| `source` | 链接源端节点 ID | `string \| number` |
| `target` | 链接目标端节点 ID | `string \| number ` |
| `zIndex` | 链接所在层次(非必需) | `number` |
### ModuleConfig 左侧组件类型定义
| 参数 | 说明 | 类型 |
| --------- | ------------------------------------ | ------------------------------------ |
| `title` | 左侧组件标题 | `string` |
| `search` | 是否需要搜索组件,以及搜索功能的配置 | `undefined\|{placeholder?: string;}` |
| `options` | 左侧组件 | `ModuleGropuItem[]` |
#### ModuleGropuItem
| 参数 | 说明 | 类型 |
| ---------- | -------- | -------------------- |
| `label` | 显示内容 | `string` |
| `value` | 标记 | `string` |
| `children` | 组件群 | `ModuleConfigItem[]` |
#### ModuleConfigItem
| 参数 | 说明 | 类型 |
| ------------- | ------------------------------------------------------------------------------------------------------------ | -------------------- |
| `label` | 显示内容 | `string` |
| `value` | 标记 | `string` |
| `moduleImage` | 图标 可根据`assets[moduleImage]\|\| assets[${moduleType ? moduleType : value}Module\|\|moduleImage `获取图片 | `ModuleConfigItem[]` |
| `target` | 组件只能放入哪些层级,如果为 undefined 或者[]默认可以放入所有层级 | `string[]` |
| `moduleProp` | 组件模版参数表单使用 | `ModulePropItem[]` |
#### ModulePropItem
| 参数 | 说明 | 类型 |
| ----------- | --------------------- | ----- |
| `InputProp` | antd 表单录入组件属性 | `any` |
| `...` | antd FormItem 属性 | `any` |