diff --git a/package.json b/package.json index a9aca5f..07b3d11 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kne-components/components-admin", - "version": "1.1.41", + "version": "1.1.42", "description": "用于实现一个后台管理系统的必要组件", "scripts": { "init": "husky", diff --git a/src/components/Apis/getApis.js b/src/components/Apis/getApis.js index 12a9244..08f795d 100644 --- a/src/components/Apis/getApis.js +++ b/src/components/Apis/getApis.js @@ -239,6 +239,23 @@ const getApis = options => { url: `${prefix}/tenant/admin/org-batch-import`, method: 'POST' }, + orgLinkConfig: { + url: `${prefix}/tenant/admin/org-link-config`, + method: 'GET' + }, + orgLinkSave: { + url: `${prefix}/tenant/admin/org-link-save`, + method: 'POST' + }, + orgLinkCancel: { + url: `${prefix}/tenant/admin/org-link-cancel`, + method: 'POST' + }, + orgLinkSync: { + url: `${prefix}/tenant/admin/org-link-sync`, + method: 'POST' + }, + userList: { url: `${prefix}/tenant/admin/user-list`, method: 'GET' @@ -407,6 +424,10 @@ const getApis = options => { url: `${prefix}/tenant/org-batch-import`, method: 'POST' }, + orgLinkConfig: { + url: `${prefix}/tenant/org-link-config`, + method: 'GET' + }, userList: { url: `${prefix}/tenant/user-list`, method: 'GET' diff --git a/src/components/MessageManger/Dashboard/useRealtimeStatisticsSSE.js b/src/components/MessageManger/Dashboard/useRealtimeStatisticsSSE.js index 9f2a97b..cf9d543 100644 --- a/src/components/MessageManger/Dashboard/useRealtimeStatisticsSSE.js +++ b/src/components/MessageManger/Dashboard/useRealtimeStatisticsSSE.js @@ -1,7 +1,8 @@ -import { useEffect, useState } from 'react'; +import { useMemo, useState } from 'react'; import { getToken } from '@kne/token-storage'; import { buildUrlWithParams } from './constants'; import { getClientIanaTimezone } from '../utils'; +import useManagedEventSource from '../../../utils/useManagedEventSource'; const isLikelyStatisticsPayload = obj => obj && @@ -44,21 +45,19 @@ const useRealtimeStatisticsSSE = sseUrl => { const [isConnected, setIsConnected] = useState(false); const [lastUpdatedAt, setLastUpdatedAt] = useState(null); - useEffect(() => { - if (!sseUrl || typeof window === 'undefined' || typeof window.EventSource !== 'function') { - return undefined; - } - - const source = new EventSource( - buildUrlWithParams(sseUrl, { - interval: 5, - token: getToken('X-User-Token'), - timezone: getClientIanaTimezone() - }) - ); + const streamUrl = useMemo(() => { + if (!sseUrl) return null; + return buildUrlWithParams(sseUrl, { + interval: 5, + token: getToken('X-User-Token'), + timezone: getClientIanaTimezone() + }); + }, [sseUrl]); - source.onopen = () => setIsConnected(true); - source.onmessage = event => { + useManagedEventSource(streamUrl, { + onOpen: () => setIsConnected(true), + onError: () => setIsConnected(false), + onMessage: event => { try { const parsed = JSON.parse(event.data); const nextData = unwrapStatisticsPayload(parsed); @@ -69,13 +68,8 @@ const useRealtimeStatisticsSSE = sseUrl => { } catch { // ignore parse errors } - }; - source.onerror = () => setIsConnected(false); - - return () => { - source.close(); - }; - }, [sseUrl]); + } + }); return { realtimeData, isConnected, lastUpdatedAt }; }; diff --git a/src/components/MessageQueue/Dashboard/index.js b/src/components/MessageQueue/Dashboard/index.js index 2976339..2b452c7 100644 --- a/src/components/MessageQueue/Dashboard/index.js +++ b/src/components/MessageQueue/Dashboard/index.js @@ -1,12 +1,13 @@ import { createWithRemoteLoader } from '@kne/remote-loader'; import Fetch from '@kne/react-fetch'; -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { Button, Card, Col, Row, Space, Statistic, Table, Tag } from 'antd'; import { ReloadOutlined } from '@ant-design/icons'; import withLocale from '../withLocale'; import { useIntl } from '@kne/react-intl'; import Menu from '../Menu'; import { buildUrlWithParams, formatPercent, formatRate, getMetricTotal } from '../utils'; +import useManagedEventSource from '../../../utils/useManagedEventSource'; const toTopicRows = current => { const topics = new Set([ @@ -42,29 +43,22 @@ const DashboardContent = withLocale(({ Page, baseUrl, pageProps, apis, initialDa setLastUpdatedAt(initialData?.timestamp); }, [initialData]); - useEffect(() => { - if (!apis?.mq?.dashboard?.sse?.url || typeof window === 'undefined' || typeof window.EventSource !== 'function') { - return undefined; - } + const mqStreamUrl = useMemo(() => { + const url = apis?.mq?.dashboard?.sse?.url; + if (!url) return null; + return buildUrlWithParams(url, { interval: 1000 }); + }, [apis]); - const source = new EventSource(buildUrlWithParams(apis.mq.dashboard.sse.url, { interval: 1000 })); - source.onopen = () => { - setIsConnected(true); - }; - source.onmessage = event => { + useManagedEventSource(mqStreamUrl, { + onOpen: () => setIsConnected(true), + onError: () => setIsConnected(false), + onMessage: event => { const nextData = JSON.parse(event.data); setData(nextData); setLastUpdatedAt(nextData.timestamp || Date.now()); setIsConnected(true); - }; - source.onerror = () => { - setIsConnected(false); - }; - - return () => { - source.close(); - }; - }, [apis]); + } + }); const current = data?.current || {}; const rows = toTopicRows(current); diff --git a/src/components/Task/locale/zh-CN.js b/src/components/Task/locale/zh-CN.js index c02af7e..5326b4a 100644 --- a/src/components/Task/locale/zh-CN.js +++ b/src/components/Task/locale/zh-CN.js @@ -30,7 +30,7 @@ const locale = { // Status Enums Pending: '等待执行', Running: '执行中', - Waiting: '等待操作', + Waiting: '等待', Success: '成功', Failed: '失败', Canceled: '已取消', @@ -76,7 +76,7 @@ const locale = { ManualExecutionStats: '手动执行任务', ManualExecutionGoMyTaskTitle: '点击此处查看我的任务', ManualExecutionTasks: '手动执行数量', - ManualPendingTasks: '等待操作', + ManualPendingTasks: '等待', ManualExecutedTasks: '当日完成', ManualPendingMaxWaitLabel: '最长等待', ManualCompletedTotalDurationLabel: '总耗时', diff --git a/src/components/Tenant/JoinInvitation/index.js b/src/components/Tenant/JoinInvitation/index.js index 8dcff3e..a3969a4 100644 --- a/src/components/Tenant/JoinInvitation/index.js +++ b/src/components/Tenant/JoinInvitation/index.js @@ -8,8 +8,8 @@ import Fetch from '@kne/react-fetch'; import CountDown from '@kne/count-down'; import { useSearchParams, useNavigate } from 'react-router-dom'; import CompanyInfo from '../CompanyInfo'; -import TenantUserPersonalCard from '../UserList/TenantUserPersonalCard'; -import normalizeTenantUserForPersonalCard from '../UserList/normalizeTenantUserForPersonalCard'; +import TenantUserPersonalCard from '../UserList/UserPersonalCard'; + import withLocale from '../withLocale'; import { useIntl } from '@kne/react-intl'; import style from './style.module.scss'; @@ -174,7 +174,7 @@ const JoinInvitation = createWithRemoteLoader({ {formatMessage({ id: 'ConfirmEmployeeInfoHint' })} diff --git a/src/components/Tenant/OrgInfo/LeaderFormInner.js b/src/components/Tenant/OrgInfo/LeaderFormInner.js new file mode 100644 index 0000000..3d77252 --- /dev/null +++ b/src/components/Tenant/OrgInfo/LeaderFormInner.js @@ -0,0 +1,37 @@ +import { createWithRemoteLoader } from '@kne/remote-loader'; +import { useIntl } from '@kne/react-intl'; +import merge from 'lodash/merge'; +import withLocale from '../withLocale'; + +const LeaderFormInnerCore = createWithRemoteLoader({ + modules: ['components-core:FormInfo'] +})(({ remoteModules, apis, orgId }) => { + const [FormInfo] = remoteModules; + const { formatMessage } = useIntl(); + const { SuperSelect } = FormInfo.fields; + const list = []; + if (apis?.userList) { + list.push( + + ); + } + return ; +}); + +export default withLocale(LeaderFormInnerCore); diff --git a/src/components/Tenant/OrgInfo/OrgLinkSetting.js b/src/components/Tenant/OrgInfo/OrgLinkSetting.js new file mode 100644 index 0000000..0b29f05 --- /dev/null +++ b/src/components/Tenant/OrgInfo/OrgLinkSetting.js @@ -0,0 +1,225 @@ +import { createWithRemoteLoader } from '@kne/remote-loader'; +import { Flex, Tag, Button, App, Descriptions, Popconfirm, Alert, Space } from 'antd'; +import { LinkOutlined, DisconnectOutlined, CloudSyncOutlined } from '@ant-design/icons'; +import { useState } from 'react'; +import withLocale from '../withLocale'; +import { useIntl } from '@kne/react-intl'; +import Fetch from '@kne/react-fetch'; +import merge from 'lodash/merge'; + +const SOURCE_OPTIONS = [ + { value: 'wecom', label: '企业微信' }, + { value: 'dingtalk', label: '钉钉' } +]; + +const SYNC_INTERVAL_OPTIONS = [ + { value: 'daily', label: '每天' }, + { value: 'weekly', label: '每7天' }, + { value: 'monthly', label: '每个月' }, + { value: 'yearly', label: '每年' }, + { value: 'off', label: '关闭' } +]; + +const getSourceLabel = value => { + const item = SOURCE_OPTIONS.find(o => o.value === value); + return item ? item.label : value; +}; + +const getSyncIntervalLabel = value => { + const item = SYNC_INTERVAL_OPTIONS.find(o => o.value === value); + return item ? item.label : value; +}; + +const LinkFormInner = withLocale(createWithRemoteLoader({ + modules: ['components-core:FormInfo', 'components-core:Global@usePreset'] +})(({ remoteModules, tenantId, envArgs, onSuccess }) => { + const [FormInfo, usePreset] = remoteModules; + const { apis, ajax } = usePreset(); + const { formatMessage } = useIntl(); + const { message } = App.useApp(); + const { Select, RadioGroup } = FormInfo.fields; + + return ( + ({ + value: item.value, + label: item.label + }))} + />, + ({ + value: item.value, + label: item.label + }))} + />, +