feat: approval, resubmit approval (#1270)
Reviewed-on: daoyoucloud/tachybase#1270 Reviewed-by: sealday <zhanglin@daoyoucloud.com> Co-authored-by: bai.zixv <bai.zixv@foxmail.com> Co-committed-by: bai.zixv <bai.zixv@foxmail.com>
This commit is contained in:
parent
81a622e77d
commit
86cc441465
@ -6,7 +6,7 @@ import { uid } from '@tachybase/utils/client';
|
|||||||
import { Spin } from 'antd';
|
import { Spin } from 'antd';
|
||||||
|
|
||||||
import { DetailsBlockProvider, SimpleDesigner } from '../../../../../components';
|
import { DetailsBlockProvider, SimpleDesigner } from '../../../../../components';
|
||||||
import { FormBlockProvider } from '../../../common/Pd.FormBlock';
|
import { FormBlockProvider } from '../../../common/FormBlock.provider';
|
||||||
import { flatSchemaArray } from '../../../constants';
|
import { flatSchemaArray } from '../../../constants';
|
||||||
import { ContextApproverConfig } from '../ApproverConfig.context';
|
import { ContextApproverConfig } from '../ApproverConfig.context';
|
||||||
import { ActionBarProvider } from './Pd.ActionBarProvider';
|
import { ActionBarProvider } from './Pd.ActionBarProvider';
|
||||||
|
@ -11,7 +11,7 @@ import { useForm } from '@tachybase/schema';
|
|||||||
import { uid } from '@tachybase/utils/client';
|
import { uid } from '@tachybase/utils/client';
|
||||||
|
|
||||||
import { useFlowContext } from '../../../../../FlowContext';
|
import { useFlowContext } from '../../../../../FlowContext';
|
||||||
import { CollectionApprovalTodos } from '../../../common/Cn.ApprovalTodos';
|
import { CollectionApprovalTodos } from '../../../common/ApprovalTodos.collection';
|
||||||
import { NAMESPACE } from '../../../locale';
|
import { NAMESPACE } from '../../../locale';
|
||||||
import { SchemaAddBlock } from '../../trigger/launcher-interface/SchemaAddBlock.component';
|
import { SchemaAddBlock } from '../../trigger/launcher-interface/SchemaAddBlock.component';
|
||||||
import { ApproverBlock } from './ApproverBlock.view';
|
import { ApproverBlock } from './ApproverBlock.view';
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
export const ApprovalActionProvider = (props) => {
|
export const ApprovalActionProvider = (props) => {
|
||||||
console.log('%c Line:2 🍰 props', 'font-size:18px;color:#4fff4B;background:#7f2b82', props);
|
|
||||||
return props.children;
|
return props.children;
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ActionInitializer, useSchemaInitializerItem } from '@tachybase/client';
|
import { ActionInitializer, useSchemaInitializerItem } from '@tachybase/client';
|
||||||
|
|
||||||
|
import { NAMESPACE } from '../../../locale';
|
||||||
|
|
||||||
// 区块-配置操作
|
// 区块-配置操作
|
||||||
export const LauncherActionConfigComponent = () => {
|
export const LauncherActionConfigComponent = () => {
|
||||||
const itemConfig = useSchemaInitializerItem();
|
const itemConfig = useSchemaInitializerItem();
|
||||||
@ -29,3 +31,29 @@ export const LauncherActionConfigComponent = () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// setup-action-resubmit
|
||||||
|
export const LauncherActionConfigReSubmit = () => {
|
||||||
|
const itemConfig = useSchemaInitializerItem();
|
||||||
|
const { action, actionProps = {}, ...restItemConfig } = itemConfig;
|
||||||
|
return (
|
||||||
|
<ActionInitializer
|
||||||
|
{...restItemConfig}
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
title: restItemConfig.title,
|
||||||
|
'x-decorator': 'ProviderActionResubmit',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
confirm: {
|
||||||
|
title: `{{t('resubmit', { ns: "${NAMESPACE}" })}}`,
|
||||||
|
content: `{{t('Are you sure you want to resubmit it?', { ns: "${NAMESPACE}" })}}`,
|
||||||
|
},
|
||||||
|
useAction: '{{ useActionResubmit }}',
|
||||||
|
},
|
||||||
|
'x-designer': 'Action.Designer',
|
||||||
|
'x-action': `reSubmit`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -2,11 +2,11 @@ import { SchemaInitializer } from '@tachybase/client';
|
|||||||
|
|
||||||
import { APPROVAL_STATUS } from '../../../constants';
|
import { APPROVAL_STATUS } from '../../../constants';
|
||||||
import { NAMESPACE } from '../../../locale';
|
import { NAMESPACE } from '../../../locale';
|
||||||
import { LauncherActionConfigComponent } from './LauncherActionConfig.component';
|
import { LauncherActionConfigComponent, LauncherActionConfigReSubmit } from './LauncherActionConfig.component';
|
||||||
|
|
||||||
// 区块-配置操作
|
// 区块-配置操作
|
||||||
export const LauncherActionConfigInitializer = new SchemaInitializer({
|
export const LauncherActionConfigInitializer = new SchemaInitializer({
|
||||||
name: 'ApprovalApplyAddActionButton',
|
name: 'LauncherActionConfigInitializer',
|
||||||
title: '{{t("Configure actions")}}',
|
title: '{{t("Configure actions")}}',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
@ -25,5 +25,12 @@ export const LauncherActionConfigInitializer = new SchemaInitializer({
|
|||||||
Component: LauncherActionConfigComponent,
|
Component: LauncherActionConfigComponent,
|
||||||
action: APPROVAL_STATUS.DRAFT,
|
action: APPROVAL_STATUS.DRAFT,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'reSubmit',
|
||||||
|
type: 'item',
|
||||||
|
title: `{{t("resubmit", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
action: APPROVAL_STATUS.RESUBMIT,
|
||||||
|
Component: LauncherActionConfigReSubmit,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -27,15 +27,21 @@ export const LauncherAddBlockButtonComponent = () => {
|
|||||||
['x-acl-action']: deleteB,
|
['x-acl-action']: deleteB,
|
||||||
...formSchema
|
...formSchema
|
||||||
} = createFormBlockSchema({
|
} = createFormBlockSchema({
|
||||||
actionInitializers: 'ApprovalApplyAddActionButton',
|
actionInitializers: 'LauncherActionConfigInitializer',
|
||||||
actions: {
|
actions: {
|
||||||
submit: {
|
submit: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: '{{t("Submit")}}',
|
title: '{{t("Submit")}}',
|
||||||
'x-decorator': 'ApplyActionStatusProvider',
|
'x-decorator': 'ApplyActionStatusProvider',
|
||||||
'x-decorator-props': { value: APPROVAL_STATUS.SUBMITTED },
|
'x-decorator-props': {
|
||||||
|
value: APPROVAL_STATUS.SUBMITTED,
|
||||||
|
},
|
||||||
'x-component': 'Action',
|
'x-component': 'Action',
|
||||||
'x-component-props': { type: 'primary', htmlType: 'submit', useAction: '{{ useSubmit }}' },
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
htmlType: 'submit',
|
||||||
|
useAction: '{{ useSubmit }}',
|
||||||
|
},
|
||||||
'x-designer': 'Action.Designer',
|
'x-designer': 'Action.Designer',
|
||||||
'x-designer-props': {},
|
'x-designer-props': {},
|
||||||
'x-action': `${APPROVAL_STATUS.SUBMITTED}`,
|
'x-action': `${APPROVAL_STATUS.SUBMITTED}`,
|
||||||
|
@ -53,11 +53,13 @@ export const SchemaAddBlock = ({ value, onChange }) => {
|
|||||||
ActionBarProvider,
|
ActionBarProvider,
|
||||||
ApplyActionStatusProvider,
|
ApplyActionStatusProvider,
|
||||||
WithdrawActionProvider,
|
WithdrawActionProvider,
|
||||||
|
ProviderActionResubmit,
|
||||||
}}
|
}}
|
||||||
scope={{
|
scope={{
|
||||||
useSubmit,
|
useSubmit,
|
||||||
useWithdrawAction,
|
useWithdrawAction,
|
||||||
useFormBlockProps,
|
useFormBlockProps,
|
||||||
|
useActionResubmit,
|
||||||
}}
|
}}
|
||||||
// @ts-ignore ugly
|
// @ts-ignore ugly
|
||||||
schema={data}
|
schema={data}
|
||||||
@ -80,6 +82,13 @@ function ApplyActionStatusProvider(props) {
|
|||||||
function WithdrawActionProvider(props) {
|
function WithdrawActionProvider(props) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useActionResubmit() {
|
||||||
|
return { run() {} };
|
||||||
|
}
|
||||||
|
function ProviderActionResubmit(props) {
|
||||||
|
return props.children;
|
||||||
|
}
|
||||||
// TODO:
|
// TODO:
|
||||||
const E = (f, o, p) =>
|
const E = (f, o, p) =>
|
||||||
// eslint-disable-next-line promise/param-names
|
// eslint-disable-next-line promise/param-names
|
||||||
|
@ -8,6 +8,7 @@ export const APPROVAL_STATUS = {
|
|||||||
SUBMITTED: 2,
|
SUBMITTED: 2,
|
||||||
PROCESSING: 3,
|
PROCESSING: 3,
|
||||||
APPROVED: 4,
|
APPROVED: 4,
|
||||||
|
RESUBMIT: 5,
|
||||||
REJECTED: -1,
|
REJECTED: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -37,17 +38,43 @@ export const approvalStatusConfigObj = approvalStatusOptions.reduce(
|
|||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
export const ApprovalStatusEnums = [
|
export const ApprovalStatusEnums = [
|
||||||
{ value: APPROVAL_STATUS.DRAFT, label: `{{t("Draft", { ns: "${NAMESPACE}" })}}`, editable: true },
|
{
|
||||||
|
value: APPROVAL_STATUS.DRAFT,
|
||||||
|
label: `{{t("Draft", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
editable: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: APPROVAL_STATUS.RETURNED,
|
value: APPROVAL_STATUS.RETURNED,
|
||||||
label: `{{t("Returned", { ns: "${NAMESPACE}" })}}`,
|
label: `{{t("Returned", { ns: "${NAMESPACE}" })}}`,
|
||||||
color: 'purple',
|
color: 'purple',
|
||||||
editable: true,
|
editable: true,
|
||||||
},
|
},
|
||||||
{ value: APPROVAL_STATUS.SUBMITTED, label: `{{t("Submitted", { ns: "${NAMESPACE}" })}}`, color: 'cyan' },
|
{
|
||||||
{ value: APPROVAL_STATUS.PROCESSING, label: `{{t("Processing", { ns: "${NAMESPACE}" })}}`, color: 'gold' },
|
value: APPROVAL_STATUS.SUBMITTED,
|
||||||
{ value: APPROVAL_STATUS.APPROVED, label: `{{t("Approved", { ns: "${NAMESPACE}" })}}`, color: 'green' },
|
label: `{{t("Submitted", { ns: "${NAMESPACE}" })}}`,
|
||||||
{ value: APPROVAL_STATUS.REJECTED, label: `{{t("Rejected", { ns: "${NAMESPACE}" })}}`, color: 'red' },
|
color: 'cyan',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: APPROVAL_STATUS.PROCESSING,
|
||||||
|
label: `{{t("Processing", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
color: 'gold',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: APPROVAL_STATUS.APPROVED,
|
||||||
|
label: `{{t("Approved", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
color: 'green',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: APPROVAL_STATUS.RESUBMIT,
|
||||||
|
label: `{{t("reSubmit", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
color: 'blue',
|
||||||
|
editable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: APPROVAL_STATUS.REJECTED,
|
||||||
|
label: `{{t("Rejected", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
color: 'red',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
export const ApprovalStatusEnumDict = ApprovalStatusEnums.reduce((e, t) => Object.assign(e, { [t.value]: t }), {});
|
export const ApprovalStatusEnumDict = ApprovalStatusEnums.reduce((e, t) => Object.assign(e, { [t.value]: t }), {});
|
||||||
export const JobStatusEnums = {
|
export const JobStatusEnums = {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ExtendCollectionsProvider, TableBlockProvider, useRecord } from '@tachybase/client';
|
import { ExtendCollectionsProvider, TableBlockProvider, useRecord } from '@tachybase/client';
|
||||||
|
|
||||||
import { CollectionApprovalTodos } from '../../common/Cn.ApprovalTodos';
|
import { CollectionApprovalTodos } from '../../common/ApprovalTodos.collection';
|
||||||
import { CollectionApprovals } from '../approval-common/Approvals.collection';
|
import { CollectionApprovals } from '../approval-common/Approvals.collection';
|
||||||
import { CollectionFlowNodes } from '../approval-common/FlowNodes.collection';
|
import { CollectionFlowNodes } from '../approval-common/FlowNodes.collection';
|
||||||
import { CollectionWorkflows } from '../approval-common/Workflows.collection';
|
import { CollectionWorkflows } from '../approval-common/Workflows.collection';
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
import { useCurrentUserContext } from '@tachybase/client';
|
||||||
|
|
||||||
|
import { useFlowContext } from '../../../../../FlowContext';
|
||||||
|
import { APPROVAL_STATUS } from '../../../constants';
|
||||||
|
import { useApproval } from '../../approval-common/ApprovalData.provider';
|
||||||
|
|
||||||
|
export function ProviderActionResubmit(props) {
|
||||||
|
const { data } = useCurrentUserContext();
|
||||||
|
const { status, createdById } = useApproval();
|
||||||
|
const { workflow } = useFlowContext();
|
||||||
|
|
||||||
|
const isSameId = data.data.id === createdById;
|
||||||
|
// const isEnabledWithdraw = workflow.enabled && workflow.config.withdrawable;
|
||||||
|
// const isNotDraft = status !== APPROVAL_STATUS.DRAFT;
|
||||||
|
|
||||||
|
if (isSameId) {
|
||||||
|
return props.children;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
@ -11,7 +11,12 @@ export function ActionBarProvider(props) {
|
|||||||
|
|
||||||
const isSameId = data.data.id === createdById;
|
const isSameId = data.data.id === createdById;
|
||||||
const isSameExcutionId = latestExecutionId === approvalExecution.id;
|
const isSameExcutionId = latestExecutionId === approvalExecution.id;
|
||||||
const isExcutionDid = [APPROVAL_STATUS.DRAFT, APPROVAL_STATUS.RETURNED, APPROVAL_STATUS.SUBMITTED].includes(status);
|
const isExcutionDid = [
|
||||||
|
APPROVAL_STATUS.DRAFT,
|
||||||
|
APPROVAL_STATUS.RETURNED,
|
||||||
|
APPROVAL_STATUS.SUBMITTED,
|
||||||
|
APPROVAL_STATUS.RESUBMIT,
|
||||||
|
].includes(status);
|
||||||
|
|
||||||
if (!isSameId || !isSameExcutionId || !isExcutionDid) {
|
if (!isSameId || !isSameExcutionId || !isExcutionDid) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -18,7 +18,7 @@ export function ApplyActionStatusProvider(props) {
|
|||||||
const { data } = useCurrentUserContext();
|
const { data } = useCurrentUserContext();
|
||||||
const isSameId = data.data.id === createdById;
|
const isSameId = data.data.id === createdById;
|
||||||
const isEnbled = workflow.enabled;
|
const isEnbled = workflow.enabled;
|
||||||
const isStatusDid = [APPROVAL_STATUS.DRAFT, APPROVAL_STATUS.RETURNED].includes(status);
|
const isStatusDid = [APPROVAL_STATUS.RESUBMIT, APPROVAL_STATUS.DRAFT, APPROVAL_STATUS.RETURNED].includes(status);
|
||||||
|
|
||||||
if (isSameId && isEnbled && isStatusDid) {
|
if (isSameId && isEnbled && isStatusDid) {
|
||||||
return <ContextApprovalStatus.Provider value={value}>{children}</ContextApprovalStatus.Provider>;
|
return <ContextApprovalStatus.Provider value={value}>{children}</ContextApprovalStatus.Provider>;
|
||||||
|
@ -12,13 +12,16 @@ import { useForm } from '@tachybase/schema';
|
|||||||
import { Result, Spin } from 'antd';
|
import { Result, Spin } from 'antd';
|
||||||
|
|
||||||
import { DetailsBlockProvider } from '../../../../../components';
|
import { DetailsBlockProvider } from '../../../../../components';
|
||||||
import { FormBlockProvider } from '../../../common/Pd.FormBlock';
|
import { FormBlockProvider } from '../../../common/FormBlock.provider';
|
||||||
|
import { APPROVAL_STATUS } from '../../../constants';
|
||||||
import { NAMESPACE } from '../../../locale';
|
import { NAMESPACE } from '../../../locale';
|
||||||
import { ApprovalContext } from '../../approval-common/ApprovalData.provider';
|
import { ApprovalContext } from '../../approval-common/ApprovalData.provider';
|
||||||
import { ContextWithActionEnabled } from '../../approval-common/WithActionEnabled.provider';
|
import { ContextWithActionEnabled } from '../../approval-common/WithActionEnabled.provider';
|
||||||
import { ContextApprovalExecution } from '../common/ApprovalExecution.provider';
|
import { ContextApprovalExecution } from '../common/ApprovalExecution.provider';
|
||||||
import { FlowContextProvider } from '../common/FlowContext.provider';
|
import { FlowContextProvider } from '../common/FlowContext.provider';
|
||||||
import { SchemaComponentContextProvider } from '../common/SchemaComponent.provider';
|
import { SchemaComponentContextProvider } from '../common/SchemaComponent.provider';
|
||||||
|
import { ProviderActionResubmit } from './ActionResubmit.provider';
|
||||||
|
import { useActionResubmit } from './hooks/useActionResubmit';
|
||||||
import { useDestroyAction } from './hooks/useDestroyAction';
|
import { useDestroyAction } from './hooks/useDestroyAction';
|
||||||
import { useFormBlockProps } from './hooks/useFormBlockProps';
|
import { useFormBlockProps } from './hooks/useFormBlockProps';
|
||||||
import { useSubmit } from './hooks/useSubmit';
|
import { useSubmit } from './hooks/useSubmit';
|
||||||
@ -59,6 +62,8 @@ export const ViewActionLaunchContent = () => {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const approvalData = data?.data;
|
const approvalData = data?.data;
|
||||||
|
|
||||||
|
const needHideProcess = actionEnabled || approvalData?.status !== APPROVAL_STATUS.RESUBMIT;
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Spin />;
|
return <Spin />;
|
||||||
}
|
}
|
||||||
@ -90,6 +95,7 @@ export const ViewActionLaunchContent = () => {
|
|||||||
ApplyActionStatusProvider,
|
ApplyActionStatusProvider,
|
||||||
WithdrawActionProvider,
|
WithdrawActionProvider,
|
||||||
DetailsBlockProvider,
|
DetailsBlockProvider,
|
||||||
|
ProviderActionResubmit,
|
||||||
}}
|
}}
|
||||||
scope={{
|
scope={{
|
||||||
useForm,
|
useForm,
|
||||||
@ -98,6 +104,7 @@ export const ViewActionLaunchContent = () => {
|
|||||||
useDetailsBlockProps: useFormBlockContext,
|
useDetailsBlockProps: useFormBlockContext,
|
||||||
useWithdrawAction,
|
useWithdrawAction,
|
||||||
useDestroyAction,
|
useDestroyAction,
|
||||||
|
useActionResubmit,
|
||||||
}}
|
}}
|
||||||
schema={{
|
schema={{
|
||||||
name: `view-${approval == null ? void 0 : approval.id}`,
|
name: `view-${approval == null ? void 0 : approval.id}`,
|
||||||
@ -116,7 +123,9 @@ export const ViewActionLaunchContent = () => {
|
|||||||
detail: {
|
detail: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-decorator': 'SchemaComponentContextProvider',
|
'x-decorator': 'SchemaComponentContextProvider',
|
||||||
'x-decorator-props': { designable: false },
|
'x-decorator-props': {
|
||||||
|
designable: true,
|
||||||
|
},
|
||||||
'x-component': 'RemoteSchemaComponent',
|
'x-component': 'RemoteSchemaComponent',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
uid: workflow?.config.applyForm,
|
uid: workflow?.config.applyForm,
|
||||||
@ -126,7 +135,7 @@ export const ViewActionLaunchContent = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
actionEnabled
|
needHideProcess
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
process: {
|
process: {
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
import { useAPIClient } from '@tachybase/client';
|
||||||
|
import { useFlowContext } from '@tachybase/plugin-workflow/client';
|
||||||
|
import { useField } from '@tachybase/schema';
|
||||||
|
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
import { APPROVAL_STATUS } from '../../../../constants';
|
||||||
|
import { useApproval } from '../../../approval-common/ApprovalData.provider';
|
||||||
|
import { useHandleRefresh } from '../../common/useHandleRefresh';
|
||||||
|
|
||||||
|
// 重新发起
|
||||||
|
export function useActionResubmit() {
|
||||||
|
const { refreshTable } = useHandleRefresh();
|
||||||
|
|
||||||
|
const field = useField();
|
||||||
|
const approval = useApproval();
|
||||||
|
const api = useAPIClient();
|
||||||
|
|
||||||
|
const { workflow } = useFlowContext();
|
||||||
|
|
||||||
|
return {
|
||||||
|
async run() {
|
||||||
|
try {
|
||||||
|
field.data = field.data ?? {};
|
||||||
|
|
||||||
|
field.data.loading = true;
|
||||||
|
|
||||||
|
const appendsConfigs = [...workflow.config.appends];
|
||||||
|
const summaryConfigs = [...workflow.config.summary];
|
||||||
|
const appends = _.intersection(appendsConfigs, summaryConfigs);
|
||||||
|
|
||||||
|
const { data: approvalData } = await api.resource('approvals').get({
|
||||||
|
filterByTk: approval.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { collectionName, data, workflowId } = approvalData.data;
|
||||||
|
|
||||||
|
const newData = _.omit(data, ['id', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy']);
|
||||||
|
|
||||||
|
await api.resource('approvals').reSubmit({
|
||||||
|
values: {
|
||||||
|
collectionName: collectionName,
|
||||||
|
data: newData,
|
||||||
|
status: APPROVAL_STATUS.RESUBMIT,
|
||||||
|
workflowId: workflowId,
|
||||||
|
collectionAppends: appends,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
field.data.loading = false;
|
||||||
|
refreshTable();
|
||||||
|
} catch (v) {
|
||||||
|
if (field.data) {
|
||||||
|
field.data.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -13,7 +13,7 @@ import _ from 'lodash';
|
|||||||
|
|
||||||
import { DetailsBlockProvider } from '../../../../../components';
|
import { DetailsBlockProvider } from '../../../../../components';
|
||||||
import { ExecutionContextProvider } from '../../../../../ExecutionContextProvider';
|
import { ExecutionContextProvider } from '../../../../../ExecutionContextProvider';
|
||||||
import { FormBlockProvider } from '../../../common/Pd.FormBlock';
|
import { FormBlockProvider } from '../../../common/FormBlock.provider';
|
||||||
import { NAMESPACE, useTranslation } from '../../../locale';
|
import { NAMESPACE, useTranslation } from '../../../locale';
|
||||||
import { ApprovalContext } from '../../approval-common/ApprovalData.provider';
|
import { ApprovalContext } from '../../approval-common/ApprovalData.provider';
|
||||||
import { ContextWithActionEnabled } from '../../approval-common/WithActionEnabled.provider';
|
import { ContextWithActionEnabled } from '../../approval-common/WithActionEnabled.provider';
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useCollectionManager, useCollectionRecordData, useCompile } from '@tachybase/client';
|
import { useCollectionManager, useCollectionRecordData, useCompile } from '@tachybase/client';
|
||||||
|
import { dayjs } from '@tachybase/utils/client';
|
||||||
|
|
||||||
import useStyles from './style';
|
import useStyles from './style';
|
||||||
|
|
||||||
@ -13,12 +14,19 @@ export const ApprovalsSummary = (props) => {
|
|||||||
const { collectionName } = record;
|
const { collectionName } = record;
|
||||||
const results = Object.entries(value).map(([key, objValue]) => {
|
const results = Object.entries(value).map(([key, objValue]) => {
|
||||||
const field = cm.getCollectionField(`${collectionName}.${key}`);
|
const field = cm.getCollectionField(`${collectionName}.${key}`);
|
||||||
|
const realValue = Object.prototype.toString.call(objValue) === '[object Object]' ? objValue?.['name'] : objValue;
|
||||||
|
// 如果是UTC时间字符串, 则转换为本地时区时间
|
||||||
|
if (isUTCString(realValue)) {
|
||||||
|
return {
|
||||||
|
label: compile(field?.uiSchema?.title || key),
|
||||||
|
value: convertUTCToLocal(realValue),
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
label: compile(field?.uiSchema?.title || key),
|
label: compile(field?.uiSchema?.title || key),
|
||||||
value: Object.prototype.toString.call(objValue) === '[object Object]' ? objValue?.['name'] : objValue,
|
value: realValue,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// 展示结果要展示一个数组对象, 是 label 和 value 的形式
|
// 展示结果要展示一个数组对象, 是 label 和 value 的形式
|
||||||
// label 放中文, value 放值
|
// label 放中文, value 放值
|
||||||
return (
|
return (
|
||||||
@ -32,3 +40,19 @@ export const ApprovalsSummary = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: 将下边的函数转移至 core 库中
|
||||||
|
// 定义正则表达式, 检测形如 2024-07-04T04:46:27.166Z 的UTC时间字符串
|
||||||
|
const utcRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
||||||
|
|
||||||
|
// 测试函数
|
||||||
|
function isUTCString(str = '') {
|
||||||
|
return utcRegex.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将UTC时间字符串转换为本地时区时间
|
||||||
|
function convertUTCToLocal(utcString) {
|
||||||
|
// 使用dayjs解析UTC时间,并转换为本地时区时间
|
||||||
|
const localDate = dayjs.utc(utcString).local().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
return localDate;
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
useRecord,
|
useRecord,
|
||||||
} from '@tachybase/client';
|
} from '@tachybase/client';
|
||||||
|
|
||||||
import { CollectionApprovalTodos } from '../../common/Cn.ApprovalTodos';
|
import { CollectionApprovalTodos } from '../../common/ApprovalTodos.collection';
|
||||||
import { CollectionApprovals } from '../approval-common/Approvals.collection';
|
import { CollectionApprovals } from '../approval-common/Approvals.collection';
|
||||||
import { CollectionFlowNodes } from '../approval-common/FlowNodes.collection';
|
import { CollectionFlowNodes } from '../approval-common/FlowNodes.collection';
|
||||||
import { CollectionWorkflows } from '../approval-common/Workflows.collection';
|
import { CollectionWorkflows } from '../approval-common/Workflows.collection';
|
||||||
|
@ -275,5 +275,6 @@
|
|||||||
"Workflow": "Workflow",
|
"Workflow": "Workflow",
|
||||||
"\"Content-Type\" only support \"application/json\", and no need to specify": "\"Content-Type\" only support \"application/json\", and no need to specify",
|
"\"Content-Type\" only support \"application/json\", and no need to specify": "\"Content-Type\" only support \"application/json\", and no need to specify",
|
||||||
"concat": "concat",
|
"concat": "concat",
|
||||||
"ms": "ms"
|
"ms": "ms",
|
||||||
|
"reSubmit": "reSubmit"
|
||||||
}
|
}
|
||||||
|
@ -355,5 +355,6 @@
|
|||||||
"Workflow": "工作流",
|
"Workflow": "工作流",
|
||||||
"\"Content-Type\" only support \"application/json\", and no need to specify": "\"Content-Type\" 请求头仅支持 \"application/json\",无需填写",
|
"\"Content-Type\" only support \"application/json\", and no need to specify": "\"Content-Type\" 请求头仅支持 \"application/json\",无需填写",
|
||||||
"concat": "连接",
|
"concat": "连接",
|
||||||
"ms": "毫秒"
|
"ms": "毫秒",
|
||||||
|
"reSubmit": "重新提交"
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,9 @@ export default class ApprovalTrigger extends Trigger {
|
|||||||
},
|
},
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
if (!workflow || !approval.changed('status') || approval.status !== APPROVAL_STATUS.SUBMITTED) {
|
const isChangedStatus = approval.changed('status');
|
||||||
|
const isAllowStatusList = [APPROVAL_STATUS.SUBMITTED, APPROVAL_STATUS.RESUBMIT].includes(approval.status);
|
||||||
|
if (!workflow || !isChangedStatus || !isAllowStatusList) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const [dataSourceName, collectionName] = parseCollectionName(approval.collectionName);
|
const [dataSourceName, collectionName] = parseCollectionName(approval.collectionName);
|
||||||
|
@ -2,6 +2,7 @@ import actions, { utils } from '@tachybase/actions';
|
|||||||
import { parseCollectionName } from '@tachybase/data-source-manager';
|
import { parseCollectionName } from '@tachybase/data-source-manager';
|
||||||
|
|
||||||
import { EXECUTION_STATUS, JOB_STATUS, PluginWorkflow } from '../..';
|
import { EXECUTION_STATUS, JOB_STATUS, PluginWorkflow } from '../..';
|
||||||
|
import { appends } from '../../../client/schemas/collection';
|
||||||
import { APPROVAL_ACTION_STATUS, APPROVAL_STATUS } from './constants';
|
import { APPROVAL_ACTION_STATUS, APPROVAL_STATUS } from './constants';
|
||||||
import { getSummary } from './tools';
|
import { getSummary } from './tools';
|
||||||
import { getAssociationName, jsonParse } from './utils';
|
import { getAssociationName, jsonParse } from './utils';
|
||||||
@ -84,7 +85,7 @@ const approvals = {
|
|||||||
/** 以下为,处理子表单子表格等关联字段的更新逻辑 */
|
/** 以下为,处理子表单子表格等关联字段的更新逻辑 */
|
||||||
const schemaOfForm = await context.db.getRepository('uiSchemas').getJsonSchema(schemaFormId);
|
const schemaOfForm = await context.db.getRepository('uiSchemas').getJsonSchema(schemaFormId);
|
||||||
const itemsOfSchema = await jsonParse(`**["x-component-props".mode]`, schemaOfForm);
|
const itemsOfSchema = await jsonParse(`**["x-component-props".mode]`, schemaOfForm);
|
||||||
const fieldAssociationName = itemsOfSchema
|
const fieldAssociationName = (itemsOfSchema || [])
|
||||||
.filter((item) => {
|
.filter((item) => {
|
||||||
return ['Nester', 'SubTable', 'PopoverNester'].includes(item?.['x-component-props']?.mode);
|
return ['Nester', 'SubTable', 'PopoverNester'].includes(item?.['x-component-props']?.mode);
|
||||||
})
|
})
|
||||||
@ -113,6 +114,61 @@ const approvals = {
|
|||||||
});
|
});
|
||||||
return actions.update(context, next);
|
return actions.update(context, next);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// NOTE: 和 create 逻辑雷同, 但是 因为原本的 create 并非纯操作, 因此拷贝一份以便方便改动
|
||||||
|
async reSubmit(context, next) {
|
||||||
|
const { status, collectionName, data, workflowId, collectionAppends } = context.action.params.values ?? {};
|
||||||
|
const [dataSourceName, cName] = parseCollectionName(collectionName);
|
||||||
|
const dataSource = context.app.dataSourceManager.dataSources.get(dataSourceName);
|
||||||
|
if (!dataSource) {
|
||||||
|
return context.throw(400, `Data source "${dataSourceName}" not found`);
|
||||||
|
}
|
||||||
|
const collection = dataSource.collectionManager.getCollection(cName);
|
||||||
|
if (!collection) {
|
||||||
|
return context.throw(400, `Collection "${cName}" not found`);
|
||||||
|
}
|
||||||
|
const workflow = await context.db.getRepository('workflows').findOne({
|
||||||
|
filterByTk: workflowId,
|
||||||
|
});
|
||||||
|
if (!workflow?.enabled) {
|
||||||
|
return context.throw(400, 'Current workflow not found or disabled, please refresh and try again');
|
||||||
|
}
|
||||||
|
const { repository, model } = collection;
|
||||||
|
const values = await repository.create({
|
||||||
|
values: {
|
||||||
|
...data,
|
||||||
|
createdBy: context.state.currentUser.id,
|
||||||
|
updatedBy: context.state.currentUser.id,
|
||||||
|
},
|
||||||
|
context,
|
||||||
|
});
|
||||||
|
const instance = values.get();
|
||||||
|
|
||||||
|
// NOTE: 这里需要取出关联字段, 用于摘要的构造;
|
||||||
|
// 有点奇怪的方式, 但是没想到更好的处理方式, 也许应该改造摘要的构造方式, 存储关联字段的id, 在前端请求具体数据.
|
||||||
|
const valuesWithAppends = await repository.findOne({
|
||||||
|
filterByTk: values.id,
|
||||||
|
appends: [...collectionAppends],
|
||||||
|
});
|
||||||
|
const summary = getSummary({
|
||||||
|
summaryConfig: workflow.config.summary,
|
||||||
|
data: valuesWithAppends,
|
||||||
|
});
|
||||||
|
Object.keys(model.associations).forEach((key) => {
|
||||||
|
delete instance[key];
|
||||||
|
});
|
||||||
|
context.action.mergeParams({
|
||||||
|
values: {
|
||||||
|
collectionName,
|
||||||
|
data: instance,
|
||||||
|
dataKey: values[collection.filterTargetKey],
|
||||||
|
workflowKey: workflow.key,
|
||||||
|
applicantRoleName: context.state.currentRole,
|
||||||
|
summary,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return actions.create(context, next);
|
||||||
|
},
|
||||||
async destroy(context, next) {
|
async destroy(context, next) {
|
||||||
const {
|
const {
|
||||||
filterByTk,
|
filterByTk,
|
||||||
|
@ -4,6 +4,7 @@ export const APPROVAL_STATUS = {
|
|||||||
SUBMITTED: 2,
|
SUBMITTED: 2,
|
||||||
PROCESSING: 3,
|
PROCESSING: 3,
|
||||||
APPROVED: 4,
|
APPROVED: 4,
|
||||||
|
RESUBMIT: 5,
|
||||||
REJECTED: -1,
|
REJECTED: -1,
|
||||||
};
|
};
|
||||||
export const APPROVAL_ACTION_STATUS = {
|
export const APPROVAL_ACTION_STATUS = {
|
||||||
|
@ -10,9 +10,10 @@ export function getSummary(params: ParamsType): object {
|
|||||||
|
|
||||||
const result = summaryConfig.reduce((summary, key) => {
|
const result = summaryConfig.reduce((summary, key) => {
|
||||||
const value = _.get(data, key);
|
const value = _.get(data, key);
|
||||||
|
const realValue = Object.prototype.toString.call(value) === '[object Object]' ? value?.['name'] : value;
|
||||||
return {
|
return {
|
||||||
...summary,
|
...summary,
|
||||||
[key]: value,
|
[key]: realValue,
|
||||||
};
|
};
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user