fix: 修改移动端抄送我的配置和界面 (#1142)

Reviewed-on: daoyoucloud/tachybase#1142
Reviewed-by: sealday <zhanglin@daoyoucloud.com>
Co-authored-by: wangjiahui <wwwjh0710@163.com>
Co-committed-by: wangjiahui <wwwjh0710@163.com>
This commit is contained in:
wangjiahui 2024-06-08 04:01:26 +08:00 committed by sealday
parent 9d98941267
commit 6fbbbc31af
11 changed files with 418 additions and 25 deletions

View File

@ -0,0 +1,143 @@
import { approvalStatusOptions } from '../constants';
import { NAMESPACE, tval } from '../locale';
export const CollectionWorkflowNotice = {
title: tval('Notice Center'),
name: 'workflowNotice',
fields: [
{
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',
},
},
},
},
],
};

View File

@ -8,6 +8,7 @@ import { ViewActionUserInitiationsContent } from './initiations/component/ViewAc
import { LauncherActionConfigInitializer } from './initiations/config/LauncherActionConfig'; import { LauncherActionConfigInitializer } from './initiations/config/LauncherActionConfig';
import { InitiationsBlock } from './initiations/InitiationsBlock'; import { InitiationsBlock } from './initiations/InitiationsBlock';
import { ViewActionTodosContent } from './todos/component/ViewActionTodosContent'; import { ViewActionTodosContent } from './todos/component/ViewActionTodosContent';
import { ViewTodosDetailsContent } from './todos/component/ViewTodosDetailsContent';
import { ViewTodosUserJobsContent } from './todos/component/ViewTodosUserJobsContent'; import { ViewTodosUserJobsContent } from './todos/component/ViewTodosUserJobsContent';
import { TodosBlock } from './todos/TodosBlock'; import { TodosBlock } from './todos/TodosBlock';
@ -17,6 +18,7 @@ class PluginApproval extends Plugin {
ApprovalBlockInitializer, ApprovalBlockInitializer,
InitiationsBlock, InitiationsBlock,
TodosBlock, TodosBlock,
ViewTodosDetailsContent,
ViewActionTodosContent, ViewActionTodosContent,
ViewActionUserInitiationsContent, ViewActionUserInitiationsContent,
ViewActionInitiationsContent, ViewActionInitiationsContent,

View File

@ -9,6 +9,7 @@ import '../style/style.css';
import { CollectionApprovals } from '../collection/Approvals.collection'; import { CollectionApprovals } from '../collection/Approvals.collection';
import { CollectionApprovalTodos } from '../collection/ApprovalTodos'; import { CollectionApprovalTodos } from '../collection/ApprovalTodos';
import { CollectionFlowNodes } from '../collection/FlowNodes.collection'; import { CollectionFlowNodes } from '../collection/FlowNodes.collection';
import { CollectionWorkflowNotice } from '../collection/notice.collection';
import { nodeCollection, todoCollection, workflowCollection } from '../collection/UserJobs.collection'; import { nodeCollection, todoCollection, workflowCollection } from '../collection/UserJobs.collection';
import { CollectionWorkflows } from '../collection/Workflows.collection'; import { CollectionWorkflows } from '../collection/Workflows.collection';
import { useTranslation } from '../locale'; import { useTranslation } from '../locale';
@ -34,6 +35,7 @@ export const TodosBlock = observer((props) => {
nodeCollection, nodeCollection,
workflowCollection, workflowCollection,
todoCollection, todoCollection,
CollectionWorkflowNotice,
]} ]}
> >
<BlockItem> <BlockItem>

View File

