From 26c53003e76d3ac8113367b15609fffdf8265a7d Mon Sep 17 00:00:00 2001 From: sealday Date: Sun, 15 Sep 2024 12:15:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=94=A8=E6=88=B7=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=20(#1540)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: sealday Reviewed-on: https://git.daoyoucloud.com/daoyoucloud/tachybase/pulls/1540 --- .../user-settings/UserSettingsLayout.tsx | 169 +++++++++++++++++- .../src/built-in/user-settings/style.ts | 81 +++++++++ packages/core/client/src/user/EditProfile.tsx | 19 +- .../plugin-users/src/client/UserProfile.tsx | 5 + .../plugin-users/src/client/index.ts | 7 + 5 files changed, 263 insertions(+), 18 deletions(-) create mode 100644 packages/core/client/src/built-in/user-settings/style.ts create mode 100644 packages/plugins/@tachybase/plugin-users/src/client/UserProfile.tsx diff --git a/packages/core/client/src/built-in/user-settings/UserSettingsLayout.tsx b/packages/core/client/src/built-in/user-settings/UserSettingsLayout.tsx index f77b1125f..50c96c6ac 100644 --- a/packages/core/client/src/built-in/user-settings/UserSettingsLayout.tsx +++ b/packages/core/client/src/built-in/user-settings/UserSettingsLayout.tsx @@ -1,7 +1,170 @@ -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; -import { Outlet } from 'react-router'; +import { Layout, Menu, Result } from 'antd'; +import { Navigate, Outlet, useLocation, useNavigate, useParams } from 'react-router'; + +import { PluginSettingsPageType, useApp } from '../../application'; +import { USER_SETTINGS_PATH, UserSettingsPageType } from '../../application/UserSettingsManager'; +import { PageHeader, useCompile } from '../../schema-component'; +import { useStyles } from './style'; + +function getMenuItems(list: PluginSettingsPageType[]) { + return list.map((item) => { + return { + key: item.name, + label: item.label, + title: item.title, + icon: item.icon, + children: item.children?.length ? getMenuItems(item.children) : undefined, + }; + }); +} + +function matchRoute(data, url) { + const keys = Object.keys(data); + if (data[url]) { + return data[url]; + } + for (const pattern of keys) { + const regexPattern = pattern.replace(/:[^/]+/g, '([^/]+)'); + const match = url.match(new RegExp(`^${regexPattern}$`)); + + if (match) { + return data[pattern]; + } + } + + return null; +} + +function replaceRouteParams(urlTemplate, params) { + // 使用正则表达式替换占位符 + return urlTemplate.replace(/:\w+/g, (match) => { + const paramName = match.substring(1); + return params?.[paramName] || match; + }); +} export const UserSettingsLayout = () => { - return ; + const { styles, theme } = useStyles(); + const app = useApp(); + const navigate = useNavigate(); + const location = useLocation(); + const params = useParams(); + const compile = useCompile(); + const settings = useMemo(() => { + const list = app.userSettingsManager.getList(); + // compile title + function traverse(settings: UserSettingsPageType[]) { + settings.forEach((item) => { + item.title = compile(item.title); + item.label = compile(item.title); + if (item.children?.length) { + traverse(item.children); + } + }); + } + traverse(list); + return list; + }, [app.userSettingsManager, compile]); + const getFirstDeepChildPath = useCallback((settings: UserSettingsPageType[]) => { + if (!settings || !settings.length) { + return '/admin'; + } + const first = settings[0]; + if (first.children?.length) { + return getFirstDeepChildPath(first.children); + } + return first.path; + }, []); + + const settingsMapByPath = useMemo>(() => { + const map = {}; + const traverse = (settings: UserSettingsPageType[]) => { + settings.forEach((item) => { + map[item.path] = item; + if (item.children?.length) { + traverse(item.children); + } + }); + }; + traverse(settings); + return map; + }, [settings]); + const currentSetting = useMemo( + () => matchRoute(settingsMapByPath, location.pathname), + [location.pathname, settingsMapByPath], + ); + const currentTopLevelSetting = useMemo(() => { + if (!currentSetting) { + return null; + } + return settings.find((item) => item.name === currentSetting.topLevelName); + }, [currentSetting, settings]); + const sidebarMenus = useMemo(() => { + return getMenuItems(settings.filter((v) => v.isTopLevel !== false).map((item) => ({ ...item, children: null }))); + }, [settings]); + if (!currentSetting || location.pathname === USER_SETTINGS_PATH || location.pathname === USER_SETTINGS_PATH + '/') { + return ; + } + if (location.pathname === currentTopLevelSetting.path && currentTopLevelSetting.children?.length > 0) { + return ; + } + return ( +
+ + + { + const plugin = settings.find((item) => item.name === key); + if (plugin.children?.length) { + return navigate(getFirstDeepChildPath(plugin.children)); + } else { + return navigate(plugin.path); + } + }} + items={sidebarMenus} + /> + + + {currentSetting && ( + 0 && currentTopLevelSetting.showTabs !== false + ? 0 + : theme.paddingSM, + }} + ghost={false} + title={currentTopLevelSetting.title} + footer={ + currentTopLevelSetting.children?.length > 0 && + currentTopLevelSetting.showTabs !== false && ( + { + navigate(replaceRouteParams(app.pluginSettingsManager.getRoutePath(key), params)); + }} + selectedKeys={[currentSetting?.name]} + mode="horizontal" + items={getMenuItems(currentTopLevelSetting.children)} + > + ) + } + /> + )} +
+ {currentSetting ? ( + + ) : ( + + )} +
+
+ +
+ ); }; diff --git a/packages/core/client/src/built-in/user-settings/style.ts b/packages/core/client/src/built-in/user-settings/style.ts new file mode 100644 index 000000000..d866315b2 --- /dev/null +++ b/packages/core/client/src/built-in/user-settings/style.ts @@ -0,0 +1,81 @@ +import { createStyles } from 'antd-style'; + +export const useStyles = createStyles(({ token, css }) => { + return { + cardActionDisabled: { + color: token.colorTextDisabled, + cursor: 'not-allowed', + }, + pageHeader: { + backgroundColor: token.colorBgContainer, + paddingTop: token.paddingSM, + paddingBottom: 0, + paddingInline: token.paddingLG, + '.ant-page-header-footer': { marginBlockStart: '0' }, + '& .ant-tabs-nav': { + marginBottom: 0, + }, + '.ant-page-header-heading-title': { + color: token.colorText, + }, + }, + pageContent: { + margin: token.marginLG, + }, + // pageContent: { + // marginTop: token.margin, + // marginBottom: token.marginLG, + // background: 'transparent', + // minHeight: '80vh', + // }, + + PluginDetailBaseInfo: { + display: 'flex', + flexDirection: 'column', + marginBottom: token.margin, + }, + + PluginDocument: { + background: token.colorBgContainer, + padding: token.paddingLG, + height: '60vh', + overflowY: 'auto', + }, + + avatar: { + '.ant-card-meta-avatar': { + marginTop: '8px', + '.ant-avatar': { borderRadius: '2px' }, + }, + }, + + version: { + display: 'block', + color: token.colorTextDescription, + fontWeight: 'normal', + fontSize: token.fontSize, + }, + card: css` + .ant-card-actions { + li .ant-space { + gap: 2px !important; + } + li a { + .anticon { + margin-right: 3px; + /* display: none; */ + } + } + li:last-child { + width: 20% !important; + } + li:first-child { + width: 80% !important; + border-inline-end: 0; + text-align: left; + padding-left: 16px; + } + } + `, + }; +}); diff --git a/packages/core/client/src/user/EditProfile.tsx b/packages/core/client/src/user/EditProfile.tsx index a97ca7d92..c5338058e 100644 --- a/packages/core/client/src/user/EditProfile.tsx +++ b/packages/core/client/src/user/EditProfile.tsx @@ -3,6 +3,7 @@ import { ISchema, uid, useForm } from '@tachybase/schema'; import { MenuProps } from 'antd'; import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router'; import { ActionContextProvider, @@ -123,6 +124,7 @@ const schema: ISchema = { export const useEditProfile = () => { const ctx = useContext(DropdownVisibleContext); const [visible, setVisible] = useState(false); + const navigate = useNavigate(); const { t } = useTranslation(); return useMemo(() => { @@ -130,22 +132,9 @@ export const useEditProfile = () => { key: 'profile', eventKey: 'EditProfile', onClick: () => { - setVisible(true); - ctx?.setVisible(false); + navigate('/admin/profilers/user-profile'); }, - label: ( -
- {t('Edit profile')} - -
e.stopPropagation()}> - -
-
-
- ), + label:
{t('Edit profile')}
, }; }, [visible]); }; diff --git a/packages/plugins/@tachybase/plugin-users/src/client/UserProfile.tsx b/packages/plugins/@tachybase/plugin-users/src/client/UserProfile.tsx new file mode 100644 index 000000000..6ec318066 --- /dev/null +++ b/packages/plugins/@tachybase/plugin-users/src/client/UserProfile.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export const UserProfile = () => { + return
user profile
; +}; diff --git a/packages/plugins/@tachybase/plugin-users/src/client/index.ts b/packages/plugins/@tachybase/plugin-users/src/client/index.ts index 9ae1caea4..9523162b5 100644 --- a/packages/plugins/@tachybase/plugin-users/src/client/index.ts +++ b/packages/plugins/@tachybase/plugin-users/src/client/index.ts @@ -2,6 +2,7 @@ import { Plugin, tval } from '@tachybase/client'; import ACLPlugin from '@tachybase/plugin-acl/client'; import { RoleUsersManager } from './RoleUsersManager'; +import { UserProfile } from './UserProfile'; import { UsersManagement } from './UsersManagement'; class PluginUsersClient extends Plugin { @@ -17,6 +18,12 @@ class PluginUsersClient extends Plugin { aclSnippet: 'pm.users', }); + this.userSettingsManager.add('user-profile', { + icon: 'UserOutlined', + title: tval('Edit profile'), + Component: UserProfile, + }); + const acl = this.app.pm.get(ACLPlugin); acl.rolesManager.add('users', { title: tval('Users'),