mirror of
https://github.com/zuiidea/antd-admin
synced 2026-10-30 13:23:49 +00:00
✨ adding useRequest
This commit is contained in:
@@ -25,6 +25,9 @@ const config: IConfig = {
|
||||
type: 'none',
|
||||
exclude: [],
|
||||
},
|
||||
mock: {
|
||||
exclude: ['utils/index'],
|
||||
},
|
||||
extraBabelPresets: ['@lingui/babel-preset-react'],
|
||||
extraBabelPlugins: [
|
||||
'macros',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Mock, Constant } from './_utils'
|
||||
import Mock from 'mockjs'
|
||||
import { Constant } from './utils'
|
||||
|
||||
const { ApiPrefix, Color } = Constant
|
||||
|
||||
@@ -135,7 +136,7 @@ const Dashboard = Mock.mock({
|
||||
],
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
[`GET ${ApiPrefix}/dashboard`](req, res) {
|
||||
res.json(Dashboard)
|
||||
},
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
declare module 'mockjs'
|
||||
declare module 'qs'
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Constant } from './_utils'
|
||||
import { Constant } from './utils'
|
||||
const { ApiPrefix } = Constant
|
||||
|
||||
const database = [
|
||||
@@ -148,7 +148,7 @@ const database = [
|
||||
},
|
||||
]
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
[`GET ${ApiPrefix}/routes`](req, res) {
|
||||
res.status(200).json(database)
|
||||
},
|
||||
+46
-34
@@ -1,9 +1,10 @@
|
||||
import { Mock, Constant, randomAvatar } from './_utils'
|
||||
import Mock from 'mockjs'
|
||||
import qs from 'qs'
|
||||
import { Constant, randomAvatar, waitTime } from './utils'
|
||||
|
||||
const { ApiPrefix } = Constant
|
||||
|
||||
let usersListData = Mock.mock({
|
||||
const usersListData = Mock.mock({
|
||||
'data|80-100': [
|
||||
{
|
||||
id: '@id',
|
||||
@@ -91,10 +92,28 @@ const NOTFOUND = {
|
||||
documentation_url: 'http://localhost:8000/request',
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
[`POST ${ApiPrefix}/user/login`](req, res) {
|
||||
const authorized = (req, res) => {
|
||||
const cookie = req.headers.cookie || ''
|
||||
const cookies = qs.parse(cookie.replace(/\s/g, ''), { delimiter: ';' })
|
||||
if (!cookies.token) {
|
||||
res.status(401).send({ message: 'Not Login' })
|
||||
return false
|
||||
}
|
||||
|
||||
const token = JSON.parse(cookies.token)
|
||||
if (cookies.token && token && token.deadline > new Date().getTime()) {
|
||||
return token
|
||||
}
|
||||
|
||||
res.status(401).send({ message: 'Not Login' })
|
||||
return false
|
||||
}
|
||||
|
||||
export default {
|
||||
async [`POST ${ApiPrefix}/user/login`](req, res) {
|
||||
await waitTime()
|
||||
const { username, password } = req.body
|
||||
const user = adminUsers.filter(item => item.username === username)
|
||||
const user = adminUsers.filter((item) => item.username === username)
|
||||
|
||||
if (user.length > 0 && user[0].password === password) {
|
||||
const now = new Date()
|
||||
@@ -113,33 +132,27 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
[`GET ${ApiPrefix}/user/logout`](req, res) {
|
||||
async [`GET ${ApiPrefix}/user/logout`](req, res) {
|
||||
await waitTime()
|
||||
res.clearCookie('token')
|
||||
res.status(200).end()
|
||||
},
|
||||
|
||||
[`GET ${ApiPrefix}/user`](req, res) {
|
||||
const cookie = req.headers.cookie || ''
|
||||
const cookies = qs.parse(cookie.replace(/\s/g, ''), { delimiter: ';' })
|
||||
const response = {}
|
||||
let user = {}
|
||||
if (!cookies.token) {
|
||||
res.status(200).send({ message: 'Not Login' })
|
||||
async [`GET ${ApiPrefix}/user`](req, res) {
|
||||
await waitTime()
|
||||
|
||||
const token = authorized(req, res)
|
||||
if (!token) {
|
||||
return
|
||||
}
|
||||
const token = JSON.parse(cookies.token)
|
||||
if (token) {
|
||||
response.success = token.deadline > new Date().getTime()
|
||||
|
||||
const userItem = adminUsers.find((_) => _.id === token.id)
|
||||
if (userItem) {
|
||||
const { password, ...rest } = userItem
|
||||
res.json(rest)
|
||||
} else {
|
||||
res.status(401).send({ message: 'Not Login' })
|
||||
}
|
||||
if (response.success) {
|
||||
const userItem = adminUsers.find(_ => _.id === token.id)
|
||||
if (userItem) {
|
||||
const { password, ...other } = userItem
|
||||
user = other
|
||||
}
|
||||
}
|
||||
response.user = user
|
||||
res.json(response)
|
||||
},
|
||||
|
||||
[`GET ${ApiPrefix}/users`](req, res) {
|
||||
@@ -151,10 +164,10 @@ module.exports = {
|
||||
let newData = database
|
||||
for (let key in other) {
|
||||
if ({}.hasOwnProperty.call(other, key)) {
|
||||
newData = newData.filter(item => {
|
||||
newData = newData.filter((item) => {
|
||||
if ({}.hasOwnProperty.call(item, key)) {
|
||||
if (key === 'address') {
|
||||
return other[key].every(iitem => item[key].indexOf(iitem) > -1)
|
||||
return other[key].every((iitem) => item[key].indexOf(iitem) > -1)
|
||||
} else if (key === 'createTime') {
|
||||
const start = new Date(other[key][0]).getTime()
|
||||
const end = new Date(other[key][1]).getTime()
|
||||
@@ -166,9 +179,8 @@ module.exports = {
|
||||
return true
|
||||
}
|
||||
return (
|
||||
String(item[key])
|
||||
.trim()
|
||||
.indexOf(decodeURI(other[key]).trim()) > -1
|
||||
String(item[key]).trim().indexOf(decodeURI(other[key]).trim()) >
|
||||
-1
|
||||
)
|
||||
}
|
||||
return true
|
||||
@@ -183,8 +195,8 @@ module.exports = {
|
||||
},
|
||||
|
||||
[`POST ${ApiPrefix}/users/delete`](req, res) {
|
||||
const { ids=[] } = req.body
|
||||
database = database.filter(item => !ids.some(_ => _ === item.id))
|
||||
const { ids = [] } = req.body
|
||||
database = database.filter((item) => !ids.some((_) => _ === item.id))
|
||||
res.status(204).end()
|
||||
},
|
||||
|
||||
@@ -221,7 +233,7 @@ module.exports = {
|
||||
const { id } = req.params
|
||||
const data = queryArray(database, id, 'id')
|
||||
if (data) {
|
||||
database = database.filter(item => item.id !== id)
|
||||
database = database.filter((item) => item.id !== id)
|
||||
res.status(204).end()
|
||||
} else {
|
||||
res.status(200).json(NOTFOUND)
|
||||
@@ -233,7 +245,7 @@ module.exports = {
|
||||
const editItem = req.body
|
||||
let isExist = false
|
||||
|
||||
database = database.map(item => {
|
||||
database = database.map((item) => {
|
||||
if (item.id === id) {
|
||||
isExist = true
|
||||
return Object.assign({}, item, editItem)
|
||||
@@ -9,7 +9,7 @@ export function queryArray(array, key, value) {
|
||||
if (!Array.isArray(array)) {
|
||||
return
|
||||
}
|
||||
return array.filter(_ => _[key] === value)
|
||||
return array.filter((_) => _[key] === value)
|
||||
}
|
||||
|
||||
export function randomNumber(min, max) {
|
||||
@@ -30,9 +30,19 @@ export function randomAvatar() {
|
||||
'photo-1551438632-e8c7d9a5d1b7.jpeg',
|
||||
'photo-1525879000488-bff3b1c387cf.jpeg',
|
||||
'photo-1487412720507-e7ab37603c6f.jpeg',
|
||||
'photo-1510227272981-87123e259b17.jpeg'
|
||||
'photo-1510227272981-87123e259b17.jpeg',
|
||||
]
|
||||
return `//image.zuiidea.com/${avatarList[randomNumber(0, avatarList.length - 1)]}?imageView2/1/w/200/h/200/format/webp/q/75|imageslim`
|
||||
return `//image.zuiidea.com/${
|
||||
avatarList[randomNumber(0, avatarList.length - 1)]
|
||||
}?imageView2/1/w/200/h/200/format/webp/q/75|imageslim`
|
||||
}
|
||||
|
||||
export const waitTime = (time: number = 300) => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(true)
|
||||
}, time)
|
||||
})
|
||||
}
|
||||
|
||||
export const Constant = {
|
||||
@@ -54,6 +64,3 @@ export const Constant = {
|
||||
sky: '#c1e0fc',
|
||||
},
|
||||
}
|
||||
|
||||
export Mock from 'mockjs'
|
||||
export qs from 'qs'
|
||||
@@ -20,6 +20,7 @@ export const config: IConfig = {
|
||||
logo: 'logo.svg',
|
||||
copyright: 'Ant Design Admin © 2021 zuiidea',
|
||||
language: language as ISupportedLocales,
|
||||
apiPrefix: '/api/v1',
|
||||
|
||||
/* Layout configuration, specify which layout to use for route. */
|
||||
layouts: [
|
||||
|
||||
+10
-2
@@ -1,4 +1,12 @@
|
||||
import { useHistory, useLocation } from 'umi'
|
||||
import { useHistory, useLocation, useRequest, UseRequestProvider } from 'umi'
|
||||
import useIntlProvider from './useIntlProvider'
|
||||
import useConfig from './useConfig'
|
||||
|
||||
export { useIntlProvider, useHistory, useLocation }
|
||||
export {
|
||||
useIntlProvider,
|
||||
useHistory,
|
||||
useLocation,
|
||||
useRequest,
|
||||
UseRequestProvider,
|
||||
useConfig,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { useContext } from 'react'
|
||||
import { ConfigContext } from '@/utils/context'
|
||||
|
||||
const useConfig = () => {
|
||||
return useContext(ConfigContext)
|
||||
}
|
||||
|
||||
export default useConfig
|
||||
@@ -3,7 +3,7 @@ import { useLocation } from '@/hooks'
|
||||
import { queryLayout } from '@/utils'
|
||||
import { config } from '@/configs'
|
||||
import PublicLayout from './PublicLayout'
|
||||
import PrimaryLayout from './PrimaryLayout'
|
||||
import PrimaryLayout from './PrimaryLayout/index'
|
||||
import './BaseLayout.less'
|
||||
|
||||
const LayoutMap = {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import React from 'react'
|
||||
import { Dropdown, Menu, Avatar } from 'antd'
|
||||
import { language, languages } from '@/configs'
|
||||
import { ISupportedLocales } from '@/typings'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { useRequest, useHistory, useConfig } from '@/hooks'
|
||||
import { logoutUser } from '@/services'
|
||||
import styles from './index.less'
|
||||
|
||||
const { SubMenu } = Menu
|
||||
|
||||
const NotFoundPage: React.FC = () => {
|
||||
const { language: locale, setLanguage, userInfo } = useConfig()
|
||||
const currentLanguage = language[locale]
|
||||
const history = useHistory()
|
||||
const { run: runLogoutUser } = useRequest(logoutUser, {
|
||||
manual: true,
|
||||
onSuccess: () => history.push('/login'),
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dropdown
|
||||
overlay={
|
||||
<Menu
|
||||
selectedKeys={[currentLanguage.value]}
|
||||
onClick={(data) => {
|
||||
setLanguage(data.key as ISupportedLocales)
|
||||
}}
|
||||
>
|
||||
{languages.map((item) => (
|
||||
<Menu.Item key={item.key}>
|
||||
<Avatar
|
||||
size="small"
|
||||
style={{ marginRight: 8 }}
|
||||
src={item.flag}
|
||||
/>
|
||||
{item.value}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<div className={styles.headerButton}>
|
||||
<Avatar size="small" src={currentLanguage.flag} />
|
||||
</div>
|
||||
</Dropdown>
|
||||
<Menu key="user" mode="horizontal">
|
||||
<SubMenu
|
||||
title={
|
||||
<>
|
||||
<span style={{ color: '#999', marginRight: 4 }}>
|
||||
<Trans>Hi,</Trans>
|
||||
</span>
|
||||
<span>{userInfo?.username}</span>
|
||||
<Avatar style={{ marginLeft: 8 }} src={userInfo?.avatar} />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Menu.Item key="SignOut" onClick={() => runLogoutUser()}>
|
||||
<Trans>Sign out</Trans>
|
||||
</Menu.Item>
|
||||
</SubMenu>
|
||||
</Menu>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default NotFoundPage
|
||||
@@ -1,5 +1,9 @@
|
||||
@import '~themes/vars.less';
|
||||
|
||||
@import '~@/themes/vars.less';
|
||||
:global {
|
||||
#root {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.backTop {
|
||||
right: 50px;
|
||||
|
||||
@@ -1,24 +1,15 @@
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useCallback,
|
||||
useContext,
|
||||
} from 'react'
|
||||
import React, { useState, useEffect, useMemo, useCallback } from 'react'
|
||||
import ProBasicLayout, {
|
||||
getMenuData,
|
||||
MenuDataItem,
|
||||
SettingDrawerProps,
|
||||
} from '@ant-design/pro-layout'
|
||||
import { Link } from 'umi'
|
||||
import { Dropdown, Menu, Avatar } from 'antd'
|
||||
import { useLocation } from '@/hooks'
|
||||
import { ConfigContext } from '@/utils/context'
|
||||
import { menus, menuIcon, language, languages, config } from '@/configs'
|
||||
import { menus, menuIcon, config } from '@/configs'
|
||||
import defaultSettings from '@/defaultSettings'
|
||||
import { ISupportedLocales } from '@/typings'
|
||||
import type { MenuProps } from 'antd/es/menu'
|
||||
import styles from './index.less'
|
||||
import HeaderRightContent from './HeaderRightContent'
|
||||
|
||||
const renderMenuItem = (menus: MenuDataItem[]): MenuDataItem[] =>
|
||||
menus.map(({ icon, children, ...item }) => ({
|
||||
@@ -34,8 +25,6 @@ const PrimaryLayout: React.FC = (props) => {
|
||||
const [selectedKeys, setSelectedKeys] = useState<string[]>(['/'])
|
||||
const { pathname } = location
|
||||
const { breadcrumbMap, menuData } = useMemo(() => getMenuData(menus), [])
|
||||
const { language: locale, setLanguage } = useContext(ConfigContext)
|
||||
const currentLanguage = language[locale]
|
||||
|
||||
useEffect(() => {
|
||||
const select = breadcrumbMap.get(pathname)
|
||||
@@ -68,30 +57,6 @@ const PrimaryLayout: React.FC = (props) => {
|
||||
[handleOnOpenChange, openKeys, selectedKeys]
|
||||
)
|
||||
|
||||
const rightContentRender = () => (
|
||||
<Dropdown
|
||||
overlay={
|
||||
<Menu
|
||||
selectedKeys={[currentLanguage.value]}
|
||||
onClick={(data) => {
|
||||
setLanguage(data.key as ISupportedLocales)
|
||||
}}
|
||||
>
|
||||
{languages.map((item) => (
|
||||
<Menu.Item key={item.key}>
|
||||
<Avatar size="small" style={{ marginRight: 8 }} src={item.flag} />
|
||||
{item.value}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<div className={styles.headerButton}>
|
||||
<Avatar size="small" src={currentLanguage.flag} />
|
||||
</div>
|
||||
</Dropdown>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProBasicLayout
|
||||
@@ -101,7 +66,7 @@ const PrimaryLayout: React.FC = (props) => {
|
||||
menuDataRender={menuDataRender}
|
||||
menuItemRender={menuItemRender}
|
||||
menuProps={menuProps}
|
||||
rightContentRender={rightContentRender}
|
||||
rightContentRender={() => <HeaderRightContent />}
|
||||
>
|
||||
{props.children}
|
||||
</ProBasicLayout>
|
||||
+36
-2
@@ -1,21 +1,55 @@
|
||||
import React from 'react'
|
||||
import { ConfigProvider } from 'antd'
|
||||
import { I18nProvider } from '@lingui/react'
|
||||
import { useIntlProvider } from '@/hooks'
|
||||
import {
|
||||
useIntlProvider,
|
||||
useRequest,
|
||||
UseRequestProvider,
|
||||
useHistory,
|
||||
} from '@/hooks'
|
||||
import { ConfigContext } from '@/utils/context'
|
||||
import request from '@/utils/request'
|
||||
import { queryUserInfo, IUserInfo } from '@/services'
|
||||
import BaseLayout from './BaseLayout'
|
||||
|
||||
const Layout: React.FC = (props) => {
|
||||
const { language, setLanguage, i18n, locale } = useIntlProvider()
|
||||
const history = useHistory()
|
||||
|
||||
const {
|
||||
data: userInfo,
|
||||
run: runQueryUserInfo,
|
||||
loading,
|
||||
} = useRequest<IUserInfo>(queryUserInfo, {
|
||||
onError: (data: any) => {
|
||||
if (data.statusCode === 401) {
|
||||
history.push('/login')
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const globalConfig = {
|
||||
language,
|
||||
setLanguage,
|
||||
userInfo: userInfo as IUserInfo,
|
||||
queryUserInfo: runQueryUserInfo,
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <div>loading</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<ConfigContext.Provider value={globalConfig}>
|
||||
<I18nProvider i18n={i18n}>
|
||||
<ConfigProvider locale={locale}>
|
||||
<BaseLayout>{props.children}</BaseLayout>
|
||||
<UseRequestProvider
|
||||
value={{
|
||||
requestMethod: request,
|
||||
}}
|
||||
>
|
||||
<BaseLayout>{props.children}</BaseLayout>
|
||||
</UseRequestProvider>
|
||||
</ConfigProvider>
|
||||
</I18nProvider>
|
||||
</ConfigContext.Provider>
|
||||
|
||||
+32
-17
@@ -4,27 +4,37 @@ import { GithubOutlined } from '@ant-design/icons'
|
||||
import { GlobalFooter } from '@/components'
|
||||
import { Trans, t } from '@lingui/macro'
|
||||
import { config, languages } from '@/configs'
|
||||
import { useRequest, useHistory, useConfig } from '@/hooks'
|
||||
import { loginUser, ILoginUserParams } from '@/services'
|
||||
import styles from './index.less'
|
||||
|
||||
const FormItem = Form.Item
|
||||
|
||||
const Login: React.FC = () => {
|
||||
const footerLinks = [
|
||||
{
|
||||
key: 'github',
|
||||
title: <GithubOutlined />,
|
||||
href: 'https://github.com/zuiidea/antd-admin',
|
||||
blankTarget: true,
|
||||
},
|
||||
...languages.map((item) => ({
|
||||
key: item.key,
|
||||
title: <span>{item.value}</span>,
|
||||
})),
|
||||
]
|
||||
const footerLinks = [
|
||||
{
|
||||
key: 'github',
|
||||
title: <GithubOutlined />,
|
||||
href: 'https://github.com/zuiidea/antd-admin',
|
||||
blankTarget: true,
|
||||
},
|
||||
...languages.map((item) => ({
|
||||
key: item.key,
|
||||
title: <span>{item.value}</span>,
|
||||
})),
|
||||
]
|
||||
|
||||
const handleFinish = (values) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(values)
|
||||
const Login: React.FC = () => {
|
||||
const { run: runLogin, loading } = useRequest(loginUser, {
|
||||
manual: true,
|
||||
})
|
||||
const history = useHistory()
|
||||
const { queryUserInfo } = useConfig()
|
||||
|
||||
const handleFinish = (values: ILoginUserParams) => {
|
||||
runLogin(values).then(() => {
|
||||
queryUserInfo()
|
||||
history.push('/dashborad')
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -50,7 +60,12 @@ const Login: React.FC = () => {
|
||||
<Input.Password placeholder={t`Password`} />
|
||||
</FormItem>
|
||||
<Row>
|
||||
<Button type="primary" htmlType="submit">
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
>
|
||||
<Trans>Sign in</Trans>
|
||||
</Button>
|
||||
<p>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import request from 'utils/request'
|
||||
import { apiPrefix } from 'utils/config'
|
||||
|
||||
import api from './api'
|
||||
|
||||
const gen = (params) => {
|
||||
let url = apiPrefix + params
|
||||
let method = 'GET'
|
||||
|
||||
const paramsArray = params.split(' ')
|
||||
if (paramsArray.length === 2) {
|
||||
method = paramsArray[0]
|
||||
url = apiPrefix + paramsArray[1]
|
||||
}
|
||||
|
||||
return function (data) {
|
||||
return request({
|
||||
url,
|
||||
data,
|
||||
method,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const APIFunction = {}
|
||||
for (const key in api) {
|
||||
APIFunction[key] = gen(api[key])
|
||||
}
|
||||
|
||||
APIFunction.queryWeather = (params) => {
|
||||
params.key = 'i7sau1babuzwhycn'
|
||||
return request({
|
||||
url: `${apiPrefix}/weather/now.json`,
|
||||
data: params,
|
||||
})
|
||||
}
|
||||
|
||||
export default APIFunction
|
||||
@@ -0,0 +1,44 @@
|
||||
import user, { TUserAPI } from '@/services/user'
|
||||
import createService, { TService } from '@/utils/createService'
|
||||
|
||||
const APIs = {
|
||||
...user,
|
||||
}
|
||||
|
||||
type RecordService<T> = {
|
||||
[P in keyof T]: TService
|
||||
}
|
||||
type TAPIService = RecordService<TUserAPI>
|
||||
const APIService = {} as TAPIService
|
||||
for (const key in APIs) {
|
||||
APIService[key] = createService(APIs[key])
|
||||
}
|
||||
|
||||
export const {
|
||||
logoutUser,
|
||||
queryUser,
|
||||
queryUserList,
|
||||
updateUser,
|
||||
createUser,
|
||||
removeUser,
|
||||
removeUserList,
|
||||
} = APIService
|
||||
|
||||
export interface IUserInfo {
|
||||
avatar: string
|
||||
id: number
|
||||
username: string
|
||||
permissions: {
|
||||
role?: string
|
||||
visit?: string[]
|
||||
}
|
||||
}
|
||||
|
||||
export const queryUserInfo = createService<IUserInfo>(user.queryUserInfo)
|
||||
|
||||
export interface ILoginUserParams {
|
||||
password: string
|
||||
username: string
|
||||
}
|
||||
|
||||
export const loginUser = createService<any, ILoginUserParams>(user.loginUser)
|
||||
@@ -1,18 +1,24 @@
|
||||
export default {
|
||||
queryRouteList: '/routes',
|
||||
export type TUserAPIProperty =
|
||||
| 'queryUserInfo'
|
||||
| 'logoutUser'
|
||||
| 'loginUser'
|
||||
| 'queryUser'
|
||||
| 'queryUserList'
|
||||
| 'updateUser'
|
||||
| 'createUser'
|
||||
| 'removeUser'
|
||||
| 'removeUserList'
|
||||
|
||||
export type TUserAPI = Record<TUserAPIProperty, string>
|
||||
|
||||
export default {
|
||||
queryUserInfo: '/user',
|
||||
logoutUser: '/user/logout',
|
||||
loginUser: 'POST /user/login',
|
||||
|
||||
queryUser: '/user/:id',
|
||||
queryUserList: '/users',
|
||||
updateUser: 'Patch /user/:id',
|
||||
createUser: 'POST /user',
|
||||
removeUser: 'DELETE /user/:id',
|
||||
removeUserList: 'POST /users/delete',
|
||||
|
||||
queryPostList: '/posts',
|
||||
|
||||
queryDashboard: '/dashboard',
|
||||
}
|
||||
} as TUserAPI
|
||||
@@ -12,5 +12,6 @@ export interface IConfig {
|
||||
title: string
|
||||
logo: string
|
||||
copyright: string
|
||||
apiPrefix: string
|
||||
layouts: ILayouItem[]
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import React from 'react'
|
||||
import { config } from '@/configs'
|
||||
import { ISupportedLocales } from '@/typings'
|
||||
import { IUserInfo } from '@/services'
|
||||
|
||||
interface IConfigContext {
|
||||
language: ISupportedLocales
|
||||
setLanguage: (language: ISupportedLocales) => void
|
||||
userInfo: IUserInfo | null
|
||||
queryUserInfo: () => void
|
||||
}
|
||||
|
||||
export const ConfigContext = React.createContext<IConfigContext>({
|
||||
language: config.language,
|
||||
setLanguage: (language) => null,
|
||||
userInfo: null,
|
||||
queryUserInfo: () => null,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import request from '@/utils/request'
|
||||
import { AxiosPromise, AxiosRequestConfig } from 'axios'
|
||||
import { config } from '@/configs'
|
||||
|
||||
const createService = <T, U = any>(params: string) => {
|
||||
let url = params
|
||||
let method = 'GET'
|
||||
|
||||
const paramsArray = params.split(' ')
|
||||
if (paramsArray.length === 2) {
|
||||
method = paramsArray[0]
|
||||
url = paramsArray[1]
|
||||
}
|
||||
|
||||
return (data: U) =>
|
||||
request<T>({
|
||||
url: config.apiPrefix + url,
|
||||
data,
|
||||
method: method as AxiosRequestConfig['method'],
|
||||
})
|
||||
}
|
||||
|
||||
export type TService = <T>(data?: any) => AxiosPromise<T>
|
||||
|
||||
export default createService
|
||||
+61
-86
@@ -1,92 +1,67 @@
|
||||
import axios from 'axios'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { message } from 'antd'
|
||||
import { CANCEL_REQUEST_MESSAGE } from '@/utils/constant'
|
||||
const { parse, compile } = require('path-to-regexp')
|
||||
import axios, { AxiosRequestConfig, AxiosPromise } from 'axios'
|
||||
|
||||
const { CancelToken } = axios
|
||||
window.cancelRequest = new Map()
|
||||
|
||||
export default function request(options) {
|
||||
let { data, url } = options
|
||||
const cloneData = cloneDeep(data)
|
||||
|
||||
try {
|
||||
let domain = ''
|
||||
const urlMatch = url.match(/[a-zA-z]+:\/\/[^/]*/)
|
||||
if (urlMatch) {
|
||||
;[domain] = urlMatch
|
||||
url = url.slice(domain.length)
|
||||
}
|
||||
|
||||
const match = parse(url)
|
||||
url = compile(url)(data)
|
||||
|
||||
for (const item of match) {
|
||||
if (item instanceof Object && item.name in cloneData) {
|
||||
delete cloneData[item.name]
|
||||
}
|
||||
}
|
||||
url = domain + url
|
||||
} catch (e) {
|
||||
message.error(e.message)
|
||||
const transformURL = (url: string, data: any) => {
|
||||
if (!data) {
|
||||
return { url, data }
|
||||
}
|
||||
let cloneData = data
|
||||
let cloneUrl = url
|
||||
try {
|
||||
cloneData = JSON.parse(JSON.stringify(data))
|
||||
} catch (error) {}
|
||||
|
||||
options.url = url
|
||||
options.cancelToken = new CancelToken((cancel) => {
|
||||
window.cancelRequest.set(Symbol(Date.now()), {
|
||||
pathname: window.location.pathname,
|
||||
cancel,
|
||||
})
|
||||
Object.entries(cloneData).forEach(([key]) => {
|
||||
const reg = new RegExp(`/:${key}`)
|
||||
if (reg.test(cloneUrl)) {
|
||||
cloneUrl = cloneUrl.replace(`/:${key}`, `/${cloneData[key]}`)
|
||||
delete cloneData[key]
|
||||
}
|
||||
})
|
||||
|
||||
return axios(options)
|
||||
.then((response) => {
|
||||
const { statusText, status, data } = response
|
||||
|
||||
let result = {}
|
||||
if (typeof data === 'object') {
|
||||
result = data
|
||||
if (Array.isArray(data)) {
|
||||
result.list = data
|
||||
}
|
||||
} else {
|
||||
result.data = data
|
||||
}
|
||||
|
||||
return Promise.resolve({
|
||||
success: true,
|
||||
message: statusText,
|
||||
statusCode: status,
|
||||
...result,
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
const { response, message } = error
|
||||
|
||||
if (String(message) === CANCEL_REQUEST_MESSAGE) {
|
||||
return {
|
||||
success: false,
|
||||
}
|
||||
}
|
||||
|
||||
let msg
|
||||
let statusCode
|
||||
|
||||
if (response && response instanceof Object) {
|
||||
const { data, statusText } = response
|
||||
statusCode = response.status
|
||||
msg = data.message || statusText
|
||||
} else {
|
||||
statusCode = 600
|
||||
msg = error.message || 'Network Error'
|
||||
}
|
||||
|
||||
/* eslint-disable */
|
||||
return Promise.reject({
|
||||
success: false,
|
||||
statusCode,
|
||||
message: msg,
|
||||
})
|
||||
})
|
||||
return { url: cloneUrl, data: cloneData }
|
||||
}
|
||||
|
||||
export type RequestConfig = AxiosRequestConfig | string
|
||||
|
||||
const request = <T>(config: RequestConfig): AxiosPromise<T> => {
|
||||
let params = config
|
||||
if (typeof config === 'object') {
|
||||
const axiosConfig = config as AxiosRequestConfig
|
||||
const { data, url } = transformURL(
|
||||
axiosConfig.url as string,
|
||||
axiosConfig.data
|
||||
)
|
||||
params = {
|
||||
...axiosConfig,
|
||||
data,
|
||||
url,
|
||||
}
|
||||
|
||||
if (
|
||||
(!params.method || params.method.toLocaleUpperCase() === 'GET') &&
|
||||
!params.params
|
||||
) {
|
||||
params.params = data
|
||||
}
|
||||
}
|
||||
|
||||
return axios(params as AxiosRequestConfig).catch((error) => {
|
||||
const { response } = error
|
||||
let message
|
||||
let statusCode = 0
|
||||
|
||||
if (response && response instanceof Object) {
|
||||
const { data, statusText } = response
|
||||
statusCode = response.status
|
||||
message = data.message || statusText
|
||||
} else {
|
||||
message = error.message
|
||||
}
|
||||
return Promise.reject({
|
||||
statusCode,
|
||||
message,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export default request
|
||||
|
||||
Reference in New Issue
Block a user