Skip to content

Commit b59a239

Browse files
mytharcherchenos
andauthored
Feat: client base entry of plugin workflow (nocobase#225)
* feat(plugin-workflow): add base client entry for workflow * fix(plugin-workflow): workflow table * feat: custom ui route (nocobase#227) * feat(plugin-workflow): add execution table * refactor(actions): expose utils of actions * fix(repo): move ".editorconfig" to root * feat(plugin-workflow): base workflow management able to add node * fix(plugin-workflow): fix empty workflow * feat(plugin-workfow): add flow canvas and style * fix(plugin-workflow): fix type for building * feat(plugin-workflow): fix add node in branch and add branch ui * feat(plugin-workflow): add calculation structure to condition config * fix(plugin-workflow): fix branch line style * feat(plugin-workflow): remove node with sub-branch * feat(plugin-workflow): add parallel node type * fix(plugin-workflow): fix dependency in client Co-authored-by: chenos <[email protected]>
1 parent e2616aa commit b59a239

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1937
-33
lines changed
File renamed without changes.

packages/actions/src/actions/add.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Context } from '..';
2-
import { getRepositoryFromParams } from './utils';
2+
import { getRepositoryFromParams } from '../utils';
33
import { BelongsToManyRepository, MultipleRelationRepository, HasManyRepository } from '@nocobase/database';
44

55
export async function add(ctx: Context, next) {

packages/actions/src/actions/create.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Context } from '..';
2-
import { getRepositoryFromParams } from './utils';
2+
import { getRepositoryFromParams } from '../utils';
33

44
export async function create(ctx: Context, next) {
55
const repository = getRepositoryFromParams(ctx);

packages/actions/src/actions/destroy.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Context } from '..';
2-
import { getRepositoryFromParams } from './utils';
2+
import { getRepositoryFromParams } from '../utils';
33

44
export async function destroy(ctx: Context, next) {
55
const repository = getRepositoryFromParams(ctx);

packages/actions/src/actions/get.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Context } from '..';
2-
import { getRepositoryFromParams } from './utils';
2+
import { getRepositoryFromParams } from '../utils';
33

44
export async function get(ctx: Context, next) {
55
const repository = getRepositoryFromParams(ctx);

packages/actions/src/actions/list.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ActionParams } from '@nocobase/resourcer';
22
import { Context } from '..';
3-
import { getRepositoryFromParams } from './utils';
3+
import { getRepositoryFromParams } from '../utils';
44

55
export const DEFAULT_PAGE = 1;
66
export const DEFAULT_PER_PAGE = 20;

packages/actions/src/actions/move.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Op, Model } from 'sequelize';
22

33
import { Context } from '..';
44
import { Collection, TargetKey, Repository, SortField } from '@nocobase/database';
5-
import { getRepositoryFromParams } from './utils';
5+
import { getRepositoryFromParams } from '../utils';
66

77
export async function move(ctx: Context, next) {
88
const repository = getRepositoryFromParams(ctx);
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
import { RelationRepositoryActionBuilder } from './utils';
1+
import { RelationRepositoryActionBuilder } from '../utils';
22
export const remove = RelationRepositoryActionBuilder('remove');

packages/actions/src/actions/set.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
import { RelationRepositoryActionBuilder } from './utils';
1+
import { RelationRepositoryActionBuilder } from '../utils';
22
export const set = RelationRepositoryActionBuilder('set');

packages/actions/src/actions/toggle.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Context } from '..';
2-
import { getRepositoryFromParams } from './utils';
2+
import { getRepositoryFromParams } from '../utils';
33
import { BelongsToManyRepository } from '@nocobase/database';
44

55
export async function toggle(ctx: Context, next) {

packages/actions/src/actions/update.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Context } from '..';
2-
import { getRepositoryFromParams } from './utils';
2+
import { getRepositoryFromParams } from '../utils';
33

