feat: approval, carbon copy (#1330)
Co-authored-by: wjh <wwwjh0710@163.com> Co-authored-by: sealday <zhanglin@daoyoucloud.com> Reviewed-on: daoyoucloud/tachybase#1330 Co-authored-by: bai.zixv <bai.zixv@foxmail.com> Co-committed-by: bai.zixv <bai.zixv@foxmail.com>
This commit is contained in:
parent
182d409c79
commit
f28a45cf7e
@ -0,0 +1,187 @@
|
||||
import { COLLECTION_NAME_APPROVAL_CARBON_COPY } from '../../common/constants';
|
||||
import { ApprovalStatusEnums } from '../constants';
|
||||
import { NAMESPACE, tval } from '../locale';
|
||||
import { approvalStatusOptions } from '../usage/approval-common/notice-columns/column.status';
|
||||
|
||||
export const CollectionApprovalCarbonCopy = {
|
||||
title: tval('ApprovalCarbonCopy'),
|
||||
name: COLLECTION_NAME_APPROVAL_CARBON_COPY,
|
||||
fields: [
|
||||
{
|
||||
type: 'bigInt',
|
||||
name: 'approvalId',
|
||||
interface: 'number',
|
||||
uiSchema: {
|
||||
type: 'number',
|
||||
title: 'ID',
|
||||
'x-component': 'InputNumber',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'createdBy',
|
||||
target: 'users',
|
||||
foreignKey: 'createdById',
|
||||
interface: 'm2o',
|
||||
uiSchema: {
|
||||
type: 'number',
|
||||
title: `{{t("Initiator", { ns: "${NAMESPACE}" })}}`,
|
||||
'x-component': 'RemoteSelect',
|
||||
'x-component-props': {
|
||||
fieldNames: {
|
||||
label: 'nickname',
|
||||
value: 'id',
|
||||
},
|
||||
service: {
|
||||
resource: 'users',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'bigInt',
|
||||
name: 'id',
|
||||
interface: 'number',
|
||||
uiSchema: {
|
||||
type: 'number',
|
||||
title: 'ID',
|
||||
'x-component': 'InputNumber',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'user',
|
||||
target: 'users',
|
||||
foreignKey: 'userId',
|
||||
interface: 'm2o',
|
||||
uiSchema: {
|
||||
type: 'number',
|
||||
title: tval('The Notified Person'),
|
||||
'x-component': 'RemoteSelect',
|
||||
'x-component-props': {
|
||||
fieldNames: {
|
||||
label: 'nickname',
|
||||
value: 'id',
|
||||
},
|
||||
service: { resource: 'users' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'node',
|
||||
target: 'flow_nodes',
|
||||
foreignKey: 'nodeId',
|
||||
interface: 'm2o',
|
||||
isAssociation: true,
|
||||
uiSchema: {
|
||||
type: 'number',
|
||||
title: `{{t("Task node", { ns: "${NAMESPACE}" })}}`,
|
||||
'x-component': 'RemoteSelect',
|
||||
'x-component-props': {
|
||||
fieldNames: {
|
||||
label: 'title',
|
||||
value: 'id',
|
||||
},
|
||||
service: {
|
||||
resource: 'flow_nodes',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'workflow',
|
||||
target: 'workflows',
|
||||
foreignKey: 'workflowId',
|
||||
interface: 'm2o',
|
||||
uiSchema: {
|
||||
type: 'number',
|
||||
title: '{{t("Workflow", { ns: "workflow" })}}',
|
||||
'x-component': 'RemoteSelect',
|
||||
'x-component-props': {
|
||||
fieldNames: {
|
||||
label: 'title',
|
||||
value: 'id',
|
||||
},
|
||||
service: { resource: 'workflows' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'integer',
|
||||
name: 'status',
|
||||
interface: 'select',
|
||||
uiSchema: {
|
||||
type: 'number',
|
||||
title: '{{t("Status", { ns: "workflow" })}}',
|
||||
'x-component': 'Select',
|
||||
enum: approvalStatusOptions,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'comment',
|
||||
interface: 'markdown',
|
||||
uiSchema: {
|
||||
type: 'string',
|
||||
'x-component': 'Markdown',
|
||||
title: `{{t("Comment", { ns: "${NAMESPACE}" })}}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
type: 'date',
|
||||
interface: 'createdAt',
|
||||
uiSchema: {
|
||||
type: 'datetime',
|
||||
title: '{{t("Created at")}}',
|
||||
'x-component': 'DatePicker',
|
||||
'x-component-props': {
|
||||
showTime: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
type: 'date',
|
||||
interface: 'updatedAt',
|
||||
uiSchema: {
|
||||
type: 'datetime',
|
||||
title: '{{t("Updated at")}}',
|
||||
'x-component': 'DatePicker',
|
||||
'x-component-props': {
|
||||
showTime: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'summary',
|
||||
interface: 'input',
|
||||
uiSchema: {
|
||||
type: 'string',
|
||||
title: tval('Summary'),
|
||||
'x-component': 'ApprovalsSummary',
|
||||
'x-component-props': {
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'integer',
|
||||
name: 'status',
|
||||
interface: 'select',
|
||||
uiSchema: {
|
||||
type: 'number',
|
||||
title: '{{t("Status", { ns: "workflow" })}}',
|
||||
'x-component': 'Select',
|
||||
enum: ApprovalStatusEnums,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
import { ApprovalStatusEnums } from '../../constants';
|
||||
import { NAMESPACE, tval } from '../../locale';
|
||||
import { ApprovalStatusEnums } from '../constants';
|
||||
import { NAMESPACE, tval } from '../locale';
|
||||
|
||||
export const CollectionApprovals = {
|
||||
title: `{{t("Approval applications", { ns: "${NAMESPACE}" })}}`,
|
@ -1,7 +1,7 @@
|
||||
import { NAMESPACE } from '../../locale';
|
||||
import { tval } from '../locale';
|
||||
|
||||
export const CollectionFlowNodes = {
|
||||
title: `{{t("Node", { ns: "${NAMESPACE}" })}}`,
|
||||
title: tval('Node'),
|
||||
name: 'flow_nodes',
|
||||
fields: [
|
||||
{
|
@ -1,4 +1,4 @@
|
||||
import { NAMESPACE } from '../../locale';
|
||||
import { NAMESPACE } from '../locale';
|
||||
|
||||
export const CollectionWorkflows = {
|
||||
title: `{{t("Workflow", { ns: "${NAMESPACE}" })}}`,
|
@ -11,7 +11,7 @@ import { useForm } from '@tachybase/schema';
|
||||
import { uid } from '@tachybase/utils/client';
|
||||
|
||||
import { useFlowContext } from '../../../../../FlowContext';
|
||||
import { CollectionApprovalTodos } from '../../../common/ApprovalTodos.collection';
|
||||
import { CollectionApprovalTodos } from '../../../collections/approvalRecords.collection';
|
||||
import { NAMESPACE } from '../../../locale';
|
||||
import { SchemaAddBlock } from '../../trigger/launcher-interface/SchemaAddBlock.component';
|
||||
import { ApproverBlock } from './ApproverBlock.view';
|
@ -0,0 +1,89 @@
|
||||
import { ArrayItems } from '@tachybase/components';
|
||||
|
||||
import { GroupType, Instruction } from '../../../../nodes';
|
||||
import { COLLECTION_NAME_APPROVAL_CARBON_COPY } from '../../../common/constants';
|
||||
import { tval } from '../../locale';
|
||||
import { AdditionNotifiedPerson } from './config-items/AddNotifiedPerson.view';
|
||||
import { ConfigButtonNotice } from './config-items/ConfigButtonNotice.view';
|
||||
import { SelectNotifiedPerson } from './config-items/SelectNotifiedPerson.view';
|
||||
import { CarbonCopyDetailContainer } from './show-interface/CarbonCopyDetailContainer.schema';
|
||||
import { configSytles } from './style';
|
||||
|
||||
export class ApprovalCarbonCopyInstruction extends Instruction {
|
||||
title = tval('ApprovalCarbonCopy');
|
||||
type = COLLECTION_NAME_APPROVAL_CARBON_COPY;
|
||||
group: GroupType = 'extended';
|
||||
description = tval(
|
||||
'In the workflow, notification messages can be viewed by the notified person in the notification center.',
|
||||
);
|
||||
components = {
|
||||
ArrayItems: ArrayItems,
|
||||
SelectNotifiedPerson: SelectNotifiedPerson,
|
||||
AdditionNotifiedPerson: AdditionNotifiedPerson,
|
||||
ConfigButtonNotice: ConfigButtonNotice,
|
||||
CarbonCopyDetail: CarbonCopyDetailContainer,
|
||||
};
|
||||
fieldset = {
|
||||
carbonCopyPerson: {
|
||||
type: 'array',
|
||||
title: tval('The Notified Person'),
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayItems',
|
||||
'x-component-props': {
|
||||
className: configSytles.ArrayItemsStyle,
|
||||
},
|
||||
|
||||
items: {
|
||||
type: 'void',
|
||||
'x-component': 'Space',
|
||||
'x-component-props': {
|
||||
className: configSytles.SpaceStyle,
|
||||
},
|
||||
properties: {
|
||||
sort: {
|
||||
type: 'void',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayItems.SortHandle',
|
||||
},
|
||||
input: {
|
||||
type: 'string',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'SelectNotifiedPerson',
|
||||
},
|
||||
remove: {
|
||||
type: 'void',
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ArrayItems.Remove',
|
||||
},
|
||||
},
|
||||
},
|
||||
properties: {
|
||||
add: {
|
||||
type: 'void',
|
||||
title: tval('Add Notified Person'),
|
||||
// HACK: 这里原本的框架逻辑, 解析竟然和 Addition 这个单词绑定, 奇怪
|
||||
// 类似的还有, Index, Remove, MoveUp, MoveDown, Copy
|
||||
// isOperationComponent() 针对这类操作组件, 做了特殊逻辑
|
||||
'x-component': 'AdditionNotifiedPerson',
|
||||
},
|
||||
},
|
||||
},
|
||||
showCarbonCopyDetail: {
|
||||
type: 'void',
|
||||
title: tval('Show Notice Detail'),
|
||||
required: true,
|
||||
'x-decorator': 'FormItem',
|
||||
'x-component': 'ConfigButtonNotice',
|
||||
properties: {
|
||||
showCarbonCopyDetail: {
|
||||
type: 'void',
|
||||
'x-component': 'CarbonCopyDetail',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
isAvailable(params) {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { ArrayItems } from '@tachybase/components';
|
||||
|
||||
import { Button, Popover, Space } from 'antd';
|
||||
|
||||
import { useWorkflowExecuted } from '../../../../../hooks';
|
||||
import { useTranslation } from '../../../locale';
|
||||
|
||||
export const AdditionNotifiedPerson = () => {
|
||||
const isWorkflowExecuted = useWorkflowExecuted();
|
||||
const array = ArrayItems.useArray() || { field: [] };
|
||||
const { t } = useTranslation();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const onSlecet = useCallback(() => {
|
||||
array.field.push('');
|
||||
setIsOpen(false);
|
||||
}, [array.field]);
|
||||
|
||||
const onQuery = useCallback(() => {
|
||||
array.field.push({ filter: {} });
|
||||
setIsOpen(false);
|
||||
}, [array.field]);
|
||||
|
||||
return (
|
||||
<Popover
|
||||
open={!isWorkflowExecuted && isOpen}
|
||||
onOpenChange={setIsOpen}
|
||||
content={<PopoverContent onSlecet={onSlecet} onQuery={onQuery} />}
|
||||
>
|
||||
<Button className="ant-formily-array-base-addition" type="dashed" block={true} disabled={isWorkflowExecuted}>
|
||||
{t('Add Notified Person')}
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
// Child Component
|
||||
const PopoverContent = ({ onSlecet, onQuery }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Space direction="vertical" size="small">
|
||||
<Button type="text" onClick={onSlecet}>
|
||||
{t('Select Notified Person')}
|
||||
</Button>
|
||||
<Button type="text" onClick={onQuery}>
|
||||
{t('Query Notified Person')}
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { ActionContextProvider } from '@tachybase/client';
|
||||
|
||||
import { Button } from 'antd';
|
||||
|
||||
import { useTranslation } from '../../../locale';
|
||||
|
||||
export const ConfigButtonNotice = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const handleButtonClick = useCallback(() => {
|
||||
setVisible(true);
|
||||
}, [setVisible]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type="primary" onClick={handleButtonClick} disabled={false}>
|
||||
{t('Go to configure')}
|
||||
</Button>
|
||||
<ActionContextProvider value={{ visible, setVisible, formValueChanged: false }}>
|
||||
{props.children}
|
||||
</ActionContextProvider>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import { RemoteSelect, SchemaComponent, useCollectionFilterOptions, useToken, Variable } from '@tachybase/client';
|
||||
import { useField } from '@tachybase/schema';
|
||||
|
||||
import { FilterDynamicComponent } from '../../../../../components';
|
||||
import { useWorkflowVariableOptions } from '../../../../../variable';
|
||||
|
||||
// XXX: 可以将这个选择组件封装为一个通用组件, 放在 workflow 下提供使用
|
||||
export const SelectNotifiedPerson = (props) => {
|
||||
if (props.value && typeof props.value === 'object') {
|
||||
return <SelectNotifiedPersonCustom {...props} />;
|
||||
} else {
|
||||
return <SelectNotifiedPersonNormal {...props} />;
|
||||
}
|
||||
};
|
||||
|
||||
const SelectNotifiedPersonNormal = ({ value, onChange }) => {
|
||||
const scope = useWorkflowVariableOptions({ types: [isUserKeyField] });
|
||||
return (
|
||||
<Variable.Input scope={scope} value={value} onChange={onChange}>
|
||||
<RemoteSelect
|
||||
fieldNames={{ label: 'nickname', value: 'id' }}
|
||||
service={{ resource: 'users' }}
|
||||
manual={false}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</Variable.Input>
|
||||
);
|
||||
};
|
||||
|
||||
const SelectNotifiedPersonCustom = () => {
|
||||
const field = useField();
|
||||
const currentFormFields = useCollectionFilterOptions('users');
|
||||
const { token } = useToken();
|
||||
return (
|
||||
<div style={{ border: `1px dashed ${token.colorBorder}`, padding: token.paddingSM }}>
|
||||
<SchemaComponent
|
||||
basePath={field.address}
|
||||
schema={{
|
||||
type: 'void',
|
||||
properties: {
|
||||
filter: {
|
||||
type: 'object',
|
||||
'x-component': 'Filter',
|
||||
'x-component-props': {
|
||||
options: currentFormFields,
|
||||
dynamicComponent: FilterDynamicComponent,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function isUserKeyField(field) {
|
||||
if (field.isForeignKey) {
|
||||
return field.target === 'users';
|
||||
} else {
|
||||
return field.collectionName === 'users' && field.name === 'id';
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import { Plugin } from '@tachybase/client';
|
||||
|
||||
// import { ArrayItems } from '@tachybase/components';
|
||||
|
||||
import { PluginWorkflow } from '../../../../Plugin';
|
||||
import { COLLECTION_NAME_APPROVAL_CARBON_COPY } from '../../../common/constants';
|
||||
import { ApprovalCarbonCopyInstruction } from './ApprovalCarbonCopy.instruction';
|
||||
import { SCCarbonCopyDetail } from './show-interface/CarbonCopyDetail.schema';
|
||||
|
||||
// NOTE: 试着用一种新的约束规则去声明文件属于哪种功能. 比如 kit.ts 导出的是单个 Plugin 下的功能集成
|
||||
// 注册一个节点
|
||||
export class KitCarbonCopy extends Plugin {
|
||||
async afterAdd() {
|
||||
await this.app.pm.add(SCCarbonCopyDetail);
|
||||
}
|
||||
|
||||
async load() {
|
||||
const pluginWorkflow = this.app.pm.get(PluginWorkflow);
|
||||
pluginWorkflow.registerInstruction(COLLECTION_NAME_APPROVAL_CARBON_COPY, ApprovalCarbonCopyInstruction);
|
||||
|
||||
// this.app.addComponents({
|
||||
// ArrayItems,
|
||||
// });
|
||||
// this.app.schemaInitializerManager.add(ApproverActionConfigInitializer);
|
||||
// this.app.schemaInitializerManager.add(ApproverAddBlockInitializer);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
createReadPrettyFormBlockSchema,
|
||||
parseCollectionName,
|
||||
SchemaInitializerItem,
|
||||
useRecordCollectionDataSourceItems,
|
||||
useSchemaInitializer,
|
||||
useSchemaInitializerItem,
|
||||
useSchemaTemplateManager,
|
||||
} from '@tachybase/client';
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import { useFlowContext } from '../../../../../FlowContext';
|
||||
|
||||
export const CarbonCopyDetailAddBlock = () => {
|
||||
const { workflow } = useFlowContext();
|
||||
const [dataSourceName, collection] = parseCollectionName(workflow.config?.collection);
|
||||
const itemConfig = useSchemaInitializerItem();
|
||||
const { insert } = useSchemaInitializer();
|
||||
const { getTemplateSchemaByMode } = useSchemaTemplateManager();
|
||||
const items = useRecordCollectionDataSourceItems('FormItem');
|
||||
const handleClick = async ({ item }) => {
|
||||
const template = item.template ? await getTemplateSchemaByMode(item) : null;
|
||||
const schema = createReadPrettyFormBlockSchema({
|
||||
actionInitializers: null,
|
||||
resource: collection,
|
||||
collection,
|
||||
dataSource: dataSourceName,
|
||||
template,
|
||||
settings: 'blockSettings:details',
|
||||
});
|
||||
|
||||
const shcemaWithPatch = {};
|
||||
|
||||
// REVIEW: 删除这个属性的必要性, 需要再思考下
|
||||
_.unset(schema, ['x-acl-action', 'x-acl-action-props']);
|
||||
|
||||
const [childSchemaKey] = Object.keys(schema.properties);
|
||||
|
||||
_.set(schema.properties[childSchemaKey], 'x-component-props.useProps', 'usePropsNoticeDetail');
|
||||
|
||||
insert(schema);
|
||||
};
|
||||
return <SchemaInitializerItem {...itemConfig} onClick={handleClick} items={items} />;
|
||||
};
|
@ -0,0 +1,75 @@
|
||||
import { gridRowColWrap, SchemaInitializer, usePlugin } from '@tachybase/client';
|
||||
|
||||
import { useFlowContext } from '../../../../../FlowContext';
|
||||
import { useAvailableUpstreams, useNodeContext } from '../../../../../nodes';
|
||||
import { PluginWorkflow } from '../../../../../Plugin';
|
||||
import { useTrigger } from '../../../../../triggers';
|
||||
import { tval } from '../../../locale';
|
||||
import { CarbonCopyDetailAddBlock } from './CarbonCopyDetail.block';
|
||||
|
||||
export const CarbonCopyDetailInitializer = new SchemaInitializer({
|
||||
name: 'CarbonCopyDetailInitializer',
|
||||
wrap: gridRowColWrap,
|
||||
title: "{{t('Add block')}}",
|
||||
items: [
|
||||
{
|
||||
name: 'notice',
|
||||
type: 'itemGroup',
|
||||
title: tval('Notice blocks'),
|
||||
children: [
|
||||
{
|
||||
name: 'detail',
|
||||
type: 'item',
|
||||
title: '{{t("Details")}}',
|
||||
Component: CarbonCopyDetailAddBlock,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'itemGroup',
|
||||
name: 'dataBlocks',
|
||||
title: '{{t("Data blocks")}}',
|
||||
checkChildrenLength: true,
|
||||
useChildren() {
|
||||
const workflowPlugin = usePlugin(PluginWorkflow);
|
||||
const { workflow } = useFlowContext();
|
||||
const trigger = useTrigger();
|
||||
const currentNodeContext = useNodeContext();
|
||||
const nodes = useAvailableUpstreams(currentNodeContext);
|
||||
const triggerInitializers = [trigger.useInitializers?.call(trigger, workflow.config)].filter(Boolean);
|
||||
const nodeBlockInitializers = nodes
|
||||
.map((node) => {
|
||||
const instruction = workflowPlugin.instructions.get(node.type);
|
||||
return instruction?.useInitializers?.call(instruction, node);
|
||||
})
|
||||
.filter(Boolean);
|
||||
return [
|
||||
...triggerInitializers,
|
||||
...(nodeBlockInitializers.length
|
||||
? [
|
||||
{
|
||||
name: 'nodes',
|
||||
type: 'subMenu',
|
||||
title: '{{t("Node result", { ns: "workflow" })}}',
|
||||
children: nodeBlockInitializers,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
].filter(Boolean);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'others',
|
||||
type: 'itemGroup',
|
||||
title: '{{t("Other blocks")}}',
|
||||
children: [
|
||||
{
|
||||
name: 'markdown',
|
||||
type: 'item',
|
||||
title: '{{t("Markdown")}}',
|
||||
component: 'MarkdownBlockInitializer',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
@ -0,0 +1,86 @@
|
||||
import React from 'react';
|
||||
import { Plugin, SchemaComponent, useAPIClient, useRequest } from '@tachybase/client';
|
||||
import { useForm } from '@tachybase/schema';
|
||||
import { uid } from '@tachybase/utils/client';
|
||||
|
||||
import { Spin } from 'antd';
|
||||
|
||||
import { CarbonCopyDetailInitializer } from './CarbonCopyDetail.initializer';
|
||||
|
||||
export class SCCarbonCopyDetail extends Plugin {
|
||||
async load() {
|
||||
this.app.schemaInitializerManager.add(CarbonCopyDetailInitializer);
|
||||
}
|
||||
}
|
||||
|
||||
function createNewSchema(id) {
|
||||
return {
|
||||
type: 'void',
|
||||
name: id,
|
||||
'x-uid': id,
|
||||
'x-component': 'Grid',
|
||||
'x-initializer': 'CarbonCopyDetailInitializer',
|
||||
};
|
||||
}
|
||||
|
||||
export const CarbonCopyDetail = (props) => {
|
||||
const { schemaData, loading, handleChange } = useAction(props);
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
return (
|
||||
<SchemaComponent
|
||||
memoized={true}
|
||||
scope={
|
||||
{
|
||||
// useSubmit: () => {
|
||||
// return { run() {} };
|
||||
// },
|
||||
// useApprovalFormBlockProps,
|
||||
// useDetailsBlockProps: useFormBlockContext,
|
||||
}
|
||||
}
|
||||
components={{}}
|
||||
schema={schemaData as any}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
function useAction(props) {
|
||||
const { value: srcID, onChange } = props;
|
||||
const apiClient = useAPIClient();
|
||||
const { values, setValuesIn } = useForm();
|
||||
const { data: schemaData, loading } = useRequest(async () => {
|
||||
if (srcID) {
|
||||
const remoteSchema = await fetchSchema(apiClient, srcID);
|
||||
if (remoteSchema['x-uid'] === srcID) {
|
||||
return remoteSchema;
|
||||
}
|
||||
}
|
||||
const id = srcID ?? uid();
|
||||
const newSchema = createNewSchema(id);
|
||||
await instertSchema(apiClient, newSchema);
|
||||
onChange(id);
|
||||
return newSchema;
|
||||
});
|
||||
|
||||
const handleChange = (data) => {};
|
||||
|
||||
return {
|
||||
schemaData,
|
||||
loading,
|
||||
handleChange,
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchSchema(apiClient, srcID): Promise<object> {
|
||||
const { data: serviceData } = await apiClient.request({ url: `uiSchemas:getJsonSchema/${srcID}` });
|
||||
|
||||
const remoteSchema = serviceData.data ?? {};
|
||||
return remoteSchema;
|
||||
}
|
||||
|
||||
async function instertSchema(apiClient, schema): Promise<void> {
|
||||
await apiClient.resource('uiSchemas').insert({ values: schema });
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
import React, { useContext, useMemo, useState } from 'react';
|
||||
import { SchemaComponent, SchemaComponentContext } from '@tachybase/client';
|
||||
import { uid } from '@tachybase/utils/client';
|
||||
|
||||
import { useFlowContext } from '../../../../../FlowContext';
|
||||
import { tval } from '../../../locale';
|
||||
import { CarbonCopyDetail } from './CarbonCopyDetail.schema';
|
||||
import { schemaStyle } from './style';
|
||||
|
||||
export const CarbonCopyDetailContainer = () => {
|
||||
const context = useContext(SchemaComponentContext);
|
||||
const [, setId] = useState(uid());
|
||||
const { workflow } = useFlowContext();
|
||||
|
||||
const contextValue = useMemo(() => {
|
||||
return {
|
||||
...context,
|
||||
refresh: () => setId(uid()),
|
||||
designable: !workflow.executed,
|
||||
};
|
||||
}, [context, workflow.executed]);
|
||||
|
||||
return (
|
||||
<SchemaComponentContext.Provider value={contextValue}>
|
||||
<SchemaComponent components={{ CarbonCopyDetail: CarbonCopyDetail }} schema={schema} />
|
||||
</SchemaComponentContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const schema = {
|
||||
name: 'drawer',
|
||||
type: 'void',
|
||||
title: tval('The interface of show notice detail'),
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-component-props': {
|
||||
className: schemaStyle.ActionStyle,
|
||||
},
|
||||
properties: {
|
||||
showCarbonCopyDetail: {
|
||||
type: 'string',
|
||||
'x-component': 'CarbonCopyDetail',
|
||||
},
|
||||
},
|
||||
};
|
@ -0,0 +1,9 @@
|
||||
import { css } from '@tachybase/client';
|
||||
|
||||
export const schemaStyle = {
|
||||
ActionStyle: css`
|
||||
.ant-drawer-body {
|
||||
background: var(--nb-box-bg);
|
||||
}
|
||||
`,
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
import { createStyles, css } from '@tachybase/client';
|
||||
|
||||
export const useStyles = createStyles({});
|
||||
|
||||
export const configSytles = {
|
||||
ArrayItemsStyle: css`
|
||||
&[disabled] {
|
||||
> .ant-formily-array-base-addition {
|
||||
display: none;
|
||||
}
|
||||
> .ant-formily-array-items-item .ant-space-item:not(:nth-child(2)) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
||||
SpaceStyle: css`
|
||||
width: 100%;
|
||||
&.ant-space.ant-space-horizontal {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
> .ant-space-item:nth-child(2) {
|
||||
flex-grow: 1;
|
||||
}
|
||||
`,
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
import { Plugin } from '@tachybase/client';
|
||||
|
||||
import { KitApprovalInstruction } from './instruction/plugin';
|
||||
import { KitApprovalInstruction } from './instruction-approval/kit';
|
||||
import { KitApprovalTrigger } from './trigger/plugin';
|
||||
|
||||
export default class KitApprovalConfiguration extends Plugin {
|
||||
|
@ -1,11 +1,13 @@
|
||||
import { Plugin } from '@tachybase/client';
|
||||
|
||||
import { KitCarbonCopy } from './configuration/instruction-carbon-copy/kit';
|
||||
import KitApprovalConfiguration from './configuration/plugin';
|
||||
import { KitApprovalUsage } from './usage/plugin';
|
||||
|
||||
export class PluginApproval extends Plugin {
|
||||
async afterAdd() {
|
||||
this.pm.add(KitApprovalConfiguration);
|
||||
this.pm.add(KitCarbonCopy);
|
||||
this.pm.add(KitApprovalUsage);
|
||||
}
|
||||
async beforeLoad() {}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { ExtendCollectionsProvider, TableBlockProvider, useRecord } from '@tachybase/client';
|
||||
|
||||
import { CollectionApprovalTodos } from '../../common/ApprovalTodos.collection';
|
||||
import { CollectionApprovals } from '../approval-common/Approvals.collection';
|
||||
import { CollectionFlowNodes } from '../approval-common/FlowNodes.collection';
|
||||
import { CollectionWorkflows } from '../approval-common/Workflows.collection';
|
||||
import { CollectionApprovalTodos } from '../../collections/approvalRecords.collection';
|
||||
import { CollectionApprovals } from '../../collections/approvals.collection';
|
||||
import { CollectionFlowNodes } from '../../collections/flowNodes.collection';
|
||||
import { CollectionWorkflows } from '../../collections/workflows.collection';
|
||||
|
||||
export const ApprovalBlockProvider = ({ children, ...props }) => {
|
||||
const {
|
||||
|
@ -4,8 +4,11 @@ import { uid } from '@tachybase/utils/client';
|
||||
|
||||
import { TableOutlined } from '@ant-design/icons';
|
||||
|
||||
import { COLLECTION_NAME_APPROVAL_CARBON_COPY } from '../../../common/constants';
|
||||
import { NAMESPACE } from '../../locale';
|
||||
import { ApprovalBlockProvider } from './ApprovalBlock.provider';
|
||||
import { CarbonCopyBlockProvider } from './carbon-copy/CarbonCopyBlock.provider';
|
||||
import { CarbonCopyCenter } from './carbon-copy/CarbonCopyCenter.schema';
|
||||
import { ApprovalBlockLaunch } from './launch/VC.ApprovalBlockLaunch';
|
||||
import { ApprovalBlockLaunchApplication } from './launch/VC.ApprovalBlockLaunchApplication';
|
||||
import { ApprovalBlockTodos } from './todos/VC.ApprovalBlockTodos';
|
||||
@ -17,6 +20,8 @@ export class SCApprovalBlock extends Plugin {
|
||||
'ApprovalBlock.Launch': ApprovalBlockLaunch,
|
||||
'ApprovalBlock.Launch.Application': ApprovalBlockLaunchApplication,
|
||||
'ApprovalBlock.Todos': ApprovalBlockTodos,
|
||||
CarbonCopyBlockProvider: CarbonCopyBlockProvider,
|
||||
CarbonCopyCenter: CarbonCopyCenter,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -70,23 +75,61 @@ const schemaItems = [
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'item',
|
||||
title: `{{t("CarbonCopy", { ns: "${NAMESPACE}" })}}`,
|
||||
'x-decorator': 'CarbonCopyBlockProvider',
|
||||
'x-component': 'CarbonCopyCenter',
|
||||
'x-toolbar': 'BlockSchemaToolbar',
|
||||
'x-settings': 'blockSettings:table',
|
||||
collection: COLLECTION_NAME_APPROVAL_CARBON_COPY,
|
||||
params: {
|
||||
appends: [
|
||||
'createdBy.id',
|
||||
'createdBy.nickname',
|
||||
'approval.status',
|
||||
'user.id',
|
||||
'user.nickname',
|
||||
'node.id',
|
||||
'node.title',
|
||||
'job.id',
|
||||
'job.status',
|
||||
'job.result',
|
||||
'workflow.id',
|
||||
'workflow.title',
|
||||
'workflow.enabled',
|
||||
'execution.id',
|
||||
'execution.status',
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const getSchemaInsert = ({ item }) => {
|
||||
const id = uid();
|
||||
const { collection, params, ['x-component']: xcomponent, action } = item;
|
||||
const {
|
||||
collection,
|
||||
params,
|
||||
['x-component']: xcomponent,
|
||||
action,
|
||||
['x-decorator']: decorator,
|
||||
['x-toolbar']: toolbar,
|
||||
['x-settings']: settings,
|
||||
} = item;
|
||||
return {
|
||||
type: 'void',
|
||||
name: id,
|
||||
'x-uid': id,
|
||||
'x-designer': 'TableBlockDesigner',
|
||||
'x-decorator': 'ApprovalBlock.Decorator',
|
||||
'x-decorator': decorator || 'ApprovalBlock.Decorator',
|
||||
'x-decorator-props': {
|
||||
collection,
|
||||
params,
|
||||
action: action || 'listCentralized',
|
||||
},
|
||||
'x-component': 'CardItem',
|
||||
'x-toolbar': toolbar,
|
||||
'x-settings': settings,
|
||||
properties: {
|
||||
block: {
|
||||
type: 'void',
|
||||
|
@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import { ExtendCollectionsProvider, TableBlockProvider, useRecord } from '@tachybase/client';
|
||||
|
||||
import { CollectionApprovalCarbonCopy } from '../../../collections/approvalCarbonCopy.collection';
|
||||
import { CollectionFlowNodes } from '../../../collections/flowNodes.collection';
|
||||
import { CollectionWorkflows } from '../../../collections/workflows.collection';
|
||||
|
||||
export const CarbonCopyBlockProvider = ({ children, ...props }) => {
|
||||
const {
|
||||
collection,
|
||||
params = {
|
||||
filter: {},
|
||||
},
|
||||
action = 'list',
|
||||
} = props;
|
||||
|
||||
const record = useRecord();
|
||||
const config = {
|
||||
collection,
|
||||
resource: collection,
|
||||
action,
|
||||
params: {
|
||||
pageSize: 20,
|
||||
sort: ['-createdAt'],
|
||||
...params,
|
||||
filter: record?.id
|
||||
? {
|
||||
dataKey: `${record.id}`,
|
||||
...params.filter,
|
||||
}
|
||||
: { ...params.filter },
|
||||
},
|
||||
rowKey: 'id',
|
||||
showIndex: true,
|
||||
dragSort: false,
|
||||
};
|
||||
|
||||
return (
|
||||
<ExtendCollectionsProvider collections={[CollectionWorkflows, CollectionFlowNodes, CollectionApprovalCarbonCopy]}>
|
||||
<TableBlockProvider name={collection} {...config}>
|
||||
{children}
|
||||
</TableBlockProvider>
|
||||
</ExtendCollectionsProvider>
|
||||
);
|
||||
};
|
@ -0,0 +1,217 @@
|
||||
import React from 'react';
|
||||
import { css, SchemaComponent } from '@tachybase/client';
|
||||
|
||||
import { NAMESPACE, tval } from '../../../locale';
|
||||
import { ColumnApprovalStatus } from '../../approval-common/approval-columns/approvalStatus.column';
|
||||
import { ColumnAction } from '../../approval-common/notice-columns/column.action';
|
||||
import { ColumnNode } from '../../approval-common/notice-columns/column.node';
|
||||
import { ColumnStatus } from '../../approval-common/notice-columns/column.status';
|
||||
import { ColumnUser } from '../../approval-common/notice-columns/column.user';
|
||||
import { ColumnWorkflow } from '../../approval-common/notice-columns/column.workflow';
|
||||
|
||||
const schemaStyles = {
|
||||
ActionbarStyle: css`
|
||||
margin-bottom: 16px;
|
||||
`,
|
||||
TableV2Style: css`
|
||||
.ant-table-cell {
|
||||
text-align: center;
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
export const CarbonCopyCenter = () => (
|
||||
<SchemaComponent
|
||||
components={{
|
||||
ColumnNode,
|
||||
ColumnWorkflow,
|
||||
ColumnUser,
|
||||
ColumnStatus,
|
||||
ColumnAction,
|
||||
ColumnApprovalStatus,
|
||||
}}
|
||||
schema={schema}
|
||||
/>
|
||||
);
|
||||
|
||||
const schema = {
|
||||
type: 'void',
|
||||
name: 'approval-carbon-copy-center',
|
||||
properties: {
|
||||
actions: {
|
||||
type: 'void',
|
||||
'x-component': 'ActionBar',
|
||||
'x-component-props': {
|
||||
className: schemaStyles.ActionbarStyle,
|
||||
},
|
||||
properties: {
|
||||
filter: {
|
||||
type: 'void',
|
||||
title: '{{ t("Filter") }}',
|
||||
'x-action': 'filter',
|
||||
'x-designer': 'Filter.Action.Designer',
|
||||
'x-component': 'Filter.Action',
|
||||
'x-use-component-props': 'useFilterActionProps',
|
||||
'x-component-props': {
|
||||
icon: 'FilterOutlined',
|
||||
},
|
||||
'x-align': 'left',
|
||||
},
|
||||
refresher: {
|
||||
type: 'void',
|
||||
title: '{{ t("Refresh") }}',
|
||||
'x-action': 'refresh',
|
||||
'x-component': 'Action',
|
||||
'x-use-component-props': 'useRefreshActionProps',
|
||||
'x-designer': 'Action.Designer',
|
||||
'x-component-props': { icon: 'ReloadOutlined' },
|
||||
'x-align': 'right',
|
||||
},
|
||||
},
|
||||
},
|
||||
table: {
|
||||
type: 'array',
|
||||
'x-component': 'TableV2',
|
||||
'x-use-component-props': 'useTableBlockProps',
|
||||
'x-component-props': {
|
||||
rowKey: 'id',
|
||||
tableLayout: 'fixed',
|
||||
className: css`
|
||||
.ant-table-cell {
|
||||
text-align: center;
|
||||
}
|
||||
`,
|
||||
},
|
||||
properties: {
|
||||
action: {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableV2.Column.Decorator',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-component-props': {
|
||||
width: 60,
|
||||
},
|
||||
title: '{{t("Actions")}}',
|
||||
properties: {
|
||||
action: {
|
||||
'x-component': 'ColumnAction',
|
||||
},
|
||||
},
|
||||
},
|
||||
approvalId: {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableV2.Column.Decorator',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-component-props': {
|
||||
width: 80,
|
||||
},
|
||||
title: tval('ID'),
|
||||
properties: {
|
||||
approvalId: {
|
||||
type: 'number',
|
||||
'x-component': 'Input',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
},
|
||||
},
|
||||
createdAt: {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableV2.Column.Decorator',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-component-props': { width: 160 },
|
||||
properties: {
|
||||
createdAt: {
|
||||
type: 'string',
|
||||
'x-component': 'CollectionField',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
},
|
||||
},
|
||||
summary: {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableV2.Column.Decorator',
|
||||
'x-component': 'TableV2.Column',
|
||||
title: tval('Summary'),
|
||||
properties: {
|
||||
summary: {
|
||||
type: 'string',
|
||||
'x-component': 'CollectionField',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
},
|
||||
},
|
||||
createdBy: {
|
||||
type: 'void',
|
||||
title: tval('Initiator'),
|
||||
'x-decorator': 'TableV2.Column.Decorator',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-component-props': { width: 160 },
|
||||
properties: {
|
||||
createdBy: {
|
||||
type: 'string',
|
||||
'x-component': 'ColumnUser',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
},
|
||||
},
|
||||
user: {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableV2.Column.Decorator',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-component-props': { width: 140 },
|
||||
title: tval('The Notified Person'),
|
||||
properties: {
|
||||
user: {
|
||||
'x-component': 'ColumnUser',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
},
|
||||
},
|
||||
status: {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableV2.Column.Decorator',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-component-props': {
|
||||
width: 100,
|
||||
},
|
||||
title: '{{t("Status", { ns: "workflow" })}}',
|
||||
properties: {
|
||||
status: {
|
||||
'x-component': 'ColumnApprovalStatus',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
},
|
||||
},
|
||||
workflow: {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableV2.Column.Decorator',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-component-props': {
|
||||
width: null,
|
||||
},
|
||||
title: '{{t("Workflow", { ns: "workflow" })}}',
|
||||
properties: {
|
||||
workflow: {
|
||||
'x-component': 'ColumnWorkflow',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
},
|
||||
},
|
||||
node: {
|
||||
type: 'void',
|
||||
'x-decorator': 'TableV2.Column.Decorator',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-component-props': {
|
||||
width: null,
|
||||
},
|
||||
title: `{{t("Task node", { ns: "${NAMESPACE}" })}}`,
|
||||
properties: {
|
||||
node: {
|
||||
'x-component': 'ColumnNode',
|
||||
'x-read-pretty': true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
@ -74,7 +74,7 @@ export const SchemaApprovalBlockLaunch = {
|
||||
'x-decorator': 'TableV2.Column.Decorator',
|
||||
'x-component': 'TableV2.Column',
|
||||
'x-component-props': { width: 80 },
|
||||
title: '{{t("ID")}}',
|
||||
title: tval('ID'),
|
||||
properties: {
|
||||
id: {
|
||||
type: 'number',
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { SchemaComponent } from '@tachybase/client';
|
||||
|
||||
import { NodeColumn } from '../../approval-common/approval-columns/column.node';
|
||||
import { UserColumn } from '../../approval-common/approval-columns/column.user';
|
||||
import { WorkflowColumn } from '../../approval-common/approval-columns/column.workflow';
|
||||
import { NodeColumn } from '../../approval-common/approval-columns/node.column';
|
||||
import { UserColumn } from '../../approval-common/approval-columns/user.column';
|
||||
import { WorkflowColumn } from '../../approval-common/approval-columns/workflow.column';
|
||||
import { ApplyButton } from './apply-button/VC.ApplyButton';
|
||||
import { SchemaApprovalBlockLaunch } from './Sm.ApprovalBlockLaunch';
|
||||
|
||||
|
@ -61,7 +61,7 @@ export const ApprovalBlockLaunchApplication = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
run();
|
||||
}, []);
|
||||
}, [decorator?.params?.filter]);
|
||||
|
||||
const onClick = useCallback(
|
||||
(targetItems) => {
|
||||
|
@ -66,7 +66,7 @@ export const SchemaApprovalBlockTodos = {
|
||||
'x-component-props': {
|
||||
width: 80,
|
||||
},
|
||||
title: '{{t("ID")}}',
|
||||
title: tval('ID'),
|
||||
properties: {
|
||||
approvalId: {
|
||||
type: 'number',
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { SchemaComponent } from '@tachybase/client';
|
||||
|
||||
import { NodeColumn } from '../../approval-common/approval-columns/column.node';
|
||||
import { ApprovalRecordStatusColumn } from '../../approval-common/approval-columns/column.status';
|
||||
import { UserColumn } from '../../approval-common/approval-columns/column.user';
|
||||
import { WorkflowColumn } from '../../approval-common/approval-columns/column.workflow';
|
||||
import { NodeColumn } from '../../approval-common/approval-columns/node.column';
|
||||
import { ApprovalRecordStatusColumn } from '../../approval-common/approval-columns/status.column';
|
||||
import { UserColumn } from '../../approval-common/approval-columns/user.column';
|
||||
import { WorkflowColumn } from '../../approval-common/approval-columns/workflow.column';
|
||||
import { SchemaApprovalBlockTodos } from './Sm.ApprovalBlockTodos';
|
||||
|
||||
// 审批-待办
|
||||
|
@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { useCollectionRecordData, useCompile } from '@tachybase/client';
|
||||
import { observer } from '@tachybase/schema';
|
||||
|
||||
import { Tag } from 'antd';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { ApprovalStatusEnumDict } from '../../../constants';
|
||||
|
||||
export const ColumnApprovalStatus = observer(
|
||||
() => {
|
||||
const record = useCollectionRecordData();
|
||||
const { approval } = record;
|
||||
return <ColumnStatusComponent value={approval?.status} record={record} />;
|
||||
},
|
||||
{ displayName: 'ColumnApprovalStatus' },
|
||||
);
|
||||
|
||||
// 审批-待办:状态列:值
|
||||
export const ColumnStatusComponent = (props) => {
|
||||
const compile = useCompile();
|
||||
const { value } = props;
|
||||
const option = ApprovalStatusEnumDict[value];
|
||||
|
||||
return <Tag color={option.color}>{compile(option.label)}</Tag>;
|
||||
};
|
@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { SchemaComponent, useCollectionRecordData } from '@tachybase/client';
|
||||
|
||||
import { NoticeDetailContent } from '../notice-show-detail/NoticeDetail.schema';
|
||||
|
||||
const createSchema = ({ record }) => {
|
||||
const { id } = record;
|
||||
return {
|
||||
name: `notice-view-${id}`,
|
||||
type: 'void',
|
||||
'x-component': 'Action.Link',
|
||||
title: '{{t("View")}}',
|
||||
properties: {
|
||||
drawer: {
|
||||
type: 'void',
|
||||
'x-component': 'Action.Drawer',
|
||||
'x-component-props': {
|
||||
className: 'nb-action-popup',
|
||||
},
|
||||
properties: {
|
||||
content: {
|
||||
type: 'void',
|
||||
'x-component': 'NoticeDetailContent',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const ColumnAction = () => {
|
||||
const record = useCollectionRecordData();
|
||||
|
||||
return (
|
||||
<SchemaComponent
|
||||
components={{
|
||||
NoticeDetailContent: NoticeDetailContent,
|
||||
}}
|
||||
schema={createSchema({ record })}
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,17 @@
|
||||
import { Field, observer, useField } from '@tachybase/schema';
|
||||
|
||||
export const ColumnNode = observer(
|
||||
() => {
|
||||
const field = useField<Field>();
|
||||
const fieldValue = field?.value;
|
||||
|
||||
if (fieldValue?.title) {
|
||||
return fieldValue.title;
|
||||
} else if (fieldValue?.id) {
|
||||
return `#${fieldValue.id}`;
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
{ displayName: 'ColumnNode' },
|
||||
);
|
@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import { useCompile, useRecord } from '@tachybase/client';
|
||||
import { Field, observer, useField } from '@tachybase/schema';
|
||||
|
||||
import { Tag } from 'antd';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { EXECUTION_STATUS } from '../../../../../constants';
|
||||
import { NAMESPACE, useTranslation } from '../../../locale';
|
||||
|
||||
export const ColumnStatus = observer(
|
||||
() => {
|
||||
const { value } = useField<Field>();
|
||||
const record = useRecord();
|
||||
return <ColumnStatusComponent value={value} record={record} />;
|
||||
},
|
||||
{ displayName: 'ColumnStatus' },
|
||||
);
|
||||
|
||||
export const ColumnStatusComponent = (props) => {
|
||||
const { value, record } = props;
|
||||
const omitProps = _.omit(props, ['value', 'record']);
|
||||
const compile = useCompile();
|
||||
const { t } = useTranslation();
|
||||
const { option = approvalStatusConfigObj[value] } = omitProps;
|
||||
const { workflow, execution, job } = record ?? {};
|
||||
|
||||
return (!workflow?.enabled || execution?.stauts || job?.status) && [].includes(value) ? (
|
||||
<Tag>{t('Unprocessed')}</Tag>
|
||||
) : (
|
||||
<Tag color={option?.color}>{compile(option?.label)}</Tag>
|
||||
);
|
||||
};
|
||||
|
||||
export const approvalStatusOptions = [
|
||||
{ value: EXECUTION_STATUS.QUEUEING, label: `{{t("Assigned", { ns: "${NAMESPACE}" })}}`, color: 'blue' },
|
||||
{ value: EXECUTION_STATUS.STARTED, label: `{{t("Pending", { ns: "${NAMESPACE}" })}}`, color: 'gold' },
|
||||
// { value: EXECUTION_STATUS.RETURNED, label: `{{t("Returned", { ns: "${NAMESPACE}" })}}`, color: 'purple' },
|
||||
{ value: 2, label: `{{t("Approved", { ns: "${NAMESPACE}" })}}`, color: 'green' },
|
||||
{ value: EXECUTION_STATUS.REJECTED, label: `{{t("Rejected", { ns: "${NAMESPACE}" })}}`, color: 'red' },
|
||||
// { value: EXECUTION_STATUS.WITHDRAWN, label: `{{t("Withdrawn", { ns: "${NAMESPACE}" })}}` },
|
||||
];
|
||||
const approvalStatusConfigObj = approvalStatusOptions.reduce(
|
||||
(e, t) =>
|
||||
Object.assign(e, {
|
||||
[t.value]: t,
|
||||
}),
|
||||
{},
|
||||
);
|
@ -0,0 +1,16 @@
|
||||
import { Field, observer, useField } from '@tachybase/schema';
|
||||
|
||||
export const ColumnUser = observer(
|
||||
() => {
|
||||
const field = useField<Field>();
|
||||
const fieldValue = field?.value;
|
||||
|
||||
if (fieldValue?.nickname) {
|
||||
return fieldValue.nickname;
|
||||
} else if (fieldValue?.id !== undefined) {
|
||||
return fieldValue.id;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
{ displayName: 'ColumnUser' },
|
||||
);
|
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { css } from '@tachybase/client';
|
||||
import { Field, observer, useField } from '@tachybase/schema';
|
||||
|
||||
import { useTranslation } from '../../../locale';
|
||||
|
||||
export const ColumnWorkflow = observer(
|
||||
() => {
|
||||
const { t } = useTranslation();
|
||||
const { value } = useField<Field>();
|
||||
const title = value?.title || `#${value?.id}`;
|
||||
|
||||
if (value?.enabled) {
|
||||
return title;
|
||||
} else {
|
||||
return (
|
||||
<span
|
||||
className={css`
|
||||
text-decoration: line-through;
|
||||
`}
|
||||
title={t('Disabled')}
|
||||
>
|
||||
{title}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
},
|
||||
{ displayName: 'ColumnWorkflow' },
|
||||
);
|
@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
SchemaComponentContext,
|
||||
useCollectionRecordData,
|
||||
useRequest,
|
||||
useSchemaComponentContext,
|
||||
} from '@tachybase/client';
|
||||
|
||||
import { Result, Spin } from 'antd';
|
||||
|
||||
import { ExecutionContextProvider } from '../../../../../ExecutionContextProvider';
|
||||
import { COLLECTION_NAME_APPROVAL_CARBON_COPY } from '../../../../common/constants';
|
||||
import { useTranslation } from '../../../locale';
|
||||
import { ProviderContextMyComponent } from './contexts/MyComponent.context';
|
||||
import { ProviderContextWorkflowNotice } from './contexts/WorkflowNotice.context';
|
||||
|
||||
export const NoticeDetailProvider = ({ children, ...props }) => {
|
||||
// THINK: 顺序问题, 工具优先, 数据之后, 外部优先, 本地滞后
|
||||
const schemaComponentContext = useSchemaComponentContext();
|
||||
const { t } = useTranslation();
|
||||
const { id } = useCollectionRecordData();
|
||||
// const { actionEnabled } = useContext(ContextWithActionEnabled);
|
||||
const { loading, data } = useRequestData(id);
|
||||
|
||||
if (loading) {
|
||||
return <Spin />;
|
||||
}
|
||||
|
||||
if (!data?.data) {
|
||||
return <Result status="error" title={t('Submission may be withdrawn, please try refresh the list.')} />;
|
||||
}
|
||||
|
||||
const itemsData = data.data;
|
||||
const { node, workflow, execution } = itemsData;
|
||||
const schemaId = node?.config.showCarbonCopyDetail;
|
||||
const { designable } = props;
|
||||
|
||||
// THINK: Provider 的顺序, 数据放在外面, 配置放在内层
|
||||
return (
|
||||
<ExecutionContextProvider workflow={workflow} nodes={workflow.nodes} execution={execution}>
|
||||
<ProviderContextWorkflowNotice value={itemsData}>
|
||||
<ProviderContextMyComponent
|
||||
value={{
|
||||
id,
|
||||
schemaId,
|
||||
}}
|
||||
>
|
||||
<SchemaComponentContext.Provider value={{ ...schemaComponentContext, designable }}>
|
||||
{children}
|
||||
</SchemaComponentContext.Provider>
|
||||
</ProviderContextMyComponent>
|
||||
</ProviderContextWorkflowNotice>
|
||||
</ExecutionContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
function useRequestData(id) {
|
||||
const { loading, data }: any = useRequest(
|
||||
{
|
||||
resource: COLLECTION_NAME_APPROVAL_CARBON_COPY,
|
||||
action: 'get',
|
||||
params: {
|
||||
filterByTk: id,
|
||||
appends: ['node', 'job', 'workflow', 'workflow.nodes', 'execution', 'execution.jobs', 'user'],
|
||||
except: ['workflow.config', 'workflow.options', 'nodes.config'],
|
||||
sort: ['-createdAt'],
|
||||
},
|
||||
},
|
||||
{
|
||||
refreshDeps: [id],
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
loading,
|
||||
data,
|
||||
};
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
RemoteSchemaComponent,
|
||||
SchemaComponent,
|
||||
SchemaComponentProvider,
|
||||
useFormBlockContext,
|
||||
} from '@tachybase/client';
|
||||
|
||||
import { DetailsBlockProvider } from '../../../../../components';
|
||||
import { tval } from '../../../locale';
|
||||
import { useContextMyComponent } from './contexts/MyComponent.context';
|
||||
import { usePropsNoticeDetail } from './hooks/usePropsNoticeDetail';
|
||||
import { NoticeDetailProvider } from './NoticeDetail.provider';
|
||||
|
||||
export const NoticeDetailContent = () => {
|
||||
return (
|
||||
<NoticeDetailProvider>
|
||||
<NoticeDetail />
|
||||
</NoticeDetailProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const NoticeDetail = (props) => {
|
||||
const { id, schemaId } = useContextMyComponent();
|
||||
|
||||
return (
|
||||
<SchemaComponent
|
||||
components={{
|
||||
NoticeDetailProvider,
|
||||
RemoteSchemaComponent,
|
||||
SchemaComponentProvider,
|
||||
DetailsBlockProvider,
|
||||
}}
|
||||
scope={{
|
||||
usePropsNoticeDetail,
|
||||
useDetailsBlockProps: useFormBlockContext,
|
||||
}}
|
||||
schema={{
|
||||
name: `content-${id}`,
|
||||
type: 'void',
|
||||
'x-component': 'Tabs',
|
||||
properties: {
|
||||
detail: {
|
||||
type: 'void',
|
||||
title: tval('Content Detail'),
|
||||
'x-component': 'Tabs.TabPane',
|
||||
properties: {
|
||||
detail: {
|
||||
type: 'void',
|
||||
'x-decorator': 'NoticeDetailProvider',
|
||||
'x-decorator-props': {
|
||||
designable: false,
|
||||
},
|
||||
'x-component': 'RemoteSchemaComponent',
|
||||
'x-component-props': {
|
||||
uid: schemaId,
|
||||
noForm: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
interface ContextMyComponentProps {
|
||||
id?: string;
|
||||
schemaId?: string;
|
||||
}
|
||||
export const ContextMyComponent = React.createContext<ContextMyComponentProps>({});
|
||||
|
||||
export const ProviderContextMyComponent = ContextMyComponent.Provider;
|
||||
|
||||
export function useContextMyComponent() {
|
||||
return React.useContext(ContextMyComponent);
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
|
||||
// NOTE: 传递的是内置表 workflowNotice 的数据
|
||||
export const ContextWorkflowNotice = React.createContext<any>({});
|
||||
|
||||
export const ProviderContextWorkflowNotice = ContextWorkflowNotice.Provider;
|
||||
|
||||
export function useContexWorkflowNotice() {
|
||||
return React.useContext(ContextWorkflowNotice);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useFormBlockContext } from '@tachybase/client';
|
||||
|
||||
import { useContexWorkflowNotice } from '../contexts/WorkflowNotice.context';
|
||||
|
||||
export function usePropsNoticeDetail() {
|
||||
const { snapshot } = useContexWorkflowNotice();
|
||||
const { form } = useFormBlockContext();
|
||||
|
||||
useEffect(() => {
|
||||
form.setValues(snapshot);
|
||||
}, [form, snapshot]);
|
||||
|
||||
return { form };
|
||||
}
|
@ -4,7 +4,7 @@ import { useCompile } from '@tachybase/client';
|
||||
import { Tag } from 'antd';
|
||||
|
||||
import { APPROVAL_STATUS, approvalStatusConfigObj, ApprovalStatusEnumDict } from '../../../constants';
|
||||
import { ColumnStatusComponent } from '../approval-columns/column.status';
|
||||
import { ColumnStatusComponent } from '../approval-columns/status.column';
|
||||
|
||||
export function renderColumnStatus(value, record, exist) {
|
||||
// return null;
|
||||
|
@ -8,10 +8,10 @@ import {
|
||||
useRecord,
|
||||
} from '@tachybase/client';
|
||||
|
||||
import { CollectionApprovalTodos } from '../../common/ApprovalTodos.collection';
|
||||
import { CollectionApprovals } from '../approval-common/Approvals.collection';
|
||||
import { CollectionFlowNodes } from '../approval-common/FlowNodes.collection';
|
||||
import { CollectionWorkflows } from '../approval-common/Workflows.collection';
|
||||
import { CollectionApprovalTodos } from '../../collections/approvalRecords.collection';
|
||||
import { CollectionApprovals } from '../../collections/approvals.collection';
|
||||
import { CollectionFlowNodes } from '../../collections/flowNodes.collection';
|
||||
import { CollectionWorkflows } from '../../collections/workflows.collection';
|
||||
|
||||
export function RecordApprovalsDecorator({ params, children }) {
|
||||
const collection = useCollection_deprecated();
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { SchemaComponent } from '@tachybase/client';
|
||||
|
||||
import { ApprovalRecordStatusColumn } from '../approval-common/approval-columns/column.status';
|
||||
import { UserColumn } from '../approval-common/approval-columns/column.user';
|
||||
import { WorkflowColumn } from '../approval-common/approval-columns/column.workflow';
|
||||
import { ApprovalRecordStatusColumn } from '../approval-common/approval-columns/status.column';
|
||||
import { UserColumn } from '../approval-common/approval-columns/user.column';
|
||||
import { WorkflowColumn } from '../approval-common/approval-columns/workflow.column';
|
||||
import { SchemaRecordApprovals } from './RecordApprovals.schema';
|
||||
|
||||
// 视图组件,创建区块-相关审批
|
||||
|
@ -1,2 +1,3 @@
|
||||
export const NOTICE_INSTRUCTION_NAMESPACE = 'notice';
|
||||
export const COLLECTION_NOTICE_NAME = 'workflowNotice';
|
||||
export const COLLECTION_NAME_APPROVAL_CARBON_COPY = 'approvalCarbonCopy';
|
||||
|
@ -278,5 +278,7 @@
|
||||
"ms": "ms",
|
||||
"Resubmit": "Resubmit",
|
||||
"Are you sure you want to resubmit it?": "Are you sure you want to resubmit it?",
|
||||
"MyLaunch":"MyLaunch"
|
||||
"MyLaunch":"MyLaunch",
|
||||
"CarbonCopy":"CarbonCopy",
|
||||
"ApprovalCarbonCopy":"Approval:CarbonCopy"
|
||||
}
|
||||
|
@ -359,5 +359,7 @@
|
||||
"ms": "毫秒",
|
||||
"Resubmit": "重新发起",
|
||||
"Are you sure you want to resubmit it?": "是否确定重新发起?",
|
||||
"MyLaunch":"我的发起"
|
||||
"MyLaunch":"我的发起",
|
||||
"CarbonCopy":"抄送",
|
||||
"ApprovalCarbonCopy":"审批:抄送"
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
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 { parsePerson } from './tools';
|
||||
|
||||
export default class ApprovalCarbonCopyInstruction extends Instruction {
|
||||
async run(node, prevJob, processor) {
|
||||
const job = await processor.saveJob({
|
||||
status: JOB_STATUS.RESOLVED,
|
||||
nodeId: node.id,
|
||||
nodeKey: node.key,
|
||||
upstreamId: prevJob?.id ?? null,
|
||||
});
|
||||
const targetPersonList = await parsePerson({ node, processor, keyName: 'carbonCopyPerson' });
|
||||
if (targetPersonList && targetPersonList.length > 0) {
|
||||
const { db } = processor.options.plugin;
|
||||
const ApprovalRepo = db.getRepository('approvals');
|
||||
const approval = await ApprovalRepo.findOne({
|
||||
filter: {
|
||||
'executions.id': processor.execution.id,
|
||||
},
|
||||
fields: ['id', 'status', 'data', 'summary', 'collectionName'],
|
||||
appends: ['approvalExecutions', 'createdBy'],
|
||||
except: ['data'],
|
||||
});
|
||||
const CarbonCopyModel = db.getModel(COLLECTION_NAME_APPROVAL_CARBON_COPY);
|
||||
const notifiedPersonDataMap = targetPersonList.map((userId, index) => ({
|
||||
userId,
|
||||
jobId: job.id,
|
||||
nodeId: node.id,
|
||||
executionId: job.executionId,
|
||||
workflowId: node.workflowId,
|
||||
index,
|
||||
// TODO: 怎么做到状态实时更新, 观察下审批发起的表的状态是怎么做的...
|
||||
createdById: approval.createdBy?.id,
|
||||
approvalId: approval.id,
|
||||
status: approval.status,
|
||||
snapshot: approval.data,
|
||||
summary: approval.summary,
|
||||
collectionName: approval.collectionName,
|
||||
dataKey: approval.dataKey,
|
||||
}));
|
||||
|
||||
await CarbonCopyModel.bulkCreate(notifiedPersonDataMap, {
|
||||
transaction: processor.transaction,
|
||||
});
|
||||
}
|
||||
|
||||
return job;
|
||||
}
|
||||
|
||||
async duplicateConfig(node, { transaction }) {
|
||||
const keyName = 'showCarbonCopyDetail';
|
||||
const uiSchemaRepo = this.workflow.app.db.getRepository<UiSchemaRepository>('uiSchemas');
|
||||
if (!node.config[keyName]) {
|
||||
return node.config;
|
||||
}
|
||||
const resultSchema = await uiSchemaRepo.duplicate(node.config[keyName], {
|
||||
transaction,
|
||||
});
|
||||
|
||||
return {
|
||||
...node.config,
|
||||
[keyName]: resultSchema?.['x-uid'],
|
||||
};
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ import { parseCollectionName } from '@tachybase/data-source-manager';
|
||||
import { traverseJSON } from '@tachybase/database';
|
||||
|
||||
import { EXECUTION_STATUS, JOB_STATUS, PluginWorkflow } from '../..';
|
||||
import { COLLECTION_NAME_APPROVAL_CARBON_COPY, COLLECTION_WORKFLOWS_NAME } from '../common/constants';
|
||||
import { APPROVAL_ACTION_STATUS, APPROVAL_STATUS } from './constants';
|
||||
import { getSummary } from './tools';
|
||||
import { getAssociationName, jsonParse } from './utils';
|
||||
@ -357,6 +358,23 @@ const approvalRecords = {
|
||||
workflow.resume(approvalRecord.job);
|
||||
},
|
||||
};
|
||||
const approvalCarbonCopy = {
|
||||
async listCentralized(context, next) {
|
||||
const centralizedApprovalFlow = await context.db.getRepository(COLLECTION_WORKFLOWS_NAME).find({
|
||||
filter: {
|
||||
type: 'approval',
|
||||
'config.centralized': true,
|
||||
},
|
||||
fields: ['id'],
|
||||
});
|
||||
context.action.mergeParams({
|
||||
filter: {
|
||||
workflowId: centralizedApprovalFlow.map((item) => item.id),
|
||||
},
|
||||
});
|
||||
return actions.list(context, next);
|
||||
},
|
||||
};
|
||||
function make(name, mod) {
|
||||
return Object.keys(mod).reduce(
|
||||
(result, key) => ({
|
||||
@ -371,5 +389,6 @@ export function init({ app }) {
|
||||
...make('workflows', workflows),
|
||||
...make('approvals', approvals),
|
||||
...make('approvalRecords', approvalRecords),
|
||||
...make(COLLECTION_NAME_APPROVAL_CARBON_COPY, approvalCarbonCopy),
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
import { defineCollection } from '@tachybase/database';
|
||||
|
||||
import { COLLECTION_NAME_APPROVAL_CARBON_COPY } from '../../common/constants';
|
||||
|
||||
// 审批-抄送
|
||||
export default defineCollection({
|
||||
namespace: 'workflow.approvalCarbonCopy',
|
||||
dumpRules: 'required',
|
||||
name: COLLECTION_NAME_APPROVAL_CARBON_COPY,
|
||||
createdBy: true,
|
||||
updatedBy: true,
|
||||
fields: [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'collectionName',
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'index',
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'dataKey',
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'approval',
|
||||
},
|
||||
{
|
||||
type: 'integer',
|
||||
name: 'status',
|
||||
},
|
||||
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'user',
|
||||
target: 'users',
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'workflow',
|
||||
onDelete: 'CASCADE',
|
||||
},
|
||||
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'execution',
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'node',
|
||||
target: 'flow_nodes',
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'job',
|
||||
target: 'jobs',
|
||||
},
|
||||
{
|
||||
type: 'jsonb',
|
||||
name: 'summary',
|
||||
defaultValue: {},
|
||||
},
|
||||
{
|
||||
type: 'jsonb',
|
||||
name: 'snapshot',
|
||||
defaultValue: {},
|
||||
},
|
||||
],
|
||||
});
|
@ -1,6 +1,6 @@
|
||||
import { defineCollection } from '@tachybase/database';
|
||||
|
||||
// 审批
|
||||
// 审批-执行态
|
||||
export default defineCollection({
|
||||
namespace: 'workflow.approvalExecutions',
|
||||
dumpRules: 'required',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defineCollection } from '@tachybase/database';
|
||||
|
||||
// 审批待办
|
||||
// 审批-待办
|
||||
export default defineCollection({
|
||||
namespace: 'workflow.approvalRecords',
|
||||
dumpRules: 'required',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defineCollection } from '@tachybase/database';
|
||||
|
||||
// 审批发起
|
||||
// 审批-发起
|
||||
export default defineCollection({
|
||||
namespace: 'workflow.approvals',
|
||||
dumpRules: 'required',
|
||||
|
@ -2,7 +2,9 @@ import path from 'path';
|
||||
import { Plugin } from '@tachybase/server';
|
||||
|
||||
import { PluginWorkflow } from '../..';
|
||||
import { COLLECTION_NAME_APPROVAL_CARBON_COPY } from '../common/constants';
|
||||
import { init } from './actions';
|
||||
import ApprovalCarbonCopyInstruction from './ApprovalCarbonCopy.instruction';
|
||||
import ApprovalInstruction from './ApprovalInstruction';
|
||||
import ApprovalTrigger from './ApprovalTrigger';
|
||||
|
||||
@ -17,6 +19,7 @@ export class PluginWorkflowApproval extends Plugin {
|
||||
this.workflow = plugin;
|
||||
plugin.triggers.register('approval', new ApprovalTrigger(plugin));
|
||||
plugin.instructions.register('approval', new ApprovalInstruction(plugin));
|
||||
plugin.instructions.register(COLLECTION_NAME_APPROVAL_CARBON_COPY, new ApprovalCarbonCopyInstruction(plugin));
|
||||
});
|
||||
}
|
||||
async load() {
|
||||
@ -36,6 +39,9 @@ export class PluginWorkflowApproval extends Plugin {
|
||||
this.app.acl.allow('approvals', '*', 'loggedIn');
|
||||
this.app.acl.allow('approvalExecutions', ['get'], 'loggedIn');
|
||||
this.app.acl.allow('approvalRecords', ['get', 'list', 'listCentralized', 'submit'], 'loggedIn');
|
||||
// NOTE: 这种命名尽量改为引用同一个变量的形式,
|
||||
// 这里因为不妨碍快速获得变量名, 所以没有保持风格一致, 直接使用推荐的方式
|
||||
this.app.acl.allow(COLLECTION_NAME_APPROVAL_CARBON_COPY, ['get', 'list', 'listCentralized', 'submit'], 'loggedIn');
|
||||
}
|
||||
async install(options) {}
|
||||
async afterEnable() {}
|
||||
|
@ -19,3 +19,26 @@ export function getSummary(params: ParamsType): object {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function parsePerson({ node, processor, keyName }) {
|
||||
const configPerson = processor
|
||||
.getParsedValue(node.config?.[keyName] ?? [], node.id)
|
||||
.flat()
|
||||
.filter(Boolean);
|
||||
|
||||
const targetPerson = new Set();
|
||||
const UserRepo = processor.options.plugin.app.db.getRepository('users');
|
||||
for (const item of configPerson) {
|
||||
if (typeof item === 'object') {
|
||||
const users = await UserRepo.find({
|
||||
...item,
|
||||
fields: ['id'],
|
||||
transaction: processor.transaction,
|
||||
});
|
||||
users.forEach((userData) => targetPerson.add(userData.id));
|
||||
} else {
|
||||
targetPerson.add(item);
|
||||
}
|
||||
}
|
||||
return [...targetPerson];
|
||||
}
|
||||
|
@ -1,2 +1,4 @@
|
||||
export const NOTICE_INSTRUCTION_NAMESPACE = 'notice';
|
||||
export const COLLECTION_NOTICE_NAME = 'workflowNotice';
|
||||
export const COLLECTION_NAME_APPROVAL_CARBON_COPY = 'approvalCarbonCopy';
|
||||
export const COLLECTION_WORKFLOWS_NAME = 'workflows';
|
||||
|
Loading…
Reference in New Issue
Block a user