feat/MApproval (#1032)

Co-authored-by: bai.zixv <bai.zixv@foxmail.com>
Co-authored-by: sealday <zhanglin@daoyoucloud.com>
Reviewed-on: daoyoucloud/tachybase#1032
Co-authored-by: wangjiahui <wwwjh0710@163.com>
Co-committed-by: wangjiahui <wwwjh0710@163.com>
This commit is contained in:
wangjiahui 2024-05-22 15:33:23 +08:00 committed by sealday
parent cfa95bfe1e
commit 602646f2c3
34 changed files with 1069 additions and 429 deletions

View File

@ -1,13 +1,16 @@
import {
ExtendCollectionsProvider,
SchemaInitializerItem,
SchemaInitializerMenu,
useSchemaInitializer,
useSchemaInitializerItem,
} from '@tachybase/client';
import React from 'react';
import { ISchema } from '@tachybase/schema';
import { Toast } from 'antd-mobile';
import { CalendarOutline } from 'antd-mobile-icons';
import { CollectionWorkflows } from './collection/Workflows.collection';
import { CollectionFlowNodes } from './collection/FlowNodes.collection';
import { CollectionApprovals } from './collection/Approvals.collection';
import { CollectionApprovalTodos } from './collection/ApprovalTodos';
export const ApprovalBlockInitializer = () => {
const { insert } = useSchemaInitializer();
@ -20,7 +23,9 @@ export const ApprovalBlockInitializer = () => {
'x-toolbar': 'BlockSchemaToolbar',
'x-settings': 'ApprovalSettings',
'x-component': item.itemComponent,
'x-component-props': {},
'x-component-props': {
collectionName: item.collectionName,
},
};
insert(schema);
};
@ -46,12 +51,15 @@ export const ApprovalInitializerItem = [
name: 'initiationsApproval',
title: '发起申请',
itemComponent: 'InitiationsBlock',
collectionName: 'workflows',
},
{
type: 'item',
name: 'currApproval',
title: '我发起的',
itemComponent: 'UserInitiationsBlock',
dataSource: 'main',
collectionName: 'approvals',
},
],
},
@ -60,5 +68,6 @@ export const ApprovalInitializerItem = [
title: '审批',
name: 'todos',
itemComponent: 'TodosBlock',
collectionName: 'approvalRecords',
},
];

View File

