aldehyde 0.2.474 → 0.2.475

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 (148) hide show
  1. package/lib/controls/action/utils.d.ts +1 -1
  2. package/lib/controls/entity-select/entity-select.d.ts +2 -2
  3. package/lib/controls/entity-select/entity-select.d.ts.map +1 -1
  4. package/lib/controls/entity-select/entity-select.js +16 -7
  5. package/lib/controls/entity-select/entity-select.js.map +1 -1
  6. package/lib/controls/entry-control.d.ts.map +1 -1
  7. package/lib/controls/entry-control.js +1 -0
  8. package/lib/controls/entry-control.js.map +1 -1
  9. package/lib/controls/select/index.d.ts.map +1 -1
  10. package/lib/controls/select/index.js +13 -7
  11. package/lib/controls/select/index.js.map +1 -1
  12. package/lib/controls/text/index.less +1 -0
  13. package/lib/controls/view-control.d.ts.map +1 -1
  14. package/lib/controls/view-control.js +1 -0
  15. package/lib/controls/view-control.js.map +1 -1
  16. package/lib/detail/button/edit-button.d.ts.map +1 -1
  17. package/lib/detail/button/edit-button.js +23 -11
  18. package/lib/detail/button/edit-button.js.map +1 -1
  19. package/lib/detail/button/view-button.d.ts.map +1 -1
  20. package/lib/detail/button/view-button.js +21 -10
  21. package/lib/detail/button/view-button.js.map +1 -1
  22. package/lib/draw-canvas/edit/components/asset-bar/index.d.ts.map +1 -1
  23. package/lib/draw-canvas/edit/components/asset-bar/index.js +1 -0
  24. package/lib/draw-canvas/edit/components/asset-bar/index.js.map +1 -1
  25. package/lib/draw-canvas/edit/components/main-header/index.d.ts +5 -0
  26. package/lib/draw-canvas/edit/components/main-header/index.d.ts.map +1 -1
  27. package/lib/draw-canvas/edit/components/main-header/index.js +54 -14
  28. package/lib/draw-canvas/edit/components/main-header/index.js.map +1 -1
  29. package/lib/draw-canvas/edit/components/render/index.d.ts +4 -0
  30. package/lib/draw-canvas/edit/components/render/index.d.ts.map +1 -1
  31. package/lib/draw-canvas/edit/components/render/index.js +20 -14
  32. package/lib/draw-canvas/edit/components/render/index.js.map +1 -1
  33. package/lib/draw-canvas/edit/components/render/types.d.ts +5 -1
  34. package/lib/draw-canvas/edit/components/render/types.d.ts.map +1 -1
  35. package/lib/draw-canvas/edit/components/setting-form/imag-upload.d.ts +26 -0
  36. package/lib/draw-canvas/edit/components/setting-form/imag-upload.d.ts.map +1 -0
  37. package/lib/draw-canvas/edit/components/setting-form/imag-upload.js +83 -0
  38. package/lib/draw-canvas/edit/components/setting-form/imag-upload.js.map +1 -0
  39. package/lib/draw-canvas/edit/components/setting-form/index.d.ts +1 -3
  40. package/lib/draw-canvas/edit/components/setting-form/index.d.ts.map +1 -1
  41. package/lib/draw-canvas/edit/components/setting-form/index.js +94 -15
  42. package/lib/draw-canvas/edit/components/setting-form/index.js.map +1 -1
  43. package/lib/draw-canvas/edit/constant.d.ts +7 -0
  44. package/lib/draw-canvas/edit/constant.d.ts.map +1 -0
  45. package/lib/draw-canvas/edit/constant.js +7 -0
  46. package/lib/draw-canvas/edit/constant.js.map +1 -0
  47. package/lib/draw-canvas/edit/index.d.ts +4 -1
  48. package/lib/draw-canvas/edit/index.d.ts.map +1 -1
  49. package/lib/draw-canvas/edit/index.js +63 -10
  50. package/lib/draw-canvas/edit/index.js.map +1 -1
  51. package/lib/draw-canvas/edit/index.less +17 -2
  52. package/lib/draw-canvas/view/index.d.ts +8 -0
  53. package/lib/draw-canvas/view/index.d.ts.map +1 -0
  54. package/lib/draw-canvas/view/index.js +50 -0
  55. package/lib/draw-canvas/view/index.js.map +1 -0
  56. package/lib/draw-canvas/view/index.less +60 -0
  57. package/lib/draw-canvas/view/view.d.ts +10 -0
  58. package/lib/draw-canvas/view/view.d.ts.map +1 -0
  59. package/lib/draw-canvas/view/view.js +104 -0
  60. package/lib/draw-canvas/view/view.js.map +1 -0
  61. package/lib/form/form-Item-group.d.ts.map +1 -1
  62. package/lib/form/form-Item-group.js +4 -4
  63. package/lib/form/form-Item-group.js.map +1 -1
  64. package/lib/lowcode-components/index.d.ts +2 -0
  65. package/lib/lowcode-components/index.d.ts.map +1 -1
  66. package/lib/lowcode-components/index.js +2 -1
  67. package/lib/lowcode-components/index.js.map +1 -1
  68. package/lib/lowcode-components/lowcode-view/component/assets.d.ts.map +1 -1
  69. package/lib/lowcode-components/lowcode-view/component/assets.js +8 -0
  70. package/lib/lowcode-components/lowcode-view/component/assets.js.map +1 -1
  71. package/lib/lowcode-components/radar-chart/index.d.ts +51 -0
  72. package/lib/lowcode-components/radar-chart/index.d.ts.map +1 -0
  73. package/lib/lowcode-components/radar-chart/index.js +276 -0
  74. package/lib/lowcode-components/radar-chart/index.js.map +1 -0
  75. package/lib/module/dtmpl-edit-card.d.ts.map +1 -1
  76. package/lib/module/dtmpl-edit-card.js +18 -1
  77. package/lib/module/dtmpl-edit-card.js.map +1 -1
  78. package/lib/module/dtmpl-edit-page.d.ts.map +1 -1
  79. package/lib/module/dtmpl-edit-page.js +19 -2
  80. package/lib/module/dtmpl-edit-page.js.map +1 -1
  81. package/lib/routable/ltmpl-route.d.ts +2 -0
  82. package/lib/routable/ltmpl-route.d.ts.map +1 -1
  83. package/lib/routable/ltmpl-route.js +20 -4
  84. package/lib/routable/ltmpl-route.js.map +1 -1
  85. package/lib/table/act-table.d.ts +2 -0
  86. package/lib/table/act-table.d.ts.map +1 -1
  87. package/lib/table/act-table.js +4 -4
  88. package/lib/table/act-table.js.map +1 -1
  89. package/lib/table/column/column-builder.d.ts.map +1 -1
  90. package/lib/table/column/column-builder.js +23 -8
  91. package/lib/table/column/column-builder.js.map +1 -1
  92. package/lib/table/relation-table.d.ts +3 -0
  93. package/lib/table/relation-table.d.ts.map +1 -1
  94. package/lib/tmpl/control-type-supportor.d.ts.map +1 -1
  95. package/lib/tmpl/control-type-supportor.js +1 -0
  96. package/lib/tmpl/control-type-supportor.js.map +1 -1
  97. package/lib/tmpl/hcservice-v3.d.ts +2 -0
  98. package/lib/tmpl/hcservice-v3.d.ts.map +1 -1
  99. package/lib/tmpl/hcservice-v3.js +34 -0
  100. package/lib/tmpl/hcservice-v3.js.map +1 -1
  101. package/lib/tmpl/interface.d.ts +13 -1
  102. package/lib/tmpl/interface.d.ts.map +1 -1
  103. package/lib/tmpl/interface.js.map +1 -1
  104. package/lib/tmpl/tmpl-config-analysis.js +1 -1
  105. package/lib/tmpl/tmpl-config-analysis.js.map +1 -1
  106. package/lib/units/index.d.ts +2 -1
  107. package/lib/units/index.d.ts.map +1 -1
  108. package/lib/units/index.js +17 -3
  109. package/lib/units/index.js.map +1 -1
  110. package/package.json +4 -1
  111. package/src/aldehyde/controls/entity-select/entity-select.tsx +18 -8
  112. package/src/aldehyde/controls/entry-control.tsx +1 -0
  113. package/src/aldehyde/controls/select/index.tsx +7 -6
  114. package/src/aldehyde/controls/text/index.less +1 -0
  115. package/src/aldehyde/controls/view-control.tsx +1 -0
  116. package/src/aldehyde/detail/button/edit-button.tsx +21 -22
  117. package/src/aldehyde/detail/button/view-button.tsx +23 -21
  118. package/src/aldehyde/draw-canvas/edit/components/asset-bar/index.tsx +3 -1
  119. package/src/aldehyde/draw-canvas/edit/components/main-header/index.tsx +59 -13
  120. package/src/aldehyde/draw-canvas/edit/components/render/draws/bg-draw.ts +130 -65
  121. package/src/aldehyde/draw-canvas/edit/components/render/draws/preview-draw.ts +33 -15
  122. package/src/aldehyde/draw-canvas/edit/components/render/handlers/drag-outside-handlers.ts +19 -22
  123. package/src/aldehyde/draw-canvas/edit/components/render/handlers/selection-handlers.ts +38 -30
  124. package/src/aldehyde/draw-canvas/edit/components/render/index.ts +17 -9
  125. package/src/aldehyde/draw-canvas/edit/components/render/tools/position-tool.ts +40 -58
  126. package/src/aldehyde/draw-canvas/edit/components/render/types.ts +5 -1
  127. package/src/aldehyde/draw-canvas/edit/components/setting-form/imag-upload.tsx +118 -0
  128. package/src/aldehyde/draw-canvas/edit/components/setting-form/index.tsx +111 -16
  129. package/src/aldehyde/draw-canvas/edit/constant.ts +6 -0
  130. package/src/aldehyde/draw-canvas/edit/index.less +17 -2
  131. package/src/aldehyde/draw-canvas/edit/index.tsx +84 -25
  132. package/src/aldehyde/draw-canvas/view/index.less +60 -0
  133. package/src/aldehyde/draw-canvas/view/index.tsx +48 -0
  134. package/src/aldehyde/draw-canvas/view/view.tsx +114 -0
  135. package/src/aldehyde/form/form-Item-group.tsx +4 -5
  136. package/src/aldehyde/lowcode-components/index.ts +4 -2
  137. package/src/aldehyde/lowcode-components/lowcode-view/component/assets.ts +8 -0
  138. package/src/aldehyde/lowcode-components/radar-chart/index.tsx +323 -0
  139. package/src/aldehyde/module/dtmpl-edit-card.tsx +18 -1
  140. package/src/aldehyde/module/dtmpl-edit-page.tsx +19 -2
  141. package/src/aldehyde/routable/ltmpl-route.tsx +39 -3
  142. package/src/aldehyde/table/act-table.tsx +7 -4
  143. package/src/aldehyde/table/column/column-builder.tsx +29 -9
  144. package/src/aldehyde/tmpl/control-type-supportor.tsx +1 -0
  145. package/src/aldehyde/tmpl/hcservice-v3.tsx +30 -0
  146. package/src/aldehyde/tmpl/interface.tsx +13 -1
  147. package/src/aldehyde/tmpl/tmpl-config-analysis.tsx +1 -1
  148. package/src/aldehyde/units/index.tsx +17 -4
