李金
7 months ago
10 changed files with 444 additions and 356 deletions
-
50src/components/avatar/index.tsx
-
111src/components/breadcrumb/index.tsx
-
84src/components/icon/index.tsx
-
51src/components/icon/picker/IconRender.tsx
-
24src/components/icon/types.ts
-
49src/layout/RootLayout.tsx
-
34src/layout/style.ts
-
294src/pages/system/menus/index.tsx
-
36src/service/system.ts
-
67src/store/user.ts
@ -0,0 +1,50 @@ |
|||||
|
import Icon from '@/components/icon' |
||||
|
import { useTranslation } from '@/i18n.ts' |
||||
|
import { currentUserAtom } from '@/store/user.ts' |
||||
|
import { Avatar as AntAvatar, Dropdown, Spin } from 'antd' |
||||
|
import { useAtomValue } from 'jotai' |
||||
|
|
||||
|
const Avatar = () => { |
||||
|
|
||||
|
const { t } = useTranslation() |
||||
|
const { data, isLoading } = useAtomValue(currentUserAtom) |
||||
|
|
||||
|
return ( |
||||
|
<div> |
||||
|
<Dropdown |
||||
|
key={'user'} |
||||
|
placement="bottomRight" |
||||
|
menu={{ |
||||
|
items: [ |
||||
|
{ |
||||
|
key: 'logout', |
||||
|
icon: <Icon type={'park:Logout'}/>, |
||||
|
label: <span style={{ |
||||
|
marginInlineStart: 8, |
||||
|
userSelect: 'none' |
||||
|
}}>{t('app.header.logout')}</span>, |
||||
|
}, |
||||
|
], |
||||
|
}} |
||||
|
> |
||||
|
<Spin spinning={isLoading}> |
||||
|
<AntAvatar |
||||
|
key="avatar" |
||||
|
size={'small'} |
||||
|
src={data?.avatar || data?.nickname?.substring(0, 1)}> |
||||
|
{!data?.avatar && data?.nickname?.substring(0, 1)} |
||||
|
</AntAvatar> |
||||
|
<span key="name" |
||||
|
style={{ |
||||
|
marginInlineStart: 8, |
||||
|
userSelect: 'none' |
||||
|
}}> |
||||
|
{data?.nickname} |
||||
|
</span> |
||||
|
</Spin> |
||||
|
</Dropdown> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default Avatar |
@ -1,25 +1,25 @@ |
|||||
export interface ReactIcon { |
export interface ReactIcon { |
||||
type: 'antd' | 'park'; |
|
||||
componentName: string; |
|
||||
props?: object; |
|
||||
|
type: 'antd' | 'park'; |
||||
|
componentName: string; |
||||
|
props?: object; |
||||
} |
} |
||||
|
|
||||
export interface IconfontIcon { |
export interface IconfontIcon { |
||||
type: 'iconfont'; |
|
||||
componentName: string; |
|
||||
props: { |
|
||||
type: string; |
|
||||
}; |
|
||||
scriptUrl?: string; |
|
||||
|
type: 'iconfont'; |
||||
|
componentName: string; |
||||
|
props: { |
||||
|
type: string; |
||||
|
}; |
||||
|
scriptUrl?: string; |
||||
} |
} |
||||
|
|
||||
export interface IconComponentProps { |
export interface IconComponentProps { |
||||
type: string; |
|
||||
|
type: string; |
||||
|
|
||||
[key: string]: any; |
|
||||
|
[key: string]: any; |
||||
} |
} |
||||
|
|
||||
/** |
/** |
||||
* 最基础的图标信息单元 |
* 最基础的图标信息单元 |
||||
*/ |
*/ |
||||
export type IconUnit = ReactIcon | IconfontIcon | IconComponentProps |
|
||||
|
export type IconUnit = IconComponentProps | ReactIcon | IconfontIcon |
@ -1,21 +1,31 @@ |
|||||
import { createStyles } from '@/theme' |
import { createStyles } from '@/theme' |
||||
|
|
||||
export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { |
export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { |
||||
const prefix = `${prefixCls}-${token?.proPrefix}-layout` |
|
||||
|
const prefix = `${prefixCls}-${token?.proPrefix}-layout` |
||||
|
|
||||
const container = { |
|
||||
[prefix]: css`
|
|
||||
|
const container = { |
||||
|
[prefix]: css`
|
||||
|
.ant-pro-global-header-logo, |
||||
|
.ant-pro-layout-bg-list { |
||||
|
user-select: none; |
||||
|
} |
||||
|
`,
|
||||
|
} |
||||
|
|
||||
`,
|
|
||||
} |
|
||||
|
|
||||
const pageContext = css`
|
|
||||
box-shadow: ${token.boxShadowSecondary}; |
|
||||
`
|
|
||||
|
const pageContext = css`
|
||||
|
box-shadow: ${token.boxShadowSecondary}; |
||||
|
`
|
||||
|
|
||||
return { |
|
||||
container: cx(container[prefix], props?.className), |
|
||||
pageContext, |
|
||||
|
const sideMenu = css`
|
||||
|
.ant-pro-base-menu-inline-group .ant-menu-item-group-title .anticon { |
||||
|
margin-inline-end: 0; |
||||
} |
} |
||||
|
`
|
||||
|
|
||||
|
return { |
||||
|
container: cx(container[prefix], props?.className), |
||||
|
pageContext, |
||||
|
sideMenu, |
||||
|
} |
||||
|
|
||||
}) |
}) |
@ -1,49 +1,62 @@ |
|||||
import { appAtom } from '@/store/system.ts' |
import { appAtom } from '@/store/system.ts' |
||||
|
import { IUserInfo } from '@/types/user' |
||||
import { AxiosResponse } from 'axios' |
import { AxiosResponse } from 'axios' |
||||
import { atom } from 'jotai/index' |
import { atom } from 'jotai/index' |
||||
import { IAuth, MenuItem } from '@/types' |
|
||||
|
import { IApiResult, IAuth, MenuItem } from '@/types' |
||||
import { LoginRequest } from '@/types/login' |
import { LoginRequest } from '@/types/login' |
||||
import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query' |
import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query' |
||||
import systemServ from '@/service/system.ts' |
import systemServ from '@/service/system.ts' |
||||
import { formatMenuData, isDev } from '@/utils' |
import { formatMenuData, isDev } from '@/utils' |
||||
|
|
||||
export const authAtom = atom<IAuth>({ |
export const authAtom = atom<IAuth>({ |
||||
isLogin: false, |
|
||||
authKey: [] |
|
||||
|
isLogin: false, |
||||
|
authKey: [] |
||||
}) |
}) |
||||
|
|
||||
const devLogin = { |
const devLogin = { |
||||
username: 'SupperAdmin', |
|
||||
password: 'kk123456', |
|
||||
code: '123456' |
|
||||
|
username: 'SupperAdmin', |
||||
|
password: 'kk123456', |
||||
|
code: '123456' |
||||
} |
} |
||||
export const loginFormAtom = atom<LoginRequest>({ |
export const loginFormAtom = atom<LoginRequest>({ |
||||
...(isDev ? devLogin : {}) |
|
||||
|
...(isDev ? devLogin : {}) |
||||
} as LoginRequest) |
} as LoginRequest) |
||||
|
|
||||
export const loginAtom = atomWithMutation<any, LoginRequest>(() => ({ |
export const loginAtom = atomWithMutation<any, LoginRequest>(() => ({ |
||||
mutationKey: [ 'login' ], |
|
||||
mutationFn: async (params) => { |
|
||||
return await systemServ.login(params) |
|
||||
}, |
|
||||
onSuccess: () => { |
|
||||
// console.log('login success', data)
|
|
||||
}, |
|
||||
retry: false, |
|
||||
|
mutationKey: [ 'login' ], |
||||
|
mutationFn: async (params) => { |
||||
|
return await systemServ.login(params) |
||||
|
}, |
||||
|
onSuccess: () => { |
||||
|
// console.log('login success', data)
|
||||
|
}, |
||||
|
retry: false, |
||||
})) |
})) |
||||
|
|
||||
export const userMenuDataAtom = atomWithQuery<any, MenuItem[]>((get) => ({ |
|
||||
enabled: false, |
|
||||
queryKey: [ 'user_menus', get(appAtom).token ], |
|
||||
|
export const currentUserAtom = atomWithQuery<IApiResult<IUserInfo>, any, IUserInfo>((get) => { |
||||
|
return { |
||||
|
queryKey: [ 'user_info', get(appAtom).token ], |
||||
queryFn: async () => { |
queryFn: async () => { |
||||
return await systemServ.user.menus() |
|
||||
}, |
|
||||
select: (data: AxiosResponse) => { |
|
||||
return formatMenuData(data.data.rows as any ?? []) |
|
||||
|
return await systemServ.user.current() |
||||
}, |
}, |
||||
initialData: () => { |
|
||||
const queryClient = get(queryClientAtom) |
|
||||
return queryClient.getQueryData([ 'user_menus', get(appAtom).token ]) |
|
||||
}, |
|
||||
retry: false, |
|
||||
|
select: (data) => { |
||||
|
return data.data |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
export const userMenuDataAtom = atomWithQuery<any, MenuItem[]>((get) => ({ |
||||
|
enabled: false, |
||||
|
queryKey: [ 'user_menus', get(appAtom).token ], |
||||
|
queryFn: async () => { |
||||
|
return await systemServ.user.menus() |
||||
|
}, |
||||
|
select: (data: AxiosResponse) => { |
||||
|
return formatMenuData(data.data.rows as any ?? []) |
||||
|
}, |
||||
|
initialData: () => { |
||||
|
const queryClient = get(queryClientAtom) |
||||
|
return queryClient.getQueryData([ 'user_menus', get(appAtom).token ]) |
||||
|
}, |
||||
|
retry: false, |
||||
})) |
})) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue