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> <Portal>
<DndContext> <DndContext>
<div <div
style={{ display: 'flex', alignItems: 'center', gap: 8, ...style, marginTop: 0 }} style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 0, ...style }}
{...others} {...others}
className={cx(others.className, 'nb-action-bar')} className={cx(others.className, 'nb-action-bar')}
> >

View File

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

View File

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

View File

@ -16,13 +16,14 @@ import * as jobActions from './actions';
import ManualInstruction from './ManualInstruction'; import ManualInstruction from './ManualInstruction';
import { MANUAL_TASK_TYPE } from '../common/constants'; import { MANUAL_TASK_TYPE } from '../common/constants';
import { Model } from '@nocobase/database';
interface WorkflowManualTaskModel { class WorkflowManualTaskModel extends Model {
id: number; declare id: number;
userId: number; declare userId: number;
workflowId: number; declare workflowId: number;
executionId: number; declare executionId: number;
status: number; declare status: number;
} }
export default class extends Plugin { export default class extends Plugin {
@ -55,7 +56,13 @@ export default class extends Plugin {
const workflowPlugin = this.app.pm.get(WorkflowPlugin) as WorkflowPlugin; const workflowPlugin = this.app.pm.get(WorkflowPlugin) as WorkflowPlugin;
workflowPlugin.registerInstruction('manual', ManualInstruction); 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( await workflowPlugin.toggleTaskStatus(
{ {
type: MANUAL_TASK_TYPE, type: MANUAL_TASK_TYPE,
@ -63,8 +70,9 @@ export default class extends Plugin {
userId: task.userId, userId: task.userId,
workflowId: task.workflowId, workflowId: task.workflowId,
}, },
Boolean(task.status), task.status === JOB_STATUS.PENDING,
options, // 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 { Badge, Button, Layout, Menu, Tabs, Tooltip } from 'antd';
import classnames from 'classnames'; import classnames from 'classnames';
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; 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 { import {
ActionContextProvider, ActionContextProvider,
@ -21,6 +21,7 @@ import {
SchemaComponent, SchemaComponent,
SchemaComponentContext, SchemaComponentContext,
SchemaComponentOptions, SchemaComponentOptions,
useAPIClient,
useApp, useApp,
useCompile, useCompile,
useDocumentTitle, useDocumentTitle,
@ -163,7 +164,11 @@ function useCurrentTaskType() {
function PopupContext(props: any) { function PopupContext(props: any) {
const { popupId } = useParams(); const { popupId } = useParams();
const { record } = usePopupRecordContext();
const navigate = useNavigate(); const navigate = useNavigate();
if (!popupId) {
return null;
}
return ( return (
<ActionContextProvider <ActionContextProvider
visible={Boolean(popupId)} visible={Boolean(popupId)}
@ -174,17 +179,24 @@ function PopupContext(props: any) {
}} }}
openMode="modal" openMode="modal"
> >
<CollectionRecordProvider record={{ id: popupId }}>{props.children}</CollectionRecordProvider> <CollectionRecordProvider record={record}>{props.children}</CollectionRecordProvider>
</ActionContextProvider> </ActionContextProvider>
); );
} }
const PopupRecordContext = createContext<any>({ record: null, setRecord: (record) => {} });
export function usePopupRecordContext() {
return useContext(PopupRecordContext);
}
export function WorkflowTasks() { export function WorkflowTasks() {
const compile = useCompile(); const compile = useCompile();
const { setTitle } = useDocumentTitle(); const { setTitle } = useDocumentTitle();
const navigate = useNavigate(); const navigate = useNavigate();
const apiClient = useAPIClient();
const { taskType, status = TASK_STATUS.PENDING, popupId } = useParams(); const { taskType, status = TASK_STATUS.PENDING, popupId } = useParams();
const { token } = useToken(); const { token } = useToken();
const [currentRecord, setCurrentRecord] = useState<any>(null);
const items = useTaskTypeItems(); const items = useTaskTypeItems();
@ -202,6 +214,24 @@ export function WorkflowTasks() {
} }
}, [items, navigate, status, taskType]); }, [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; const typeKey = taskType ?? items[0].key;
return ( return (
@ -227,88 +257,95 @@ export function WorkflowTasks() {
} }
`} `}
> >
<SchemaComponentContext.Provider value={{ designable: false }}> <PopupRecordContext.Provider
<SchemaComponent value={{
components={{ record: currentRecord,
Layout, setRecord: setCurrentRecord,
PageHeader, }}
StatusTabs, >
}} <SchemaComponentContext.Provider value={{ designable: false }}>
schema={{ <SchemaComponent
name: `${taskType}-${status}`, components={{
type: 'void', Layout,
'x-decorator': 'List.Decorator', PageHeader,
'x-decorator-props': { StatusTabs,
collection, }}
action, schema={{
params: { name: `${taskType}-${status}`,
pageSize: 20, type: 'void',
sort: ['-createdAt'], 'x-decorator': 'List.Decorator',
...params, 'x-decorator-props': {
}, collection,
}, action,
properties: { params: {
header: { pageSize: 20,
type: 'void', sort: ['-createdAt'],
'x-component': 'PageHeader', ...params,
'x-component-props': {
className: classnames('pageHeaderCss'),
style: {
background: token.colorBgContainer,
padding: '12px 24px 0 24px',
},
title,
},
properties: {
tabs: {
type: 'void',
'x-component': 'StatusTabs',
},
}, },
}, },
content: { properties: {
type: 'void', header: {
'x-component': 'Layout.Content', type: 'void',
'x-component-props': { 'x-component': 'PageHeader',
className: contentClass, 'x-component-props': {
style: { className: classnames('pageHeaderCss'),
padding: `${token.paddingPageVertical}px ${token.paddingPageHorizontal}px`, style: {
}, background: token.colorBgContainer,
}, padding: '12px 24px 0 24px',
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: { title,
item: { },
type: 'object', properties: {
'x-decorator': 'List.Item', tabs: {
'x-component': Item, type: 'void',
'x-read-pretty': true, '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, </SchemaComponentContext.Provider>
'x-component': Detail, </PopupRecordContext.Provider>
},
},
}}
/>
</SchemaComponentContext.Provider>
</Layout> </Layout>
</Layout> </Layout>
); );

View File

@ -188,4 +188,4 @@ export { default as useStyles } from './style';
export { Trigger, useTrigger } from './triggers'; export { Trigger, useTrigger } from './triggers';
export * from './utils'; export * from './utils';
export * from './variable'; 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 * @experimental
*/ */
public async toggleTaskStatus(task: WorkflowTaskModel, done: boolean, { transaction }: Transactionable) { public async toggleTaskStatus(task: WorkflowTaskModel, on: boolean, { transaction }: Transactionable) {
const { db } = this.app; const { db } = this.app;
const repository = db.getRepository('workflowTasks') as WorkflowTasksRepository; const repository = db.getRepository('workflowTasks') as WorkflowTasksRepository;
if (done) { if (on) {
await repository.updateOrCreate({
filterKeys: ['key', 'type'],
values: task,
transaction,
});
} else {
await repository.destroy({ await repository.destroy({
filter: { filter: {
type: task.type, type: task.type,
@ -804,12 +810,6 @@ export default class PluginWorkflowServer extends Plugin {
}, },
transaction, transaction,
}); });
} else {
await repository.updateOrCreate({
filterKeys: ['key', 'type'],
values: task,
transaction,
});
} }
// NOTE: // NOTE: