@hzab/flowlong-designer 0.0.1

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.
@@ -0,0 +1,178 @@
1
+ import { useRef, useState, useContext } from "react";
2
+ import { Drawer, Button, Input, Form, Alert, Tag } from "antd";
3
+ import { UserOutlined, EditOutlined } from "@ant-design/icons";
4
+
5
+ import AddNode from "../AddNode";
6
+ import { SelectContext } from "../../../Selector/SelectContext";
7
+
8
+ import "./index.less";
9
+
10
+ export const Promoter = (props) => {
11
+ const selectorCtx = useContext(SelectContext);
12
+ const { modelValue: nodeConfig = {}, onChange } = props;
13
+
14
+ const [drawer, setDrawer] = useState(false);
15
+ const [isEditTitle, setIsEditTitle] = useState(false);
16
+
17
+ const [form, setForm] = useState({
18
+ nodeName: "",
19
+ setType: undefined,
20
+ nodeAssigneeList: [],
21
+ directorMode: undefined,
22
+ termAuto: false,
23
+ examineLevel: undefined,
24
+ selectMode: undefined,
25
+ directorLevel: undefined,
26
+ term: undefined,
27
+ termMode: undefined,
28
+ examineMode: undefined,
29
+ conditionList: [],
30
+ });
31
+
32
+ const nodeTitleRef = useRef();
33
+
34
+ function onShow() {
35
+ setForm(JSON.parse(JSON.stringify(nodeConfig)));
36
+ setIsEditTitle(false);
37
+ setDrawer(true);
38
+ }
39
+
40
+ function toText(nodeConfig) {
41
+ if (nodeConfig.nodeAssigneeList && nodeConfig.nodeAssigneeList.length > 0) {
42
+ return nodeConfig.nodeAssigneeList.map((item) => item.name).join("、");
43
+ } else {
44
+ return "所有人";
45
+ }
46
+ }
47
+
48
+ function onEditTitle() {
49
+ setIsEditTitle(true);
50
+ if (nodeTitleRef.current) {
51
+ // @ts-ignore
52
+ nodeTitleRef.current?.focus();
53
+ }
54
+ }
55
+ function onSaveTitle(e) {
56
+ setForm((f) => {
57
+ // @ts-ignore
58
+ f.nodeName = nodeTitleRef.current?.input?.value;
59
+ return { ...f };
60
+ });
61
+ setIsEditTitle(false);
62
+ }
63
+
64
+ function onDrawerClose() {
65
+ setDrawer(false);
66
+ }
67
+
68
+ function onDrawerSave() {
69
+ // nodeConfig.conditionNodes[curIdxRef.current] = form;
70
+ onChange && onChange({ ...form });
71
+ onDrawerClose();
72
+ }
73
+
74
+ function onRoleDel(idx) {
75
+ setForm((f) => {
76
+ f.nodeAssigneeList.splice(idx, 1);
77
+ return { ...f };
78
+ });
79
+ }
80
+
81
+ return (
82
+ <div className="promoter-wrap">
83
+ <div className="node-wrap">
84
+ <div className="node-wrap-box start-node" onClick={onShow}>
85
+ <div className="title">
86
+ <UserOutlined className="icon" />
87
+ <span>{nodeConfig.nodeName}</span>
88
+ </div>
89
+ <div className="content">
90
+ <span>{toText(nodeConfig)}</span>
91
+ </div>
92
+ </div>
93
+ <AddNode
94
+ modelValue={nodeConfig.childNode}
95
+ onChange={(val) => {
96
+ nodeConfig.childNode = val;
97
+ onChange && onChange({ ...nodeConfig });
98
+ }}
99
+ ></AddNode>
100
+
101
+ <Drawer
102
+ className={"promoter-drawer"}
103
+ open={drawer}
104
+ onClose={onDrawerClose}
105
+ destroyOnClose
106
+ getContainer="body"
107
+ width={600}
108
+ title={
109
+ <div className="node-wrap-drawer-title">
110
+ {isEditTitle ? (
111
+ <Input
112
+ ref={nodeTitleRef}
113
+ autoFocus
114
+ defaultValue={form.nodeName}
115
+ onBlur={onSaveTitle}
116
+ onPressEnter={onSaveTitle}
117
+ />
118
+ ) : (
119
+ <div className="drawer-title">
120
+ {form.nodeName || "发起人"}
121
+ <EditOutlined className="node-wrap-drawer__title-edit" onClick={onEditTitle} />
122
+ </div>
123
+ )}
124
+ </div>
125
+ }
126
+ footer={
127
+ <>
128
+ <Button type="primary" onClick={onDrawerSave}>
129
+ 保存
130
+ </Button>
131
+ <Button onClick={onDrawerClose}>取消</Button>
132
+ </>
133
+ }
134
+ >
135
+ <Form label-position="top" initialValues={form}>
136
+ <Form.Item label="谁可以发起此审批">
137
+ <Button
138
+ type="primary"
139
+ onClick={() =>
140
+ selectorCtx.setSelectHandler(2, form.nodeAssigneeList, (selected) => {
141
+ setForm((f) => {
142
+ // 设置 人员、角色 列表数组
143
+ f.nodeAssigneeList = selected;
144
+ return { ...f };
145
+ });
146
+ })
147
+ }
148
+ >
149
+ 选择角色
150
+ </Button>
151
+ <div className="tags-list">
152
+ {form.nodeAssigneeList?.map((it, idx) => {
153
+ return (
154
+ <Tag
155
+ key={idx}
156
+ className="node-assignee-item"
157
+ closable
158
+ onClose={() => {
159
+ onRoleDel(idx);
160
+ }}
161
+ >
162
+ {it.name}
163
+ </Tag>
164
+ );
165
+ })}
166
+ </div>
167
+ </Form.Item>
168
+ </Form>
169
+ {!form.nodeAssigneeList || form.nodeAssigneeList?.length <= 0 ? (
170
+ <Alert message="不指定则默认所有人都可发起此审批" type="info" />
171
+ ) : null}
172
+ </Drawer>
173
+ </div>
174
+ </div>
175
+ );
176
+ };
177
+
178
+ export default Promoter;
@@ -0,0 +1,7 @@
1
+ .send {
2
+ .node-wrap-box {
3
+ .title {
4
+ background-color: #3296fa;
5
+ }
6
+ }
7
+ }
@@ -0,0 +1,201 @@
1
+ import { useRef, useState, useContext } from "react";
2
+ import { Drawer, Button, Input, Form, Tag, Switch } from "antd";
3
+ import { EditOutlined, SendOutlined, DeleteOutlined } from "@ant-design/icons";
4
+
5
+ import AddNode from "../AddNode";
6
+ import { SelectContext } from "../../../Selector/SelectContext";
7
+
8
+ import "./index.less";
9
+
10
+ export const Send = (props) => {
11
+ const selectorCtx = useContext(SelectContext);
12
+ const { modelValue: nodeConfig = {}, onChange } = props;
13
+
14
+ const [drawer, setDrawer] = useState(false);
15
+ const [isEditTitle, setIsEditTitle] = useState(false);
16
+
17
+ const [form, setForm] = useState({
18
+ nodeName: "",
19
+ setType: undefined,
20
+ nodeAssigneeList: [],
21
+ directorMode: undefined,
22
+ termAuto: false,
23
+ examineLevel: undefined,
24
+ selectMode: undefined,
25
+ directorLevel: undefined,
26
+ term: undefined,
27
+ termMode: undefined,
28
+ examineMode: undefined,
29
+ conditionList: [],
30
+ userSelectFlag: undefined,
31
+ });
32
+
33
+ const nodeTitleRef = useRef();
34
+
35
+ function onShow() {
36
+ setForm(JSON.parse(JSON.stringify(nodeConfig)));
37
+ setIsEditTitle(false);
38
+ setDrawer(true);
39
+ }
40
+
41
+ function onDrawerClose() {
42
+ setDrawer(false);
43
+ }
44
+
45
+ function onEditTitle() {
46
+ setIsEditTitle(true);
47
+ if (nodeTitleRef.current) {
48
+ // @ts-ignore
49
+ nodeTitleRef.current?.focus();
50
+ }
51
+ }
52
+ function onSaveTitle(e) {
53
+ setForm((f) => {
54
+ // @ts-ignore
55
+ f.nodeName = nodeTitleRef.current?.input?.value;
56
+ return { ...f };
57
+ });
58
+ setIsEditTitle(false);
59
+ }
60
+
61
+ function onDrawerSave() {
62
+ // nodeConfig.conditionNodes[curIdxRef.current] = form;
63
+ onChange && onChange({ ...form });
64
+ onDrawerClose();
65
+ }
66
+
67
+ function delNode() {
68
+ onChange && onChange({ ...nodeConfig.childNode });
69
+ }
70
+
71
+ function toText(nodeConfig) {
72
+ if (nodeConfig.nodeAssigneeList && nodeConfig.nodeAssigneeList.length > 0) {
73
+ const users = nodeConfig.nodeAssigneeList.map((item) => item.name).join("、");
74
+ return users;
75
+ } else {
76
+ if (nodeConfig.userSelectFlag) {
77
+ return "发起人自选";
78
+ } else {
79
+ return false;
80
+ }
81
+ }
82
+ }
83
+
84
+ function delUser(index) {
85
+ form.nodeAssigneeList.splice(index, 1);
86
+ setForm((f) => ({ ...f }));
87
+ }
88
+
89
+ return (
90
+ <div className="send">
91
+ <div className="node-wrap">
92
+ <div className="node-wrap-box" onClick={onShow}>
93
+ <div className="title">
94
+ <SendOutlined className="icon" />
95
+ <span>{nodeConfig.nodeName || "抄送人"}</span>
96
+ <DeleteOutlined
97
+ className="close"
98
+ onClick={(e) => {
99
+ e.stopPropagation();
100
+ delNode();
101
+ }}
102
+ />
103
+ </div>
104
+ <div className="content">
105
+ {toText(nodeConfig) ? <span>{toText(nodeConfig)}</span> : <span className="placeholder">请选择人员</span>}
106
+ </div>
107
+ </div>
108
+ <AddNode
109
+ modelValue={nodeConfig.childNode}
110
+ onChange={(val) => {
111
+ nodeConfig.childNode = val;
112
+ onChange && onChange({ ...nodeConfig });
113
+ }}
114
+ ></AddNode>
115
+
116
+ <Drawer
117
+ v-model="drawer"
118
+ open={drawer}
119
+ onClose={onDrawerClose}
120
+ destroyOnClose
121
+ getContainer="body"
122
+ width={600}
123
+ title={
124
+ <div className="node-wrap-drawer-title">
125
+ {isEditTitle ? (
126
+ <Input
127
+ ref={nodeTitleRef}
128
+ autoFocus
129
+ defaultValue={form.nodeName}
130
+ onBlur={onSaveTitle}
131
+ onPressEnter={onSaveTitle}
132
+ />
133
+ ) : (
134
+ <div className="drawer-title">
135
+ {form.nodeName || "抄送人设置"}
136
+ <EditOutlined className="node-wrap-drawer__title-edit" onClick={onEditTitle} />
137
+ </div>
138
+ )}
139
+ </div>
140
+ }
141
+ footer={
142
+ <>
143
+ <Button type="primary" onClick={onDrawerSave}>
144
+ 保存
145
+ </Button>
146
+ <Button onClick={onDrawerClose}>取消</Button>
147
+ </>
148
+ }
149
+ >
150
+ <Form label-position="top" initialValues={form}>
151
+ <Form.Item label="选择要抄送的人员">
152
+ <Button
153
+ type="primary"
154
+ onClick={() => {
155
+ selectorCtx.setSelectHandler(1, form.nodeAssigneeList, (selected) => {
156
+ setForm((f) => {
157
+ // 设置 人员、角色 列表数组
158
+ f.nodeAssigneeList = selected;
159
+ return { ...f };
160
+ });
161
+ });
162
+ }}
163
+ >
164
+ 选择人员
165
+ </Button>
166
+ <div className="tags-list">
167
+ {form.nodeAssigneeList?.map((it, idx) => {
168
+ return (
169
+ <Tag
170
+ key={it.id ?? idx}
171
+ className="node-assignee-item"
172
+ closable
173
+ onClose={() => {
174
+ delUser(idx);
175
+ }}
176
+ >
177
+ {it.name}
178
+ </Tag>
179
+ );
180
+ })}
181
+ </div>
182
+ </Form.Item>
183
+ <Form.Item label="允许发起人自选抄送人">
184
+ <Switch
185
+ checked={form.userSelectFlag}
186
+ onChange={(val) => {
187
+ setForm((f) => {
188
+ form.userSelectFlag = val;
189
+ return { ...f };
190
+ });
191
+ }}
192
+ ></Switch>
193
+ </Form.Item>
194
+ </Form>
195
+ </Drawer>
196
+ </div>
197
+ </div>
198
+ );
199
+ };
200
+
201
+ export default Send;
@@ -0,0 +1,29 @@
1
+ .dark .sc-workflow-design {
2
+ .node-wrap-box,
3
+ .auto-judge {
4
+ background: #2b2b2b;
5
+ }
6
+ .col-box {
7
+ background: var(--el-bg-color);
8
+ }
9
+ .top-left-cover-line,
10
+ .top-right-cover-line,
11
+ .bottom-left-cover-line,
12
+ .bottom-right-cover-line {
13
+ background-color: var(--el-bg-color);
14
+ }
15
+ .node-wrap-box::before,
16
+ .auto-judge::before {
17
+ background-color: var(--el-bg-color);
18
+ }
19
+ .branch-box .add-branch {
20
+ background: var(--el-bg-color);
21
+ }
22
+ .end-node .end-node-text {
23
+ color: #ccc;
24
+ }
25
+ .auto-judge .sort-left:hover,
26
+ .auto-judge .sort-right:hover {
27
+ background: var(--el-bg-color);
28
+ }
29
+ }
@@ -0,0 +1,30 @@
1
+ import Promoter from "./Nodes/Promoter";
2
+ import Approver from "./Nodes/Approver";
3
+ import Send from "./Nodes/Send";
4
+ import Branch from "./Nodes/Branch";
5
+
6
+ export const NodeWrap = (props) => {
7
+ const { modelValue: nodeConfig, onChange } = props;
8
+ return (
9
+ <div className="node-wrap">
10
+ {nodeConfig.type == 0 ? <Promoter modelValue={nodeConfig} onChange={onChange}></Promoter> : null}
11
+
12
+ {nodeConfig.type == 1 ? <Approver modelValue={nodeConfig} onChange={onChange}></Approver> : null}
13
+
14
+ {nodeConfig.type == 2 ? <Send modelValue={nodeConfig} onChange={onChange}></Send> : null}
15
+ {nodeConfig.type == 4 ? <Branch modelValue={nodeConfig} onChange={onChange} ItemSlot={NodeWrap}></Branch> : null}
16
+
17
+ {nodeConfig.childNode ? (
18
+ <NodeWrap
19
+ modelValue={nodeConfig.childNode}
20
+ onChange={(val) => {
21
+ nodeConfig.childNode = val;
22
+ onChange && onChange(nodeConfig);
23
+ }}
24
+ ></NodeWrap>
25
+ ) : null}
26
+ </div>
27
+ );
28
+ };
29
+
30
+ export default NodeWrap;
@@ -0,0 +1,23 @@
1
+ import { useRef, useState } from "react";
2
+
3
+ import NodeWrap from "../NodeWrap";
4
+ import Selector from "../Selector";
5
+
6
+ export const ScWorkflow = (props) => {
7
+ const { modelValue: nodeConfig, onChange } = props;
8
+
9
+ return (
10
+ <div className="sc-workflow-design">
11
+ <div className="box-scale">
12
+ {nodeConfig ? <NodeWrap modelValue={nodeConfig} onChange={onChange}></NodeWrap> : null}
13
+ <div className="end-node">
14
+ <div className="end-node-circle"></div>
15
+ <div className="end-node-text">流程结束</div>
16
+ </div>
17
+ </div>
18
+ <Selector />
19
+ </div>
20
+ );
21
+ };
22
+
23
+ export default ScWorkflow;
@@ -0,0 +1,32 @@
1
+ import { createContext } from "react";
2
+
3
+ export interface ISelectHandlerI {
4
+ /** 弹窗展示 */
5
+ open: boolean;
6
+ /** 选择类型 */
7
+ type: number;
8
+ /** 已选中的数据 */
9
+ value: [];
10
+ /** 选中保存回调 */
11
+ onSave: (selected) => void;
12
+ listModel,
13
+ treeModel,
14
+ roleListModel,
15
+ }
16
+
17
+ /**
18
+ * 选择组件 context
19
+ */
20
+ export const SelectContext = createContext({
21
+ selectHandler: {
22
+ open: false,
23
+ type: undefined,
24
+ value: [],
25
+ onSave: (s) => {},
26
+ listModel:undefined,
27
+ treeModel:undefined,
28
+ roleListModel:undefined
29
+ },
30
+ setSelectHandler(type, value, onSave) {},
31
+ onClose() {},
32
+ });
@@ -0,0 +1,139 @@
1
+ .selector-modal {
2
+ --border-color-light: #ccc;
3
+ .sc-user-select {
4
+ display: flex;
5
+ }
6
+ .sc-user-select__left {
7
+ // width: 400px;
8
+ }
9
+ .sc-user-select__right {
10
+ flex: 1;
11
+ }
12
+
13
+ .sc-user-select__search {
14
+ padding-bottom: 10px;
15
+ }
16
+
17
+ .sc-user-select__select {
18
+ display: flex;
19
+ border: 1px solid var(--border-color-light);
20
+ background: var(--el-color-white);
21
+ }
22
+ .sc-user-select__tree {
23
+ width: 200px;
24
+ height: 300px;
25
+ border-right: 1px solid var(--border-color-light);
26
+ overflow: auto;
27
+ }
28
+ .sc-user-select__user {
29
+ // width: 200px;
30
+ flex: 1;
31
+ height: 300px;
32
+ display: flex;
33
+ flex-direction: column;
34
+ overflow: auto;
35
+ }
36
+ .sc-user-select__user__list {
37
+ flex: 1;
38
+ overflow: auto;
39
+ .sc-user-select__user__item {
40
+ display: flex;
41
+ align-items: center;
42
+ padding: 5px 20px;
43
+ cursor: pointer;
44
+ &.active {
45
+ background: var(--el-color-primary-light-9);
46
+ }
47
+ }
48
+ }
49
+ .sc-user-select__user footer {
50
+ height: 36px;
51
+ padding-top: 5px;
52
+ border-top: 1px solid var(--border-color-light);
53
+ }
54
+
55
+ .sc-user-select__toicon {
56
+ display: flex;
57
+ justify-content: center;
58
+ align-items: center;
59
+ margin: 0 10px;
60
+ }
61
+ .sc-user-select__toicon i {
62
+ display: flex;
63
+ justify-content: center;
64
+ align-items: center;
65
+ background: #ccc;
66
+ width: 20px;
67
+ height: 20px;
68
+ text-align: center;
69
+ line-height: 20px;
70
+ border-radius: 50%;
71
+ color: #fff;
72
+ }
73
+
74
+ .sc-user-select__selected {
75
+ height: 345px;
76
+ width: 200px;
77
+ border: 1px solid var(--border-color-light);
78
+ background: var(--el-color-white);
79
+ }
80
+
81
+ .sc-user-select__selected header {
82
+ height: 43px;
83
+ line-height: 43px;
84
+ border-bottom: 1px solid var(--border-color-light);
85
+ padding: 0 15px;
86
+ font-size: 12px;
87
+ }
88
+ .sc-user-select__selected ul {
89
+ height: 300px;
90
+ overflow: auto;
91
+ padding-left: 0;
92
+ }
93
+ .sc-user-select__selected li {
94
+ display: flex;
95
+ align-items: center;
96
+ justify-content: space-between;
97
+ padding: 5px 5px 5px 15px;
98
+ height: 38px;
99
+ }
100
+ .sc-user-select__selected li .name {
101
+ display: flex;
102
+ align-items: center;
103
+ .ant-avatar{
104
+ margin-right: 10px;
105
+ }
106
+ }
107
+ .sc-user-select__selected li .name .el-avatar {
108
+ background: #409eff;
109
+ margin-right: 10px;
110
+ }
111
+ .sc-user-select__selected li .name label {
112
+ }
113
+ .sc-user-select__selected li .delete {
114
+ display: none;
115
+ }
116
+ .sc-user-select__selected li:hover {
117
+ background: var(--el-color-primary-light-9);
118
+ }
119
+ .sc-user-select__selected li:hover .delete {
120
+ display: inline-block;
121
+ }
122
+
123
+ .sc-user-select-role .sc-user-select__left {
124
+ width: 200px;
125
+ }
126
+ .sc-user-select-role .sc-user-select__tree {
127
+ border: none;
128
+ height: 343px;
129
+ }
130
+ .sc-user-select-role .sc-user-select__selected {
131
+ }
132
+
133
+ [data-theme="dark"] .sc-user-select__selected li:hover {
134
+ background: rgba(0, 0, 0, 0.2);
135
+ }
136
+ [data-theme="dark"] .sc-user-select__toicon i {
137
+ background: #383838;
138
+ }
139
+ }