文章目录
React前端实例教程
源码下载
https://download.csdn.net/download/chinaxsw/19746373
项目介绍
ReactDemo用到的技术
# 阿里antd组件调用
antd
# 钩子函数
hooks
# 状态钩子
useState
# action 钩子
useReducer
# 共享状态钩子
useContext
# 副作用钩子
useEffect
# 服务调用
axios
# 阿里图标库
iconfont
1.建立项目
环境搭建
安装node.js(详细安装过程及配置,百度一下)
开始建立项目
# create-react-app建立项目
npx create-react-app react-demo
# 运行项目
yarn start
# react路由
yarn add react-router-dom
antd
# antd
yarn add antd
# 引入样式 index.js
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
antd按需加载
# antd 的 JS 代码默认支持基于 ES modules 的 tree shaking。
2.整理项目目录结构
src
|--assets # 资源目录
|--components # 组件目录
|--pages # 页面目录
|--......
|--routes # 路由目录
|--index.js
|--servics # 服务目录
|--store # store目录
|--context # context目录
|--provider # provider目录
|--reducers # reducers目录
|--utils # utils目录
App.css
App.js
index.js
插件搜索es7安装(非必须,可不安装插件)
ES7 React/Redux/GraphQL/React-Native snippets
创建页面
pages
|--admin
|--dashboard
|--Index.js
|--product
|--Save.js
|--List.js
|--login
|--Login.js
|--PageNotFound.js
3.路由配置
routes/index.js
import Index from "../pages/admin/dashboard/Index"
import ProductSave from "../pages/admin/product/Save"
import ProductList from "../pages/admin/product/List"
import Login from "../pages/login/Login"
import PageNotFound from "../pages/PageNotFound"
export const mainRoutes = [
{
path: '/',
component: Login,
exact: true
},
{
path: '/login',
component: Login
},
{
path: '/404',
component: PageNotFound
}
]
export const adminRoutes = [
{
path: '/admin/dashboard',
component: Index
},
{
path: '/admin/product/save',
component: ProductSave,
exact: true
},
{
path: '/admin/product/list',
component: ProductList,
},
]
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import { mainRoutes } from './routes'
import reportWebVitals from './reportWebVitals'
import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom'
ReactDOM.render(
<Router>
<Switch>
<Route path="/admin" render={routeProps => <App {...routeProps} />}></Route>
{mainRoutes.map(route => {
return <Route key={route.path} {...route}></Route>
})}
<Redirect to="/404" />
</Switch>
</Router>
, document.getElementById('root')
)
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals()
App.js
import React from 'react'
import { Switch, Route, Redirect } from 'react-router-dom'
import { adminRoutes } from './routes'
import './App.css'
import { Button } from 'antd'
function App () {
return (
<div className="App">
<h2>Demo</h2>
<Button type='primary'>按钮</Button>
<Switch>
{adminRoutes.map(route => {
return (
<Route
key={route.path}
path={route.path}
exact={route.exact}
render={routeProps => {
return <route.component {...routeProps} />
}}
/>
)
})}
<Redirect to="/404" />
</Switch>
</div>
)
}
export default App
4.后台页面布局
新建组件 components/admin/frame/Index.js
import React, { useState, useEffect } from "react"
import { Layout, Menu } from 'antd'
import {
MenuUnfoldOutlined,
MenuFoldOutlined,
UserOutlined,
VideoCameraOutlined,
UploadOutlined,
} from '@ant-design/icons'
import './Index.css'
function Index (props) {
const { Header, Sider, Content, Footer } = Layout
const [collapsed, setCollapsed] = useState(false)
const [contentHeight, setContentHeight] = useState(600)
useEffect(() => {
setContentHeight(window.innerHeight - 189)
})
return (
<Layout>
<Sider trigger={null} collapsible collapsed={collapsed}>
<div className="logo" />
<Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
<Menu.Item key="1" icon={<UserOutlined />}>
nav 1
</Menu.Item>
<Menu.Item key="2" icon={<VideoCameraOutlined />}>
nav 2
</Menu.Item>
<Menu.Item key="3" icon={<UploadOutlined />}>
nav 3
</Menu.Item>
</Menu>
</Sider>
<Layout className="site-layout">
<Header className="site-layout-background" style={{ padding: 0 }}>
{React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
className: 'trigger',
onClick: () => { setCollapsed(!collapsed) },
})}
</Header>
<Content
className="site-layout-background"
style={{
margin: '24px 16px',
padding: 24,
minHeight: contentHeight,
}}
>
{props.children}
</Content>
<Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer>
</Layout>
</Layout>
)
}
export default Index
components/admin/frame/Index.css
.trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color 0.3s;
}
.trigger:hover {
color: #1890ff;
}
.logo {
height: 32px;
background: rgba(255, 255, 255, 0.3);
margin: 16px;
}
.site-layout .site-layout-background {
background: #fff;
}
App.js
import React from 'react'
import { Switch, Route, Redirect } from 'react-router-dom'
import { adminRoutes } from './routes'
import Frame from './components/admin/frame/Index'
import { isLogined } from './utils/auth'
import './App.css'
function App () {
return (
isLogined() ?
<Frame>
<Switch>
{adminRoutes.map(route => {
return (
<Route
key={route.path}
path={route.path}
exact={route.exact}
render={routeProps => {
return <route.component {...routeProps} />
}}
/>
)
})}
<Redirect to="/404" />
</Switch>
</Frame> : <Redirect to="/login" />
)
}
export default App
5.登录页面 login.js
登录页面 login.js
import React from 'react'
import { loginApi } from '../../services/auth'
import { setToken } from '../../utils/auth'
import { Row, Col, Form, Input, Button, Checkbox } from 'antd'
import { UserOutlined, LockOutlined } from '@ant-design/icons'
function Login (props) {
const onFinish = (values) => {
loginApi({
username: values.username,
password: values.password
})
.then(res => {
if (res.code == 2000) {
setToken(res.data.token)
props.history.push("/admin/dashboard")
} else {
alert('账号密码错误')
}
})
.catch(err => {
alert('账号密码错误')
})
}
return (
<div>
<div style={{ height: '100px' }}></div>
<Row>
<Col span={8}></Col>
<Col span={8} style={{ fontSize: '20px', fontWeight: 'bolder' }}>管理员登录</Col>
<Col span={8}></Col>
</Row>
<Row style={{ marginTop: '20px' }}>
<Col span={8}></Col>
<Col span={8}>
<Form
name="normal_login"
className="login-form"
initialValues={{ remember: true }}
onFinish={onFinish}
>
<Form.Item
name="username"
rules={[{ required: true, message: 'Please input your Username!' }]}
>
<Input prefix={<UserOutlined className="site-form-item-icon" />} placeholder="Username" style={{ height: '42px' }} />
</Form.Item>
<Form.Item
name="password"
rules={[{ required: true, message: 'Please input your Password!' }]}
>
<Input
prefix={<LockOutlined className="site-form-item-icon" />}
type="password"
placeholder="Password"
style={{ height: '42px' }}
/>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" style={{ width: '100%', height: '42px' }}>
Log in
</Button>
</Form.Item>
</Form>
</Col>
<Col span={8}></Col>
</Row>
</div>
)
}
export default Login
6.封装axios
# 安装axios
yarn add axios
项目目录 src/utils/auth.js
export function getToken () {
return localStorage.getItem("token")
}
export function setToken (token) {
localStorage.setItem("token", token)
}
export function clearToken () {
localStorage.removeItem("token")
}
export function isLogined () {
if (localStorage.getItem("token")) {
return true
}
return false
}
项目目录 src/utils/request.js
import axios from 'axios'
import { getToken } from './auth'
var baseUrl = "http://localhost:9000"
export function getBaseUrl () {
return baseUrl
}
const instance = axios.create({
baseURL: baseUrl,
timeout: 5000
})
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
config.headers['authorization'] = 'Bearer ' + getToken()
//config.headers.Authorization = getToken()
return config
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error)
})
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response.data
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error)
})
export function get (url, params) {
return instance.get(url, {
params
})
}
export function post (url, data) {
return instance.post(url, data)
}
export function put (url, params) {
return instance.put(url, params)
}
export function del (url) {
return instance.delete(url)
}
项目目录 src/services/auth.js
import { post } from '../utils/request'
/**
* 用户登录
* @param {userName,password} user
*/
export function loginApi (user) {
return post('/api/admin/login', user)
}
项目目录 src/services/admin.js
import { get, post, del, getBaseUrl } from '../utils/request'
/**
* 获取服务地址
*/
export function getServerUrl () {
return getBaseUrl()
}
/**
* 获取管理员信息
* @param {*} id
*/
export function getAdminByIdApi (id) {
return get('/api/admin/get/' + id)
}
/**
* 添加产品
* @param {*} data
*/
export function saveProductApi (data) {
return post('/api/product/save/', data)
}
/**
* 删除产品信息
* @param URLSearchParams id
*/
export function removeProductApi (id) {
const params = new URLSearchParams()
params.append('id', id)
return post('/api/product/remove/', params)
}
/**
* 修改产品
* @param {*} data
*/
export function updateProductApi (data) {
return post('/api/product/update/', data)
}
/**
* 获取产品信息
*/
export function getProductListApi () {
return get('/api/product/list')
}
admin/products/List.js
import React, { useState, useEffect } from 'react'
import { getProductsListApi } from '../../../services/admin'
function List (props) {
const [productsData, setProductsData] = useState([])
useEffect(() => {
getProductsListApi()
.then(res => {
if (res.code === 2000) {
setProductsData(res.data)
} else {
alert('获取信息失败')
}
})
.catch(err => {
alert('获取信息失败')
})
}, [])
return (
<div>
<h2>商品列表</h2>
<div>
<ul>
{productsData.map((item, index) => (
<li>item.name</li>
))}
</ul>
</div>
</div>
)
}
export default List
7.实现全局状态管理
userReducer,useContext,useEffect 三者配合
建立Context文件
/src/store/context/index.js
import { createContext } from 'react'
const Context = createContext(null)
export default Context
建立reducers文件
/src/store/reducers/index.js
const reducer = (state, action) => {
switch (action.type) {
case 'SET_ADMIN':
return { ...state, admin: action.admin }
case 'SET_MENU_STATUS':
return { ...state, menuStatus: action.menuStatus }
case 'SET_COLLAPSED':
return { ...state, collapsed: action.collapsed }
default:
return state
}
}
export default reducer
建立全局Provider组件
/src/store/provider/GlobalProvider.js
import React, { useReducer, useEffect } from "react"
import reducer from '../reducers'
import Context from '../context'
export const GlobalProvider = props => {
/**
* 如果没有state,新建state
*/
let stateStorage = JSON.parse(localStorage.getItem("state"))
if (stateStorage === null) {
localStorage.setItem("state", JSON.stringify({}))
} else {
localStorage.setItem("state", JSON.stringify(stateStorage))
}
stateStorage = JSON.parse(localStorage.getItem("state"))
/**
* useReducer
* admin需要持久化
*/
const initGlobal = {
admin: stateStorage.admin === undefined ? {} : stateStorage.admin,
menuStatus: stateStorage.menuStatus === undefined ? ['1'] : stateStorage.menuStatus,
collapsed: false
}
const [state, dispatch] = useReducer(reducer, initGlobal)
/**
* admin,menuStatus
* useReducer持久化(menuStatus登录后重置)
* 存入localStorage
*/
useEffect(() => {
if (JSON.stringify(state.admin) !== '{}') {
initGlobal.admin = state.admin
}
let stateStorage = JSON.parse(localStorage.getItem("state"))
stateStorage.admin = initGlobal.admin
initGlobal.menuStatus = state.menuStatus
stateStorage.menuStatus = initGlobal.menuStatus
localStorage.setItem("state", JSON.stringify(stateStorage))
}, [state])
return (
// 把dispatch方法也挂载在value中,供直接调用更新数据使用
<Context.Provider value={{ state, dispatch }}>
{props.children}
</Context.Provider >
)
}
export default GlobalProvider
修改index.js
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import { mainRoutes } from './routes'
import reportWebVitals from './reportWebVitals'
import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom'
import GlobalProvider from './store/provider/GlobalProvider'
ReactDOM.render(
<GlobalProvider>
<Router>
<Switch>
<Route path="/admin" render={routeProps => <App {...routeProps} />}></Route>
{mainRoutes.map(route => {
return <Route key={route.path} {...route}></Route>
})}
<Redirect to="/404" />
</Switch>
</Router>
</GlobalProvider>
, document.getElementById('root')
)
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals()
修改login.js
import React, { useRedudcer, useContext } from 'react'
import Context from '../../store/context'
import { loginApi } from '../../services/auth'
import { setToken } from '../../utils/auth'
import { Row, Col, Form, Input, Button, Checkbox } from 'antd'
import { UserOutlined, LockOutlined } from '@ant-design/icons'
function Login (props) {
let { state, dispatch } = useContext(Context)
const onFinish = (values) => {
loginApi({
username: values.username,
password: values.password
})
.then(res => {
if (res.code == 2000) {
setToken(res.data.token)
dispatch({
type: 'SET_ADMIN',
admin: res.data.admin
}, [state])
dispatch({
type: 'SET_MENU_STATUS',
menuStatus: ['1']
}, [state])
props.history.push("/admin/dashboard")
} else {
alert('账号密码错误')
}
})
.catch(err => {
alert('账号密码错误')
})
props.history.push("/admin/dashboard")
}
return (
<div>
<div style={{ height: '100px' }}></div>
<Row>
<Col span={8}></Col>
<Col span={8} style={{ fontSize: '20px', fontWeight: 'bolder' }}>管理员登录</Col>
<Col span={8}></Col>
</Row>
<Row style={{ marginTop: '20px' }}>
<Col span={8}></Col>
<Col span={8}>
<Form
name="normal_login"
className="login-form"
initialValues={{ remember: true }}
onFinish={onFinish}
>
<Form.Item
name="username"
rules={[{ required: true, message: 'Please input your Username!' }]}
>
<Input prefix={<UserOutlined className="site-form-item-icon" />} placeholder="Username" style={{ height: '42px' }} />
</Form.Item>
<Form.Item
name="password"
rules={[{ required: true, message: 'Please input your Password!' }]}
>
<Input
prefix={<LockOutlined className="site-form-item-icon" />}
type="password"
placeholder="Password"
style={{ height: '42px' }}
/>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" style={{ width: '100%', height: '42px' }}>
Log in
</Button>
</Form.Item>
</Form>
</Col>
<Col span={8}></Col>
</Row>
</div>
)
}
export default Login
修改/src/components/admin/frame/index.js
import React, { useState, useEffect, useContext } from "react"
import { withRouter } from 'react-router-dom'
import Context from '../../../store/context'
import { clearToken } from '../../../utils/auth'
import { Layout, Menu, Avatar, Dropdown } from 'antd'
import {
MenuUnfoldOutlined,
MenuFoldOutlined,
UserOutlined,
DownOutlined,
} from '@ant-design/icons'
import './Index.css'
import '../../../assest/fonts/iconfont.css'
function Index (props) {
let { state, dispatch } = useContext(Context)
const { Header, Sider, Content, Footer } = Layout
const [collapsed, setCollapsed] = useState(false)
const [contentHeight, setContentHeight] = useState(600)
const popMenu = (
<Menu onClick={(p) => {
if (p.key === 'logOut') {
clearToken()
dispatch({
type: 'SET_ADMIN',
admin: {}
}, [state.admin])
let stateStorage = JSON.parse(localStorage.getItem("state"))
stateStorage.admin = {}
localStorage.setItem("state", JSON.stringify(stateStorage))
props.history.push("./login")
}
}}>
<Menu.Item key="logOut">安全退出</Menu.Item>
</Menu>
)
useEffect(() => {
setContentHeight(window.innerHeight - 189)
setCollapsed(state.collapsed)
})
return (
<Layout>
<Sider trigger={null} collapsible collapsed={collapsed}>
<div className="logo" ></div>
<Menu theme="dark" mode="inline"
defaultSelectedKeys={state.menuStatus}
onClick={(p) => {
if (p.key === '1') {
dispatch({
type: 'SET_MENU_STATUS',
menuStatus: ['1']
})
props.history.push("/admin/dashboard")
}
if (p.key === '2') {
dispatch({
type: 'SET_MENU_STATUS',
menuStatus: ['2']
})
props.history.push("/admin/product/save")
}
if (p.key === '3') {
dispatch({
type: 'SET_MENU_STATUS',
menuStatus: ['3']
})
props.history.push("/admin/product/list")
}
}}>
<Menu.Item key="1" icon={<i className="iconfont iconshouye" style={{ fontSize: '16px' }} />}>
Dashboard
</Menu.Item>
<Menu.Item key="2" icon={<i className="iconfont iconxinzengshangpin" style={{ fontSize: '16px' }} />}>
产品添加
</Menu.Item>
<Menu.Item key="3" icon={<i className="iconfont iconshangpin" style={{ fontSize: '18px' }} />}>
产品列表
</Menu.Item>
</Menu>
</Sider>
<Layout className="site-layout">
<Header className="site-layout-background" style={{ padding: 0 }}>
{React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
className: 'trigger',
onClick: () => {
dispatch({
type: 'SET_COLLAPSED',
collapsed: !collapsed
}, [state])
},
})}
<div className="dropdown">
<Dropdown overlay={popMenu}>
<div>
<Avatar size="small" style={{ backgroundColor: '#1890ff', marginRight: '10px' }} icon={<UserOutlined />} />
<span>超级管理员</span>
<DownOutlined />
</div>
</Dropdown>
</div>
</Header>
<Content
className="site-layout-background"
style={{
margin: '24px 16px',
padding: 24,
minHeight: contentHeight,
}}
>
{props.children}
</Content>
<Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer>
</Layout>
</Layout>
)
}
export default withRouter(Index)
修改/src/pages/admin/dashboard/index.js
import React, { useContext } from 'react'
import Context from '../../../store/context'
import { Button } from 'antd'
function Index () {
let { state, dispatch } = useContext(Context)
return (
<div>
<h2>Dashboard</h2>
<h2>登录账号{JSON.stringify(state.admin.username)}</h2>
</div>
)
}
export default Index
修改/src/pages/admin/dashboard/index.css
.trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color 0.3s;
}
.trigger:hover {
color: #1890ff;
}
.logo {
height: 32px;
background: rgba(255, 255, 255, 0.3);
margin: 16px;
}
.site-layout .site-layout-background {
background: #fff;
}
.dropdown {
float: right;
margin-right: 20px;
}
修改/src/pages/admin/products/Save.js
import React from 'react'
import { withRouter } from 'react-router-dom'
import { Form, Input, InputNumber, Button } from 'antd'
import { saveProductApi } from '../../../services/admin'
function Save (props) {
const layout = {
labelCol: { span: 2 },
wrapperCol: { span: 22 },
}
const tailLayout = {
wrapperCol: { offset: 2, span: 22 },
}
const onFinish = (values) => {
saveProductApi(values)
.then(res => {
if (res.code === 2000) {
props.history.push("/admin/product/list")
alert('操作成功')
} else {
alert('操作失败')
}
})
.catch(err => {
console.log(err)
})
}
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo)
}
return (
<div>
<h2>添加产品</h2>
<Form
{...layout}
style={{ marginTop: '30px' }}
id="addForm"
name="basic"
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
<Form.Item
label="产品名称"
name="name"
rules={[{ required: true, message: 'Please input your products name!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="产品价格"
name="price"
rules={[{ required: true, message: 'Please input your products price!' }]}
>
<InputNumber
formatter={value => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
parser={value => value.replace(/\$\s?|(,*)/g, '')}
style={{ width: '100%' }}
/>
</Form.Item>
<Form.Item {...tailLayout} style={{ textAlign: 'right' }}>
<Button type="primary" htmlType="submit">
确认提交
</Button>
</Form.Item>
</Form>
</div>
)
}
export default withRouter(Save)
修改/src/pages/admin/products/List.js
import React, { useState, useEffect } from 'react'
import { getProductListApi, removeProductApi, updateProductApi } from '../../../services/admin'
import { Form, Input, InputNumber, Button, Table, Space, Modal, Divider } from 'antd'
function List (props) {
const [product, setProduct] = useState({})
const [productData, setProductData] = useState([])
const [isModalVisible, setIsModalVisible] = useState(false)
const [loading, setLoading] = useState(false)
const getProductList = () => {
getProductListApi()
.then(res => {
if (res.code === 2000) {
setProductData(res.data)
} else {
alert('操作失败')
}
})
.catch(err => {
console.log(err)
})
}
const showModal = () => {
setIsModalVisible(true)
}
const handleCancel = () => {
setIsModalVisible(false)
}
const layout = {
labelCol: { span: 4 },
wrapperCol: { span: 20 },
}
const tailLayout = {
wrapperCol: { offset: 4, span: 20 },
}
const onFinish = (values) => {
updateProductApi(values)
.then(res => {
if (res.code === 2000) {
getProductList()
handleCancel()
setProduct({})
alert('操作成功')
} else {
alert('操作失败')
}
})
.catch(err => {
console.log(err)
})
}
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo)
}
useEffect(() => {
getProductList()
}, [])
const columns = [
{
title: '产品名称',
dataIndex: 'name',
key: 'name',
width: 500
},
{
title: '产品价格',
dataIndex: 'price',
key: 'price',
width: 100
},
{
title: '操作',
key: 'action',
width: 100,
render: (text, record) => (
<Space size="middle">
<Button type="link" onClick={() => {
setProduct(record)
showModal()
}}>修改</Button>
<Button type="link" onClick={() => {
removeProductApi(record.id)
.then(res => {
if (res.code === 2000) {
getProductList()
} else {
alert('操作失败')
}
})
.catch(err => {
console.log(err)
})
}}>删除</Button>
</Space>
),
},
]
return (
<div>
<Modal title="Basic Modal"
title="产品修改"
visible={isModalVisible}
okText="确认修改"
okButtonProps={{ htmlType: 'submit', form: 'editForm' }}
cancelText="取消修改"
onCancel={handleCancel}
>
<Form
{...layout}
id="editForm"
name="basic"
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
<Form.Item
label="id"
name="id"
initialValue={product.id}
hidden={true}
rules={[{ required: true, message: 'Please input your product name!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="产品名称"
name="name"
initialValue={product.name}
rules={[{ required: true, message: 'Please input your product name!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="产品价格"
name="price"
initialValue={product.price}
rules={[{ required: true, message: 'Please input your product price!' }]}
>
<InputNumber
formatter={value => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
parser={value => value.replace(/\$\s?|(,*)/g, '')}
style={{ width: '100%' }}
/>
</Form.Item>
</Form>
</Modal>
<h2>商品列表</h2>
<Table rowKey={(record) => record.id} dataSource={productData} columns={columns} />
</div>
)
}
export default List
8.后台接口
/src/services/auth.js
import { post } from '../utils/request'
/**
* 用户登录
* @param {userName,password} user
*/
export function loginApi (user) {
return post('/api/admin/login', user)
}
/src/services/admin.js
import { get, post, del, getBaseUrl } from '../utils/request'
/**
* 获取服务地址
*/
export function getServerUrl () {
return getBaseUrl()
}
/**
* 获取管理员信息
* @param {*} id
*/
export function getAdminByIdApi (id) {
return get('/api/admin/get/' + id)
}
/**
* 添加产品
* @param {*} data
*/
export function saveProductApi (data) {
return post('/api/product/save/', data)
}
/**
* 删除产品信息
* @param URLSearchParams id
*/
export function removeProductApi (id) {
const params = new URLSearchParams()
params.append('id', id)
return post('/api/product/remove/', params)
}
/**
* 修改产品
* @param {*} data
*/
export function updateProductApi (data) {
return post('/api/product/update/', data)
}
/**
* 获取产品信息
*/
export function getProductListApi () {
return get('/api/product/list')
}
9.整合iconfont阿里图标库
1.建立/src/assest/fonts文件夹
2.将iconfont生成的项目图标库下载
3.将下载的图标库解压后内容复制到/src/assest/fonts文件夹
4.项目中引用
import '../../../assest/fonts/iconfont.css'
5.使用iconfont图标
<i className="iconfont iconshangpin" style={{ fontSize: '18px' }} />
10.打包部署
# 项目打包
yarn build
liunx部署
nginx安装配置
# nginx下载
http://nginx.org/download/nginx-1.19.6.tar.gz
# nginx解压
tar zxvf nginx-1.19.6.tar.gz
nginx.conf配置
...
#部署react-demo
server {
# 监听端口为8008
listen 8008;
# 定义网站域名,可以写多个,用空格分隔。
server_name localhost;
#在server{}里有很多location配置段
location / {
#定义网站根目录,目录可以是相对路径也可以是绝对路径。
root /var/www/react-demo;
#定义站点的默认页。
index index.html index.htm;
}
}
...
将打包后的文件(项目目录下build文件夹里的所有文件)拷贝到/var/www/react-demo/里
启动nignx
# 进入nginx目录
cd /usr/local/nginx/sbin
# 启动nginx
nginx
# 重新载入配置文件
nginx -s reload
# 重启 Nginx
nginx -s reopen
# 停止 Nginx
nginx -s stop
访问 http://localhost:8008