diff --git a/README.md b/README.md index b5b171e..857672f 100644 --- a/README.md +++ b/README.md @@ -148,9 +148,6 @@ Or clone it for local development: $ git clone https://github.com/yuntijs/dumi-theme-yunti.git $ cd dumi-theme-yunti $ pnpm install -# run theme dev -$ pnpm dev -# run example dev $ pnpm docs:dev ``` diff --git a/example/.dumirc.ts b/example/.dumirc.ts index 8a31c76..e8d5f23 100644 --- a/example/.dumirc.ts +++ b/example/.dumirc.ts @@ -41,6 +41,14 @@ const themeConfig = defineThemeConfig({ socialLinks: { github: homepage, }, + header: { + userActionButton: { + button: { + className: 'user-action-button', + type: 'primary', + }, + }, + }, title: 'Dumi Theme YuntiJS', description: 'Yunti documentation site theme package designed for Dumi 2', keywords: ['theme', 'antd', 'dumi', 'dumi-theme'], diff --git a/src/locales/en-US.json b/src/locales/en-US.json index c554710..3bc5210 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -1,3 +1,4 @@ { + "header.actions.user": "Login / Sign up", "header.nav.home": "Home" } diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index fb4411f..aa2fd76 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -1,3 +1,4 @@ { + "header.actions.user": "登录/注册", "header.nav.home": "首页" } diff --git a/src/slots/Header/UserActionButton.tsx b/src/slots/Header/UserActionButton.tsx new file mode 100644 index 0000000..5de42ab --- /dev/null +++ b/src/slots/Header/UserActionButton.tsx @@ -0,0 +1,84 @@ +import { Icon } from '@lobehub/ui'; +import { Avatar, Button, Dropdown } from 'antd'; +import { createStyles } from 'antd-style'; +import { useIntl } from 'dumi'; +import { LogOut, User2 } from 'lucide-react'; +import React, { memo, useCallback, useEffect, useState } from 'react'; + +import { headerSel, useSiteStore } from '@/store'; + +export const useStyles = createStyles(({ css }) => { + return { + avatar: css` + cursor: pointer; + `, + menu: css` + width: 180px; + `, + }; +}); + +const LOGIN_USER_KEY = '__LOGIN_USER'; + +export const HeaderUserActionButton: React.FC = memo(() => { + const { styles } = useStyles(); + const headerConfig = useSiteStore(headerSel); + const loginUser = useSiteStore(s => s.loginUser); + const intl = useIntl(); + const [userInfo, setUserInfo] = useState(loginUser); + useEffect(() => { + const loginUserStr = window.localStorage.getItem(LOGIN_USER_KEY); + if (loginUserStr) { + try { + const loginUser = JSON.parse(loginUserStr); + setUserInfo(loginUser); + useSiteStore.setState({ loginUser }); + } catch (error) { + console.warn(`parse login user info from ${LOGIN_USER_KEY} failed`, error); + } + } + }, []); + + const clearLoginUser = useCallback(() => { + window.localStorage.removeItem(LOGIN_USER_KEY); + }, []); + + if (!headerConfig?.userActionButton?.button) { + return; + } + if (userInfo?.user) { + return ( + , + label: 账户中心, + key: 'account', + }, + { type: 'divider' }, + { + icon: , + label: ( + + 退出登录 + + ), + key: 'logout', + }, + ], + }} + > + + {userInfo.user.charAt(0).toUpperCase()} + + + ); + } + return ( + + ); +}); diff --git a/src/slots/Header/index.tsx b/src/slots/Header/index.tsx index 2ad1629..2434815 100644 --- a/src/slots/Header/index.tsx +++ b/src/slots/Header/index.tsx @@ -1,4 +1,5 @@ import { Header as Head } from '@lobehub/ui'; +import { Flex } from 'antd'; import { useResponsive } from 'antd-style'; import { memo } from 'react'; @@ -12,6 +13,7 @@ import DiscordButton from './DiscordButton'; import GithubButton from './GithubButton'; import LangSwitch from './LangSwitch'; import ThemeSwitch from './ThemeSwitch'; +import { HeaderUserActionButton } from './UserActionButton'; const Header = memo(() => { const hasHeader = useSiteStore(s => Boolean(s.routeMeta.frontmatter)); @@ -23,17 +25,18 @@ const Header = memo(() => { + -
- + +
) : ( <> - + + -
+ ) } diff --git a/src/store/initialState.ts b/src/store/initialState.ts index ae309aa..5175911 100644 --- a/src/store/initialState.ts +++ b/src/store/initialState.ts @@ -44,6 +44,11 @@ export interface ISiteData { themeConfig: AllSiteThemeConfig; } +export interface ILoginUser { + user: string; + avatar?: string; +} + export interface SiteStore { locale: ILocale; location: Location; @@ -52,6 +57,7 @@ export interface SiteStore { sidebar?: ISidebarGroup[]; siteData: ISiteData; tabMeta?: NonNullable[0]['meta']; + loginUser?: ILoginUser; } export const initialState: SiteStore = { diff --git a/src/store/selectors/siteBasicInfo.ts b/src/store/selectors/siteBasicInfo.ts index 3493857..dbf4416 100644 --- a/src/store/selectors/siteBasicInfo.ts +++ b/src/store/selectors/siteBasicInfo.ts @@ -7,3 +7,7 @@ export const githubSel = (s: SiteStore) => s.siteData.themeConfig.socialLinks?.g export const discordSel = (s: SiteStore) => s.siteData.themeConfig.socialLinks?.discord || ''; export const giscusSel = (s: SiteStore) => s.siteData.themeConfig.giscus; + +export const headerSel = (s: SiteStore) => { + return s.siteData.themeConfig.header; +}; diff --git a/src/types/config.ts b/src/types/config.ts index e8e9a64..f5ed708 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -1,4 +1,5 @@ import type { FeaturesProps, FooterProps, HeroProps } from '@lobehub/ui'; +import { ButtonProps, MenuProps } from 'antd'; import type { IThemeConfig, SocialTypes } from 'dumi/dist/client/theme-api/types'; import { FooterColumn } from 'rc-footer/es/column'; @@ -92,6 +93,13 @@ export interface SiteThemeConfig { socialLinks?: { [key in SocialTypes | 'discord']?: string; }; + header?: { + /** 用户信息,默认会从 __LOGIN_USER 本地存储中获取 user 和 avatar,不存在的话展示 登录按钮 */ + userActionButton?: { + button?: Omit; + menuItems?: MenuProps['items']; + }; + }; title?: string; /** 默认描述,未设置描述的页面,该值会用于生成 标签 */ description?: string;