Browse Source

调整页面布局,增加PageTitle显示

main
dark 5 months ago
parent
commit
2b470f64e2
  1. 4
      src/App.css
  2. 14
      src/components/avatar/index.tsx
  3. 2
      src/components/loading/FetchLoading.tsx
  4. 2
      src/components/table/style.ts
  5. 2
      src/global.d.ts
  6. 65
      src/hooks/useInlineStyle.ts
  7. 63
      src/hooks/useResizeObserver.ts
  8. 20
      src/layout/ListPageLayout.tsx
  9. 27
      src/layout/RootLayout.tsx
  10. 21
      src/layout/TwoColPageLayout.tsx
  11. 17
      src/layout/style.ts
  12. 8
      src/pages/system/menus/index.tsx
  13. 5
      src/pages/websites/domain/index.tsx
  14. 5
      src/store/system.ts
  15. 203
      src/store/system/user.ts
  16. 9
      src/utils/index.ts

4
src/App.css

@ -18,3 +18,7 @@
gap: 5px; gap: 5px;
} }
} }
.ant-drawer .ant-drawer-footer{
background-color: #fcfcfc;
}

14
src/components/avatar/index.tsx

@ -1,19 +1,27 @@
import Icon from '@/components/icon' import Icon from '@/components/icon'
import { useTranslation } from '@/i18n.ts' import { useTranslation } from '@/i18n.ts'
import { currentUserAtom, logoutAtom } from '@/store/system/user.ts'
import { currentStaticUserAtom, currentUserAtom, logoutAtom } from '@/store/system/user.ts'
import { Avatar as AntAvatar, Dropdown, Spin } from 'antd' import { Avatar as AntAvatar, Dropdown, Spin } from 'antd'
import { useAtomValue } from 'jotai'
import { useAtomValue, useSetAtom } from 'jotai'
import { useNavigate } from '@tanstack/react-router' import { useNavigate } from '@tanstack/react-router'
import { useStyle } from './style' import { useStyle } from './style'
import { useEffect } from 'react'
const Avatar = () => { const Avatar = () => {
const { styles } = useStyle() const { styles } = useStyle()
const { t } = useTranslation() const { t } = useTranslation()
const { data, isLoading } = useAtomValue(currentUserAtom)
const { data, isLoading, isSuccess } = useAtomValue(currentUserAtom)
const updateUser = useSetAtom(currentStaticUserAtom)
const { mutate: logout } = useAtomValue(logoutAtom) const { mutate: logout } = useAtomValue(logoutAtom)
const navigate = useNavigate() const navigate = useNavigate()
useEffect(() => {
if (isSuccess) {
updateUser(data)
}
}, [ isSuccess ])
return ( return (
<div className={styles.container}> <div className={styles.container}>
<Dropdown <Dropdown

2
src/components/loading/FetchLoading.tsx

@ -5,7 +5,7 @@ import Loading from './index.tsx'
const FetchLoading = () => { const FetchLoading = () => {
const isFetching = useIsFetching() const isFetching = useIsFetching()
return (
return (
<> <>
<Loading loading={isFetching > 0}/> <Loading loading={isFetching > 0}/>
</> </>

2
src/components/table/style.ts

@ -10,7 +10,7 @@ export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any)
--toolbar-height: 65px; --toolbar-height: 65px;
--alter-height: 0px; --alter-height: 0px;
--padding: 33px; --padding: 33px;
--table-body-height: calc(var(--toolbar-height, 65px) + var(--alter-height, 0px) + var(--header-height, 56px) + var(--padding, 20px) * 4);
--table-body-height: calc( var(--pageHeader, 0px) + var(--toolbar-height, 65px) + var(--alter-height, 0px) + var(--header-height, 56px) + var(--padding, 20px) * 4);
.ant-table-body { .ant-table-body {
overflow: auto scroll; overflow: auto scroll;

2
src/global.d.ts

@ -3,6 +3,7 @@ import { QueryClient } from '@tanstack/react-query'
import { Router } from '@tanstack/react-router' import { Router } from '@tanstack/react-router'
import { RouteOptions } from '@tanstack/react-router/src/route.ts' import { RouteOptions } from '@tanstack/react-router/src/route.ts'
import { Attributes, ReactNode } from 'react' import { Attributes, ReactNode } from 'react'
import { System } from '@/types'
export type LayoutType = 'list' | 'form' | 'tree' | 'normal' export type LayoutType = 'list' | 'form' | 'tree' | 'normal'
@ -13,6 +14,7 @@ export type IAppData = {
baseUrl: string; baseUrl: string;
token: string; token: string;
device: string; device: string;
currentUser: System.IUser
} }
export type TRouter = { export type TRouter = {

65
src/hooks/useInlineStyle.ts

@ -0,0 +1,65 @@
import React, { useEffect, useRef } from 'react'
interface UseStyleOptions {
selector?: string;
styles: React.CSSProperties & { [key: `--${string}`]: string };
}
const applyStyles = (element: HTMLElement, styles: React.CSSProperties) => {
Object.entries(styles).forEach(([ key, value ]) => {
element.style.setProperty(key, value as string)
})
}
const removeStyles = (element: HTMLElement, styles: React.CSSProperties) => {
Object.keys(styles).forEach(key => {
element.style.removeProperty(key)
})
}
function useInlineStyle({ selector, styles }: UseStyleOptions) {
const elementRef = useRef<HTMLElement | null>(null)
useEffect(() => {
let targetElement: HTMLElement | null = null
const manageStyles = () => {
targetElement = selector ? document.querySelector<HTMLElement>(selector) : elementRef.current
if (!targetElement) return
applyStyles(targetElement, styles)
return () => {
if (targetElement) {
removeStyles(targetElement, styles)
}
}
}
if (selector) {
if (document.readyState === 'complete' || document.readyState === 'interactive') {
manageStyles()
} else {
document.addEventListener('DOMContentLoaded', manageStyles)
return () => {
document.removeEventListener('DOMContentLoaded', manageStyles)
}
}
} else {
manageStyles()
}
return () => {
if (targetElement) {
removeStyles(targetElement, styles)
}
}
}, [ selector, styles ])
return elementRef
}
export default useInlineStyle

63
src/hooks/useResizeObserver.ts

@ -0,0 +1,63 @@
import { useState, useEffect, useRef } from 'react'
interface UseResizeObserverOptions {
selector?: string;
}
interface Size {
width: number;
height: number;
inlineSize: number;
blockSize: number;
}
function useResizeObserver({ selector }: UseResizeObserverOptions = {}) {
const [ size, setSize ] = useState<Size>({ width: 0, height: 0, inlineSize: 0, blockSize: 0 })
const elementRef = useRef<HTMLElement | null>(null)
useEffect(() => {
const handleResize = (entries: ResizeObserverEntry[]) => {
if (entries[0]) {
const [ { inlineSize, blockSize } ] = entries[0].borderBoxSize
const { width, height } = entries[0].contentRect
setSize({ width, height, inlineSize, blockSize })
}
}
let resizeObserver: ResizeObserver | null = null
let targetElement: HTMLElement | null = null
const observeElement = () => {
targetElement = selector ? document.querySelector<HTMLElement>(selector) : elementRef.current
if (!targetElement) return
resizeObserver = new ResizeObserver(handleResize)
resizeObserver.observe(targetElement)
}
if (selector) {
if (document.readyState === 'complete' || document.readyState === 'interactive') {
observeElement()
} else {
document.addEventListener('DOMContentLoaded', observeElement)
return () => {
document.removeEventListener('DOMContentLoaded', observeElement)
}
}
} else {
observeElement()
}
return () => {
if (resizeObserver && targetElement) {
resizeObserver.unobserve(targetElement)
resizeObserver.disconnect()
}
}
}, [ selector, elementRef.current ])
return [ elementRef, size ] as const
}
export default useResizeObserver

20
src/layout/ListPageLayout.tsx

@ -1,6 +1,10 @@
import React from 'react' import React from 'react'
import { useStyle } from '@/layout/style.ts' import { useStyle } from '@/layout/style.ts'
import { PageContainer, PageContainerProps } from '@ant-design/pro-components' import { PageContainer, PageContainerProps } from '@ant-design/pro-components'
import { useAtomValue } from 'jotai'
import { currentMenuAtom } from '@/store/system.ts'
import useResizeObserver from '@/hooks/useResizeObserver.ts'
import useInlineStyle from '@/hooks/useInlineStyle.ts'
interface IListPageLayoutProps extends PageContainerProps { interface IListPageLayoutProps extends PageContainerProps {
children: React.ReactNode children: React.ReactNode
@ -11,13 +15,25 @@ const ListPageLayout: React.FC<IListPageLayoutProps> = (
{ {
className, children, authHeight = true, ...props className, children, authHeight = true, ...props
}) => { }) => {
const { styles, cx } = useStyle({ className: 'two-col' })
const { styles, cx } = useStyle({ className: 'one-col' })
const currentMenu = useAtomValue(currentMenuAtom)
const [ , headerSize ] = useResizeObserver({
selector: '.ant-page-header',
})
useInlineStyle({
styles: {
'--pageHeader': `${headerSize.blockSize}px`,
},
selector: `.one-col`,
})
return ( return (
<> <>
<PageContainer <PageContainer
breadcrumbRender={false} title={false}
ghost={false}
// breadcrumbRender={false}
title={ currentMenu?.title }
className={cx(styles.container, styles.pageCard, styles.layoutTable, className)} className={cx(styles.container, styles.pageCard, styles.layoutTable, className)}
{...props} {...props}

27
src/layout/RootLayout.tsx

@ -2,21 +2,21 @@ import Avatar from '@/components/avatar'
import PageBreadcrumb from '@/components/breadcrumb' import PageBreadcrumb from '@/components/breadcrumb'
import ErrorPage from '@/components/error/error.tsx' import ErrorPage from '@/components/error/error.tsx'
import SelectLang from '@/components/select-lang' import SelectLang from '@/components/select-lang'
import { appAtom } from '@/store/system.ts'
import { userMenuDataAtom } from '@/store/system/user.ts'
import { appAtom, currentMenuAtom } from '@/store/system.ts'
import { currentStaticUserAtom, userMenuDataAtom } from '@/store/system/user.ts'
import { MenuItem } from '@/global' import { MenuItem } from '@/global'
import { ProConfigProvider, ProLayout, } from '@ant-design/pro-components' import { ProConfigProvider, ProLayout, } from '@ant-design/pro-components'
import { zhCNIntl, enUSIntl } from '@ant-design/pro-provider/es/intl' import { zhCNIntl, enUSIntl } from '@ant-design/pro-provider/es/intl'
import { CatchBoundary, Link, Outlet, useNavigate } from '@tanstack/react-router' import { CatchBoundary, Link, Outlet, useNavigate } from '@tanstack/react-router'
import { ConfigProvider } from '@/components/config-provider' import { ConfigProvider } from '@/components/config-provider'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useAtomValue } from 'jotai'
import { useAtomValue, useSetAtom } from 'jotai'
import { useStyle } from '@/layout/style.ts' import { useStyle } from '@/layout/style.ts'
import zh from 'antd/locale/zh_CN' import zh from 'antd/locale/zh_CN'
import en from 'antd/locale/en_US' import en from 'antd/locale/en_US'
import type { MenuDataItem } from '@ant-design/pro-layout/es/typing' import type { MenuDataItem } from '@ant-design/pro-layout/es/typing'
import { convertToMenu, flattenTree } from '@/utils' import { convertToMenu, flattenTree } from '@/utils'
import { Flex, Menu, Space } from 'antd'
import { Flex, Menu, Space, Watermark } from 'antd'
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons' import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons'
import { If, Then } from 'react-if' import { If, Then } from 'react-if'
@ -47,8 +47,10 @@ export default () => {
const navigate = useNavigate() const navigate = useNavigate()
const { styles } = useStyle() const { styles } = useStyle()
const currentUser = useAtomValue(currentStaticUserAtom)
const { data: menuData = [], isLoading } = useAtomValue(userMenuDataAtom) const { data: menuData = [], isLoading } = useAtomValue(userMenuDataAtom)
const { language } = useAtomValue(appAtom) const { language } = useAtomValue(appAtom)
const setCurrentMenu = useSetAtom(currentMenuAtom)
const items = getBreadcrumbData(menuData, location.pathname) const items = getBreadcrumbData(menuData, location.pathname)
const [ pathname, setPathname ] = useState(location.pathname) const [ pathname, setPathname ] = useState(location.pathname)
const [ openMenuKeys, setOpenKeys ] = useState<string[]>([]) const [ openMenuKeys, setOpenKeys ] = useState<string[]>([])
@ -80,6 +82,10 @@ export default () => {
} }
}, [ location.pathname ]) }, [ location.pathname ])
useEffect(() => {
}, [location.pathname])
return ( return (
<div <div
className={styles.container} className={styles.container}
@ -100,6 +106,15 @@ export default () => {
return document.getElementById('crazy-pro-layout') || document.body return document.getElementById('crazy-pro-layout') || document.body
}} }}
> >
<Watermark {
...{
rotate: -31,
content: currentUser?.nickname,
fontColor: 'rgba(0,0,0,.15)',
fontSize: 17,
zIndex: 1009,
}
} style={{ width: '100vw', height: '100vh'}}>
<ProLayout <ProLayout
token={{ token={{
header: { header: {
@ -127,7 +142,7 @@ export default () => {
collapsedButtonRender={false} collapsedButtonRender={false}
// collapsed={false} // collapsed={false}
postMenuData={() => { postMenuData={() => {
return menuData.filter(item=>!item.hidden).map(item => ({
return menuData.filter(item => !item.hidden).map(item => ({
...item, ...item,
children: [], children: [],
})) as any })) as any
@ -209,6 +224,7 @@ export default () => {
onClick={(menu) => { onClick={(menu) => {
const info = menusFlatten.current?.find(item => item.path === menu.key) const info = menusFlatten.current?.find(item => item.path === menu.key)
if (info) { if (info) {
setCurrentMenu(info)
// setOpenKeys([ info.path as string ]) // setOpenKeys([ info.path as string ])
navigate({ navigate({
to: info.path, to: info.path,
@ -304,6 +320,7 @@ export default () => {
> >
</ProLayout>*/} </ProLayout>*/}
</ProLayout> </ProLayout>
</Watermark>
</ConfigProvider> </ConfigProvider>
</ProConfigProvider> </ProConfigProvider>
</CatchBoundary> </CatchBoundary>

21
src/layout/TwoColPageLayout.tsx

@ -3,6 +3,10 @@ import { PageContainer, PageContainerProps } from '@ant-design/pro-components'
import { Flexbox } from 'react-layout-kit' import { Flexbox } from 'react-layout-kit'
import { DraggablePanel, DraggablePanelProps } from '@/components/draggable-panel' import { DraggablePanel, DraggablePanelProps } from '@/components/draggable-panel'
import { useStyle } from './style' import { useStyle } from './style'
import { useAtomValue } from 'jotai/index'
import { currentMenuAtom } from '@/store/system.ts'
import useResizeObserver from '@/hooks/useResizeObserver.ts'
import useInlineStyle from '@/hooks/useInlineStyle.ts'
interface ITreePageLayoutProps { interface ITreePageLayoutProps {
children?: React.ReactNode children?: React.ReactNode
@ -14,10 +18,23 @@ interface ITreePageLayoutProps {
export const TwoColPageLayout: React.FC<ITreePageLayoutProps> = ({ className, ...props }) => { export const TwoColPageLayout: React.FC<ITreePageLayoutProps> = ({ className, ...props }) => {
const { styles, cx } = useStyle({ className: 'two-col' }) const { styles, cx } = useStyle({ className: 'two-col' })
const currentMenu = useAtomValue(currentMenuAtom)
const [ , headerSize ] = useResizeObserver({
selector: '.ant-page-header',
})
useInlineStyle({
styles: {
'--pageHeader': `${headerSize.blockSize}px`,
},
selector: `.two-col`,
})
return ( return (
<PageContainer <PageContainer
breadcrumbRender={false} title={false}
className={cx(styles.pageCard, styles.layoutTable, className)}
breadcrumbRender={false}
title={currentMenu?.title}
className={cx(styles.container, styles.pageCard, styles.layoutTable, className)}
{...props.pageProps} {...props.pageProps}
> >
<Flexbox horizontal className={styles.authHeight}> <Flexbox horizontal className={styles.authHeight}>

17
src/layout/style.ts

@ -40,6 +40,9 @@ export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any)
padding-block: 20px; padding-block: 20px;
} }
.ant-page-header{
background-color: white;
}
.ant-page-header-no-children { .ant-page-header-no-children {
height: 0px; height: 0px;
} }
@ -52,8 +55,8 @@ export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any)
.ant-pro-card-body{ .ant-pro-card-body{
padding-block-start: 0; padding-block-start: 0;
overflow: auto; overflow: auto;
height: calc(100vh - 160px);
min-height: calc(100vh - 160px);
height: calc(100vh - 160px - var(--pageHeader, 0px));
min-height: calc(100vh - 160px - var(--pageHeader, 0px));
${scrollbarBackground} ${scrollbarBackground}
} }
@ -77,6 +80,10 @@ export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any)
height: 100%; height: 100%;
} }
.layoutkit-flexbox{
height: calc(100% - var(--pageHeader, 0px));
}
` `
const pageContext = css` const pageContext = css`
@ -157,6 +164,8 @@ export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any)
.ant-menu-inline-collapsed >.ant-menu-item{ .ant-menu-inline-collapsed >.ant-menu-item{
padding-inline: calc(50% - 8px); padding-inline: calc(50% - 8px);
height: 52px;
line-height: 52px;
} }
.ant-menu-inline >.ant-menu-submenu>.ant-menu-submenu-title, .ant-menu-inline >.ant-menu-submenu>.ant-menu-submenu-title,
@ -182,6 +191,7 @@ export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any)
.ant-menu-light>.ant-menu .ant-menu-item-selected{ .ant-menu-light>.ant-menu .ant-menu-item-selected{
background-color: #3f9eff; background-color: #3f9eff;
color: white; color: white;
} }
` `
@ -282,12 +292,13 @@ export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any)
const body = css` const body = css`
overflow: hidden; overflow: hidden;
--bodyHeader: 50px; --bodyHeader: 50px;
--pageHeader: 0px;
.ant-pro-page-container { .ant-pro-page-container {
width: 100%; width: 100%;
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
height: calc(100vh - var(--bodyHeader, 50px));
height: calc(100vh - var(--bodyHeader, 50px) - var(--pageHeader, 0px));
} }
` `

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

@ -179,6 +179,12 @@ const Menus = () => {
<Form.Item label={t('system.menus.form.path', '路由')} name={'path'}> <Form.Item label={t('system.menus.form.path', '路由')} name={'path'}>
<Input/> <Input/>
</Form.Item> </Form.Item>
<Form.Item label={t('system.menus.form.active', '菜单高亮')}
name={'active'}
help={t('system.menus.form.activeHelp','子节点或详情页需要高亮的上级菜单路由地址')}
>
<Input/>
</Form.Item>
<Form.Item label={t('system.menus.form.component', '视图')} <Form.Item label={t('system.menus.form.component', '视图')}
name={'component'} name={'component'}
@ -202,7 +208,7 @@ const Menus = () => {
loading={isPending} loading={isPending}
onClick={() => { onClick={() => {
form.validateFields().then((values) => { form.validateFields().then((values) => {
console.log(values)
// console.log(values)
mutate(values) mutate(values)
}) })
}} }}

5
src/pages/websites/domain/index.tsx

@ -17,6 +17,7 @@ import { useStyle } from './style'
import { FilterOutlined } from '@ant-design/icons' import { FilterOutlined } from '@ant-design/icons'
import { getValueCount } from '@/utils' import { getValueCount } from '@/utils'
import { Table as ProTable } from '@/components/table' import { Table as ProTable } from '@/components/table'
import { Link } from '@tanstack/react-router'
const i18nPrefix = 'websiteDomains.list' const i18nPrefix = 'websiteDomains.list'
@ -48,8 +49,10 @@ const WebsiteDomain = () => {
{ {
title: t(`${i18nPrefix}.columns.name`, '域名'), title: t(`${i18nPrefix}.columns.name`, '域名'),
dataIndex: 'name', dataIndex: 'name',
render(_text, record){
return <Link to={'/websites/record' }>{record.name}</Link>
}
}, },
{ {
title: t(`${i18nPrefix}.columns.dns_account_id`, 'DNS账号'), title: t(`${i18nPrefix}.columns.dns_account_id`, 'DNS账号'),
dataIndex: 'dns_account_id', dataIndex: 'dns_account_id',

5
src/store/system.ts

@ -2,6 +2,8 @@ import { IAppData } from '@/global'
import { createStore } from 'jotai' import { createStore } from 'jotai'
import { atomWithStorage } from 'jotai/utils' import { atomWithStorage } from 'jotai/utils'
import { changeLanguage as setLang } from 'i18next' import { changeLanguage as setLang } from 'i18next'
import { atom } from 'jotai/index'
import { System } from '@/types'
/** /**
* app全局状态 * app全局状态
@ -24,6 +26,9 @@ export const getAppData = () => {
return appStore.get(appAtom) return appStore.get(appAtom)
} }
export const currentMenuAtom = atom<System.IMenu>(null)
export const changeLanguage = (lang: string, reload?: boolean) => { export const changeLanguage = (lang: string, reload?: boolean) => {
setLang(lang) setLang(lang)
updateAppData({ language: lang }) updateAppData({ language: lang })

203
src/store/system/user.ts

@ -1,5 +1,5 @@
import { appAtom, setToken } from '@/store/system.ts' import { appAtom, setToken } from '@/store/system.ts'
import { atom } from 'jotai/index'
import { atom } from 'jotai'
import { IApiResult, IAuth, IPage, IPageResult, MenuItem } from '@/global' import { IApiResult, IAuth, IPage, IPageResult, MenuItem } from '@/global'
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'
@ -7,70 +7,77 @@ import { formatMenuData, isDev } from '@/utils'
import { message } from 'antd' import { message } from 'antd'
import { t } from 'i18next' import { t } from 'i18next'
import { System } from '@/types' import { System } from '@/types'
import { atomWithStorage } from 'jotai/utils'
import { IUserInfo } from '@/types/system/user'
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<System.LoginRequest>({ export const loginFormAtom = atom<System.LoginRequest>({
...(isDev ? devLogin : {})
...(isDev ? devLogin : {})
} as System.LoginRequest) } as System.LoginRequest)
export const loginAtom = atomWithMutation<any, System.LoginRequest>((get) => ({ export const loginAtom = atomWithMutation<any, System.LoginRequest>((get) => ({
mutationKey: [ 'login' ],
mutationFn: async (params) => {
return await systemServ.login(params)
},
onSuccess: (res) => {
message.success(t('login.success'))
// console.log('login success', res)
get(userMenuDataAtom).refetch().then()
return res.data
},
retry: false,
mutationKey: [ 'login' ],
mutationFn: async (params) => {
return await systemServ.login(params)
},
onSuccess: (res) => {
message.success(t('login.success'))
// console.log('login success', res)
get(userMenuDataAtom).refetch().then()
return res.data
},
retry: false,
})) }))
export const logoutAtom = atomWithMutation(() => ({ export const logoutAtom = atomWithMutation(() => ({
mutationKey: [ 'logout' ],
mutationFn: async () => {
setToken('')
return true
},
mutationKey: [ 'logout' ],
mutationFn: async () => {
setToken('')
return true
},
})) }))
export const currentUserAtom = atomWithQuery<IApiResult<System.IUserInfo>, any, System.IUserInfo>((get) => {
return {
queryKey: [ 'user_info', get(appAtom).token ],
queryFn: async () => {
return await systemServ.user.current()
},
select: (data) => {
return data.data
}
}
})
export const currentStaticUserAtom = atomWithStorage<IUserInfo | null>('user', null)
export const userMenuDataAtom = atomWithQuery<IApiResult<IPageResult<System.IMenu[]>>, any, MenuItem[]>((get) => ({
enabled: false,
queryKey: [ 'user_menus', get(appAtom).token ],
export const currentUserAtom = atomWithQuery<IApiResult<System.IUserInfo>, any, System.IUserInfo>((get) => {
return {
queryKey: [ 'user_info', get(appAtom).token ],
queryFn: async () => { queryFn: async () => {
return await systemServ.user.menus()
return await systemServ.user.current()
}, },
select: (data) => { select: (data) => {
return formatMenuData(data.data.rows as any ?? [], [])
// store.set(currentStaticUserAtom, data.data)
return data.data
}, },
retry: false,
}
})
export const userMenuDataAtom = atomWithQuery<IApiResult<IPageResult<System.IMenu[]>>, any, MenuItem[]>((get) => ({
enabled: false,
queryKey: [ 'user_menus', get(appAtom).token ],
queryFn: async () => {
return await systemServ.user.menus()
},
select: (data) => {
return formatMenuData(data.data.rows as any ?? [], [])
},
retry: false,
})) }))
export type UserSearch = { export type UserSearch = {
dept_id?: any,
key?: string
dept_id?: any,
key?: string
} }
export const userSearchAtom = atom<UserSearch>({} as UserSearch) export const userSearchAtom = atom<UserSearch>({} as UserSearch)
@ -78,87 +85,87 @@ export const userSearchAtom = atom<UserSearch>({} as UserSearch)
//=======user page store====== //=======user page store======
export const userPageAtom = atom<IPage>({ export const userPageAtom = atom<IPage>({
pageSize: 10,
page: 1
pageSize: 10,
page: 1
}) })
// user list // user list
export const userListAtom = atomWithQuery((get) => { export const userListAtom = atomWithQuery((get) => {
return {
queryKey: [ 'user_list', get(userSearchAtom), get(userPageAtom) ],
queryFn: async ({ queryKey: [ , params, page ] }) => {
return await systemServ.user.list({
...params as any,
...page as any,
})
},
select: (data) => {
return data.data
},
return {
queryKey: [ 'user_list', get(userSearchAtom), get(userPageAtom) ],
queryFn: async ({ queryKey: [ , params, page ] }) => {
return await systemServ.user.list({
...params as any,
...page as any,
})
},
select: (data) => {
return data.data
},
}
}
}) })
// user selected // user selected
export const userSelectedAtom = atom<System.IUser>({} as System.IUser) export const userSelectedAtom = atom<System.IUser>({} as System.IUser)
export const defaultUserData = { export const defaultUserData = {
id: 0,
dept_id: 0,
role_id: 0,
id: 0,
dept_id: 0,
role_id: 0,
} as System.IUser } as System.IUser
//save or update user //save or update user
export const saveOrUpdateUserAtom = atomWithMutation<IApiResult, System.IUser>((get) => ({ export const saveOrUpdateUserAtom = atomWithMutation<IApiResult, System.IUser>((get) => ({
mutationKey: [ 'save_user' ],
mutationFn: async (params) => {
params.status = params.status ? '1' : '0'
const isAdd = 0 === params.id
if (isAdd) {
return await systemServ.user.add(params)
}
return await systemServ.user.update(params)
},
onSuccess: (res) => {
const isAdd = !!res.data?.id
message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功'))
mutationKey: [ 'save_user' ],
mutationFn: async (params) => {
params.status = params.status ? '1' : '0'
const isAdd = 0 === params.id
if (isAdd) {
return await systemServ.user.add(params)
}
return await systemServ.user.update(params)
},
onSuccess: (res) => {
const isAdd = !!res.data?.id
message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功'))
//刷新userList
get(queryClientAtom).invalidateQueries({ queryKey: [ 'user_list' ] })
//刷新userList
get(queryClientAtom).invalidateQueries({ queryKey: [ 'user_list' ] })
return res
},
return res
},
})) }))
//delete user //delete user
export const batchUserIdsAtom = atom<number[]>([]) export const batchUserIdsAtom = atom<number[]>([])
export const deleteUserAtom = atomWithMutation<IApiResult, number[]>((get) => ({ export const deleteUserAtom = atomWithMutation<IApiResult, number[]>((get) => ({
mutationKey: [ 'delete_user' ],
mutationFn: async (params) => {
return await systemServ.user.batchDelete(params)
},
onSuccess: () => {
message.success(t('message.deleteSuccess', '删除成功'))
//刷新userList
get(queryClientAtom).invalidateQueries({ queryKey: [ 'user_list' ] })
return true
},
mutationKey: [ 'delete_user' ],
mutationFn: async (params) => {
return await systemServ.user.batchDelete(params)
},
onSuccess: () => {
message.success(t('message.deleteSuccess', '删除成功'))
//刷新userList
get(queryClientAtom).invalidateQueries({ queryKey: [ 'user_list' ] })
return true
},
})) }))
//reset password //reset password
export const resetPasswordAtom = atomWithMutation<IApiResult, number>(() => ({ export const resetPasswordAtom = atomWithMutation<IApiResult, number>(() => ({
mutationKey: [ 'reset_password' ],
mutationFn: async (id) => {
return await systemServ.user.resetPassword(id)
},
onSuccess: () => {
message.success(t('message.resetSuccess', '重置成功'))
//刷新userList
// get(queryClientAtom).invalidateQueries({ queryKey: [ 'user_list' ] })
return true
},
onError: () => {
message.error(t('message.resetError', '重置失败'))
},
mutationKey: [ 'reset_password' ],
mutationFn: async (id) => {
return await systemServ.user.resetPassword(id)
},
onSuccess: () => {
message.success(t('message.resetSuccess', '重置成功'))
//刷新userList
// get(queryClientAtom).invalidateQueries({ queryKey: [ 'user_list' ] })
return true
},
onError: () => {
message.error(t('message.resetError', '重置失败'))
},
})) }))

9
src/utils/index.ts

@ -64,13 +64,16 @@ export const convertToMenu = (data: any[], format?: (item: any) => any) => {
const result: MenuItemProps[] = [] const result: MenuItemProps[] = []
format = format ?? ((item: any) => item) format = format ?? ((item: any) => item)
for (const item of data) { for (const item of data) {
if (item.hidden) {
continue
}
const _item = format(item) const _item = format(item)
if (_item.children && _item.children.length) { if (_item.children && _item.children.length) {
result.push( {
result.push({
..._item, ..._item,
children: convertToMenu(_item.children, format), children: convertToMenu(_item.children, format),
}) })
}else {
} else {
result.push(_item) result.push(_item)
} }
} }
@ -155,7 +158,7 @@ export const getValueCount = (obj: any, filterObj: any = {}) => {
// 获取对象中所有值的数量 // 获取对象中所有值的数量
let count = 0 let count = 0
for (const key in obj) { for (const key in obj) {
if (['page', 'pageSize', 'pageIndex'].includes(key)){
if ([ 'page', 'pageSize', 'pageIndex' ].includes(key)) {
continue continue
} }
if (Object.prototype.hasOwnProperty.call(obj, key) && obj[key]) { if (Object.prototype.hasOwnProperty.call(obj, key) && obj[key]) {

Loading…
Cancel
Save