aldehyde 0.2.472 → 0.2.474

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/lib/controls/entity-select/entity-select.js +1 -1
  2. package/lib/controls/entity-select/entity-select.js.map +1 -1
  3. package/lib/controls/entry-control.js +2 -2
  4. package/lib/controls/entry-control.js.map +1 -1
  5. package/lib/controls/icon-selector/icon/phonenode-menu-icon/iconfont.css +47 -3
  6. package/lib/controls/icon-selector/icon/phonenode-menu-icon/iconfont.js +12 -12
  7. package/lib/controls/icon-selector/icon/phonenode-menu-icon/iconfont.js.map +1 -1
  8. package/lib/controls/icon-selector/icon/phonenode-menu-icon/iconfont.json +77 -0
  9. package/lib/controls/icon-selector/icon/phonenode-menu-icon/iconfont.ttf +0 -0
  10. package/lib/controls/icon-selector/icon/phonenode-menu-icon/iconfont.woff +0 -0
  11. package/lib/controls/icon-selector/icon/phonenode-menu-icon/iconfont.woff2 +0 -0
  12. package/lib/draw-canvas/edit/components/asset-bar/index.d.ts +5 -0
  13. package/lib/draw-canvas/edit/components/asset-bar/index.d.ts.map +1 -0
  14. package/lib/draw-canvas/edit/components/asset-bar/index.js +78 -0
  15. package/lib/draw-canvas/edit/components/asset-bar/index.js.map +1 -0
  16. package/lib/draw-canvas/edit/components/asset-bar/index.less +36 -0
  17. package/lib/draw-canvas/edit/components/main-header/index.d.ts +14 -0
  18. package/lib/draw-canvas/edit/components/main-header/index.d.ts.map +1 -0
  19. package/lib/draw-canvas/edit/components/main-header/index.js +163 -0
  20. package/lib/draw-canvas/edit/components/main-header/index.js.map +1 -0
  21. package/lib/draw-canvas/edit/components/main-header/index.less +21 -0
  22. package/lib/draw-canvas/edit/components/render/index.d.ts +86 -0
  23. package/lib/draw-canvas/edit/components/render/index.d.ts.map +1 -0
  24. package/lib/draw-canvas/edit/components/render/index.js +686 -0
  25. package/lib/draw-canvas/edit/components/render/index.js.map +1 -0
  26. package/lib/draw-canvas/edit/components/render/types.d.ts +243 -0
  27. package/lib/draw-canvas/edit/components/render/types.d.ts.map +1 -0
  28. package/lib/draw-canvas/edit/components/render/types.js +66 -0
  29. package/lib/draw-canvas/edit/components/render/types.js.map +1 -0
  30. package/lib/draw-canvas/edit/components/setting-form/index.d.ts +19 -0
  31. package/lib/draw-canvas/edit/components/setting-form/index.d.ts.map +1 -0
  32. package/lib/draw-canvas/edit/components/setting-form/index.js +164 -0
  33. package/lib/draw-canvas/edit/components/setting-form/index.js.map +1 -0
  34. package/lib/draw-canvas/edit/index.d.ts +5 -0
  35. package/lib/draw-canvas/edit/index.d.ts.map +1 -0
  36. package/lib/draw-canvas/edit/index.js +112 -0
  37. package/lib/draw-canvas/edit/index.js.map +1 -0
  38. package/lib/draw-canvas/edit/index.less +34 -0
  39. package/lib/form/form-Item-group.d.ts.map +1 -1
  40. package/lib/form/form-Item-group.js +1 -1
  41. package/lib/form/form-Item-group.js.map +1 -1
  42. package/lib/icon/local-aliIcon/iconfont.js +5 -5
  43. package/lib/icon/local-aliIcon/iconfont.js.map +1 -1
  44. package/lib/table/relation-table.d.ts +4 -0
  45. package/lib/table/relation-table.d.ts.map +1 -1
  46. package/lib/tmpl/hcservice-v3.d.ts +1 -0
  47. package/lib/tmpl/hcservice-v3.d.ts.map +1 -1
  48. package/lib/tmpl/hcservice-v3.js +27 -0
  49. package/lib/tmpl/hcservice-v3.js.map +1 -1
  50. package/lib/tmpl/interface.d.ts +4 -0
  51. package/lib/tmpl/interface.d.ts.map +1 -1
  52. package/lib/tmpl/interface.js.map +1 -1
  53. package/lib/units/index.d.ts +1 -0
  54. package/lib/units/index.d.ts.map +1 -1
  55. package/lib/units/index.js +16 -0
  56. package/lib/units/index.js.map +1 -1
  57. package/package.json +1 -1
  58. package/src/aldehyde/controls/entity-select/entity-select.tsx +1 -1
  59. package/src/aldehyde/controls/entry-control.tsx +2 -2
  60. package/src/aldehyde/controls/icon-selector/icon/phonenode-menu-icon/iconfont.css +47 -3
  61. package/src/aldehyde/controls/icon-selector/icon/phonenode-menu-icon/iconfont.js +1 -1
  62. package/src/aldehyde/controls/icon-selector/icon/phonenode-menu-icon/iconfont.json +77 -0
  63. package/src/aldehyde/controls/icon-selector/icon/phonenode-menu-icon/iconfont.ttf +0 -0
  64. package/src/aldehyde/controls/icon-selector/icon/phonenode-menu-icon/iconfont.woff +0 -0
  65. package/src/aldehyde/controls/icon-selector/icon/phonenode-menu-icon/iconfont.woff2 +0 -0
  66. package/src/aldehyde/draw-canvas/edit/components/asset-bar/index.less +36 -0
  67. package/src/aldehyde/draw-canvas/edit/components/asset-bar/index.tsx +93 -0
  68. package/src/aldehyde/draw-canvas/edit/components/main-header/index.less +21 -0
  69. package/src/aldehyde/draw-canvas/edit/components/main-header/index.tsx +187 -0
  70. package/src/aldehyde/draw-canvas/edit/components/render/draws/bg-draw.ts +98 -0
  71. package/src/aldehyde/draw-canvas/edit/components/render/draws/contextmenu-draw.ts +307 -0
  72. package/src/aldehyde/draw-canvas/edit/components/render/draws/graph-draw.ts +251 -0
  73. package/src/aldehyde/draw-canvas/edit/components/render/draws/index.ts +7 -0
  74. package/src/aldehyde/draw-canvas/edit/components/render/draws/link-draw.ts +1416 -0
  75. package/src/aldehyde/draw-canvas/edit/components/render/draws/preview-draw.ts +257 -0
  76. package/src/aldehyde/draw-canvas/edit/components/render/draws/ref-line-draw.ts +72 -0
  77. package/src/aldehyde/draw-canvas/edit/components/render/draws/ruler-draw.ts +167 -0
  78. package/src/aldehyde/draw-canvas/edit/components/render/graphs/base-graph.ts +241 -0
  79. package/src/aldehyde/draw-canvas/edit/components/render/graphs/bezier.ts +542 -0
  80. package/src/aldehyde/draw-canvas/edit/components/render/graphs/circle.ts +700 -0
  81. package/src/aldehyde/draw-canvas/edit/components/render/graphs/curve.ts +501 -0
  82. package/src/aldehyde/draw-canvas/edit/components/render/graphs/index.ts +6 -0
  83. package/src/aldehyde/draw-canvas/edit/components/render/graphs/line.ts +494 -0
  84. package/src/aldehyde/draw-canvas/edit/components/render/graphs/rect.ts +681 -0
  85. package/src/aldehyde/draw-canvas/edit/components/render/handlers/drag-handlers.ts +69 -0
  86. package/src/aldehyde/draw-canvas/edit/components/render/handlers/drag-outside-handlers.ts +162 -0
  87. package/src/aldehyde/draw-canvas/edit/components/render/handlers/graph-handlers.ts +108 -0
  88. package/src/aldehyde/draw-canvas/edit/components/render/handlers/index.ts +9 -0
  89. package/src/aldehyde/draw-canvas/edit/components/render/handlers/key-move-handlers.ts +50 -0
  90. package/src/aldehyde/draw-canvas/edit/components/render/handlers/link-handlers.ts +46 -0
  91. package/src/aldehyde/draw-canvas/edit/components/render/handlers/selection-handlers.ts +385 -0
  92. package/src/aldehyde/draw-canvas/edit/components/render/handlers/shutcut-handlers.ts +46 -0
  93. package/src/aldehyde/draw-canvas/edit/components/render/handlers/text-handlers.ts +82 -0
  94. package/src/aldehyde/draw-canvas/edit/components/render/handlers/zoom-handlers.ts +60 -0
  95. package/src/aldehyde/draw-canvas/edit/components/render/index.ts +768 -0
  96. package/src/aldehyde/draw-canvas/edit/components/render/tools/align-tool.ts +91 -0
  97. package/src/aldehyde/draw-canvas/edit/components/render/tools/asset-tool.ts +142 -0
  98. package/src/aldehyde/draw-canvas/edit/components/render/tools/attract-tool.ts +440 -0
  99. package/src/aldehyde/draw-canvas/edit/components/render/tools/copy-tool.ts +269 -0
  100. package/src/aldehyde/draw-canvas/edit/components/render/tools/import-export-tool.ts +603 -0
  101. package/src/aldehyde/draw-canvas/edit/components/render/tools/index.ts +9 -0
  102. package/src/aldehyde/draw-canvas/edit/components/render/tools/link-tool.ts +225 -0
  103. package/src/aldehyde/draw-canvas/edit/components/render/tools/position-tool.ts +212 -0
  104. package/src/aldehyde/draw-canvas/edit/components/render/tools/selection-tool.ts +132 -0
  105. package/src/aldehyde/draw-canvas/edit/components/render/tools/z-index-tool.ts +227 -0
  106. package/src/aldehyde/draw-canvas/edit/components/render/types.ts +287 -0
  107. package/src/aldehyde/draw-canvas/edit/components/render/utils/a-star.ts +116 -0
  108. package/src/aldehyde/draw-canvas/edit/components/render/utils/bezier-scene-func.ts +73 -0
  109. package/src/aldehyde/draw-canvas/edit/components/setting-form/index.tsx +200 -0
  110. package/src/aldehyde/draw-canvas/edit/index.less +34 -0
  111. package/src/aldehyde/draw-canvas/edit/index.tsx +138 -0
  112. package/src/aldehyde/form/form-Item-group.tsx +1 -0
  113. package/src/aldehyde/icon/local-aliIcon/iconfont.js +1 -1
  114. package/src/aldehyde/tmpl/hcservice-v3.tsx +14 -0
  115. package/src/aldehyde/tmpl/interface.tsx +2 -0
  116. package/src/aldehyde/units/index.tsx +15 -0
