From f719c1160673c2cd44d1fc8fae9aec2a2c4cf900 Mon Sep 17 00:00:00 2001 From: jialin Date: Mon, 29 Jun 2026 14:48:48 +0800 Subject: [PATCH] refactor(perferences): use center wrapper layout --- src/locales/en-US/common.ts | 7 + src/locales/en-US/users.ts | 2 + src/locales/ja-JP/common.ts | 6 + src/locales/ja-JP/users.ts | 2 + src/locales/ru-RU/common.ts | 6 + src/locales/ru-RU/users.ts | 2 + src/locales/tr-TR/common.ts | 6 + src/locales/tr-TR/users.ts | 2 + src/locales/zh-CN/common.ts | 4 + src/locales/zh-CN/users.ts | 1 + src/pages/profile/components/appearance.tsx | 170 ++++++++++++------ .../components/modify-password-form.tsx | 132 ++++++++++++++ src/pages/profile/components/public-key.tsx | 36 ---- src/pages/profile/components/security.tsx | 39 ++++ .../profile/components/settings-group.tsx | 84 +++++++++ .../profile/components/settings-section.tsx | 53 ++++++ .../profile/components/theme-preview.tsx | 119 ++++++++++++ src/pages/profile/index-copy.tsx | 69 +++++++ src/pages/profile/index.tsx | 88 ++++----- 19 files changed, 684 insertions(+), 144 deletions(-) create mode 100644 src/pages/profile/components/modify-password-form.tsx delete mode 100644 src/pages/profile/components/public-key.tsx create mode 100644 src/pages/profile/components/security.tsx create mode 100644 src/pages/profile/components/settings-group.tsx create mode 100644 src/pages/profile/components/settings-section.tsx create mode 100644 src/pages/profile/components/theme-preview.tsx create mode 100644 src/pages/profile/index-copy.tsx diff --git a/src/locales/en-US/common.ts b/src/locales/en-US/common.ts index 54d0c0ac..b44c87d3 100644 --- a/src/locales/en-US/common.ts +++ b/src/locales/en-US/common.ts @@ -198,6 +198,8 @@ export default { 'common.table.user': 'User', 'common.settings.instructions': 'Instructions', 'common.settings.language': 'Language', + 'common.settings.language.tips': + 'Set the display language for the interface.', 'common.delete.confirm': 'Are you sure you want to delete the selected {type}?', 'common.delete.single.confirm': @@ -251,6 +253,11 @@ export default { 'common.appearance.tips': 'Default follows system preference.', 'common.button.forgotpassword': 'Forgot password?', 'common.appearance.theme': 'Theme', + 'common.appearance.description': + 'Customize how the interface looks on your device.', + 'common.security': 'Security', + 'common.security.description': + 'Manage the password used to sign in to your account.', 'common.page.wentwrong': 'Something went wrong.', 'common.page.refresh.tips': 'The page may need to be updated. Try refreshing it!', diff --git a/src/locales/en-US/users.ts b/src/locales/en-US/users.ts index d18098c4..f08bd681 100644 --- a/src/locales/en-US/users.ts +++ b/src/locales/en-US/users.ts @@ -28,6 +28,8 @@ export default { 'users.password.modify.title': 'Modify Password', 'users.password.modify.description': "For your account's security, please change your initial password.", + 'users.password.modify.tips': + 'Regularly updating your password helps keep your account secure.', 'users.password.confirm': 'Confirm New Password', 'users.password.confirm.empty': 'Please confirm the new password.', 'users.password.confirm.error': 'The two passwords entered do not match.', diff --git a/src/locales/ja-JP/common.ts b/src/locales/ja-JP/common.ts index dfb3c321..22bfb2a6 100644 --- a/src/locales/ja-JP/common.ts +++ b/src/locales/ja-JP/common.ts @@ -199,6 +199,7 @@ export default { 'common.table.user': 'ユーザー', 'common.settings.instructions': '手順', 'common.settings.language': '言語', + 'common.settings.language.tips': 'インターフェースの表示言語を設定します。', 'common.delete.confirm': '選択した {type} を削除してもよろしいですか?', 'common.delete.single.confirm': '{name} を削除してもよろしいですか?', @@ -251,6 +252,11 @@ export default { 'common.appearance.tips': 'Default follows system preference.', 'common.button.forgotpassword': 'Forgot password?', 'common.appearance.theme': 'Theme', + 'common.appearance.description': + 'デバイス上でのインターフェースの表示をカスタマイズします。', + 'common.security': 'セキュリティ', + 'common.security.description': + 'アカウントへのログインに使用するパスワードを管理します。', 'common.page.wentwrong': 'Something went wrong.', 'common.page.refresh.tips': 'The page may need to be updated. Try refreshing it!', diff --git a/src/locales/ja-JP/users.ts b/src/locales/ja-JP/users.ts index 090ce1a0..894cbb68 100644 --- a/src/locales/ja-JP/users.ts +++ b/src/locales/ja-JP/users.ts @@ -29,6 +29,8 @@ export default { 'users.password.modify.title': 'パスワードを変更', 'users.password.modify.description': 'アカウントのセキュリティのため、初期パスワードを変更してください。', + 'users.password.modify.tips': + 'パスワードを定期的に更新すると、アカウントの安全を保てます。', 'users.password.confirm': '新しいパスワードを確認', 'users.password.confirm.empty': '新しいパスワードを確認してください。', 'users.password.confirm.error': '入力された2つのパスワードが一致しません。', diff --git a/src/locales/ru-RU/common.ts b/src/locales/ru-RU/common.ts index 7e870fb7..9af9534d 100644 --- a/src/locales/ru-RU/common.ts +++ b/src/locales/ru-RU/common.ts @@ -197,6 +197,7 @@ export default { 'common.table.user': 'Пользователь', 'common.settings.instructions': 'Инструкции', 'common.settings.language': 'Язык', + 'common.settings.language.tips': 'Задайте язык отображения интерфейса.', 'common.delete.confirm': 'Вы уверены, что хотите удалить выбранный {type}?', 'common.delete.single.confirm': 'Вы уверены, что хотите удалить {name}?', @@ -250,6 +251,11 @@ export default { 'common.appearance.tips': 'По умолчанию соответствует системным настройкам.', 'common.button.forgotpassword': 'Забыли пароль?', 'common.appearance.theme': 'Тема', + 'common.appearance.description': + 'Настройте внешний вид интерфейса на вашем устройстве.', + 'common.security': 'Безопасность', + 'common.security.description': + 'Управляйте паролем для входа в учётную запись.', 'common.page.wentwrong': 'Что-то пошло не так.', 'common.page.refresh.tips': 'Страница может нуждаться в обновлении. Попробуйте обновить её!', diff --git a/src/locales/ru-RU/users.ts b/src/locales/ru-RU/users.ts index a9b9e144..6e7c3af0 100644 --- a/src/locales/ru-RU/users.ts +++ b/src/locales/ru-RU/users.ts @@ -29,6 +29,8 @@ export default { 'users.password.modify.title': 'Смена пароля', 'users.password.modify.description': 'В целях безопасности измените первоначальный пароль.', + 'users.password.modify.tips': + 'Регулярное обновление пароля помогает защитить вашу учётную запись.', 'users.password.confirm': 'Подтвердите новый пароль', 'users.password.confirm.empty': 'Подтвердите новый пароль', 'users.password.confirm.error': 'Пароли не совпадают', diff --git a/src/locales/tr-TR/common.ts b/src/locales/tr-TR/common.ts index f43b12d1..e0383b1c 100644 --- a/src/locales/tr-TR/common.ts +++ b/src/locales/tr-TR/common.ts @@ -199,6 +199,7 @@ export default { 'common.table.user': 'Kullanıcı', 'common.settings.instructions': 'Talimatlar', 'common.settings.language': 'Dil', + 'common.settings.language.tips': 'Arayüzün görüntüleme dilini ayarlayın.', 'common.delete.confirm': 'Seçili {type} öğesini silmek istediğinizden emin misiniz?', 'common.delete.single.confirm': @@ -254,6 +255,11 @@ export default { 'common.appearance.tips': 'Varsayılan olarak sistem tercihini takip eder.', 'common.button.forgotpassword': 'Şifrenizi mi unuttunuz?', 'common.appearance.theme': 'Tema', + 'common.appearance.description': + 'Arayüzün cihazınızdaki görünümünü özelleştirin.', + 'common.security': 'Güvenlik', + 'common.security.description': + 'Hesabınıza giriş yapmak için kullanılan parolayı yönetin.', 'common.page.wentwrong': 'Bir şeyler ters gitti.', 'common.page.refresh.tips': 'Sayfanın güncellenmesi gerekebilir. Yenilemeyi deneyin!', diff --git a/src/locales/tr-TR/users.ts b/src/locales/tr-TR/users.ts index a35e56f4..ae892426 100644 --- a/src/locales/tr-TR/users.ts +++ b/src/locales/tr-TR/users.ts @@ -29,6 +29,8 @@ export default { 'users.password.modify.title': 'Şifreyi Değiştir', 'users.password.modify.description': 'Hesabınızın güvenliği için lütfen başlangıç şifrenizi değiştirin.', + 'users.password.modify.tips': + 'Parolanızı düzenli olarak güncellemek hesabınızın güvenliğini korumaya yardımcı olur.', 'users.password.confirm': 'Yeni Şifreyi Onayla', 'users.password.confirm.empty': 'Lütfen yeni şifreyi tekrar girin.', 'users.password.confirm.error': 'Girilen iki şifre eşleşmiyor.', diff --git a/src/locales/zh-CN/common.ts b/src/locales/zh-CN/common.ts index 43fdff0f..49a5b8a6 100644 --- a/src/locales/zh-CN/common.ts +++ b/src/locales/zh-CN/common.ts @@ -190,6 +190,7 @@ export default { 'common.table.user': '用户', 'common.settings.instructions': '操作指引', 'common.settings.language': '语言', + 'common.settings.language.tips': '设置界面的显示语言。', 'common.delete.confirm': '确定删除选中的{type}吗?', 'common.delete.single.confirm': '确定删除以下内容?\n {name}', @@ -246,6 +247,9 @@ export default { 'common.appearance.tips': '默认跟随系统设置', 'common.button.forgotpassword': '忘记密码?', 'common.appearance.theme': '主题', + 'common.appearance.description': '自定义界面在您设备上的视觉表现。', + 'common.security': '安全设置', + 'common.security.description': '管理用于登录账户的密码。', 'common.page.wentwrong': '哎呀,出了点问题', 'common.page.refresh.tips': '页面似乎需要更新,刷新一下试试吧!', 'common.tips.escape.disable': '请点击「取消」按钮或右上角 X 关闭窗口', diff --git a/src/locales/zh-CN/users.ts b/src/locales/zh-CN/users.ts index c371905e..0b900b18 100644 --- a/src/locales/zh-CN/users.ts +++ b/src/locales/zh-CN/users.ts @@ -27,6 +27,7 @@ export default { 'users.password.length': '长度在6至64个字符之间', 'users.password.modify.title': '修改密码', 'users.password.modify.description': '为了确保您的账户安全,请修改初始密码', + 'users.password.modify.tips': '定期更新密码有助于保护账户安全。', 'users.password.confirm': '确认新密码', 'users.password.confirm.empty': '请确认新密码', 'users.password.confirm.error': '两次输入的密码不一致', diff --git a/src/pages/profile/components/appearance.tsx b/src/pages/profile/components/appearance.tsx index 5993e2fb..04a91228 100644 --- a/src/pages/profile/components/appearance.tsx +++ b/src/pages/profile/components/appearance.tsx @@ -1,92 +1,150 @@ import useUserSettings from '@/hooks/use-user-settings'; import langConfigMap from '@/locales/lang-config-map'; -import { MoonOutlined, SunOutlined } from '@ant-design/icons'; +import { CheckCircleFilled } from '@ant-design/icons'; import { BaseSelect } from '@gpustack/core-ui'; import { getAllLocales, setLocale, useIntl } from '@umijs/max'; +import { createStyles } from 'antd-style'; +import classNames from 'classnames'; import _ from 'lodash'; import React from 'react'; -import styled from 'styled-components'; -const Wrapper = styled.div` - display: flex; - flex-direction: column; - gap: 16px; - padding: 16px 0; -`; +import { SettingRow, SettingsGroup } from './settings-group'; +import ThemePreview, { PreviewMode } from './theme-preview'; -const SettingsItem = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - max-width: 300px; - .label { +const useStyles = createStyles(({ token, css }) => ({ + cards: css` + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16px; + `, + card: css` + padding: 8px; + border: 1px solid ${token.colorBorderSecondary}; + border-radius: ${token.borderRadiusLG + 2}px; + background: ${token.colorBgContainer}; + cursor: pointer; + transition: + border-color 0.2s, + box-shadow 0.2s; + + &:hover { + border-color: ${token.colorPrimaryBorderHover}; + } + `, + cardActive: css` + border-color: ${token.colorPrimary}; + box-shadow: 0 0 0 1px ${token.colorPrimary}; + `, + meta: css` display: flex; align-items: center; + justify-content: space-between; + padding: 12px 8px 6px; + `, + label: css` font-size: 14px; - font-weight: var(--font-weight-500); - } -`; + font-weight: var(--font-weight-medium); + color: ${token.colorText}; + `, + check: css` + font-size: 18px; + color: ${token.colorPrimary}; + `, + radio: css` + width: 16px; + height: 16px; + border-radius: 50%; + border: 1.5px solid ${token.colorBorder}; + ` +})); const Appearance: React.FC = () => { const { setTheme, userSettings } = useUserSettings(); - const intl = useIntl(); + const { styles } = useStyles(); const allLocals = getAllLocales(); - const ThemeOptions = [ + const themeOptions: { value: PreviewMode; label: string }[] = [ { value: 'light', - label: intl.formatMessage({ id: 'common.appearance.light' }), - icon: + label: intl.formatMessage({ id: 'common.appearance.lightmode' }) }, { value: 'realDark', - label: intl.formatMessage({ id: 'common.appearance.dark' }), - icon: + label: intl.formatMessage({ id: 'common.appearance.darkmode' }) }, { value: 'auto', - label: intl.formatMessage({ id: 'common.appearance.system' }), - icon: + label: intl.formatMessage({ id: 'common.appearance.system' }) } ]; - const handleOnChange = (value: 'light' | 'realDark' | 'auto') => { - setTheme(value); - }; - const languageOptions = allLocals.map((locale) => ({ value: locale, label: _.get(langConfigMap, [locale, 'label']) })); + const handleSelectTheme = (value: PreviewMode) => { + setTheme(value); + }; + return ( - - - - {intl.formatMessage({ id: 'common.appearance.theme' })} - - - - - - {intl.formatMessage({ id: 'common.settings.language' })} - - { - setLocale(value, false); - }} - style={{ width: 200 }} - > - - + + +
+ {themeOptions.map((option) => { + const active = userSettings.mode === option.value; + return ( +
handleSelectTheme(option.value)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleSelectTheme(option.value); + } + }} + > + +
+ {option.label} + {active ? ( + + ) : ( + + )} +
+
+ ); + })} +
+
+ + { + setLocale(value, false); + }} + style={{ width: 200 }} + /> + } + /> +
); }; diff --git a/src/pages/profile/components/modify-password-form.tsx b/src/pages/profile/components/modify-password-form.tsx new file mode 100644 index 00000000..a7ea2064 --- /dev/null +++ b/src/pages/profile/components/modify-password-form.tsx @@ -0,0 +1,132 @@ +import { PasswordReg } from '@/config'; +import { INPUT_WIDTH } from '@/constants'; +import useSubmitLock from '@/hooks/use-submit-lock'; +import { updatePassword } from '@/pages/login/apis'; +import { Input as CInput, FormButtons } from '@gpustack/core-ui'; +import { useIntl } from '@umijs/max'; +import { Form, message } from 'antd'; +import React from 'react'; + +interface FormData { + new_password: string; + current_password: string; + confirm_password?: string; +} + +interface ModifyPasswordFormProps { + onCancel: () => void; + onSuccess?: () => void; +} + +const ModifyPasswordForm: React.FC = ({ + onCancel, + onSuccess +}) => { + const [form] = Form.useForm(); + const intl = useIntl(); + const { guard, run, release } = useSubmitLock(); + + const handleSubmit = () => { + guard(() => form.submit()); + }; + + const onFinish = async (values: FormData) => { + await run(async () => { + await updatePassword({ + new_password: values.new_password, + current_password: values.current_password + }); + message.success(intl.formatMessage({ id: 'common.message.success' })); + onSuccess?.(); + }); + }; + + return ( +
+ + name="current_password" + rules={[ + { + required: true, + message: intl.formatMessage( + { id: 'common.form.rule.input' }, + { + name: intl.formatMessage({ + id: 'users.form.currentpassword' + }) + } + ) + } + ]} + > + + + + name="new_password" + rules={[ + { + required: true, + pattern: PasswordReg, + message: intl.formatMessage({ + id: 'users.form.rule.password' + }) + } + ]} + > + + + ({ + validator(_, value) { + if (!value || getFieldValue('new_password') === value) { + return Promise.resolve(); + } + return Promise.reject( + new Error( + intl.formatMessage({ id: 'users.password.confirm.error' }) + ) + ); + } + }) + ]} + > + + + + + ); +}; + +ModifyPasswordForm.displayName = 'ModifyPasswordForm'; + +export default ModifyPasswordForm; diff --git a/src/pages/profile/components/public-key.tsx b/src/pages/profile/components/public-key.tsx deleted file mode 100644 index 59d99862..00000000 --- a/src/pages/profile/components/public-key.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { INPUT_WIDTH } from '@/constants'; -import { Textarea } from '@gpustack/core-ui'; -import { useIntl } from '@umijs/max'; -import { Button, Form } from 'antd'; -import React from 'react'; - -interface PublicKeyFormData { - public_key?: string; -} - -const PublicKey: React.FC = () => { - const intl = useIntl(); - const [form] = Form.useForm(); - - return ( -
- name="public_key"> - - - - - ); -}; - -PublicKey.displayName = 'PublicKey'; - -export default PublicKey; diff --git a/src/pages/profile/components/security.tsx b/src/pages/profile/components/security.tsx new file mode 100644 index 00000000..78de4631 --- /dev/null +++ b/src/pages/profile/components/security.tsx @@ -0,0 +1,39 @@ +import { useIntl } from '@umijs/max'; +import { Button } from 'antd'; +import React, { useState } from 'react'; +import ModifyPasswordForm from './modify-password-form'; +import { SettingRow, SettingsGroup } from './settings-group'; + +const Security: React.FC = () => { + const intl = useIntl(); + const [open, setOpen] = useState(false); + + return ( + + setOpen(true)}> + {intl.formatMessage({ id: 'users.form.updatepassword' })} + + ) + } + > + {open && ( + setOpen(false)} + onSuccess={() => setOpen(false)} + /> + )} + + + ); +}; + +Security.displayName = 'Security'; + +export default Security; diff --git a/src/pages/profile/components/settings-group.tsx b/src/pages/profile/components/settings-group.tsx new file mode 100644 index 00000000..571df05c --- /dev/null +++ b/src/pages/profile/components/settings-group.tsx @@ -0,0 +1,84 @@ +import { createStyles } from 'antd-style'; +import React from 'react'; + +const useStyles = createStyles(({ token, css }) => ({ + group: css` + /* Match the page panel surface (page-box.less): 8px radius + the + lighter container border, rather than the heavier component token. */ + border: 1px solid ${token.colorBorder}; + border-radius: 8px; + background: ${token.colorBgContainer}; + overflow: hidden; + `, + row: css` + padding: 16px 20px; + + & + & { + border-top: 1px solid ${token.colorBorderSecondary}; + } + `, + head: css` + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + `, + info: css` + min-width: 0; + `, + title: css` + font-size: 14px; + font-weight: var(--font-weight-medium); + color: ${token.colorText}; + line-height: 22px; + `, + description: css` + margin-top: 2px; + font-size: 13px; + color: ${token.colorTextTertiary}; + line-height: 20px; + `, + extra: css` + flex-shrink: 0; + `, + body: css` + margin-top: 16px; + ` +})); + +export const SettingsGroup: React.FC<{ children: React.ReactNode }> = ({ + children +}) => { + const { styles } = useStyles(); + return
{children}
; +}; + +interface SettingRowProps { + title: React.ReactNode; + description?: React.ReactNode; + extra?: React.ReactNode; + children?: React.ReactNode; +} + +export const SettingRow: React.FC = ({ + title, + description, + extra, + children +}) => { + const { styles } = useStyles(); + return ( +
+
+
+
{title}
+ {description && ( +
{description}
+ )} +
+ {extra &&
{extra}
} +
+ {children &&
{children}
} +
+ ); +}; diff --git a/src/pages/profile/components/settings-section.tsx b/src/pages/profile/components/settings-section.tsx new file mode 100644 index 00000000..5cc53f3c --- /dev/null +++ b/src/pages/profile/components/settings-section.tsx @@ -0,0 +1,53 @@ +import { createStyles } from 'antd-style'; +import React from 'react'; + +const useStyles = createStyles(({ token, css }) => ({ + section: css` + & + & { + margin-top: 40px; + } + `, + header: css` + margin-bottom: 16px; + `, + title: css` + margin: 0; + font-size: 16px; + font-weight: var(--font-weight-medium); + color: ${token.colorTextHeading}; + line-height: 24px; + `, + description: css` + margin: 4px 0 0; + font-size: 13px; + color: ${token.colorTextTertiary}; + line-height: 20px; + ` +})); + +interface SettingsSectionProps { + title: React.ReactNode; + description?: React.ReactNode; + children: React.ReactNode; +} + +const SettingsSection: React.FC = ({ + title, + description, + children +}) => { + const { styles } = useStyles(); + return ( +
+
+

{title}

+ {description &&

{description}

} +
+ {children} +
+ ); +}; + +SettingsSection.displayName = 'SettingsSection'; + +export default SettingsSection; diff --git a/src/pages/profile/components/theme-preview.tsx b/src/pages/profile/components/theme-preview.tsx new file mode 100644 index 00000000..a3ce0eab --- /dev/null +++ b/src/pages/profile/components/theme-preview.tsx @@ -0,0 +1,119 @@ +import { createStyles } from 'antd-style'; +import React from 'react'; + +export type PreviewMode = 'light' | 'realDark' | 'auto'; + +const PALETTE = { + light: { + surface: '#ffffff', + bar: '#e7ebf2', + block: '#f1f4f9', + accent: '#cdd7e8' + }, + dark: { + surface: '#0f1729', + bar: '#1d2740', + block: '#27324d', + accent: '#39445f' + } +}; + +const useStyles = createStyles(({ css }) => ({ + preview: css` + width: 100%; + aspect-ratio: 16 / 10; + border-radius: 8px; + overflow: hidden; + display: flex; + pointer-events: none; + user-select: none; + `, + half: css` + height: 100%; + display: flex; + flex-direction: column; + padding: 10px; + gap: 8px; + overflow: hidden; + `, + topbar: css` + height: 8px; + border-radius: 3px; + flex-shrink: 0; + `, + body: css` + flex: 1; + display: flex; + gap: 8px; + `, + sidebar: css` + width: 22%; + border-radius: 4px; + `, + main: css` + flex: 1; + border-radius: 4px; + `, + footer: css` + height: 6px; + border-radius: 3px; + flex-shrink: 0; + `, + systemIcon: css` + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 24px; + color: rgba(255, 255, 255, 0.55); + mix-blend-mode: difference; + z-index: 2; + `, + systemWrap: css` + position: relative; + ` +})); + +const Mockup: React.FC<{ tone: 'light' | 'dark'; flex?: number }> = ({ + tone, + flex = 1 +}) => { + const { styles } = useStyles(); + const c = PALETTE[tone]; + return ( +
+
+
+
+
+
+
+
+ ); +}; + +const ThemePreview: React.FC<{ mode: PreviewMode }> = ({ mode }) => { + const { styles } = useStyles(); + + if (mode === 'auto') { + return ( +
+ + +
+ ); + } + + return ( +
+ +
+ ); +}; + +ThemePreview.displayName = 'ThemePreview'; + +export default ThemePreview; diff --git a/src/pages/profile/index-copy.tsx b/src/pages/profile/index-copy.tsx new file mode 100644 index 00000000..8ece3d0a --- /dev/null +++ b/src/pages/profile/index-copy.tsx @@ -0,0 +1,69 @@ +import useTabActive from '@/hooks/use-tab-active'; +import { useIntl, useModel } from '@umijs/max'; +import { Tabs, TabsProps } from 'antd'; +import React, { useCallback, useMemo, useState } from 'react'; +import styled from 'styled-components'; +import PageBox from '../_components/page-box'; +import Appearance from './components/appearance'; +import ModifyPasswordn from './components/modify-password'; + +const Wrapper = styled.div` + .ant-page-header-heading { + padding-inline: 8px; + } +`; + +const Profile: React.FC = () => { + const intl = useIntl(); + const { initialState, setInitialState } = useModel('@@initialState') || {}; + const { setTabActive, getTabActive, tabsMap } = useTabActive(); + const [activeKey, setActiveKey] = useState( + initialState?.currentUser?.source === 'Local' + ? 'modify-password' + : 'appearance' + ); + + const items: TabsProps['items'] = useMemo(() => { + if (initialState?.currentUser?.source !== 'Local') { + return [ + { + key: 'appearance', + label: intl.formatMessage({ id: 'common.appearance' }), + children: + } + ]; + } + return [ + { + key: 'modify-password', + label: intl.formatMessage({ id: 'users.form.updatepassword' }), + children: + }, + { + key: 'appearance', + label: intl.formatMessage({ id: 'common.appearance' }), + children: + } + ]; + }, [intl, initialState?.currentUser?.source]); + + const handleChangeTab = useCallback((key: string) => { + setActiveKey(key); + setTabActive(tabsMap.userSettings, key); + }, []); + + return ( + + + + ); +}; + +Profile.displayName = 'Profile'; + +export default Profile; diff --git a/src/pages/profile/index.tsx b/src/pages/profile/index.tsx index 8ece3d0a..bbd5875c 100644 --- a/src/pages/profile/index.tsx +++ b/src/pages/profile/index.tsx @@ -1,65 +1,49 @@ -import useTabActive from '@/hooks/use-tab-active'; import { useIntl, useModel } from '@umijs/max'; -import { Tabs, TabsProps } from 'antd'; -import React, { useCallback, useMemo, useState } from 'react'; -import styled from 'styled-components'; +import { createStyles } from 'antd-style'; +import React from 'react'; import PageBox from '../_components/page-box'; import Appearance from './components/appearance'; -import ModifyPasswordn from './components/modify-password'; +import Security from './components/security'; +import SettingsSection from './components/settings-section'; -const Wrapper = styled.div` - .ant-page-header-heading { - padding-inline: 8px; - } -`; +const useStyles = createStyles(({ css }) => ({ + wrapper: css` + width: 100%; + max-width: 720px; + margin: 0 auto; + padding: 8px 0 40px; + ` +})); const Profile: React.FC = () => { const intl = useIntl(); - const { initialState, setInitialState } = useModel('@@initialState') || {}; - const { setTabActive, getTabActive, tabsMap } = useTabActive(); - const [activeKey, setActiveKey] = useState( - initialState?.currentUser?.source === 'Local' - ? 'modify-password' - : 'appearance' - ); - - const items: TabsProps['items'] = useMemo(() => { - if (initialState?.currentUser?.source !== 'Local') { - return [ - { - key: 'appearance', - label: intl.formatMessage({ id: 'common.appearance' }), - children: - } - ]; - } - return [ - { - key: 'modify-password', - label: intl.formatMessage({ id: 'users.form.updatepassword' }), - children: - }, - { - key: 'appearance', - label: intl.formatMessage({ id: 'common.appearance' }), - children: - } - ]; - }, [intl, initialState?.currentUser?.source]); - - const handleChangeTab = useCallback((key: string) => { - setActiveKey(key); - setTabActive(tabsMap.userSettings, key); - }, []); + const { styles } = useStyles(); + const { initialState } = useModel('@@initialState') || {}; + const isLocalUser = initialState?.currentUser?.source === 'Local'; return ( - +
+ + + + + {isLocalUser && ( + + + + )} +
); };