fix: 审批, 自动刷新机制和撤回后更改子表格关联字段 (#1260)

Reviewed-on: daoyoucloud/tachybase#1260
Co-authored-by: bai.zixv <bai.zixv@foxmail.com>
Co-committed-by: bai.zixv <bai.zixv@foxmail.com>
This commit is contained in:
bai.zixv 2024-07-02 17:54:18 +08:00 committed by sealday
parent a15f5aedf0
commit ef2e2c9beb
9 changed files with 108 additions and 34 deletions

View File

@ -0,0 +1,19 @@
import { useActionContext, useAPIClient, useTableBlockContext } from '@tachybase/client';
// NOTE: 用于处理关闭弹窗, 和刷新外边显示的表格
export function useHandleRefresh(delay = 500) {
const { service } = useTableBlockContext();
const { setVisible, setSubmitted } = useActionContext() as any;
const refreshTable = () => {
setVisible(false);
setSubmitted?.(true);
setTimeout(() => {
service.refresh();
}, delay);
};
return {
refreshTable,
};
}

View File

@ -22,11 +22,24 @@ export const ViewActionLaunch = ({ popoverComponent = 'Action.Drawer', popoverCo
drawer: { drawer: {
type: 'void', type: 'void',
'x-component': popoverComponent, 'x-component': popoverComponent,
'x-component-props': { className: 'nb-action-popup', ...popoverComponentProps }, 'x-component-props': {
className: 'nb-action-popup',
...popoverComponentProps,
},
properties: { properties: {
content: Object.assign({ type: 'void' }, record.approvalId ? {} : { 'x-decorator': 'RecordDecorator' }, { content: Object.assign(
'x-component': 'ViewActionContent', {
}), type: 'void',
},
record.approvalId
? {}
: {
'x-decorator': 'RecordDecorator',
},
{
'x-component': 'ViewActionContent',
},
),
}, },
}, },
}, },

View File

@ -58,8 +58,6 @@ export const ViewActionLaunchContent = () => {
) as any; ) as any;
// @ts-ignore // @ts-ignore
const approvalData = data?.data; const approvalData = data?.data;
const { approval, execution, ...approvalValue } = approvalData || {};
const { workflow } = approval || {};
if (loading) { if (loading) {
return <Spin />; return <Spin />;
@ -69,6 +67,9 @@ export const ViewActionLaunchContent = () => {
return <Result status="error" title="Loading failed" />; return <Result status="error" title="Loading failed" />;
} }
const { approval, execution, ...approvalValue } = approvalData || {};
const { workflow } = approval || {};
return ( return (
<FlowContextProvider <FlowContextProvider
value={{ value={{

View File

@ -1,27 +1,30 @@
import { useActionContext, useAPIClient } from '@tachybase/client'; import { useAPIClient } from '@tachybase/client';
import { useField } from '@tachybase/schema'; import { useField } from '@tachybase/schema';
import _ from 'lodash'; import _ from 'lodash';
import { useApproval } from '../../../approval-common/ApprovalData.provider'; import { useApproval } from '../../../approval-common/ApprovalData.provider';
import { useHandleRefresh } from '../../common/useHandleRefresh';
export function useDestroyAction() { export function useDestroyAction() {
const { refreshTable } = useHandleRefresh();
const field = useField(); const field = useField();
const { setVisible, setSubmitted } = useActionContext() as any;
const approval = useApproval(); const approval = useApproval();
const apiClient = useAPIClient(); const apiClient = useAPIClient();
return { return {
async run() { async run() {
try { try {
console.log('%c Line:16 🍓 run', 'font-size:18px;color:#6ec1c2;background:#93c0a4');
_.set(field, ['data', 'loading'], true); _.set(field, ['data', 'loading'], true);
await apiClient.resource('approvals').destroy({ await apiClient.resource('approvals').destroy({
filterByTk: approval.id, filterByTk: approval.id,
}); });
setSubmitted(true); refreshTable();
setVisible(false);
} catch (err) { } catch (err) {
_.set(field, ['data', 'loading'], false); _.set(field, ['data', 'loading'], false);
} }

View File

@ -1,40 +1,43 @@
import { useActionContext, useAPIClient } from '@tachybase/client'; import { useAPIClient } from '@tachybase/client';
import { useField, useForm } from '@tachybase/schema'; import { useField, useForm } from '@tachybase/schema';
import _ from 'lodash'; import _ from 'lodash';
import { useFlowContext } from '../../../../../../FlowContext'; import { useFlowContext } from '../../../../../../FlowContext';
import { useApproval } from '../../../approval-common/ApprovalData.provider'; import { useApproval } from '../../../approval-common/ApprovalData.provider';
import { useHandleRefresh } from '../../common/useHandleRefresh';
import { useContextApprovalStatus } from '../Pd.ApplyActionStatus'; import { useContextApprovalStatus } from '../Pd.ApplyActionStatus';
export function useSubmit() { export function useSubmit() {
const from = useForm(); const { refreshTable } = useHandleRefresh();
const apiClient = useAPIClient();
const form = useForm();
const field = useField(); const field = useField();
const { setVisible, setSubmitted } = useActionContext() as any;
const { id } = useApproval(); const { id } = useApproval();
const { workflow } = useFlowContext(); const { workflow } = useFlowContext();
const contextApprovalStatus = useContextApprovalStatus(); const contextApprovalStatus = useContextApprovalStatus();
const apiClient = useAPIClient();
return { return {
async run() { async run() {
try { try {
from.submit(); form.submit();
_.set(field, ['data', 'loading'], true); _.set(field, ['data', 'loading'], true);
apiClient.resource('approvals').update({ apiClient.resource('approvals').update({
filterByTk: id, filterByTk: id,
values: { values: {
collectionName: workflow.config.collection, collectionName: workflow.config.collection,
data: from.values, data: form.values,
status: contextApprovalStatus, status: contextApprovalStatus,
schemaFormId: workflow.config.applyForm,
}, },
}); });
setSubmitted(true);
setVisible(false); form.reset();
from.reset();
_.set(field, ['data', 'loading'], false); _.set(field, ['data', 'loading'], false);
refreshTable();
} catch (m) { } catch (m) {
_.set(field, ['data', 'loading'], false); _.set(field, ['data', 'loading'], false);
} }

View File

@ -1,30 +1,33 @@
import { useActionContext, useAPIClient } from '@tachybase/client'; import { useActionContext, useAPIClient, useTableBlockContext } from '@tachybase/client';
import { useField } from '@tachybase/schema'; import { useField } from '@tachybase/schema';
import { useApproval } from '../../../approval-common/ApprovalData.provider'; import { useApproval } from '../../../approval-common/ApprovalData.provider';
import { useHandleRefresh } from '../../common/useHandleRefresh';
// 撤回 // 撤回
export function useWithdrawAction() { export function useWithdrawAction() {
const { refreshTable } = useHandleRefresh(100);
const field = useField(); const field = useField();
const { setVisible, setSubmitted } = useActionContext() as any;
const approval = useApproval(); const approval = useApproval();
const api = useAPIClient(); const api = useAPIClient();
return { return {
async run() { async run() {
try { try {
field.data = field.data ?? {}; field.data = field.data ?? {};
field.data.loading = true; field.data.loading = true;
await api.resource('approvals').withdraw({ await api.resource('approvals').withdraw({
filterByTk: approval.id, filterByTk: approval.id,
}); });
// setSubmitted(true);
setVisible(false);
setSubmitted(true);
field.data.loading = false; field.data.loading = false;
refreshTable();
} catch (v) { } catch (v) {
console.log('%c Line:33 🌽 v', 'font-size:18px;color:#33a5ff;background:#fca650', v);
if (field.data) { if (field.data) {
field.data.loading = false; field.data.loading = false;
} }

View File

@ -1,36 +1,39 @@
import { useActionContext, useAPIClient, useRefreshActionProps } from '@tachybase/client'; import { useAPIClient } from '@tachybase/client';
import { useField, useForm } from '@tachybase/schema'; import { useField, useForm } from '@tachybase/schema';
import { useHandleRefresh } from '../../common/useHandleRefresh';
import { useContextApprovalAction } from '../Pd.ApprovalAction'; import { useContextApprovalAction } from '../Pd.ApprovalAction';
import { useContextApprovalExecutions } from '../Pd.ApprovalExecutions'; import { useContextApprovalExecutions } from '../Pd.ApprovalExecutions';
export function useSubmit() { export function useSubmit() {
const { refreshTable } = useHandleRefresh();
const field = useField(); const field = useField();
const api = useAPIClient(); const api = useAPIClient();
const form = useForm(); const form = useForm();
const approvalExecutions = useContextApprovalExecutions(); const approvalExecutions = useContextApprovalExecutions();
const { status } = useContextApprovalAction(); const { status } = useContextApprovalAction();
const { setVisible, setSubmitted } = useActionContext() as any;
// TODO: 自动更新外层block, 提升交互体验
// const { onClick: refreshAction } = useRefreshActionProps();
return { return {
run: async () => { run: async () => {
try { try {
if (form.values.status) { if (form.values.status) {
return; return;
} }
await form.submit(); await form.submit();
field.data = field.data ?? {}; field.data = field.data ?? {};
field.data.loading = true; field.data.loading = true;
setVisible(false);
await api.resource('approvalRecords').submit({ await api.resource('approvalRecords').submit({
filterByTk: approvalExecutions.id, filterByTk: approvalExecutions.id,
values: { ...form.values, status }, values: { ...form.values, status },
}); });
field.data.loading = false; field.data.loading = false;
await form.reset(); await form.reset();
// refreshAction?.();
setSubmitted?.(true); refreshTable();
} catch (error) { } catch (error) {
console.error(error); console.error(error);
field.data && (field.data.loading = false); field.data && (field.data.loading = false);

View File

@ -4,6 +4,7 @@ import { parseCollectionName } from '@tachybase/data-source-manager';
import { EXECUTION_STATUS, JOB_STATUS, PluginWorkflow } from '../..'; import { EXECUTION_STATUS, JOB_STATUS, PluginWorkflow } from '../..';
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';
const workflows = { const workflows = {
async listApprovalFlows(context, next) { async listApprovalFlows(context, next) {
@ -71,13 +72,27 @@ const approvals = {
return actions.create(context, next); return actions.create(context, next);
}, },
async update(context, next) { async update(context, next) {
const { collectionName, data, status } = context.action.params.values ?? {}; const { collectionName, data, status, schemaFormId } = context.action.params.values ?? {};
const [dataSourceName, cName] = parseCollectionName(collectionName); const [dataSourceName, cName] = parseCollectionName(collectionName);
const dataSource = context.app.dataSourceManager.dataSources.get(dataSourceName); const dataSource = context.app.dataSourceManager.dataSources.get(dataSourceName);
const collection = dataSource.collectionManager.getCollection(cName); const collection = dataSource.collectionManager.getCollection(cName);
/** 以下为,处理子表单子表格等关联字段的更新逻辑 */
const schemaOfForm = await context.db.getRepository('uiSchemas').getJsonSchema(schemaFormId);
const itemsOfSchema = await jsonParse(`**["x-component-props".mode]`, schemaOfForm);
const fieldAssociationName = itemsOfSchema
.filter((item) => {
return ['Nester', 'SubTable', 'PopoverNester'].includes(item?.['x-component-props']?.mode);
})
.map((item) => getAssociationName(item['x-collection-field']));
const updateAssociationValues = [...new Set(fieldAssociationName)];
/** 以上为,处理子表单子表格等关联字段的更新逻辑 */
const [target] = await collection.repository.update({ const [target] = await collection.repository.update({
filterByTk: data[collection.filterTargetKey], filterByTk: data[collection.filterTargetKey],
values: data, values: data,
updateAssociationValues,
}); });
context.action.mergeParams({ context.action.mergeParams({
values: { values: {

View File

@ -0,0 +1,14 @@
import jsonata from 'jsonata';
export function getAssociationName(str) {
const lastIndex = str.lastIndexOf('.');
if (lastIndex !== -1) {
return str.substring(lastIndex + 1);
}
return str;
}
export function jsonParse(expression, scope) {
const result = jsonata(expression).evaluate(scope);
return result;
}