@@ -391,7 +389,7 @@ const GraphOrg = createWithRemoteLoader({
extra={
}>
{node.name}
-
+
{node.description &&
{node.description}
}
@@ -484,7 +482,7 @@ const TreeOrg = ({ data, ids, apis, onSuccess, onViewUsers, linkedSource }) => {
{nodeData.name}
-
+
@@ -787,9 +785,7 @@ const OrgInfo = createWithRemoteLoader({
/>
{linkSettingProps ? (
- : }
- onClick={() => setLinkDrawerOpen(true)}>
+ } onClick={() => setLinkDrawerOpen(true)}>
{linkedSource ? `${SOURCE_LABEL_MAP[linkedSource] || linkedSource}` : formatMessage({ id: 'OrgLinkTitle' })}
) : null}
@@ -817,6 +813,7 @@ const OrgInfo = createWithRemoteLoader({
width={480}
destroyOnClose>
{
@@ -936,56 +933,60 @@ const OrgInfo = createWithRemoteLoader({
{formatMessage({ id: 'ImportSelectedCount' }, { selected: importSelectedRowKeys.length, total: parsedRows.length })}
-
-
record.users.length > 0,
- expandedRowRender: record =>
- record.users.length ? (
-
-
-
-
- {formatMessage({ id: 'ImportNestedUsers' })}
-
-
+
+
record.users.length > 0,
+ expandedRowRender: record =>
+ record.users.length ? (
+
+
+
+
+ {formatMessage({ id: 'ImportNestedUsers' })}
-
-
- ) : null
- }}
- />
-
+
+
+
+ ) : null
+ }}
+ />
+
) : null}
- {activeKey === 'tree' && }
- {activeKey === 'graph' && }
+ {activeKey === 'tree' && (
+
+ )}
+ {activeKey === 'graph' && (
+
+ )}
);
diff --git a/src/components/Tenant/Setting/Org.js b/src/components/Tenant/Setting/Org.js
index c9b06f7..4d596cf 100644
--- a/src/components/Tenant/Setting/Org.js
+++ b/src/components/Tenant/Setting/Org.js
@@ -33,9 +33,10 @@ const OrgInner = createWithRemoteLoader({
children: (
{
+ {...Object.assign({}, apis.tenant.orgLinkConfig)}
+ render={({ data: linkConfigData, reload: reloadLinkConfig }) => {
const linkedSource = linkConfigData?.enabled ? linkConfigData.source : null;
+ const syncSupported = linkConfigData?.syncSupported;
return (
{
+ reloadLinkConfig();
+ reload();
+ }
+ } : null}
onViewUsers={
allowViewUsers
? org => {
const query = filterToUrlParams([
- { name: 'tenantOrgId', label: 'tenantOrgId', value: { label: org.name || '', value: String(org.id) } },
+ { name: 'tenantOrgId', label: 'tenantOrgId', value: { label: org.name || '', value: String(org.id) } }
]);
navigate(`${baseUrl}/user?${query.toString()}`);
}
@@ -63,7 +72,11 @@ const OrgInner = createWithRemoteLoader({
save: allowSave && Object.assign({}, apis.tenant.orgSave),
remove: allowRemove && Object.assign({}, apis.tenant.orgRemove),
userList: Object.assign({}, apis.tenant.userList),
- import: allowImport && Object.assign({}, apis.tenant.orgBatchImport)
+ import: allowImport && Object.assign({}, apis.tenant.orgBatchImport),
+ orgLinkConfig: Object.assign({}, apis.tenant.orgLinkConfig),
+ orgLinkSave: Object.assign({}, apis.tenant.orgLinkSave),
+ orgLinkSync: Object.assign({}, apis.tenant.orgLinkSync),
+ orgLinkCancel: Object.assign({}, apis.tenant.orgLinkCancel)
}}
/>
diff --git a/src/components/Tenant/Setting/User.js b/src/components/Tenant/Setting/User.js
index 65019a7..3bb7ce2 100644
--- a/src/components/Tenant/Setting/User.js
+++ b/src/components/Tenant/Setting/User.js
@@ -2,7 +2,8 @@ import { createWithRemoteLoader } from '@kne/remote-loader';
import UserList from '../UserList';
import withLocale from '../withLocale';
import { useIntl } from '@kne/react-intl';
-import { useState } from 'react';
+import { useState, useCallback } from 'react';
+import get from 'lodash/get';
const User = createWithRemoteLoader({
modules: [
@@ -16,7 +17,8 @@ const User = createWithRemoteLoader({
})(({ remoteModules, menu, children, pageProps: originPageProps, apis: extraApis = {} }) => {
const [Page, usePreset, Permissions, usePermissionsPass, TablePage, FilterProvider] = remoteModules;
const { formatMessage } = useIntl();
- const { apis } = usePreset();
+ const { apis, plugins } = usePreset();
+ const getActions = get(plugins, 'tenant.getUserListActions');
const [target, setTarget] = useState({});
const filter = Object.assign({}, { value: [] }, target.filter);
const allowCreate = usePermissionsPass({ request: ['setting:user-manager:create'] });
@@ -24,6 +26,10 @@ const User = createWithRemoteLoader({
const allowRemove = usePermissionsPass({ request: ['setting:user-manager:remove'] });
const allowInvite = usePermissionsPass({ request: ['setting:user-manager:invite'] });
+ const handleMount = useCallback(data => {
+ setTarget(data);
+ }, []);
+
const pageProps = Object.assign({}, originPageProps, {
menu,
title: formatMessage({ id: 'UserManagement' }),
@@ -33,7 +39,8 @@ const User = createWithRemoteLoader({
+ children:
});
}}
/>
diff --git a/src/components/Tenant/UserList/Actions/SendMessage.js b/src/components/Tenant/UserList/Actions/SendMessage.js
new file mode 100644
index 0000000..0e849ea
--- /dev/null
+++ b/src/components/Tenant/UserList/Actions/SendMessage.js
@@ -0,0 +1,108 @@
+import { createWithRemoteLoader } from '@kne/remote-loader';
+import merge from 'lodash/merge';
+import { App, Button, Tag, Flex } from 'antd';
+import withLocale from '../../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+import { SOURCE_LABEL_MAP } from '../../constants';
+
+const SendMessage = createWithRemoteLoader({
+ modules: ['components-core:FormInfo@useFormModal', 'components-core:FormInfo', 'components-admin:Editor', 'components-core:Global@usePreset']
+})(({ remoteModules, apis, selectedRows, onSuccess, size }) => {
+ const [useFormModal, FormInfo, Editor, usePreset] = remoteModules;
+ const { ajax } = usePreset();
+ const formModal = useFormModal();
+ const { formatMessage } = useIntl();
+ const { message } = App.useApp();
+
+ const externalUsers = selectedRows.filter(row => row.syncSource && row.sourceId);
+ const syncSource = externalUsers.length > 0 ? externalUsers[0].syncSource : null;
+ const sourceLabel = SOURCE_LABEL_MAP[syncSource] || syncSource || '';
+
+ const { TextArea, RadioGroup, Input } = FormInfo.fields;
+
+ const handleClick = () => {
+ if (externalUsers.length === 0) {
+ return;
+ }
+
+ formModal({
+ title: formatMessage({ id: 'SendOrgMessageTitle' }, { type: sourceLabel }),
+ size: 'small',
+ formProps: {
+ onSubmit: async formData => {
+ const { data: resData } = await ajax(
+ merge({}, apis.sendOrgMessage, {
+ data: {
+ userIds: externalUsers.map(u => String(u.id)),
+ content: formData.content,
+ msgtype: formData.msgtype
+ }
+ })
+ );
+ if (resData.code !== 0) {
+ return false;
+ }
+ message.success(formatMessage({ id: 'SendMessageSuccess' }, { count: externalUsers.length }));
+ onSuccess?.();
+ }
+ },
+ children: (
+
+
+
+ {formatMessage({ id: 'SendMessageTarget' })}({externalUsers.length})
+
+
+ {externalUsers.map(u => (
+ {u.name}
+ ))}
+
+
+ ,
+
+ )
+ });
+ };
+
+ return (
+
+ );
+});
+
+export default withLocale(SendMessage);
diff --git a/src/components/Tenant/UserList/Actions/index.js b/src/components/Tenant/UserList/Actions/index.js
index ea019be..5a5188d 100644
--- a/src/components/Tenant/UserList/Actions/index.js
+++ b/src/components/Tenant/UserList/Actions/index.js
@@ -8,54 +8,57 @@ import { useIntl } from '@kne/react-intl';
const Actions = createWithRemoteLoader({
modules: ['components-core:ButtonGroup']
-})(withLocale(({ remoteModules, moreType, children, itemClassName, ...props }) => {
- const [ButtonGroup] = remoteModules;
- const { formatMessage } = useIntl();
- const actionList = [
- {
- ...props,
- children: formatMessage({ id: 'EditUser' }),
- hidden: !props.apis.save,
- buttonComponent: Edit
- },
- {
- ...props,
- children: formatMessage({ id: 'InviteUser' }),
- buttonComponent: Invite,
- hidden: props.data?.userId
- },
- {
- ...props,
- children: formatMessage({ id: 'Open' }),
- buttonComponent: SetStatus,
- hidden: props.data?.status === 'open' || !props.apis.save
- },
- {
- ...props,
- children: formatMessage({ id: 'Close' }),
- buttonComponent: SetStatus,
- hidden: props.data?.status === 'closed' || !props.apis.save,
- message: formatMessage({ id: 'CloseUserConfirm' }),
- isDelete: false
- },
- {
- ...props,
- children: formatMessage({ id: 'Delete' }),
- buttonComponent: Remove,
- hidden: !props.apis.delete,
- message: formatMessage({ id: 'DeleteUserConfirm' })
- }
- ];
+})(
+ withLocale(({ remoteModules, moreType, children, itemClassName, ...props }) => {
+ const [ButtonGroup] = remoteModules;
+ const { formatMessage } = useIntl();
+ const actionList = [
+ {
+ ...props,
+ children: formatMessage({ id: 'EditUser' }),
+ hidden: !props.apis.save,
+ buttonComponent: Edit
+ },
+ {
+ ...props,
+ children: formatMessage({ id: 'InviteUser' }),
+ buttonComponent: Invite,
+ hidden: props.data?.userId
+ },
+ {
+ ...props,
+ children: formatMessage({ id: 'Open' }),
+ buttonComponent: SetStatus,
+ hidden: props.data?.status === 'open' || !props.apis.save
+ },
+ {
+ ...props,
+ children: formatMessage({ id: 'Close' }),
+ buttonComponent: SetStatus,
+ hidden: props.data?.status === 'closed' || !props.apis.save,
+ message: formatMessage({ id: 'CloseUserConfirm' }),
+ isDelete: false
+ },
+ {
+ ...props,
+ children: formatMessage({ id: 'Delete' }),
+ buttonComponent: Remove,
+ hidden: !props.apis.delete,
+ message: formatMessage({ id: 'DeleteUserConfirm' })
+ }
+ ];
- if (typeof children === 'function') {
- return children({
- itemClassName,
- moreType,
- list: actionList
- });
- }
+ if (typeof children === 'function') {
+ return children({
+ ...props,
+ itemClassName,
+ moreType,
+ list: actionList
+ });
+ }
- return ;
-}));
+ return ;
+ })
+);
export default Actions;
diff --git a/src/components/Tenant/UserList/FormInner.js b/src/components/Tenant/UserList/FormInner.js
index 350b340..0c94738 100644
--- a/src/components/Tenant/UserList/FormInner.js
+++ b/src/components/Tenant/UserList/FormInner.js
@@ -2,31 +2,64 @@ import { createWithRemoteLoader } from '@kne/remote-loader';
import { useMemo } from 'react';
import { Flex } from 'antd';
import get from 'lodash/get';
+import merge from 'lodash/merge';
import withLocale from '../withLocale';
import { useIntl } from '@kne/react-intl';
import useRefCallback from '@kne/use-ref-callback';
import getRoleListApi from '../Role/getRoleListApi';
+const markSyncedOrgDisabled = items => {
+ if (!Array.isArray(items)) return items;
+ return items.map(item => {
+ const newItem = { ...item };
+ if (item.syncSource) {
+ newItem.disabled = true;
+ }
+ if (item.children) {
+ newItem.children = markSyncedOrgDisabled(item.children);
+ }
+ return newItem;
+ });
+};
+
+const getOrgListApi = (apis, { disableSynced } = {}) => {
+ if (!apis.orgList) return apis.orgList;
+ if (!disableSynced) return apis.orgList;
+ return merge({}, apis.orgList, {
+ transformData: data => {
+ if (Array.isArray(data)) {
+ return markSyncedOrgDisabled(data);
+ }
+ return Object.assign({}, data, {
+ pageData: markSyncedOrgDisabled(data.pageData || [])
+ });
+ }
+ });
+};
+
const FormInnerInner = createWithRemoteLoader({
modules: ['components-core:FormInfo', 'components-core:Global@usePreset']
-})(({ remoteModules, apis }) => {
+})(({ remoteModules, apis, data }) => {
const [FormInfo, usePreset] = remoteModules;
const { formatMessage } = useIntl();
const { plugins } = usePreset();
const { Avatar, Input, PhoneNumber, TextArea, SuperSelectTree, SuperSelect } = FormInfo.fields;
+ const isSynced = !!data?.synced;
+ const orgListApi = useMemo(() => getOrgListApi(apis, { disableSynced: !isSynced }), [apis, isSynced]);
const getFormInner = useRefCallback(() => {
const formInner = [
,
- ,
+ ,
,
,
- ,
- ,
-
+ ,
+ ,
+
];
const UserFormInner = get(plugins, 'tenantAdmin.UserFormInner');
if (UserFormInner && (UserFormInner.$$typeof || typeof UserFormInner.type === 'function')) {
diff --git a/src/components/Tenant/UserList/UserPersonalCard/index.js b/src/components/Tenant/UserList/UserPersonalCard/index.js
index c24b0f9..8707188 100644
--- a/src/components/Tenant/UserList/UserPersonalCard/index.js
+++ b/src/components/Tenant/UserList/UserPersonalCard/index.js
@@ -2,13 +2,14 @@ import '@kne/react-box/dist/index.css';
import classnames from 'classnames';
import { createWithRemoteLoader } from '@kne/remote-loader';
import { PersonalCard } from '@kne/react-box';
-import { Typography } from 'antd';
+import { Typography, Tag } from 'antd';
import get from 'lodash/get';
import { useIntl } from '@kne/react-intl';
import withLocale from '../../withLocale';
import getUserOrgDisplayItems from '../getUserOrgDisplayItems';
import buildRolesTitle from '../../Role/buildRolesTitle';
import style from './style.module.scss';
+import { getSourceIcon, SOURCE_LABEL_MAP } from '../../constants';
/** 副标题单行省略,悬停展示完整内容 */
const renderEllipsisTitle = text => {
@@ -76,9 +77,15 @@ const buildPersonalCardProps = (data, context = {}) => {
let moreInfo = buildMoreInfo(data, { formatMessage });
moreInfo = applyPlugins(moreInfo, data, { formatMessage, plugins });
+ const sourceTag = data?.syncSource ? (
+
+ {SOURCE_LABEL_MAP[data.syncSource] || data.syncSource}
+
+ ) : null;
+
return {
mode: 'horizontal',
- name: data?.name,
+ name: data?.name ? <>{data.name}{sourceTag}> : data?.name,
email: data?.email,
phone: data?.phone,
description: data?.description,
diff --git a/src/components/Tenant/UserList/index.js b/src/components/Tenant/UserList/index.js
index f6f444a..0a8c655 100644
--- a/src/components/Tenant/UserList/index.js
+++ b/src/components/Tenant/UserList/index.js
@@ -1,18 +1,20 @@
import { createWithRemoteLoader } from '@kne/remote-loader';
-import { useRef, useEffect, useMemo } from 'react';
-import { Flex } from 'antd';
+import merge from 'lodash/merge';
+import { useRef, useEffect, useMemo, useCallback } from 'react';
+import { Flex, Button } from 'antd';
import Actions from './Actions';
import Create from './Actions/Create';
+import SendMessage from './Actions/SendMessage';
import withLocale from '../withLocale';
import { useIntl } from '@kne/react-intl';
import useRefCallback from '@kne/use-ref-callback';
import useFilterList from './useFilterList';
import useColumns from './useColumns';
-import useListApi from './useListApi';
+import get from 'lodash/get';
const UserList = createWithRemoteLoader({
- modules: ['components-core:Table@TablePage', 'components-core:Filter', 'components-core:Global@usePreset']
+ modules: ['components-core:Table', 'components-core:Filter', 'components-core:Global@usePreset']
})(
withLocale(
({
@@ -20,31 +22,49 @@ const UserList = createWithRemoteLoader({
apis,
topOptionsSize,
onMount,
+ getActions,
children,
initialTenantOrgId,
initialOrgName,
initialUserId,
allowQueryIdForUserFilter
}) => {
- const [TablePage, Filter, usePreset] = remoteModules;
- const { ajax } = usePreset();
+ const [Table, Filter, usePreset] = remoteModules;
+ const { TablePage, useSelectedRow } = Table;
const tableRef = useRef();
const { formatMessage } = useIntl();
const {
- SearchInput, getFilterValue, createFilterValueMapper,
- useUrlFilter, createUrlFilterReader, multiSelectInterceptor, fields: filterFields
+ SearchInput,
+ getFilterValue,
+ createFilterValueMapper,
+ useUrlFilter,
+ createUrlFilterReader,
+ multiSelectInterceptor,
+ fields: filterFields
} = Filter;
const { InputFilterItem, AdvancedSelectFilterItem, SuperSelectFilterItem } = filterFields;
const { plugins } = usePreset();
- const mapFilterValue = useMemo(() => createFilterValueMapper({
- id: 'string',
- roles: 'multi',
- tenantOrgId: 'single'
- }), [createFilterValueMapper]);
+ const selectedRow = useSelectedRow();
+ const { selectedRowKeys, selectedRows, setSelectedRows } = selectedRow;
+
+ const clearSelection = useCallback(() => {
+ setSelectedRows([]);
+ }, [setSelectedRows]);
+
+ const mapFilterValue = useMemo(
+ () =>
+ createFilterValueMapper({
+ id: 'string',
+ roles: 'multi',
+ tenantOrgId: 'single',
+ synced: 'single'
+ }),
+ [createFilterValueMapper]
+ );
const [filter, setFilter] = useUrlFilter({
- readUrlParams: (searchParams) => {
+ readUrlParams: searchParams => {
const reader = createUrlFilterReader(searchParams);
const tenantOrgEntry = reader.takeFilterEntry('tenantOrgId');
let userEntry = reader.takeFilterEntry('userId');
@@ -65,7 +85,7 @@ const UserList = createWithRemoteLoader({
orgName,
userId,
tenantOrgEntry,
- userEntry: userId ? (userEntry || { label: String(userId), value: String(userId) }) : null
+ userEntry: userId ? userEntry || { label: String(userId), value: String(userId) } : null
};
},
buildFilter: ({ tenantOrgId, orgName, tenantOrgEntry, userEntry }) => {
@@ -95,13 +115,40 @@ const UserList = createWithRemoteLoader({
});
const filterValue = useMemo(() => mapFilterValue(filter, getFilterValue), [filter, getFilterValue, mapFilterValue]);
- const filterList = useFilterList({ formatMessage, apis, InputFilterItem, AdvancedSelectFilterItem, SuperSelectFilterItem, multiSelectInterceptor });
+ const filterList = useFilterList({
+ formatMessage,
+ apis,
+ InputFilterItem,
+ AdvancedSelectFilterItem,
+ SuperSelectFilterItem,
+ multiSelectInterceptor
+ });
const columns = useColumns({ formatMessage, apis, plugins });
- const listApi = useListApi({ apis, filterValue, ajax });
+
+ const hasExternalSelected = selectedRows.some(row => row.syncSource);
const topOptions = (
-
+
+ {selectedRowKeys.length > 0 ? (
+
+ {formatMessage({ id: 'SelectedCount' }, { count: selectedRowKeys.length })}
+
+
+ ) : null}
+ {apis.sendOrgMessage && hasExternalSelected && (
+ {
+ clearSelection();
+ tableRef.current.reload();
+ }}
+ />
+ )}
{apis.create && (
tableRef.current.reload()}>
{formatMessage({ id: 'Add' })}
@@ -111,7 +158,11 @@ const UserList = createWithRemoteLoader({
);
const tableOptions = {
- ...listApi,
+ ...merge({}, apis.list, {
+ params: {
+ filter: filterValue
+ }
+ }),
ref: tableRef,
columns: [
...columns,
@@ -120,21 +171,20 @@ const UserList = createWithRemoteLoader({
type: 'options',
title: formatMessage({ id: 'Operation' }),
fixed: 'right',
- valueOf: item => ({
- children: (
- tableRef.current.reload()}
- />
- )
- })
+ valueOf: item => {
+ return {
+ children: (
+ tableRef.current.reload()}>
+ {getActions}
+
+ )
+ };
+ }
}
],
name: 'tenant-user-list',
- pagination: { paramsType: 'params' }
+ pagination: { paramsType: 'params' },
+ rowSelection: selectedRow
};
const handlerMount = useRefCallback(() => {
@@ -143,7 +193,7 @@ const UserList = createWithRemoteLoader({
useEffect(() => {
handlerMount();
- }, [handlerMount, filter]);
+ }, [handlerMount, filter, selectedRowKeys]);
if (typeof children === 'function') {
return children({ filter: { value: filter, onChange: setFilter }, filterList, topOptions, tableOptions });
diff --git a/src/components/Tenant/UserList/useColumns.js b/src/components/Tenant/UserList/useColumns.js
index 5528aff..c8a0aaf 100644
--- a/src/components/Tenant/UserList/useColumns.js
+++ b/src/components/Tenant/UserList/useColumns.js
@@ -1,8 +1,11 @@
import { useMemo } from 'react';
import get from 'lodash/get';
+import { Tag } from 'antd';
import UserOrgTags from './UserOrgTags';
import buildRolesTitle from '../Role/buildRolesTitle';
+import { getSourceIcon, SOURCE_LABEL_MAP } from '../constants';
+
const getColumns = ({ formatMessage }) => {
return [
{
@@ -25,16 +28,6 @@ const getColumns = ({ formatMessage }) => {
primary: false,
hover: false
},
- {
- name: 'phone',
- title: formatMessage({ id: 'PhoneTitle' }),
- type: 'other'
- },
- {
- name: 'email',
- title: formatMessage({ id: 'Email' }),
- type: 'other'
- },
{
name: 'roles',
title: formatMessage({ id: 'UserRole' }),
@@ -57,6 +50,32 @@ const getColumns = ({ formatMessage }) => {
text: item.status === 'open' ? formatMessage({ id: 'Open' }) : formatMessage({ id: 'Close' })
})
},
+ {
+ name: 'syncSource',
+ title: formatMessage({ id: 'IsSynced' }),
+ type: 'other',
+ valueOf: item => {
+ if (!item.syncSource) {
+ return formatMessage({ id: 'SyncedInternal' });
+ }
+ const label = SOURCE_LABEL_MAP[item.syncSource] || item.syncSource;
+ return (
+
+ {label}
+
+ );
+ }
+ },
+ {
+ name: 'phone',
+ title: formatMessage({ id: 'PhoneTitle' }),
+ type: 'other'
+ },
+ {
+ name: 'email',
+ title: formatMessage({ id: 'Email' }),
+ type: 'other'
+ },
{
name: 'description',
type: 'description',
diff --git a/src/components/Tenant/UserList/useFilterList.js b/src/components/Tenant/UserList/useFilterList.js
index 75a6b10..f66cc00 100644
--- a/src/components/Tenant/UserList/useFilterList.js
+++ b/src/components/Tenant/UserList/useFilterList.js
@@ -36,6 +36,21 @@ const useFilterList = ({ formatMessage, apis, InputFilterItem, AdvancedSelectFil
]
})
}}
+ />,
+ ({
+ pageData: [
+ { label: formatMessage({ id: 'Yes' }), value: 'true' },
+ { label: formatMessage({ id: 'No' }), value: 'false' }
+ ]
+ })
+ }}
/>
]
],
diff --git a/src/components/Tenant/UserList/useListApi.js b/src/components/Tenant/UserList/useListApi.js
deleted file mode 100644
index a365268..0000000
--- a/src/components/Tenant/UserList/useListApi.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import { useMemo } from 'react';
-import merge from 'lodash/merge';
-
-const extractListBody = response => response?.data?.results ?? response?.data?.data ?? response?.data;
-
-const useListApi = ({ apis, filterValue, ajax }) => {
- return useMemo(() => {
- const api = merge({}, apis.list, {
- params: {
- filter: filterValue,
- ...(apis.list?.params || {})
- }
- });
- if (api.loader) {
- return api;
- }
- return Object.assign({}, api, {
- loader: async () => extractListBody(await ajax(merge({}, api)))
- });
- }, [apis.list, filterValue, ajax]);
-};
-
-export default useListApi;
diff --git a/src/components/Tenant/constants.js b/src/components/Tenant/constants.js
new file mode 100644
index 0000000..d0750f1
--- /dev/null
+++ b/src/components/Tenant/constants.js
@@ -0,0 +1,18 @@
+import { WechatWorkOutlined, DingdingOutlined, CloudOutlined } from '@ant-design/icons';
+
+const SOURCE_LABEL_MAP = {
+ wecom: '企业微信',
+ dingtalk: '钉钉'
+};
+
+const SOURCE_ICON_MAP = {
+ wecom: WechatWorkOutlined,
+ dingtalk: DingdingOutlined
+};
+
+const getSourceIcon = source => {
+ const IconComponent = SOURCE_ICON_MAP[source];
+ return IconComponent ? : ;
+};
+
+export { SOURCE_LABEL_MAP, SOURCE_ICON_MAP, getSourceIcon };
diff --git a/src/components/Tenant/locale/en-US.js b/src/components/Tenant/locale/en-US.js
index 9f1d24c..6852427 100644
--- a/src/components/Tenant/locale/en-US.js
+++ b/src/components/Tenant/locale/en-US.js
@@ -25,6 +25,11 @@ const locale = {
// UserList
FilterUserId: 'ID',
FilterStatus: 'Status',
+ FilterSynced: 'External Source',
+ SyncedExternal: 'Synced',
+ SyncedLocal: 'Manual',
+ SyncedInternal: 'Internal',
+ IsSynced: 'Source',
Keyword: 'Keyword',
Add: 'Add',
Operation: 'Operation',
diff --git a/src/components/Tenant/locale/zh-CN.js b/src/components/Tenant/locale/zh-CN.js
index 999f104..813ac87 100644
--- a/src/components/Tenant/locale/zh-CN.js
+++ b/src/components/Tenant/locale/zh-CN.js
@@ -25,6 +25,11 @@ const locale = {
// UserList
FilterUserId: 'ID',
FilterStatus: '状态',
+ FilterSynced: '外部来源',
+ SyncedExternal: '外部同步',
+ SyncedLocal: '手动添加',
+ SyncedInternal: '系统内部',
+ IsSynced: '来源',
Keyword: '关键字',
Add: '添加',
Operation: '操作',
@@ -235,6 +240,22 @@ const locale = {
OrgLinkSyncSuccess: '同步成功',
OrgSourceFrom: '来源:{source}',
EditOrgLeader: '修改负责人',
+
+ // SendMessage
+ SendOrgMessage: '发送{type}消息',
+ SendOrgMessageTitle: '发送{type}消息',
+ SendMessageTarget: '接收人',
+ SendMessageContent: '消息内容',
+ SendMessageType: '消息类型',
+ SendMessageTypeText: '文本',
+ SendMessageTypeMarkdown: 'Markdown',
+ SendMessagePlaceholder: '请输入消息内容',
+ SendMessageContentRequired: '请输入消息内容',
+ SendMessageSuccess: '已成功发送给 {count} 人',
+ SendMessageFailed: '发送失败',
+ Send: '发送',
+ SelectedCount: '已选: {count} 项',
+ DeselectAll: '取消',
};
export default locale;
diff --git a/src/components/TenantAdmin/TabDetail/Org/index.js b/src/components/TenantAdmin/TabDetail/Org/index.js
index 70e8d65..0ed99d5 100644
--- a/src/components/TenantAdmin/TabDetail/Org/index.js
+++ b/src/components/TenantAdmin/TabDetail/Org/index.js
@@ -8,72 +8,91 @@ import { Flex } from 'antd';
const Org = createWithRemoteLoader({
modules: ['components-core:Filter@filterToUrlParams', 'components-core:Global@usePreset']
-})(withLocale(({ remoteModules, tenant }) => {
- const [filterToUrlParams, usePreset] = remoteModules;
- const { apis } = usePreset();
- const { formatMessage } = useIntl();
- const [searchParams, setSearchParams] = useSearchParams();
- return (
- {
- const linkedSource = linkConfigData?.enabled ? linkConfigData.source : null;
- return (
- {
- return (
- {
- const next = new URLSearchParams(searchParams);
- next.set('tab', 'user');
- const filterParams = filterToUrlParams([
- { name: 'tenantOrgId', label: formatMessage({ id: 'Department' }), value: { label: org.name || String(org.id), value: String(org.id) } }
- ]);
- filterParams.forEach((value, key) => {
- next.set(key, value);
- });
- setSearchParams(next);
- }}
- apis={{
- create: Object.assign({}, apis.tenantAdmin.orgCreate, {
- data: { tenantId: tenant.id }
- }),
- save: Object.assign({}, apis.tenantAdmin.orgSave, {
- data: { tenantId: tenant.id }
- }),
- remove: Object.assign({}, apis.tenantAdmin.orgRemove, {
- data: { tenantId: tenant.id }
- }),
- userList: Object.assign({}, apis.tenantAdmin.userList, {
- params: { tenantId: tenant.id }
- }),
- import: apis.tenantAdmin.orgBatchImport
- }}
- />
- );
- }}
- />
- );
- }}
- />
- );
-}));
+})(
+ withLocale(({ remoteModules, tenant }) => {
+ const [filterToUrlParams, usePreset] = remoteModules;
+ const { apis } = usePreset();
+ const { formatMessage } = useIntl();
+ const [searchParams, setSearchParams] = useSearchParams();
+ return (
+ {
+ const linkedSource = linkConfigData?.enabled ? linkConfigData.source : null;
+ const syncSupported = linkConfigData?.syncSupported;
+ return (
+ {
+ return (
+ {
+ const next = new URLSearchParams(searchParams);
+ next.set('tab', 'user');
+ const filterParams = filterToUrlParams([
+ {
+ name: 'tenantOrgId',
+ label: formatMessage({ id: 'Department' }),
+ value: { label: org.name || String(org.id), value: String(org.id) }
+ }
+ ]);
+ filterParams.forEach((value, key) => {
+ next.set(key, value);
+ });
+ setSearchParams(next);
+ }}
+ apis={{
+ create: Object.assign({}, apis.tenantAdmin.orgCreate, {
+ data: { tenantId: tenant.id }
+ }),
+ save: Object.assign({}, apis.tenantAdmin.orgSave, {
+ data: { tenantId: tenant.id }
+ }),
+ remove: Object.assign({}, apis.tenantAdmin.orgRemove, {
+ data: { tenantId: tenant.id }
+ }),
+ userList: Object.assign({}, apis.tenantAdmin.userList, {
+ params: { tenantId: tenant.id }
+ }),
+ import: apis.tenantAdmin.orgBatchImport,
+ orgLinkConfig: Object.assign({}, apis.tenantAdmin.orgLinkConfig, {
+ params: { tenantId: tenant.id }
+ }),
+ orgLinkSave: Object.assign({}, apis.tenantAdmin.orgLinkSave, {
+ data: { tenantId: tenant.id }
+ }),
+ orgLinkSync: Object.assign({}, apis.tenantAdmin.orgLinkSync, {
+ data: { tenantId: tenant.id }
+ }),
+ orgLinkCancel: Object.assign({}, apis.tenantAdmin.orgLinkCancel, {
+ data: { tenantId: tenant.id }
+ })
+ }}
+ />
+ );
+ }}
+ />
+ );
+ }}
+ />
+ );
+ })
+);
export default Org;
diff --git a/src/components/TenantAdmin/TabDetail/User/index.js b/src/components/TenantAdmin/TabDetail/User/index.js
index 47e1ae2..caefa92 100644
--- a/src/components/TenantAdmin/TabDetail/User/index.js
+++ b/src/components/TenantAdmin/TabDetail/User/index.js
@@ -22,7 +22,8 @@ const User = createWithRemoteLoader({
remove: Object.assign({}, apis.tenantAdmin.userRemove, { data: { tenantId: tenant.id } }),
setStatus: Object.assign({}, apis.tenantAdmin.userSetStatus, { data: { tenantId: tenant.id } }),
inviteToken: Object.assign({}, apis.tenantAdmin.userInviteToken, { params: { tenantId: tenant.id } }),
- userInviteMessage: Object.assign({}, apis.tenantAdmin.userInviteMessage, { data: { tenantId: tenant.id } })
+ userInviteMessage: Object.assign({}, apis.tenantAdmin.userInviteMessage, { data: { tenantId: tenant.id } }),
+ sendOrgMessage: Object.assign({}, apis.tenantAdmin.sendOrgMessage, { data: { tenantId: tenant.id } })
},
pluginApis
)}
diff --git a/src/components/TenantAdmin/locale/en-US.js b/src/components/TenantAdmin/locale/en-US.js
index bb3893c..dfa07e6 100644
--- a/src/components/TenantAdmin/locale/en-US.js
+++ b/src/components/TenantAdmin/locale/en-US.js
@@ -81,6 +81,9 @@ const locale = {
OrgLinkSaveSuccess: 'Link configuration saved successfully',
OrgLinkCancelSuccess: 'Link cancelled successfully',
OrgLinkSyncSuccess: 'Sync completed successfully',
+ OrgLinkStatus: 'Sync Status',
+ OrgLinkSyncPendingTip: 'The first sync has not completed yet. Please wait for it to finish before manually syncing.',
+ OrgLinkSyncRunningTip: 'Sync is in progress. Please wait for it to complete.',
OrgSourceFrom: 'Source: {source}',
EditOrgLeader: 'Edit Leader'
};
diff --git a/src/components/TenantAdmin/locale/zh-CN.js b/src/components/TenantAdmin/locale/zh-CN.js
index 0c68cec..50e916e 100644
--- a/src/components/TenantAdmin/locale/zh-CN.js
+++ b/src/components/TenantAdmin/locale/zh-CN.js
@@ -80,6 +80,9 @@ const locale = {
OrgLinkSaveSuccess: '关联配置保存成功',
OrgLinkCancelSuccess: '已取消关联',
OrgLinkSyncSuccess: '同步成功',
+ OrgLinkStatus: '同步状态',
+ OrgLinkSyncPendingTip: '首次同步尚未完成,请等待同步完成后再手动同步',
+ OrgLinkSyncRunningTip: '正在同步中,请等待完成后再操作',
OrgSourceFrom: '来源:{source}',
EditOrgLeader: '修改负责人',
};
diff --git a/src/components/UserSelect/index.js b/src/components/UserSelect/index.js
index fc94d52..03b0827 100644
--- a/src/components/UserSelect/index.js
+++ b/src/components/UserSelect/index.js
@@ -28,7 +28,7 @@ const createComponent = (callback = item => item) => {
pageData: (data.pageData || []).map(item =>
Object.assign({}, item, {
value: item.id,
- label: item.nickname || item.email || item.phone
+ label: item.nickname || item.name || item.email || item.phone
})
)
});