aldehyde 0.2.481 → 0.2.483
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.
- package/lib/controls/direct-file-view/index.d.ts.map +1 -1
- package/lib/controls/direct-file-view/index.js +4 -4
- package/lib/controls/direct-file-view/index.js.map +1 -1
- package/lib/controls/direct-upload/index.d.ts.map +1 -1
- package/lib/controls/direct-upload/index.js +3 -3
- package/lib/controls/direct-upload/index.js.map +1 -1
- package/lib/controls/entry-control.d.ts +2 -1
- package/lib/controls/entry-control.d.ts.map +1 -1
- package/lib/controls/entry-control.js +10 -0
- package/lib/controls/entry-control.js.map +1 -1
- package/lib/controls/select/index.d.ts +8 -2
- package/lib/controls/select/index.d.ts.map +1 -1
- package/lib/controls/select/index.js +35 -13
- package/lib/controls/select/index.js.map +1 -1
- package/lib/controls/upload-read-excel/index.d.ts +10 -0
- package/lib/controls/upload-read-excel/index.d.ts.map +1 -0
- package/lib/controls/upload-read-excel/index.js +30 -0
- package/lib/controls/upload-read-excel/index.js.map +1 -0
- package/lib/detail/dtmpl.css +5 -0
- package/lib/detail/view/dtmpl-view.d.ts.map +1 -1
- package/lib/detail/view/dtmpl-view.js +12 -2
- package/lib/detail/view/dtmpl-view.js.map +1 -1
- package/lib/draw-canvas-edit/components/asset-bar/index.less +2 -2
- package/lib/draw-canvas-edit/components/main-header/index.d.ts.map +1 -1
- package/lib/draw-canvas-edit/components/main-header/index.js +32 -2
- package/lib/draw-canvas-edit/components/main-header/index.js.map +1 -1
- package/lib/draw-canvas-edit/components/render/components/index.less +61 -0
- package/lib/draw-canvas-edit/components/render/components/workstation-component.d.ts +7 -0
- package/lib/draw-canvas-edit/components/render/components/workstation-component.d.ts.map +1 -1
- package/lib/draw-canvas-edit/components/render/components/workstation-component.js +122 -15
- package/lib/draw-canvas-edit/components/render/components/workstation-component.js.map +1 -1
- package/lib/draw-canvas-edit/components/render/handlers/selection-handlers.d.ts.map +1 -1
- package/lib/draw-canvas-edit/components/render/handlers/selection-handlers.js +19 -5
- package/lib/draw-canvas-edit/components/render/handlers/selection-handlers.js.map +1 -1
- package/lib/draw-canvas-edit/components/render/index.d.ts.map +1 -1
- package/lib/draw-canvas-edit/components/render/index.js +15 -4
- package/lib/draw-canvas-edit/components/render/index.js.map +1 -1
- package/lib/draw-canvas-edit/components/render/tools/asset-tool.d.ts +2 -0
- package/lib/draw-canvas-edit/components/render/tools/asset-tool.d.ts.map +1 -1
- package/lib/draw-canvas-edit/components/render/tools/asset-tool.js +22 -11
- package/lib/draw-canvas-edit/components/render/tools/asset-tool.js.map +1 -1
- package/lib/draw-canvas-edit/components/render/tools/component-factory.js.map +1 -1
- package/lib/draw-canvas-edit/components/render/tools/import-export-tool.d.ts +1 -0
- package/lib/draw-canvas-edit/components/render/tools/import-export-tool.d.ts.map +1 -1
- package/lib/draw-canvas-edit/components/render/tools/import-export-tool.js +35 -22
- package/lib/draw-canvas-edit/components/render/tools/import-export-tool.js.map +1 -1
- package/lib/draw-canvas-edit/components/render/types.d.ts +3 -0
- package/lib/draw-canvas-edit/components/render/types.d.ts.map +1 -1
- package/lib/draw-canvas-edit/components/render/types.js.map +1 -1
- package/lib/draw-canvas-edit/components/setting-form/index.d.ts.map +1 -1
- package/lib/draw-canvas-edit/components/setting-form/index.js +10 -0
- package/lib/draw-canvas-edit/components/setting-form/index.js.map +1 -1
- package/lib/draw-canvas-edit/index.d.ts.map +1 -1
- package/lib/draw-canvas-edit/index.js +5 -2
- package/lib/draw-canvas-edit/index.js.map +1 -1
- package/lib/draw-canvas-view/index.less +7 -0
- package/lib/draw-canvas-view/view.d.ts.map +1 -1
- package/lib/draw-canvas-view/view.js +16 -21
- package/lib/draw-canvas-view/view.js.map +1 -1
- package/lib/form/dtmpl-form.d.ts.map +1 -1
- package/lib/form/dtmpl-form.js +11 -2
- package/lib/form/dtmpl-form.js.map +1 -1
- package/lib/form/field-group-form.d.ts.map +1 -1
- package/lib/form/field-group-form.js +1 -1
- package/lib/form/field-group-form.js.map +1 -1
- package/lib/form/form-Item-group.d.ts +2 -1
- package/lib/form/form-Item-group.d.ts.map +1 -1
- package/lib/form/form-Item-group.js +2 -2
- package/lib/form/form-Item-group.js.map +1 -1
- package/lib/list/card-list/card-list-item.d.ts.map +1 -1
- package/lib/list/card-list/card-list-item.js +3 -2
- package/lib/list/card-list/card-list-item.js.map +1 -1
- package/lib/list/card-list/index.less +1 -1
- package/lib/lowcode-components/carousel/index.js +3 -3
- package/lib/lowcode-components/carousel/index.js.map +1 -1
- package/lib/module/ltmpl-table.d.ts +1 -0
- package/lib/module/ltmpl-table.d.ts.map +1 -1
- package/lib/module/ltmpl-table.js +2 -2
- package/lib/module/ltmpl-table.js.map +1 -1
- package/lib/table/act-table.d.ts.map +1 -1
- package/lib/table/act-table.js +21 -20
- package/lib/table/act-table.js.map +1 -1
- package/lib/table/column/column-builder.d.ts.map +1 -1
- package/lib/table/column/column-builder.js +26 -16
- package/lib/table/column/column-builder.js.map +1 -1
- package/lib/table/column/index.less +22 -0
- package/lib/table/query-table.d.ts +5 -0
- package/lib/table/query-table.d.ts.map +1 -1
- package/lib/table/query-table.js +17 -5
- package/lib/table/query-table.js.map +1 -1
- package/lib/table/relation-table.d.ts +1 -1
- package/lib/table/relation-table.d.ts.map +1 -1
- package/lib/table/relation-table.js +5 -24
- package/lib/table/relation-table.js.map +1 -1
- package/lib/tmpl/hc-data-source.d.ts +1 -0
- package/lib/tmpl/hc-data-source.d.ts.map +1 -1
- package/lib/tmpl/hc-data-source.js +9 -0
- package/lib/tmpl/hc-data-source.js.map +1 -1
- package/lib/tmpl/interface.d.ts +5 -0
- package/lib/tmpl/interface.d.ts.map +1 -1
- package/lib/tmpl/interface.js.map +1 -1
- package/package.json +2 -1
- package/src/aldehyde/controls/direct-file-view/index.tsx +5 -4
- package/src/aldehyde/controls/direct-upload/index.tsx +4 -3
- package/src/aldehyde/controls/entry-control.tsx +11 -1
- package/src/aldehyde/controls/select/index.tsx +25 -14
- package/src/aldehyde/controls/upload-read-excel/index.tsx +44 -0
- package/src/aldehyde/detail/dtmpl.css +5 -0
- package/src/aldehyde/detail/view/dtmpl-view.tsx +22 -2
- package/src/aldehyde/draw-canvas-edit/components/asset-bar/index.less +2 -2
- package/src/aldehyde/draw-canvas-edit/components/main-header/index.tsx +33 -2
- package/src/aldehyde/draw-canvas-edit/components/render/components/index.less +61 -0
- package/src/aldehyde/draw-canvas-edit/components/render/components/workstation-component.tsx +131 -15
- package/src/aldehyde/draw-canvas-edit/components/render/handlers/selection-handlers.ts +17 -5
- package/src/aldehyde/draw-canvas-edit/components/render/index.ts +16 -3
- package/src/aldehyde/draw-canvas-edit/components/render/tools/asset-tool.ts +21 -7
- package/src/aldehyde/draw-canvas-edit/components/render/tools/component-factory.ts +1 -1
- package/src/aldehyde/draw-canvas-edit/components/render/tools/import-export-tool.ts +34 -22
- package/src/aldehyde/draw-canvas-edit/components/render/types.ts +3 -0
- package/src/aldehyde/draw-canvas-edit/components/setting-form/index.tsx +12 -0
- package/src/aldehyde/draw-canvas-edit/index.tsx +5 -2
- package/src/aldehyde/draw-canvas-view/index.less +7 -0
- package/src/aldehyde/draw-canvas-view/view.tsx +18 -21
- package/src/aldehyde/form/dtmpl-form.tsx +20 -1
- package/src/aldehyde/form/field-group-form.tsx +6 -5
- package/src/aldehyde/form/form-Item-group.tsx +5 -4
- package/src/aldehyde/list/card-list/card-list-item.tsx +3 -2
- package/src/aldehyde/list/card-list/index.less +1 -1
- package/src/aldehyde/lowcode-components/carousel/index.tsx +3 -3
- package/src/aldehyde/lowcode-components/data.d.ts +1 -0
- package/src/aldehyde/module/ltmpl-table.tsx +3 -2
- package/src/aldehyde/table/act-table.tsx +13 -14
- package/src/aldehyde/table/column/column-builder.tsx +29 -17
- package/src/aldehyde/table/column/index.less +22 -0
- package/src/aldehyde/table/query-table.tsx +15 -5
- package/src/aldehyde/table/relation-table.tsx +6 -21
- package/src/aldehyde/tmpl/hc-data-source.tsx +8 -0
- package/src/aldehyde/tmpl/interface.tsx +5 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React, { useContext } from "react";
|
|
2
|
+
import { Upload, Button, message } from 'antd';
|
|
3
|
+
import { UploadOutlined } from '@ant-design/icons';
|
|
4
|
+
import * as XLSX from 'xlsx';
|
|
5
|
+
import { LocaleContext } from "../../locale/LocaleProvider";
|
|
6
|
+
|
|
7
|
+
// 读取Excel文件
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
onChangeData: (data: { [key: string]: any }[]) => void; // 数据更新
|
|
11
|
+
children?: React.JSX.Element;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const Index = (props: Props) => {
|
|
15
|
+
const { onChangeData, children } = props;
|
|
16
|
+
const { translate } = useContext(LocaleContext);
|
|
17
|
+
|
|
18
|
+
const handleUpload = (file) => {
|
|
19
|
+
const isXlsx = file.name.toLowerCase().endsWith('.xlsx');
|
|
20
|
+
if (!isXlsx) {
|
|
21
|
+
message.warning(translate('${仅支持上传.xlsx格式}'));
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const reader = new FileReader();
|
|
25
|
+
reader.readAsArrayBuffer(file);
|
|
26
|
+
reader.onload = e => {
|
|
27
|
+
const buffer = new Uint8Array(e.target.result as ArrayBuffer);
|
|
28
|
+
const wb = XLSX.read(buffer, { type: 'array' });
|
|
29
|
+
const sheet = wb.Sheets[wb.SheetNames[0]];
|
|
30
|
+
const data = XLSX.utils.sheet_to_json(sheet, { defval: '' });
|
|
31
|
+
onChangeData(data);
|
|
32
|
+
};
|
|
33
|
+
// 阻止自动上传
|
|
34
|
+
return false;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Upload accept=".xlsx" beforeUpload={handleUpload} showUploadList={false}>
|
|
39
|
+
{children || <Button icon={<UploadOutlined />}>上传Excel</Button>}
|
|
40
|
+
</Upload>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default Index;
|
|
@@ -111,7 +111,7 @@ class DtmplView extends React.PureComponent<DtmplViewProps, DtmplViewStat> {
|
|
|
111
111
|
serverKey,
|
|
112
112
|
mainCode,
|
|
113
113
|
showRightNav,
|
|
114
|
-
parentId, maxColsOnRow,
|
|
114
|
+
parentId, maxColsOnRow, code
|
|
115
115
|
} = this.props;
|
|
116
116
|
const { dtmplData, loading, id } = this.state;
|
|
117
117
|
const { pageLayout } = dtmplConfig;
|
|
@@ -120,7 +120,7 @@ class DtmplView extends React.PureComponent<DtmplViewProps, DtmplViewStat> {
|
|
|
120
120
|
groups = dtmplConfig.groups;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
const groupElements = groups.map((item) => {
|
|
124
124
|
if (item.type === "embed") {
|
|
125
125
|
if (!dtmplData) {
|
|
126
126
|
return null;
|
|
@@ -253,6 +253,26 @@ class DtmplView extends React.PureComponent<DtmplViewProps, DtmplViewStat> {
|
|
|
253
253
|
);
|
|
254
254
|
}
|
|
255
255
|
});
|
|
256
|
+
|
|
257
|
+
if (dtmplConfig.viewRactions?.length) { // 关系查询
|
|
258
|
+
const viewRactions = dtmplConfig.viewRactions.map(item => {
|
|
259
|
+
const children = <LtmplTable funcMode="common" readOnly={true} serverKey={serverKey} sourceId={item.id} mainCode={code} />;
|
|
260
|
+
return pageLayout === "horizontalTabs" ? { key: item.id, label: item.title, children } :
|
|
261
|
+
<CollapseCard
|
|
262
|
+
title={item.title}
|
|
263
|
+
key={item.id}
|
|
264
|
+
id={id + item.id}
|
|
265
|
+
className="hoverable"
|
|
266
|
+
styles={{ header: { background: "#f2f4f5" } }}
|
|
267
|
+
loading={loading}
|
|
268
|
+
>
|
|
269
|
+
{children}
|
|
270
|
+
</CollapseCard>;
|
|
271
|
+
});
|
|
272
|
+
groupElements.push(...viewRactions);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return groupElements;
|
|
256
276
|
};
|
|
257
277
|
|
|
258
278
|
render() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
|
2
|
-
import { Dropdown, Modal, Space, Button, Divider, Tooltip } from 'antd';
|
|
3
|
-
import { ImportOutlined, ExportOutlined, SaveOutlined, QuestionCircleOutlined, MergeCellsOutlined, NumberOutlined } from '@ant-design/icons';
|
|
2
|
+
import { Dropdown, Modal, Space, Button, Divider, Tooltip, message } from 'antd';
|
|
3
|
+
import { ImportOutlined, ExportOutlined, SaveOutlined, QuestionCircleOutlined, MergeCellsOutlined, NumberOutlined, SettingOutlined } from '@ant-design/icons';
|
|
4
4
|
import { gzipSync, strToU8 } from 'fflate';
|
|
5
5
|
import { uint8ArrayToHex } from 'uint8array-extras';
|
|
6
6
|
import HcserviceV3 from "../../../tmpl/hcservice-v3";
|
|
@@ -8,6 +8,7 @@ import IconFont from "../../../icon/aliIcon";
|
|
|
8
8
|
import { Render } from '../render';
|
|
9
9
|
import { GraphType, LinkType, AlignType } from '../render/types';
|
|
10
10
|
import * as Draws from '../render/draws';
|
|
11
|
+
import UploadReadExcel from "../../../controls/upload-read-excel";
|
|
11
12
|
import './index.less';
|
|
12
13
|
|
|
13
14
|
// 布局类型
|
|
@@ -78,6 +79,29 @@ const MainHeader = (props: MainHeaderProps) => {
|
|
|
78
79
|
input.click();
|
|
79
80
|
}, [render]);
|
|
80
81
|
|
|
82
|
+
// 批量设置组件属性
|
|
83
|
+
const handleBatchSetNodeConfig = useCallback((data: { [key: string]: any }[]) => {
|
|
84
|
+
if (!render || !data?.length) return;
|
|
85
|
+
const allAssets = render.getAllAssets();
|
|
86
|
+
data.forEach((item) => {
|
|
87
|
+
let nodeId, setting;
|
|
88
|
+
switch (item["组件类型"]) {
|
|
89
|
+
case "workstation":
|
|
90
|
+
const order = item["序号"];
|
|
91
|
+
const workstationNumber = item["工位编号"];
|
|
92
|
+
nodeId = allAssets.find((asset) => asset.componentType === "workstation" && asset.settings.order === order)?.id;
|
|
93
|
+
setting = { workstationNumber };
|
|
94
|
+
break;
|
|
95
|
+
default:
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
if (nodeId) {
|
|
99
|
+
render.updateAssetById(nodeId, setting);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
message.success("设置成功");
|
|
103
|
+
}, [render]);
|
|
104
|
+
|
|
81
105
|
// 保存
|
|
82
106
|
const handleSave = async () => {
|
|
83
107
|
if (!recordCode) {
|
|
@@ -210,6 +234,13 @@ const MainHeader = (props: MainHeaderProps) => {
|
|
|
210
234
|
<Tooltip title="快捷键">
|
|
211
235
|
<Button type="text" onClick={() => setShowShortCut(true)} ><QuestionCircleOutlined /></Button>
|
|
212
236
|
</Tooltip>
|
|
237
|
+
<Tooltip title="组件属性批量配置">
|
|
238
|
+
<>
|
|
239
|
+
<UploadReadExcel onChangeData={handleBatchSetNodeConfig} >
|
|
240
|
+
<Button type="text" ><SettingOutlined /></Button>
|
|
241
|
+
</UploadReadExcel>
|
|
242
|
+
</>
|
|
243
|
+
</Tooltip>
|
|
213
244
|
<Tooltip title="导入">
|
|
214
245
|
<Button type="text" onClick={handleRestore} ><ImportOutlined /></Button>
|
|
215
246
|
</Tooltip>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// 工位卡片
|
|
2
|
+
.workstation-card {
|
|
3
|
+
overflow: hidden;
|
|
4
|
+
|
|
5
|
+
.card-list,
|
|
6
|
+
.ant-card {
|
|
7
|
+
margin: 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.ant-card {
|
|
11
|
+
height: 100%;
|
|
12
|
+
|
|
13
|
+
.ant-card-body {
|
|
14
|
+
padding: 0 !important;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.card-list-item-head {
|
|
19
|
+
margin: 12px 0 8px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.card-list-item-head-title {
|
|
23
|
+
.card-list-item-head-primary {
|
|
24
|
+
min-height: 0px !important;
|
|
25
|
+
|
|
26
|
+
.primary-column {
|
|
27
|
+
font-size: 16px !important;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.card-list-item-head-second {
|
|
32
|
+
min-height: 0px !important;
|
|
33
|
+
margin-top: 0px !important;
|
|
34
|
+
|
|
35
|
+
.second-column {
|
|
36
|
+
font-size: 12px !important;
|
|
37
|
+
opacity: 0.8;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.card-list-item-content {
|
|
43
|
+
padding: 0 12px;
|
|
44
|
+
|
|
45
|
+
.card-list-item-main {
|
|
46
|
+
background-color: transparent;
|
|
47
|
+
margin-bottom: 8px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.card-show-cols {
|
|
51
|
+
padding: 0;
|
|
52
|
+
background-color: transparent;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.card-list-item-footer {
|
|
57
|
+
text-align: right;
|
|
58
|
+
padding: 8px;
|
|
59
|
+
border-top: 1px solid #e8e8e8;
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/aldehyde/draw-canvas-edit/components/render/components/workstation-component.tsx
CHANGED
|
@@ -1,32 +1,147 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
2
3
|
import Konva from 'konva';
|
|
4
|
+
import { Card, ConfigProvider } from "antd";
|
|
3
5
|
import { Render } from '../index';
|
|
4
6
|
import { AssetSettings } from '../types';
|
|
5
|
-
import
|
|
7
|
+
import LtmplTable from "../../../../module/ltmpl-table";
|
|
8
|
+
import { antdLangMp } from "../../../../index";
|
|
9
|
+
import "./index.less";
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
// 详情卡片
|
|
12
|
+
const DetailCard = (props: { workstationNumber: string, pageId: string, locale: string }) => {
|
|
13
|
+
const { pageId, workstationNumber, locale } = props;
|
|
14
|
+
|
|
15
|
+
return <ConfigProvider locale={antdLangMp[locale]}>
|
|
16
|
+
<Card
|
|
17
|
+
className="workstation-card"
|
|
18
|
+
styles={{
|
|
19
|
+
body: { padding: 0 },
|
|
20
|
+
root: { width: 200, boxShadow: '0 2px 8px rgba(0,0,0,0.2)', border: "none" }
|
|
21
|
+
}}
|
|
22
|
+
>
|
|
23
|
+
<LtmplTable
|
|
24
|
+
sourceId={pageId}
|
|
25
|
+
funcMode="simple"
|
|
26
|
+
defShowType="cardList"
|
|
27
|
+
defaultCriteriaValue={{ thirdCriterion: workstationNumber }}
|
|
28
|
+
showPagination={false}
|
|
29
|
+
showAggregates={false}
|
|
30
|
+
defRowCardCount={1}
|
|
31
|
+
/>
|
|
32
|
+
</Card >
|
|
33
|
+
</ConfigProvider >;
|
|
9
34
|
}
|
|
10
35
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const { node, settings, pointerPos } = props;
|
|
14
|
-
const { pageId, pageConfig, workstationNumber } = settings;
|
|
15
|
-
if (!node || !pageId || !workstationNumber || !pointerPos) return;
|
|
16
|
-
const pageInfo = { pageNo: 1, pageSize: 1, thirdCriterion: workstationNumber };
|
|
17
|
-
const queryKey = await HcserviceV3.requestLtmplQueryKey(null, pageId, pageInfo);
|
|
18
|
-
const { entities } = await HcserviceV3.requestQueryData(null, queryKey.key, pageInfo);
|
|
19
|
-
console.log("entitiesentities", entities);
|
|
20
|
-
const renderCard = <div></div>;
|
|
36
|
+
export interface WorkstationSettings extends AssetSettings {
|
|
37
|
+
workstationNumber: string; // 工位编号
|
|
21
38
|
}
|
|
22
39
|
|
|
23
40
|
export class WorkstationComponent {
|
|
24
41
|
private render: Render;
|
|
42
|
+
// 卡片挂载管理
|
|
43
|
+
private cardRoot: ReturnType<typeof createRoot> | null = null;
|
|
44
|
+
private cardWrapper: HTMLDivElement | null = null;
|
|
45
|
+
private currentNodeId: string | null = null; // 当前打开的卡片对应的工位标识
|
|
25
46
|
|
|
26
47
|
constructor(render: Render) {
|
|
27
48
|
this.render = render;
|
|
28
49
|
}
|
|
29
50
|
|
|
51
|
+
// 关闭并销毁卡片,清理所有监听器
|
|
52
|
+
private closeCard = () => {
|
|
53
|
+
const stage = this.render?.stage;
|
|
54
|
+
if (stage) { // 清理 stage 事件监听
|
|
55
|
+
stage.off('click tap');
|
|
56
|
+
}
|
|
57
|
+
if (this.cardRoot) { // 销毁 React 根节点
|
|
58
|
+
this.cardRoot.unmount();
|
|
59
|
+
this.cardRoot = null;
|
|
60
|
+
}
|
|
61
|
+
if (this.cardWrapper) { // 移除 DOM 节点
|
|
62
|
+
this.cardWrapper.remove();
|
|
63
|
+
this.cardWrapper = null;
|
|
64
|
+
}
|
|
65
|
+
this.currentNodeId = null;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// 根据容器边界重新计算卡片位置,避免被遮挡
|
|
69
|
+
private adjustCardPosition = (pointerPos: { x: number; y: number }) => {
|
|
70
|
+
if (!this.cardWrapper || !this.render.stage) return;
|
|
71
|
+
const container = this.render.stage.getContent();
|
|
72
|
+
const containerWidth = container.clientWidth;
|
|
73
|
+
const containerHeight = container.clientHeight;
|
|
74
|
+
const cardWidth = this.cardWrapper.offsetWidth;
|
|
75
|
+
const cardHeight = this.cardWrapper.offsetHeight;
|
|
76
|
+
if (!cardWidth || !cardHeight) return;
|
|
77
|
+
const padding = 8; // 距离容器边缘的最小间距
|
|
78
|
+
let translateX = -(cardWidth / 2); // 默认水平居中于指针
|
|
79
|
+
let translateY = -(cardHeight + padding); // 默认显示在指针上方
|
|
80
|
+
// 水平边界处理:卡片左侧超出
|
|
81
|
+
const cardLeft = pointerPos.x + translateX;
|
|
82
|
+
if (cardLeft < padding) {
|
|
83
|
+
translateX = padding - pointerPos.x;
|
|
84
|
+
}
|
|
85
|
+
// 水平边界处理:卡片右侧超出
|
|
86
|
+
const cardRight = pointerPos.x + translateX + cardWidth;
|
|
87
|
+
if (cardRight > containerWidth - padding) {
|
|
88
|
+
translateX = containerWidth - padding - cardWidth - pointerPos.x;
|
|
89
|
+
}
|
|
90
|
+
// 垂直边界处理:卡片上方超出 → 尝试翻转到指针下方
|
|
91
|
+
const cardTop = pointerPos.y + translateY;
|
|
92
|
+
if (cardTop < padding) {
|
|
93
|
+
// 检查下方空间是否足够
|
|
94
|
+
const cardBottomWhenBelow = pointerPos.y + padding + cardHeight;
|
|
95
|
+
if (cardBottomWhenBelow <= containerHeight - padding) {
|
|
96
|
+
translateY = padding; // 翻转到指针下方
|
|
97
|
+
} else {
|
|
98
|
+
// 上下空间都不足,强制贴住容器上边缘
|
|
99
|
+
translateY = padding - pointerPos.y;
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
// 即使上方不超出,也检查下方是否被容器底部裁切
|
|
103
|
+
const cardBottom = cardTop + cardHeight;
|
|
104
|
+
if (cardBottom > containerHeight - padding) {
|
|
105
|
+
translateY = padding; // 翻转到下方
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
this.cardWrapper.style.left = `${pointerPos.x}px`;
|
|
109
|
+
this.cardWrapper.style.top = `${pointerPos.y}px`;
|
|
110
|
+
this.cardWrapper.style.transform = `translate(${translateX}px, ${translateY}px)`;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// 渲染工位详情卡片
|
|
114
|
+
private renderCard = async (props: { node: Konva.Group, settings: WorkstationSettings, pointerPos: { x: number, y: number } }) => {
|
|
115
|
+
const { node, settings, pointerPos } = props;
|
|
116
|
+
const { pageId, workstationNumber } = settings;
|
|
117
|
+
if (!node || !pageId || !workstationNumber || !pointerPos) return;
|
|
118
|
+
// 如果同一个工位的卡片已经渲染,不再重复渲染
|
|
119
|
+
const nodeId = node.id();
|
|
120
|
+
if (this.currentNodeId === nodeId) return;
|
|
121
|
+
this.closeCard(); // 先关闭已有卡片(不同工位时)
|
|
122
|
+
const container = this.render.stage.getContent();
|
|
123
|
+
this.cardWrapper = document.createElement('div');
|
|
124
|
+
this.cardWrapper.style.cssText = `
|
|
125
|
+
position:absolute;
|
|
126
|
+
left:${pointerPos.x}px;
|
|
127
|
+
top:${pointerPos.y}px;
|
|
128
|
+
transform:translate(-50%, calc(-100% - 8px));
|
|
129
|
+
z-index:1000;
|
|
130
|
+
`;
|
|
131
|
+
// 用原生监听器在 wrapper 层截断事件,阻止冒泡到 container(Konva 监听层)
|
|
132
|
+
// this.cardWrapper.addEventListener('click', (e) => e.stopImmediatePropagation());
|
|
133
|
+
this.cardWrapper.addEventListener('mousedown', (e) => e.stopImmediatePropagation());
|
|
134
|
+
container.appendChild(this.cardWrapper); // 挂载到 container 下面
|
|
135
|
+
this.cardRoot = createRoot(this.cardWrapper);
|
|
136
|
+
this.cardRoot.render(<DetailCard workstationNumber={workstationNumber} pageId={pageId} locale={this.render?.config?.locale} />);
|
|
137
|
+
this.currentNodeId = nodeId; // 记录当前打开的工位
|
|
138
|
+
this.render.stage.on('click tap', this.closeCard); // 点击画布外关闭卡片
|
|
139
|
+
this.render.stage.on('wheel', this.closeCard); // 缩放画布关闭卡片
|
|
140
|
+
this.render.stage.on('dragmove', this.closeCard); // 平移画布关闭卡片
|
|
141
|
+
// 渲染完成后根据容器边界重新校准卡片位置,避免被遮挡
|
|
142
|
+
requestAnimationFrame(() => this.adjustCardPosition(pointerPos));
|
|
143
|
+
};
|
|
144
|
+
|
|
30
145
|
/**
|
|
31
146
|
* 创建工位组件
|
|
32
147
|
*/
|
|
@@ -97,8 +212,9 @@ export class WorkstationComponent {
|
|
|
97
212
|
componentOnClick(node: Konva.Group, settings: WorkstationSettings) {
|
|
98
213
|
node.off("click");
|
|
99
214
|
node.on('click', (e) => {
|
|
100
|
-
|
|
101
|
-
|
|
215
|
+
e.cancelBubble = true; // 阻止事件向上冒泡到 Stage
|
|
216
|
+
const pointerPos = e.target.getStage()?.getPointerPosition();
|
|
217
|
+
this.renderCard({ node, settings, pointerPos });
|
|
102
218
|
});
|
|
103
219
|
}
|
|
104
220
|
}
|
|
@@ -141,16 +141,28 @@ export class SelectionHandlers implements Handler {
|
|
|
141
141
|
} else if (parent instanceof Konva.Transformer) {
|
|
142
142
|
// transformer 点击事件交给 transformer 自己的 handler
|
|
143
143
|
} else if (parent instanceof Konva.Group) {
|
|
144
|
-
|
|
145
|
-
if (e.evt.button === MouseButton["左键"] && !e.evt.ctrlKey) {
|
|
144
|
+
if (e.evt.button === MouseButton["左键"]) {
|
|
146
145
|
if (!this.render.ignore(parent) && !this.render.ignoreDraw(e.target)) {
|
|
147
146
|
// 锁定节点:在 select 之前先禁用 Konva 内置拖拽,防止 race condition
|
|
148
147
|
if (parent.attrs.locked) {
|
|
149
148
|
parent.draggable(false);
|
|
150
149
|
}
|
|
151
|
-
if (e.evt.ctrlKey || e.evt.metaKey) { //
|
|
152
|
-
this.render.selectionTool.
|
|
153
|
-
|
|
150
|
+
if (e.evt.ctrlKey || e.evt.metaKey) { // Ctrl+点选:切换选择
|
|
151
|
+
const idx = this.render.selectionTool.selectingNodes.findIndex((n) => n.id() === parent.id());
|
|
152
|
+
if (idx >= 0) {
|
|
153
|
+
// 已选中则移除
|
|
154
|
+
const nodes = [...this.render.selectionTool.selectingNodes];
|
|
155
|
+
nodes.splice(idx, 1);
|
|
156
|
+
if (nodes.length > 0) {
|
|
157
|
+
this.render.selectionTool.select(nodes);
|
|
158
|
+
} else {
|
|
159
|
+
this.render.selectionTool.selectingClear();
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
// 未选中则添加
|
|
163
|
+
this.render.selectionTool.select([...this.render.selectionTool.selectingNodes, parent]);
|
|
164
|
+
}
|
|
165
|
+
} else { // 普通点击:单选
|
|
154
166
|
this.render.selectionTool.select([parent]);
|
|
155
167
|
// 单选时无法通过 transformer 获取拖动初始位置 transformerMousedownPos
|
|
156
168
|
// 此时直接取目标的 getClientRect 位置
|
|
@@ -540,7 +540,11 @@ export class Render {
|
|
|
540
540
|
|
|
541
541
|
// 获取素材设置
|
|
542
542
|
getAssetSettings(asset?: Konva.Node): AssetSettings {
|
|
543
|
-
|
|
543
|
+
if (!asset) {
|
|
544
|
+
return { ...Render.AssetSettingsDefault };
|
|
545
|
+
}
|
|
546
|
+
const attrs = asset.attrs;
|
|
547
|
+
const base = attrs.assetSettings ?? { ...Render.AssetSettingsDefault };
|
|
544
548
|
return {
|
|
545
549
|
// 特定
|
|
546
550
|
...base,
|
|
@@ -554,9 +558,11 @@ export class Render {
|
|
|
554
558
|
x: parseFloat((asset?.position().x ?? 0).toFixed(1)),
|
|
555
559
|
y: parseFloat((asset?.position().y ?? 0).toFixed(1)),
|
|
556
560
|
rotation: parseFloat((asset?.rotation() ?? 0).toFixed(1)),
|
|
561
|
+
width: parseFloat(((attrs.width ?? 0) * (asset?.scaleX?.() ?? 1)).toFixed(1)),
|
|
562
|
+
height: parseFloat(((attrs.height ?? 0) * (asset?.scaleY?.() ?? 1)).toFixed(1)),
|
|
557
563
|
tension:
|
|
558
|
-
|
|
559
|
-
|
|
564
|
+
attrs.assetType === AssetType.Graph &&
|
|
565
|
+
attrs.graphType === "Curve"
|
|
560
566
|
? base.tension
|
|
561
567
|
: undefined
|
|
562
568
|
};
|
|
@@ -702,6 +708,13 @@ export class Render {
|
|
|
702
708
|
}
|
|
703
709
|
}
|
|
704
710
|
}
|
|
711
|
+
// 通过 scale 实现视觉尺寸变更(设置 Group 的 width/height 只影响裁剪,不会缩放内部子元素)
|
|
712
|
+
if (settings.width || settings.height) {
|
|
713
|
+
const baseWidth = asset.width() || 1;
|
|
714
|
+
const baseHeight = asset.height() || 1;
|
|
715
|
+
asset.scaleX(settings.width / baseWidth);
|
|
716
|
+
asset.scaleY(settings.height / baseHeight);
|
|
717
|
+
}
|
|
705
718
|
asset.setAttr('assetSettings', settings);
|
|
706
719
|
if (update) {
|
|
707
720
|
// 更新历史
|
|
@@ -6,14 +6,23 @@ const gifler = (window as any).gifler;
|
|
|
6
6
|
|
|
7
7
|
export class AssetTool {
|
|
8
8
|
private render: Render;
|
|
9
|
+
// 同一个 svgXML 内容只转一次 Blob,避免重复创建 Blob URL
|
|
10
|
+
private svgBlobCache: Map<string, string> = new Map();
|
|
11
|
+
// 同地址 svg 只 fetch 一次,避免重复网络请求
|
|
12
|
+
private svgRawCache: Map<string, string> = new Map();
|
|
13
|
+
|
|
9
14
|
constructor(render: Render) {
|
|
10
15
|
this.render = render;
|
|
11
16
|
}
|
|
12
17
|
|
|
13
|
-
// 加载 svg xml
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
// 加载 svg xml(带 Blob 缓存)
|
|
19
|
+
loadSvgXML(svgXML: string) {
|
|
20
|
+
let url = this.svgBlobCache.get(svgXML);
|
|
21
|
+
if (!url) {
|
|
22
|
+
const blob = new Blob([svgXML], { type: 'image/svg+xml' });
|
|
23
|
+
url = URL.createObjectURL(blob);
|
|
24
|
+
this.svgBlobCache.set(svgXML, url);
|
|
25
|
+
}
|
|
17
26
|
return new Promise<Konva.Image>((resolve) => {
|
|
18
27
|
Konva.Image.fromURL(url, (imageNode) => {
|
|
19
28
|
imageNode.setAttrs({ svgXML } as any);
|
|
@@ -22,10 +31,15 @@ export class AssetTool {
|
|
|
22
31
|
});
|
|
23
32
|
}
|
|
24
33
|
|
|
25
|
-
// 加载 svg
|
|
34
|
+
// 加载 svg(带 fetch 缓存 + Blob 缓存)
|
|
26
35
|
async loadSvg(src: string) {
|
|
27
|
-
|
|
28
|
-
|
|
36
|
+
let rawSvg = this.svgRawCache.get(src);
|
|
37
|
+
if (!rawSvg) {
|
|
38
|
+
rawSvg = await (await fetch(src)).text();
|
|
39
|
+
this.svgRawCache.set(src, rawSvg);
|
|
40
|
+
}
|
|
41
|
+
const svgXML = this.render.setSvgXMLSettings(rawSvg, this.render.getAssetSettings());
|
|
42
|
+
return this.loadSvgXML(svgXML);
|
|
29
43
|
}
|
|
30
44
|
|
|
31
45
|
// 加载 gif
|
|
@@ -31,7 +31,7 @@ export class ComponentFactory {
|
|
|
31
31
|
*/
|
|
32
32
|
async createImageComponent(options: CreateImageComponentOptions) {
|
|
33
33
|
const { src, type, componentType } = options;
|
|
34
|
-
const { pageHeight, pageWidth } = this.render.getPageSettings()
|
|
34
|
+
const { pageHeight, pageWidth } = this.render.getPageSettings();
|
|
35
35
|
const target = await this.render.assetTool[{ svg: "loadSvg", gif: "loadGif" }[type] || "loadImg"](handleImgUrl(src));
|
|
36
36
|
let width = target.width();
|
|
37
37
|
let height = target.height();
|
|
@@ -99,34 +99,46 @@ export class ImportExportTool {
|
|
|
99
99
|
});
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
// 汇总图片组件
|
|
103
|
+
getImgNodes(nodes: Konva.Node[] = []) {
|
|
104
|
+
const temNodes = nodes.map(node => {
|
|
105
105
|
if (node instanceof Konva.Group) {
|
|
106
|
-
|
|
107
|
-
await this.restoreImage(node.getChildren());
|
|
106
|
+
return this.getImgNodes(node.getChildren());
|
|
108
107
|
} else if (node instanceof Konva.Image) {
|
|
108
|
+
return node;
|
|
109
|
+
}
|
|
110
|
+
}).flat(Infinity).filter(Boolean);
|
|
111
|
+
return temNodes;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 恢复图片(用于导入)
|
|
115
|
+
async restoreImage(nodes: Konva.Node[] = []) {
|
|
116
|
+
const imgNodes = this.getImgNodes(nodes);
|
|
117
|
+
const tasks = imgNodes.map(node => {
|
|
118
|
+
// 处理图片
|
|
119
|
+
if (node.attrs.svgXML) { // svg 素材
|
|
120
|
+
return this.render.assetTool.loadSvgXML(node.attrs.svgXML);
|
|
121
|
+
} else if (node.attrs.gif) { // gif 素材
|
|
122
|
+
return this.render.assetTool.loadGif(node.attrs.gif);
|
|
123
|
+
} else if (node.attrs.src) { // 其他图片素材
|
|
124
|
+
return this.loadImage(node.attrs.src);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
Promise.all(tasks).then(res => {
|
|
128
|
+
res.forEach((imageNode, index) => {
|
|
129
|
+
const node = imgNodes[index];
|
|
109
130
|
// 处理图片
|
|
110
|
-
if (node
|
|
111
|
-
|
|
112
|
-
const url = URL.createObjectURL(blob);
|
|
113
|
-
const image = await this.loadImage(url);
|
|
114
|
-
if (image) { // 设置图片
|
|
115
|
-
node.image(image);
|
|
116
|
-
}
|
|
117
|
-
} else if (node.attrs.gif) { // gif 素材
|
|
118
|
-
const imageNode = await this.render.assetTool.loadGif(node.attrs.gif);
|
|
119
|
-
if (imageNode) {
|
|
131
|
+
if (imageNode && node) {
|
|
132
|
+
if (node.attrs.svgXML) { // svg 素材
|
|
120
133
|
node.image(imageNode.image());
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
node.image(image);
|
|
134
|
+
} else if (node.attrs.gif) { // gif 素材
|
|
135
|
+
node.image(imageNode.image());
|
|
136
|
+
} else if (node.attrs.src) { // 其他图片素材
|
|
137
|
+
node.image(imageNode);
|
|
126
138
|
}
|
|
127
139
|
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
140
|
+
})
|
|
141
|
+
});
|
|
130
142
|
}
|
|
131
143
|
|
|
132
144
|
// 恢复图形
|
|
@@ -15,6 +15,7 @@ export interface RenderConfig {
|
|
|
15
15
|
attractNode?: boolean;
|
|
16
16
|
readonly?: boolean;
|
|
17
17
|
themeToken?: { [key: string]: any };
|
|
18
|
+
locale?: string; // 语言
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export type RenderEvents = {
|
|
@@ -274,6 +275,8 @@ export interface AssetSettings {
|
|
|
274
275
|
text: string;
|
|
275
276
|
x: number;
|
|
276
277
|
y: number;
|
|
278
|
+
width?: number;
|
|
279
|
+
height?: number;
|
|
277
280
|
rotation: number;
|
|
278
281
|
tension: number;
|
|
279
282
|
dataSource?: string;
|
|
@@ -27,6 +27,17 @@ const positionSetting = {
|
|
|
27
27
|
]
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
// 尺寸配置
|
|
31
|
+
const sizeSetting = {
|
|
32
|
+
title: "宽高",
|
|
33
|
+
isGroup: true,
|
|
34
|
+
fields: [
|
|
35
|
+
{ title: "宽", noStyle: true, id: "width", extControlType: "float", colSpan: 1, min: 0, style: { width: "100%" } },
|
|
36
|
+
{ title: "高", noStyle: true, id: "height", extControlType: "float", colSpan: 1, min: 0, style: { width: "100%" } }
|
|
37
|
+
]
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
|
|
30
41
|
// 旋转角度配置
|
|
31
42
|
const rotationSetting = { fields: [{ title: "旋转角度", id: "rotation", extControlType: "int", min: -3600, max: 3600, style: { width: "100%" } }] };
|
|
32
43
|
|
|
@@ -39,6 +50,7 @@ const baseGraphSetting = [
|
|
|
39
50
|
{ title: "填充颜色", id: "fill", extControlType: "color", style: { width: "100%" } },
|
|
40
51
|
]
|
|
41
52
|
},
|
|
53
|
+
sizeSetting,
|
|
42
54
|
positionSetting,
|
|
43
55
|
rotationSetting
|
|
44
56
|
];
|