@ -23,6 +23,8 @@ export const TabApprovalItem = observer((props) => {
changeApprovalRecordsService(api, params?.[tabKey], filter, cm, compile, t, setData, input); changeApprovalRecordsService(api, params?.[tabKey], filter, cm, compile, t, setData, input);
} else if (collectionName === 'users_jobs') { } else if (collectionName === 'users_jobs') {
changeUsersJobsService(api, t, cm, compile, input, setData, params?.[tabKey], filter); changeUsersJobsService(api, t, cm, compile, input, setData, params?.[tabKey], filter);
} else if (collectionName === 'workflowNotice') {
changeWorkflowNoticeService(api, t, cm, compile, input, setData, params?.[tabKey], filter);
} }
}, [filter, params, input]); }, [filter, params, input]);
@ -35,12 +37,7 @@ export const TabApprovalItem = observer((props) => {
<List.Item <List.Item
key={index} key={index}
onClick={() => { onClick={() => {
let url; const url = `/mobile/approval/${collectionName}/${item.id}/${item.categoryTitle}/detailspage`;
if (collectionName === 'approvalRecords') {
url = `/mobile/approval/${item.id}/${item.categoryTitle}/detailspage`;
} else if (collectionName === 'users_jobs') {
url = `/mobile/approval/${item.id}/userJobspage`;
}
navigate(url); navigate(url);
}} }}
> >
@ -98,20 +95,21 @@ const changeApprovalRecordsService = (api, params, filter, cm, compile, t, setDa
const statusType = approvalTodoListStatus(item, t); const statusType = approvalTodoListStatus(item, t);
const categoryTitle = item.workflow.title.replace('审批流:', ''); const categoryTitle = item.workflow.title.replace('审批流:', '');
const collectionName = item.workflow?.config?.collection || item.execution?.context?.collectionName; const collectionName = item.workflow?.config?.collection || item.execution?.context?.collectionName;
const summary = Object.entries(item.summary).map(([key, value]) => { const summary = Object.entries(item.summary)?.map(([key, value]) => {
const field = cm.getCollectionField(`${collectionName}.${key}`); const field = cm.getCollectionField(`${collectionName}.${key}`);
return { return {
label: compile(field?.uiSchema?.title || key), label: compile(field?.uiSchema?.title || key),
value: (Object.prototype.toString.call(value) === '[object Object]' ? value?.['name'] : value) || '', value: (Object.prototype.toString.call(value) === '[object Object]' ? value?.['name'] : value) || '',
}; };
}); });
const nickName = item.snapshot.createdBy?.nickname || item.execution?.context?.data.createdBy?.nickname;
return { return {
...item, ...item,
title: `${item.snapshot.createdBy.nickname}${categoryTitle}`, title: `${nickName}${categoryTitle}`,
categoryTitle: categoryTitle, categoryTitle: categoryTitle,
statusTitle: t(statusType.label), statusTitle: t(statusType.label),
statusColor: statusType.color, statusColor: statusType.color,
reason: summary, reason: summary || [],
priorityTitle: priorityType.label, priorityTitle: priorityType.label,
priorityColor: priorityType.color, priorityColor: priorityType.color,
}; };
@ -144,9 +142,10 @@ const changeUsersJobsService = (api, t, cm, compile, input, setData, params, fil
); );
const statusType = ExecutionStatusOptions.find((value) => value.value === item.status); const statusType = ExecutionStatusOptions.find((value) => value.value === item.status);
const categoryTitle = item.workflow.title.replace('审批流:', ''); const categoryTitle = item.workflow.title.replace('审批流:', '');
const nickName = item.execution?.context?.data?.createdBy?.nickname;
return { return {
...item, ...item,
title: `${item.user?.nickname}${categoryTitle}`, title: `${nickName}${categoryTitle}`,
categoryTitle: categoryTitle, categoryTitle: categoryTitle,
statusTitle: t(statusType.label), statusTitle: t(statusType.label),
statusColor: statusType.color, statusColor: statusType.color,
@ -167,3 +166,65 @@ const changeUsersJobsService = (api, t, cm, compile, input, setData, params, fil
console.error; console.error;
}); });
}; };
export const changeWorkflowNoticeService = (api, t, cm, compile, input, setData, params, filter) => {
api
.request({
url: 'workflowNotice:listCentralized',
params: {
pageSize: 9999,
filter: { ...params, ...filter },
appends: [
'user.id',
'user.nickname',
'node.id',
'node.title',
'job.id',
'job.status',
'job.result',
'workflow.id',
'workflow.title',
'workflow.enabled',
'execution.id',
'execution.status',
],
},
})
.then((res) => {
const result = res.data?.data.map((item) => {
const priorityType = ApprovalPriorityType.find(
(priorityItem) => priorityItem.value === item.snapshot?.priority,
);
const statusType = approvalStatusOptions.find((value) => value.value === item.status);
const categoryTitle = item.workflow.title.replace('审批流:', '');
const collectionName = item.collectionName;
const summary = Object.entries(item.summary).map(([key, value]) => {
const field = cm.getCollectionField(`${collectionName}.${key}`);
return {
label: compile(field?.uiSchema?.title || key),
value: (Object.prototype.toString.call(value) === '[object Object]' ? value?.['name'] : value) || '',
};
});
return {
...item,
title: `${item.user?.nickname}${categoryTitle}`,
categoryTitle: categoryTitle,
statusTitle: t(statusType.label),
statusColor: statusType.color,
reason: summary,
priorityTitle: priorityType.label,
priorityColor: priorityType.color,
};
});
const filterResult = result.filter((value) => value.title.includes(input));
filterResult.sort((a, b) => {
return Date.parse(b.createdAt) - Date.parse(a.createdAt);
});
setData(filterResult);
})
.catch(() => {
console.error;
});
};

View File