@@ -4,4 +4,5 @@
4
4
  text-overflow: ellipsis;
5
5
  width: fit-content;
6
6
  max-width: 100%;
7
+ min-width: 100%;
7
8
  }
@@ -147,6 +147,7 @@ function renderControl(
147
147
  case "textarea":
148
148
  case "text-view":
149
149
  case "preselect":
150
+ case "scan-code":
150
151
  if (holderType == "table") {
151
152
  viewControl = (
152
153
  <EllipsisText
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { Button } from "antd";
2
+ import { Button, Tooltip } from "antd";
3
3
  import { DoEditParam, DtmplData, LtmplConfig } from "../../tmpl/interface";
4
4
  import { EditOutlined } from "@ant-design/icons";
5
5
  import ActionUtils from "../../controls/action/utils";
@@ -13,26 +13,23 @@ interface EditButtonProps {
13
13
 
14
14
  const EditButton: React.FC<EditButtonProps> = (props) => {
15
15
  const { doEdit, data, ltmplConfig } = props;
16
+ const { classEditConfigs, buttonEditAction, editBtnTitle } = ltmplConfig || {};
16
17
  let button = undefined;
17
- if (ltmplConfig.classEditConfigs) {
18
- ltmplConfig.classEditConfigs.forEach((classEditConfig) => {
18
+ if (classEditConfigs) {
19
+ classEditConfigs.forEach((classEditConfig) => {
19
20
  if (
20
21
  !button &&
21
22
  ActionUtils.isShow(classEditConfig.preposes, [data], undefined)
22
23
  ) {
24
+ const btnProps: any = {
25
+ size: "small",
26
+ type: "dashed",
27
+ onClick: () => doEdit({ code: data.code, mode: "update", dtmplSourceId: classEditConfig.id })
28
+ };
23
29
  button = (
24
- <Button
25
- size="small"
26
- type="dashed"
27
- icon={<EditOutlined />}
28
- onClick={() =>
29
- doEdit({
30
- code: data.code,
31
- mode: "update",
32
- dtmplSourceId: classEditConfig.id,
33
- })
34
- }
35
- ></Button>
30
+ (!editBtnTitle || editBtnTitle?.length < 4) ?
31
+ <Button {...btnProps}>{editBtnTitle || <EditOutlined />}</Button> :
32
+ <Tooltip title={editBtnTitle}><Button {...btnProps} icon={<EditOutlined />} /></Tooltip>
36
33
  );
37
34
  }
38
35
  });
@@ -42,18 +39,20 @@ const EditButton: React.FC<EditButtonProps> = (props) => {
42
39
  if (
43
40
  !button &&
44
41
  ActionUtils.isShow(
45
- ltmplConfig.buttonEditAction?.preposes,
42
+ buttonEditAction?.preposes,
46
43
  [data],
47
44
  undefined
48
45
  )
49
46
  ) {
47
+ const btnProps: any = {
48
+ size: "small",
49
+ type: "dashed",
50
+ onClick: () => doEdit({ code: data.code, mode: "update" })
51
+ };
50
52
  button = (
51
- <Button
52
- size="small"
53
- type="dashed"
54
- icon={<EditOutlined />}
55
- onClick={() => doEdit({ code: data.code, mode: "update" })}
56
- ></Button>
53
+ (!editBtnTitle || editBtnTitle?.length < 4) ?
54
+ <Button {...btnProps}>{editBtnTitle || <EditOutlined />}</Button> :
55
+ <Tooltip title={editBtnTitle}><Button {...btnProps} icon={<EditOutlined />} /></Tooltip>
57
56
  );
58
57
  }
59
58
 
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { Button } from "antd";
2
+ import { Button, Tooltip } from "antd";
3
3
  import { DtmplData, LtmplConfig, ShowViewParam } from "../../tmpl/interface";
4
4
  import { AlignCenterOutlined } from "@ant-design/icons";
5
5
  import ActionUtils from "../../controls/action/utils";
@@ -14,25 +14,24 @@ interface ViewButtonProps {
14
14
 
15
15
  const ViewButton: React.FC<ViewButtonProps> = (props) => {
16
16
  const { showView, data, ltmplConfig, buttonType = "primary" } = props;
17
+ const { classViewConfigs, detailBtnTitle, buttonViewAction } = ltmplConfig || {};
17
18
  let button = undefined;
18
- if (ltmplConfig.classViewConfigs) {
19
- ltmplConfig.classViewConfigs.forEach((classViewConfig) => {
19
+ if (classViewConfigs) {
20
+ classViewConfigs.forEach((classViewConfig) => {
20
21
  if (
21
22
  !button &&
22
23
  ActionUtils.isShow(classViewConfig.preposes, [data], undefined)
23
24
  ) {
25
+ const btnProps: any = {
26
+ size: "small",
27
+ type: buttonType,
28
+ onClick: () => showView({ code: data.code, dtmplSourceId: classViewConfig.id, })
29
+ };
24
30
  button = (
25
- <Button
26
- size="small"
27
- type={buttonType}
28
- icon={<AlignCenterOutlined />}
29
- onClick={() =>
30
- showView({
31
- code: data.code,
32
- dtmplSourceId: classViewConfig.id,
33
- })
34
- }
35
- ></Button>
31
+ (!detailBtnTitle || detailBtnTitle?.length < 4) ?
32
+ <Button {...btnProps} >
33
+ {detailBtnTitle || <AlignCenterOutlined />}
34
+ </Button> : <Tooltip title={detailBtnTitle}><Button {...btnProps} icon={<AlignCenterOutlined />} /></Tooltip>
36
35
  );
37
36
  }
38
37
  });
@@ -42,18 +41,21 @@ const ViewButton: React.FC<ViewButtonProps> = (props) => {
42
41
  if (
43
42
  !button &&
44
43
  ActionUtils.isShow(
45
- ltmplConfig.buttonViewAction?.preposes,
44
+ buttonViewAction?.preposes,
46
45
  [data],
47
46
  undefined
48
47
  )
49
48
  ) {
49
+ const btnProps: any = {
50
+ size: "small",
51
+ type: buttonType,
52
+ onClick: () => showView({ code: data.code })
53
+ };
50
54
  button = (
51
- <Button
52
- size="small"
53
- type={buttonType}
54
- icon={<AlignCenterOutlined />}
55
- onClick={() => showView({ code: data.code })}
56
- ></Button>
55
+ (!detailBtnTitle || detailBtnTitle?.length < 4) ?
56
+ <Button {...btnProps}>
57
+ {detailBtnTitle || <AlignCenterOutlined />}
58
+ </Button> : <Tooltip title={detailBtnTitle}><Button {...btnProps} icon={<AlignCenterOutlined />} /></Tooltip>
57
59
  );
58
60
  }
59
61
 
@@ -37,7 +37,8 @@ interface AssetItem {
37
37
  body: {
38
38
  path: string;
39
39
  suffix: string;
40
- }
40
+ };
41
+ type: string; // 组件类型
41
42
  }
42
43
 
43
44
  const AssetList = () => {
@@ -52,6 +53,7 @@ const AssetList = () => {
52
53
  const url = handleImgUrl(item.body.path);
53
54
  e.dataTransfer.setData('src', url);
54
55
  e.dataTransfer.setData('type', item.body.suffix);
56
+ e.dataTransfer.setData('componentType', item.type); // 组件类型
55
57
  // e.dataTransfer.setData('points', '[]');
56
58
  };
57
59
 
@@ -1,5 +1,9 @@
1
1
  import React, { useState, useEffect, useCallback, useMemo } from 'react';
2
2
  import { Dropdown, Modal, Space, Button, Divider, Tooltip } from 'antd';
3
+ import { ImportOutlined, ExportOutlined, SaveOutlined, QuestionCircleOutlined, MergeCellsOutlined, NumberOutlined } from '@ant-design/icons';
4
+ import { gzipSync, strToU8 } from 'fflate';
5
+ import { uint8ArrayToHex } from 'uint8array-extras';
6
+ import HcserviceV3 from "../../../../tmpl/hcservice-v3";
3
7
  import IconFont from "../../../../icon/aliIcon";
4
8
  import { Render } from '../render';
5
9
  import { GraphType, LinkType, AlignType } from '../render/types';
@@ -35,12 +39,17 @@ interface MainHeaderProps {
35
39
  render: Render | null;
36
40
  graphType?: GraphType; // 图类型
37
41
  texting?: boolean; // 添加文本状态
42
+ showGrid?: boolean; // 显示网格
43
+ showAttract?: boolean; // 显示磁吸
38
44
  onGraphTypeChange: (type: GraphType | undefined) => void; // 更新图类型
39
45
  onTexting: (texting: boolean) => void;
46
+ setShowGrid?: (show: boolean) => void;
47
+ setShowAttract?: (show: boolean) => void;
48
+ recordCode: string;
40
49
  }
41
50
 
42
51
  const MainHeader = (props: MainHeaderProps) => {
43
- const { render, graphType, onGraphTypeChange, onTexting, texting } = props;
52
+ const { render, graphType, onGraphTypeChange, onTexting, texting, recordCode, showGrid, setShowGrid, showAttract, setShowAttract } = props;
44
53
  const [scale, setScale] = useState<number>(100); // 缩放
45
54
  const [history, setHistory] = useState<string[]>([]); // 历史记录
46
55
  const [historyIndex, setHistoryIndex] = useState(-1); // 当前历史记录标签
@@ -69,17 +78,37 @@ const MainHeader = (props: MainHeaderProps) => {
69
78
  input.click();
70
79
  }, [render]);
71
80
 
81
+ // 保存
82
+ const handleSave = async () => {
83
+ if (!recordCode) {
84
+ return;
85
+ }
86
+ setLoading(true);
87
+ const data = render.importExportTool.save();
88
+ // 1. 将普通字符串转换为 Uint8Array
89
+ const dataBuffer = strToU8(data);
90
+ // 2. 压缩为 ByteArray
91
+ const content = gzipSync(dataBuffer, { level: 9 });
92
+ // 3. 将 Uint8Array 转为 16进制字符串
93
+ const hexString = uint8ArrayToHex(content);
94
+ await HcserviceV3.postDiagram2DConfig(recordCode, hexString);
95
+ setLoading(false);
96
+ }
97
+
72
98
  // 数据导出
73
- const handleSave = useCallback((type: 'json' | 'svg' | 'asset' | 'png' | 'assetPng') => {
99
+ const handleExport = useCallback((type: 'json' | 'svg' | 'asset' | 'png' | 'assetPng', action?: "save" | "download") => {
74
100
  if (!render) return;
75
- const data: { name: string; content: string }[] = [];
76
101
  switch (type) {
77
102
  case 'json':
78
- data.push({ name: 'data.json', content: render.importExportTool.save() });
79
- break;
103
+ if (action === "save") {
104
+ handleSave();
105
+ } else {
106
+ download("data.json", render.importExportTool.save());
107
+ }
108
+ return;
80
109
  case 'asset':
81
- data.push({ name: 'asset.json', content: render.importExportTool.getAsset() });
82
- break;
110
+ download("asset.json", render.importExportTool.getAsset());
111
+ return;
83
112
  case 'png':
84
113
  download('image.png', render.importExportTool.getAssetImage(2));
85
114
  return;
@@ -87,9 +116,6 @@ const MainHeader = (props: MainHeaderProps) => {
87
116
  download('image.png', render.importExportTool.getAssetImage());
88
117
  return;
89
118
  }
90
- if (data.length) {
91
- data.forEach(({ name, content }) => download(name, content));
92
- }
93
119
  }, [render]);
94
120
 
95
121
  // 下载辅助函数
@@ -138,8 +164,8 @@ const MainHeader = (props: MainHeaderProps) => {
138
164
 
139
165
  // 缩放菜单选项
140
166
  const scaleMenu = [
141
- { key: "fit", label: <a onClick={() => handleScale('fit')}>自适应大小</a> },
142
- { key: "reset", label: <a onClick={() => handleScale('reset')}>重置位置大小</a> },
167
+ { key: "fit", label: <a onClick={() => handleScale('fit')}>自适应</a> },
168
+ { key: "reset", label: <a onClick={() => handleScale('reset')}>重置位置及缩放</a> },
143
169
  { key: "position", label: <a onClick={() => handleScale('position')}>仅重置位置</a> }
144
170
  ];
145
171
 
@@ -173,9 +199,29 @@ const MainHeader = (props: MainHeaderProps) => {
173
199
  <Tooltip title="插入文字">
174
200
  <Button type="text" onClick={() => onTexting(true)} className={texting ? "active" : ""} ><IconFont type="icon-wenben" /></Button>
175
201
  </Tooltip>
202
+ <Tooltip title="网格">
203
+ <Button type="text" onClick={() => setShowGrid(!showGrid)} className={showGrid ? "active" : ""} ><NumberOutlined /></Button>
204
+ </Tooltip>
205
+ <Tooltip title="磁吸">
206
+ <Button type="text" onClick={() => setShowAttract(!showAttract)} className={showAttract ? "active" : ""} ><MergeCellsOutlined /></Button>
207
+ </Tooltip>
208
+ </Space>
209
+ <Space size={4} separator={<Divider vertical styles={{ root: { margin: "0 4px" } }} />}>
210
+ <Tooltip title="快捷键">
211
+ <Button type="text" onClick={() => setShowShortCut(true)} ><QuestionCircleOutlined /></Button>
212
+ </Tooltip>
213
+ <Tooltip title="导入">
214
+ <Button type="text" onClick={handleRestore} ><ImportOutlined /></Button>
215
+ </Tooltip>
216
+ <Tooltip title="导出">
217
+ <Button type="text" onClick={() => handleExport("json")} ><ExportOutlined /></Button>
218
+ </Tooltip>
219
+ <Tooltip title="保存">
220
+ <Button loading={loading} type="text" onClick={() => handleExport("json", "save")} ><SaveOutlined /></Button>
221
+ </Tooltip>
176
222
  </Space>
177
223
  {showShortCut &&
178
- <Modal title="快捷键" open={showShortCut} onCancel={() => setShowShortCut(false)}>
224
+ <Modal title="快捷键" width={600} footer={null} open={showShortCut} onCancel={() => setShowShortCut(false)}>
179
225
  <p>1、复制、粘贴、多选、全选、删除、上一步、下一步等快捷键与一般文档编辑器类似;</p>
180
226
  <p>2、放大缩小,【Win】鼠标上滚动下滚动,【Mac】触控板双指放大、缩小;</p>
181
227
  <p>3、画布拖动,在空白处,【Win】右键按下移动,【Mac】control + 触控板三指移动;</p>
@@ -5,6 +5,8 @@ import { Draw, BaseDraw, Render } from '../types';
5
5
 
6
6
  export interface BgDrawOption {
7
7
  size: number;
8
+ readonly?: boolean;
9
+ showGrid?: boolean;
8
10
  }
9
11
 
10
12
  export class BgDraw extends BaseDraw implements Draw {
@@ -12,87 +14,150 @@ export class BgDraw extends BaseDraw implements Draw {
12
14
  static override readonly name = 'bg';
13
15
  option: BgDrawOption;
14
16
 
17
+ // 独立容器:用于存放静态的背景和画布
18
+ private staticGroup: Konva.Group;
19
+ // 独立容器:专门用于存放动态网格
20
+ private gridGroup: Konva.Group;
21
+ // 标记静态元素是否需要重绘
22
+ private isStaticInitialized: boolean = false;
23
+
15
24
  constructor(render: Render, layer: Konva.Layer, option: BgDrawOption) {
16
25
  super(render, layer);
17
26
  this.option = option;
18
27
  this.group.listening(false);
28
+
29
+ // 预先创建好两个独立的 Group
30
+ this.staticGroup = new Konva.Group({ name: 'bg-static', listening: false });
31
+ this.gridGroup = new Konva.Group({ name: 'bg-grid', listening: false });
32
+
33
+ // 注意添加顺序:静态在下,网格在上
34
+ this.group.add(this.staticGroup);
35
+ this.group.add(this.gridGroup);
36
+
37
+ // 监听页面配置变化,当配置改变时,重置静态层初始化状态
38
+ this.render.on('page-settings-change', () => {
39
+ this.isStaticInitialized = false;
40
+ // 触发整体重绘,让 draw 方法重新执行
41
+ this.draw();
42
+ });
43
+ }
44
+
45
+ // 更新配置项,触发重绘
46
+ public updateOption(newOption: Partial<BgDrawOption>) {
47
+ this.option = { ...this.option, ...newOption };
48
+ this.draw();
19
49
  }
20
50
 
21
51
  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; // 网格尺寸
52
+ // 1. 如果静态元素未初始化,或者配置发生变化导致标记被重置,则重新绘制
53
+ if (!this.isStaticInitialized) {
54
+ this.renderStaticElements();
55
+ this.isStaticInitialized = true;
56
+ }
57
+ // 2. 仅重绘网格部分(拖拽/缩放时高频调用)
58
+ if (!this.option.readonly && this.option.showGrid) { // 只读页面不显示网格
59
+ this.renderGrid();
60
+ } else { // 清除已渲染网格
61
+ this.gridGroup.destroyChildren();
62
+ }
63
+ }
64
+
65
+ /**
66
+ * 渲染静态元素(仅在初始化或配置改变时执行)
67
+ */
68
+ private renderStaticElements() {
69
+ // 清除旧的静态元素
70
+ this.staticGroup.destroyChildren();
71
+ const { width, height, x, y } = this.render.getStageState();
72
+ const { pageWidth, pageHeight, background, backgroundImg } = this.render.getPageSettings();
26
73
 
27
- // 计算列数和行数(基于stage 尺寸)
74
+ // 背景层(填满整个 stage
75
+ const bgRect = new Konva.Rect({
76
+ name: `${this.constructor.name}__background`,
77
+ x: this.render.toStageValue(-x + this.render.rulerSize),
78
+ y: this.render.toStageValue(-y + this.render.rulerSize),
79
+ width: this.render.toStageValue(width),
80
+ height: this.render.toStageValue(height),
81
+ listening: false,
82
+ fill: 'transparent'
83
+ });
84
+
85
+ // 画布边框
86
+ const canvas = new Konva.Rect({
87
+ name: this.constructor.name,
88
+ x: 0,
89
+ y: 0,
90
+ width: pageWidth,
91
+ height: pageHeight,
92
+ stroke: this.option.readonly ? "transparent" : 'rgba(255,0,0,0.3)',
93
+ strokeWidth: this.render.toStageValue(2),
94
+ listening: false,
95
+ dash: [this.render.toStageValue(6), this.render.toStageValue(6)],
96
+ fill: background || 'transparent',
97
+ });
98
+
99
+ this.staticGroup.add(bgRect);
100
+ this.staticGroup.add(canvas);
101
+
102
+ // 处理背景图(异步)
103
+ if (backgroundImg?.src) {
104
+ const img = new Image();
105
+ img.crossOrigin = 'anonymous'; // 如果图片在 CDN 上,必须加上此属性防止跨域问题
106
+ img.src = backgroundImg.src;
107
+ img.onload = () => {
108
+ canvas.fill(null);
109
+ canvas.fillPatternImage(img);
110
+ canvas.fillPatternRepeat('no-repeat');
111
+ // 让图片自适应填满矩形(等比缩放裁剪效果)
112
+ const scaleX = pageWidth / img.width;
113
+ const scaleY = pageHeight / img.height;
114
+ canvas.fillPatternScaleX(scaleX);
115
+ canvas.fillPatternScaleY(scaleY);
116
+ };
117
+ }
118
+ }
119
+
120
+ /**
121
+ * 渲染网格(每次拖拽/缩放时调用)
122
+ */
123
+ private renderGrid() {
124
+ // 仅清空网格 Group,不影响静态层
125
+ this.gridGroup.destroyChildren();
126
+ const { width, height, x, y } = this.render.getStageState();
127
+ const cellSize = this.option.size;
28
128
  const lenX = Math.ceil(this.render.toStageValue(width + this.render.rulerSize) / cellSize);
29
129
  const lenY = Math.ceil(this.render.toStageValue(height + this.render.rulerSize) / cellSize);
30
130
  const startX = -Math.ceil(this.render.toStageValue(x) / cellSize);
31
131
  const startY = -Math.ceil(this.render.toStageValue(y) / cellSize);
132
+ const lines: Konva.Line[] = [];
32
133
 
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
- );
134
+ // 生成竖线
135
+ for (let i = startX; i < lenX + startX + 2; i++) {
136
+ lines.push(new Konva.Line({
137
+ points: [
138
+ cellSize * i, this.render.toStageValue(-y + this.render.rulerSize),
139
+ cellSize * i, this.render.toStageValue(height - y + this.render.rulerSize)
140
+ ],
141
+ stroke: '#ddd',
142
+ strokeWidth: this.render.toStageValue(1),
143
+ listening: false
144
+ }));
78
145
  }
79
146
 
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
- );
147
+ // 生成横线
148
+ for (let j = startY; j < lenY + startY + 2; j++) {
149
+ lines.push(new Konva.Line({
150
+ points: [
151
+ this.render.toStageValue(-x + this.render.rulerSize), cellSize * j,
152
+ this.render.toStageValue(width - x + this.render.rulerSize), cellSize * j
153
+ ],
154
+ stroke: '#ddd',
155
+ strokeWidth: this.render.toStageValue(1),
156
+ listening: false
157
+ }));
94
158
  }
95
159
 
96
- this.group.add(group);
160
+ // 批量添加线条到网格 Group
161
+ this.gridGroup.add(...lines);
97
162
  }
98
163
  }
@@ -25,7 +25,7 @@ export class PreviewDraw extends BaseDraw implements Draw {
25
25
  override draw() {
26
26
  this.clear();
27
27
  const stageState = this.render.getStageState();
28
- const { pageWidth, pageHeight, background } = this.render.getPageSettings(); // 画布尺寸
28
+ const { pageWidth, pageHeight, background, backgroundImg } = this.render.getPageSettings(); // 画布尺寸
29
29
  const previewMargin = 20; // 预览框的外边距
30
30
  // 预览框 group
31
31
  const group = new Konva.Group({
@@ -101,7 +101,6 @@ export class PreviewDraw extends BaseDraw implements Draw {
101
101
  height: group.height(),
102
102
  stroke: '#666',
103
103
  strokeWidth: this.render.toStageValue(1),
104
- fill: !background ? "#ffffff" : background
105
104
  });
106
105
 
107
106
  // 根据预览框内部拖动,同步画布的移动
@@ -139,19 +138,38 @@ export class PreviewDraw extends BaseDraw implements Draw {
139
138
  this.state.moving = false;
140
139
  })
141
140
  group.add(bg);
142
- // 预览框 边框
143
- group.add(
144
- new Konva.Rect({
145
- name: this.constructor.name,
146
- x: 0,
147
- y: 0,
148
- width: this.render.toStageValue(pageWidth * stageState.scale),
149
- height: this.render.toStageValue(pageHeight * stageState.scale),
150
- stroke: 'rgba(255,0,0,0.2)',
151
- strokeWidth: 1 / this.option.size,
152
- listening: false
153
- })
154
- );
141
+ // 预览框 画布
142
+ const canvas = new Konva.Rect({
143
+ name: this.constructor.name,
144
+ x: 0,
145
+ y: 0,
146
+ width: this.render.toStageValue(pageWidth * stageState.scale),
147
+ height: this.render.toStageValue(pageHeight * stageState.scale),
148
+ stroke: 'rgba(255,0,0,0.2)',
149
+ strokeWidth: 1 / this.option.size,
150
+ listening: false,
151
+ fill: !background ? "#ffffff" : background
152
+ });
153
+
154
+ group.add(canvas);
155
+
156
+ // // 处理背景图(异步)
157
+ // if (backgroundImg?.src) {
158
+ // const img = new Image();
159
+ // img.crossOrigin = 'anonymous'; // 如果图片在 CDN 上,必须加上此属性防止跨域问题
160
+ // img.src = backgroundImg.src;
161
+ // img.onload = () => {
162
+ // canvas.fill(null);
163
+ // canvas.fillPatternImage(img);
164
+ // canvas.fillPatternRepeat('no-repeat');
165
+ // // 让图片自适应填满矩形(等比缩放裁剪效果)
166
+ // const scaleX = pageWidth / img.width;
167
+ // const scaleY = pageHeight / img.height;
168
+ // canvas.fillPatternScaleX(scaleX);
169
+ // canvas.fillPatternScaleY(scaleY);
170
+ // };
171
+ // }
172
+
155
173
  // 复制提取的节点,用作预览
156
174
  for (const node of nodes) {
157
175
  const copy = node.clone();