@ -1,9 +1,54 @@
import { SchemaSettings } from '@tachybase/client';
import {
SchemaSettings,
SchemaSettingsDataScope,
removeNullCondition,
useCollection,
useCollectionManager,
useDataLoadingMode,
useDesignable,
useDetailsBlockContext,
useFormBlockContext,
} from '@tachybase/client';
import { useField, useFieldSchema } from '@tachybase/schema';
import _ from 'lodash';
export const ApprovalSettings = new SchemaSettings({
name: 'ApprovalSettings',
items: [
{
name: 'setTheDataScope',
Component: SchemaSettingsDataScope,
useComponentProps() {
const fieldSchema = useFieldSchema();
const collectionName = fieldSchema['x-component-props']['collectionName'];
const field = useField();
const { form } = useFormBlockContext();
const { dn } = useDesignable();
return {
collectionName,
defaultFilter: fieldSchema?.['x-component-props']?.params?.filter || {},
form: form,
onSubmit: ({ filter }) => {
_.set(field.componentProps, 'params', {
...field.componentProps?.params,
filter,
});
fieldSchema['x-component-props']['params'] = field.componentProps.params;
dn.emit('patch', {
schema: {
['x-uid']: fieldSchema['x-uid'],
'x-component-props': fieldSchema['x-component-props'],
},
});
},
};
},
// useVisible() {
// const fieldSchema = useFieldSchema();
// return !isTabSearchCollapsibleInputItem(fieldSchema['x-component']);
// },
},
{
name: 'divider',
type: 'divider',

View File

@ -0,0 +1,94 @@
import { approvalStatusOptions } from '../constants';
import { NAMESPACE } from '../locale';
export const CollectionApprovalTodos = {
title: `{{t("Approval todos", { ns: "${NAMESPACE}" })}}`,
name: 'approvalRecords',
fields: [
{
type: 'bigInt',
name: 'approvalId',
interface: 'number',
uiSchema: { type: 'number', title: 'ID', 'x-component': 'InputNumber' },
},
{
type: 'belongsTo',
name: 'user',
target: 'users',
foreignKey: 'userId',
interface: 'm2o',
uiSchema: {
type: 'number',
title: `{{t("Assignee", { ns: "${NAMESPACE}" })}}`,
'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 },
},
},
],
};

View File

@ -0,0 +1,63 @@
import { ApprovalStatusEnums } from '../constants';
import { NAMESPACE } from '../locale';
export const CollectionApprovals = {
title: `{{t("Approval applications", { ns: "${NAMESPACE}" })}}`,
name: 'approvals',
fields: [
{
type: 'bigInt',
name: 'id',
interface: 'number',
uiSchema: { type: 'number', title: 'ID', 'x-component': 'InputNumber' },
},
{
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: '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: 'integer',
name: 'status',
interface: 'select',
uiSchema: {
type: 'number',
title: '{{t("Status", { ns: "workflow" })}}',
'x-component': 'Select',
enum: ApprovalStatusEnums,
},
},
{
name: 'createdAt',
type: 'date',
interface: 'createdAt',
uiSchema: {
type: 'datetime',
title: '{{t("Created at")}}',
'x-component': 'DatePicker',
'x-component-props': { showTime: true },
},
},
],
};

View File

@ -0,0 +1,28 @@
import { NAMESPACE } from '../locale';
export const CollectionFlowNodes = {
title: `{{t("Node", { ns: "${NAMESPACE}" })}}`,
name: 'flow_nodes',
fields: [
{
type: 'bigInt',
name: 'id',
interface: 'm2o',
uiSchema: {
type: 'number',
title: 'ID',
'x-component': 'RemoteSelect',
'x-component-props': {
fieldNames: { label: 'title', value: 'id' },
service: { resource: 'flow_nodes', params: { filter: { type: 'manual' } } },
},
},
},
{
type: 'string',
name: 'title',
interface: 'input',
uiSchema: { type: 'string', title: '{{t("Title")}}', 'x-component': 'Input' },
},
],
};

View File

@ -0,0 +1,28 @@
import { NAMESPACE } from '../locale';
export const CollectionWorkflows = {
title: `{{t("Workflow", { ns: "${NAMESPACE}" })}}`,
name: 'workflows',
fields: [
{
type: 'string',
name: 'title',
interface: 'input',
uiSchema: { title: '{{t("Name")}}', type: 'string', 'x-component': 'Input', required: true },
},
{
type: 'boolean',
name: 'enabled',
interface: 'select',
uiSchema: {
type: 'boolean',
title: '{{t("Status", { ns: "workflow" })}}',
'x-component': 'Select',
enum: [
{ label: '{{t("On", { ns: "workflow" })}}', value: true },
{ label: '{{t("Off", { ns: "workflow" })}}', value: false },
],
},
},
],
};

View File

@ -2,21 +2,21 @@ import { createStyles, useCurrentUserContext } from '@tachybase/client';
import { EXECUTION_STATUS } from '@tachybase/plugin-workflow/client';
import _ from 'lodash';
import React, { useMemo } from 'react';
import { APPROVAL_ACTION_STATUS, APPROVAL_STATUS } from '../constants';
import { lang, usePluginTranslation } from '../locale';
import { APPROVAL_ACTION_STATUS, APPROVAL_STATUS, ApprovalStatusEnums, approvalStatusOptions } from '../constants';
import { lang, usePluginTranslation, useTranslation } from '../locale';
import { useContextApprovalExecution } from '../context/ApprovalExecution';
import { ContextWithActionEnabled } from '../context/WithActionEnabled';
import { Space, Steps, Tag } from 'antd-mobile';
import { dayjs } from '@tachybase/utils/client';
// 审批(发起/待办)区块-查看-审批处理
export const ApprovalProcess = (props) => {
const { t } = usePluginTranslation();
const { approval: approvalContext } = useContextApprovalExecution();
const { styles } = getStyles();
const { data } = useCurrentUserContext();
const { Step } = Steps;
const results = useMemo(() => getResults({ approval: approvalContext, currentUser: data }), [approvalContext, data]);
// const columns = useMemo(() => getAntdTableColumns({ t, styles }), [t, styles]);
const stepsResult = getStepsResult(results, t);
return (
<ContextWithActionEnabled.Provider value={{ actionEnabled: props.actionEnabled }}>
@ -25,6 +25,32 @@ export const ApprovalProcess = (props) => {
<Table key={item.id} dataSource={item.records} rowKey={'id'} pagination={false} columns={columns} />
))}
</Space> */}
<Steps direction="vertical">
{stepsResult.map((item, index) => {
return (
<Step
title={item.title}
key={index}
description={
<Space direction="vertical">
{item.description.map((deItem, indexs) => {
return (
<Space key={indexs}>
{deItem.userName}
<Tag color={deItem.status.color} fill="outline">
{t(deItem.status.label)}
</Tag>
{deItem.date}
</Space>
);
})}
</Space>
}
status="finish"
/>
);
})}
</Steps>
</ContextWithActionEnabled.Provider>
);
};
@ -112,6 +138,60 @@ function getResults({ approval, currentUser }) {
);
}
const getStepsResult = (result) => {
const stepData = result.map((value) => {});
const getStepsResult = (result, t) => {
const stepData = [];
result.forEach((item) => {
const stepItem = {};
item.records.forEach((value, index) => {
const status = {};
if (
(!(value.workflow != null && value.workflow.enabled) ||
(value.execution != null && value.execution.stauts) ||
value.job?.status) &&
[APPROVAL_STATUS.ASSIGNED, APPROVAL_STATUS.PENDING].includes(value.status)
) {
status['label'] = 'Unprocessed';
status['color'] = 'default';
} else {
const approvalStatus = approvalStatusOptions.find((option) => option.value === value.status);
const approvalActionStatus = ApprovalStatusEnums.find((option) => option.value === value.status);
if (value.nodeId) {
status['label'] = approvalStatus?.label || approvalActionStatus?.label;
status['color'] = approvalStatus?.color || approvalActionStatus?.color || 'default';
} else {
status['label'] = approvalActionStatus?.label || approvalStatus?.label;
status['color'] = approvalActionStatus?.color || approvalStatus?.color || 'default';
}
}
if (Object.keys(stepItem).includes(value.nodeId ? value.nodeId.toString() : '')) {
stepItem[value.nodeId].description.push({
userName: value.user.nickname,
status,
date: value.status === 0 ? '' : dayjs(value.execution.updatedAt).format('YYYY-MM-DD hh:mm:ss'),
});
} else {
stepItem[value.nodeId || value.node?.title || t(status['label'])] = {
title: value.node?.title || t(status['label']),
index,
description: [
{
userName: value.user.nickname,
status,
date:
value.status === 0
? ''
: dayjs(
value.nodeId ? value.execution?.updatedAt : value.execution?.createdAt || value.updatedAt,
).format('YYYY-MM-DD hh:mm:ss'),
},
],
};
}
});
const sort = Object.values(stepItem).sort((a, b) => {
return a['index'] - b['index'];
});
stepData.push(...sort);
});
return stepData.filter(Boolean);
};

View File

@ -0,0 +1,97 @@
import { observer } from '@tachybase/schema';
import { dayjs } from '@tachybase/utils/client';
import { AutoCenter, Button, DatePicker, Grid, Popup, Space, Toast } from 'antd-mobile';
import { DownOutline } from 'antd-mobile-icons';
import React, { useState } from 'react';
export const ApprovalReachDataType = observer((props) => {
const { changeFilter, filter } = props as any;
const [pickerVisible, setPickerVisible] = useState(false);
const [popupVisible, setPopupVisible] = useState(false);
const now = new Date();
const [currPicker, setCurrPicker] = useState('start');
const [startData, setStartData] = useState<any>(dayjs(now).startOf('date').toISOString());
const [endData, setEndData] = useState<any>(now);
return (
<>
<Space
onClick={() => {
setPopupVisible(true);
}}
>
{filter['createdAt'] ? `${formatDate(startData)}-${formatDate(endData)} ` : '到达日期'}
<DownOutline />
</Space>
<Popup
visible={popupVisible}
bodyStyle={{
borderTopLeftRadius: '8px',
borderTopRightRadius: '8px',
minHeight: '15vh',
}}
onMaskClick={() => {
setPopupVisible(false);
}}
>
<Grid columns={9} style={{ height: '10vh' }}>
<Grid.Item
span={4}
onClick={() => {
setPickerVisible(true), setCurrPicker('start');
}}
>
<AutoCenter style={{ lineHeight: '10vh' }}>{formatDate(startData) || ''}</AutoCenter>
</Grid.Item>
<Grid.Item style={{ lineHeight: '10vh' }}></Grid.Item>
<Grid.Item
style={{ lineHeight: '10vh' }}
span={4}
onClick={() => {
setPickerVisible(true), setCurrPicker('end');
}}
>
<AutoCenter>{formatDate(endData) || ''}</AutoCenter>
</Grid.Item>
</Grid>
<Button
block
color="primary"
style={{ width: '99%' }}
onClick={() => {
if (!startData || !endData) {
Toast.show({
content: '请将时间范围填写完整',
});
} else {
const changeData = { ...filter };
changeData['createdAt'] = { $dateBetween: [startData, endData] };
changeFilter(changeData);
setPopupVisible(false);
}
}}
>
</Button>
<DatePicker
visible={pickerVisible}
onClose={() => {
setPickerVisible(false);
}}
max={now}
onConfirm={(e) => {
if (currPicker === 'start') {
setStartData(e);
} else {
setEndData(dayjs(e).endOf('date').toISOString());
}
}}
/>
</Popup>
</>
);
});
const formatDate = (checkDate) => {
return dayjs(checkDate).format('YYYY-MM-DD');
};

View File

@ -0,0 +1,64 @@
import { useAPIClient } from '@tachybase/client';
import { observer, useFieldSchema } from '@tachybase/schema';
import { Picker, Space } from 'antd-mobile';
import { DownOutline } from 'antd-mobile-icons';
import React, { useEffect, useState } from 'react';
import { useTranslation } from '../locale';
export const ApprovalTemplateType = observer((props) => {
const { changeFilter, filter } = props as any;
const [visible, setVisible] = useState(false);
const [columns, setColumns] = useState([]);
const api = useAPIClient();
const { t } = useTranslation();
useEffect(() => {
api
.request({
url: 'workflows:list',
params: { pageSize: 9999, filter: { type: { $eq: 'approval' }, enabled: { $eq: true } } },
})
.then((res) => {
const columnsData = res.data.data.map((value) => {
return { label: value.title?.replace('审批流:', '') || '', value: value.id };
});
columnsData.unshift({
value: 'all',
label: t('All'),
});
setColumns(columnsData);
})
.catch(() => {
console.error;
});
}, []);
return (
<>
<Space
onClick={() => {
setVisible(true);
}}
>
{columns.length && filter.workflowId && filter.workflowId !== 'all'
? columns.find((item) => item.value === filter.workflowId).label
: '模版类型'}
<DownOutline />
</Space>
<Picker
columns={[columns]}
visible={visible}
onClose={() => {
setVisible(false);
}}
onConfirm={(e) => {
const changeData = { ...filter };
if (e[0] === 'all') {
delete changeData['workflowId'];
} else {
changeData['workflowId'] = e[0];
}
changeFilter(changeData);
}}
/>
</>
);
});

View File

@ -18,6 +18,11 @@ export const APPROVAL_STATUS = {
/**撤回 */
WITHDRAWN: -3,
};
export const ProcessedStatus = [1, 2, -1, -3];
export const PendingStatus = [0];
export const approvalStatusOptions = [
{ value: APPROVAL_STATUS.ASSIGNED, label: `Assigned`, color: 'blue' },
{ value: APPROVAL_STATUS.PENDING, label: `Pending`, color: 'gold' },

View File

@ -16,8 +16,7 @@ import { Fragment, useContext, useMemo, useRef } from 'react';
import { useContextApprovalExecution } from './ApprovalExecution';
export const FormBlockProvider = (props) => {
const { approvalExecution } = useContextApprovalExecution();
const { snapshot } = approvalExecution;
const { snapshot } = useContextApprovalExecution();
const fieldSchema = useFieldSchema();
const field = useField();
const formBlockRef = useRef(null);

View File

@ -8,6 +8,7 @@ import { ViewActionTodosContent } from './todos/component/ViewActionTodosContent
import { UserInitiationsBlock } from './initiations/UserInitiationsBlock';
import { ViewActionUserInitiationsContent } from './initiations/component/ViewActionUserInitiationsContent';
import { ApprovalProcess } from './component/ApprovalProcess.view';
import { ViewActionInitiationsContent } from './initiations/component/ViewActionInitiationsContent';
class PluginApproval extends Plugin {
async load() {
@ -18,6 +19,7 @@ class PluginApproval extends Plugin {
UserInitiationsBlock,
ViewActionTodosContent,
ViewActionUserInitiationsContent,
ViewActionInitiationsContent,
'ApprovalCommon.ViewComponent.MApprovalProcess': ApprovalProcess,
});
this.app.schemaSettingsManager.add(ApprovalSettings);

View File

@ -1,10 +1,17 @@
import { BlockItem, css, useAPIClient, useRequest } from '@tachybase/client';
import { BlockItem, ExtendCollectionsProvider, css, useAPIClient, useRequest } from '@tachybase/client';
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { AutoCenter, Card, SearchBar, Selector, Space } from 'antd-mobile';
import { TeamFill } from 'antd-mobile-icons';
import { CollectionWorkflows } from '../collection/Workflows.collection';
import { CollectionFlowNodes } from '../collection/FlowNodes.collection';
import { CollectionApprovals } from '../collection/Approvals.collection';
import { CollectionApprovalTodos } from '../collection/ApprovalTodos';
import { observer } from '@tachybase/schema';
export const InitiationsBlock = () => {
export const InitiationsBlock = observer((props) => {
const { params } = props as any;
const [filter, setFilter] = useState({});
const [options, setOptions] = useState([]);
const api = useAPIClient();
const navigate = useNavigate();
@ -12,7 +19,7 @@ export const InitiationsBlock = () => {
api
.request({
url: 'workflows:list',
params: { pageSize: 9999, filter: { type: { $eq: 'approval' }, enabled: { $eq: true } } },
params: { pageSize: 9999, filter: { ...params?.filter, ...filter } },
})
.then((res) => {
const option = res?.data?.data.map((value) => {
@ -23,7 +30,7 @@ export const InitiationsBlock = () => {
<div
style={{ display: 'flex', flexDirection: 'column', alignContent: 'center' }}
onClick={(c) => {
navigate(`/mobile/${value.config.collection}/approval/${value.id}/page`);
navigate(`/mobile/${value.title?.replace('审批流:', '') || ''}/approval/${value.id}/page`);
}}
>
<div>
@ -37,29 +44,45 @@ export const InitiationsBlock = () => {
setOptions(option);
})
.catch(() => {});
}, []);
}, [params, filter]);
return (
<BlockItem>
<div style={{ padding: '10px', height: '90vh' }}>
<SearchBar placeholder="请输入内容" clearable style={{ '--background': '#ffffff' }} />
<Card
className={css`
overflow: hidden;
margin-top: 20px;
.adm-card-body {
padding: 0;
}
`}
style={{ padding: '0', backgroundColor: '#f3f3f3' }}
>
<Selector
style={{ '--color': '#ffffff', '--gap': '1px' }}
columns={3}
showCheckMark={false}
options={options}
<ExtendCollectionsProvider
collections={[CollectionWorkflows, CollectionFlowNodes, CollectionApprovals, CollectionApprovalTodos]}
>
<BlockItem>
<div style={{ padding: '10px', minHeight: '90vh' }}>
<SearchBar
placeholder="请输入内容"
clearable
style={{ '--background': '#ffffff' }}
onChange={(value) => {
if (!value) setFilter({});
}}
onSearch={(value) => {
setFilter({
title: { $includes: value },
});
}}
/>
</Card>
</div>
</BlockItem>
<Card
className={css`
overflow: hidden;
margin-top: 20px;
.adm-card-body {
padding: 0;
}
`}
style={{ padding: '0', backgroundColor: '#f3f3f3' }}
>
<Selector
style={{ '--color': '#ffffff', '--gap': '1px' }}
columns={3}
showCheckMark={false}
options={options}
/>
</Card>
</div>
</BlockItem>
</ExtendCollectionsProvider>
);
};
});

View File

@ -1,35 +1,48 @@
import { BlockItem, SchemaComponent, css } from '@tachybase/client';
import { BlockItem, ExtendCollectionsProvider, SchemaComponent, css } from '@tachybase/client';
import { Divider, SearchBar, Space, Tabs } from 'antd-mobile';
import React, { useState } from 'react';
import { useFieldSchema } from '@tachybase/schema';
import { ApprovalTemplateType } from './component/ApprovalTemplateType';
import React, { useEffect, useState } from 'react';
import { observer, useFieldSchema } from '@tachybase/schema';
import { ApprovalTemplateType } from '../component/ApprovalTemplateType';
import { ApprovalStatus } from './component/ApprovalStatus';
import { ApprovalItem } from './component/ApprovalItem';
import { ApprovalReachDataType } from './component/ApprovalReachDataType';
import { ApprovalReachDataType } from '../component/ApprovalReachDataType';
import '../style/style.css';
import { CollectionWorkflows } from '../collection/Workflows.collection';
import { CollectionFlowNodes } from '../collection/FlowNodes.collection';
import { CollectionApprovals } from '../collection/Approvals.collection';
import { CollectionApprovalTodos } from '../collection/ApprovalTodos';
export const UserInitiationsBlock = () => {
export const UserInitiationsBlock = observer((props) => {
const [filter, setFilter] = useState({});
const fieldSchema = useFieldSchema();
fieldSchema['x-component-props'] = fieldSchema['x-component-props']?.['approvalKey']
? fieldSchema['x-component-props']
: (() => {
fieldSchema['x-component-props']['approvalKey'] = 'pending';
return fieldSchema['x-component-props'];
})();
const props = fieldSchema['x-component-props'];
const changeFilter = (data) => {
setFilter(data);
};
const filterProps = {
...props,
changeFilter,
filter,
};
return (
<BlockItem>
<SearchBar placeholder="搜索人名,标题、内容" clearable style={{ '--background': '#ffffff', padding: '10px' }} />
<Space justify="evenly" style={spaceStyle}>
{/* 模版类型 */}
<ApprovalTemplateType />
{/* 到达日期 */}
<ApprovalReachDataType />
{/* 审批状态 */}
<ApprovalStatus />
</Space>
<ApprovalItem />
</BlockItem>
<ExtendCollectionsProvider
collections={[CollectionWorkflows, CollectionFlowNodes, CollectionApprovals, CollectionApprovalTodos]}
>
<BlockItem>
<SearchBar
placeholder="搜索人名,标题、内容"
clearable
style={{ '--background': '#ffffff', padding: '10px' }}
/>
<Space justify="evenly" className="userInitiationsStyle">
{/* 模版类型 */}
<ApprovalTemplateType {...filterProps} />
{/* 到达日期 */}
<ApprovalReachDataType {...filterProps} />
{/* 审批状态 */}
<ApprovalStatus {...filterProps} />
</Space>
<ApprovalItem {...filterProps} />
</BlockItem>
</ExtendCollectionsProvider>
);
};
const spaceStyle = { width: '100%', fontSize: '10px', color: '#8e8e8e' };
});

View File

@ -1,56 +1,25 @@
import { useAPIClient, useCollectionManager, useCurrentUserContext } from '@tachybase/client';
import { useFieldSchema } from '@tachybase/schema';
import { useAPIClient, useCurrentUserContext } from '@tachybase/client';
import { Badge, Empty, List, Space, Tag } from 'antd-mobile';
import React, { useState } from 'react';
import { useAsyncEffect } from 'ahooks';
import { useDeepCompareEffect } from 'ahooks';
import { ApprovalPriorityType, ApprovalStatusEnums } from '../../constants';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from '../../locale';
import { observer } from '@tachybase/schema';
export const ApprovalItem = () => {
const fieldSchema = useFieldSchema();
const props = fieldSchema['x-component-props'];
const cm = useCollectionManager();
export const ApprovalItem = observer((props) => {
const { filter, params } = props as any;
const api = useAPIClient();
const [data, setData] = useState([]);
const { t } = useTranslation();
const navigate = useNavigate();
const user = useCurrentUserContext();
useAsyncEffect(async () => {
api
.request({
url: 'approvals:listCentralized',
params: { pageSize: 99999, appends: ['workflow'] },
})
.then((res) => {
const result = res.data?.data.map((item) => {
const priorityType = ApprovalPriorityType.find((priorityItem) => priorityItem.value === item.data.priority);
const statusType = approvalTodoListStatus(item, t);
const categoryTitle = item.workflow.title.replace('审批流:', '');
return {
...item,
title: `${user.data.data.nickname}${categoryTitle}`,
categoryTitle: categoryTitle,
statusTitle: t(statusType.label),
statusColor: statusType.color,
reason: item.data.reason || item.data.reason_pay,
priorityTitle: priorityType.label,
priorityColor: priorityType.color,
};
});
result.sort((a, b) => {
return Date.parse(b.createdAt) - Date.parse(a.createdAt);
});
setData(result);
})
.catch(() => {
console.error;
});
}, props);
useDeepCompareEffect(() => {
changService(api, setData, user, { ...params?.filter, ...filter }, t);
}, [filter, params]);
return (
<div style={{ marginTop: '10px', minHeight: '70vh' }}>
<div style={{ minHeight: '73vh' }}>
{data.length ? (
<List style={{ '--font-size': '12px' }}>
{data.map((item, index) => {
@ -82,9 +51,42 @@ export const ApprovalItem = () => {
)}
</div>
);
};
});
const approvalTodoListStatus = (item, t) => {
const { status } = item;
return ApprovalStatusEnums.find((value) => value.value === status);
};
const changService = (api, setData, user, filter, t) => {
console.log('🚀 ~ changService ~ filter:', filter);
api
.request({
url: 'approvals:listCentralized',
params: { pageSize: 99999, appends: ['workflow'], filter },
})
.then((res) => {
const result = res.data?.data.map((item) => {
const priorityType = ApprovalPriorityType.find((priorityItem) => priorityItem.value === item.data.priority);
const statusType = approvalTodoListStatus(item, t);
const categoryTitle = item.workflow.title.replace('审批流:', '');
return {
...item,
title: `${user.data.data.nickname}${categoryTitle}`,
categoryTitle: categoryTitle,
statusTitle: t(statusType.label),
statusColor: statusType.color,
reason: item.data.reason || item.data.reason_pay,
priorityTitle: priorityType.label,
priorityColor: priorityType.color,
};
});
result.sort((a, b) => {
return Date.parse(b.createdAt) - Date.parse(a.createdAt);
});
setData(result);
})
.catch(() => {
console.error;
});
};

View File

@ -1,31 +0,0 @@
import { Picker, Space } from 'antd-mobile';
import { DownOutline } from 'antd-mobile-icons';
import React, { useState } from 'react';
export const ApprovalReachDataType = () => {
const [visible, setVisible] = useState(false);
return (
<>
<Space>
<DownOutline />
</Space>
<Picker
columns={columns}
visible={visible}
onClose={() => {
setVisible(false);
}}
/>
</>
);
};
const columns = [
[
{ label: '全部', value: '1' },
{ label: '近7日', value: '2' },
{ label: '近30日', value: '3' },
{ label: '自定义区间', value: '4' },
],
];

View File

@ -2,8 +2,10 @@ import { Picker, Space } from 'antd-mobile';
import { DownOutline } from 'antd-mobile-icons';
import React, { useState } from 'react';
import { useTranslation } from '../../locale';
import { observer, useFieldSchema } from '@tachybase/schema';
export const ApprovalStatus = () => {
export const ApprovalStatus = observer((props) => {
const { changeFilter, filter } = props as any;
const [visible, setVisible] = useState(false);
const { t } = useTranslation();
return (
@ -13,12 +15,14 @@ export const ApprovalStatus = () => {
setVisible(true);
}}
>
{filter['status'] && filter['status'] !== 'all'
? t(columns.find((item) => item.value === filter['status']).label)
: '申请状态'}
<DownOutline />
</Space>
<Picker
columns={[
columns[0].map((value) => {
columns.map((value) => {
return { ...value, label: t(value.label) };
}),
]}
@ -26,18 +30,26 @@ export const ApprovalStatus = () => {
onClose={() => {
setVisible(false);
}}
value={[filter['status'] || 'all']}
onConfirm={(e) => {
const changeData = { ...filter };
if (e[0] === 'all') {
delete changeData['status'];
} else {
changeData['status'] = e[0];
}
changeFilter(changeData);
}}
/>
</>
);
};
});
const columns = [
[
{ label: 'Draft', value: '0' },
{ label: 'Returned', value: '1' },
{ label: 'Submitted', value: '2' },
{ label: 'Processing', value: '3' },
{ label: 'Approved', value: '4' },
{ label: 'Rejected', value: '-1' },
],
{ label: 'All', value: 'all' },
{ label: 'Draft', value: '0' },
{ label: 'Returned', value: '1' },
{ label: 'Submitted', value: '2' },
{ label: 'Processing', value: '3' },
{ label: 'Approved', value: '4' },
{ label: 'Rejected', value: '-1' },
];

View File

@ -1,37 +0,0 @@
import { Picker, Space } from 'antd-mobile';
import { DownOutline } from 'antd-mobile-icons';
import React, { useState } from 'react';
export const ApprovalTemplateType = () => {
const [visible, setVisible] = useState(false);
return (
<>
<Space
onClick={() => {
setVisible(true);
}}
>
<DownOutline />
</Space>
<Picker
columns={columns}
visible={visible}
onClose={() => {
setVisible(false);
}}
/>
</>
);
};
const columns = [
[
{ label: '全部', value: '0' },
{ label: '入职申请', value: '1' },
{ label: '转正申请', value: '2' },
{ label: '调用申请', value: '3' },
{ label: '离职申请', value: '4' },
{ label: 'xxx申请', value: '5' },
],
];

View File

@ -0,0 +1,123 @@
import React, { createContext, useEffect, useState } from 'react';
import {
CollectionProvider_deprecated,
RemoteSchemaComponent,
SchemaComponent,
SchemaComponentContext,
parseCollectionName,
useAPIClient,
} from '@tachybase/client';
import { useContext } from 'react';
import { NavBar } from 'antd-mobile';
import { useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from '../../locale';
import '../../style/style.css';
import { APPROVAL_ACTION_STATUS } from '../../constants';
import { useCreateSubmit } from '../hook/useCreateSubmit';
export const ViewActionInitiationsContent = () => {
const params = useParams();
const navigate = useNavigate();
const { name, id } = params;
const { t } = useTranslation();
const context = useContext(SchemaComponentContext);
const api = useAPIClient();
const [schema, setSchema] = useState({});
useEffect(() => {
api
.request({
url: 'workflows:list',
params: {
pageSize: 9999,
filter: {
type: { $eq: 'approval' },
enabled: { $eq: true },
id: { $eq: id },
},
},
})
.then((res) => {
const formConfig = res.data.data[0];
const [dataSource, name] = parseCollectionName(formConfig.config.collection);
const resSchema = {
type: 'void',
'x-component': 'MPage',
'x-designer': 'MPage.Designer',
'x-component-props': {},
properties: {
Approval: {
type: 'void',
'x-decorator': 'CollectionProvider_deprecated',
'x-decorator-props': {
name,
dataSource,
},
'x-component': 'RemoteSchemaComponent',
'x-component-props': {
uid: formConfig.config.applyForm,
noForm: true,
},
},
},
};
setSchema(resSchema);
})
.catch(() => {
console.error;
});
}, []);
return (
<div style={{ minHeight: '100vh', backgroundColor: '#f3f3f3', overflow: 'auto' }}>
<NavBar
onBack={() => {
navigate(-1);
}}
className="navBarStyle"
>
{name}
</NavBar>
<SchemaComponentContext.Provider value={{ ...context, designable: false }}>
<SchemaComponent
schema={schema}
components={{
RemoteSchemaComponent: RemoteSchemaComponent,
CollectionProvider_deprecated: CollectionProvider_deprecated,
ApplyActionStatusProvider: ContextInitiationsApprovalStatusProvider,
ActionBarProvider,
WithdrawActionProvider: WithdrawActionProvider,
}}
scope={{
useSubmit: useCreateSubmit,
useWithdrawAction,
}}
/>
</SchemaComponentContext.Provider>
</div>
);
};
export function WithdrawActionProvider() {
return null;
}
export function useWithdrawAction() {
return { run() {} };
}
export function ActionBarProvider(props) {
return props.children;
}
const ContextInitiationsApprovalStatus = createContext(APPROVAL_ACTION_STATUS.SUBMITTED);
export function useContextApprovalStatus() {
return useContext(ContextInitiationsApprovalStatus);
}
export function ContextInitiationsApprovalStatusProvider(props) {
return (
<ContextInitiationsApprovalStatus.Provider value={props.value}>
{props.children}
</ContextInitiationsApprovalStatus.Provider>
);
}

View File

@ -10,7 +10,7 @@ import {
useRequest,
} from '@tachybase/client';
import { DetailsBlockProvider, FlowContext } from '@tachybase/plugin-workflow/client';
import { useForm } from '@tachybase/schema';
import { useFieldSchema, useForm } from '@tachybase/schema';
import { Result, Spin } from 'antd';
import { useContext } from 'react';
import { ContextWithActionEnabled } from '../../context/WithActionEnabled';
@ -22,7 +22,7 @@ import { ActionBarProvider } from '../provider/ActionBar';
import { ApplyActionStatusProvider } from '../provider/ApplyActionStatus';
import { WithdrawActionProvider } from '../provider/WithdrawAction';
import { ContextApprovalExecution } from '../../context/ApprovalExecution';
import { useSubmit } from '../hook/useSubmit';
import { useUpdateSubmit } from '../hook/useUpadteSubmit';
import { useFormBlockProps } from '../hook/useFormBlockProps';
import { useWithdrawAction } from '../hook/useWithdrawAction';
import { FileOutline, UserContactOutline } from 'antd-mobile-icons';
@ -136,17 +136,17 @@ const UserInitiationsComponent = (applyDetail, t, currContext) => {
};
const approvalSchema = {
// type: 'void',
// 'x-component': 'MPage',
// 'x-designer': 'MPage.Designer',
// 'x-component-props': {},
// properties: {
// process: {
// type: 'void',
// 'x-decorator': 'CardItem',
// 'x-component': 'ApprovalCommon.ViewComponent.ApprovalProcess',
// },
// },
type: 'void',
'x-component': 'MPage',
'x-designer': 'MPage.Designer',
'x-component-props': {},
properties: {
process: {
type: 'void',
'x-decorator': 'CardItem',
'x-component': 'ApprovalCommon.ViewComponent.MApprovalProcess',
},
},
};
return (
@ -163,7 +163,7 @@ const UserInitiationsComponent = (applyDetail, t, currContext) => {
}}
scope={{
useForm,
useSubmit: useSubmit,
useSubmit: useUpdateSubmit,
useFormBlockProps,
useDetailsBlockProps: useFormBlockContext,
useWithdrawAction,

View File

@ -0,0 +1,43 @@
import { joinCollectionName, useAPIClient, useBlockRequestContext, useCollection_deprecated } from '@tachybase/client';
import { useField, useForm } from '@tachybase/schema';
import { APPROVAL_ACTION_STATUS } from '../../constants';
import { useParams } from 'react-router-dom';
export function useCreateSubmit() {
const from = useForm();
const field = useField();
const { __parent } = useBlockRequestContext();
const collection = useCollection_deprecated();
const contextWe = APPROVAL_ACTION_STATUS.SUBMITTED;
const apiClient = useAPIClient();
const params = useParams();
const { id: workflowId } = params;
return {
async run() {
try {
from.submit();
field.data = field.data || {};
field.data.loading = true;
await apiClient.resource('approvals').create({
values: {
collectionName: joinCollectionName(collection.dataSource, collection.name),
data: from.values,
status: contextWe,
workflowId: workflowId,
},
});
from.reset();
field.data.loading = false;
const service = __parent.service;
if (service) {
service.refresh();
}
} catch (h) {
field.data && (field.data.loading = false);
}
},
};
}

View File

@ -5,7 +5,7 @@ import _ from 'lodash';
import { useContextApprovalExecution } from '../../context/ApprovalExecution';
import { useContextApprovalStatus } from '../provider/ApplyActionStatus';
export function useSubmit() {
export function useUpdateSubmit() {
const from = useForm();
const field = useField();
const { approval } = useContextApprovalExecution();

View File

@ -19,3 +19,17 @@
.approvalContext{
margin: 6vh 0 6vh 0;
}
.todosSpacStyle{
width:100%;
font-size: 10px;
color: #8e8e8e;
}
.userInitiationsStyle{
width: 100%;
margin-top: 10px;
padding: 10px 0 10px 0;
background-color: #ffffff;
}

View File

@ -1,79 +1,100 @@
import { BlockItem, SchemaComponent, css } from '@tachybase/client';
import { Divider, SearchBar, Space, Tabs } from 'antd-mobile';
import React, { useState } from 'react';
import { BlockItem, ExtendCollectionsProvider } from '@tachybase/client';
import { SearchBar, Space, Tabs } from 'antd-mobile';
import React, { useEffect, useState } from 'react';
import { TabApplicantType } from './component/TabApplicantType';
import { TabTemplateType } from './component/TabTemplateType';
import { TabReachDataType } from './component/TabReachDataType';
import { TabBatchProcessingType } from './component/TabBatchProcessingType';
import { TabApprovalType } from './component/TabApprovalType';
import { TabReadingType } from './component/TabReadingType';
import { TabApprovalItem } from './component/TabApprovalItem';
import { useFieldSchema } from '@tachybase/schema';
import { observer } from '@tachybase/schema';
import '../style/style.css';
import { ApprovalReachDataType } from '../component/ApprovalReachDataType';
import { ApprovalTemplateType } from '../component/ApprovalTemplateType';
import { PendingStatus, ProcessedStatus } from '../constants';
import { CollectionWorkflows } from '../collection/Workflows.collection';
import { CollectionFlowNodes } from '../collection/FlowNodes.collection';
import { CollectionApprovals } from '../collection/Approvals.collection';
import { CollectionApprovalTodos } from '../collection/ApprovalTodos';
export const TodosBlock = () => {
const fieldSchema = useFieldSchema();
fieldSchema['x-component-props'] = fieldSchema['x-component-props']?.['approvalKey']
? fieldSchema['x-component-props']
: (() => {
fieldSchema['x-component-props']['approvalKey'] = 'pending';
return fieldSchema['x-component-props'];
})();
const props = fieldSchema['x-component-props'];
export const TodosBlock = observer((props) => {
const [tabValue, setTabValue] = useState('pending');
const [filter, setFilter] = useState({});
const changeFilter = (data) => {
setFilter(data);
};
useEffect(() => {
const changeTabs = { ...filter };
switch (tabValue) {
case 'pending':
changeTabs['status'] = PendingStatus;
break;
case 'processed':
changeTabs['status'] = ProcessedStatus;
break;
case 'duplicate':
break;
}
setFilter(changeTabs);
}, [tabValue]);
const filterProps = {
...props,
filter,
changeFilter,
};
return (
<BlockItem>
<SearchBar placeholder="搜索人名,标题、内容" clearable style={{ '--background': '#ffffff', padding: '10px' }} />
<Tabs
onChange={(key) => {
props['approvalKey'] = key;
}}
style={{ '--title-font-size': '12px', backgroundColor: '#ffffff', marginTop: '10px' }}
>
<Tabs.Tab title="待处理" key="pending">
<Space justify="evenly" style={spaceStyle}>
{/* 模版类型 */}
<TabTemplateType />
{/* 申请人 */}
<TabApplicantType />
{/* 到达日期 */}
<TabReachDataType />
{/* 批量处理 */}
<TabBatchProcessingType />
</Space>
<TabApprovalItem />
</Tabs.Tab>
<Tabs.Tab title="已处理" key="processed">
<Space justify="evenly" style={spaceStyle}>
{/* 阅读状态 */}
<TabReadingType />
{/* 审批状态 */}
<TabApprovalType />
{/* 模版类型 */}
<TabTemplateType />
{/* 申请人 */}
<TabApplicantType />
{/* 到达日期 */}
<TabReachDataType />
</Space>
<TabApprovalItem />
</Tabs.Tab>
<Tabs.Tab title="抄送我" key="duplicate">
<Space justify="evenly" style={spaceStyle}>
{/* 阅读状态 */}
<TabReadingType />
{/* 审批状态 */}
<TabApprovalType />
{/* 模版类型 */}
<TabTemplateType />
{/* 申请人 */}
<TabApplicantType />
{/* 到达日期 */}
<TabReachDataType />
</Space>
<TabApprovalItem />
</Tabs.Tab>
</Tabs>
</BlockItem>
<ExtendCollectionsProvider
collections={[CollectionWorkflows, CollectionFlowNodes, CollectionApprovals, CollectionApprovalTodos]}
>
<BlockItem>
<SearchBar
placeholder="搜索人名,标题、内容"
clearable
style={{ '--background': '#ffffff', padding: '10px' }}
/>
<Tabs
onChange={(key) => {
setFilter({});
setTabValue(key);
}}
style={{ '--title-font-size': '12px', backgroundColor: '#ffffff', marginTop: '10px' }}
>
<Tabs.Tab title="待处理" key="pending">
<Space justify="evenly" className="todosSpacStyle">
{/* 模版类型 */}
<ApprovalTemplateType {...filterProps} />
{/* 申请人 */}
<TabApplicantType {...filterProps} />
{/* 到达日期 */}
<ApprovalReachDataType {...filterProps} />
</Space>
<TabApprovalItem {...filterProps} />
</Tabs.Tab>
<Tabs.Tab title="已处理" key="processed">
<Space justify="evenly" className="todosSpacStyle">
{/* 审批状态 */}
<TabApprovalType {...filterProps} />
{/* 模版类型 */}
<ApprovalTemplateType {...filterProps} />
{/* 申请人 */}
<TabApplicantType {...filterProps} />
{/* 到达日期 */}
<ApprovalReachDataType {...filterProps} />
</Space>
<TabApprovalItem {...filterProps} />
</Tabs.Tab>
<Tabs.Tab title="抄送我" key="duplicate">
<Space justify="evenly" className="todosSpacStyle">
{/* 审批状态 */}
<TabApprovalType {...filterProps} />
{/* 模版类型 */}
<ApprovalTemplateType {...filterProps} />
{/* 申请人 */}
<TabApplicantType {...filterProps} />
{/* 到达日期 */}
<ApprovalReachDataType {...filterProps} />
</Space>
<TabApprovalItem {...filterProps} />
</Tabs.Tab>
</Tabs>
</BlockItem>
</ExtendCollectionsProvider>
);
};
const spaceStyle = { width: '100%', fontSize: '10px', color: '#8e8e8e' };
});

View File

@ -1,10 +1,36 @@
import { useFieldSchema } from '@tachybase/schema';
import { useAPIClient } from '@tachybase/client';
import { observer, useFieldSchema } from '@tachybase/schema';
import { Picker, Space } from 'antd-mobile';
import { DownOutline } from 'antd-mobile-icons';
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { useTranslation } from '../../locale';
export const TabApplicantType = () => {
export const TabApplicantType = observer((props) => {
const { changeFilter, filter } = props as any;
const [visible, setVisible] = useState(false);
const api = useAPIClient();
const [columns, setColumns] = useState([]);
const { t } = useTranslation();
useEffect(() => {
api
.request({ url: 'users:list', params: { pageSize: 99999 } })
.then((res) => {
const userData = res.data.data.map((item) => {
return {
label: item.nickname,
value: item.id,
};
});
userData.unshift({
label: t('All'),
value: 'all',
});
setColumns(userData);
})
.catch(() => {
console.error;
});
}, []);
return (
<Space>
<Space
@ -12,25 +38,27 @@ export const TabApplicantType = () => {
setVisible(true);
}}
>
{columns.length && filter['userId'] && filter['userId'] !== 'all'
? columns.find((item) => item.value === filter['userId']).label
: '申请人'}
<DownOutline />
</Space>
<Picker
columns={columns}
columns={[columns]}
visible={visible}
onClose={() => {
setVisible(false);
}}
onConfirm={(e) => {
const changeData = { ...filter };
if (e[0] === 'all') {
delete changeData['userId'];
} else {
changeData['userId'] = e[0];
}
changeFilter(changeData);
}}
/>
</Space>
);
};
const columns = [
[
{ label: '111', value: '1' },
{ label: '222', value: '2' },
{ label: '333', value: '3' },
{ label: '444', value: '4' },
],
];
});

View File

@ -1,5 +1,5 @@
import { useAPIClient, useCollectionManager, useRequest } from '@tachybase/client';
import { connect, useFieldSchema } from '@tachybase/schema';
import { mergeFilter, useAPIClient, useCollectionManager, useRequest } from '@tachybase/client';
import { connect, observer, useFieldSchema } from '@tachybase/schema';
import { Badge, Empty, List, Space, Tag } from 'antd-mobile';
import React, { useEffect, useState } from 'react';
import { useAsyncEffect } from 'ahooks';
@ -7,10 +7,8 @@ import { APPROVAL_STATUS, ApprovalPriorityType, approvalStatusOptions } from '..
import { useNavigate } from 'react-router-dom';
import { tval, useTranslation } from '../../locale';
export const TabApprovalItem = () => {
const fieldSchema = useFieldSchema();
const props = fieldSchema['x-component-props'];
const cm = useCollectionManager();
export const TabApprovalItem = observer((props) => {
const { filter, params } = props as any;
const api = useAPIClient();
const [data, setData] = useState([]);
const { t } = useTranslation();
@ -20,7 +18,11 @@ export const TabApprovalItem = () => {
api
.request({
url: 'approvalRecords:listCentralized',
params: { pageSize: 99999, appends: ['execution', 'job', 'node', 'workflow'] },
params: {
pageSize: 99999,
appends: ['execution', 'job', 'node', 'workflow'],
filter: { ...params?.filter, ...filter },
},
})
.then((res) => {
const result = res.data?.data.map((item) => {
@ -50,7 +52,7 @@ export const TabApprovalItem = () => {
.catch(() => {
console.error;
});
}, props);
}, [filter, params]);
return (
<div style={{ marginTop: '10px', minHeight: '70vh' }}>
@ -85,7 +87,7 @@ export const TabApprovalItem = () => {
)}
</div>
);
};
});
const approvalTodoListStatus = (item, t) => {
const { workflow, execution, job, status } = item;

View File

@ -1,9 +1,25 @@
import { observer } from '@tachybase/schema';
import { Picker, Space } from 'antd-mobile';
import { DownOutline } from 'antd-mobile-icons';
import React, { useState } from 'react';
import { ProcessedStatus, approvalStatusOptions } from '../../constants';
import { useTranslation } from '../../locale';
export const TabApprovalType = () => {
export const TabApprovalType = observer((props) => {
const [visible, setVisible] = useState(false);
const { changeFilter, filter } = props as any;
const { t } = useTranslation();
const columns = approvalStatusOptions
.map((item) => {
if (ProcessedStatus.includes(item.value)) {
return {
label: t(item.label),
value: item.value,
};
}
})
.filter(Boolean);
columns.unshift({ label: t('All'), value: 'all' });
return (
<>
<Space
@ -11,26 +27,27 @@ export const TabApprovalType = () => {
setVisible(true);
}}
>
{columns.length && typeof filter['status'] === 'number'
? columns.find((item) => item.value === filter['status']).label
: '审批状态'}
<DownOutline />
</Space>
<Picker
columns={columns}
columns={[columns]}
visible={visible}
onClose={() => {
setVisible(false);
}}
onConfirm={(e) => {
const changeData = { ...filter };
if (e[0] === 'all') {
changeData['status'] = ProcessedStatus;
} else {
changeData['status'] = e[0];
}
changeFilter(changeData);
}}
/>
</>
);
};
const columns = [
[
{ label: '全部', value: '0' },
{ label: '审批中', value: '1' },
{ label: '已通过', value: '2' },
{ label: '已驳回', value: '3' },
{ label: '已撤销', value: '4' },
],
];
});

View File

@ -1,12 +0,0 @@
import { Space } from 'antd-mobile';
import { FillinOutline } from 'antd-mobile-icons';
import React from 'react';
export const TabBatchProcessingType = () => {
return (
<Space>
<FillinOutline />
</Space>
);
};

View File

@ -1,31 +0,0 @@
import { Picker, Space } from 'antd-mobile';
import { DownOutline } from 'antd-mobile-icons';
import React, { useState } from 'react';
export const TabReachDataType = () => {
const [visible, setVisible] = useState(false);
return (
<>
<Space>
<DownOutline />
</Space>
<Picker
columns={columns}
visible={visible}
onClose={() => {
setVisible(false);
}}
/>
</>
);
};
const columns = [
[
{ label: '全部', value: '1' },
{ label: '近7日', value: '2' },
{ label: '近30日', value: '3' },
{ label: '自定义区间', value: '4' },
],
];

View File

@ -1,30 +0,0 @@
import { Picker, Space } from 'antd-mobile';
import { DownOutline } from 'antd-mobile-icons';
import React, { useState } from 'react';
export const TabReadingType = () => {
const [visible, setVisible] = useState(false);
return (
<>
<Space>
<DownOutline />
</Space>
<Picker
columns={columns}
visible={visible}
onClose={() => {
setVisible(false);
}}
/>
</>
);
};
const columns = [
[
{ label: '全部', value: '1' },
{ label: '已读', value: '2' },
{ label: '未读', value: '3' },
],
];

View File

@ -1,37 +0,0 @@
import { Picker, Space } from 'antd-mobile';
import { DownOutline } from 'antd-mobile-icons';
import React, { useState } from 'react';
export const TabTemplateType = () => {
const [visible, setVisible] = useState(false);
return (
<>
<Space
onClick={() => {
setVisible(true);
}}
>
<DownOutline />
</Space>
<Picker
columns={columns}
visible={visible}
onClose={() => {
setVisible(false);
}}
/>
</>
);
};
const columns = [
[
{ label: '全部', value: '0' },
{ label: '入职申请', value: '1' },
{ label: '转正申请', value: '2' },
{ label: '调用申请', value: '3' },
{ label: '离职申请', value: '4' },
{ label: 'xxx申请', value: '5' },
],
];

View File

@ -10,6 +10,10 @@ export class PluginApprovalMobileClient extends Plugin {
async load() {
this.app.router.add('mobile.approval.page', {
path: '/mobile/:name/approval/:id/page',
Component: 'ViewActionInitiationsContent',
});
this.app.router.add('mobile.approval.userPage', {
path: '/mobile/:name/:id/page',
Component: 'ViewActionUserInitiationsContent',
});

View File

@ -61,5 +61,6 @@
"End on reject": "End on reject",
"If checked, the workflow will be terminated after rejection branch processed.": "If checked, the workflow will be terminated after rejection branch processed.",
"Disabled": "Disabled",
"Trigger data": "Trigger data"
"Trigger data": "Trigger data",
"All":"All"
}

View File

@ -76,5 +76,6 @@
"When checked, the workflow will terminate when the rejection branch ends.": "勾选后,否决分支结束后工作流将终止。",
"Disabled": "已失效",
"Submission may be withdrawn, please try refresh the list.": "提交可能已被撤回,请尝试刷新列表。",
"Trigger data": "触发器数据"
"Trigger data": "触发器数据",
"All":"全部"
}