@ -27,13 +27,14 @@ import { ApprovalFormBlockDecorator } from '../provider/ApprovalFormBlock';
import '../../style/style.css'; import '../../style/style.css';
import { MobileProvider } from '@tachybase/plugin-mobile-client/client'; import { MobileProvider } from '@tachybase/plugin-mobile-client/client';
import { observer } from '@tachybase/schema';
// 审批-待办-查看: 内容 // 审批-待办-查看: 内容
export const ViewActionTodosContent = () => { export const ViewActionTodosContent = observer((props) => {
const { id } = props as any;
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const params = useParams();
const { id } = params;
const api = useAPIClient(); const api = useAPIClient();
const [noDate, setNoDate] = useState(false); const [noDate, setNoDate] = useState(false);
const [recordData, setRecordDate] = useState({}); const [recordData, setRecordDate] = useState({});
@ -115,7 +116,7 @@ export const ViewActionTodosContent = () => {
</div> </div>
</div> </div>
); );
}; });
const todosComponent = (applyDetail) => { const todosComponent = (applyDetail) => {
const formContextSchema = { const formContextSchema = {

View File

@ -0,0 +1,36 @@
import React from 'react';
import { SchemaComponent } from '@tachybase/client';
import { observer } from '@tachybase/schema';
import { useParams } from 'react-router-dom';
import { ViewActionTodosContent } from './ViewActionTodosContent';
import { ViewTodosUserJobsContent } from './ViewTodosUserJobsContent';
import { ViewTodosWorkflowNoticeContent } from './ViewTodosWorkflowNoticeContent';
export const ViewTodosDetailsContent = () => {
const params = useParams();
const { id, type } = params;
const schema = {
type: 'void',
name: 'TabProcessedItem',
title: 'TodosDetails',
'x-component': component[type],
'x-component-props': {
id,
},
};
return (
<SchemaComponent
schema={schema}
components={{ ViewActionTodosContent, ViewTodosUserJobsContent, ViewTodosWorkflowNoticeContent }}
/>
);
};
const component = {
approvalRecords: 'ViewActionTodosContent',
users_jobs: 'ViewTodosUserJobsContent',
workflowNotice: 'ViewTodosWorkflowNoticeContent',
};

View File

@ -18,6 +18,7 @@ import { useTranslation } from '../../locale';
import '../../style/style.css'; import '../../style/style.css';
import { MobileProvider } from '@tachybase/plugin-mobile-client/client'; import { MobileProvider } from '@tachybase/plugin-mobile-client/client';
import { observer } from '@tachybase/schema';
import { Registry } from '@tachybase/utils/client'; import { Registry } from '@tachybase/utils/client';
import { ManualFormType } from '../../constants'; import { ManualFormType } from '../../constants';
@ -29,12 +30,11 @@ import { FormBlockProvider } from '../provider/FormBlockProvider';
import { ManualActionStatusProvider } from '../provider/ManualActionStatusProvider'; import { ManualActionStatusProvider } from '../provider/ManualActionStatusProvider';
// 审批-执行处理-查看: 内容 // 审批-执行处理-查看: 内容
export const ViewTodosUserJobsContent = () => { export const ViewTodosUserJobsContent = observer((props) => {
const { id } = props as any;
const { t } = useTranslation(); const { t } = useTranslation();
const { actionEnabled } = useContext(ContextWithActionEnabled); const { actionEnabled } = useContext(ContextWithActionEnabled);
const navigate = useNavigate(); const navigate = useNavigate();
const params = useParams();
const { id } = params;
const api = useAPIClient(); const api = useAPIClient();
const [noDate, setNoDate] = useState(false); const [noDate, setNoDate] = useState(false);
const workflowPlugin = usePlugin(PluginWorkflowClient); const workflowPlugin = usePlugin(PluginWorkflowClient);
@ -138,7 +138,7 @@ export const ViewTodosUserJobsContent = () => {
</div> </div>
</div> </div>
); );
}; });
function useDetailsBlockProps() { function useDetailsBlockProps() {
const { form } = useFormBlockContext(); const { form } = useFormBlockContext();

View File

@ -0,0 +1,137 @@
import React, { useContext, useEffect, useState } from 'react';
import {
CollectionProvider,
CollectionProvider_deprecated,
RemoteSchemaComponent,
SchemaComponent,
SchemaComponentProvider,
useAPIClient,
useFormBlockContext,
usePlugin,
} from '@tachybase/client';
import { DetailsBlockProvider, FlowContext, linkNodes } from '@tachybase/plugin-workflow/client';
import { Result } from 'antd';
import { NavBar, Skeleton } from 'antd-mobile';
import _ from 'lodash';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from '../../locale';
import '../../style/style.css';
import { MobileProvider } from '@tachybase/plugin-mobile-client/client';
import { observer } from '@tachybase/schema';
import { ContextApprovalExecution } from '../../context/ApprovalExecution';
import { usePropsNoticeDetail } from '../hook/usePropsNoticeDetail';
// 审批-抄送-查看: 内容
export const ViewTodosWorkflowNoticeContent = observer((props) => {
const { id } = props as any;
const { t } = useTranslation();
const navigate = useNavigate();
const api = useAPIClient();
const [noDate, setNoDate] = useState(false);
const [noticeData, setNoticeData] = useState({});
const [schemaId, setSchemaId] = useState('');
const [flowContext, setFlowContext] = useState<any>(null);
useEffect(() => {
api
.request({
url: 'workflowNotice:get',
params: {
filterByTk: id,
appends: ['execution', 'node', 'job', 'workflow', 'workflow.nodes', 'execution.jobs', 'user'],
except: ['workflow.config', 'workflow.options', 'nodes.config'],
sort: ['-createdAt'],
},
})
.then(({ data }) => {
if (!data.data) setNoDate(true);
const { node, workflow: { nodes = [], ...workflow } = {}, execution, ...userJob } = data?.data ?? {};
linkNodes(nodes);
setFlowContext({
userJob,
workflow,
nodes,
execution,
});
setSchemaId(node?.config.showNoticeDetail);
setNoticeData(data.data);
return;
})
.catch(() => {
console.error;
});
}, []);
if (noDate) {
return <Result status="error" title={t('Submission may be withdrawn, please try refresh the list.')} />;
}
return (
<div style={{ minHeight: '100vh', backgroundColor: '#f3f3f3', overflow: 'auto' }}>
<NavBar
onBack={() => {
navigate(-1);
}}
className="navBarStyle"
>
{'抄送'}
</NavBar>
<div className="approvalContext">
{noticeData && schemaId ? (
<FlowContext.Provider value={flowContext}>
<CollectionProvider name={noticeData['collectionName']}>
<ContextApprovalExecution.Provider value={noticeData}>
<SchemaComponent
components={{
RemoteSchemaComponent,
SchemaComponentProvider,
DetailsBlockProvider,
MobileProvider,
}}
scope={{
usePropsNoticeDetail,
useDetailsBlockProps: useFormBlockContext,
}}
schema={{
type: 'void',
'x-component': 'MobileProvider',
properties: {
page: {
type: 'void',
'x-component': 'MPage',
'x-designer': 'MPage.Designer',
'x-component-props': {},
properties: {
detail: {
type: 'void',
'x-decorator': 'NoticeDetailProvider',
'x-decorator-props': {
designable: false,
},
'x-component': 'RemoteSchemaComponent',
'x-component-props': {
uid: schemaId,
noForm: true,
},
},
},
},
},
}}
/>
</ContextApprovalExecution.Provider>
</CollectionProvider>
</FlowContext.Provider>
) : (
<div>
<Skeleton.Title animated />
<Skeleton.Paragraph lineCount={5} animated />
</div>
)}
</div>
</div>
);
});

View File

@ -43,7 +43,7 @@ export const tabDuplicateSchema = (t, props, parantUid) => {
'x-component': 'TabDuplicateItem', 'x-component': 'TabDuplicateItem',
'x-component-props': { 'x-component-props': {
...props, ...props,
collectionName: 'approvalRecords', collectionName: 'workflowNotice',
settingBlock: true, settingBlock: true,
tabKey: 'duplicate', tabKey: 'duplicate',
parantUid, parantUid,

View File

@ -0,0 +1,15 @@
import { useEffect } from 'react';
import { useFormBlockContext } from '@tachybase/client';
import { useContextApprovalExecution } from '../../context/ApprovalExecution';
export function usePropsNoticeDetail() {
const { snapshot } = useContextApprovalExecution();
const { form } = useFormBlockContext();
useEffect(() => {
form.setValues(snapshot);
}, [form, snapshot]);
return { form };
}

View File

@ -19,12 +19,8 @@ export class PluginApprovalMobileClient extends Plugin {
Component: 'ViewActionUserInitiationsContent', Component: 'ViewActionUserInitiationsContent',
}); });
this.app.router.add('mobile.approval.detailspage', { this.app.router.add('mobile.approval.detailspage', {
path: '/mobile/:name/:id/:category/detailspage', path: '/mobile/:name/:type/:id/:category/detailspage',
Component: 'ViewActionTodosContent', Component: 'ViewTodosDetailsContent',
});
this.app.router.add('mobile.approval.userJobspage', {
path: '/mobile/:name/:id/userJobspage',
Component: 'ViewTodosUserJobsContent',
}); });
} }
} }