fix(department): 审批三期 (#1507)

Reviewed-on: daoyoucloud/tachybase#1507
Reviewed-by: sealday <zhanglin@daoyoucloud.com>
Co-authored-by: bai.zixv <bai.zixv@foxmail.com>
Co-committed-by: bai.zixv <bai.zixv@foxmail.com>
This commit is contained in:
bai.zixv 2024-09-12 12:05:05 +08:00 committed by sealday
parent 7f4bfce63a
commit 5b5fa2d4ed
37 changed files with 277 additions and 69 deletions

View File

@ -66,7 +66,13 @@ const Cascade = connect((props) => {
resource resource
.list({ .list({
pageSize: 9999, pageSize: 9999,
filter: { id: { $eq: propsValue.id } }, filter: propsValue.id
? {
id: {
$eq: propsValue.id,
},
}
: {},
sort, sort,
tree: true, tree: true,
}) })
@ -199,6 +205,7 @@ export const InternalCascader = observer(
(props: any) => { (props: any) => {
const { options: collectionField } = useAssociationFieldContext(); const { options: collectionField } = useAssociationFieldContext();
const selectForm = useMemo(() => createForm(), []); const selectForm = useMemo(() => createForm(), []);
// @ts-ignore
const { t } = useTranslation(); const { t } = useTranslation();
const field: any = useField(); const field: any = useField();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();

View File

@ -9,7 +9,9 @@ import { UserMainDepartmentFieldSetting } from './settings/UserMainDepartmentFie
export class DepartmentsPlugin extends Plugin { export class DepartmentsPlugin extends Plugin {
async afterAdd() { async afterAdd() {
// 用户和权限-部门
this.pm.add(KitMainTabDepartments); this.pm.add(KitMainTabDepartments);
// 用户和权限-角色和权限-部门
this.pm.add(KitRoleAuth); this.pm.add(KitRoleAuth);
} }
async load() { async load() {
@ -24,3 +26,4 @@ export class DepartmentsPlugin extends Plugin {
this.app.schemaSettingsManager.add(DepartmentOwnersFieldSetting); this.app.schemaSettingsManager.add(DepartmentOwnersFieldSetting);
} }
} }

View File

@ -11,7 +11,12 @@ export const ProviderDepartmentIndex = ({ children }) => {
resource: 'users', resource: 'users',
action: 'list', action: 'list',
params: { params: {
appends: ['departments', 'departments.parent(recursively=true)'], appends: [
'departments',
'departments.parent(recursively=true)',
'mainDepartment',
'mainDepartment.parent(recursively=true)',
],
filter: department filter: department
? { ? {
'departments.id': department.id, 'departments.id': department.id,

View File

@ -4,7 +4,10 @@ import { SchemaComponentContext, useSchemaComponentContext } from '@tachybase/cl
import { ViewDepartmentManagement } from './DepartmentManagement.view'; import { ViewDepartmentManagement } from './DepartmentManagement.view';
import { ProviderDepartmentIndex } from './DepartmentIndex.provider'; import { ProviderDepartmentIndex } from './DepartmentIndex.provider';
// TODO: 名称有待重新确认 /**
* TODO: 组件名称有待重新确认
* @returns
*/
export const DepartmentIndex = () => { export const DepartmentIndex = () => {
const context = useSchemaComponentContext(); const context = useSchemaComponentContext();
return ( return (

View File

@ -1,12 +1,12 @@
import React, { useContext } from 'react'; import React from 'react';
import { ContextDepartments } from '../context/Department.context'; import { useContextDepartments } from '../context/Department.context';
import { useGetDepTree } from '../hooks/useGetDepTree'; import { useGetDepTree } from '../hooks/useGetDepTree';
import { InternalSuperiorDepartmentSelect } from './InternalSuperiorDepartmentSelect'; import { InternalSuperiorDepartmentSelect } from './InternalSuperiorDepartmentSelect';
export const DepartmentSelect = () => { export const DepartmentSelect = () => {
const depTree = useGetDepTree(); const depTree = useGetDepTree();
const { departmentsResource } = useContext(ContextDepartments); const { departmentsResource } = useContextDepartments();
const { service } = departmentsResource || {}; const { service } = departmentsResource || {};
const { data } = service || {}; const { data } = service || {};

View File

@ -1,7 +1,7 @@
import React, { useContext, useEffect } from 'react'; import React, { useEffect } from 'react';
import { useRecord } from '@tachybase/client'; import { useRecord } from '@tachybase/client';
import { ContextDepartments } from '../context/Department.context'; import { useContextDepartments } from '../context/Department.context';
import { useGetDepTree } from '../hooks/useGetDepTree'; import { useGetDepTree } from '../hooks/useGetDepTree';
import { InternalSuperiorDepartmentSelect } from './InternalSuperiorDepartmentSelect'; import { InternalSuperiorDepartmentSelect } from './InternalSuperiorDepartmentSelect';
@ -9,7 +9,7 @@ export const SuperiorDepartmentSelect = () => {
const depTree = useGetDepTree(); const depTree = useGetDepTree();
const { setTreeData, getChildrenIds } = depTree; const { setTreeData, getChildrenIds } = depTree;
const record = useRecord(); const record = useRecord();
const { departmentsResource } = useContext(ContextDepartments); const { departmentsResource } = useContextDepartments();
const { const {
service: { data }, service: { data },
} = departmentsResource || {}; } = departmentsResource || {};

View File

@ -20,7 +20,7 @@ interface contextType {
}; };
} }
export const ContextDepartments = React.createContext<contextType>({ const ContextDepartments = React.createContext<contextType>({
user: {}, user: {},
department: {}, department: {},
}); });

View File

@ -6,6 +6,10 @@ import { Select } from 'antd';
import { ViewUnKnownOwerns } from './UnknownOwerns.view'; import { ViewUnKnownOwerns } from './UnknownOwerns.view';
/**
* -
* @returns
*/
export const DepartmentOwnersField = () => { export const DepartmentOwnersField = () => {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const record = useRecord(); const record = useRecord();
@ -18,7 +22,7 @@ export const DepartmentOwnersField = () => {
}; };
const useSelectOwners = () => { const useSelectOwners = () => {
const { setVisible: setVisible } = useActionContext(); const { setVisible } = useActionContext();
return { return {
run() { run() {
const fieldValue = field.value || []; const fieldValue = field.value || [];

View File

@ -1,24 +1,25 @@
import React, { useContext, useState } from 'react'; import React, { useState } from 'react';
import { ActionContextProvider, RecordProvider, SchemaComponent, SchemaComponentOptions } from '@tachybase/client'; import { ActionContextProvider, RecordProvider, SchemaComponent, SchemaComponentOptions } from '@tachybase/client';
import { UserOutlined } from '@ant-design/icons'; import { UserOutlined } from '@ant-design/icons';
import { Button, Divider, Row, theme } from 'antd'; import { Button, Divider, Row, theme } from 'antd';
import { useTranslation } from '../../../../locale'; import { useTranslation } from '../../../../locale';
import { ContextDepartments } from '../context/Department.context'; import { useContextDepartments } from '../context/Department.context';
import { ProviderContextDepartmentsExpanded } from '../context/DepartmentsExpanded.context'; import { ProviderContextDepartmentsExpanded } from '../context/DepartmentsExpanded.context';
import { useGetDepTree } from '../hooks/useGetDepTree'; import { useGetDepTree } from '../hooks/useGetDepTree';
import { AddNewDepartment } from './AddNewDepartment.view'; import { AddNewDepartment } from './AddNewDepartment.view';
import { DepartmentOwnersField } from './DepartmentOwnersField.component'; import { DepartmentOwnersField } from './DepartmentOwnersField.component';
import { DepartmentsSearch } from './DepartmentsSearch.component'; import { DepartmentsSearch } from './DepartmentsSearch.component';
import { DepartmentsTree } from './DepartmentsTree.component'; import { DepartmentsTree } from './DepartmentsTree.component';
import { useCreateDepartment } from './useCreateDepartment'; import { useCreateDepartment } from './scopes/useCreateDepartment';
import { useUpdateDepartment } from './useUpdateDepartment'; import { useUpdateDepartment } from './scopes/useUpdateDepartment';
interface drawerState { interface drawerState {
node?: object; node?: object;
schema?: object; schema?: object;
} }
// NOTE: 部门左边-部门列表部分 // NOTE: 部门左边-部门列表部分
export const DepartmentsBlock = () => { export const DepartmentsBlock = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -26,10 +27,13 @@ export const DepartmentsBlock = () => {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [drawer, setDrawer] = useState<drawerState>({}); const [drawer, setDrawer] = useState<drawerState>({});
const { department, setDepartment } = useContext(ContextDepartments); const { department, setDepartment } = useContextDepartments();
const { token } = theme.useToken(); const { token } = theme.useToken();
const LabelComp = ({ node }) => <DepartmentsTree.Item node={node} setVisible={setVisible} setDrawer={setDrawer} />;
const value = useGetDepTree({ const value = useGetDepTree({
label: ({ node }) => <DepartmentsTree.Item node={node} setVisible={setVisible} setDrawer={setDrawer} />, label: LabelComp,
}); });
const schema = drawer.schema || {}; const schema = drawer.schema || {};
@ -63,6 +67,7 @@ export const DepartmentsBlock = () => {
</Row> </Row>
<Divider style={{ margin: '12px 0' }} /> <Divider style={{ margin: '12px 0' }} />
<DepartmentsTree /> <DepartmentsTree />
{/* 操作浮层部分 */}
<ActionContextProvider <ActionContextProvider
value={{ value={{
visible, visible,

View File

@ -1,10 +1,10 @@
import React, { useContext, useState } from 'react'; import React, { useState } from 'react';
import { createStyles, useAPIClient, useRequest } from '@tachybase/client'; import { createStyles, useAPIClient, useRequest } from '@tachybase/client';
import { Button, Dropdown, Empty, Input, theme } from 'antd'; import { Button, Dropdown, Empty, Input, theme } from 'antd';
import { useTranslation } from '../../../../locale'; import { useTranslation } from '../../../../locale';
import { ContextDepartments } from '../context/Department.context'; import { useContextDepartments } from '../context/Department.context';
const useStyles = createStyles(({ css }) => ({ const useStyles = createStyles(({ css }) => ({
searchDropdown: css` searchDropdown: css`
@ -22,7 +22,7 @@ export const DepartmentsSearch = () => {
const limit = 10; const limit = 10;
const api = useAPIClient(); const api = useAPIClient();
const { setDepartment, setUser } = useContext(ContextDepartments); const { setDepartment, setUser } = useContextDepartments();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [keyword, setKeyword] = useState(''); const [keyword, setKeyword] = useState('');
const [users, setUsers] = useState([]); const [users, setUsers] = useState([]);

View File

@ -1,27 +1,31 @@
import React, { useContext, useEffect } from 'react'; import React, { useEffect } from 'react';
import { css, useAPIClient, useResourceActionContext } from '@tachybase/client'; import { css, useAPIClient, useResourceActionContext } from '@tachybase/client';
import { MoreOutlined } from '@ant-design/icons'; import { MoreOutlined } from '@ant-design/icons';
import { App, Dropdown, Empty, Tree } from 'antd'; import { App, Dropdown, Empty, Tree } from 'antd';
import { useTranslation } from '../../../../locale'; import { useTranslation } from '../../../../locale';
import { schemaDepartmentEdit } from './DepartmentEdit.schema'; import { getUserListByDepartment } from '../../utils/getUserListByDepartment';
import { schemaDepartmentNewSub } from './DepartmentNewSub.schema'; import { useContextDepartments } from '../context/Department.context';
import { ContextDepartments } from '../context/Department.context';
import { useContextDepartmentsExpanded } from '../context/DepartmentsExpanded.context'; import { useContextDepartmentsExpanded } from '../context/DepartmentsExpanded.context';
import { schemaDepartmentEdit } from './schemas/schemaDepartmentEdit';
import { schemaDepartmentNewSub } from './schemas/schemaDepartmentNewSub';
// 部门左边-部门列表 // 部门左边-部门列表
export const DepartmentsTree = () => { export const DepartmentsTree = () => {
const api = useAPIClient();
const { data, loading } = useResourceActionContext(); const { data, loading } = useResourceActionContext();
const { department, setDepartment, setUser } = useContext(ContextDepartments); const { department, setDepartment, setUser } = useContextDepartments();
const { treeData, nodeMap, loadData, loadedKeys, setLoadedKeys, initData, expandedKeys, setExpandedKeys } = const { treeData, nodeMap, loadData, loadedKeys, setLoadedKeys, initData, expandedKeys, setExpandedKeys } =
useContextDepartmentsExpanded() as any; useContextDepartmentsExpanded() as any;
const onSelect = (keys) => { const onSelect = async (keys) => {
if (!keys.length) { if (!keys.length) {
return; return;
} }
const department = nodeMap[keys[0]]; const department = nodeMap[keys[0]];
const userList = await getUserListByDepartment(api, department.id);
setDepartment(department); setDepartment(department);
setUser(null); setUser(null);
}; };
@ -82,7 +86,7 @@ export const DepartmentsTree = () => {
); );
}; };
DepartmentsTree.Item = ({ node, setVisible, setDrawer }) => { const DepartmentsTreeItem = ({ node, setVisible, setDrawer }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { refreshAsync } = useResourceActionContext(); const { refreshAsync } = useResourceActionContext();
const { setLoadedKeys, expandedKeys, setExpandedKeys } = useContextDepartmentsExpanded(); const { setLoadedKeys, expandedKeys, setExpandedKeys } = useContextDepartmentsExpanded();
@ -90,7 +94,7 @@ DepartmentsTree.Item = ({ node, setVisible, setDrawer }) => {
const API = useAPIClient(); const API = useAPIClient();
const showModalDelete = () => { const showModalDelete = () => {
modal.confirm({ modal.confirm({
title: t('Delete'), title: t('Delete department'),
content: t('Are you sure you want to delete it?'), content: t('Are you sure you want to delete it?'),
onOk: async () => { onOk: async () => {
await API.resource('departments').destroy({ filterByTk: node.id }); await API.resource('departments').destroy({ filterByTk: node.id });
@ -115,7 +119,7 @@ DepartmentsTree.Item = ({ node, setVisible, setDrawer }) => {
setVisible(true); setVisible(true);
}; };
const onClick: any = ({ key, domeEvent }) => { const onClick: any = ({ key, domeEvent }) => {
domeEvent.stopPropagation(); domeEvent?.stopPropagation();
switch (key) { switch (key) {
case 'new-sub': case 'new-sub':
setSchema(schemaDepartmentNewSub); setSchema(schemaDepartmentNewSub);
@ -162,3 +166,5 @@ DepartmentsTree.Item = ({ node, setVisible, setDrawer }) => {
</div> </div>
); );
}; };
DepartmentsTree.Item = DepartmentsTreeItem;

View File

@ -2,6 +2,9 @@ import { useEffect } from 'react';
import { useActionContext, useAPIClient, useRecord, useRequest } from '@tachybase/client'; import { useActionContext, useAPIClient, useRecord, useRequest } from '@tachybase/client';
import { uid } from '@tachybase/schema'; import { uid } from '@tachybase/schema';
/**
*
*/
export const schemaDepartmentEdit = { export const schemaDepartmentEdit = {
type: 'object', type: 'object',
properties: { properties: {

View File

@ -1,6 +1,9 @@
import { useActionContext, useRecord, useRequest } from '@tachybase/client'; import { useActionContext, useRecord, useRequest } from '@tachybase/client';
import { uid } from '@tachybase/schema'; import { uid } from '@tachybase/schema';
/**
*
*/
export const schemaDepartmentNewSub = { export const schemaDepartmentNewSub = {
type: 'object', type: 'object',
properties: { properties: {

View File

@ -1,7 +1,6 @@
import { useActionContext, useAPIClient, useResourceActionContext } from '@tachybase/client'; import { useActionContext, useAPIClient, useResourceActionContext } from '@tachybase/client';
import { useField, useForm } from '@tachybase/schema'; import { useField, useForm } from '@tachybase/schema';
import { useContextDepartmentsExpanded } from '../../context/DepartmentsExpanded.context';
import { useContextDepartmentsExpanded } from '../context/DepartmentsExpanded.context';
export const useCreateDepartment = () => { export const useCreateDepartment = () => {
const form = useForm(); const form = useForm();

View File

@ -1,8 +1,8 @@
import { useActionContext, useAPIClient, useRecord, useResourceActionContext } from '@tachybase/client'; import { useActionContext, useAPIClient, useRecord, useResourceActionContext } from '@tachybase/client';
import { useField, useForm } from '@tachybase/schema'; import { useField, useForm } from '@tachybase/schema';
import { useContextDepartments } from '../context/Department.context'; import { useContextDepartments } from '../../context/Department.context';
import { useContextDepartmentsExpanded } from '../context/DepartmentsExpanded.context'; import { useContextDepartmentsExpanded } from '../../context/DepartmentsExpanded.context';
export const useUpdateDepartment = () => { export const useUpdateDepartment = () => {
const field = useField(); const field = useField();

View File

@ -1,12 +1,12 @@
import React, { useContext } from 'react'; import React from 'react';
import { EllipsisWithTooltip } from '@tachybase/client'; import { EllipsisWithTooltip } from '@tachybase/client';
import { useField } from '@tachybase/schema'; import { useField } from '@tachybase/schema';
import { getDepartmentStr } from '../../utils/getDepartmentStr'; import { getDepartmentStr } from '../../utils/getDepartmentStr';
import { ContextDepartments } from '../context/Department.context'; import { useContextDepartments } from '../context/Department.context';
export const DepartmentField = () => { export const DepartmentField = () => {
const { setDepartment } = useContext(ContextDepartments); const { setDepartment } = useContextDepartments();
const field = useField<{ value: Array<any> }>(); const field = useField<{ value: Array<any> }>();
const fieldValues = field.value || []; const fieldValues = field.value || [];

View File

@ -1,5 +1,7 @@
import { uid } from '@tachybase/schema'; import { uid } from '@tachybase/schema';
import { tval } from '../../../../locale';
export const getSchemaDepartmentsUsersBlock = (department, user) => { export const getSchemaDepartmentsUsersBlock = (department, user) => {
const schemaNotUser = user const schemaNotUser = user
? {} ? {}
@ -38,7 +40,7 @@ export const getSchemaDepartmentsUsersBlock = (department, user) => {
'x-decorator': 'Table.Column.Decorator', 'x-decorator': 'Table.Column.Decorator',
'x-component': 'Table.Column', 'x-component': 'Table.Column',
'x-component-props': { style: { minWidth: 100 } }, 'x-component-props': { style: { minWidth: 100 } },
title: '{{t("Owner")}}', title: tval('Owner'),
properties: { properties: {
isOwner: { isOwner: {
type: 'boolean', type: 'boolean',
@ -105,7 +107,23 @@ export const getSchemaDepartmentsUsersBlock = (department, user) => {
'x-decorator': 'Table.Column.Decorator', 'x-decorator': 'Table.Column.Decorator',
'x-component': 'Table.Column', 'x-component': 'Table.Column',
properties: { properties: {
departments: { type: 'string', 'x-component': 'CollectionField', 'x-read-pretty': true }, departments: {
type: 'string',
'x-component': 'CollectionField',
'x-read-pretty': true,
},
},
},
mainDepartment: {
type: 'void',
'x-decorator': 'Table.Column.Decorator',
'x-component': 'Table.Column',
properties: {
mainDepartment: {
type: 'string',
'x-component': 'CollectionField',
'x-read-pretty': true,
},
}, },
}, },
...tableSchemaDepartMent, ...tableSchemaDepartMent,

View File

@ -1,9 +1,9 @@
import React, { useContext, useEffect, useMemo } from 'react'; import React, { useEffect, useMemo } from 'react';
import { SchemaComponent, useResourceActionContext } from '@tachybase/client'; import { SchemaComponent, useResourceActionContext } from '@tachybase/client';
import { useTranslation } from '../../../../locale'; import { useTranslation } from '../../../../locale';
import { useDepartmentFilterActionProps } from '../../common/scopes/useDepartmentFilterActionProps'; import { useDepartmentFilterActionProps } from '../../common/scopes/useDepartmentFilterActionProps';
import { ContextDepartments } from '../context/Department.context'; import { useContextDepartments } from '../context/Department.context';
import { ViewAddMembers } from './AddMembers.view'; import { ViewAddMembers } from './AddMembers.view';
import { DepartmentField } from './DepartmentField.component'; import { DepartmentField } from './DepartmentField.component';
import { getSchemaDepartmentsUsersBlock } from './DepartmentsUsersBlock.schema'; import { getSchemaDepartmentsUsersBlock } from './DepartmentsUsersBlock.schema';
@ -18,7 +18,7 @@ import { UserDepartmentsField } from './UserDepartmentsField.component';
// 部门右边-用户列表部分 // 部门右边-用户列表部分
export const ViewDepartmentsUsersBlock = () => { export const ViewDepartmentsUsersBlock = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { department, user } = useContext(ContextDepartments); const { department, user } = useContextDepartments();
const { data, setState } = useResourceActionContext(); const { data, setState } = useResourceActionContext();
const MemberActions = () => <ViewMemberActions department={department} />; const MemberActions = () => <ViewMemberActions department={department} />;

View File

@ -1,17 +1,19 @@
import React from 'react'; import React from 'react';
import { SchemaComponent } from '@tachybase/client'; import { SchemaComponent } from '@tachybase/client';
import { useTranslation } from '../../../../locale';
import { schemaRowRemoveAction as schema } from './RowRemoveAction.schema'; import { schemaRowRemoveAction as schema } from './RowRemoveAction.schema';
import { useRemoveMemberAction } from './scopes/useRemoveMemberAction'; import { useRemoveMemberAction } from './scopes/useRemoveMemberAction';
export const ViewRowRemoveAction = (props) => { export const ViewRowRemoveAction = (props) => {
const { t } = useTranslation();
const { department } = props; const { department } = props;
return department ? ( return department ? (
<SchemaComponent <SchemaComponent
schema={schema} schema={schema}
scope={{ scope={{
useRemoveMemberAction: useRemoveMemberAction, t,
useRemoveMemberAction,
}} }}
/> />
) : null; ) : null;

View File

@ -13,7 +13,7 @@ import { App, Button, Dropdown, Tag } from 'antd';
import { useTranslation } from '../../../../locale'; import { useTranslation } from '../../../../locale';
import { getDepartmentStr } from '../../utils/getDepartmentStr'; import { getDepartmentStr } from '../../utils/getDepartmentStr';
import { ViewUnknownUserDepartment } from './UnknownUserDepartment.view'; import { ViewUserSelectDepartment } from './UserSelectDepartment.view';
export const UserDepartmentsField = () => { export const UserDepartmentsField = () => {
const { modal, message } = App.useApp(); const { modal, message } = App.useApp();
@ -112,23 +112,25 @@ export const UserDepartmentsField = () => {
refresh(); refresh();
}; };
const C = (l, u) => { const setFunc = (key, val) => {
switch (l) { switch (key) {
case 'setMain': case 'setMain':
setMain(u); setMain(val);
break; break;
case 'setOwner': case 'setOwner':
setOwner(u); setOwner(val);
break; break;
case 'removeOwner': case 'removeOwner':
removeOwner(u); removeOwner(val);
break; break;
case 'remove': case 'remove':
showModalRemove(u); showModalRemove(val);
} }
}; };
const useDisabled = () => ({ disabled: (l) => field.value.some((u) => u.id === l.id) }); const useDisabled = () => ({
disabled: (target) => target && field.value.some((value) => value.id === target.id),
});
return ( return (
<ActionContextProvider value={{ visible: visible, setVisible: setVisible }}> <ActionContextProvider value={{ visible: visible, setVisible: setVisible }}>
<Fragment> <Fragment>
@ -146,10 +148,20 @@ export const UserDepartmentsField = () => {
<Dropdown <Dropdown
menu={{ menu={{
items: [ items: [
...(val.isMain ? [] : [{ label: t('Set as main department'), key: 'setMain' }]), ...(val.isMain
{ label: t('Remove'), key: 'remove' }, ? []
: [
{
key: 'setMain',
label: t('Set as main department'),
},
]),
{
key: 'remove',
label: t('Remove'),
},
], ],
onClick: ({ key }) => C(key, val), onClick: ({ key }) => setFunc(key, val),
}} }}
> >
<div style={{ float: 'right' }}> <div style={{ float: 'right' }}>
@ -159,9 +171,9 @@ export const UserDepartmentsField = () => {
</Dropdown> </Dropdown>
</Tag> </Tag>
))} ))}
<Button key={1} icon={<PlusOutlined />} onClick={() => setVisible(true)} />, <Button key={1} icon={<PlusOutlined />} onClick={() => setVisible(true)} />
</Fragment> </Fragment>
<ViewUnknownUserDepartment user={user} useAddDepartments={useAddDepartments} useDisabled={useDisabled} /> <ViewUserSelectDepartment user={user} useAddDepartments={useAddDepartments} useDisabled={useDisabled} />
</ActionContextProvider> </ActionContextProvider>
); );
}; };

View File

@ -1,4 +1,4 @@
export const schemaUnknownUserDepartment = { export const schemaUserSelectDepartment = {
type: 'void', type: 'void',
properties: { properties: {
drawer: { drawer: {

View File

@ -1,12 +1,13 @@
import React from 'react'; import React from 'react';
import { SchemaComponent } from '@tachybase/client'; import { SchemaComponent } from '@tachybase/client';
import { useTranslation } from '../../../../locale';
import { ViewDepartmentTable } from '../../common/DepartmentTable.view'; import { ViewDepartmentTable } from '../../common/DepartmentTable.view';
import { useDataSource } from './scopes/useDataSource'; import { useDataSource } from './scopes/useDataSource';
import { schemaUnknownUserDepartment as schema } from './UnknownUserDepartment.schema'; import { schemaUserSelectDepartment as schema } from './UserSelectDepartment.schema';
// TODO: 有待重新命名组件 export const ViewUserSelectDepartment = (props) => {
export const ViewUnknownUserDepartment = (props) => { const { t } = useTranslation();
const { user, useAddDepartments, useDisabled } = props; const { user, useAddDepartments, useDisabled } = props;
return ( return (
<SchemaComponent <SchemaComponent
@ -16,6 +17,7 @@ export const ViewUnknownUserDepartment = (props) => {
DepartmentTable: ViewDepartmentTable, DepartmentTable: ViewDepartmentTable,
}} }}
scope={{ scope={{
t,
user, user,
useDataSource, useDataSource,
useAddDepartments, useAddDepartments,

View File

@ -1,10 +1,10 @@
import { useContext, useEffect } from 'react'; import { useEffect } from 'react';
import { useResourceActionContext } from '@tachybase/client'; import { useResourceActionContext } from '@tachybase/client';
import { ContextDepartments } from '../../context/Department.context'; import { useContextDepartments } from '../../context/Department.context';
export const useMembersDataSource = (props) => { export const useMembersDataSource = (props) => {
const { user } = useContext(ContextDepartments); const { user } = useContextDepartments();
const ctx = useResourceActionContext(); const ctx = useResourceActionContext();
useEffect(() => { useEffect(() => {
if (user) { if (user) {

View File

@ -1,11 +1,11 @@
import React, { useContext } from 'react'; import React from 'react';
import { CollectionProvider_deprecated, ResourceActionContext } from '@tachybase/client'; import { CollectionProvider_deprecated, ResourceActionContext } from '@tachybase/client';
import { collectionUsers } from '../collections/users.collection'; import { collectionUsers } from '../collections/users.collection';
import { ContextDepartments } from '../context/Department.context'; import { useContextDepartments } from '../context/Department.context';
export const ProviderUserResource = ({ children }) => { export const ProviderUserResource = ({ children }) => {
const { usersResource } = useContext(ContextDepartments); const { usersResource } = useContextDepartments();
const { service } = usersResource || {}; const { service } = usersResource || {};
return ( return (

View File

@ -0,0 +1,15 @@
export const getUserListByDepartment = async (api, departmentId: string) => {
if (!departmentId) {
return [];
}
const APIResource = api.resource('users');
const { data } = await APIResource.list({
filter: { 'departments.id': departmentId },
appends: ['departments', 'departments.parent(recursively=true)'],
});
const userList = data.data;
return userList;
};

View File

@ -4,6 +4,7 @@
"Add departments": "Add departments", "Add departments": "Add departments",
"Add members": "Add members", "Add members": "Add members",
"All users": "All users", "All users": "All users",
"Are you sure you want to delete it?": "Are you sure you want to delete it?",
"Are you sure you want to remove it?": "Are you sure you want to remove it?", "Are you sure you want to remove it?": "Are you sure you want to remove it?",
"Are you sure you want to remove these departments?": "Are you sure you want to remove these departments?", "Are you sure you want to remove these departments?": "Are you sure you want to remove these departments?",
"Are you sure you want to remove these members?": "Are you sure you want to remove these members?", "Are you sure you want to remove these members?": "Are you sure you want to remove these members?",
@ -55,6 +56,7 @@
"Roles management": "Roles management", "Roles management": "Roles management",
"Search for departments, users": "Search for departments, users", "Search for departments, users": "Search for departments, users",
"Search results": "Search results", "Search results": "Search results",
"Select Departments": "Select Departments",
"Select submit data type": "Select submit data type", "Select submit data type": "Select submit data type",
"Set as main department": "Set as main department", "Set as main department": "Set as main department",
"Signature input": "Signature input", "Signature input": "Signature input",

View File

@ -4,6 +4,7 @@
"Add departments": "添加部门", "Add departments": "添加部门",
"Add members": "添加成员", "Add members": "添加成员",
"All users": "所有用户", "All users": "所有用户",
"Are you sure you want to delete it?": "你确定要删除吗?",
"Are you sure you want to remove it?": "你确定要移除吗?", "Are you sure you want to remove it?": "你确定要移除吗?",
"Are you sure you want to remove these departments?": "你确定要移除这些部门吗?", "Are you sure you want to remove these departments?": "你确定要移除这些部门吗?",
"Are you sure you want to remove these members?": "你确定要移除这些成员吗?", "Are you sure you want to remove these members?": "你确定要移除这些成员吗?",
@ -56,6 +57,7 @@
"Roles management": "角色管理", "Roles management": "角色管理",
"Search for departments, users": "搜索部门、用户", "Search for departments, users": "搜索部门、用户",
"Search results": "搜索结果", "Search results": "搜索结果",
"Select Departments": "选择部门",
"Select actions": "选择操作", "Select actions": "选择操作",
"Select submit data type": "增量提交", "Select submit data type": "增量提交",
"Set as main department": "设置为主属部门", "Set as main department": "设置为主属部门",

View File

@ -8,6 +8,7 @@ import { useWorkflowAnyExecuted } from '../../../../hooks';
import { Instruction } from '../../../../nodes'; import { Instruction } from '../../../../nodes';
import { APPROVAL_ACTION_STATUS } from '../../constants'; import { APPROVAL_ACTION_STATUS } from '../../constants';
import { NAMESPACE } from '../../locale'; import { NAMESPACE } from '../../locale';
import { ViewSkipApproval } from './approval-config/SkipApproval.view';
import { AssigneesAddition } from './approval-config/VC.AssigneesAddition'; import { AssigneesAddition } from './approval-config/VC.AssigneesAddition';
import { AssigneesSelect } from './approval-config/VC.AssigneesSelect'; import { AssigneesSelect } from './approval-config/VC.AssigneesSelect';
import { ContentTooltip } from './approval-config/VC.ContentTooltip'; import { ContentTooltip } from './approval-config/VC.ContentTooltip';
@ -116,6 +117,64 @@ export class ApprovalInstruction extends Instruction {
}, },
}, },
}, },
skipArrpoval: {
type: 'array',
required: false,
title: `{{t("SkipArrpoval", { ns: "${NAMESPACE}" })}}`,
'x-decorator': 'FormItem',
'x-component': 'ArrayItems',
'x-component-props': {
className: css`
&[disabled] {
> .ant-formily-array-base-addition {
display: none;
}
> .ant-formily-array-items-item .ant-space-item:not(:nth-child(2)) {
display: none;
}
}
`,
},
items: {
type: 'void',
'x-component': 'Space',
'x-component-props': {
className: css`
width: 100%;
&.ant-space.ant-space-horizontal {
flex-wrap: nowrap;
}
> .ant-space-item:nth-child(2) {
flex-grow: 1;
}
`,
},
properties: {
sort: {
type: 'void',
'x-decorator': 'FormItem',
'x-component': 'ArrayItems.SortHandle',
},
input: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'ViewSkipApproval',
},
remove: {
type: 'void',
'x-decorator': 'FormItem',
'x-component': 'ArrayItems.Remove',
},
},
},
properties: {
add: {
type: 'void',
title: `{{t("Add Condition", { ns: "${NAMESPACE}" })}}`,
'x-component': 'AssigneesAddition',
},
},
},
negotiation: { negotiation: {
type: 'number', type: 'number',
title: `{{t("Negotiation mode", { ns: "${NAMESPACE}" })}}`, title: `{{t("Negotiation mode", { ns: "${NAMESPACE}" })}}`,
@ -186,6 +245,7 @@ export class ApprovalInstruction extends Instruction {
NegotiationConfig, NegotiationConfig,
RadioWithTooltip, RadioWithTooltip,
AssigneesAddition, AssigneesAddition,
ViewSkipApproval,
}; };
isAvailable({ workflow, upstream, branchIndex }) { isAvailable({ workflow, upstream, branchIndex }) {

View File

@ -0,0 +1,18 @@
import { FilterDynamicComponent } from '../../../../../components';
export const getSchemaSkipApproval = (params) => {
const { currentFormFields } = params;
return {
type: 'void',
properties: {
filter: {
type: 'object',
'x-component': 'Filter',
'x-component-props': {
options: currentFormFields,
dynamicComponent: FilterDynamicComponent,
},
},
},
};
};

View File

@ -0,0 +1,17 @@
import { default as React } from 'react';
import { SchemaComponent, useCollectionFilterOptions, useToken } from '@tachybase/client';
import { useField } from '@tachybase/schema';
import { getSchemaSkipApproval } from './SkipApproval.schema';
export const ViewSkipApproval = () => {
const field = useField();
const currentFormFields = useCollectionFilterOptions('users');
const { token } = useToken();
const schema = getSchemaSkipApproval({ currentFormFields });
return (
<div style={{ border: `1px dashed ${token.colorBorder}`, padding: token.paddingSM }}>
<SchemaComponent schema={schema} basePath={field.address} />
</div>
);
};

View File

@ -6,8 +6,11 @@ import { KitApprovalUsage } from './usage/plugin';
export class PluginApproval extends Plugin { export class PluginApproval extends Plugin {
async afterAdd() { async afterAdd() {
// 审批配置
this.pm.add(KitApprovalConfiguration); this.pm.add(KitApprovalConfiguration);
// 审批抄送
this.pm.add(KitCarbonCopy); this.pm.add(KitCarbonCopy);
// 审批用户界面
this.pm.add(KitApprovalUsage); this.pm.add(KitApprovalUsage);
} }
async beforeLoad() {} async beforeLoad() {}

View File

@ -14,8 +14,8 @@ export class DataMappingInstruction extends Instruction {
fieldset = { fieldset = {
sourceArray: { sourceArray: {
type: 'array', type: 'array',
title: tval('Data source array'), title: tval('Data source map'),
description: tval('Data source array'), description: tval('Data source map'),
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
'x-component': 'ArrayTable', 'x-component': 'ArrayTable',
items: { items: {

View File

@ -44,6 +44,12 @@ export default class extends Instruction {
'x-content': `{{t("Allow multiple records as result", { ns: "${NAMESPACE}" })}}`, 'x-content': `{{t("Allow multiple records as result", { ns: "${NAMESPACE}" })}}`,
description: `{{t("If checked, when there are multiple records in the query result, an array will be returned as the result, which can be operated on one by one using a loop node. Otherwise, only one record will be returned.", { ns: "${NAMESPACE}" })}}`, description: `{{t("If checked, when there are multiple records in the query result, an array will be returned as the result, which can be operated on one by one using a loop node. Otherwise, only one record will be returned.", { ns: "${NAMESPACE}" })}}`,
}, },
isTree: {
type: 'boolean',
'x-decorator': 'FormItem',
'x-component': 'Checkbox',
'x-content': `{{t("Allow query for tree data", { ns: "${NAMESPACE}" })}}`,
},
params: { params: {
type: 'object', type: 'object',
'x-component': 'fieldset', 'x-component': 'fieldset',

View File

@ -14,6 +14,7 @@
"Aggregator function": "Aggregator function", "Aggregator function": "Aggregator function",
"All pass": "All pass", "All pass": "All pass",
"All succeeded": "All succeeded", "All succeeded": "All succeeded",
"Allow query for tree data": "Allow query for tree data",
"An expression for calculation in each rows": "An expression for calculation in each rows", "An expression for calculation in each rows": "An expression for calculation in each rows",
"And": "And", "And": "And",
"Any pass": "Any pass", "Any pass": "Any pass",
@ -216,6 +217,7 @@
"Separately": "Separately", "Separately": "Separately",
"Sequentially": "Sequentially", "Sequentially": "Sequentially",
"Show Notice Detail": "Show Notice Detail", "Show Notice Detail": "Show Notice Detail",
"SkipArrpoval": "SkipArrpoval",
"Starts on": "Starts on", "Starts on": "Starts on",
"Status": "Status", "Status": "Status",
"String operation": "String operation", "String operation": "String operation",

View File

@ -22,6 +22,7 @@
"All pass": "全部通过", "All pass": "全部通过",
"All succeeded": "全部成功", "All succeeded": "全部成功",
"Allow multiple records as result": "允许结果是多条数据", "Allow multiple records as result": "允许结果是多条数据",
"Allow query for tree data": "添加查询条件为树结构表字段: tree: true",
"Allow the initiator to withdraw the approval before the approval starts.": "在审批开始之前,允许发起人撤回审批。", "Allow the initiator to withdraw the approval before the approval starts.": "在审批开始之前,允许发起人撤回审批。",
"Allowed to be withdrawn": "允许撤回", "Allowed to be withdrawn": "允许撤回",
"And": "会签", "And": "会签",
@ -273,6 +274,7 @@
"Separately": "分别处理", "Separately": "分别处理",
"Sequentially": "顺序", "Sequentially": "顺序",
"Show Notice Detail": "显示通知详情", "Show Notice Detail": "显示通知详情",
"SkipArrpoval": "跳过当前审批",
"Some node meets error.": "某个节点出错。", "Some node meets error.": "某个节点出错。",
"Started and executing, maybe waiting for an async callback (manual, delay etc.).": "已开始执行,可能在等待异步回调(人工、延时等)。", "Started and executing, maybe waiting for an async callback (manual, delay etc.).": "已开始执行,可能在等待异步回调(人工、延时等)。",
"Starts on": "开始于", "Starts on": "开始于",

View File

@ -118,6 +118,7 @@ async function parseAssignees(node, processor) {
} }
return [...assignees]; return [...assignees];
} }
export default class ApprovalInstruction extends Instruction { export default class ApprovalInstruction extends Instruction {
async run(node, prevJob, processor) { async run(node, prevJob, processor) {
const job = await processor.saveJob({ const job = await processor.saveJob({

View File

@ -9,7 +9,13 @@ import { toJSON } from '../utils';
export class QueryInstruction extends Instruction { export class QueryInstruction extends Instruction {
async run(node: FlowNodeModel, input, processor: Processor) { async run(node: FlowNodeModel, input, processor: Processor) {
const { collection, multiple, params = {}, failOnEmpty = false } = node.config; const { collection, multiple, isTree, params = {}, failOnEmpty = false } = node.config;
const otherOptions: any = {};
if (isTree) {
otherOptions.tree = true;
}
const [dataSourceName, collectionName] = parseCollectionName(collection); const [dataSourceName, collectionName] = parseCollectionName(collection);
@ -22,6 +28,7 @@ export class QueryInstruction extends Instruction {
sort = [], sort = [],
...options ...options
} = processor.getParsedValue(params, node.id); } = processor.getParsedValue(params, node.id);
const appends = options.appends const appends = options.appends
? Array.from( ? Array.from(
options.appends.reduce((set, field) => { options.appends.reduce((set, field) => {
@ -33,6 +40,7 @@ export class QueryInstruction extends Instruction {
: options.appends; : options.appends;
const result = await (multiple ? repository.find : repository.findOne).call(repository, { const result = await (multiple ? repository.find : repository.findOne).call(repository, {
...options, ...options,
...otherOptions,
...utils.pageArgsToLimitArgs(page, pageSize), ...utils.pageArgsToLimitArgs(page, pageSize),
sort: sort sort: sort
.filter((item) => item.field) .filter((item) => item.field)