Browse Source

feat: Add the `GGEditor` demo (#3810)

* feat: Add the `GGEditor` demo

* style: Update the size of the card

* feat: Extract the `MenuItem` and `ToolbarButton` components

* feat: Add the shape type of the line

* feat: Extract the `DetailForm` components

* feat: add PageHeader

* fix: add all local laug

* fix: Fix the height of the editor
pull/3827/head
高力 8 years ago
committed by 陈帅
parent
commit
1978073929
  1. 23
      config/router.config.js
  2. 1
      package.json
  3. 29
      public/ggeditor/flow/decision.svg
  4. 29
      public/ggeditor/flow/model.svg
  5. 29
      public/ggeditor/flow/normal.svg
  6. 29
      public/ggeditor/flow/start.svg
  7. 29
      public/ggeditor/koni/bank.svg
  8. 29
      public/ggeditor/koni/country.svg
  9. 14
      public/ggeditor/koni/icon.svg
  10. 29
      public/ggeditor/koni/person.svg
  11. 2
      src/locales/en-US.js
  12. 11
      src/locales/en-US/editor.js
  13. 4
      src/locales/en-US/menu.js
  14. 2
      src/locales/pt-BR.js
  15. 11
      src/locales/pt-BR/editor.js
  16. 4
      src/locales/pt-BR/menu.js
  17. 2
      src/locales/zh-CN.js
  18. 9
      src/locales/zh-CN/editor.js
  19. 4
      src/locales/zh-CN/menu.js
  20. 2
      src/locales/zh-TW.js
  21. 9
      src/locales/zh-TW/editor.js
  22. 4
      src/locales/zh-TW/menu.js
  23. 43
      src/pages/Editor/GGEditor/Flow/index.js
  24. 41
      src/pages/Editor/GGEditor/Flow/index.less
  25. 45
      src/pages/Editor/GGEditor/Koni/index.js
  26. 33
      src/pages/Editor/GGEditor/Koni/shape/nodes/KoniCustomNode.js
  27. 40
      src/pages/Editor/GGEditor/Mind/index.js
  28. 7
      src/pages/Editor/GGEditor/common/IconFont/index.js
  29. 36
      src/pages/Editor/GGEditor/components/EditorContextMenu/FlowContextMenu.js
  30. 3
      src/pages/Editor/GGEditor/components/EditorContextMenu/KoniContextMenu.js
  31. 20
      src/pages/Editor/GGEditor/components/EditorContextMenu/MenuItem.js
  32. 24
      src/pages/Editor/GGEditor/components/EditorContextMenu/MindContextMenu.js
  33. 5
      src/pages/Editor/GGEditor/components/EditorContextMenu/index.js
  34. 39
      src/pages/Editor/GGEditor/components/EditorContextMenu/index.less
  35. 129
      src/pages/Editor/GGEditor/components/EditorDetailPanel/DetailForm.js
  36. 29
      src/pages/Editor/GGEditor/components/EditorDetailPanel/FlowDetailPanel.js
  37. 3
      src/pages/Editor/GGEditor/components/EditorDetailPanel/KoniDetailPanel.js
  38. 20
      src/pages/Editor/GGEditor/components/EditorDetailPanel/MindDetailPanel.js
  39. 5
      src/pages/Editor/GGEditor/components/EditorDetailPanel/index.js
  40. 10
      src/pages/Editor/GGEditor/components/EditorDetailPanel/index.less
  41. 55
      src/pages/Editor/GGEditor/components/EditorItemPanel/FlowItemPanel.js
  42. 51
      src/pages/Editor/GGEditor/components/EditorItemPanel/KoniItemPanel.js
  43. 4
      src/pages/Editor/GGEditor/components/EditorItemPanel/index.js
  44. 20
      src/pages/Editor/GGEditor/components/EditorItemPanel/index.less
  45. 13
      src/pages/Editor/GGEditor/components/EditorMinimap/index.js
  46. 32
      src/pages/Editor/GGEditor/components/EditorToolbar/FlowToolbar.js
  47. 3
      src/pages/Editor/GGEditor/components/EditorToolbar/KoniToolbar.js
  48. 27
      src/pages/Editor/GGEditor/components/EditorToolbar/MindToolbar.js
  49. 24
      src/pages/Editor/GGEditor/components/EditorToolbar/ToolbarButton.js
  50. 5
      src/pages/Editor/GGEditor/components/EditorToolbar/index.js
  51. 39
      src/pages/Editor/GGEditor/components/EditorToolbar/index.less
  52. 129
      src/pages/Editor/GGEditor/mock/worldCup2018.json

23
config/router.config.js

@ -93,6 +93,29 @@ export default [
},
],
},
// editor
{
name: 'editor',
icon: 'highlight',
path: '/editor',
routes: [
{
path: '/editor/flow',
name: 'flow',
component: './Editor/GGEditor/Flow',
},
{
path: '/editor/mind',
name: 'mind',
component: './Editor/GGEditor/Mind',
},
{
path: '/editor/koni',
name: 'koni',
component: './Editor/GGEditor/Koni',
},
],
},
// list
{
path: '/list',

1
package.json

@ -39,6 +39,7 @@
"classnames": "^2.2.6",
"dva": "^2.4.1",
"enquire-js": "^0.2.1",
"gg-editor": "^2.0.2",
"hash.js": "^1.1.7",
"lodash": "^4.17.11",
"lodash-decorators": "^6.0.1",

29
public/ggeditor/flow/decision.svg

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="86px" height="78px" viewBox="0 0 86 78" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 49.1 (51147) - http://www.bohemiancoding.com/sketch -->
<title>Group 3</title>
<desc>Created with Sketch.</desc>
<defs>
<path d="M42.6703722,1.66995728 L77.6353026,33.0218945 C79.2800648,34.4967003 79.4178417,37.0256099 77.9430359,38.6703722 C77.8461185,38.7784583 77.7433887,38.8811881 77.6353026,38.9781055 L42.6703722,70.3300427 C41.1508289,71.6925687 38.8491711,71.6925687 37.3296278,70.3300427 L2.36469744,38.9781055 C0.719935183,37.5032997 0.582158272,34.9743901 2.0569641,33.3296278 C2.1538815,33.2215417 2.25661129,33.1188119 2.36469744,33.0218945 L37.3296278,1.66995728 C38.8491711,0.307431303 41.1508289,0.307431303 42.6703722,1.66995728 Z" id="path-1"></path>
<filter x="-8.8%" y="-6.9%" width="117.5%" height="119.4%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Flow-01" transform="translate(-7.000000, -184.000000)">
<g id="Group-3" transform="translate(10.000000, 185.000000)">
<g id="Polygon">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use fill-opacity="0.92" fill="#E6FFFB" fill-rule="evenodd" xlink:href="#path-1"></use>
<path stroke="#5CDBD3" stroke-width="1" d="M42.3365757,2.04222047 C41.0069753,0.850010239 38.9930247,0.850010239 37.6634243,2.04222047 L2.69849396,33.3941577 C2.60391858,33.4789604 2.51403002,33.568849 2.42922729,33.6634243 C1.13877219,35.1025913 1.25932699,37.3153872 2.69849396,38.6058423 L37.6634243,69.9577795 C38.9930247,71.1499898 41.0069753,71.1499898 42.3365757,69.9577795 L77.301506,38.6058423 C77.3960814,38.5210396 77.48597,38.431151 77.5707727,38.3365757 C78.8612278,36.8974087 78.740673,34.6846128 77.301506,33.3941577 L42.3365757,2.04222047 Z"></path>
</g>
<text id="decision" font-family="PingFangSC-Regular, PingFang SC" font-size="12" font-weight="normal" line-spacing="12" fill="#000000" fill-opacity="0.65">
<tspan x="18" y="42">Decision</tspan>
</text>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

29
public/ggeditor/flow/model.svg

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="88px" height="56px" viewBox="0 0 88 56" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 49.1 (51147) - http://www.bohemiancoding.com/sketch -->
<title>Group 4</title>
<desc>Created with Sketch.</desc>
<defs>
<rect id="path-1" x="0" y="0" width="80" height="48" rx="24"></rect>
<filter x="-8.8%" y="-10.4%" width="117.5%" height="129.2%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Flow-01" transform="translate(-102.000000, -195.000000)">
<g id="Group-4" transform="translate(106.000000, 197.000000)">
<g id="Rectangle-15-Copy-35">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use fill-opacity="0.92" fill="#F9F0FF" fill-rule="evenodd" xlink:href="#path-1"></use>
<rect stroke="#B37FEB" stroke-width="1" x="0.5" y="0.5" width="79" height="47" rx="23.5"></rect>
</g>
<text id="model" font-family="PingFangSC-Regular, PingFang SC" font-size="12" font-weight="normal" line-spacing="12" fill="#000000" fill-opacity="0.65">
<tspan x="24" y="29">Model</tspan>
</text>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

29
public/ggeditor/flow/normal.svg

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="88px" height="56px" viewBox="0 0 88 56" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 49.1 (51147) - http://www.bohemiancoding.com/sketch -->
<title>Group</title>
<desc>Created with Sketch.</desc>
<defs>
<rect id="path-1" x="0" y="0" width="80" height="48" rx="4"></rect>
<filter x="-8.8%" y="-10.4%" width="117.5%" height="129.2%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Flow-01" transform="translate(-6.000000, -105.000000)">
<g id="Group" transform="translate(10.000000, 107.000000)">
<g id="Rectangle-15-Copy">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use fill-opacity="0.92" fill="#E6F7FF" fill-rule="evenodd" xlink:href="#path-1"></use>
<rect stroke="#1890FF" stroke-width="1" x="0.5" y="0.5" width="79" height="47" rx="4"></rect>
</g>
<text id="normal" font-family="PingFangSC-Regular, PingFang SC" font-size="12" font-weight="normal" line-spacing="12" fill="#000000" fill-opacity="0.65">
<tspan x="21" y="29">Normal</tspan>
</text>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

29
public/ggeditor/flow/start.svg

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="80px" height="80px" viewBox="0 0 80 80" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 49.1 (51147) - http://www.bohemiancoding.com/sketch -->
<title>Group 2</title>
<desc>Created with Sketch.</desc>
<defs>
<circle id="path-1" cx="36" cy="36" r="36"></circle>
<filter x="-9.7%" y="-6.9%" width="119.4%" height="119.4%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.04 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Flow-01" transform="translate(-106.000000, -93.000000)">
<g id="Group-2" transform="translate(110.000000, 95.000000)">
<g id="Oval">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use fill-opacity="0.92" fill="#FFF2E8" fill-rule="evenodd" xlink:href="#path-1"></use>
<circle stroke="#FFC069" stroke-width="1" cx="36" cy="36" r="35.5"></circle>
</g>
<text id="start" font-family="PingFangSC-Regular, PingFang SC" font-size="12" font-weight="normal" line-spacing="12" fill="#000000" fill-opacity="0.65">
<tspan x="23" y="41">Start</tspan>
</text>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

29
public/ggeditor/koni/bank.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.6 KiB

29
public/ggeditor/koni/country.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.6 KiB

14
public/ggeditor/koni/icon.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.5 KiB

29
public/ggeditor/koni/person.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.6 KiB

2
src/locales/en-US.js

@ -10,6 +10,7 @@ import settingDrawer from './en-US/settingDrawer';
import settings from './en-US/settings';
import pwa from './en-US/pwa';
import component from './en-US/component';
import editor from './en-US/editor';
export default {
'navBar.lang': 'Languages',
@ -32,4 +33,5 @@ export default {
...settings,
...pwa,
...component,
...editor,
};

11
src/locales/en-US/editor.js

@ -0,0 +1,11 @@
export default {
'app.editor.flow.title': 'Flowchart Editor',
'app.editor.flow.description':
'The flow chart is an excellent way to represent the idea of the algorithm.',
'app.editor.koni.title': 'Koni Editor',
'app.editor.koni.description':
'The topology diagram refers to the network structure diagram composed of network node devices and communication media.',
'app.editor.mind.title': 'Mind Map Editor',
'app.editor.mind.description':
'The brain map is an effective graphical thinking tool for expressing divergent thinking. It is simple but effective and is a practical thinking tool.',
};

4
src/locales/en-US/menu.js

@ -38,4 +38,8 @@ export default {
'menu.account.settings': 'Account Settings',
'menu.account.trigger': 'Trigger Error',
'menu.account.logout': 'Logout',
'menu.editor': 'Editor',
'menu.editor.flow': 'Flow Editor',
'menu.editor.mind': 'Mind Editor',
'menu.editor.koni': 'Koni Editor',
};

2
src/locales/pt-BR.js

@ -10,6 +10,7 @@ import settingDrawer from './pt-BR/settingDrawer';
import settings from './pt-BR/settings';
import pwa from './pt-BR/pwa';
import component from './pt-BR/component';
import editor from './pt-BR/editor';
export default {
'navBar.lang': 'Idiomas',
@ -32,4 +33,5 @@ export default {
...settings,
...pwa,
...component,
...editor,
};

11
src/locales/pt-BR/editor.js

@ -0,0 +1,11 @@
export default {
'app.editor.flow.title': 'Editor de diagrama de flujo',
'app.editor.flow.description':
'El diagrama de flujo es una excelente manera de representar la idea del algoritmo.',
'app.editor.koni.title': 'Editor de topologia',
'app.editor.koni.description':
'El diagrama de topología se refiere al diagrama de estructura de red compuesto por dispositivos de nodo de red y medios de comunicación.',
'app.editor.mind.title': 'Editor de mapas cerebrales',
'app.editor.mind.description':
'El mapa cerebral es una herramienta de pensamiento gráfico eficaz para expresar el pensamiento divergente. Es simple pero efectivo y es una herramienta de pensamiento práctico.',
};

4
src/locales/pt-BR/menu.js

@ -38,4 +38,8 @@ export default {
'menu.account.settings': 'Configurar Conta',
'menu.account.trigger': 'Disparar Erro',
'menu.account.logout': 'Sair',
'menu.editor': 'Editor',
'menu.editor.flow': 'Flow Editor',
'menu.editor.mind': 'Mind Editor',
'menu.editor.koni': 'Koni Editor',
};

2
src/locales/zh-CN.js

@ -10,6 +10,7 @@ import settingDrawer from './zh-CN/settingDrawer';
import settings from './zh-CN/settings';
import pwa from './zh-CN/pwa';
import component from './zh-CN/component';
import editor from './zh-CN/editor';
export default {
'navBar.lang': '语言',
@ -32,4 +33,5 @@ export default {
...settings,
...pwa,
...component,
...editor,
};

9
src/locales/zh-CN/editor.js

@ -0,0 +1,9 @@
export default {
'app.editor.flow.title': '流程图编辑器',
'app.editor.flow.description': '千言万语不如一张图,流程图是表示算法思路的好方法',
'app.editor.koni.title': '拓扑编辑器',
'app.editor.koni.description': '拓扑结构图是指由网络节点设备和通信介质构成的网络结构图',
'app.editor.mind.title': '脑图编辑器',
'app.editor.mind.description':
'脑图是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。',
};

4
src/locales/zh-CN/menu.js

@ -38,4 +38,8 @@ export default {
'menu.account.settings': '个人设置',
'menu.account.trigger': '触发报错',
'menu.account.logout': '退出登录',
'menu.editor': '编辑页',
'menu.editor.flow': '流程编辑器',
'menu.editor.mind': '脑图编辑器',
'menu.editor.koni': '拓扑编辑器',
};

2
src/locales/zh-TW.js

@ -10,6 +10,7 @@ import settingDrawer from './zh-TW/settingDrawer';
import settings from './zh-TW/settings';
import pwa from './zh-TW/pwa';
import component from './zh-TW/component';
import editor from './zh-TW/editor';
export default {
'navBar.lang': '語言',
@ -32,4 +33,5 @@ export default {
...settings,
...pwa,
...component,
...editor,
};

9
src/locales/zh-TW/editor.js

@ -0,0 +1,9 @@
export default {
'app.editor.flow.title': '流程圖編輯器',
'app.editor.flow.description': '千言萬語不如一張圖,流程圖是表示算法思路的好方法',
'app.editor.koni.title': '拓撲編輯器',
'app.editor.koni.description': '拓撲結構圖是指由網絡節點設備和通信介質構成的網絡結構圖',
'app.editor.mind.title': '腦圖編輯器',
'app.editor.mind.description':
'腦圖是表達發散性思維的有效圖形思維工具 ,它簡單卻又很有效,是一種實用性的思維工具',
};

4
src/locales/zh-TW/menu.js

@ -38,4 +38,8 @@ export default {
'menu.exception.not-find': '404',
'menu.exception.server-error': '500',
'menu.exception.trigger': '触发错误',
'menu.editor': '編輯頁',
'menu.editor.flow': '流程編輯器',
'menu.editor.mind': '腦圖編輯器',
'menu.editor.koni': '拓撲編輯器',
};

43
src/pages/Editor/GGEditor/Flow/index.js

@ -0,0 +1,43 @@
import React from 'react';
import { Row, Col } from 'antd';
import GGEditor, { Flow } from 'gg-editor';
import EditorMinimap from '../components/EditorMinimap';
import { FlowContextMenu } from '../components/EditorContextMenu';
import { FlowToolbar } from '../components/EditorToolbar';
import { FlowItemPanel } from '../components/EditorItemPanel';
import { FlowDetailPanel } from '../components/EditorDetailPanel';
import styles from './index.less';
import { FormattedMessage } from 'umi/locale';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
const FlowPage = () => {
return (
<PageHeaderWrapper
title={<FormattedMessage id="app.editor.flow.title" />}
content={<FormattedMessage id="app.editor.flow.description" />}
>
<GGEditor className={styles.editor}>
<Row type="flex" className={styles.editorHd}>
<Col span={24}>
<FlowToolbar />
</Col>
</Row>
<Row type="flex" className={styles.editorBd}>
<Col span={4} className={styles.editorSidebar}>
<FlowItemPanel />
</Col>
<Col span={16} className={styles.editorContent}>
<Flow className={styles.flow} />
</Col>
<Col span={4} className={styles.editorSidebar}>
<FlowDetailPanel />
<EditorMinimap />
</Col>
</Row>
<FlowContextMenu />
</GGEditor>
</PageHeaderWrapper>
);
};
export default FlowPage;

41
src/pages/Editor/GGEditor/Flow/index.less

@ -0,0 +1,41 @@
.editor {
display: flex;
flex: 1;
flex-direction: column;
width: 100%;
height: calc(100vh - 250px);
background: #fff;
}
.editorHd {
padding: 8px;
border: 1px solid #e6e9ed;
}
.editorBd {
flex: 1;
}
.editorSidebar,
.editorContent {
display: flex;
flex-direction: column;
}
.editorSidebar {
background: #fafafa;
&:first-child {
border-right: 1px solid #e6e9ed;
}
&:last-child {
border-left: 1px solid #e6e9ed;
}
}
.flow,
.mind,
.koni {
flex: 1;
}

45
src/pages/Editor/GGEditor/Koni/index.js

@ -0,0 +1,45 @@
import React from 'react';
import { Row, Col } from 'antd';
import GGEditor, { Koni } from 'gg-editor';
import EditorMinimap from '../components/EditorMinimap';
import { KoniContextMenu } from '../components/EditorContextMenu';
import { KoniToolbar } from '../components/EditorToolbar';
import { KoniItemPanel } from '../components/EditorItemPanel';
import { KoniDetailPanel } from '../components/EditorDetailPanel';
import KoniCustomNode from './shape/nodes/KoniCustomNode';
import styles from '../Flow/index.less';
import { FormattedMessage } from 'umi/locale';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
const KoniPage = () => {
return (
<PageHeaderWrapper
title={<FormattedMessage id="app.editor.koni.title" />}
content={<FormattedMessage id="app.editor.koni.description" />}
>
<GGEditor className={styles.editor}>
<Row type="flex" className={styles.editorHd}>
<Col span={24}>
<KoniToolbar />
</Col>
</Row>
<Row type="flex" className={styles.editorBd}>
<Col span={4} className={styles.editorSidebar}>
<KoniItemPanel />
</Col>
<Col span={16} className={styles.editorContent}>
<Koni className={styles.koni} />
</Col>
<Col span={4} className={styles.editorSidebar}>
<KoniDetailPanel />
<EditorMinimap />
</Col>
</Row>
<KoniCustomNode />
<KoniContextMenu />
</GGEditor>
</PageHeaderWrapper>
);
};
export default KoniPage;

33
src/pages/Editor/GGEditor/Koni/shape/nodes/KoniCustomNode.js

@ -0,0 +1,33 @@
import React from 'react';
import { RegisterNode } from 'gg-editor';
class KoniCustomNode extends React.Component {
render() {
const config = {
draw(item) {
const keyShape = this.drawKeyShape(item);
// draw label
this.drawLabel(item);
// draw image
const group = item.getGraphicGroup();
const model = item.getModel();
group.addShape('image', {
attrs: {
x: -7,
y: -7,
img: model.icon,
},
});
return keyShape;
},
};
return <RegisterNode name="koni-custom-node" config={config} />;
}
}
export default KoniCustomNode;

40
src/pages/Editor/GGEditor/Mind/index.js

@ -0,0 +1,40 @@
import React from 'react';
import { Row, Col } from 'antd';
import GGEditor, { Mind } from 'gg-editor';
import EditorMinimap from '../components/EditorMinimap';
import { MindContextMenu } from '../components/EditorContextMenu';
import { MindToolbar } from '../components/EditorToolbar';
import { MindDetailPanel } from '../components/EditorDetailPanel';
import data from '../mock/worldCup2018.json';
import styles from '../Flow/index.less';
import { FormattedMessage } from 'umi/locale';
import PageHeaderWrapper from '@/components/PageHeaderWrapper';
const MindPage = () => {
return (
<PageHeaderWrapper
title={<FormattedMessage id="app.editor.mind.title" />}
content={<FormattedMessage id="app.editor.mind.description" />}
>
<GGEditor className={styles.editor}>
<Row type="flex" className={styles.editorHd}>
<Col span={24}>
<MindToolbar />
</Col>
</Row>
<Row type="flex" className={styles.editorBd}>
<Col span={20} className={styles.editorContent}>
<Mind data={data} className={styles.mind} />
</Col>
<Col span={4} className={styles.editorSidebar}>
<MindDetailPanel />
<EditorMinimap />
</Col>
</Row>
<MindContextMenu />
</GGEditor>
</PageHeaderWrapper>
);
};
export default MindPage;

7
src/pages/Editor/GGEditor/common/IconFont/index.js

@ -0,0 +1,7 @@
import { Icon } from 'antd';
const IconFont = Icon.createFromIconfontCN({
scriptUrl: 'https://at.alicdn.com/t/font_1101588_01zniftxm9yp.js',
});
export default IconFont;

36
src/pages/Editor/GGEditor/components/EditorContextMenu/FlowContextMenu.js

@ -0,0 +1,36 @@
import React from 'react';
import { NodeMenu, EdgeMenu, GroupMenu, MultiMenu, CanvasMenu, ContextMenu } from 'gg-editor';
import MenuItem from './MenuItem';
import styles from './index.less';
const FlowContextMenu = () => {
return (
<ContextMenu className={styles.contextMenu}>
<NodeMenu>
<MenuItem command="copy" />
<MenuItem command="delete" />
</NodeMenu>
<EdgeMenu>
<MenuItem command="delete" />
</EdgeMenu>
<GroupMenu>
<MenuItem command="copy" />
<MenuItem command="delete" />
<MenuItem command="unGroup" icon="ungroup" text="Ungroup" />
</GroupMenu>
<MultiMenu>
<MenuItem command="copy" />
<MenuItem command="paste" />
<MenuItem command="addGroup" icon="group" text="Add Group" />
<MenuItem command="delete" />
</MultiMenu>
<CanvasMenu>
<MenuItem command="undo" />
<MenuItem command="redo" />
<MenuItem command="pasteHere" icon="paste" text="Paste Here" />
</CanvasMenu>
</ContextMenu>
);
};
export default FlowContextMenu;

3
src/pages/Editor/GGEditor/components/EditorContextMenu/KoniContextMenu.js

@ -0,0 +1,3 @@
import FlowContextMenu from './FlowContextMenu';
export default FlowContextMenu;

20
src/pages/Editor/GGEditor/components/EditorContextMenu/MenuItem.js

@ -0,0 +1,20 @@
import React from 'react';
import { Command } from 'gg-editor';
import upperFirst from 'lodash/upperFirst';
import IconFont from '../../common/IconFont';
import styles from './index.less';
const MenuItem = props => {
const { command, icon, text } = props;
return (
<Command name={command}>
<div className={styles.item}>
<IconFont type={`icon-${icon || command}`} />
<span>{text || upperFirst(command)}</span>
</div>
</Command>
);
};
export default MenuItem;

24
src/pages/Editor/GGEditor/components/EditorContextMenu/MindContextMenu.js

@ -0,0 +1,24 @@
import React from 'react';
import { NodeMenu, CanvasMenu, ContextMenu } from 'gg-editor';
import MenuItem from './MenuItem';
import styles from './index.less';
const MindContextMenu = () => {
return (
<ContextMenu className={styles.contextMenu}>
<NodeMenu>
<MenuItem command="append" text="Topic" />
<MenuItem command="appendChild" icon="append-child" text="Subtopic" />
<MenuItem command="collapse" text="Fold" />
<MenuItem command="expand" text="Unfold" />
<MenuItem command="delete" />
</NodeMenu>
<CanvasMenu>
<MenuItem command="undo" />
<MenuItem command="redo" />
</CanvasMenu>
</ContextMenu>
);
};
export default MindContextMenu;

5
src/pages/Editor/GGEditor/components/EditorContextMenu/index.js

@ -0,0 +1,5 @@
import FlowContextMenu from './FlowContextMenu';
import MindContextMenu from './MindContextMenu';
import KoniContextMenu from './KoniContextMenu';
export { FlowContextMenu, MindContextMenu, KoniContextMenu };

39
src/pages/Editor/GGEditor/components/EditorContextMenu/index.less

@ -0,0 +1,39 @@
.contextMenu {
display: none;
overflow: hidden;
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
.item {
display: flex;
align-items: center;
padding: 5px 12px;
cursor: pointer;
transition: all 0.3s;
user-select: none;
&:hover {
background: #e6f7ff;
}
i {
margin-right: 8px;
}
}
:global {
.disable {
:local {
.item {
color: rgba(0, 0, 0, 0.25);
cursor: auto;
&:hover {
background: #fff;
}
}
}
}
}
}

129
src/pages/Editor/GGEditor/components/EditorDetailPanel/DetailForm.js

@ -0,0 +1,129 @@
import React, { Fragment } from 'react';
import { Card, Form, Input, Select } from 'antd';
import { withPropsAPI } from 'gg-editor';
import upperFirst from 'lodash/upperFirst';
const { Item } = Form;
const { Option } = Select;
const inlineFormItemLayout = {
labelCol: {
sm: { span: 8 },
},
wrapperCol: {
sm: { span: 16 },
},
};
class DetailForm extends React.Component {
get item() {
const { propsAPI } = this.props;
return propsAPI.getSelected()[0];
}
handleSubmit = e => {
if (e && e.preventDefault) {
e.preventDefault();
}
const { form, propsAPI } = this.props;
const { getSelected, executeCommand, update } = propsAPI;
setTimeout(() => {
form.validateFieldsAndScroll((err, values) => {
if (err) {
return;
}
const item = getSelected()[0];
if (!item) {
return;
}
executeCommand(() => {
update(item, {
...values,
});
});
});
}, 0);
};
renderEdgeShapeSelect = () => {
return (
<Select onChange={this.handleSubmit}>
<Option value="flow-smooth">Smooth</Option>
<Option value="flow-polyline">Polyline</Option>
<Option value="flow-polyline-round">Polyline Round</Option>
</Select>
);
};
renderNodeDetail = () => {
const { form } = this.props;
const { label } = this.item.getModel();
return (
<Item label="Label" {...inlineFormItemLayout}>
{form.getFieldDecorator('label', {
initialValue: label,
})(<Input onBlur={this.handleSubmit} />)}
</Item>
);
};
renderEdgeDetail = () => {
const { form } = this.props;
const { label = '', shape = 'flow-smooth' } = this.item.getModel();
return (
<Fragment>
<Item label="Label" {...inlineFormItemLayout}>
{form.getFieldDecorator('label', {
initialValue: label,
})(<Input onBlur={this.handleSubmit} />)}
</Item>
<Item label="Shape" {...inlineFormItemLayout}>
{form.getFieldDecorator('shape', {
initialValue: shape,
})(this.renderEdgeShapeSelect())}
</Item>
</Fragment>
);
};
renderGroupDetail = () => {
const { form } = this.props;
const { label = '新建分组' } = this.item.getModel();
return (
<Item label="Label" {...inlineFormItemLayout}>
{form.getFieldDecorator('label', {
initialValue: label,
})(<Input onBlur={this.handleSubmit} />)}
</Item>
);
};
render() {
const { type } = this.props;
if (!this.item) {
return null;
}
return (
<Card type="inner" size="small" title={upperFirst(type)} bordered={false}>
<Form onSubmit={this.handleSubmit}>
{type === 'node' && this.renderNodeDetail()}
{type === 'edge' && this.renderEdgeDetail()}
{type === 'group' && this.renderGroupDetail()}
</Form>
</Card>
);
}
}
export default Form.create()(withPropsAPI(DetailForm));

29
src/pages/Editor/GGEditor/components/EditorDetailPanel/FlowDetailPanel.js

@ -0,0 +1,29 @@
import React from 'react';
import { Card } from 'antd';
import { NodePanel, EdgePanel, GroupPanel, MultiPanel, CanvasPanel, DetailPanel } from 'gg-editor';
import DetailForm from './DetailForm';
import styles from './index.less';
const FlowDetailPanel = () => {
return (
<DetailPanel className={styles.detailPanel}>
<NodePanel>
<DetailForm type="node" />
</NodePanel>
<EdgePanel>
<DetailForm type="edge" />
</EdgePanel>
<GroupPanel>
<DetailForm type="group" />
</GroupPanel>
<MultiPanel>
<Card type="inner" size="small" title="Multi Select" bordered={false} />
</MultiPanel>
<CanvasPanel>
<Card type="inner" size="small" title="Canvas" bordered={false} />
</CanvasPanel>
</DetailPanel>
);
};
export default FlowDetailPanel;

3
src/pages/Editor/GGEditor/components/EditorDetailPanel/KoniDetailPanel.js

@ -0,0 +1,3 @@
import FlowDetailPanel from './FlowDetailPanel';
export default FlowDetailPanel;

20
src/pages/Editor/GGEditor/components/EditorDetailPanel/MindDetailPanel.js

@ -0,0 +1,20 @@
import React from 'react';
import { Card } from 'antd';
import { NodePanel, CanvasPanel, DetailPanel } from 'gg-editor';
import DetailForm from './DetailForm';
import styles from './index.less';
const MindDetailPanel = () => {
return (
<DetailPanel className={styles.detailPanel}>
<NodePanel>
<DetailForm type="node" />
</NodePanel>
<CanvasPanel>
<Card type="inner" size="small" title="Canvas" bordered={false} />
</CanvasPanel>
</DetailPanel>
);
};
export default MindDetailPanel;

5
src/pages/Editor/GGEditor/components/EditorDetailPanel/index.js

@ -0,0 +1,5 @@
import FlowDetailPanel from './FlowDetailPanel';
import MindDetailPanel from './MindDetailPanel';
import KoniDetailPanel from './KoniDetailPanel';
export { FlowDetailPanel, MindDetailPanel, KoniDetailPanel };

10
src/pages/Editor/GGEditor/components/EditorDetailPanel/index.less

@ -0,0 +1,10 @@
.detailPanel {
flex: 1;
background: #fafafa;
:global {
.ant-card {
background: #fafafa;
}
}
}

55
src/pages/Editor/GGEditor/components/EditorItemPanel/FlowItemPanel.js

@ -0,0 +1,55 @@
import React from 'react';
import { Card } from 'antd';
import { ItemPanel, Item } from 'gg-editor';
import styles from './index.less';
const FlowItemPanel = () => {
return (
<ItemPanel className={styles.itemPanel}>
<Card bordered={false}>
<Item
type="node"
size="72*72"
shape="flow-circle"
model={{
color: '#FA8C16',
label: 'Start',
}}
src="/ggeditor/flow/start.svg"
/>
<Item
type="node"
size="80*48"
shape="flow-rect"
model={{
color: '#1890FF',
label: 'Normal',
}}
src="/ggeditor/flow/normal.svg"
/>
<Item
type="node"
size="80*72"
shape="flow-rhombus"
model={{
color: '#13C2C2',
label: 'Decision',
}}
src="/ggeditor/flow/decision.svg"
/>
<Item
type="node"
size="80*48"
shape="flow-capsule"
model={{
color: '#722ED1',
label: 'Model',
}}
src="/ggeditor/flow/model.svg"
/>
</Card>
</ItemPanel>
);
};
export default FlowItemPanel;

51
src/pages/Editor/GGEditor/components/EditorItemPanel/KoniItemPanel.js

@ -0,0 +1,51 @@
import React from 'react';
import { Card } from 'antd';
import { ItemPanel, Item } from 'gg-editor';
import styles from './index.less';
const KoniItemPanel = () => {
return (
<ItemPanel className={styles.itemPanel}>
<Card bordered={false}>
<Item
type="node"
size="40"
shape="koni-custom-node"
model={{
color: '#69C0FF',
label: 'Bank',
labelOffsetY: 28,
icon: '/ggeditor/koni/icon.svg',
}}
src="/ggeditor/koni/bank.svg"
/>
<Item
type="node"
size="40"
shape="koni-custom-node"
model={{
color: '#5CDBD3',
label: 'Person',
labelOffsetY: 28,
icon: '/ggeditor/koni/icon.svg',
}}
src="/ggeditor/koni/person.svg"
/>
<Item
type="node"
size="40"
shape="koni-custom-node"
model={{
color: '#B37FEB',
label: 'Country',
labelOffsetY: 28,
icon: '/ggeditor/koni/icon.svg',
}}
src="/ggeditor/koni/country.svg"
/>
</Card>
</ItemPanel>
);
};
export default KoniItemPanel;

4
src/pages/Editor/GGEditor/components/EditorItemPanel/index.js

@ -0,0 +1,4 @@
import FlowItemPanel from './FlowItemPanel';
import KoniItemPanel from './KoniItemPanel';
export { FlowItemPanel, KoniItemPanel };

20
src/pages/Editor/GGEditor/components/EditorItemPanel/index.less

@ -0,0 +1,20 @@
.itemPanel {
flex: 1;
background: #fafafa;
:global {
.ant-card {
background: #fafafa;
}
.ant-card-body {
display: flex;
flex-direction: column;
align-items: center;
> div {
margin-bottom: 16px;
}
}
}
}

13
src/pages/Editor/GGEditor/components/EditorMinimap/index.js

@ -0,0 +1,13 @@
import React from 'react';
import { Card } from 'antd';
import { Minimap } from 'gg-editor';
const EditorMinimap = () => {
return (
<Card type="inner" size="small" title="Minimap" bordered={false}>
<Minimap height={200} />
</Card>
);
};
export default EditorMinimap;

32
src/pages/Editor/GGEditor/components/EditorToolbar/FlowToolbar.js

@ -0,0 +1,32 @@
import React from 'react';
import { Divider } from 'antd';
import { Toolbar } from 'gg-editor';
import ToolbarButton from './ToolbarButton';
import styles from './index.less';
const FlowToolbar = () => {
return (
<Toolbar className={styles.toolbar}>
<ToolbarButton command="undo" />
<ToolbarButton command="redo" />
<Divider type="vertical" />
<ToolbarButton command="copy" />
<ToolbarButton command="paste" />
<ToolbarButton command="delete" />
<Divider type="vertical" />
<ToolbarButton command="zoomIn" icon="zoom-in" text="Zoom In" />
<ToolbarButton command="zoomOut" icon="zoom-out" text="Zoom Out" />
<ToolbarButton command="autoZoom" icon="fit-map" text="Fit Map" />
<ToolbarButton command="resetZoom" icon="actual-size" text="Actual Size" />
<Divider type="vertical" />
<ToolbarButton command="toBack" icon="to-back" text="To Back" />
<ToolbarButton command="toFront" icon="to-front" text="To Front" />
<Divider type="vertical" />
<ToolbarButton command="multiSelect" icon="multi-select" text="Multi Select" />
<ToolbarButton command="addGroup" icon="group" text="Add Group" />
<ToolbarButton command="unGroup" icon="ungroup" text="Ungroup" />
</Toolbar>
);
};
export default FlowToolbar;

3
src/pages/Editor/GGEditor/components/EditorToolbar/KoniToolbar.js

@ -0,0 +1,3 @@
import FlowToolbar from './FlowToolbar';
export default FlowToolbar;

27
src/pages/Editor/GGEditor/components/EditorToolbar/MindToolbar.js

@ -0,0 +1,27 @@
import React from 'react';
import { Divider } from 'antd';
import { Toolbar } from 'gg-editor';
import ToolbarButton from './ToolbarButton';
import styles from './index.less';
const FlowToolbar = () => {
return (
<Toolbar className={styles.toolbar}>
<ToolbarButton command="undo" />
<ToolbarButton command="redo" />
<Divider type="vertical" />
<ToolbarButton command="zoomIn" icon="zoom-in" text="Zoom In" />
<ToolbarButton command="zoomOut" icon="zoom-out" text="Zoom Out" />
<ToolbarButton command="autoZoom" icon="fit-map" text="Fit Map" />
<ToolbarButton command="resetZoom" icon="actual-size" text="Actual Size" />
<Divider type="vertical" />
<ToolbarButton command="append" text="Topic" />
<ToolbarButton command="appendChild" icon="append-child" text="Subtopic" />
<Divider type="vertical" />
<ToolbarButton command="collapse" text="Fold" />
<ToolbarButton command="expand" text="Unfold" />
</Toolbar>
);
};
export default FlowToolbar;

24
src/pages/Editor/GGEditor/components/EditorToolbar/ToolbarButton.js

@ -0,0 +1,24 @@
import React from 'react';
import { Tooltip } from 'antd';
import { Command } from 'gg-editor';
import upperFirst from 'lodash/upperFirst';
import IconFont from '../../common/IconFont';
import styles from './index.less';
const ToolbarButton = props => {
const { command, icon, text } = props;
return (
<Command name={command}>
<Tooltip
title={text || upperFirst(command)}
placement="bottom"
overlayClassName={styles.tooltip}
>
<IconFont type={`icon-${icon || command}`} />
</Tooltip>
</Command>
);
};
export default ToolbarButton;

5
src/pages/Editor/GGEditor/components/EditorToolbar/index.js

@ -0,0 +1,5 @@
import FlowToolbar from './FlowToolbar';
import MindToolbar from './MindToolbar';
import KoniToolbar from './KoniToolbar';
export { FlowToolbar, MindToolbar, KoniToolbar };

39
src/pages/Editor/GGEditor/components/EditorToolbar/index.less

@ -0,0 +1,39 @@
.toolbar {
display: flex;
align-items: center;
:global {
.command i {
display: inline-block;
width: 27px;
height: 27px;
margin: 0 6px;
padding-top: 6px;
text-align: center;
border: 1px solid #fff;
cursor: pointer;
&:hover {
border: 1px solid #e6e9ed;
}
}
.disable i {
color: rgba(0, 0, 0, 0.25);
cursor: auto;
&:hover {
border: 1px solid #fff;
}
}
}
}
.tooltip {
:global {
.ant-tooltip-inner {
font-size: 12px;
border-radius: 0;
}
}
}

129
src/pages/Editor/GGEditor/mock/worldCup2018.json

@ -0,0 +1,129 @@
{
"roots": [
{
"label": "法国",
"children": [
{
"label": "克罗地亚",
"side": "left",
"children": [
{
"label": "克罗地亚",
"children": [
{
"label": "克罗地亚",
"children": [
{
"label": "克罗地亚"
},
{
"label": "丹麦"
}
]
},
{
"label": "俄罗斯",
"children": [
{
"label": "俄罗斯"
},
{
"label": "西班牙"
}
]
}
]
},
{
"label": "英格兰",
"children": [
{
"label": "英格兰",
"children": [
{
"label": "英格兰"
},
{
"label": "哥伦比亚"
}
]
},
{
"label": "瑞典",
"children": [
{
"label": "瑞士"
},
{
"label": "瑞典"
}
]
}
]
}
]
},
{
"label": "法国",
"side": "right",
"children": [
{
"label": "法国",
"children": [
{
"label": "法国",
"children": [
{
"label": "法国"
},
{
"label": "阿根廷"
}
]
},
{
"label": "乌拉圭",
"children": [
{
"label": "乌拉圭"
},
{
"label": "葡萄牙"
}
]
}
]
},
{
"label": "比利时",
"children": [
{
"label": "比利时",
"children": [
{
"label": "比利时"
},
{
"label": "日本"
}
]
},
{
"label": "巴西",
"children": [
{
"label": "巴西"
},
{
"label": "墨西哥"
}
]
}
]
}
]
}
]
}
]
}
Loading…
Cancel
Save