diff --git a/.dumi/theme/common/BehaviorMap/BehaviorMap.tsx b/.dumi/theme/common/BehaviorMap/BehaviorMap.tsx
new file mode 100644
index 0000000000..5dd49559ff
--- /dev/null
+++ b/.dumi/theme/common/BehaviorMap/BehaviorMap.tsx
@@ -0,0 +1,333 @@
+import G6 from '@antv/g6';
+import { createStyles, css } from 'antd-style';
+import { useRouteMeta } from 'dumi';
+import React, { useEffect, useRef } from 'react';
+
+G6.registerNode('behavior-start-node', {
+ draw: (cfg, group) => {
+ const textWidth = G6.Util.getTextSize(cfg!.label, 16)[0];
+ const size = [textWidth + 20 * 2, 48];
+ const keyShape = group!.addShape('rect', {
+ name: 'start-node',
+ attrs: {
+ width: size[0],
+ height: size[1],
+ y: -size[1] / 2,
+ radius: 8,
+ fill: '#fff',
+ },
+ });
+ group!.addShape('text', {
+ attrs: {
+ text: `${cfg!.label}`,
+ fill: 'rgba(0, 0, 0, 0.88)',
+ fontSize: 16,
+ fontWeight: 500,
+ x: 20,
+ textBaseline: 'middle',
+ },
+ name: 'start-node-text',
+ });
+ return keyShape;
+ },
+ getAnchorPoints() {
+ return [
+ [0, 0.5],
+ [1, 0.5],
+ ];
+ },
+});
+
+G6.registerNode(
+ 'behavior-sub-node',
+ {
+ draw: (cfg, group) => {
+ const textWidth = G6.Util.getTextSize(cfg!.label, 14)[0];
+ const padding = 16;
+ const size = [textWidth + 16 * 2 + (cfg!.targetType ? 12 : 0) + (cfg!.link ? 20 : 0), 40];
+ const keyShape = group!.addShape('rect', {
+ name: 'sub-node',
+ attrs: {
+ width: size[0],
+ height: size[1],
+ y: -size[1] / 2,
+ radius: 8,
+ fill: '#fff',
+ cursor: 'pointer',
+ },
+ });
+ group!.addShape('text', {
+ attrs: {
+ text: `${cfg!.label}`,
+ x: cfg!.targetType ? 12 + 16 : padding,
+ fill: 'rgba(0, 0, 0, 0.88)',
+ fontSize: 14,
+ textBaseline: 'middle',
+ cursor: 'pointer',
+ },
+ name: 'sub-node-text',
+ });
+ if (cfg!.targetType) {
+ group!.addShape('rect', {
+ name: 'sub-node-type',
+ attrs: {
+ width: 8,
+ height: 8,
+ radius: 4,
+ y: -4,
+ x: 12,
+ fill: cfg!.targetType === 'mvp' ? '#1677ff' : '#A0A0A0',
+ cursor: 'pointer',
+ },
+ });
+ }
+ if (cfg!.children) {
+ const { length } = cfg!.children as any;
+ group!.addShape('rect', {
+ name: 'sub-node-children-length',
+ attrs: {
+ width: 20,
+ height: 20,
+ radius: 10,
+ y: -10,
+ x: size[0] - 4,
+ fill: '#404040',
+ cursor: 'pointer',
+ },
+ });
+ group!.addShape('text', {
+ name: 'sub-node-children-length-text',
+ attrs: {
+ text: `${length}`,
+ x: size[0] + 6 - G6.Util.getTextSize(`${length}`, 12)[0] / 2,
+ textBaseline: 'middle',
+ fill: '#fff',
+ fontSize: 12,
+ cursor: 'pointer',
+ },
+ });
+ }
+ if (cfg!.link) {
+ group!.addShape('dom', {
+ attrs: {
+ width: 16,
+ height: 16,
+ x: size[0] - 12 - 16,
+ y: -8,
+ cursor: 'pointer',
+ // DOM's html
+ html: `
+
+
+
+ `,
+ },
+ // 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
+ name: 'sub-node-link',
+ });
+ }
+ return keyShape;
+ },
+ getAnchorPoints() {
+ return [
+ [0, 0.5],
+ [1, 0.5],
+ ];
+ },
+ options: {
+ stateStyles: {
+ hover: {
+ stroke: '#1677ff',
+ 'sub-node-link': {
+ html: `
+
+
+
+ `,
+ },
+ },
+ },
+ },
+ },
+ 'rect',
+);
+
+const dataTransform = (data: BehaviorMapItem) => {
+ const changeData = (d: any, level = 0) => {
+ const clonedData: any = {
+ ...d,
+ };
+ switch (level) {
+ case 0:
+ clonedData.type = 'behavior-start-node';
+ break;
+ case 1:
+ clonedData.type = 'behavior-sub-node';
+ clonedData.collapsed = true;
+ break;
+ default:
+ clonedData.type = 'behavior-sub-node';
+ break;
+ }
+
+ if (d.children) {
+ clonedData.children = d.children.map((child: any) => changeData(child, level + 1));
+ }
+ return clonedData;
+ };
+ return changeData(data);
+};
+
+type BehaviorMapItem = {
+ id: string;
+ label: string;
+ targetType?: 'mvp' | 'extension';
+ children?: BehaviorMapItem[];
+ link?: string;
+};
+
+const useStyle = createStyles(() => ({
+ container: css`
+ width: 100%;
+ height: 600px;
+ background-color: #f5f5f5;
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ overflow: hidden;
+ position: relative;
+ `,
+ title: css`
+ position: absolute;
+ top: 20px;
+ left: 20px;
+ font-size: 16px;
+ `,
+ tips: css`
+ display: flex;
+ position: absolute;
+ bottom: 20px;
+ right: 20px;
+ `,
+ mvp: css`
+ margin-right: 20px;
+ display: flex;
+ align-items: center;
+ &::before {
+ display: block;
+ width: 8px;
+ height: 8px;
+ margin-right: 8px;
+ background-color: #1677ff;
+ border-radius: 50%;
+ content: '';
+ }
+ `,
+ extension: css`
+ display: flex;
+ align-items: center;
+ &::before {
+ display: block;
+ width: 8px;
+ height: 8px;
+ margin-right: 8px;
+ background-color: #a0a0a0;
+ border-radius: 50%;
+ content: '';
+ }
+ `,
+}));
+
+export type BehaviorMapProps = {
+ data: BehaviorMapItem;
+};
+
+const BehaviorMap: React.FC = ({ data }) => {
+ const ref = useRef(null);
+ const { styles } = useStyle();
+ const meta = useRouteMeta();
+
+ useEffect(() => {
+ const graph = new G6.TreeGraph({
+ container: ref.current!,
+ width: ref.current!.scrollWidth,
+ height: ref.current!.scrollHeight,
+ renderer: 'svg',
+ modes: {
+ default: ['collapse-expand', 'drag-canvas'],
+ },
+ defaultEdge: {
+ type: 'cubic-horizontal',
+ style: {
+ lineWidth: 1,
+ stroke: '#BFBFBF',
+ },
+ },
+ layout: {
+ type: 'mindmap',
+ direction: 'LR',
+ getHeight: () => 48,
+ getWidth: (node: any) => G6.Util.getTextSize(node.label, 16)[0] + 20 * 2,
+ getVGap: () => 10,
+ getHGap: () => 60,
+ getSide: (node: any) => node.data.direction,
+ },
+ });
+
+ graph.on('node:mouseenter', (e) => {
+ graph.setItemState(e.item!, 'hover', true);
+ });
+ graph.on('node:mouseleave', (e) => {
+ graph.setItemState(e.item!, 'hover', false);
+ });
+ graph.on('node:click', (e) => {
+ const { link } = e.item!.getModel();
+ if (link) {
+ window.location.hash = link as string;
+ }
+ });
+
+ graph.data(dataTransform(data));
+ graph.render();
+ graph.fitCenter();
+ }, []);
+
+ return (
+
+
{`${meta.frontmatter.title} 行为模式地图`}
+
+
+ );
+};
+
+export default BehaviorMap;
diff --git a/.dumi/theme/common/BehaviorMap/index.tsx b/.dumi/theme/common/BehaviorMap/index.tsx
index 5dd49559ff..8b919751b1 100644
--- a/.dumi/theme/common/BehaviorMap/index.tsx
+++ b/.dumi/theme/common/BehaviorMap/index.tsx
@@ -1,333 +1,41 @@
-import G6 from '@antv/g6';
-import { createStyles, css } from 'antd-style';
-import { useRouteMeta } from 'dumi';
-import React, { useEffect, useRef } from 'react';
+import type { FC } from 'react';
+import React, { Suspense } from 'react';
+import { Skeleton } from 'antd';
+import { createStyles } from 'antd-style';
+import type { BehaviorMapProps } from './BehaviorMap';
-G6.registerNode('behavior-start-node', {
- draw: (cfg, group) => {
- const textWidth = G6.Util.getTextSize(cfg!.label, 16)[0];
- const size = [textWidth + 20 * 2, 48];
- const keyShape = group!.addShape('rect', {
- name: 'start-node',
- attrs: {
- width: size[0],
- height: size[1],
- y: -size[1] / 2,
- radius: 8,
- fill: '#fff',
- },
- });
- group!.addShape('text', {
- attrs: {
- text: `${cfg!.label}`,
- fill: 'rgba(0, 0, 0, 0.88)',
- fontSize: 16,
- fontWeight: 500,
- x: 20,
- textBaseline: 'middle',
- },
- name: 'start-node-text',
- });
- return keyShape;
- },
- getAnchorPoints() {
- return [
- [0, 0.5],
- [1, 0.5],
- ];
- },
-});
+const InternalBehaviorMap = React.lazy(() => import('./BehaviorMap'));
-G6.registerNode(
- 'behavior-sub-node',
- {
- draw: (cfg, group) => {
- const textWidth = G6.Util.getTextSize(cfg!.label, 14)[0];
- const padding = 16;
- const size = [textWidth + 16 * 2 + (cfg!.targetType ? 12 : 0) + (cfg!.link ? 20 : 0), 40];
- const keyShape = group!.addShape('rect', {
- name: 'sub-node',
- attrs: {
- width: size[0],
- height: size[1],
- y: -size[1] / 2,
- radius: 8,
- fill: '#fff',
- cursor: 'pointer',
- },
- });
- group!.addShape('text', {
- attrs: {
- text: `${cfg!.label}`,
- x: cfg!.targetType ? 12 + 16 : padding,
- fill: 'rgba(0, 0, 0, 0.88)',
- fontSize: 14,
- textBaseline: 'middle',
- cursor: 'pointer',
- },
- name: 'sub-node-text',
- });
- if (cfg!.targetType) {
- group!.addShape('rect', {
- name: 'sub-node-type',
- attrs: {
- width: 8,
- height: 8,
- radius: 4,
- y: -4,
- x: 12,
- fill: cfg!.targetType === 'mvp' ? '#1677ff' : '#A0A0A0',
- cursor: 'pointer',
- },
- });
- }
- if (cfg!.children) {
- const { length } = cfg!.children as any;
- group!.addShape('rect', {
- name: 'sub-node-children-length',
- attrs: {
- width: 20,
- height: 20,
- radius: 10,
- y: -10,
- x: size[0] - 4,
- fill: '#404040',
- cursor: 'pointer',
- },
- });
- group!.addShape('text', {
- name: 'sub-node-children-length-text',
- attrs: {
- text: `${length}`,
- x: size[0] + 6 - G6.Util.getTextSize(`${length}`, 12)[0] / 2,
- textBaseline: 'middle',
- fill: '#fff',
- fontSize: 12,
- cursor: 'pointer',
- },
- });
- }
- if (cfg!.link) {
- group!.addShape('dom', {
- attrs: {
- width: 16,
- height: 16,
- x: size[0] - 12 - 16,
- y: -8,
- cursor: 'pointer',
- // DOM's html
- html: `
-
-
-
- `,
- },
- // 在 G6 3.3 及之后的版本中,必须指定 name,可以是任意字符串,但需要在同一个自定义元素类型中保持唯一性
- name: 'sub-node-link',
- });
- }
- return keyShape;
- },
- getAnchorPoints() {
- return [
- [0, 0.5],
- [1, 0.5],
- ];
- },
- options: {
- stateStyles: {
- hover: {
- stroke: '#1677ff',
- 'sub-node-link': {
- html: `
-
-
-
- `,
- },
- },
- },
- },
- },
- 'rect',
-);
-
-const dataTransform = (data: BehaviorMapItem) => {
- const changeData = (d: any, level = 0) => {
- const clonedData: any = {
- ...d,
- };
- switch (level) {
- case 0:
- clonedData.type = 'behavior-start-node';
- break;
- case 1:
- clonedData.type = 'behavior-sub-node';
- clonedData.collapsed = true;
- break;
- default:
- clonedData.type = 'behavior-sub-node';
- break;
- }
-
- if (d.children) {
- clonedData.children = d.children.map((child: any) => changeData(child, level + 1));
- }
- return clonedData;
- };
- return changeData(data);
-};
-
-type BehaviorMapItem = {
- id: string;
- label: string;
- targetType?: 'mvp' | 'extension';
- children?: BehaviorMapItem[];
- link?: string;
-};
-
-const useStyle = createStyles(() => ({
- container: css`
+const useStyle = createStyles(({ token, css }) => ({
+ fallback: css`
width: 100%;
- height: 600px;
- background-color: #f5f5f5;
- border: 1px solid #e8e8e8;
- border-radius: 8px;
- overflow: hidden;
- position: relative;
+ > * {
+ width: 100%;
+ border-radius: 8px;
+ }
`,
- title: css`
- position: absolute;
- top: 20px;
- left: 20px;
+ placeholder: css`
+ color: ${token.colorTextDescription};
font-size: 16px;
`,
- tips: css`
- display: flex;
- position: absolute;
- bottom: 20px;
- right: 20px;
- `,
- mvp: css`
- margin-right: 20px;
- display: flex;
- align-items: center;
- &::before {
- display: block;
- width: 8px;
- height: 8px;
- margin-right: 8px;
- background-color: #1677ff;
- border-radius: 50%;
- content: '';
- }
- `,
- extension: css`
- display: flex;
- align-items: center;
- &::before {
- display: block;
- width: 8px;
- height: 8px;
- margin-right: 8px;
- background-color: #a0a0a0;
- border-radius: 50%;
- content: '';
- }
- `,
}));
-export type BehaviorMapProps = {
- data: BehaviorMapItem;
-};
-
-const BehaviorMap: React.FC = ({ data }) => {
- const ref = useRef(null);
+const BehaviorMapFallback = () => {
const { styles } = useStyle();
- const meta = useRouteMeta();
-
- useEffect(() => {
- const graph = new G6.TreeGraph({
- container: ref.current!,
- width: ref.current!.scrollWidth,
- height: ref.current!.scrollHeight,
- renderer: 'svg',
- modes: {
- default: ['collapse-expand', 'drag-canvas'],
- },
- defaultEdge: {
- type: 'cubic-horizontal',
- style: {
- lineWidth: 1,
- stroke: '#BFBFBF',
- },
- },
- layout: {
- type: 'mindmap',
- direction: 'LR',
- getHeight: () => 48,
- getWidth: (node: any) => G6.Util.getTextSize(node.label, 16)[0] + 20 * 2,
- getVGap: () => 10,
- getHGap: () => 60,
- getSide: (node: any) => node.data.direction,
- },
- });
-
- graph.on('node:mouseenter', (e) => {
- graph.setItemState(e.item!, 'hover', true);
- });
- graph.on('node:mouseleave', (e) => {
- graph.setItemState(e.item!, 'hover', false);
- });
- graph.on('node:click', (e) => {
- const { link } = e.item!.getModel();
- if (link) {
- window.location.hash = link as string;
- }
- });
-
- graph.data(dataTransform(data));
- graph.render();
- graph.fitCenter();
- }, []);
return (
-
-
{`${meta.frontmatter.title} 行为模式地图`}
-
+
+
+ 正在载入行为模式地图...
+
);
};
+const BehaviorMap: FC
= (props) => (
+ }>
+
+
+);
+
export default BehaviorMap;
diff --git a/.dumirc.ts b/.dumirc.ts
index b3fa1e6917..ac25d459ef 100644
--- a/.dumirc.ts
+++ b/.dumirc.ts
@@ -40,6 +40,9 @@ export default defineConfig({
analytics: {
ga_v2: 'UA-72788897-1',
},
+ analyze: {
+ analyzerPort: 'auto',
+ },
links: [
{
rel: 'preload',