44
export async function update(ctx: Context, next) {
55
const repository = getRepositoryFromParams(ctx);

packages/actions/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { Action } from '@nocobase/resourcer';
44
import lodash from 'lodash';
55
import * as actions from './actions';
66

7+
export * as utils from './utils';
8+
79
export type Next = () => Promise<any>;
810

911
export interface Context extends Koa.Context {

packages/actions/src/actions/utils.ts packages/actions/src/utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { MultipleRelationRepository, Repository } from '@nocobase/database';
2-
import { Context } from '..';
2+
import { Context } from '.';
33

44
export function getRepositoryFromParams(ctx: Context) {
55
const { resourceName, resourceOf } = ctx.action;

packages/api/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const plugins = [
4848
'@nocobase/plugin-users',
4949
'@nocobase/plugin-acl',
5050
'@nocobase/plugin-china-region',
51+
'@nocobase/plugin-workflow',
5152
];
5253

5354
for (const plugin of plugins) {

packages/app/src/pages/index.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ import {
2626
SignupPage,
2727
SystemSettingsProvider,
2828
SystemSettingsShortcut,
29+
useRequest,
30+
WorkflowPage,
31+
WorkflowShortcut,
2932
useRoutes
3033
} from '@nocobase/client';
3134
import { notification } from 'antd';
@@ -62,6 +65,7 @@ const providers = [
6265
RouteSchemaComponent,
6366
SigninPage,
6467
SignupPage,
68+
WorkflowPage,
6569
BlockTemplatePage,
6670
BlockTemplateDetails,
6771
},
@@ -75,6 +79,7 @@ const providers = [
7579
ACLShortcut,
7680
DesignableSwitch,
7781
CollectionManagerShortcut,
82+
WorkflowShortcut,
7883
SystemSettingsShortcut,
7984
SchemaTemplateShortcut,
8085
},

packages/client/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@formily/antd": "^2.0.15",
2424
"@formily/core": "^2.0.15",
2525
"@formily/react": "^2.0.15",
26+
"@nocobase/utils": "0.6.0-alpha.0",
2627
"ahooks": "^3.0.5",
2728
"antd": "^4.18.9",
2829
"axios": "^0.24.0",

packages/client/src/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ export * from './schema-templates';
2121
export * from './settings-form';
2222
export * from './system-settings';
2323
export * from './user';
24-
24+
export * from './workflow';

packages/client/src/route-switch/antd/admin-layout/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ const InternalAdminLayout = (props: any) => {
9595
{ component: 'DesignableSwitch', pin: true },
9696
{ component: 'CollectionManagerShortcut', pin: true },
9797
{ component: 'ACLShortcut', pin: true },
98+
{ component: 'WorkflowShortcut', pin: true },
9899
{ component: 'SchemaTemplateShortcut', pin: true },
99100
{ component: 'SystemSettingsShortcut' },
100101
]}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from "react";
2+
import { ResourceActionProvider, useRecord } from "..";
3+
4+
export const ExecutionResourceProvider = ({ request, ...others }) => {
5+
const workflow = useRecord();
6+
const props = {
7+
...others,
8+
request: {
9+
...request,
10+
params: {
11+
...request?.params,
12+
filter: {
13+
...(request?.params?.filter),
14+
workflowId: workflow.id
15+
}
16+
}
17+
},
18+
workflow
19+
};
20+
21+
return (
22+
<ResourceActionProvider {...props} />
23+
);
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import React, { useContext } from 'react';
2+
import { Dropdown, Menu, Button } from 'antd';
3+
import { PlusOutlined } from '@ant-design/icons';
4+
import { cx } from '@emotion/css';
5+
import { addButtonClass, branchBlockClass, branchClass, nodeBlockClass, nodeCardClass, nodeHeaderClass, nodeTitleClass } from './style';
6+
7+
import {
8+
useCollection,
9+
useResourceActionContext
10+
} from '..';
11+
import { Instruction, instructions, Node } from './nodes';
12+
13+
14+
15+
16+
function makeNodes(nodes): void {
17+
const nodesMap = new Map();
18+
nodes.forEach(item => nodesMap.set(item.id, item));
19+
for (let node of nodesMap.values()) {
20+
if (node.upstreamId) {
21+
node.upstream = nodesMap.get(node.upstreamId);
22+
}
23+
24+
if (node.downstreamId) {
25+
node.downstream = nodesMap.get(node.downstreamId);
26+
}
27+
}
28+
}
29+
30+
const FlowContext = React.createContext(null);
31+
32+
export function useFlowContext() {
33+
return useContext(FlowContext);
34+
}
35+
36+
export function WorkflowCanvas() {
37+
const { data, refresh, loading } = useResourceActionContext();
38+
39+
if (!data?.data && !loading) {
40+
return <div>加载失败</div>;
41+
}
42+
43+
const { nodes = [] } = data?.data ?? {};
44+
45+
makeNodes(nodes);
46+
47+
const entry = nodes.find(item => !item.upstream);
48+
49+
return (
50+
<FlowContext.Provider value={{
51+
nodes,
52+
onNodeAdded: refresh,
53+
onNodeRemoved: refresh
54+
}}>
55+
<div className={branchBlockClass}>
56+
<Branch entry={entry} />
57+
</div>
58+
<div className={cx(nodeCardClass)}>结束</div>
59+
</FlowContext.Provider>
60+
);
61+
}
62+
63+
export function Branch({
64+
from = null,
65+
entry = null,
66+
branchIndex = null,
67+
controller = null
68+
}) {
69+
const list = [];
70+
for (let node = entry; node; node = node.downstream) {
71+
list.push(node);
72+
}
73+
74+
return (
75+
<div className={cx(branchClass)}>
76+
<div className="workflow-branch-lines" />
77+
{controller}
78+
<AddButton upstream={from} branchIndex={branchIndex} />
79+
<div className="workflow-node-list">
80+
{list.map(item => {
81+
return (
82+
<div key={item.id} className={cx(nodeBlockClass)}>
83+
<Node data={item} />
84+
<AddButton upstream={item} />
85+
</div>
86+
);
87+
})}
88+
</div>
89+
</div>
90+
);
91+
}
92+
93+
// TODO(bug): useless observable
94+
// const instructionsList = observable(Array.from(instructions.getValues()));
95+
96+
interface AddButtonProps {
97+
upstream;
98+
branchIndex?: number;
99+
};
100+
101+
function AddButton({ upstream, branchIndex = null }: AddButtonProps) {
102+
const { resource } = useCollection();
103+
const { data } = useResourceActionContext();
104+
const { onNodeAdded } = useFlowContext();
105+
106+
async function onCreate({ keyPath }) {
107+
const type = keyPath.pop();
108+
const config = {};
109+
const [optionKey] = keyPath;
110+
if (optionKey) {
111+
const { value } = instructions.get(type).options.find(item => item.key === optionKey);
112+
Object.assign(config, value);
113+
}
114+
115+
const { data: { data: node } } = await resource.create({
116+
values: {
117+
type,
118+
workflowId: data.data.id,
119+
upstreamId: upstream?.id ?? null,
120+
branchIndex,
121+
config
122+
}
123+
});
124+
125+
onNodeAdded(node);
126+
}
127+
128+
return (
129+
<div className={cx(addButtonClass)}>
130+
<Dropdown trigger={['click']} overlay={
131+
<Menu onClick={ev => onCreate(ev)}>
132+
{(Array.from(instructions.getValues()) as Instruction[]).map(item => item.options
133+
? (
134+
<Menu.SubMenu key={item.type} title={item.title}>
135+
{item.options.map(option => (
136+
<Menu.Item key={option.key}>{option.label}</Menu.Item>
137+
))}
138+
</Menu.SubMenu>
139+
)
140+
: (
141+
<Menu.Item key={item.type}>{item.title}</Menu.Item>
142+
))}
143+
</Menu>
144+
}>
145+
<Button shape="circle" icon={<PlusOutlined />} />
146+
</Dropdown>
147+
</div>
148+
);
149+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react';
2+
import { Link } from 'react-router-dom';
3+
import { useActionContext, useRecord } from '..';
4+
5+
6+
export const WorkflowLink = () => {
7+
const { id } = useRecord();
8+
const { setVisible } = useActionContext();
9+
return (
10+
<Link to={`/admin/workflows/${id}`} onClick={() => setVisible(false)}>流程配置</Link>
11+
);
12+
}

0 commit comments

Comments
 (0)