diff --git a/build/resolvers/antd.ts b/build/resolvers/antd.ts index 4506424..ae57846 100644 --- a/build/resolvers/antd.ts +++ b/build/resolvers/antd.ts @@ -35,18 +35,17 @@ interface IMatcher { const matchComponents: IMatcher[] = [ { - pattern: /^Avatar/, - styleDir: 'avatar' + pattern: /^Anchor/, + styleDir: 'anchor' }, { pattern: /^AutoComplete/, styleDir: 'auto-complete' }, { - pattern: /^Anchor/, - styleDir: 'anchor' + pattern: /^Avatar/, + styleDir: 'avatar' }, - { pattern: /^Badge/, styleDir: 'badge' @@ -59,14 +58,18 @@ const matchComponents: IMatcher[] = [ pattern: /^Button/, styleDir: 'button' }, - { - pattern: /^Checkbox/, - styleDir: 'checkbox' - }, { pattern: /^Card/, styleDir: 'card' }, + { + pattern: /^CheckableTag/, + styleDir: 'tag' + }, + { + pattern: /^Checkbox/, + styleDir: 'checkbox' + }, { pattern: /^Collapse/, styleDir: 'collapse' @@ -75,76 +78,30 @@ const matchComponents: IMatcher[] = [ pattern: /^Descriptions/, styleDir: 'descriptions' }, - { - pattern: /^RangePicker|^WeekPicker|^MonthPicker/, - styleDir: 'date-picker' - }, { pattern: /^Dropdown/, styleDir: 'dropdown' }, - { pattern: /^Form/, styleDir: 'form' }, + { + pattern: /^Image/, + styleDir: 'image' + }, { pattern: /^InputNumber/, styleDir: 'input-number' }, - - { - pattern: /^Input|^Textarea/, - styleDir: 'input' - }, - { - pattern: /^Statistic/, - styleDir: 'statistic' - }, - { - pattern: /^CheckableTag/, - styleDir: 'tag' - }, - { - pattern: /^TimeRangePicker/, - styleDir: 'time-picker' - }, { pattern: /^Layout/, styleDir: 'layout' }, - { - pattern: /^Menu|^SubMenu/, - styleDir: 'menu' - }, - - { - pattern: /^Table/, - styleDir: 'table' - }, - { - pattern: /^TimePicker|^TimeRangePicker/, - styleDir: 'time-picker' - }, - { - pattern: /^Radio/, - styleDir: 'radio' - }, - - { - pattern: /^Image/, - styleDir: 'image' - }, - { pattern: /^List/, styleDir: 'list' }, - - { - pattern: /^Tab/, - styleDir: 'tabs' - }, { pattern: /^Mentions/, styleDir: 'mentions' @@ -154,37 +111,72 @@ const matchComponents: IMatcher[] = [ styleDir: 'qr-code' }, { - pattern: /^Step/, - styleDir: 'steps' + pattern: /^Radio/, + styleDir: 'radio' }, - { - pattern: /^Skeleton/, - styleDir: 'skeleton' - }, - { pattern: /^Select/, styleDir: 'select' }, { - pattern: /^TreeSelect/, - styleDir: 'tree-select' + pattern: /^Skeleton/, + styleDir: 'skeleton' }, { - pattern: /^Tree|^DirectoryTree/, - styleDir: 'tree' + pattern: /^Statistic/, + styleDir: 'statistic' }, { - pattern: /^Typography/, - styleDir: 'typography' + pattern: /^Step/, + styleDir: 'steps' + }, + { + pattern: /^Tab/, + styleDir: 'tabs' + }, + { + pattern: /^Table/, + styleDir: 'table' }, { pattern: /^Timeline/, styleDir: 'timeline' }, + { + pattern: /^TimeRangePicker/, + styleDir: 'time-picker' + }, + { + pattern: /^Typography/, + styleDir: 'typography' + }, + { + pattern: /^TreeSelect/, + styleDir: 'tree-select' + }, { pattern: /^Upload/, styleDir: 'upload' + }, + { + pattern: /^Input|^Textarea/, + styleDir: 'input' + }, + { + pattern: /^Menu|^SubMenu/, + styleDir: 'menu' + }, + { + pattern: /^Tree|^DirectoryTree/, + styleDir: 'tree' + }, + { + pattern: /^MonthPicker|^RangePicker|^WeekPicker/, + styleDir: 'date-picker' + }, + { + pattern: /^TimePicker|^TimeRangePicker/, + styleDir: 'time-picker' } ] @@ -257,12 +249,12 @@ const getSideEffects = (compName: string, options: AntDesignResolverOptions): Si const primitiveNames = [ 'Affix', + 'Alert', 'Anchor', 'AnchorLink', 'AutoComplete', 'AutoCompleteOptGroup', 'AutoCompleteOption', - 'Alert', 'Avatar', 'AvatarGroup', 'BackTop', @@ -277,105 +269,106 @@ const primitiveNames = [ 'Card', 'CardGrid', 'CardMeta', - 'Collapse', - 'CollapsePanel', 'Carousel', 'Cascader', + 'CheckableTag', 'Checkbox', 'CheckboxGroup', 'Col', + 'Collapse', + 'CollapsePanel', 'Comment', 'ConfigProvider', 'DatePicker', - 'MonthPicker', - 'WeekPicker', - 'RangePicker', - 'QuarterPicker', 'Descriptions', 'DescriptionsItem', + 'DirectoryTree', 'Divider', + 'Drawer', 'Dropdown', 'DropdownButton', - 'Drawer', 'Empty', 'FloatButton', 'Form', 'FormItem', 'FormItemRest', 'Grid', - 'Input', - 'InputGroup', - 'InputPassword', - 'InputSearch', - 'Textarea', 'Image', 'ImagePreviewGroup', + 'Input', + 'InputGroup', 'InputNumber', + 'InputPassword', + 'InputSearch', 'Layout', + 'LayoutContent', + 'LayoutFooter', 'LayoutHeader', 'LayoutSider', - 'LayoutFooter', - 'LayoutContent', 'List', 'ListItem', 'ListItemMeta', + 'LocaleProvider', + 'Mentions', + 'MentionsOption', 'Menu', 'MenuDivider', 'MenuItem', 'MenuItemGroup', - 'SubMenu', - 'Mentions', - 'MentionsOption', 'Modal', - 'Statistic', - 'StatisticCountdown', + 'MonthPicker', 'PageHeader', 'Pagination', 'Popconfirm', 'Popover', 'Progress', + 'QRCode', + 'QuarterPicker', 'Radio', 'RadioButton', 'RadioGroup', + 'RangePicker', 'Rate', 'Result', 'Row', - 'QRCode', + 'Segmented', 'Select', 'SelectOptGroup', 'SelectOption', 'Skeleton', - 'SkeletonButton', 'SkeletonAvatar', - 'SkeletonInput', + 'SkeletonButton', 'SkeletonImage', + 'SkeletonInput', 'Slider', 'Space', 'Spin', - 'Steps', + 'Statistic', + 'StatisticCountdown', 'Step', + 'Steps', + 'SubMenu', 'Switch', 'Table', 'TableColumn', 'TableColumnGroup', 'TableSummary', - 'TableSummaryRow', 'TableSummaryCell', + 'TableSummaryRow', + 'TabPane', + 'Tabs', + 'Tag', + 'Textarea', + 'Timeline', + 'TimelineItem', + 'TimePicker', + 'TimeRangePicker', + 'Tooltip', 'Transfer', 'Tree', 'TreeNode', - 'DirectoryTree', 'TreeSelect', 'TreeSelectNode', - 'Tabs', - 'TabPane', - 'Tag', - 'CheckableTag', - 'TimePicker', - 'TimeRangePicker', - 'Timeline', - 'TimelineItem', - 'Tooltip', 'Typography', 'TypographyLink', 'TypographyParagraph', @@ -383,7 +376,7 @@ const primitiveNames = [ 'TypographyTitle', 'Upload', 'UploadDragger', - 'LocaleProvider' + 'WeekPicker' ] const prefix = 'Antd' diff --git a/src/App.tsx b/src/App.tsx index d013cdc..bd7ae57 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,9 +2,14 @@ import { theme } from 'antd' import zh_CN from 'antd/locale/zh_CN' import BaseStyles from '@/assets/css/base.style' import CommonStyles from '@/assets/css/common.style' -import { COLOR_PRODUCTION } from '@/constants/common.constants' +import { + COLOR_PRODUCTION, + THEME_DARK, + THEME_FOLLOW_SYSTEM, + THEME_LIGHT +} from '@/constants/common.constants' import { getRouter } from '@/router' -import { init } from '@/util/common' +import { getThemeMode, init } from '@/util/common' import FullscreenLoadingMask from '@/components/common/FullscreenLoadingMask' export const AppContext = createContext({ @@ -17,19 +22,38 @@ const App = () => { const [notificationInstance, notificationHolder] = notification.useNotification() const [modalInstance, modalHolder] = AntdModal.useModal() const [routerState, setRouterState] = useState(getRouter) - const [isDarkMode, setIsDarkMode] = useState(false) + const [themeMode, setThemeMode] = useState(getThemeMode()) + const [isSystemDarkMode, setIsSystemDarkMode] = useState(false) + + const getIsDark = () => { + switch (themeMode) { + case THEME_FOLLOW_SYSTEM: + return isSystemDarkMode + case THEME_LIGHT: + return false + case THEME_DARK: + return true + } + } useEffect(() => { init(messageInstance, notificationInstance, modalInstance) + const darkThemeMq = window.matchMedia('(prefers-color-scheme: dark)') - setIsDarkMode(darkThemeMq.matches) - const listener = (ev: MediaQueryListEvent) => { - setIsDarkMode(ev.matches) + setIsSystemDarkMode(darkThemeMq.matches) + const darkThemeMqChangeListener = (ev: MediaQueryListEvent) => { + setIsSystemDarkMode(ev.matches) } - darkThemeMq.addEventListener('change', listener) + darkThemeMq.addEventListener('change', darkThemeMqChangeListener) + + const themeModeChangeListener = () => { + setThemeMode(getThemeMode()) + } + window.addEventListener('localStorageChange', themeModeChangeListener) return () => { - darkThemeMq.removeEventListener('change', listener) + darkThemeMq.removeEventListener('change', darkThemeMqChangeListener) + window.removeEventListener('localStorageChange', themeModeChangeListener) } }, []) @@ -37,7 +61,7 @@ const App = () => { { refreshRouter: () => { setRouterState(getRouter()) }, - isDarkMode + isDarkMode: getIsDark() }} > }> diff --git a/src/assets/css/components/common/sidebar/footer.style.ts b/src/assets/css/components/common/sidebar/footer.style.ts index 136e8a7..a95cd2b 100644 --- a/src/assets/css/components/common/sidebar/footer.style.ts +++ b/src/assets/css/components/common/sidebar/footer.style.ts @@ -11,17 +11,6 @@ const slideIn = keyframes` } ` -const slideOut = keyframes` - 0% { - transform: translateX(0); - opacity: 1; - } - 100% { - transform: translateX(-10px); - opacity: 0; - } -` - export default createStyles(({ cx, css, token }) => { const collapsedExit = cx(css` opacity: 0; @@ -29,7 +18,6 @@ export default createStyles(({ cx, css, token }) => { padding-left: ${token.paddingXS}px; left: 100%; z-index: 1000; - animation: ${slideOut} 0.1s ease; transform: translateX(-100%); `) diff --git a/src/assets/css/pages/user/index.style.ts b/src/assets/css/pages/user/index.style.ts index 0185f82..0e46214 100644 --- a/src/assets/css/pages/user/index.style.ts +++ b/src/assets/css/pages/user/index.style.ts @@ -63,6 +63,7 @@ export default createStyles(({ token }) => ({ header: { justifyContent: 'space-between', + alignItems: 'center', '> *': { flex: '0 0 auto' @@ -70,7 +71,7 @@ export default createStyles(({ token }) => ({ }, title: { - fontSize: token.fontSizeXL, + fontSize: token.fontSizeHeading3, fontWeight: 'bolder' }, @@ -90,8 +91,9 @@ export default createStyles(({ token }) => ({ }, row: { - alignItems: 'center', justifyContent: 'space-between', + alignItems: 'center', + padding: `0 ${token.paddingLG}px`, '> *': { flex: '0 0 auto' @@ -99,7 +101,7 @@ export default createStyles(({ token }) => ({ }, label: { - fontSize: token.fontSize, + fontSize: token.fontSizeLG, fontWeight: 'bolder', flex: 1 }, diff --git a/src/assets/svg/themeDark.svg b/src/assets/svg/themeDark.svg new file mode 100644 index 0000000..7be4207 --- /dev/null +++ b/src/assets/svg/themeDark.svg @@ -0,0 +1 @@ + diff --git a/src/assets/svg/themeLight.svg b/src/assets/svg/themeLight.svg new file mode 100644 index 0000000..83f714a --- /dev/null +++ b/src/assets/svg/themeLight.svg @@ -0,0 +1 @@ + diff --git a/src/assets/svg/themeSystem.svg b/src/assets/svg/themeSystem.svg new file mode 100644 index 0000000..60f387f --- /dev/null +++ b/src/assets/svg/themeSystem.svg @@ -0,0 +1 @@ + diff --git a/src/components/common/Sidebar/Footer.tsx b/src/components/common/Sidebar/Footer.tsx index 18cf537..7d9bb77 100644 --- a/src/components/common/Sidebar/Footer.tsx +++ b/src/components/common/Sidebar/Footer.tsx @@ -1,11 +1,12 @@ import Icon from '@ant-design/icons' import useStyles from '@/assets/css/components/common/sidebar/footer.style' -import { SidebarContext } from '@/components/common/Sidebar/index' -import { notification } from '@/util/common' +import { THEME_DARK, THEME_FOLLOW_SYSTEM, THEME_LIGHT } from '@/constants/common.constants' +import { getThemeMode, notification, setThemeMode, ThemeMode } from '@/util/common' import { getRedirectUrl } from '@/util/route' import { getAvatar, getLoginStatus, getNickname, removeToken } from '@/util/auth' import { navigateToLogin, navigateToUser } from '@/util/navigation' import { r_auth_logout } from '@/services/auth' +import { SidebarContext } from '@/components/common/Sidebar/index' const Footer = () => { const { styles, theme, cx } = useStyles() @@ -77,6 +78,31 @@ const Footer = () => { 登录 + {!getLoginStatus() && !isCollapse && ( + + options={[ + { + icon: , + title: '跟随系统', + value: THEME_FOLLOW_SYSTEM + }, + { + label: , + title: '亮色', + value: THEME_LIGHT + }, + { + label: , + title: '深色', + value: THEME_DARK + } + ]} + defaultValue={getThemeMode()} + onChange={setThemeMode} + size={'small'} + block + /> + )}