refactor(plugin-workflow): change task center popup logic

This commit is contained in:
mytharcher 2025-04-13 11:54:57 +08:00
parent 2dacb7b878
commit 7f35abe348
7 changed files with 144 additions and 96 deletions

View File

@ -72,7 +72,7 @@ const InternalActionBar: FC = (props: any) => {
<Portal>
<DndContext>
<div
style={{ display: 'flex', alignItems: 'center', gap: 8, ...style, marginTop: 0 }}
style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 0, ...style }}
{...others}
className={cx(others.className, 'nb-action-bar')}
>

View File

@ -158,7 +158,7 @@ const InternalList = withSkeletonComponent(
>
<AntdList
{...props}
pagination={!meta || !field.value?.length ? false : paginationProps}
pagination={!meta || !field.value?.length || count <= field.value?.length ? false : paginationProps}
loading={service?.loading}
>
{field.value?.length

View File

@ -50,6 +50,7 @@ import WorkflowPlugin, {
JOB_STATUS,
WorkflowTitle,
TASK_STATUS,
usePopupRecordContext,
} from '@nocobase/plugin-workflow/client';
import { NAMESPACE, useLang } from '../locale';
@ -619,11 +620,13 @@ function TaskItem() {
const token = useAntdToken();
const record = useCollectionRecordData();
const navigate = useNavigate();
const { setRecord } = usePopupRecordContext();
const onOpen = useCallback(
(e: React.MouseEvent) => {
const targetElement = e.target as Element; // 将事件目标转换为Element类型
const currentTargetElement = e.currentTarget as Element;
if (currentTargetElement.contains(targetElement)) {
setRecord(record);
navigate(`./${record.id}`);
}
e.stopPropagation();

View File

@ -16,13 +16,14 @@ import * as jobActions from './actions';
import ManualInstruction from './ManualInstruction';
import { MANUAL_TASK_TYPE } from '../common/constants';
import { Model } from '@nocobase/database';
interface WorkflowManualTaskModel {
id: number;
userId: number;
workflowId: number;
executionId: number;
status: number;
class WorkflowManualTaskModel extends Model {
declare id: number;
declare userId: number;
declare workflowId: number;
declare executionId: number;
declare status: number;
}
export default class extends Plugin {
@ -55,7 +56,13 @@ export default class extends Plugin {
const workflowPlugin = this.app.pm.get(WorkflowPlugin) as WorkflowPlugin;
workflowPlugin.registerInstruction('manual', ManualInstruction);
this.db.on('workflowManualTasks.afterSave', async (task: WorkflowManualTaskModel, options) => {
this.db.on('workflowManualTasks.afterSave', async (task: WorkflowManualTaskModel, { transaction }) => {
// const allCount = await (task.constructor as typeof WorkflowManualTaskModel).count({
// where: {
// userId: task.userId,
// },
// transaction,
// });
await workflowPlugin.toggleTaskStatus(
{
type: MANUAL_TASK_TYPE,
@ -63,8 +70,9 @@ export default class extends Plugin {
userId: task.userId,
workflowId: task.workflowId,
},
Boolean(task.status),
options,
task.status === JOB_STATUS.PENDING,
// allCount,
{ transaction },
);
});
}

View File

@ -11,7 +11,7 @@ import { PageHeader } from '@ant-design/pro-layout';
import { Badge, Button, Layout, Menu, Tabs, Tooltip } from 'antd';
import classnames from 'classnames';
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { Link, Outlet, useNavigate, useParams } from 'react-router-dom';
import { Link, useNavigate, useParams } from 'react-router-dom';
import {
ActionContextProvider,
@ -21,6 +21,7 @@ import {
SchemaComponent,
SchemaComponentContext,
SchemaComponentOptions,
useAPIClient,
useApp,
useCompile,
useDocumentTitle,
@ -163,7 +164,11 @@ function useCurrentTaskType() {
function PopupContext(props: any) {
const { popupId } = useParams();
const { record } = usePopupRecordContext();
const navigate = useNavigate();
if (!popupId) {
return null;
}
return (
<ActionContextProvider
visible={Boolean(popupId)}
@ -174,17 +179,24 @@ function PopupContext(props: any) {
}}
openMode="modal"
>
<CollectionRecordProvider record={{ id: popupId }}>{props.children}</CollectionRecordProvider>
<CollectionRecordProvider record={record}>{props.children}</CollectionRecordProvider>
</ActionContextProvider>
);
}
const PopupRecordContext = createContext<any>({ record: null, setRecord: (record) => {} });
export function usePopupRecordContext() {
return useContext(PopupRecordContext);
}
export function WorkflowTasks() {
const compile = useCompile();
const { setTitle } = useDocumentTitle();
const navigate = useNavigate();
const apiClient = useAPIClient();
const { taskType, status = TASK_STATUS.PENDING, popupId } = useParams();
const { token } = useToken();
const [currentRecord, setCurrentRecord] = useState<any>(null);
const items = useTaskTypeItems();
@ -202,6 +214,24 @@ export function WorkflowTasks() {
}
}, [items, navigate, status, taskType]);
useEffect(() => {
if (popupId && !currentRecord) {
apiClient
.resource(collection)
.get({
filterByTk: popupId,
})
.then((res) => {
if (res.data?.data) {
setCurrentRecord(res.data.data);
}
})
.catch((err) => {
console.error(err);
});
}
}, [popupId, collection, currentRecord, apiClient]);
const typeKey = taskType ?? items[0].key;
return (
@ -227,88 +257,95 @@ export function WorkflowTasks() {
}
`}
>
<SchemaComponentContext.Provider value={{ designable: false }}>
<SchemaComponent
components={{
Layout,
PageHeader,
StatusTabs,
}}
schema={{
name: `${taskType}-${status}`,
type: 'void',
'x-decorator': 'List.Decorator',
'x-decorator-props': {
collection,
action,
params: {
pageSize: 20,
sort: ['-createdAt'],
...params,
},
},
properties: {
header: {
type: 'void',
'x-component': 'PageHeader',
'x-component-props': {
className: classnames('pageHeaderCss'),
style: {
background: token.colorBgContainer,
padding: '12px 24px 0 24px',
},
title,
},
properties: {
tabs: {
type: 'void',
'x-component': 'StatusTabs',
},
<PopupRecordContext.Provider
value={{
record: currentRecord,
setRecord: setCurrentRecord,
}}
>
<SchemaComponentContext.Provider value={{ designable: false }}>
<SchemaComponent
components={{
Layout,
PageHeader,
StatusTabs,
}}
schema={{
name: `${taskType}-${status}`,
type: 'void',
'x-decorator': 'List.Decorator',
'x-decorator-props': {
collection,
action,
params: {
pageSize: 20,
sort: ['-createdAt'],
...params,
},
},
content: {
type: 'void',
'x-component': 'Layout.Content',
'x-component-props': {
className: contentClass,
style: {
padding: `${token.paddingPageVertical}px ${token.paddingPageHorizontal}px`,
},
},
properties: {
list: {
type: 'array',
'x-component': 'List',
'x-component-props': {
className: css`
> .itemCss:not(:last-child) {
border-bottom: none;
}
`,
locale: {
emptyText: `{{ t("No data yet", { ns: "${NAMESPACE}" }) }}`,
},
properties: {
header: {
type: 'void',
'x-component': 'PageHeader',
'x-component-props': {
className: classnames('pageHeaderCss'),
style: {
background: token.colorBgContainer,
padding: '12px 24px 0 24px',
},
properties: {
item: {
type: 'object',
'x-decorator': 'List.Item',
'x-component': Item,
'x-read-pretty': true,
title,
},
properties: {
tabs: {
type: 'void',
'x-component': 'StatusTabs',
},
},
},
content: {
type: 'void',
'x-component': 'Layout.Content',
'x-component-props': {
className: contentClass,
style: {
padding: `${token.paddingPageVertical}px ${token.paddingPageHorizontal}px`,
},
},
properties: {
list: {
type: 'array',
'x-component': 'List',
'x-component-props': {
className: css`
> .itemCss:not(:last-child) {
border-bottom: none;
}
`,
locale: {
emptyText: `{{ t("No data yet", { ns: "${NAMESPACE}" }) }}`,
},
},
properties: {
item: {
type: 'object',
'x-decorator': 'List.Item',
'x-component': Item,
'x-read-pretty': true,
},
},
},
},
},
popup: {
type: 'void',
'x-decorator': PopupContext,
'x-component': Detail,
},
},
popup: {
type: 'void',
'x-decorator': PopupContext,
'x-component': Detail,
},
},
}}
/>
</SchemaComponentContext.Provider>
}}
/>
</SchemaComponentContext.Provider>
</PopupRecordContext.Provider>
</Layout>
</Layout>
);

View File

@ -188,4 +188,4 @@ export { default as useStyles } from './style';
export { Trigger, useTrigger } from './triggers';
export * from './utils';
export * from './variable';
export { TASK_STATUS } from './WorkflowTasks';
export { TASK_STATUS, usePopupRecordContext } from './WorkflowTasks';

View File

@ -793,10 +793,16 @@ export default class PluginWorkflowServer extends Plugin {
/**
* @experimental
*/
public async toggleTaskStatus(task: WorkflowTaskModel, done: boolean, { transaction }: Transactionable) {
public async toggleTaskStatus(task: WorkflowTaskModel, on: boolean, { transaction }: Transactionable) {
const { db } = this.app;
const repository = db.getRepository('workflowTasks') as WorkflowTasksRepository;
if (done) {
if (on) {
await repository.updateOrCreate({
filterKeys: ['key', 'type'],
values: task,
transaction,
});
} else {
await repository.destroy({
filter: {
type: task.type,
@ -804,12 +810,6 @@ export default class PluginWorkflowServer extends Plugin {
},
transaction,
});
} else {
await repository.updateOrCreate({
filterKeys: ['key', 'type'],
values: task,
transaction,
});
}
// NOTE: