blob: 34ceb15caa8b0daa20e4b8675665844eebd7faa3 [file] [log] [blame]
86133aaa3f5d2025-04-20 21:33:29 +08001import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
2import { history, useModel } from '@umijs/max';
3import { Spin } from 'antd';
4import { createStyles } from 'antd-style';
5import { stringify } from 'querystring';
6import type { MenuInfo } from 'rc-menu/lib/interface';
7import React, { useCallback } from 'react';
8import { flushSync } from 'react-dom';
9import HeaderDropdown from '../HeaderDropdown';
10import { setRemoteMenu } from '@/services/session';
11import { clearSessionToken } from '@/access';
12import { logout } from '@/services/system/auth';
13
14export type GlobalHeaderRightProps = {
15 menu?: boolean;
16 children?: React.ReactNode;
17};
18
19export const AvatarName = () => {
20 const { initialState } = useModel('@@initialState');
21 const { currentUser } = initialState || {};
22 return <span className="anticon">{currentUser?.nickName}</span>;
23};
24
25const useStyles = createStyles(({ token }) => {
26 return {
27 action: {
28 display: 'flex',
29 height: '48px',
30 marginLeft: 'auto',
31 overflow: 'hidden',
32 alignItems: 'center',
33 padding: '0 8px',
34 cursor: 'pointer',
35 borderRadius: token.borderRadius,
36 '&:hover': {
37 backgroundColor: token.colorBgTextHover,
38 },
39 },
40 };
41});
42
43export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu, children }) => {
44 /**
45 * 退出登录,并且将当前的 url 保存
46 */
47 const loginOut = async () => {
48 await logout();
49 clearSessionToken();
50 setRemoteMenu(null);
51 const { search, pathname } = window.location;
52 const urlParams = new URL(window.location.href).searchParams;
53 /** 此方法会跳转到 redirect 参数所在的位置 */
54 const redirect = urlParams.get('redirect');
55 // Note: There may be security issues, please note
56 if (window.location.pathname !== '/user/login' && !redirect) {
57 history.replace({
58 pathname: '/user/login',
59 search: stringify({
60 redirect: pathname + search,
61 }),
62 });
63 }
64 };
65 const { styles } = useStyles();
66
67 const { initialState, setInitialState } = useModel('@@initialState');
68
69 const onMenuClick = useCallback(
70 (event: MenuInfo) => {
71 const { key } = event;
72 if (key === 'logout') {
73 flushSync(() => {
74 setInitialState((s) => ({ ...s, currentUser: undefined }));
75 });
76 loginOut();
77 return;
78 }
79 history.push(`/account/${key}`);
80 },
81 [setInitialState],
82 );
83
84 const loading = (
85 <span className={styles.action}>
86 <Spin
87 size="small"
88 style={{
89 marginLeft: 8,
90 marginRight: 8,
91 }}
92 />
93 </span>
94 );
95
96 if (!initialState) {
97 return loading;
98 }
99
100 const { currentUser } = initialState;
101
102 if (!currentUser || !currentUser.nickName) {
103 return loading;
104 }
105
106 const menuItems = [
107 ...(menu
108 ? [
109 {
110 key: 'center',
111 icon: <UserOutlined />,
112 label: '个人中心',
113 },
114 {
115 key: 'settings',
116 icon: <SettingOutlined />,
117 label: '个人设置',
118 },
119 {
120 type: 'divider' as const,
121 },
122 ]
123 : []),
124 {
125 key: 'logout',
126 icon: <LogoutOutlined />,
127 label: '退出登录',
128 },
129 ];
130
131 return (
132 <HeaderDropdown
133 menu={{
134 selectedKeys: [],
135 onClick: onMenuClick,
136 items: menuItems,
137 }}
138 >
139 {children}
140 </HeaderDropdown>
141 );
142};