feat: refactor resubmit (#1290)
Co-authored-by: sealday <sealday@gmail.com> Reviewed-on: https://git.daoyoucloud.com:8443/daoyoucloud/tachybase/pulls/1290
This commit is contained in:
		
							parent
							
								
									0b958cf528
								
							
						
					
					
						commit
						1d5306d427
					
				@ -38,3 +38,5 @@ export default class DatabaseUtils {
 | 
			
		||||
    return this.db.options.schema || 'public';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export * from './traverseJSON';
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										112
									
								
								packages/core/database/src/database-utils/traverseJSON.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								packages/core/database/src/database-utils/traverseJSON.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,112 @@
 | 
			
		||||
import { Collection } from '@tachybase/database';
 | 
			
		||||
 | 
			
		||||
type TraverseOptions = {
 | 
			
		||||
  collection: Collection;
 | 
			
		||||
  exclude?: string[];
 | 
			
		||||
  include?: string[];
 | 
			
		||||
  through?: string;
 | 
			
		||||
  excludePk?: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const traverseHasMany = (arr: any[], { collection, exclude = [], include = [] }: TraverseOptions) => {
 | 
			
		||||
  if (!arr) {
 | 
			
		||||
    return arr;
 | 
			
		||||
  }
 | 
			
		||||
  return arr.map((item) => traverseJSON(item, { collection, exclude, include }));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const traverseBelongsToMany = (arr: any[], { collection, exclude = [], through }: TraverseOptions) => {
 | 
			
		||||
  if (!arr) {
 | 
			
		||||
    return arr;
 | 
			
		||||
  }
 | 
			
		||||
  const throughCollection = collection.db.getCollection(through);
 | 
			
		||||
  return arr.map((item) => {
 | 
			
		||||
    const data = traverseJSON(item[through], { collection: throughCollection, exclude });
 | 
			
		||||
    if (data && Object.keys(data).length) {
 | 
			
		||||
      item[through] = data;
 | 
			
		||||
    } else {
 | 
			
		||||
      delete item[through];
 | 
			
		||||
    }
 | 
			
		||||
    return traverseJSON(item, {
 | 
			
		||||
      collection,
 | 
			
		||||
      excludePk: false,
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const parseInclude = (keys: string[]) => {
 | 
			
		||||
  const map = {};
 | 
			
		||||
  for (const key of keys) {
 | 
			
		||||
    const args = key.split('.');
 | 
			
		||||
    const field = args.shift();
 | 
			
		||||
    map[field] = map[field] || [];
 | 
			
		||||
    if (args.length) {
 | 
			
		||||
      map[field].push(args.join('.'));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return map;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const traverseJSON = (data, options: TraverseOptions) => {
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return data;
 | 
			
		||||
  }
 | 
			
		||||
  const { collection, exclude = [], include = [], excludePk = true } = options;
 | 
			
		||||
  const map = parseInclude(include);
 | 
			
		||||
  const result = {};
 | 
			
		||||
  for (const key of Object.keys(data || {})) {
 | 
			
		||||
    const subInclude = map[key];
 | 
			
		||||
    if (include.length > 0 && !subInclude) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (exclude.includes(key)) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (['createdAt', 'updatedAt', 'createdBy', 'createdById', 'updatedById', 'updatedBy'].includes(key)) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    const field = collection.getField(key);
 | 
			
		||||
    if (!field) {
 | 
			
		||||
      result[key] = data[key];
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (field.options.primaryKey && excludePk) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (field.options.isForeignKey) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (['sort', 'password', 'sequence'].includes(field.type)) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (field.type === 'hasOne') {
 | 
			
		||||
      result[key] = traverseJSON(data[key], {
 | 
			
		||||
        collection: collection.db.getCollection(field.target),
 | 
			
		||||
        exclude: [field.foreignKey],
 | 
			
		||||
        include: subInclude,
 | 
			
		||||
      });
 | 
			
		||||
    } else if (field.type === 'hasMany') {
 | 
			
		||||
      result[key] = traverseHasMany(data[key], {
 | 
			
		||||
        collection: collection.db.getCollection(field.target),
 | 
			
		||||
        exclude: [field.foreignKey],
 | 
			
		||||
        include: subInclude,
 | 
			
		||||
      });
 | 
			
		||||
    } else if (field.type === 'belongsTo') {
 | 
			
		||||
      result[key] = traverseJSON(data[key], {
 | 
			
		||||
        collection: collection.db.getCollection(field.target),
 | 
			
		||||
        // exclude: [field.foreignKey],
 | 
			
		||||
        include: subInclude,
 | 
			
		||||
        excludePk: false,
 | 
			
		||||
      });
 | 
			
		||||
    } else if (field.type === 'belongsToMany') {
 | 
			
		||||
      result[key] = traverseBelongsToMany(data[key], {
 | 
			
		||||
        collection: collection.db.getCollection(field.target),
 | 
			
		||||
        exclude: [field.foreignKey, field.otherKey],
 | 
			
		||||
        through: field.through,
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      result[key] = data[key];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return result;
 | 
			
		||||
};
 | 
			
		||||
@ -38,6 +38,7 @@ export * from './relation-repository/single-relation-repository';
 | 
			
		||||
export * from './repository';
 | 
			
		||||
export * from './update-associations';
 | 
			
		||||
export { snakeCase } from './utils';
 | 
			
		||||
export * from './database-utils';
 | 
			
		||||
export * from './value-parsers';
 | 
			
		||||
export * from './view-collection';
 | 
			
		||||
export * from './view/view-inference';
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { Context } from '@tachybase/actions';
 | 
			
		||||
import { Collection } from '@tachybase/database';
 | 
			
		||||
import { traverseJSON } from '@tachybase/database';
 | 
			
		||||
 | 
			
		||||
export const dateTemplate = async (ctx: Context, next) => {
 | 
			
		||||
  const { resourceName, actionName } = ctx.action;
 | 
			
		||||
@ -14,114 +14,3 @@ export const dateTemplate = async (ctx: Context, next) => {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type TraverseOptions = {
 | 
			
		||||
  collection: Collection;
 | 
			
		||||
  exclude?: string[];
 | 
			
		||||
  include?: string[];
 | 
			
		||||
  through?: string;
 | 
			
		||||
  excludePk?: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const traverseHasMany = (arr: any[], { collection, exclude = [], include = [] }: TraverseOptions) => {
 | 
			
		||||
  if (!arr) {
 | 
			
		||||
    return arr;
 | 
			
		||||
  }
 | 
			
		||||
  return arr.map((item) => traverseJSON(item, { collection, exclude, include }));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const traverseBelongsToMany = (arr: any[], { collection, exclude = [], through }: TraverseOptions) => {
 | 
			
		||||
  if (!arr) {
 | 
			
		||||
    return arr;
 | 
			
		||||
  }
 | 
			
		||||
  const throughCollection = collection.db.getCollection(through);
 | 
			
		||||
  return arr.map((item) => {
 | 
			
		||||
    const data = traverseJSON(item[through], { collection: throughCollection, exclude });
 | 
			
		||||
    if (data && Object.keys(data).length) {
 | 
			
		||||
      item[through] = data;
 | 
			
		||||
    } else {
 | 
			
		||||
      delete item[through];
 | 
			
		||||
    }
 | 
			
		||||
    return traverseJSON(item, {
 | 
			
		||||
      collection,
 | 
			
		||||
      excludePk: false,
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const parseInclude = (keys: string[]) => {
 | 
			
		||||
  const map = {};
 | 
			
		||||
  for (const key of keys) {
 | 
			
		||||
    const args = key.split('.');
 | 
			
		||||
    const field = args.shift();
 | 
			
		||||
    map[field] = map[field] || [];
 | 
			
		||||
    if (args.length) {
 | 
			
		||||
      map[field].push(args.join('.'));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return map;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const traverseJSON = (data, options: TraverseOptions) => {
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return data;
 | 
			
		||||
  }
 | 
			
		||||
  const { collection, exclude = [], include = [], excludePk = true } = options;
 | 
			
		||||
  const map = parseInclude(include);
 | 
			
		||||
  const result = {};
 | 
			
		||||
  for (const key of Object.keys(data || {})) {
 | 
			
		||||
    const subInclude = map[key];
 | 
			
		||||
    if (include.length > 0 && !subInclude) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (exclude.includes(key)) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (['createdAt', 'updatedAt', 'createdBy', 'createdById', 'updatedById', 'updatedBy'].includes(key)) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    const field = collection.getField(key);
 | 
			
		||||
    if (!field) {
 | 
			
		||||
      result[key] = data[key];
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (field.options.primaryKey && excludePk) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (field.options.isForeignKey) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (['sort', 'password', 'sequence'].includes(field.type)) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    if (field.type === 'hasOne') {
 | 
			
		||||
      result[key] = traverseJSON(data[key], {
 | 
			
		||||
        collection: collection.db.getCollection(field.target),
 | 
			
		||||
        exclude: [field.foreignKey],
 | 
			
		||||
        include: subInclude,
 | 
			
		||||
      });
 | 
			
		||||
    } else if (field.type === 'hasMany') {
 | 
			
		||||
      result[key] = traverseHasMany(data[key], {
 | 
			
		||||
        collection: collection.db.getCollection(field.target),
 | 
			
		||||
        exclude: [field.foreignKey],
 | 
			
		||||
        include: subInclude,
 | 
			
		||||
      });
 | 
			
		||||
    } else if (field.type === 'belongsTo') {
 | 
			
		||||
      result[key] = traverseJSON(data[key], {
 | 
			
		||||
        collection: collection.db.getCollection(field.target),
 | 
			
		||||
        // exclude: [field.foreignKey],
 | 
			
		||||
        include: subInclude,
 | 
			
		||||
        excludePk: false,
 | 
			
		||||
      });
 | 
			
		||||
    } else if (field.type === 'belongsToMany') {
 | 
			
		||||
      result[key] = traverseBelongsToMany(data[key], {
 | 
			
		||||
        collection: collection.db.getCollection(field.target),
 | 
			
		||||
        exclude: [field.foreignKey, field.otherKey],
 | 
			
		||||
        through: field.through,
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      result[key] = data[key];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return result;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -9,8 +9,6 @@ import {
 | 
			
		||||
} from '@tachybase/client';
 | 
			
		||||
import { uid } from '@tachybase/utils/client';
 | 
			
		||||
 | 
			
		||||
import { Spin } from 'antd';
 | 
			
		||||
 | 
			
		||||
import { useFlowContext } from '../../../../../FlowContext';
 | 
			
		||||
 | 
			
		||||
//  发起人操作界面-创建区块
 | 
			
		||||
@ -19,30 +17,26 @@ export const SchemaAddBlock = ({ value, onChange }) => {
 | 
			
		||||
  const { workflow } = useFlowContext();
 | 
			
		||||
  const { components } = useContext(SchemaComponentContext);
 | 
			
		||||
 | 
			
		||||
  // TODO:
 | 
			
		||||
  // 获取对应数据表的表单Schema
 | 
			
		||||
  const { data, loading } = useRequest(() =>
 | 
			
		||||
    E(this, null, function* () {
 | 
			
		||||
      let m;
 | 
			
		||||
      if (value) {
 | 
			
		||||
        const { data: h } = yield api.request({ url: `uiSchemas:getJsonSchema/${value}` });
 | 
			
		||||
        if (((m = h.data) == null ? void 0 : m['x-uid']) === value) return h.data;
 | 
			
		||||
      }
 | 
			
		||||
      const l = uid(),
 | 
			
		||||
        d = {
 | 
			
		||||
          type: 'void',
 | 
			
		||||
          name: l,
 | 
			
		||||
          'x-uid': l,
 | 
			
		||||
          'x-component': 'Grid',
 | 
			
		||||
          'x-initializer': 'ApprovalApplyAddBlockButton',
 | 
			
		||||
          properties: {},
 | 
			
		||||
        };
 | 
			
		||||
      return yield api.resource('uiSchemas').insert({ values: d }), onChange(l), d;
 | 
			
		||||
    }),
 | 
			
		||||
  );
 | 
			
		||||
  const { data, loading } = useRequest(async () => {
 | 
			
		||||
    if (value) {
 | 
			
		||||
      const { data } = await api.request({ url: `uiSchemas:getJsonSchema/${value}` });
 | 
			
		||||
      if (data.data?.['x-uid'] === value) return data.data;
 | 
			
		||||
    }
 | 
			
		||||
    const name = uid();
 | 
			
		||||
    const values = {
 | 
			
		||||
      type: 'void',
 | 
			
		||||
      name: name,
 | 
			
		||||
      'x-uid': name,
 | 
			
		||||
      'x-component': 'Grid',
 | 
			
		||||
      'x-initializer': 'ApprovalApplyAddBlockButton',
 | 
			
		||||
      properties: {},
 | 
			
		||||
    };
 | 
			
		||||
    return await api.resource('uiSchemas').insert({ values }), onChange(name), values;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  if (loading) {
 | 
			
		||||
    return <Spin />;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
@ -61,7 +55,6 @@ export const SchemaAddBlock = ({ value, onChange }) => {
 | 
			
		||||
          useFormBlockProps,
 | 
			
		||||
          useActionResubmit,
 | 
			
		||||
        }}
 | 
			
		||||
        // @ts-ignore ugly
 | 
			
		||||
        schema={data}
 | 
			
		||||
      />
 | 
			
		||||
    </SchemaComponentProvider>
 | 
			
		||||
@ -89,24 +82,3 @@ function useActionResubmit() {
 | 
			
		||||
function ProviderActionResubmit(props) {
 | 
			
		||||
  return props.children;
 | 
			
		||||
}
 | 
			
		||||
// TODO:
 | 
			
		||||
const E = (f, o, p) =>
 | 
			
		||||
  // eslint-disable-next-line promise/param-names
 | 
			
		||||
  new Promise((r, G) => {
 | 
			
		||||
    const u = ($) => {
 | 
			
		||||
        try {
 | 
			
		||||
          A(p.next($));
 | 
			
		||||
        } catch (U) {
 | 
			
		||||
          G(U);
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      y = ($) => {
 | 
			
		||||
        try {
 | 
			
		||||
          A(p.throw($));
 | 
			
		||||
        } catch (U) {
 | 
			
		||||
          G(U);
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      A = ($) => ($.done ? r($.value) : Promise.resolve($.value).then(u, y));
 | 
			
		||||
    A((p = p.apply(f, o)).next());
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -1,19 +1,17 @@
 | 
			
		||||
import { useCurrentUserContext } from '@tachybase/client';
 | 
			
		||||
 | 
			
		||||
import { useFlowContext } from '../../../../../FlowContext';
 | 
			
		||||
import { APPROVAL_STATUS } from '../../../constants';
 | 
			
		||||
import { useApproval } from '../../approval-common/ApprovalData.provider';
 | 
			
		||||
import { useResubmit } from '../../approval-common/Resubmit.provider';
 | 
			
		||||
 | 
			
		||||
export function ProviderActionResubmit(props) {
 | 
			
		||||
  const { data } = useCurrentUserContext();
 | 
			
		||||
  const { isResubmit } = useResubmit();
 | 
			
		||||
  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) {
 | 
			
		||||
  const isDraft = status === APPROVAL_STATUS.DRAFT;
 | 
			
		||||
  if (isSameId && !isResubmit && !isDraft) {
 | 
			
		||||
    return props.children;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,14 +11,8 @@ export function ActionBarProvider(props) {
 | 
			
		||||
 | 
			
		||||
  const isSameId = data.data.id === createdById;
 | 
			
		||||
  const isSameExcutionId = latestExecutionId === approvalExecution.id;
 | 
			
		||||
  const isExcutionDid = [
 | 
			
		||||
    APPROVAL_STATUS.DRAFT,
 | 
			
		||||
    APPROVAL_STATUS.RETURNED,
 | 
			
		||||
    APPROVAL_STATUS.SUBMITTED,
 | 
			
		||||
    APPROVAL_STATUS.RESUBMIT,
 | 
			
		||||
  ].includes(status);
 | 
			
		||||
 | 
			
		||||
  if (!isSameId || !isSameExcutionId || !isExcutionDid) {
 | 
			
		||||
  if (!isSameId || !isSameExcutionId) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ import { useCurrentUserContext } from '@tachybase/client';
 | 
			
		||||
import { useFlowContext } from '../../../../../FlowContext';
 | 
			
		||||
import { APPROVAL_STATUS } from '../../../constants';
 | 
			
		||||
import { useApproval } from '../../approval-common/ApprovalData.provider';
 | 
			
		||||
import { useResubmit } from '../../approval-common/Resubmit.provider';
 | 
			
		||||
 | 
			
		||||
const ContextApprovalStatus = createContext(APPROVAL_STATUS.SUBMITTED);
 | 
			
		||||
 | 
			
		||||
@ -16,11 +17,16 @@ export function ApplyActionStatusProvider(props) {
 | 
			
		||||
  const { status, createdById } = useApproval();
 | 
			
		||||
  const { workflow } = useFlowContext();
 | 
			
		||||
  const { data } = useCurrentUserContext();
 | 
			
		||||
  const { isResubmit } = useResubmit();
 | 
			
		||||
  const isSameId = data.data.id === createdById;
 | 
			
		||||
  const isEnbled = workflow.enabled;
 | 
			
		||||
  const isEnabled = workflow.enabled;
 | 
			
		||||
  const isStatusDid = [APPROVAL_STATUS.RESUBMIT, APPROVAL_STATUS.DRAFT, APPROVAL_STATUS.RETURNED].includes(status);
 | 
			
		||||
 | 
			
		||||
  if (isSameId && isEnbled && isStatusDid) {
 | 
			
		||||
  if (value === APPROVAL_STATUS.DRAFT && status === APPROVAL_STATUS.DRAFT) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((isSameId && isEnabled && isStatusDid) || (isSameId && isEnabled && isResubmit)) {
 | 
			
		||||
    return <ContextApprovalStatus.Provider value={value}>{children}</ContextApprovalStatus.Provider>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,17 +3,19 @@ import { useCurrentUserContext } from '@tachybase/client';
 | 
			
		||||
import { useFlowContext } from '../../../../../FlowContext';
 | 
			
		||||
import { APPROVAL_STATUS } from '../../../constants';
 | 
			
		||||
import { useApproval } from '../../approval-common/ApprovalData.provider';
 | 
			
		||||
import { useResubmit } from '../../approval-common/Resubmit.provider';
 | 
			
		||||
 | 
			
		||||
export function WithdrawActionProvider({ children }) {
 | 
			
		||||
  const { data } = useCurrentUserContext();
 | 
			
		||||
  const { status, createdById } = useApproval();
 | 
			
		||||
  const { workflow } = useFlowContext();
 | 
			
		||||
  const { isResubmit } = useResubmit();
 | 
			
		||||
 | 
			
		||||
  const isSameId = data.data.id === createdById;
 | 
			
		||||
  const isEnabledWithdraw = workflow.enabled && workflow.config.withdrawable;
 | 
			
		||||
  const isStatusSubmitted = APPROVAL_STATUS.SUBMITTED === status;
 | 
			
		||||
 | 
			
		||||
  if (isSameId && isEnabledWithdraw && isStatusSubmitted) {
 | 
			
		||||
  if (isSameId && isEnabledWithdraw && isStatusSubmitted && !isResubmit) {
 | 
			
		||||
    return children;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@ import { FormBlockProvider } from '../../../common/FormBlock.provider';
 | 
			
		||||
import { APPROVAL_STATUS } from '../../../constants';
 | 
			
		||||
import { NAMESPACE } from '../../../locale';
 | 
			
		||||
import { ApprovalContext } from '../../approval-common/ApprovalData.provider';
 | 
			
		||||
import { ResubmitProvider } from '../../approval-common/Resubmit.provider';
 | 
			
		||||
import { ContextWithActionEnabled } from '../../approval-common/WithActionEnabled.provider';
 | 
			
		||||
import { ContextApprovalExecution } from '../common/ApprovalExecution.provider';
 | 
			
		||||
import { FlowContextProvider } from '../common/FlowContext.provider';
 | 
			
		||||
@ -85,77 +86,79 @@ export const ViewActionLaunchContent = () => {
 | 
			
		||||
    >
 | 
			
		||||
      <ApprovalContext.Provider value={approval}>
 | 
			
		||||
        <ContextApprovalExecution.Provider value={approvalValue}>
 | 
			
		||||
          <SchemaComponent
 | 
			
		||||
            components={{
 | 
			
		||||
              SchemaComponentProvider: SchemaComponentProvider,
 | 
			
		||||
              RemoteSchemaComponent: RemoteSchemaComponent,
 | 
			
		||||
              SchemaComponentContextProvider,
 | 
			
		||||
              FormBlockProvider,
 | 
			
		||||
              ActionBarProvider,
 | 
			
		||||
              ApplyActionStatusProvider,
 | 
			
		||||
              WithdrawActionProvider,
 | 
			
		||||
              DetailsBlockProvider,
 | 
			
		||||
              ProviderActionResubmit,
 | 
			
		||||
            }}
 | 
			
		||||
            scope={{
 | 
			
		||||
              useForm,
 | 
			
		||||
              useSubmit: useSubmit,
 | 
			
		||||
              useFormBlockProps,
 | 
			
		||||
              useDetailsBlockProps: useFormBlockContext,
 | 
			
		||||
              useWithdrawAction,
 | 
			
		||||
              useDestroyAction,
 | 
			
		||||
              useActionResubmit,
 | 
			
		||||
            }}
 | 
			
		||||
            schema={{
 | 
			
		||||
              name: `view-${approval == null ? void 0 : approval.id}`,
 | 
			
		||||
              type: 'void',
 | 
			
		||||
              properties: {
 | 
			
		||||
                tabs: {
 | 
			
		||||
                  type: 'void',
 | 
			
		||||
                  'x-component': 'Tabs',
 | 
			
		||||
                  properties: Object.assign(
 | 
			
		||||
                    {
 | 
			
		||||
                      detail: {
 | 
			
		||||
                        type: 'void',
 | 
			
		||||
                        title: `{{t('Application content', { ns: '${NAMESPACE}' })}}`,
 | 
			
		||||
                        'x-component': 'Tabs.TabPane',
 | 
			
		||||
                        properties: {
 | 
			
		||||
                          detail: {
 | 
			
		||||
                            type: 'void',
 | 
			
		||||
                            'x-decorator': 'SchemaComponentContextProvider',
 | 
			
		||||
                            'x-decorator-props': {
 | 
			
		||||
                              designable: false,
 | 
			
		||||
                            },
 | 
			
		||||
                            'x-component': 'RemoteSchemaComponent',
 | 
			
		||||
                            'x-component-props': {
 | 
			
		||||
                              uid: workflow?.config.applyForm,
 | 
			
		||||
                              noForm: true,
 | 
			
		||||
                            },
 | 
			
		||||
                          },
 | 
			
		||||
                        },
 | 
			
		||||
                      },
 | 
			
		||||
                    },
 | 
			
		||||
                    needHideProcess
 | 
			
		||||
                      ? {}
 | 
			
		||||
                      : {
 | 
			
		||||
                          process: {
 | 
			
		||||
                            type: 'void',
 | 
			
		||||
                            title: `{{t('Approval process', { ns: '${NAMESPACE}' })}}`,
 | 
			
		||||
                            'x-component': 'Tabs.TabPane',
 | 
			
		||||
                            properties: {
 | 
			
		||||
                              process: {
 | 
			
		||||
                                type: 'void',
 | 
			
		||||
                                'x-decorator': 'CardItem',
 | 
			
		||||
                                'x-component': 'ApprovalCommon.ViewComponent.ApprovalProcess',
 | 
			
		||||
          <ResubmitProvider>
 | 
			
		||||
            <SchemaComponent
 | 
			
		||||
              components={{
 | 
			
		||||
                SchemaComponentProvider,
 | 
			
		||||
                RemoteSchemaComponent,
 | 
			
		||||
                SchemaComponentContextProvider,
 | 
			
		||||
                FormBlockProvider,
 | 
			
		||||
                ActionBarProvider,
 | 
			
		||||
                ApplyActionStatusProvider,
 | 
			
		||||
                WithdrawActionProvider,
 | 
			
		||||
                DetailsBlockProvider,
 | 
			
		||||
                ProviderActionResubmit,
 | 
			
		||||
              }}
 | 
			
		||||
              scope={{
 | 
			
		||||
                useForm,
 | 
			
		||||
                useSubmit,
 | 
			
		||||
                useFormBlockProps,
 | 
			
		||||
                useDetailsBlockProps: useFormBlockContext,
 | 
			
		||||
                useWithdrawAction,
 | 
			
		||||
                useDestroyAction,
 | 
			
		||||
                useActionResubmit,
 | 
			
		||||
              }}
 | 
			
		||||
              schema={{
 | 
			
		||||
                name: `view-${approval == null ? void 0 : approval.id}`,
 | 
			
		||||
                type: 'void',
 | 
			
		||||
                properties: {
 | 
			
		||||
                  tabs: {
 | 
			
		||||
                    type: 'void',
 | 
			
		||||
                    'x-component': 'Tabs',
 | 
			
		||||
                    properties: Object.assign(
 | 
			
		||||
                      {
 | 
			
		||||
                        detail: {
 | 
			
		||||
                          type: 'void',
 | 
			
		||||
                          title: `{{t('Application content', { ns: '${NAMESPACE}' })}}`,
 | 
			
		||||
                          'x-component': 'Tabs.TabPane',
 | 
			
		||||
                          properties: {
 | 
			
		||||
                            detail: {
 | 
			
		||||
                              type: 'void',
 | 
			
		||||
                              'x-decorator': 'SchemaComponentContextProvider',
 | 
			
		||||
                              'x-decorator-props': {
 | 
			
		||||
                                designable: false,
 | 
			
		||||
                              },
 | 
			
		||||
                              'x-component': 'RemoteSchemaComponent',
 | 
			
		||||
                              'x-component-props': {
 | 
			
		||||
                                uid: workflow?.config.applyForm,
 | 
			
		||||
                                noForm: true,
 | 
			
		||||
                              },
 | 
			
		||||
                            },
 | 
			
		||||
                          },
 | 
			
		||||
                        },
 | 
			
		||||
                  ),
 | 
			
		||||
                      },
 | 
			
		||||
                      needHideProcess
 | 
			
		||||
                        ? {}
 | 
			
		||||
                        : {
 | 
			
		||||
                            process: {
 | 
			
		||||
                              type: 'void',
 | 
			
		||||
                              title: `{{t('Approval process', { ns: '${NAMESPACE}' })}}`,
 | 
			
		||||
                              'x-component': 'Tabs.TabPane',
 | 
			
		||||
                              properties: {
 | 
			
		||||
                                process: {
 | 
			
		||||
                                  type: 'void',
 | 
			
		||||
                                  'x-decorator': 'CardItem',
 | 
			
		||||
                                  'x-component': 'ApprovalCommon.ViewComponent.ApprovalProcess',
 | 
			
		||||
                                },
 | 
			
		||||
                              },
 | 
			
		||||
                            },
 | 
			
		||||
                          },
 | 
			
		||||
                    ),
 | 
			
		||||
                  },
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            }}
 | 
			
		||||
          />
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
          </ResubmitProvider>
 | 
			
		||||
        </ContextApprovalExecution.Provider>
 | 
			
		||||
      </ApprovalContext.Provider>
 | 
			
		||||
    </FlowContextProvider>
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ import { Button, Dropdown } from 'antd';
 | 
			
		||||
import { useTranslation } from '../../../../locale';
 | 
			
		||||
import { FlowContextProvider } from '../../common/FlowContext.provider';
 | 
			
		||||
import { useActionResubmit } from '../hooks/useActionResubmit';
 | 
			
		||||
import { useSubmit } from './hooks/useSubmit';
 | 
			
		||||
import { useCreateSubmit } from './hooks/useSubmit';
 | 
			
		||||
import { useWithdrawAction } from './hooks/useWithdrawAction';
 | 
			
		||||
import { ActionBarProvider } from './Pd.ActionBar';
 | 
			
		||||
import { ApplyActionStatusProvider } from './Pd.ActionStatus';
 | 
			
		||||
@ -108,15 +108,16 @@ export const ApplyButton = () => {
 | 
			
		||||
        <SchemaComponent
 | 
			
		||||
          schema={schema}
 | 
			
		||||
          components={{
 | 
			
		||||
            RemoteSchemaComponent: RemoteSchemaComponent,
 | 
			
		||||
            CollectionProvider_deprecated: CollectionProvider_deprecated,
 | 
			
		||||
            FlowContextProvider: FlowContextProvider,
 | 
			
		||||
            ApplyActionStatusProvider: ApplyActionStatusProvider,
 | 
			
		||||
            RemoteSchemaComponent,
 | 
			
		||||
            CollectionProvider_deprecated,
 | 
			
		||||
            FlowContextProvider,
 | 
			
		||||
            ApplyActionStatusProvider,
 | 
			
		||||
            ActionBarProvider,
 | 
			
		||||
            WithdrawActionProvider: WithdrawActionProvider,
 | 
			
		||||
            ProviderActionResubmit: () => null,
 | 
			
		||||
            WithdrawActionProvider,
 | 
			
		||||
          }}
 | 
			
		||||
          scope={{
 | 
			
		||||
            useSubmit: useSubmit,
 | 
			
		||||
            useSubmit: useCreateSubmit,
 | 
			
		||||
            useWithdrawAction,
 | 
			
		||||
            useActionResubmit,
 | 
			
		||||
          }}
 | 
			
		||||
 | 
			
		||||
@ -7,21 +7,23 @@ import {
 | 
			
		||||
} from '@tachybase/client';
 | 
			
		||||
import { useField, useForm } from '@tachybase/schema';
 | 
			
		||||
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { useFlowContext } from '../../../../../../../FlowContext';
 | 
			
		||||
import { useContextApprovalStatus } from '../Pd.ActionStatus';
 | 
			
		||||
 | 
			
		||||
export function useSubmit() {
 | 
			
		||||
export function useCreateSubmit() {
 | 
			
		||||
  const from = useForm();
 | 
			
		||||
  const field = useField();
 | 
			
		||||
  const { setVisible } = useActionContext();
 | 
			
		||||
  const { __parent } = useBlockRequestContext();
 | 
			
		||||
  const collection = useCollection_deprecated();
 | 
			
		||||
  const contextWe = useContextApprovalStatus();
 | 
			
		||||
  const status = useContextApprovalStatus();
 | 
			
		||||
  const apiClient = useAPIClient();
 | 
			
		||||
  const { workflow } = useFlowContext();
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    async run() {
 | 
			
		||||
    async run({ approvalStatus }) {
 | 
			
		||||
      try {
 | 
			
		||||
        from.submit();
 | 
			
		||||
        field.data = field.data || {};
 | 
			
		||||
@ -30,8 +32,8 @@ export function useSubmit() {
 | 
			
		||||
        await apiClient.resource('approvals').create({
 | 
			
		||||
          values: {
 | 
			
		||||
            collectionName: joinCollectionName(collection.dataSource, collection.name),
 | 
			
		||||
            data: from.values,
 | 
			
		||||
            status: contextWe,
 | 
			
		||||
            data: _.omit(from.values, [collection.getPrimaryKey()]),
 | 
			
		||||
            status: typeof approvalStatus !== 'undefined' ? approvalStatus : status,
 | 
			
		||||
            workflowId: workflow.id,
 | 
			
		||||
          },
 | 
			
		||||
        });
 | 
			
		||||
@ -43,7 +45,7 @@ export function useSubmit() {
 | 
			
		||||
        if (service) {
 | 
			
		||||
          service.refresh();
 | 
			
		||||
        }
 | 
			
		||||
      } catch (h) {
 | 
			
		||||
      } catch (error) {
 | 
			
		||||
        field.data && (field.data.loading = false);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@ -1,59 +1,14 @@
 | 
			
		||||
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';
 | 
			
		||||
import { useResubmit } from '../../../approval-common/Resubmit.provider';
 | 
			
		||||
 | 
			
		||||
// 重新发起
 | 
			
		||||
export function useActionResubmit() {
 | 
			
		||||
  const { refreshTable } = useHandleRefresh();
 | 
			
		||||
 | 
			
		||||
  const field = useField();
 | 
			
		||||
  const approval = useApproval();
 | 
			
		||||
  const api = useAPIClient();
 | 
			
		||||
 | 
			
		||||
  const { workflow } = useFlowContext();
 | 
			
		||||
  const { setResubmit } = useResubmit();
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      setResubmit(true);
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ import { useForm } from '@tachybase/schema';
 | 
			
		||||
import { useFlowContext } from '../../../../../../FlowContext';
 | 
			
		||||
import { ApprovalStatusEnumDict } from '../../../../constants';
 | 
			
		||||
import { useApproval } from '../../../approval-common/ApprovalData.provider';
 | 
			
		||||
import { useResubmit } from '../../../approval-common/Resubmit.provider';
 | 
			
		||||
import { useContextApprovalExecution } from '../../common/ApprovalExecution.provider';
 | 
			
		||||
 | 
			
		||||
export function useFormBlockProps() {
 | 
			
		||||
@ -13,11 +14,12 @@ export function useFormBlockProps() {
 | 
			
		||||
  const { workflow } = useFlowContext();
 | 
			
		||||
  const form = useForm();
 | 
			
		||||
  const { data } = useCurrentUserContext();
 | 
			
		||||
  const { isResubmit } = useResubmit();
 | 
			
		||||
 | 
			
		||||
  const { editable } = ApprovalStatusEnumDict[approval.status];
 | 
			
		||||
 | 
			
		||||
  const needEditable =
 | 
			
		||||
    editable &&
 | 
			
		||||
    (isResubmit || editable) &&
 | 
			
		||||
    approval?.latestExecutionId === approvalExecution.id &&
 | 
			
		||||
    approval?.createdById === data?.data.id &&
 | 
			
		||||
    workflow.enabled;
 | 
			
		||||
 | 
			
		||||
@ -5,12 +5,17 @@ import _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { useFlowContext } from '../../../../../../FlowContext';
 | 
			
		||||
import { useApproval } from '../../../approval-common/ApprovalData.provider';
 | 
			
		||||
import { useResubmit } from '../../../approval-common/Resubmit.provider';
 | 
			
		||||
import { useHandleRefresh } from '../../common/useHandleRefresh';
 | 
			
		||||
import { useCreateSubmit } from '../apply-button/hooks/useSubmit';
 | 
			
		||||
import { useContextApprovalStatus } from '../Pd.ApplyActionStatus';
 | 
			
		||||
 | 
			
		||||
export function useSubmit() {
 | 
			
		||||
  const { refreshTable } = useHandleRefresh();
 | 
			
		||||
  const apiClient = useAPIClient();
 | 
			
		||||
  const { isResubmit } = useResubmit();
 | 
			
		||||
  const { run: create } = useCreateSubmit();
 | 
			
		||||
  const status = useContextApprovalStatus();
 | 
			
		||||
 | 
			
		||||
  const form = useForm();
 | 
			
		||||
  const field = useField();
 | 
			
		||||
@ -20,7 +25,10 @@ export function useSubmit() {
 | 
			
		||||
  const contextApprovalStatus = useContextApprovalStatus();
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    async run() {
 | 
			
		||||
    async run(props) {
 | 
			
		||||
      if (isResubmit) {
 | 
			
		||||
        return await create({ approvalStatus: status });
 | 
			
		||||
      }
 | 
			
		||||
      try {
 | 
			
		||||
        form.submit();
 | 
			
		||||
        _.set(field, ['data', 'loading'], true);
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,17 @@
 | 
			
		||||
import React, { useContext, useState } from 'react';
 | 
			
		||||
 | 
			
		||||
interface ResubmitProps {
 | 
			
		||||
  isResubmit?: boolean;
 | 
			
		||||
  setResubmit?: any;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ResubmitContext = React.createContext<ResubmitProps>({});
 | 
			
		||||
 | 
			
		||||
export const ResubmitProvider = ({ children }) => {
 | 
			
		||||
  const [isResubmit, setResubmit] = useState(false);
 | 
			
		||||
  return <ResubmitContext.Provider value={{ isResubmit, setResubmit }}>{children}</ResubmitContext.Provider>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const useResubmit = () => {
 | 
			
		||||
  return useContext(ResubmitContext);
 | 
			
		||||
};
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
import actions, { utils } from '@tachybase/actions';
 | 
			
		||||
import { parseCollectionName } from '@tachybase/data-source-manager';
 | 
			
		||||
import { traverseJSON } from '@tachybase/database';
 | 
			
		||||
 | 
			
		||||
import { EXECUTION_STATUS, JOB_STATUS, PluginWorkflow } from '../..';
 | 
			
		||||
import { appends } from '../../../client/schemas/collection';
 | 
			
		||||
import { APPROVAL_ACTION_STATUS, APPROVAL_STATUS } from './constants';
 | 
			
		||||
import { getSummary } from './tools';
 | 
			
		||||
import { getAssociationName, jsonParse } from './utils';
 | 
			
		||||
@ -47,7 +47,7 @@ const approvals = {
 | 
			
		||||
    const { repository, model } = collection;
 | 
			
		||||
    const values = await repository.create({
 | 
			
		||||
      values: {
 | 
			
		||||
        ...data,
 | 
			
		||||
        ...traverseJSON(data, { collection }),
 | 
			
		||||
        createdBy: context.state.currentUser.id,
 | 
			
		||||
        updatedBy: context.state.currentUser.id,
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user