@@ -0,0 +1,187 @@
1
+ import React, { useState, useEffect, useCallback, useMemo } from 'react';
2
+ import { Dropdown, Modal, Space, Button, Divider, Tooltip } from 'antd';
3
+ import IconFont from "../../../../icon/aliIcon";
4
+ import { Render } from '../render';
5
+ import { GraphType, LinkType, AlignType } from '../render/types';
6
+ import * as Draws from '../render/draws';
7
+ import './index.less';
8
+
9
+ // 布局类型
10
+ const alignTypes = [
11
+ { label: '垂直居中', value: 'Middle', icon: "icon-middle-L" },
12
+ { label: '左对齐', value: 'Left', icon: "icon-left-L" },
13
+ { label: '右对齐', value: 'Right', icon: "icon-right-L" },
14
+ { label: '水平居中', value: 'Center', icon: "icon-center-L" },
15
+ { label: '上对齐', value: 'Top', icon: "icon-top-L" },
16
+ { label: '下对齐', value: 'Bottom', icon: "icon-bottom-L" }
17
+ ];
18
+
19
+ // 连线类型
20
+ const lineTypes = [
21
+ { label: '折线', value: 'curve', icon: "icon-zhexian" },
22
+ { label: '贝塞尔曲线', value: 'bezier', icon: "icon-bezier" },
23
+ { label: '自动', value: 'auto', icon: "icon-zhexian-auto" },
24
+ ];
25
+
26
+ // 图形类型
27
+ const graphTypes = [
28
+ { label: '折线', value: 'Curve', icon: "icon-zhexian" },
29
+ { label: '贝塞尔曲线', value: 'Bezier', icon: "icon-bezier" },
30
+ { label: '矩形', value: 'Rect', icon: 'icon-rect' },
31
+ { label: '圆', value: 'Circle', icon: 'icon-circle' },
32
+ ];
33
+
34
+ interface MainHeaderProps {
35
+ render: Render | null;
36
+ graphType?: GraphType; // 图类型
37
+ texting?: boolean; // 添加文本状态
38
+ onGraphTypeChange: (type: GraphType | undefined) => void; // 更新图类型
39
+ onTexting: (texting: boolean) => void;
40
+ }
41
+
42
+ const MainHeader = (props: MainHeaderProps) => {
43
+ const { render, graphType, onGraphTypeChange, onTexting, texting } = props;
44
+ const [scale, setScale] = useState<number>(100); // 缩放
45
+ const [history, setHistory] = useState<string[]>([]); // 历史记录
46
+ const [historyIndex, setHistoryIndex] = useState(-1); // 当前历史记录标签
47
+ const [selection, setSelection] = useState<unknown[]>([]); // 选中组件
48
+ const [currentLinkType, setCurrentLinkType] = useState<LinkType>("curve"); // 连接线类型
49
+ const [showShortCut, setShowShortCut] = useState<boolean>(false); // 快捷键显示
50
+ const [loading, setLoading] = useState<boolean>(false);
51
+
52
+ // 文件导入
53
+ const handleRestore = useCallback(() => {
54
+ if (!render) return;
55
+ const input = document.createElement('input');
56
+ input.type = 'file';
57
+ input.accept = '.json';
58
+ input.onchange = () => {
59
+ const file = input.files?.[0];
60
+ if (file) {
61
+ const reader = new FileReader();
62
+ reader.onload = async () => {
63
+ await render.importExportTool.restore(reader.result as string);
64
+ render.positionTool.positionFit();
65
+ }
66
+ reader.readAsText(file);
67
+ }
68
+ }
69
+ input.click();
70
+ }, [render]);
71
+
72
+ // 数据导出
73
+ const handleSave = useCallback((type: 'json' | 'svg' | 'asset' | 'png' | 'assetPng') => {
74
+ if (!render) return;
75
+ const data: { name: string; content: string }[] = [];
76
+ switch (type) {
77
+ case 'json':
78
+ data.push({ name: 'data.json', content: render.importExportTool.save() });
79
+ break;
80
+ case 'asset':
81
+ data.push({ name: 'asset.json', content: render.importExportTool.getAsset() });
82
+ break;
83
+ case 'png':
84
+ download('image.png', render.importExportTool.getAssetImage(2));
85
+ return;
86
+ case 'assetPng':
87
+ download('image.png', render.importExportTool.getAssetImage());
88
+ return;
89
+ }
90
+ if (data.length) {
91
+ data.forEach(({ name, content }) => download(name, content));
92
+ }
93
+ }, [render]);
94
+
95
+ // 下载辅助函数
96
+ const download = (name: string, content: string) => {
97
+ const a = document.createElement('a');
98
+ a.download = name;
99
+ a.href = content.startsWith('blob:') ? content : URL.createObjectURL(new Blob([content]));
100
+ a.click();
101
+ }
102
+
103
+ // 工具操作
104
+ const handleAlign = useCallback((type: AlignType) => {
105
+ render?.alignTool.align(type);
106
+ }, [render])
107
+
108
+ // 更改组件连接线类型
109
+ const handleLinkType = useCallback((type: LinkType) => {
110
+ (render?.draws[Draws.LinkDraw.name] as unknown as { changeLinkType: (t: LinkType) => void })?.changeLinkType(type);
111
+ }, [render])
112
+
113
+ // 重置缩放
114
+ const handleScale = useCallback((action: 'fit' | 'reset' | 'position') => {
115
+ const tool = render?.positionTool;
116
+ if (!tool) return;
117
+ action === 'fit' ? tool.positionFit() : action === 'reset' ? tool.positionZoomReset() : tool.positionReset();
118
+ }, [render])
119
+
120
+ // 更改图类型
121
+ const handleGraph = (type: GraphType) => onGraphTypeChange(graphType === type ? undefined : type);
122
+
123
+ // 绑定事件
124
+ useEffect(() => {
125
+ if (!render) return;
126
+ render.on('selection-change', setSelection);
127
+ render.on('history-change', ({ records, index }: { records: string[]; index: number }) => {
128
+ setHistory(records);
129
+ setHistoryIndex(index);
130
+ });
131
+ render.on('link-type-change', setCurrentLinkType);
132
+ render.on('scale-change', (v: number) => setScale(v * 100));
133
+ render.on('loading', setLoading);
134
+ }, [render]);
135
+
136
+ // 是否选中多个组件
137
+ const isSelects = useMemo(() => selection.length <= 1, [selection]);
138
+
139
+ // 缩放菜单选项
140
+ const scaleMenu = [
141
+ { key: "fit", label: <a onClick={() => handleScale('fit')}>自适应大小</a> },
142
+ { key: "reset", label: <a onClick={() => handleScale('reset')}>重置位置大小</a> },
143
+ { key: "position", label: <a onClick={() => handleScale('position')}>仅重置位置</a> }
144
+ ];
145
+
146
+ return (
147
+ <div className="main-header">
148
+ <Space size={4} separator={<Divider vertical styles={{ root: { margin: "0 4px" } }} />}>
149
+ <Dropdown menu={{ items: scaleMenu }}>
150
+ <span style={{ cursor: "pointer" }}>{`${scale.toFixed(0)}%`}</span>
151
+ </Dropdown>
152
+ <Tooltip title="撤销">
153
+ <Button type="text" onClick={() => render?.prevHistory()} disabled={historyIndex <= 0} >↶</Button>
154
+ </Tooltip>
155
+ <Tooltip title="恢复">
156
+ <Button type="text" onClick={() => render?.nextHistory()} disabled={historyIndex >= history.length - 1} >↷</Button>
157
+ </Tooltip>
158
+ {alignTypes.map(type => (
159
+ <Tooltip title={type.label}>
160
+ <Button type="text" key={type.value} onClick={() => handleAlign(type.value as AlignType)} disabled={isSelects} ><IconFont type={type.icon} /></Button>
161
+ </Tooltip>
162
+ ))}
163
+ {lineTypes.map(type => (
164
+ <Tooltip title={`连接线:${type.label}`}>
165
+ <Button type="text" key={type.value} onClick={() => handleLinkType(type.value as LinkType)} disabled={currentLinkType === type.value}><IconFont type={type.icon} /></Button>
166
+ </Tooltip>
167
+ ))}
168
+ {graphTypes.map(type => (
169
+ <Tooltip title={`画${type.label}`}>
170
+ <Button type="text" key={type.value} onClick={() => handleGraph(type.value as GraphType)} className={graphType === type.value ? 'active' : ''} ><IconFont type={type.icon} /></Button>
171
+ </Tooltip>
172
+ ))}
173
+ <Tooltip title="插入文字">
174
+ <Button type="text" onClick={() => onTexting(true)} className={texting ? "active" : ""} ><IconFont type="icon-wenben" /></Button>
175
+ </Tooltip>
176
+ </Space>
177
+ {showShortCut &&
178
+ <Modal title="快捷键" open={showShortCut} onCancel={() => setShowShortCut(false)}>
179
+ <p>1、复制、粘贴、多选、全选、删除、上一步、下一步等快捷键与一般文档编辑器类似;</p>
180
+ <p>2、放大缩小,【Win】鼠标上滚动下滚动,【Mac】触控板双指放大、缩小;</p>
181
+ <p>3、画布拖动,在空白处,【Win】右键按下移动,【Mac】control + 触控板三指移动;</p>
182
+ </Modal>}
183
+ </div >
184
+ )
185
+ }
186
+
187
+ export default MainHeader;
@@ -0,0 +1,98 @@
1
+ import Konva from 'konva';
2
+ import { Draw, BaseDraw, Render } from '../types';
3
+
4
+ // 画布
5
+
6
+ export interface BgDrawOption {
7
+ size: number;
8
+ }
9
+
10
+ export class BgDraw extends BaseDraw implements Draw {
11
+ // @ts-ignore
12
+ static override readonly name = 'bg';
13
+ option: BgDrawOption;
14
+
15
+ constructor(render: Render, layer: Konva.Layer, option: BgDrawOption) {
16
+ super(render, layer);
17
+ this.option = option;
18
+ this.group.listening(false);
19
+ }
20
+
21
+ override draw() {
22
+ this.clear(); // 重置
23
+ const { width, height, x, y } = this.render.getStageState(); // 获取页面配置
24
+ const { pageWidth, pageHeight, background } = this.render.getPageSettings(); // 画布配置,尺寸(使用设置的 pageWidth/pageHeight)
25
+ const cellSize = this.option.size; // 网格尺寸
26
+
27
+ // 计算列数和行数(基于stage 尺寸)
28
+ const lenX = Math.ceil(this.render.toStageValue(width + this.render.rulerSize) / cellSize);
29
+ const lenY = Math.ceil(this.render.toStageValue(height + this.render.rulerSize) / cellSize);
30
+ const startX = -Math.ceil(this.render.toStageValue(x) / cellSize);
31
+ const startY = -Math.ceil(this.render.toStageValue(y) / cellSize);
32
+
33
+ const group = new Konva.Group();
34
+
35
+ // 背景层(填满整个 stage)
36
+ group.add(
37
+ new Konva.Rect({
38
+ name: `${this.constructor.name}__background`,
39
+ x: this.render.toStageValue(-x + this.render.rulerSize),
40
+ y: this.render.toStageValue(-y + this.render.rulerSize),
41
+ width: this.render.toStageValue(width),
42
+ height: this.render.toStageValue(height),
43
+ listening: false,
44
+ fill: 'transparent'
45
+ })
46
+ );
47
+
48
+ // 画布边框(使用 pageWidth/pageHeight)
49
+ group.add(
50
+ new Konva.Rect({
51
+ name: this.constructor.name,
52
+ x: 0,
53
+ y: 0,
54
+ width: pageWidth,
55
+ height: pageHeight,
56
+ stroke: 'rgba(255,0,0,0.3)',
57
+ strokeWidth: this.render.toStageValue(2),
58
+ listening: false,
59
+ dash: [this.render.toStageValue(6), this.render.toStageValue(6)],
60
+ fill: background || 'transparent'
61
+ })
62
+ );
63
+
64
+ // 网格竖线
65
+ for (let x = startX; x < lenX + startX + 2; x++) {
66
+ group.add(
67
+ new Konva.Line({
68
+ name: this.constructor.name,
69
+ points: [
70
+ [cellSize * x, this.render.toStageValue(-y + this.render.rulerSize)],
71
+ [cellSize * x, this.render.toStageValue(height - y + this.render.rulerSize)]
72
+ ].flat(),
73
+ stroke: '#ddd',
74
+ strokeWidth: this.render.toStageValue(1),
75
+ listening: false
76
+ })
77
+ );
78
+ }
79
+
80
+ // 网格横线
81
+ for (let y = startY; y < lenY + startY + 2; y++) {
82
+ group.add(
83
+ new Konva.Line({
84
+ name: this.constructor.name,
85
+ points: [
86
+ [this.render.toStageValue(-x + this.render.rulerSize), cellSize * y],
87
+ [this.render.toStageValue(width - x + this.render.rulerSize), cellSize * y]
88
+ ].flat(),
89
+ stroke: '#ddd',
90
+ strokeWidth: this.render.toStageValue(1),
91
+ listening: false
92
+ })
93
+ );
94
+ }
95
+
96
+ this.group.add(group);
97
+ }
98
+ }
@@ -0,0 +1,307 @@
1
+ import Konva from 'konva';
2
+ import { BaseDraw, Draw, Render, MouseButton } from '../types';
3
+ import { LinkDraw } from '../draws';
4
+
5
+ // 右键菜单
6
+
7
+ export interface ContextmenuDrawOption { }
8
+
9
+ export class ContextmenuDraw extends BaseDraw implements Draw {
10
+ // @ts-ignore
11
+ static override readonly name = 'contextmenu';
12
+ option: ContextmenuDrawOption;
13
+ state: {
14
+ target: Konva.Node | null, // 右键目标节点(或空白处)
15
+ menuIsMousedown: boolean, // 菜单被鼠标按下状态
16
+ lastPos: Konva.Vector2d | null, // 记录鼠标按下位置(用于判断按下和释放的时候是不是同一位置)
17
+ right: boolean, // 鼠标按下的是否是右键
18
+ }
19
+
20
+ constructor(render: Render, layer: Konva.Layer, option: ContextmenuDrawOption) {
21
+ super(render, layer);
22
+ this.option = option;
23
+ this.state = { target: null, menuIsMousedown: false, lastPos: null, right: false };
24
+ }
25
+
26
+ override draw() {
27
+ this.clear();
28
+ if (this.state.target) {
29
+ // 菜单数组
30
+ const menus: Array<{ name: string, action: (e: Konva.KonvaEventObject<MouseEvent>) => void }> = [];
31
+ if (this.state.target === this.render.stage) {
32
+ // 空白处
33
+ menus.push({
34
+ name: '自适应大小',
35
+ action: () => this.render.positionTool.positionFit()
36
+ });
37
+ menus.push({
38
+ name: '恢复位置',
39
+ action: () => this.render.positionTool.positionReset()
40
+ });
41
+ menus.push({
42
+ name: '恢复大小位置',
43
+ action: () => this.render.positionTool.positionZoomReset()
44
+ });
45
+ } else if (!this.render.config.readonly) {
46
+ if (this.state.target.name() === 'link-line') {
47
+ menus.push({
48
+ name: '删除', // 删除连接线
49
+ action: () => this.render.linkTool.remove(this.state.target as Konva.Line)
50
+ });
51
+ } else {
52
+ // 未选择:真实节点,即素材的容器 group
53
+ // 已选择:transformer
54
+ const target = this.state.target.parent;
55
+ // 目标
56
+ menus.push({
57
+ name: '复制',
58
+ action: () => {
59
+ if (target) {
60
+ this.render.copyTool.copy([target]);
61
+ }
62
+ }
63
+ });
64
+ menus.push({
65
+ name: '删除',
66
+ action: () => {
67
+ if (target) {
68
+ this.render.remove([target]);
69
+ }
70
+ }
71
+ });
72
+ menus.push({
73
+ name: '上移',
74
+ action: () => {
75
+ if (target) {
76
+ this.render.zIndexTool.up([target]);
77
+ }
78
+ }
79
+ });
80
+ menus.push({
81
+ name: '下移',
82
+ action: () => {
83
+ if (target) {
84
+ this.render.zIndexTool.down([target]);
85
+ }
86
+ }
87
+ });
88
+ menus.push({
89
+ name: '置顶',
90
+ action: () => {
91
+ if (target) {
92
+ this.render.zIndexTool.top([target]);
93
+ }
94
+ }
95
+ });
96
+ menus.push({
97
+ name: '置底',
98
+ action: () => {
99
+ if (target) {
100
+ this.render.zIndexTool.bottom([target]);
101
+ }
102
+ }
103
+ });
104
+ if (target instanceof Konva.Transformer) {
105
+ const pos = this.render.stage.getPointerPosition();
106
+ if (pos) {
107
+ // 获取所有图形
108
+ const shapes = target.nodes();
109
+ if (shapes.length > 1) {
110
+ // zIndex 倒序(大的优先)
111
+ shapes.sort((a, b) => b.zIndex() - a.zIndex());
112
+ // 提取重叠目标
113
+ const selected = shapes.find((shape) =>
114
+ // 关键 api
115
+ Konva.Util.haveIntersection(
116
+ { ...pos, width: 1, height: 1 },
117
+ shape.getClientRect()
118
+ )
119
+ );
120
+ // 对齐菜单
121
+ menus.push({
122
+ name: '垂直居中' + (selected ? '于目标' : ''),
123
+ action: () => this.render.alignTool.align("Middle", selected)
124
+ });
125
+ menus.push({
126
+ name: '左对齐' + (selected ? '于目标' : ''),
127
+ action: () => this.render.alignTool.align("Left", selected)
128
+ });
129
+ menus.push({
130
+ name: '右对齐' + (selected ? '于目标' : ''),
131
+ action: () => this.render.alignTool.align("Right", selected)
132
+ });
133
+ menus.push({
134
+ name: '水平居中' + (selected ? '于目标' : ''),
135
+ action: () => this.render.alignTool.align("Center", selected)
136
+ });
137
+ menus.push({
138
+ name: '上对齐' + (selected ? '于目标' : ''),
139
+ action: () => this.render.alignTool.align("Top", selected)
140
+ });
141
+ menus.push({
142
+ name: '下对齐' + (selected ? '于目标' : ''),
143
+ action: () => this.render.alignTool.align("Bottom", selected)
144
+ });
145
+ }
146
+ }
147
+ }
148
+ }
149
+ }
150
+
151
+ // stage 状态
152
+ const stageState = this.render.getStageState();
153
+ // 绘制右键菜单
154
+ const group = new Konva.Group({
155
+ name: 'contextmenu',
156
+ width: stageState.width,
157
+ height: stageState.height
158
+ });
159
+ let top = 0;
160
+ // 菜单每项高度
161
+ const lineHeight = 30;
162
+ const pos = this.render.stage.getPointerPosition();
163
+ if (pos) {
164
+ for (const menu of menus) {
165
+ // 框
166
+ const rect = new Konva.Rect({
167
+ x: this.render.toStageValue(pos.x - stageState.x),
168
+ y: this.render.toStageValue(pos.y + top - stageState.y),
169
+ width: this.render.toStageValue(150),
170
+ height: this.render.toStageValue(lineHeight),
171
+ fill: '#fff',
172
+ stroke: '#999',
173
+ strokeWidth: this.render.toStageValue(1),
174
+ name: 'contextmenu'
175
+ });
176
+ // 标题
177
+ const text = new Konva.Text({
178
+ x: this.render.toStageValue(pos.x - stageState.x),
179
+ y: this.render.toStageValue(pos.y + top - stageState.y),
180
+ text: menu.name,
181
+ name: 'contextmenu',
182
+ listening: false,
183
+ fontSize: this.render.toStageValue(16),
184
+ fill: '#333',
185
+ width: this.render.toStageValue(150),
186
+ height: this.render.toStageValue(lineHeight),
187
+ align: 'center',
188
+ verticalAlign: 'middle'
189
+ });
190
+ group.add(rect);
191
+ group.add(text);
192
+ // 菜单事件
193
+ rect.on('pointerclick', (e) => {
194
+ if (e.evt.button === MouseButton.左键) {
195
+ // 触发事件
196
+ menu.action(e);
197
+ // 移除菜单
198
+ this.group.getChildren().forEach((o) => {
199
+ o.destroy();
200
+ });
201
+ this.group.removeChildren();
202
+ this.state.target = null;
203
+ }
204
+ e.evt.preventDefault();
205
+ e.evt.stopPropagation();
206
+ })
207
+ rect.on('mousedown', (e) => {
208
+ if (e.evt.button === MouseButton.左键) {
209
+ this.state.menuIsMousedown = true;
210
+ // 按下效果
211
+ rect.fill('#dfdfdf');
212
+ }
213
+ e.evt.preventDefault();
214
+ e.evt.stopPropagation();
215
+ })
216
+ rect.on('mouseup', (e) => {
217
+ if (e.evt.button === MouseButton.左键) {
218
+ this.state.menuIsMousedown = false;
219
+ }
220
+ })
221
+ rect.on('mouseenter', (e) => {
222
+ if (this.state.menuIsMousedown) {
223
+ rect.fill('#dfdfdf');
224
+ } else {
225
+ // hover in
226
+ rect.fill('#efefef');
227
+ }
228
+ e.evt.preventDefault();
229
+ e.evt.stopPropagation();
230
+ })
231
+ rect.on('mouseout', () => {
232
+ // hover out
233
+ rect.fill('#fff');
234
+ })
235
+ rect.on('contextmenu', (e) => {
236
+ e.evt.preventDefault();
237
+ e.evt.stopPropagation();
238
+ })
239
+ top += lineHeight - 1;
240
+ }
241
+ }
242
+ this.group.add(group);
243
+ }
244
+ }
245
+
246
+ handlers = {
247
+ stage: {
248
+ mousedown: (e: Konva.KonvaEventObject<GlobalEventHandlersEventMap['mousedown']>) => {
249
+ this.state.lastPos = this.render.stage.getPointerPosition();
250
+ if (e.evt.button === MouseButton.左键) {
251
+ if (!this.state.menuIsMousedown) {
252
+ // 没有按下菜单,清除菜单
253
+ this.state.target = null;
254
+ this.draw();
255
+ }
256
+ } else if (e.evt.button === MouseButton.右键 && !e.evt.ctrlKey) {
257
+ // (判断 ctrlKey 为了排查 mac 拖动快捷键)
258
+ // 右键按下
259
+ this.state.right = true;
260
+ }
261
+ },
262
+ mousemove: () => {
263
+ if (this.state.target && this.state.right) {
264
+ // 拖动画布时(右键),清除菜单
265
+ this.state.target = null;
266
+ this.draw();
267
+ }
268
+ },
269
+ mouseup: () => {
270
+ this.state.right = false;
271
+ },
272
+ contextmenu: (e: Konva.KonvaEventObject<GlobalEventHandlersEventMap['contextmenu']>) => {
273
+ // (判断 ctrlKey 为了排查 mac 拖动快捷键)
274
+ if (!e.evt.ctrlKey) {
275
+ const pos = this.render.stage.getPointerPosition();
276
+ if (pos && this.state.lastPos) {
277
+ const linkGroup = this.render.layerCover.find(`.${LinkDraw.name}`)[0] as Konva.Group;
278
+ // 右键目标可能为 连接线
279
+ let lineSelection: Konva.Node | null = null;
280
+ if (linkGroup) {
281
+ const linkLines = linkGroup.find('.link-line');
282
+ for (const line of linkLines) {
283
+ if (Konva.Util.haveIntersection({ ...pos, width: 1, height: 1 }, line.getClientRect())) {
284
+ // 右键目标为 连接线
285
+ lineSelection = line;
286
+ break;
287
+ }
288
+ }
289
+ }
290
+ if (pos.x === this.state.lastPos.x || pos.y === this.state.lastPos.y) {
291
+ // 右键 连接线/其它目标
292
+ this.state.target = lineSelection ?? e.target;
293
+ } else {
294
+ this.state.target = null;
295
+ }
296
+ this.draw();
297
+ }
298
+ }
299
+ },
300
+ wheel: () => {
301
+ // 画布缩放时,清除菜单
302
+ this.state.target = null;
303
+ this.draw();
304
+ }
305
+ }
306
+ }
307
+ }