fix: approval, show process & feat: approval, lastNode (#1343)
Reviewed-on: daoyoucloud/tachybase#1343 Co-authored-by: bai.zixv <bai.zixv@foxmail.com> Co-committed-by: bai.zixv <bai.zixv@foxmail.com>
This commit is contained in:
parent
73fdc790f4
commit
220bf172fc
@ -60,7 +60,10 @@ export const CollectionApprovalTodos = {
|
||||
type: 'number',
|
||||
title: `{{t("Task node", { ns: "${NAMESPACE}" })}}`,
|
||||
'x-component': 'RemoteSelect',
|
||||
'x-component-props': { fieldNames: { label: 'title', value: 'id' }, service: { resource: 'flow_nodes' } },
|
||||
'x-component-props': {
|
||||
fieldNames: { label: 'title', value: 'id' },
|
||||
service: { resource: 'flow_nodes' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -45,7 +45,14 @@ const schemaItems = [
|
||||
'x-component': 'ApprovalBlock.Launch',
|
||||
collection: 'approvals',
|
||||
params: {
|
||||
appends: ['createdBy.nickname', 'workflow.title', 'workflow.enabled'],
|
||||
appends: [
|
||||
'createdBy.nickname',
|
||||
'workflow.title',
|
||||
'workflow.enabled',
|
||||
'records.id',
|
||||
'records.status',
|
||||
'records.node.title',
|
||||
],
|
||||
except: ['data'],
|
||||
},
|
||||
},
|
||||
|
@ -156,6 +156,21 @@ export const SchemaApprovalBlockLaunch = {
|
||||
},
|
||||
},
|
||||
},
|
||||
lastNode: {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableV2.Column.Decorator',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-component-props': {
|
||||
width: 200,
|
||||
},
|
||||
title: `{{t("Task node", { ns: "${NAMESPACE}" })}}`,
|
||||
properties: {
|
||||
lastNode: {
|
||||
'x-component': 'ApprovalLastNodeColumn',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -63,7 +63,7 @@ export const ViewActionLaunchContent = () => {
|
||||
// @ts-ignore
|
||||
const approvalData = data?.data;
|
||||
|
||||
const needHideProcess = actionEnabled || approvalData?.status !== APPROVAL_STATUS.RESUBMIT;
|
||||
const needHideProcess = actionEnabled;
|
||||
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
|
@ -4,13 +4,27 @@ import { createStyles, useCurrentUserContext } from '@tachybase/client';
|
||||
import { Space, Table } from 'antd';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { EXECUTION_STATUS } from '../../../../constants';
|
||||
import { APPROVAL_ACTION_STATUS, APPROVAL_STATUS } from '../../constants';
|
||||
import { lang, usePluginTranslation } from '../../locale';
|
||||
import { usePluginTranslation } from '../../locale';
|
||||
import { useApproval } from './ApprovalData.provider';
|
||||
import { getAntdTableColumns } from './process-columns/columns';
|
||||
import { getAntdTableColumns } from './process-columns';
|
||||
import { getResults } from './tools';
|
||||
import { ContextWithActionEnabled } from './WithActionEnabled.provider';
|
||||
|
||||
const getStyles = createStyles(({ css, token }) => ({
|
||||
layout: css`
|
||||
display: flex;
|
||||
`,
|
||||
columnDetail: css`
|
||||
.ant-description-textarea {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
time {
|
||||
display: block;
|
||||
color: ${token.colorTextTertiary};
|
||||
}
|
||||
`,
|
||||
}));
|
||||
|
||||
// 审批(发起/待办)区块-查看-审批处理
|
||||
export const ApprovalProcess = (props) => {
|
||||
const { t } = usePluginTranslation();
|
||||
@ -32,88 +46,3 @@ export const ApprovalProcess = (props) => {
|
||||
</ContextWithActionEnabled.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = createStyles(({ css, token }) => ({
|
||||
layout: css`
|
||||
display: flex;
|
||||
`,
|
||||
columnDetail: css`
|
||||
.ant-description-textarea {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
time {
|
||||
display: block;
|
||||
color: ${token.colorTextTertiary};
|
||||
}
|
||||
`,
|
||||
}));
|
||||
|
||||
function getResults({ approval, currentUser }) {
|
||||
const { workflow, approvalExecutions, records } = approval;
|
||||
|
||||
approvalExecutions.sort((a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt));
|
||||
|
||||
const approvalExecution = approvalExecutions.reduce(
|
||||
(newObj, curr) =>
|
||||
Object.assign(newObj, {
|
||||
[curr.id]: Object.assign(curr, {
|
||||
records: [
|
||||
{
|
||||
groupCount: 1,
|
||||
node: {
|
||||
title: lang('Apply'),
|
||||
},
|
||||
user: {
|
||||
...approval.createdBy,
|
||||
id: approval.createdById,
|
||||
},
|
||||
status: curr.status ? APPROVAL_STATUS.SUBMITTED : approval.status,
|
||||
updatedAt: curr.createdAt,
|
||||
execution: { ...curr },
|
||||
},
|
||||
],
|
||||
}),
|
||||
}),
|
||||
{},
|
||||
);
|
||||
records
|
||||
.sort((prevRecord, nextRecord) => {
|
||||
const prev = new Date(prevRecord.job?.createdAt);
|
||||
const next = new Date(nextRecord.job?.createdAt);
|
||||
return prev < next ? -1 : prev > next ? 1 : prevRecord.id - nextRecord.id;
|
||||
})
|
||||
.forEach((record) => {
|
||||
const approvalExecutionId = approvalExecution[record.approvalExecutionId];
|
||||
const omitApprovalExecutionId = _.omit(approvalExecutionId, ['records']);
|
||||
(record.workflow = workflow),
|
||||
(record.execution = { ...omitApprovalExecutionId }),
|
||||
approvalExecutionId.records.push(record),
|
||||
approvalExecutionId.jobs || (approvalExecutionId.jobs = {}),
|
||||
approvalExecutionId.jobs[record.jobId]
|
||||
? (approvalExecutionId.jobs[record.jobId].first.groupCount += 1)
|
||||
: ((approvalExecutionId.jobs[record.jobId] = { first: record }),
|
||||
(record.groupCount = 1),
|
||||
(record.statusCount = { [APPROVAL_ACTION_STATUS.APPROVED]: 0, [APPROVAL_ACTION_STATUS.REJECTED]: 0 })),
|
||||
[APPROVAL_ACTION_STATUS.APPROVED, APPROVAL_ACTION_STATUS.REJECTED].includes(record.status) &&
|
||||
(approvalExecutionId.jobs[record.jobId].first.statusCount[record.status] += 1);
|
||||
}),
|
||||
approval.createdById === (currentUser == null ? void 0 : currentUser.data.id) &&
|
||||
approvalExecutions.forEach((approvalExecution) => {
|
||||
approvalExecution.status === EXECUTION_STATUS.CANCELED &&
|
||||
approvalExecution.records.length === 1 &&
|
||||
((approvalExecution.records[0].groupCount = 2),
|
||||
approvalExecution.records.push({
|
||||
user: { nickname: approval.createdBy.nickname },
|
||||
status: APPROVAL_ACTION_STATUS.WITHDRAWN,
|
||||
updatedAt: approvalExecution.updatedAt,
|
||||
}));
|
||||
});
|
||||
const aELength = approvalExecutions.length;
|
||||
|
||||
return approvalExecutions.filter(
|
||||
(approvalExecution, index) =>
|
||||
(aELength - 1 === index &&
|
||||
(!approvalExecution.status || approvalExecution.status === EXECUTION_STATUS.CANCELED)) ||
|
||||
approvalExecution.records.length > 1,
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { useCollectionRecordData } from '@tachybase/client';
|
||||
|
||||
import { APPROVAL_ACTION_STATUS } from '../../../constants';
|
||||
|
||||
export const ApprovalLastNodeColumn = () => {
|
||||
const approvalContext = useCollectionRecordData();
|
||||
|
||||
const lastNodeTitle = useMemo(() => getLastNodeTitle(approvalContext), [approvalContext]);
|
||||
|
||||
return <div>{lastNodeTitle}</div>;
|
||||
};
|
||||
|
||||
function getLastNodeTitle(approvalContext) {
|
||||
const { records } = approvalContext;
|
||||
let targetRecord = records.find(({ status }) => status !== APPROVAL_ACTION_STATUS.APPROVED);
|
||||
if (!targetRecord) {
|
||||
targetRecord = records.pop() || {};
|
||||
}
|
||||
|
||||
const lastNodeTitle = targetRecord.node?.title;
|
||||
|
||||
return lastNodeTitle;
|
||||
}
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { useCollectionManager, useCollectionRecordData, useCompile } from '@tachybase/client';
|
||||
import { dayjs } from '@tachybase/utils/client';
|
||||
|
||||
import useStyles from './style';
|
||||
import useStyles from '../style';
|
||||
|
||||
export const ApprovalsSummary = (props) => {
|
||||
const { value = '' } = props;
|
@ -1,17 +1,17 @@
|
||||
import { Plugin } from '@tachybase/client';
|
||||
|
||||
import { ApprovalLastNodeColumn } from './approval-columns/lastNode.column';
|
||||
import { ApprovalsSummary } from './approval-columns/summary.column';
|
||||
import { ApprovalDataProvider } from './ApprovalData.provider';
|
||||
import { ApprovalProcess } from './ApprovalProcess.view';
|
||||
import { ApprovalsSummary } from './ApprovalsSummary.view';
|
||||
|
||||
export class KitApprovalCommon extends Plugin {
|
||||
async load() {
|
||||
this.app.addComponents({
|
||||
// ApprovalCommon,
|
||||
|
||||
'ApprovalCommon.Provider.ApprovalDataProvider': ApprovalDataProvider,
|
||||
'ApprovalCommon.ViewComponent.ApprovalProcess': ApprovalProcess,
|
||||
ApprovalsSummary: ApprovalsSummary,
|
||||
ApprovalLastNodeColumn: ApprovalLastNodeColumn,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ export const getAntdTableColumns = ({ t, styles }) => {
|
||||
};
|
||||
},
|
||||
render: renderColumnTaskNode,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: t('User'),
|
@ -28,7 +28,6 @@ export function renderColumnTaskNode(text, { node, job, groupCount, statusCount
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
|
||||
.ant-tag {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
import { EXECUTION_STATUS } from '../../../../constants';
|
||||
import { APPROVAL_ACTION_STATUS, APPROVAL_STATUS } from '../../constants';
|
||||
import { lang } from '../../locale';
|
||||
|
||||
export function getResults({ approval, currentUser }) {
|
||||
const { workflow, approvalExecutions, records } = approval;
|
||||
approvalExecutions.sort((a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt));
|
||||
|
||||
const approvalExecution = approvalExecutions.reduce(
|
||||
(newObj, curr) =>
|
||||
Object.assign(newObj, {
|
||||
[curr.id]: Object.assign(curr, {
|
||||
records: [
|
||||
{
|
||||
groupCount: 1,
|
||||
node: {
|
||||
title: lang('Apply'),
|
||||
},
|
||||
user: {
|
||||
...approval.createdBy,
|
||||
id: approval.createdById,
|
||||
},
|
||||
status: curr.status ? APPROVAL_STATUS.SUBMITTED : approval.status,
|
||||
updatedAt: curr.createdAt,
|
||||
execution: { ...curr },
|
||||
},
|
||||
],
|
||||
}),
|
||||
}),
|
||||
{},
|
||||
);
|
||||
records
|
||||
.sort((prevRecord, nextRecord) => {
|
||||
const prev = new Date(prevRecord.job?.createdAt);
|
||||
const next = new Date(nextRecord.job?.createdAt);
|
||||
return prev < next ? -1 : prev > next ? 1 : prevRecord.id - nextRecord.id;
|
||||
})
|
||||
.forEach((record) => {
|
||||
const approvalExecutionId = approvalExecution[record.approvalExecutionId];
|
||||
const omitApprovalExecutionId = _.omit(approvalExecutionId, ['records']);
|
||||
(record.workflow = workflow),
|
||||
(record.execution = { ...omitApprovalExecutionId }),
|
||||
approvalExecutionId.records.push(record),
|
||||
approvalExecutionId.jobs || (approvalExecutionId.jobs = {}),
|
||||
approvalExecutionId.jobs[record.jobId]
|
||||
? (approvalExecutionId.jobs[record.jobId].first.groupCount += 1)
|
||||
: ((approvalExecutionId.jobs[record.jobId] = { first: record }),
|
||||
(record.groupCount = 1),
|
||||
(record.statusCount = { [APPROVAL_ACTION_STATUS.APPROVED]: 0, [APPROVAL_ACTION_STATUS.REJECTED]: 0 })),
|
||||
[APPROVAL_ACTION_STATUS.APPROVED, APPROVAL_ACTION_STATUS.REJECTED].includes(record.status) &&
|
||||
(approvalExecutionId.jobs[record.jobId].first.statusCount[record.status] += 1);
|
||||
}),
|
||||
approval.createdById === (currentUser == null ? void 0 : currentUser.data.id) &&
|
||||
approvalExecutions.forEach((approvalExecution) => {
|
||||
approvalExecution.status === EXECUTION_STATUS.CANCELED &&
|
||||
approvalExecution.records.length === 1 &&
|
||||
((approvalExecution.records[0].groupCount = 2),
|
||||
approvalExecution.records.push({
|
||||
user: { nickname: approval.createdBy.nickname },
|
||||
status: APPROVAL_ACTION_STATUS.WITHDRAWN,
|
||||
updatedAt: approvalExecution.updatedAt,
|
||||
}));
|
||||
});
|
||||
const aELength = approvalExecutions.length;
|
||||
return approvalExecutions.filter(
|
||||
(approvalExecution, index) =>
|
||||
(aELength - 1 === index &&
|
||||
(!approvalExecution.status || approvalExecution.status === EXECUTION_STATUS.CANCELED)) ||
|
||||
approvalExecution.records.length > 1,
|
||||
);
|
||||
}
|
@ -3,6 +3,7 @@ import { UiSchemaRepository } from '@tachybase/plugin-ui-schema-storage';
|
||||
import { JOB_STATUS } from '../../constants';
|
||||
import Instruction from '../../instructions';
|
||||
import { COLLECTION_NAME_APPROVAL_CARBON_COPY } from '../common/constants';
|
||||
import { APPROVAL_STATUS } from './constants';
|
||||
import { parsePerson } from './tools';
|
||||
|
||||
export default class ApprovalCarbonCopyInstruction extends Instruction {
|
||||
@ -25,6 +26,8 @@ export default class ApprovalCarbonCopyInstruction extends Instruction {
|
||||
appends: ['approvalExecutions', 'createdBy'],
|
||||
except: ['data'],
|
||||
});
|
||||
// NOTE: 只有新发起审批的时候, 才生成抄送副本. 否则, 会生成不必要的重复副本
|
||||
if ([APPROVAL_STATUS.SUBMITTED].includes(approval.status)) {
|
||||
const CarbonCopyModel = db.getModel(COLLECTION_NAME_APPROVAL_CARBON_COPY);
|
||||
const notifiedPersonDataMap = targetPersonList.map((userId, index) => ({
|
||||
userId,
|
||||
@ -33,7 +36,6 @@ export default class ApprovalCarbonCopyInstruction extends Instruction {
|
||||
executionId: job.executionId,
|
||||
workflowId: node.workflowId,
|
||||
index,
|
||||
// TODO: 怎么做到状态实时更新, 观察下审批发起的表的状态是怎么做的...
|
||||
createdById: approval.createdBy?.id,
|
||||
approvalId: approval.id,
|
||||
status: approval.status,
|
||||
@ -47,6 +49,7 @@ export default class ApprovalCarbonCopyInstruction extends Instruction {
|
||||
transaction: processor.transaction,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return job;
|
||||
}
|
||||
|
@ -370,6 +370,7 @@ const approvalCarbonCopy = {
|
||||
context.action.mergeParams({
|
||||
filter: {
|
||||
workflowId: centralizedApprovalFlow.map((item) => item.id),
|
||||
// approval.status: APPROVAL_STATUS.DRAFT,
|
||||
},
|
||||
});
|
||||
return actions.list(context, next);
|
||||
|
Loading…
Reference in New Issue
Block a user