Browse Source

显示当前用户信息

main
李金 7 months ago
parent
commit
363eec0b27
  1. 50
      src/components/avatar/index.tsx
  2. 111
      src/components/breadcrumb/index.tsx
  3. 84
      src/components/icon/index.tsx
  4. 51
      src/components/icon/picker/IconRender.tsx
  5. 24
      src/components/icon/types.ts
  6. 49
      src/layout/RootLayout.tsx
  7. 34
      src/layout/style.ts
  8. 294
      src/pages/system/menus/index.tsx
  9. 36
      src/service/system.ts
  10. 67
      src/store/user.ts

50
src/components/avatar/index.tsx

@ -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

111
src/components/breadcrumb/index.tsx

@ -5,73 +5,74 @@ import { getIcon } from '@/components/icon'
import { memo, useCallback } from 'react'
export const PageBreadcrumb = memo((props: BreadcrumbProps & {
showIcon?: boolean;
showIcon?: boolean;
}) => {
const nav = useNavigate()
const { items = [], showIcon = true, ...other } = props
const nav = useNavigate()
const { items = [], showIcon = true, ...other } = props
const renderIcon = useCallback((icon: any) => {
if (icon && showIcon) {
return getIcon(icon)
}
return null
}, [])
const renderIcon = useCallback((icon: any) => {
if (icon && showIcon) {
return getIcon(icon)
}
return null
}, [])
const itemRender = (route) => {
const itemRender = (route) => {
const isLast = route?.path === items[items.length - 1]?.path
const isLast = route?.path === items[items.length - 1]?.path
if (route.children) {
const items = route.children.map((item) => {
return {
...item,
key: item.path || item.name,
label: item.name,
}
if (route.children) {
const items = route.children.map((item) => {
return {
...item,
key: item.path || item.name,
label: item.name,
}
})
return (
<Dropdown menu={{
items, onClick: (e) => {
nav({
to: e.key
})
return (
<Dropdown menu={{
items, onClick: (e) => {
nav({
to: e.key
})
}
}}
trigger={[ 'hover' ]}>
{
(!route.component || !route.path)? <a className={'item'}>{renderIcon(route.icon)}<span>{route.name}</span>
<DownOutlined/></a>
: <Link to={`/${route.path}`}
preload={false}
className={'item'}>{renderIcon(route.icon)}<span>{route.name}</span>
<DownOutlined/>
</Link>
}
}
}}
trigger={[ 'hover' ]}>
{
(!route.component || !route.path) ?
<a className={'item'}>{renderIcon(route.icon)}<span>{route.name}</span>
<DownOutlined/></a>
: <Link to={`/${route.path}`}
preload={false}
className={'item'}>{renderIcon(route.icon)}<span>{route.name}</span>
<DownOutlined/>
</Link>
}
</Dropdown>
)
}
return isLast || !route.path ? (
<span className={'item'}>{renderIcon(route.icon)}<span>{route.name}</span></span>
) : (
<Link to={`/${route.path}`}
preload={false}
className={'item'}>{renderIcon(route.icon)}<span>{route.name}</span></Link>
)
</Dropdown>
)
}
return (
<>
<Breadcrumb {...other}
items={items}
itemRender={itemRender}
/>
</>
return isLast || !route.path ? (
<span className={'item'}>{renderIcon(route.icon)}<span>{route.name}</span></span>
) : (
<Link to={`/${route.path}`}
preload={false}
className={'item'}>{renderIcon(route.icon)}<span>{route.name}</span></Link>
)
}
return (
<div style={{ userSelect: 'none' }}>
<Breadcrumb {...other}
items={items}
itemRender={itemRender}
/>
</div>
)
})
export default PageBreadcrumb

84
src/components/icon/index.tsx

@ -5,56 +5,66 @@ import * as AntIcons from '@ant-design/icons/es/icons'
import IconItem from './picker/IconRender.tsx'
import { IconUnit } from './types.ts'
export function Icon(props: IconUnit) {
const { type, ...other } = props
type Prefix = 'antd:' | 'park:';
type IconType = `${Prefix}${string}`;
if (type && [ 'antd:', 'park:' ].includes(type as string)) {
const [ t, c ] = type.split(':')
return <IconItem {...props as any} type={t} componentName={c}/>
}
interface IconProps extends Pick<IconUnit, 'type'> {
type: IconType | IconUnit['type']
}
const AntIcon = AntIcons[type as keyof typeof AntIcons]
if (AntIcon) {
return <AntIcon {...other}/>
}
function isAntdOrParkIcon(value: string): value is IconType {
return value.startsWith('antd:') || value.startsWith('park:')
}
//如果是http或https链接,直接返回图片
if (type && (type.startsWith('http') || type.startsWith('https') || type.startsWith('data:image'))) {
// @ts-ignore 没有办法把所有的属性都传递给img
return <img src={type} alt="icon" width={16} height={16} {...other}/>
}
export function Icon(props: IconProps) {
const { type, ...other } = props
if (type && isAntdOrParkIcon(type)) {
const [ t, c ] = type.split(':')
return <IconItem {...other as any} type={t} componentName={c}/>
}
if (ALL_ICON_KEYS.indexOf(type as IconType) < 0) {
return null
}
const AntIcon = AntIcons[type as keyof typeof AntIcons]
if (AntIcon) {
return <AntIcon {...other}/>
}
//如果是http或https链接,直接返回图片
if (type && (type.startsWith('http') || type.startsWith('https') || type.startsWith('data:image'))) {
// @ts-ignore 没有办法把所有的属性都传递给img
return <img src={type} alt="icon" width={16} height={16} {...other}/>
}
return (
<Fragment>
<IconAll type={type as IconType}
theme="outline" size="20" fill="#868686" strokeWidth={3}
{...other}/>
</Fragment>
)
if (ALL_ICON_KEYS.indexOf(type as IconType) < 0) {
return null
}
return (
<Fragment>
<IconAll type={type as IconType}
theme="outline" size="20" fill="#868686" strokeWidth={3}
{...other}/>
</Fragment>
)
}
// eslint-disable-next-line react-refresh/only-export-components
export const getIcon = (type: string, props?: Partial<IIconAllProps>) => {
if (React.isValidElement(type)) {
return type
}
//判断是否为json格式
if (type && type.startsWith('{') && type.endsWith('}')) {
try {
const obj = JSON.parse(type)
type = obj.type
props = obj
} catch (e) { /* empty */
}
if (React.isValidElement(type)) {
return type
}
//判断是否为json格式
if (type && type.startsWith('{') && type.endsWith('}')) {
try {
const obj = JSON.parse(type)
type = obj.type
props = obj
} catch (e) { /* empty */
}
}
return <Icon type={type} {...props}/>
return <Icon type={type} {...props}/>
}
export default Icon

51
src/components/icon/picker/IconRender.tsx

@ -4,37 +4,40 @@ import { AntdIcon, ParkIcon, ALL_ICON_KEYS } from './icons.ts'
import { IconType } from '@icon-park/react/es/all'
export interface IconRenderProps {
type: 'antd' | 'park';
componentName?: string;
props?: | {
type?: string;
} | any;
type: 'antd' | 'park';
componentName?: string;
props?: | {
type?: string;
} | any;
}
export interface IIconRender {
(props: IconRenderProps): JSX.Element;
(props: IconRenderProps): JSX.Element;
}
const Render: FC<IconRenderProps> = memo(
({ type, componentName, props }) => {
switch (type) {
case 'antd':
// eslint-disable-next-line no-case-declarations
const AIcon = AntdIcon[componentName!]
return <AIcon {...props} />
case 'park':
// eslint-disable-next-line no-case-declarations
if (ALL_ICON_KEYS.indexOf(componentName as IconType) < 0) {
return null
}
return <ParkIcon type={componentName} {...props} />
default: {
return null
}
}
},
({ type, componentName, props }) => {
switch (type) {
case 'antd':
// eslint-disable-next-line no-case-declarations
const AIcon = AntdIcon[componentName!]
if (!AIcon) {
return null
}
return <AIcon {...props} />
case 'park':
// eslint-disable-next-line no-case-declarations
if (ALL_ICON_KEYS.indexOf(componentName as IconType) < 0) {
return null
}
return <ParkIcon type={componentName} {...props} />
default: {
return null
}
}
},
)
const IconIRender = Render as IIconRender

24
src/components/icon/types.ts

@ -1,25 +1,25 @@
export interface ReactIcon {
type: 'antd' | 'park';
componentName: string;
props?: object;
type: 'antd' | 'park';
componentName: string;
props?: object;
}
export interface IconfontIcon {
type: 'iconfont';
componentName: string;
props: {
type: string;
};
scriptUrl?: string;
type: 'iconfont';
componentName: string;
props: {
type: string;
};
scriptUrl?: string;
}
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

49
src/layout/RootLayout.tsx

@ -1,19 +1,21 @@
import Avatar from '@/components/avatar'
import PageBreadcrumb from '@/components/breadcrumb'
import ErrorPage from '@/components/error/error.tsx'
import SelectLang from '@/components/select-lang'
import { useTranslation } from '@/i18n.ts'
import { appAtom } from '@/store/system.ts'
import { userMenuDataAtom } from '@/store/user.ts'
import { MenuItem } from '@/types'
import { ProConfigProvider, ProLayout, } from '@ant-design/pro-components'
import { zhCNIntl, enUSIntl } from '@ant-design/pro-provider/es/intl'
import { CatchBoundary, Link, Outlet } from '@tanstack/react-router'
import { Dropdown } from 'antd'
import { ConfigProvider} from '@/components/config-provider'
import { ConfigProvider } from '@/components/config-provider'
import { useState } from 'react'
import Icon from '../components/icon'
import defaultProps from './_defaultProps'
import { useAtomValue } from 'jotai'
import { useStyle } from '@/layout/style.ts'
import zh from 'antd/locale/zh_CN'
import en from 'antd/locale/en_US'
//根据menuData生成Breadcrumb所需的数据
const getBreadcrumbData = (menuData: MenuItem[], pathname: string) => {
@ -42,13 +44,13 @@ export default () => {
const { styles } = useStyle()
const { t } = useTranslation()
const { data: menuData = [], isLoading } = useAtomValue(userMenuDataAtom)
const { language } = useAtomValue(appAtom)
const items = getBreadcrumbData(menuData, location.pathname)
const [ pathname, setPathname ] = useState(location.pathname)
return (
<div
className={styles.container}
className={styles.container}
id="crazy-pro-layout"
style={{
height: '100vh',
@ -59,8 +61,9 @@ export default () => {
getResetKey={() => 'reset-page'}
errorComponent={ErrorPage}
>
<ProConfigProvider hashed={false}>
<ProConfigProvider hashed={false} intl={language === 'zh-CN' ? zhCNIntl : enUSIntl}>
<ConfigProvider
locale={language === 'zh-CN' ? zh : en}
getTargetContainer={() => {
return document.getElementById('crazy-pro-layout') || document.body
}}
@ -88,25 +91,12 @@ export default () => {
collapsedShowGroupTitle: true,
loading: isLoading,
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: '管理员',
render: (_, dom) => {
// src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
render: () => {
return (
<Dropdown
menu={{
items: [
{
key: 'logout',
icon: <Icon type={'Logout'}/>,
label: t('app.header.logout'),
},
],
}}
>
{dom}
</Dropdown>
<Avatar/>
)
},
}}
@ -114,16 +104,19 @@ export default () => {
if (props.isMobile) return []
if (typeof window === 'undefined') return []
return [
<SelectLang/>
<SelectLang/>,
]
}}
menuProps={{
className: styles.sideMenu,
}}
menuRender={(_, defaultDom) => (
<>
<span style={{ userSelect: 'none' }}>
{defaultDom}
</>
</span>
)}
menuItemRender={(item, dom) => {
return <div onClick={() => {
return <div style={{ userSelect: 'none' }} onClick={() => {
setPathname(item.path || '/dashboard')
}}
>

34
src/layout/style.ts

@ -1,21 +1,31 @@
import { createStyles } from '@/theme'
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,
}
})

294
src/pages/system/menus/index.tsx

@ -17,154 +17,158 @@ import { createLazyFileRoute } from '@tanstack/react-router'
const Menus = () => {
const { styles } = useStyle()
const { t } = useTranslation()
const [ form ] = Form.useForm()
const { mutate, isPending, isSuccess, error, isError } = useAtomValue(saveOrUpdateMenuAtom)
const { data = [] } = useAtomValue(menuDataAtom)
const currentMenu = useAtomValue<MenuItem>(selectedMenuAtom) ?? {}
useEffect(() => {
if (isSuccess) {
message.success(t('saveSuccess', '保存成功'))
}
if (isError) {
notification.error({
message: t('errorTitle', '错误'),
description: (error as any).message ?? t('saveFail', '保存失败'),
})
}
}, [ isError, isSuccess ])
return (
<PageContainer breadcrumbRender={false} title={false} className={styles.container}>
<Flexbox horizontal>
<DraggablePanel expandable={false} placement="left" maxWidth={800} style={{ width: '100%', }}>
<ProCard title={t('system.menus.title', '菜单')}
extra={
<BatchButton/>
}
>
<MenuTree form={form}/>
</ProCard>
</DraggablePanel>
<Flexbox className={styles.box}>
<Form form={form}
initialValues={currentMenu!}
labelCol={{ flex: '110px' }}
labelAlign="left"
labelWrap
wrapperCol={{ flex: 1 }}
colon={false}
className={styles.form}
>
<ProCard title={t('system.menus.setting', '配置')}
className={styles.formSetting}
>
<Form.Item hidden={true} label={'id'} name={'id'}>
<Input disabled={true}/>
</Form.Item>
<Form.Item label={t('system.menus.form.title', '菜单名称')} name={'title'}>
<Input/>
</Form.Item>
<Form.Item label={t('system.menus.form.parent', '上级菜单')} name={'parent_id'}>
<TreeSelect
treeData={[
{ id: 0, title: '顶级菜单', children: data as any },
]}
treeDefaultExpandAll={true}
fieldNames={{
label: 'title',
value: 'id'
}}/>
</Form.Item>
<Form.Item label={t('system.menus.form.type', '类型')} name={'type'}>
<Radio.Group
options={[
{
label: t('system.menus.form.typeOptions.menu', '菜单'),
value: 'menu'
},
{
label: t('system.menus.form.typeOptions.iframe', 'iframe'),
value: 'iframe'
},
{
label: t('system.menus.form.typeOptions.link', '外链'),
value: 'link'
},
{
label: t('system.menus.form.typeOptions.button', '按钮'),
value: 'button'
},
]}
optionType="button"
buttonStyle="solid"
/>
</Form.Item>
<Form.Item label={t('system.menus.form.name', '别名')} name={'name'}>
<Input/>
</Form.Item>
<Form.Item label={t('system.menus.form.icon', '图标')} name={'icon'}>
<IconPicker placement={'left'}/>
</Form.Item>
<Form.Item label={t('system.menus.form.sort', '排序')} name={'sort'}>
<InputNumber/>
</Form.Item>
<Form.Item label={t('system.menus.form.path', '路由')} name={'path'}>
<Input/>
</Form.Item>
<Form.Item label={t('system.menus.form.component', '视图')}
name={'component'}
help={t('system.menus.form.component.componentHelp', '视图路径,相对于src/pages')}
>
<Input addonBefore={'pages/'}/>
</Form.Item>
<Form.Item label={' '}>
<Button type="primary"
htmlType={'submit'}
loading={isPending}
onClick={() => {
form.validateFields().then((values) => {
mutate(values)
})
}}
>
{t('system.menus.form.save', '保存')}
</Button>
</Form.Item>
</ProCard>
<ProCard title={t('system.menus.form.button', '按钮')}
className={styles.formButtons}
colSpan={8}>
<Form.Item noStyle={true} name={'button'}
shouldUpdate={(prevValues: MenuItem, curValues) => {
return prevValues.id !== curValues.id
}}>
<ButtonTable form={form} key={(currentMenu as any).id}/>
</Form.Item>
</ProCard>
</Form>
</Flexbox>
</Flexbox>
</PageContainer>
)
const { styles } = useStyle()
const { t } = useTranslation()
const [ form ] = Form.useForm()
const { mutate, isPending, isSuccess, error, isError } = useAtomValue(saveOrUpdateMenuAtom)
const { data = [] } = useAtomValue(menuDataAtom)
const currentMenu = useAtomValue<MenuItem>(selectedMenuAtom) ?? {}
useEffect(() => {
if (isSuccess) {
message.success(t('saveSuccess', '保存成功'))
}
if (isError) {
notification.error({
message: t('errorTitle', '错误'),
description: (error as any).message ?? t('saveFail', '保存失败'),
})
}
}, [ isError, isSuccess ])
return (
<PageContainer breadcrumbRender={false} title={false} className={styles.container}>
<Flexbox horizontal>
<DraggablePanel expandable={false}
placement="left"
defaultSize={{ width: 300 }}
maxWidth={500}
>
<ProCard title={t('system.menus.title', '菜单')}
extra={
<BatchButton/>
}
>
<MenuTree form={form}/>
</ProCard>
</DraggablePanel>
<Flexbox className={styles.box}>
<Form form={form}
initialValues={currentMenu!}
labelCol={{ flex: '110px' }}
labelAlign="left"
labelWrap
wrapperCol={{ flex: 1 }}
colon={false}
className={styles.form}
>
<ProCard title={t('system.menus.setting', '配置')}
className={styles.formSetting}
>
<Form.Item hidden={true} label={'id'} name={'id'}>
<Input disabled={true}/>
</Form.Item>
<Form.Item label={t('system.menus.form.title', '菜单名称')} name={'title'}>
<Input/>
</Form.Item>
<Form.Item label={t('system.menus.form.parent', '上级菜单')} name={'parent_id'}>
<TreeSelect
treeData={[
{ id: 0, title: '顶级菜单', children: data as any },
]}
treeDefaultExpandAll={true}
fieldNames={{
label: 'title',
value: 'id'
}}/>
</Form.Item>
<Form.Item label={t('system.menus.form.type', '类型')} name={'type'}>
<Radio.Group
options={[
{
label: t('system.menus.form.typeOptions.menu', '菜单'),
value: 'menu'
},
{
label: t('system.menus.form.typeOptions.iframe', 'iframe'),
value: 'iframe'
},
{
label: t('system.menus.form.typeOptions.link', '外链'),
value: 'link'
},
{
label: t('system.menus.form.typeOptions.button', '按钮'),
value: 'button'
},
]}
optionType="button"
buttonStyle="solid"
/>
</Form.Item>
<Form.Item label={t('system.menus.form.name', '别名')} name={'name'}>
<Input/>
</Form.Item>
<Form.Item label={t('system.menus.form.icon', '图标')} name={'icon'}>
<IconPicker placement={'left'}/>
</Form.Item>
<Form.Item label={t('system.menus.form.sort', '排序')} name={'sort'}>
<InputNumber/>
</Form.Item>
<Form.Item label={t('system.menus.form.path', '路由')} name={'path'}>
<Input/>
</Form.Item>
<Form.Item label={t('system.menus.form.component', '视图')}
name={'component'}
help={t('system.menus.form.component.componentHelp', '视图路径,相对于src/pages')}
>
<Input addonBefore={'pages/'}/>
</Form.Item>
<Form.Item label={' '}>
<Button type="primary"
htmlType={'submit'}
loading={isPending}
onClick={() => {
form.validateFields().then((values) => {
mutate(values)
})
}}
>
{t('system.menus.form.save', '保存')}
</Button>
</Form.Item>
</ProCard>
<ProCard title={t('system.menus.form.button', '按钮')}
className={styles.formButtons}
colSpan={8}>
<Form.Item noStyle={true} name={'button'}
shouldUpdate={(prevValues: MenuItem, curValues) => {
return prevValues.id !== curValues.id
}}>
<ButtonTable form={form} key={(currentMenu as any).id}/>
</Form.Item>
</ProCard>
</Form>
</Flexbox>
</Flexbox>
</PageContainer>
)
}
export const Route = createLazyFileRoute('/system/menus')({
component: Menus
component: Menus
})

36
src/service/system.ts

@ -1,3 +1,4 @@
import { IUserInfo } from '@/types/user'
import request from '../request.ts'
import { LoginRequest, LoginResponse } from '@/types/login'
import { createCURD } from '@/service/base.ts'
@ -5,25 +6,28 @@ import { IMenu } from '@/types/menus'
import { IRole } from '@/types/roles'
const systemServ = {
dept: {
...createCURD('/sys/dept')
dept: {
...createCURD('/sys/dept')
},
menus: {
...createCURD<any, IMenu>('/sys/menu')
},
login: (data: LoginRequest) => {
return request.post<LoginResponse>('/sys/login', data)
},
user: {
current: () => {
return request.get<IUserInfo>('/sys/user/info')
},
menus: {
...createCURD<any, IMenu>('/sys/menu')
},
login: (data: LoginRequest) => {
return request.post<LoginResponse>('/sys/login', data)
},
user: {
menus: () => {
return request.get<IMenu[]>('/sys/user/menus')
}
},
role: {
...createCURD<any, IRole>('/sys/role')
menus: () => {
return request.get<IMenu[]>('/sys/user/menus')
}
},
role: {
...createCURD<any, IRole>('/sys/role')
}
}

67
src/store/user.ts

@ -1,49 +1,62 @@
import { appAtom } from '@/store/system.ts'
import { IUserInfo } from '@/types/user'
import { AxiosResponse } from 'axios'
import { atom } from 'jotai/index'
import { IAuth, MenuItem } from '@/types'
import { IApiResult, IAuth, MenuItem } from '@/types'
import { LoginRequest } from '@/types/login'
import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query'
import systemServ from '@/service/system.ts'
import { formatMenuData, isDev } from '@/utils'
export const authAtom = atom<IAuth>({
isLogin: false,
authKey: []
isLogin: false,
authKey: []
})
const devLogin = {
username: 'SupperAdmin',
password: 'kk123456',
code: '123456'
username: 'SupperAdmin',
password: 'kk123456',
code: '123456'
}
export const loginFormAtom = atom<LoginRequest>({
...(isDev ? devLogin : {})
...(isDev ? devLogin : {})
} as 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 () => {
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,
}))
Loading…
Cancel
Save