React前端实例教程

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

11.完结

源码下载

https://download.csdn.net/download/chinaxsw/19746373

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

问渠科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值