Skip to content

Commit

Permalink
升级 reduxå 原版 到 redux/toolkit, 减少大量模板代码,比如 actions, connect,代码更容易跟踪和阅读
Browse files Browse the repository at this point in the history
  • Loading branch information
tearf001 committed Mar 16, 2022
1 parent be71ff3 commit e4edeb3
Show file tree
Hide file tree
Showing 23 changed files with 311 additions and 212 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ module.exports = {
'jsx-control-statements/jsx-use-if-tag': 0,
'react/no-array-index-key': 0,
'react/jsx-props-no-spreading': 0,
'no-param-reassign': 0, // redux/toolkit使用immer库, 保证数据不被修改
// 禁止使用 var
'no-var': 'error',
// 可以使用 debugger
Expand Down
3 changes: 1 addition & 2 deletions src/app_models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@ export interface UserInfo {
displayName?: string
password?: string
token: string
permission?: Permission[]
roleList?: string[]
permission: Permission[]
}
2 changes: 1 addition & 1 deletion src/assets/js/publicFunc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const closeTabAction = (
returnUrl: string = '/',
cb?: () => void
) => {
const { curTab } = store.getState().storeData
const { curTab } = store.getState().tab
const { href } = window.location
const pathname = href.split('#')[1]
// 删除tab
Expand Down
28 changes: 17 additions & 11 deletions src/components/common/header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,24 @@ import {
LoadingOutlined
} from '@ant-design/icons'
import Breadcrumb from '@/components/common/breadcrumb'
import { connect } from 'react-redux'
import * as actions from '@/store/actions'
import { Icon } from '@iconify/react'
import { oidcLogout } from '@/config/oidc_setting'
import { useAppDispatch, useAppSelector } from '@/store/redux-hooks'
import { selectUserInfo, setUserInfo } from '@/store/slicers/userSlice'
import {
selectTheme,
setCollapsed as setCollapsedGlobal,
setTheme
} from '@/store/slicers/appSlice'

import style from './Header.module.less'

interface Props extends ReduxProps {}

const Header: FC<Props> = ({
storeData: { theme, userInfo },
setStoreData
}) => {
const Header: FC<Props> = () => {
const dispatch = useAppDispatch()
const theme = useAppSelector(selectTheme)
const userInfo = useAppSelector(selectUserInfo)
const history = useHistory()
const { username = '-' } = userInfo
const firstWord = username.slice(0, 1)
Expand All @@ -29,15 +35,15 @@ const Header: FC<Props> = ({
if (userInfo.is_oidc_user) {
setLoading(true)
await oidcLogout()
await setStoreData('SET_USERINFO', {})
dispatch(setUserInfo({})) // 清除用户信息 下同
} else {
await setStoreData('SET_USERINFO', {})
dispatch(setUserInfo({}))
history.replace({ pathname: '/login' })
}
}

const changeTheme = (themes: string) => {
setStoreData('SET_THEME', themes)
dispatch(setTheme(themes))
}

const menu = (
Expand All @@ -51,7 +57,7 @@ const Header: FC<Props> = ({

const toggle = (): void => {
setCollapsed(!collapsed)
setStoreData('SET_COLLAPSED', !collapsed)
dispatch(setCollapsedGlobal(!collapsed))
}

// 更换主题
Expand Down Expand Up @@ -121,4 +127,4 @@ const Header: FC<Props> = ({
</Layout.Header>
)
}
export default connect((state) => state, actions)(Header)
export default Header
26 changes: 13 additions & 13 deletions src/components/common/menu/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React, { useState, useEffect, useCallback, FC } from 'react'
import React, { FC, useCallback, useEffect, useState } from 'react'
import { Link, useLocation } from 'react-router-dom'
import { Menu, Layout } from 'antd'
import { Layout, Menu } from 'antd'

import MyIconFont from '@/components/common/myIconfont'
import { getKeyName, flattenRoutes } from '@/assets/js/publicFunc'
import { flattenRoutes, getKeyName } from '@/assets/js/publicFunc'
import menus from '@/config/menu'
import logo from '@/assets/img/logo.png'
import { connect } from 'react-redux'
import * as actions from '@/store/actions'
import { useAppSelector } from '@/store/redux-hooks'
import { selectUserInfo } from '@/store/slicers/userSlice'
import { selectCollapsed, selectTheme } from '@/store/slicers/appSlice'
import styles from './Menu.module.less'

const { SubMenu } = Menu
Expand All @@ -17,20 +18,22 @@ interface Props extends ReduxProps {}

type MenuType = CommonObjectType<string>

const MenuView: FC<Props> = ({ storeData: { theme, userInfo, collapsed } }) => {
const MenuView: FC<Props> = () => {
const userInfo = useAppSelector(selectUserInfo)
const collapsed = useAppSelector(selectCollapsed)
const theme = useAppSelector(selectTheme)
const { pathname } = useLocation()
const { tabKey: curKey = 'home' } = getKeyName(pathname)
const [current, setCurrent] = useState(curKey)
const { permission = [] } = userInfo
// 递归逐级向上获取最近一级的菜单,并高亮
const higherMenuKey = useCallback(
(checkKey = 'home', path = pathname) => {
const higherKey = checkKey
if (
checkKey === '403' ||
flatMenu.some((item: MenuType) => item.key === checkKey)
) {
return higherKey
return checkKey
}
const higherPath = path.match(/(.*)\//g)[0].replace(/(.*)\//, '$1')
const { tabKey } = getKeyName(higherPath)
Expand Down Expand Up @@ -78,7 +81,7 @@ const MenuView: FC<Props> = ({ storeData: { theme, userInfo, collapsed } }) => {
const creatSubMenu = (data: CommonObjectType): JSX.Element => {
const menuItemList = []
data.routes.map((item: MenuType) => {
const arr = permission.filter((ele: MenuType) => item.key === ele.code)
const arr = permission.filter((ele) => item.key === ele.code)
if (arr.length > 0) {
menuItemList.push(renderMenu(item))
}
Expand Down Expand Up @@ -138,7 +141,4 @@ const MenuView: FC<Props> = ({ storeData: { theme, userInfo, collapsed } }) => {
)
}

export default connect(
(state) => state,
actions
)(MenuView)
export default MenuView
16 changes: 7 additions & 9 deletions src/components/common/myEcharts/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import React, { FC } from 'react'
import ReactEcharts from 'echarts-for-react'
import { connect } from 'react-redux'
import { useAppSelector } from '@/store/redux-hooks'
import { selectTheme } from '@/store/slicers/appSlice'

interface Props extends ReduxProps {
option: object;
style?: object;
option: object
style?: object
}

const MyEcharts: FC<Props> = ({
option = {},
style = {},
storeData: { theme }
}) => {
const MyEcharts: FC<Props> = ({ option = {}, style = {} }) => {
const theme = useAppSelector(selectTheme)
const themeColor = theme === 'default' ? {} : { theme: 'dark' }
const options = {
...option,
Expand All @@ -25,4 +23,4 @@ const MyEcharts: FC<Props> = ({
return <ReactEcharts option={options} {...themeColor} style={style} />
}

export default connect((state) => state)(MyEcharts)
export default MyEcharts
47 changes: 23 additions & 24 deletions src/components/common/tabPanes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ import { Tabs, Alert, Dropdown, Menu } from 'antd'
import Home from '@/pages/home'
import { getKeyName, isAuthorized } from '@/assets/js/publicFunc'
import { SyncOutlined } from '@ant-design/icons'
import { connect } from 'react-redux'
import * as actions from '@/store/actions'
import { useAppDispatch, useAppSelector } from '@/store/redux-hooks'
import {
selectTabs,
selectReloadPath,
setTabs,
setReloadPath
} from '@/store/slicers/tabSlice'
import style from './TabPanes.module.less'

const { TabPane } = Tabs
Expand All @@ -28,32 +33,29 @@ const initPane = [
]

interface Props extends ReduxProps {
defaultActiveKey: string;
defaultActiveKey: string
panesItem: {
title: string,
content: Component,
key: string,
closable: boolean,
title: string
content: Component
key: string
closable: boolean
path: string
};
tabActiveKey: string;
}
tabActiveKey: string
}

// 多页签组件
const TabPanes: FC<Props> = (props) => {
const dispatch = useAppDispatch()
const reloadPath = useAppSelector(selectReloadPath)
const curTab = useAppSelector(selectTabs)
const [activeKey, setActiveKey] = useState<string>('')
const [panes, setPanes] = useState<CommonObjectType[]>(initPane)
const [isReload, setIsReload] = useState<boolean>(false)
const [selectedPanel, setSelectedPanel] = useState<CommonObjectType>({})
const pathRef: RefType = useRef<string>('')

const {
storeData: { curTab, reloadPath },
setStoreData,
defaultActiveKey,
panesItem,
tabActiveKey
} = props
const { defaultActiveKey, panesItem, tabActiveKey } = props

const history = useHistory()
const { pathname, search } = useLocation()
Expand All @@ -70,9 +72,9 @@ const TabPanes: FC<Props> = (props) => {
],
[]
)
setStoreData('SET_CURTAB', pathArr)
dispatch(setTabs(pathArr))
},
[setStoreData]
[dispatch]
)

// 从本地存储中恢复已打开的tab列表
Expand Down Expand Up @@ -157,9 +159,9 @@ const TabPanes: FC<Props> = (props) => {
setIsReload(false)
}, 1000)

setStoreData('SET_RELOADPATH', pathname + search)
dispatch(setReloadPath(pathname + search))
setTimeout(() => {
setStoreData('SET_RELOADPATH', 'null')
dispatch(setReloadPath('null'))
}, 500)
}

Expand Down Expand Up @@ -313,7 +315,4 @@ const TabPanes: FC<Props> = (props) => {
)
}

export default connect(
(state) => state,
actions
)(TabPanes)
export default TabPanes
46 changes: 21 additions & 25 deletions src/config/oidc_setting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
UserManagerSettings,
WebStorageStateStore
} from 'oidc-client'
import { UserInfo } from '@/app_models/user'

class Constants {
public static stsAuthority = process.env.REACT_APP_STS_URI as string // sts服务器
Expand Down Expand Up @@ -62,10 +63,8 @@ export const normalize_roles = (profile: Profile) => {
return roles
}

interface OidcProvideUser {
username: string
displayName: string
token: string
// oidc 外部用户, 适配之后的用户, 即为UserInfo wit
interface OidcUser extends UserInfo {
roleList?: any[]
is_oidc_user: true
}
Expand All @@ -74,48 +73,45 @@ interface OidcProvideUser {
const timeout = (prom, time) =>
Promise.race([prom(), new Promise((_r, rej) => setTimeout(rej, time))])

export type OidcUserAdaptFn<TUser> = (user: User) => Promise<TUser>
// 转换函数
export type OidcUserAdaptFn<TUser extends OidcUser> = (
user: User
) => Promise<TUser>

// 默认的适配函数.此处你可以通过api查询进行转换
async function defaultAdoptingUser<TUser extends OidcProvideUser>(
async function defaultAdaptingUser<TUser extends OidcUser>(
user: User
): Promise<TUser> {
const targetUser = {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const targetUser = <TUser>{
username: user.profile.given_name || user.profile.name,
displayName: user.profile.preferred_username || user.profile.given_name,
token: user.access_token,
is_oidc_user: true,
roleList: normalize_roles(user.profile)
// todo permissions handler
}
return targetUser as TUser
return targetUser
}

export async function callOidcLogin<TUser extends OidcProvideUser>(
storeCallback: (adoptedUser: TUser) => unknown,
adoptingFn = defaultAdoptingUser,
export async function callOidcLogin<TUser extends OidcUser>(
storeCallback: (adoptedUser: TUser | null) => unknown,
adoptingFn = defaultAdaptingUser,
time = 5000 // 限时,以免oidc服务器超时
) {
const user: User = await timeout(() => userManager.signinRedirect(), time)
// console.log('已登录用户', user)
if (user) {
const userInfo = await adoptingFn<TUser>(user)
userInfo.is_oidc_user = true
storeCallback(userInfo)
if (storeCallback) {
storeCallback(user ? await adoptingFn<TUser>(user) : null)
}
}

export async function loadOidcUser<TUser extends OidcProvideUser>(
storeCallback: (adoptedUser: TUser) => unknown,
adoptingFn = defaultAdoptingUser,
export async function loadOidcUser<TUser extends OidcUser>(
storeCallback: (adoptedUser: TUser | null) => unknown, // 存储用户信息的回调
adoptingFn = defaultAdaptingUser, // 适配函数,即把oidc的用户信息转换为本地用户信息
time = 5000 // 限时,以免oidc服务器超时
) {
const user: User = await timeout(() => userManager.getUser(), time)
// console.log('已登录用户', user)
if (user) {
const userInfo = await adoptingFn<TUser>(user)
userInfo.is_oidc_user = true
storeCallback(userInfo)
}
storeCallback(user ? await adoptingFn<TUser>(user) : null)
}

export async function oidcLogout() {
Expand Down
8 changes: 4 additions & 4 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import zhCN from 'antd/es/locale/zh_CN'
import moment from 'moment'
import 'moment/locale/zh-cn'
import { loadOidcUser } from '@/config/oidc_setting'
import { setStoreData } from '@/store/actions'
import { setUserInfo } from '@/store/slicers/userSlice'
import App from './App'
import '@/assets/css/public.less'
import '@/utils'
Expand Down Expand Up @@ -39,9 +39,9 @@ if (process.env.NODE_ENV === 'development') {
* @see https://mswjs.io/docs/recipes/deferred-mounting
*/
appReady.then(async () => {
await loadOidcUser((provided) => {
store.dispatch(setStoreData('SET_USERINFO', provided))
})
await loadOidcUser(
(provided) => provided && store.dispatch(setUserInfo(provided))
)

ReactDOM.render(
<ReduxProvider store={store}>
Expand Down
Loading

0 comments on commit e4edeb3

Please sign in to comment.