React project - ordering background management system - react framework to realize the background management system (including permission processing) - novices must see when they enter the pit!

1, Foreword

I also wrote a blog about a vue project before. To be honest, I did write it in general, but I did my best to write it. Today, we use react to write a background management system. It's still a novice entry-level version. The boss can row away.

First of all, we should clarify a concept, what is the background management system?

To sum up, the background management system is relative to the foreground system (that is, our common applications). For example, meituan APP has a corresponding background management system, which is responsible for business management, etc.

vue and react, as the two most popular front-end frameworks, are undoubtedly one of the contents that front-end developers must learn.

Warm tips, before reading this blog, you must master the relevant knowledge of react, otherwise you will be confused and react can go My previous blog learning!

2, Project introduction

The order background management system is based on react. It is a management system to facilitate the management of merchant takeout business. The main modules include user management, merchant management, order management, revenue and expenditure management, platform management, security management, login and other modules.

Due to the limited time, this project only realizes the user management, merchant management, order management and login modules (the functions are almost the same).

List of project static pages:

  • Main page

  • Landing page

3, Related technology

Create the official scaffold through create react app, create the layout component layouts using class components, quickly realize the static structure of the side navigation bar through the third-party component library antd, use props to realize the communication between parent and child components, define the internal initial value of the component, and realize the two-way binding of react form elements through onChange() event function, Use the life cycle hook function to obtain the initial state of the component, and pass parameters to the context state tree,
React router realizes component jump, secondary routing, three pole routing, etc. function components can operate state data through hooks, and redux can carry out state management, similar to vueX in vue and third-party library ecarts to realize data visual display, etc.

The above is basically the technology used in this project.

4, Functions implemented by the project

4.1 function analysis

In the user management, merchant management and order management module, the management and analysis of user, merchant and order information are mainly realized through the addition, deletion, modification and query of data.
Three large modules, a total of 5 sets of data addition, deletion, modification and query, which are realized by different technologies (full of dry goods, don't you collect them yet)

	 modular         Technology used

User management module:
    1,User list	(mock data)
    2,Communication information	(encapsulation axios+antd assembly)
Merchant management module:
	3,Business listings 	(async+await)
	4,Login account	(hooks)
Order management module:
	5,Order list	(reduX)

Note: since there are no back-end personnel in this project, all interfaces are implemented through mock data.

4.2 project structure


Project structure analysis:
1) App.js is a level-1 routing configuration, which is used to jump to the login page and layout page;

class App extends React.Component {
  constructor(props){
    super(props);  
  }

  render = () => (
    <div className="App">
      <BrowserRouter>
          <Switch>
              <Redirect exact={true} from="/" to="/login"></Redirect>
              <Route path="/layouts" component={Layouts}></Route>
              <Route path="/login" component={Login}></Route>
          </Switch> 
      </BrowserRouter>
    </div>
  )
}

2) . in the layout component layouts.js, it is a secondary routing configuration and dynamically displays the content area;

import './index.css'
import {withRouter} from 'react-router-dom'
import Header from '../components/header/header'
import Aside from '../components/aside/aside'
import Userlist from '../pages/Userlist/userlist'
import Commuinfo from '../pages/commuinfo/commuinfo'
import Userstati from '../pages/userstati/userstati'
import Merchanlist from '../pages/merchanlist/merchanlist'
import Loginaccou from '../pages/loginaccou/loginaccou'
import Orderlist from '../pages/orderlist/orderlist'
import Merchans from '../pages/merchans/merchans'
import Orderstati from '../pages/orderstati/orderstati'
import Notfound from '../pages/notfound/notfound'
import {Route,Switch,Redirect} from 'react-router-dom'

function Layouts(props){
    return (
        <div className="admin_box">
            <div className="admin_top_box">
                <Header></Header>
            </div>
            <div className="admin_bottom_box">
                    <div className="admin_left">
                        <Aside></Aside>
                    </div>
                    <div className="admin_right">
                        {/*  Switch Exclusive matching */}
                        <Switch>
                            {/* exact Strict mode, Redirect*/}
                            <Redirect exact={true} from="/layouts" to={JSON.parse(localStorage.getItem('paths'))[0].path}></Redirect>
                            <Route path="/layouts/userlist" component={Userlist}></Route>
                            <Route path="/layouts/commuinfo" component={Commuinfo}></Route>
                            <Route path="/layouts/userstati" component={Userstati}></Route>
                            <Route path="/layouts/merchanlist" component={Merchanlist}></Route>
                            <Route path="/layouts/loginaccou" component={Loginaccou}></Route>     
                            <Route path="/layouts/orderlist" component={Orderlist}></Route>  
                            <Route path="/layouts/merchas" component={Merchans}></Route>         
                            <Route path="/layouts/orderstati" component={Orderstati}></Route>         

                            {/* It must match */}
                            <Route component={Notfound}></Route>
                        </Switch>
                    </div>
            </div>
        </div>
    )
}

//High level component, get routing context
export default withRouter(Layouts)

4.3 axios packaging and mock data

4.3.1 axios packaging

Before we start to add, delete, change and query, we must first encapsulate axios, otherwise the following code may be incomprehensible. The encapsulation of axios has been in my previous blog, You can click here to learn.

In this project, due to the similar functions (addition, deletion, modification and query), the encapsulated axios is actually similar. For details, please refer to the following example code:

import {get,post,remove,update} from './index'

//query
export function getUserList(params){
    //If there is no parameter, query all data. If there is a parameter, search according to the parameter
    return get('/userlist',params)
}

//Delete (by id)
export function removeUserList(params) {
    //params id of the data to be deleted
    return remove('/userlist',params)
}

//add to
export function addUserList(params) {
    //params added data
    return post("/userlist",params);
}

//modify
export function updateUserList(id,params) {
    //id which data to modify, params modified data
    return update('/userlist',id,params)
}

4.3.2 mock data

All data in this project is stored in mock data. You can learn about the general structure first.

4.4. Implementation of adding, deleting, modifying and querying

4.4.1 user list (mock data)

The user list page is used to manage user information, that is, add, delete, modify and query.

The page layout is as follows:


Next, I will analyze the addition, deletion, modification and query in turn.

  • check

Implementation idea:

1,When the page is loaded for the first time, initiate a request (no parameters are transmitted) to obtain all data (encapsulate the function to obtain all data);
2,Enter the user name in the search box, click search (two-way binding processing here), send a request (pass parameters), and render in response;

Implementation code:

1. Query data (no parameter transfer)

//Function to get all data
getAllData() {
    getUserList().then(data=>{
        data.forEach((item,idx)=>{
            item.key = idx
        })
        this.setState({
            data:data
        })
    })
}

//Call the function after rendering
componentDidMount(){
    this.getAllData()
}

2. Query data (transfer parameter)

//Query (query by criteria when clicking)
getByParams=()=>{
    //The parameters passed have been specified as objects
    let params = this.state.findParams
    getUserList(params)
    .then(data=>{
        data.forEach((item,idx)=>{
            item.key = idx
        })
        this.setState({
            data:data
        })
    })
}

3. Bidirectional binding processing

//Bidirectional binding (query)
changeSearch=(e)=>{
    this.setState({
        searchData:{
            ...this.state.searchData,
            [e.target.name]:e.target.value
        }
    })
}

Note: each data must have a key, otherwise an error will be reported!

  • increase

Implementation idea:

1,The user clicks the Add button to pop up the add box;
2,User input data (bidirectional binding is performed here);
3,Click Save to initiate a request and add the data to the database;
4,Retrieve data.

Add box:


Implementation code:

//Bidirectional binding processing
this.state = {
	modelData:{
       "id": '',
         "tel": '',
         "username": "",
         "age": '',
         "sex": "",
         "regist_time": "",
         "ligin_count": '15',
         "code": '700',
         "ip_adress": ""
   }
}

//Bidirectional binding (add the value of the input box in the pop-up box)
changeVal=(ev)=>{
    this.setState({
        modelData:{...this.state.modelData,[ev.target.name]:ev.target.value}
    })
}

//Show add box
showAdd=()=>{       
   this.setState({
        isshowAdd:'block'
    })
}


//Add data
addUser=()=>{
    addUserList(this.state.modelData)
    .then(data=>{
        this.setState({
            isshowAdd:"none",            
            modelData:{
                "id": '',
                "tel": '',
                "username": "",
                "age": '',
                "sex": "",
                "regist_time": "",
                "ligin_count": '15',
                "code": '700',
                "ip_adress": "127.0.0.1"
              }
        },()=>{
            this.getAllData();
        });    
    });  
}
  • change

Implementation idea:

1,Click the Modify button to pop up the modify box, and record,(Data of current row)Transmission;
2,The data of the current line is displayed in the modify box, and the user starts to modify the data (here, two-way binding processing);
3,Click Modify to initiate a request to modify the data;
4,Retrieve data.

The modify box pops up:

Implementation code:

//Show modification box
updateData=(idx)=>{
    //The modify box is displayed, and the currently modified index and two-way bound data are updated
    this.setState({
        isshowUpdate:'block',
        index:idx.id,
        modelData:idx
    })
}

//Click Modify to modify the data
upDate=()=>{
    //Modify specified data
    let index = this.state.index
    console.log('index:',index);
    updateUserList(index,this.state.modelData)
    .then(data=>{
        this.setState({
            isshowUpdate:'none',
            modelData:{
                "id": "",
                "tel": "",
                "username": "",
                "age": "",
                "sex": "",
                "regist_time": "",
                "ligin_count": 15,
                "code": 700,
                "ip_adress": "127.0.0.1"
              }
        },()=>{
            console.log('this.state.modelData',this.state.modelData);
            this.getAllData()
        })        
    })
}
  • Delete

Implementation idea:

1,Click the delete button to pop up a confirmation prompt box and pass it at the same time id;
2,Click OK to initiate a request and delete the data;
3,Retrieve data.

A confirmation box pops up:


Implementation code:

//The delete confirmation box is displayed
showConfirm =(idx)=>{
    let that = this
    const { confirm } = Modal; 
        confirm({
          icon: <ExclamationCircleOutlined />,
          content: <p>Are you sure to delete?</p>,
          onOk(){
            that.removeData(idx)
          },
          onCancel(){
            that.destroyAll()
          } 
    })
  }

//delete
removeData=(idx)=>{
    removeUserList(this.state.data[idx].id)
    .then(data=>{
        this.getAllData()
    })    
}

4.4.2 communication information (encapsulating axios+antd components)

Communication information is to manage the communication information in the user list, mainly through the addition, deletion, modification and query of communication information.

The implementation ideas and steps are similar to the user list, so I won't write the implementation ideas. I'll mainly talk about different technical points.

  • check

1. No parameter transfer (get all data)

//Get all data
getAllData(){
    getUserComm()
    .then(data=>{
        data.forEach((item,idx)=>{
            item.key = idx
        })
        this.setState({
            data:data
        })
    })
}

//Get data after template rendering
componentDidMount(){
    this.getAllData()
}

2. Pass parameters (click the search button)

findData=()=>{
    //Parameter as object
    let username = this.state.searchData
    getUserComm(username)  
    .then(data=>{
        data.forEach((item,idx)=>{
            item.key = idx
        })
        this.setState({
            data:data
        })
    })
}

3. Bidirectional binding processing

//Bidirectional binding (query)
changeSearch=(e)=>{
     this.setState({
         searchData:{
             ...this.state.searchData,
             [e.target.name]:e.target.value
         }
     })
 }
  • increase

Show add box

Example code:

//Show add box
showAddComm=()=>{
    this.showModal()
}

//Add data
addCommu=()=>{
    addUserComm(this.state.changeData)
    .then(data=>{
        this.setState({
            changeData:{
                "id":"",
                "username":"",
                "tel":"",
                "states":"To be reviewed",
                "local":"",
                "adress":"",
                "mailcode":"",
                "qqcode":"",
                "email":""
            },
            visible: false,
        },()=>{
            this.getAllData()
        });
    })   
}
  • change

Show modification box

Example code:

//Show modification box
updateData=(idx)=>{
    this.setState({
        visibleUpd:true,
        index:idx.id,
        changeData:idx
    })
}

//Modify data
updDatas=()=>{
    let index = this.state.index
    updateUserComm(index,this.state.changeData)
    .then(data=>{
        this.setState({
            visibleUpd:false,
            changeData:{
                "id":"",
                "username":"",
                "tel":"",
                "states":"To be reviewed",
                "local":"",
                "adress":"",
                "mailcode":"",
                "qqcode":"",
                "email":""
              }
        },()=>{
            this.getAllData()
        })        
    })
}
  • Delete
//Delete (ID value)
removeData=(idx)=>{
    let id = this.state.data[idx].id
    removeUserComm(id)
    .then(()=>{
        this.getAllData()
    })
}

Tip: in react, this point in the event function is undefined. Pay attention when using this.

4.4.3 merchant list (async+await)

async and await are new to es7 and are used to completely solve the problem of callback hell. That is to change asynchronous code into synchronous code.

Of course, there is a prerequisite. The function modifying async must return the promise object (the project encapsulates axios to return the promise object, so don't worry). Those who are interested can Click my previous blog to view

In this module, I mainly use async and await to change asynchronous code into synchronous code, which not only reduces the amount of code, but also greatly improves the readability.

Merchant list is the management of merchant information, which is still the form of addition, deletion, modification and query. (refer to user list for implementation ideas)

  • check
//Get all data (async)
async getAllData(){
	//Data is equivalent to the formal parameter data of. then(data), that is, the data returned by the back end
    let data = await getmerchanlist()
    //When await is used, only after the asynchronous code decorated with await is executed, the following code can be executed
    data.forEach((item,idx)=>{
        item.key = idx
    })
    this.setState({
        data:data
    })
}

//Get data after template rendering
componentDidMount(){
    this.getAllData()
}
  • increase
//Show add box
showAddComm=()=>{
    this.showModal()
}

//Add data
async addCommu(){
    await addmerchanlist(this.state.changeData)
    this.setState({
        changeData:{
            "id":"",
            "merchanname":"",
            "type":"",
            "cost":"",
            "commrating":"",
            "local":"",
            "num":"",
            "tranroute":"",
            "tel":"",
            "username":""
        },
        visible: false,
    },()=>{
        this.getAllData()
    });   
}
  • change
//Show modification box
updateData=(idx)=>{
    this.setState({
        visibleUpd:true,
        index:idx.id,
        changeData:idx
    })
}

//Modify data
async updDatas(){
    let index = this.state.index
    await updatemerchanlist(index,this.state.changeData)
    this.setState({
        visibleUpd:false,
        changeData:{
            "id":"",
            "merchanname":"",
            "type":"",
            "cost":"",
            "commrating":"",
            "local":"",
            "num":"",
            "tranroute":"",
            "tel":"",
            "username":""
          }
    },()=>{
        this.getAllData()
    })     
}
  • Delete
// Show confirmation box
showConfirm =(idx)=>{
    let that = this
    const { confirm } = Modal; 
        confirm({
          icon: <ExclamationCircleOutlined />,
          content: <p>Are you sure to delete?</p>,
          onOk(){
            console.log('onok In function this',this);
            that.removeData(idx)
          },
          onCancel(){
            that.destroyAll()
          } 
    })
}


//Delete data
async removeData(idx){
    let id = this.state.data[idx].id
    await removemerchanlist(id)
    this.getAllData()
}

4.4.4. Login accounts (hooks)

Here comes the point!!!

Login account is used to manage the login account of the merchant, such as the security level of the account.

As we all know, in react, only class components (stateful components) can operate state, while function components (stateless components) cannot operate state.

hoos can solve the disadvantage of function components. In the login account module, I use hooks, that is, function components to add, delete, modify and query.

Before using a function component, we need to give the initial state of the function component, which is equivalent to the state in the class component.

import {useState,useEffect} from 'react'

//loginaccdata is the original data, the initial is an empty array, and setloginaccdata is the status function to update the initial value
let [loginaccdata,setloginaccdata] = useState([])

//Show modify box and add box
let [showK,changeK] = useState({
    visible:false,
    visibleUp:false,
    disabled:true,
    bounds: { left: 0, top: 0, bottom: 0, right: 0 },
})

//deconstruction
let {visible,visibleUp,disabled,bounds} = showK
// console.log('bounds',bounds);

//Record the currently modified data subscript. useState returns an array
let [index,changeIndex] = useState(0)

//Modify the box and add data bound in both directions
let [changeData,setChangedata] = useState({
        "id": "",
        "name": "",
        "accouname": "",
        "tel": "",
        "email": "",
        "registime": "",
        "root": "administrators",
        "roottype": "All permissions",
        "state": "",
        "username": "",
        "workcode": "",
        "department": "",
        "position": "",
        "logincount": "",
        "order": "",
        "safelevel": ""
})

//Search box bidirectional bound data
let [queryObj,changeQue] = useState({
    'id':""
})
  • check

1. No parameters (get all data when the page is loaded for the first time)

//Get all data
async function getAllData() {
    let data = await getLoginaccou()
    data.forEach((item,idx)=>{
        item.key = idx
    })
    setloginaccdata(data)
}

//With two parameters, the function will not be executed until the first rendering is completed, [] means that it does not depend on any parameters
useEffect(()=>{
    getAllData()
},[])
useEffect()The second parameter is[]When, equivalent to componentDidMount(),That is, the page is executed after the first rendering

2. Yes (click the search button to execute)

//Search data (condition search id)
async function searchData(){
    let data = await getLoginaccou(queryObj)
    data.forEach((item,idx)=>{
        item.key = idx
    })
    setloginaccdata(data)
}

3. Search criteria bidirectional binding

//Bidirectional binding function of query criteria
function changeValQ(e) {
    changeQue({
        ...queryObj,
        [e.target.name]:e.target.value
    })
}
  • increase
//Show add box
function showAdd() {
    showModal()
}

//Add data
async function addCommu(){
    await addLoginaccou(changeData)
    //Clear bidirectional bound data
    setChangedata({
        "id": "",
        "name": "",
        "accouname": "",
        "tel": "",
        "email": "",
        "registime": "",
        "root": "administrators",
        "roottype": "All permissions",
        "state": "",
        "username": "",
        "workcode": "",
        "department": "",
        "position": "",
        "logincount": "",
        "order": "",
        "safelevel": ""
})  
    handleCancel()
    getAllData()
}
  • change
//Show modification box
function updateData(index){
    showModalUp()
    setChangedata(loginaccdata[index])
    changeIndex(index)
}

//Modify data
async function update() {
    let id = loginaccdata[index].id
    await updateLoginaccou(id,changeData)
    //Clear bidirectional bound data
    setChangedata({
        "id": "",
        "name": "",
        "accouname": "",
        "tel": "",
        "email": "",
        "registime": "",
        "root": "administrators",
        "roottype": "All permissions",
        "state": "",
        "username": "",
        "workcode": "",
        "department": "",
        "position": "",
        "logincount": "",
        "order": "",
        "safelevel": ""
})
        handleCancelUp()
        getAllData()
}
  • Delete
//The delete confirmation box is displayed
function showConfirm(id){
    const { confirm } = Modal; 
        confirm({
          icon: <ExclamationCircleOutlined />,
          content: <p>Are you sure to delete?</p>,
          onOk(){
            removeData(id)
          },
          onCancel(){
            destroyAll()
          } 
    })
}

//Delete data
async function removeData(idx){
    //idx is the data id to be deleted
    await removeLoginaccou(idx)
    getAllData()
}

The implementation ideas of function components and class components are the same, but the function components need to define the initial state and modify the initial state, which is equivalent to the setState() function in class components. Only in this way can the component display the latest value.

4.4.5 order list (reduX)

Still the point!!!

hooks and reduX should be the difficulty and focus of this project. reduX is equivalent to vueX in vue and is also used for state management. But personally, reduX is more complicated than vueX. If you don't know reduX, you can Click here reduX.

In the previous analysis of the project structure, there is a folder for processing reduX. Now we can analyze this folder. Only after understanding this can you use reduX.

First, the file structure is as follows:

index.js to configure reduX And instantiate one store Object and export;
alldata.js It's split reducer;(This is the data used in the order list module)
actions.js extract actions(Operations on data, including asynchronous code)

In index.js,

import { createStore ,combineReducers,applyMiddleware} from "redux";
import thunk from 'redux-thunk'
import alldata from './alldata'
import isload from './load'


let storeReducer = combineReducers({
    alldata,isload
})

let store = createStore(storeReducer,applyMiddleware(thunk))

export default store;

In alldata.js,

var data = []

export default (db=data,action)=>{
    const {type,payload} = action

    switch(type){
        case "CHANGEDATA":
            payload.forEach((item,idx)=>item.key = idx)
            return payload;
        
        case "DELDATA":
            payload.forEach((item,idx)=>item.key = idx)
            return payload;

        case "ADDORDER":
            payload.forEach((item,idx)=>item.key = idx)
            return payload;

        case "UPDATT":
            payload.forEach((item,idx)=>item.key = idx)
            console.log('It came after modification payload',payload);
            return payload;

    }

    return db;
}

In actions.js,

import { getOrderlist ,removeOrderlist,addOrderlist,updateOrderlist} from "../api/orderlist";

//query
export const All = (seardata)=>(async (dispatch)=>{
    //The return value of the outer function is a function
    let data = await getOrderlist(seardata)

    dispatch({
        type:"CHANGEDATA",
        payload:data
    })
})

//delete
export const DEl = (id) =>(async (dispatch)=>{

    console.log('DEL Yes');
    await removeOrderlist(id)

    let data = await getOrderlist()
    // console.log('deleted data ';
    dispatch({
        type:"DELDATA",
        payload:data
    })
})

//add to
export const ADD = (datainfo)=>(async (dispatch)=>{

    console.log('From saving datainfo',datainfo);
    await addOrderlist(datainfo)
    let data = await getOrderlist()

    dispatch({
        type:"ADDORDER",
        payload:data
    })

})

//modify
export const UPD = (setparams) =>(async (dispatch)=>{

    // console.log('modify parameters sent ', setparams);
    await updateOrderlist(setparams.id,setparams.data)
    let data = await getOrderlist()

    dispatch({
        type:"UPDATT",
        payload:data
    })
})

//Display and hide of load
export const SHOWLOAD = (payload)=>({
    type:"SHOWLOAD",
    payload
})

People who may not have mastered reduX are a little confused. It doesn't matter. In fact, their thinking is similar to vueX. The difference is
1) vue and vue are responsive. If the data in state changes, the components will change in response.
2) . however, reduX needs to subscribe to data changes and manually modify the data before it can respond to the page.

Different from the previous module, the order list module not only uses reduX, but also uses three-level routing.

Three level routing idea:

1,There are search, add buttons and a search box at the top of the order list page;
2,The content below is three-level routing, that is, the part of dynamic switching;
3,Click Add to jump to the add order page, click search to jump to the order list page, and the order list page is displayed by default;

Implementation code:

export default function Orderlist(props){

    return (
        <div className={style.orderlist}>
            <div className={style.ordertop}>
                <h3>Order list</h3>
                <Input value={seardata.id} onChange={changeval} size="large" name="id" allowClear="true" style={{width:"300px",marginLeft:"20px"}} placeholder="Please enter ID" />
                <button className="sear" onClick={()=>searchData()}>search</button>
                <button onClick={()=>toAdd()}>increase</button>   
            </div>
            <div className={style.ordercont}>
                <Redirect exact={true} from="/layouts/orderlist" to="/layouts/orderlist/orders"></Redirect>
                <Route path="/layouts/orderlist/orders" component={Orders}></Route>
                <Route path="/layouts/orderlist/orderadd" component={Orderadd}></Route>
            </div>
        </div>
    )
}

Page display:

  • check

First, define the initial state of the function component and the change of subscription data.

import store from '../../store'
import {All,DEl,UPD} from '../../store/actions'

//Original data
let [listdata,setlistdata] = useState(store.getState().data)

//After changing the state, you also need to subscribe to render the page responsively
store.subscribe(()=>{
    setlistdata(store.getState().alldata)
})

//First load execution
useEffect(()=>{
    //Execute after the first rendering, [] is independent of any parameters
    getAllData()
},[])

1. No parameter transmission

//Get all data
function getAllData(){
    //Send action to get all data
    store.dispatch(All())    
}

2. Transmission parameter

//Bidirectional binding of query criteria
function changeval(e){
    setsear({
        ...seardata,
        [e.target.name]:e.target.value
    })
}

//query
function searchData(){
    store.dispatch(All(seardata))
}
  • increase
//Confirm adding function
function addorder(){
    store.dispatch(ADD(orderadd))
    setorderadd({
        "id": "",
        "ordercode": "",
        "ordertime": "",
        "ordermoney": "",
        "goodsname": "",
        "goodscount": "",
        "ordertype": "",
        "username": "",
        "state": ""
    })    
}
  • change
//Show modification box
function updateData(record){
    showModal()
    setorder(record)
    setindex(record.id)
}

//Modify data
function updataorder(){
    store.dispatch(UPD({
        id:index,
        data:orderset
    }))
    handleCancel()
    setorder(
        {
            "id": "",
            "ordercode": "",
            "ordertime": "",
            "ordermoney": "",
            "goodsname": "",
            "goodscount": "",
            "ordertype": "",
            "username": "",
            "state": ""
        })
}
  • Delete
//The delete confirmation box is displayed
function showConfirm(record){
    const { confirm } = Modal; 
        confirm({
          icon: <ExclamationCircleOutlined />,
          content: <p>Are you sure to delete?</p>,
          onOk(){
            removeData(record.id)
          },
          onCancel(){
            destroyAll()
          } 
    })
}

//Delete data
function removeData(id){
    store.dispatch(DEl(id))
}

The implementation idea of reduX is:

1,Distributed in the component, parameters can be transferred, and the parameter is an object;
2,actions Initiate a request in the and send the required value to reducer;
3,reducer Inside modification state Defined data;
4,Where the data is used in the component, subscribe and render in response.

If you still don't understand, see you in the comment area below!

Well, the above is the business function implementation of the five modules of this project, and the technologies used almost cover react. Of course, many places are not perfect enough, which can be pointed out in the comment area.

4.5. Realization of login function

Generally, there is no registration in the background management system. The accounts are assigned by the administrator, and the permission levels are different (we'll talk about this later).

Therefore, you only need to implement the login function. (you can't enter the system without logging in) the static effect has been shown in the previous section. You can refer to it.

Because the project is implemented through mock data, it can only realize login verification in the form of simple account + password.

Implementation idea of login module:

1,mock Data add a data to record the user name and password;
2,encapsulation axios,Click login at the current end to initiate a request and verify whether the user name and password are correct;
3,If the account passwords are correct, return token Information;
4,Front end received token,Prompt login success, save locally token And user name, and enter the management system at the same time;
5,If the login fails, a failure message will be prompted.

token is used to verify whether the user is legal. Only legal users can send requests to obtain data.

mock data display:

"loginadmin": [
    {
      "id": "0001",
      "username": "xx",
      "userpass": "xxx",
    }
]

Encapsulating axios:

//Login (get)
export function getlogin(url,params){

    let queryStr = queryStrFn(params)
    return new Promise(function(resolve,reject){
        
        service.get(url+"?"+queryStr)
        .then(res=>{
            if(res.data.length!==0){
                //Data found
                resolve({
                    code:1,
                    token:ranToken(),
                    paths:res.data[0].paths,
                    curd:res.data[0].zsgc
                });
            }else{
                //No data found
                resolve(res.data)
            }
        });
    });
}

//A random analog token
function ranToken(){
    let str = "ZXCVBNMASDFGHJKSRDTJFGLQWERTYUIOP";
    let str_len = str.length;
    let token_str = "";

    //A random 100 bit string
    for(let i=0;i<100;i++){
        let rannum = parseInt(Math.random()*str_len)
        token_str+=str.charAt(rannum)
    }

    return token_str
}

Click login at the front end:

//Sign in
async tologin(){
    let data = await loginAdmin(this.state.loginobj)
    if(data.length!==0){
        //Find the data and store the token
        message.info('Login succeeded');
        localStorage.setItem('token',data.token)
        localStorage.setItem('paths',JSON.stringify(data.paths))
        localStorage.setItem('username',this.state.loginobj.username)
        this.props.history.push('/layouts')
    }else{
        //No data found
        message.info('Login failed');
    } 
}

On the login page, you can log in through your mobile phone number or email. The switching between the two components is also realized through secondary routing.

Login page secondary routing implementation code:

render(){
   return (
       <div className={style.login}>
           <div className={style.logincont}>
               <div className={style.loginhead}>
                   <div onClick={this.tellogin}>
                       Mobile number login
                   </div>
                   <div onClick={this.emaillogin}>
                       Mailbox login
                   </div>
               </div>
               <div className={style.logincontt}>
                   <Switch>
                       <Redirect exact={true} from="/login" to="/login/tellogin"></Redirect>
                       <Route path="/login/tellogin" component={Tellogin}></Route>
                       <Route path="/login/emaillogin" component={Emaillogin}></Route>
                   </Switch>
               </div>     
           </div>
       </div>
   )
}

There is no route guard in react, so you need to write a function to verify whether you log in, which is in layouts.js;

//Verify whether to log in. If not, jump back to the login page
function isLogin(){
    let username = localStorage.getItem('username')
    console.log('username',);
    if(!username){
      //If you are not logged in, skip to the login page
      message.info('Please log in first!');
      props.history.push('/login')
    }
}


//High level component, get routing context
export default withRouter(Layouts)

Note here that if layouts is not a page that jumps through routing, there will be no routing context and high-level components need to be used.

4.6. Permission processing

As for what is permission, please refer to My blog , which introduces the concept of permission and basic operation ideas in detail.

The idea is not written here, but directly to the code:

mock data:

"loginadmin": [
    {
      "id": "0001",
      "username": "xxx",
      "userpass": "xxx",
      "paths": [
        {
          "path": "/layouts/userlist"
        },
        {
          "path": "/layouts/commuinfo"
        },
        {
          "path": "/layouts/userstati"
        }
      ]
    }
]

Log in to the front end and save the path:

//Sign in
async tologin(){
    let data = await loginAdmin(this.state.loginobj)
    // console.log('returned data '; 
    if(data.length!==0){
        //Find the data and store the token
        message.info('Login succeeded');
        localStorage.setItem('token',data.token)
        localStorage.setItem('paths',JSON.stringify(data.paths))
        localStorage.setItem('curd',data.curd)
        localStorage.setItem('username',this.state.loginobj.username)
        this.props.history.push('/layouts')
    }else{
        //No data found
        message.info('Login failed');
    } 
}

grants filter user path:

import Userlist from '../pages/Userlist/userlist'
import Commuinfo from '../pages/commuinfo/commuinfo'
import Userstati from '../pages/userstati/userstati'
import Merchanlist from '../pages/merchanlist/merchanlist'
import Loginaccou from '../pages/loginaccou/loginaccou'
import Orderlist from '../pages/orderlist/orderlist'
import Merchans from '../pages/merchans/merchans'
import Orderstati from '../pages/orderstati/orderstati'

let stores = [
    {   
        title:"User list",
        path: "/layouts/userlist",
        component:Userlist
    },
    {
        title:"Communication information",
        path: "/layouts/commuinfo",
        component:Commuinfo
    },
    {
        title:"User analysis",
        path: "/layouts/userstati",
        component:Userstati
    },
    {
        title:"Business listings ",
        path: "/layouts/merchanlist",
        component:Merchanlist
    },
    {
        title:"Login account",
        path: "/layouts/loginaccou",
        component:Loginaccou
    },
    {
        title:"Merchant analysis",
        path: "/layouts/merchas",
        component:Merchans
    },
    {
        title:"Order list",
        path: "/layouts/orderlist",
        component:Orderlist
    },
    {
        title:"Order analysis",
        path: "/layouts/orderstati",
        component:Orderstati
    }
]

//1. Get permission path of local storage


//2. Filter the permission path of the currently logged in user from all routing configurations
export default () =>{
    let paths = JSON.parse(localStorage.getItem('paths'))
    let curd = localStorage.getItem('curd')

    let userpath = stores.filter(items=>(
        paths.some((item)=>(
            item.path == items.path
        ))
    ))
    
    userpath[curd] = curd;

    return userpath; 
}

Dynamically display the navigation bar:

import React from 'react'
import { Menu } from 'antd';
import { MailOutlined, AppstoreOutlined, SettingOutlined,LayoutOutlined, ShareAltOutlined ,RestOutlined} from '@ant-design/icons';
import {NavLink} from 'react-router-dom'
import userpathsFn from '../../grants'

import './aside.css'

const { SubMenu } = Menu;


export default class Aside extends React.Component {
  
  constructor(props){
    super()
    this.state = {
      userpaths:[]
    }
  }
  
  //Get the new user permission routing configuration after filtering
  componentDidMount(){
      this.setState({
          userpaths:userpathsFn()
      })
  }
  
  isshowmeau(meaupath){
    console.log('userpathsFn()',userpathsFn());
      let res = userpathsFn().some((item)=>item.path==meaupath)
      return res

  }

  render() {

    return (
      <>    
        <Menu
          style={{ width: 161 }}
          defaultOpenKeys={['sub1']}
          // defaultSelectedKeys={['1']}
          mode="inline"
          theme = 'light'
        >
          <SubMenu key="sub1" icon={<MailOutlined />} title="user management ">

              {/* {
                 this.state.userpaths.map((item,idx)=>(

                  <Menu.Item key={idx}>
                      <NavLink to={item.path} activeClassName="tochange">
                          {item.title}
                      </NavLink>
                  </Menu.Item>
                 ))
                  
              } */}
                  <Menu.Item key="1" style={{display:this.isshowmeau('/layouts/userlist')?"block":"none"}}>
                      <NavLink to="/layouts/userlist" activeClassName="tochange">
                    User list
                      </NavLink>
                    </Menu.Item>
                  <Menu.Item key="2" style={{display:this.isshowmeau('/layouts/commuinfo')?"block":"none"}}>
                        <NavLink to="/layouts/commuinfo" activeClassName="tochange">
                    Communication information
                        </NavLink>
                    </Menu.Item>
                  <Menu.Item key="3" style={{display:this.isshowmeau('/layouts/userstati')?"block":"none"}}>
                    <NavLink to="/layouts/userstati" activeClassName="tochange">
                        statistical analysis
                      </NavLink>
                  </Menu.Item>
          </SubMenu>
          <SubMenu key="sub2" icon={<AppstoreOutlined />} title="Merchant management">
              <Menu.Item key="4" style={{display:this.isshowmeau('/layouts/merchanlist')?"block":"none"}}>
                  <NavLink to="/layouts/merchanlist" activeClassName="tochange">
                      Business listings 
                  </NavLink>
              </Menu.Item>
              <Menu.Item key="5" style={{display:this.isshowmeau('/layouts/loginaccou')?"block":"none"}}>
                  <NavLink to="/layouts/loginaccou" activeClassName="tochange">
                      Login account
                  </NavLink>
              </Menu.Item>
              <Menu.Item key="6" style={{display:"none"}}>Login record</Menu.Item>
              <Menu.Item key="7" style={{display:"none"}}>qualification information </Menu.Item>
              <Menu.Item key="8" style={{display:this.isshowmeau('/layouts/merchas')?"block":"none"}}>
                  <NavLink to="/layouts/merchas" activeClassName="tochange">
                      Merchant analysis
                  </NavLink>
              </Menu.Item>
          </SubMenu>  
          <SubMenu key="sub3" icon={<LayoutOutlined />} title="Order management">
              <Menu.Item key="9" style={{display:this.isshowmeau('/layouts/orderlist')?"block":"none"}}>
                  <NavLink to="/layouts/orderlist" activeClassName="tochange">
                      Order list
                  </NavLink>
              </Menu.Item>
              <Menu.Item key="10" style={{display:this.isshowmeau('/layouts/orderstati')?"block":"none"}}>
                  <NavLink to="/layouts/orderstati" activeClassName="tochange">
                      statistical analysis
                  </NavLink>
              </Menu.Item>
          </SubMenu>
          <SubMenu key="sub4" icon={<SettingOutlined />} title="Revenue and expenditure management">
            <Menu.Item key="11" style={{display:"none"}}>Revenue and expenditure list</Menu.Item>
            <Menu.Item key="12" style={{display:"none"}}>Revenue and expenditure analysis</Menu.Item>
          </SubMenu>
          <SubMenu key="sub5" icon={<RestOutlined />} title="Platform management">
            <Menu.Item key="13" style={{display:"none"}}>Management Center</Menu.Item>
            <Menu.Item key="14" style={{display:"none"}}>SMS Management</Menu.Item>
            <Menu.Item key="15" style={{display:"none"}}>Promotion information</Menu.Item>
            <Menu.Item key="16" style={{display:"none"}}>Basic settings</Menu.Item>
          </SubMenu>
          <SubMenu key="sub6" icon={<ShareAltOutlined />} title="security management ">
            <Menu.Item key="17" style={{display:"none"}}>essential information</Menu.Item>
            <Menu.Item key="18" style={{display:"none"}}>Password settings</Menu.Item>
          </SubMenu>
        </Menu>
      </>
    );
  }
}

4.7. Ecarts data visualization processing

echarts is a third-party database for data visualization based on js. It is also used in this project to make the statistical analysis of data clear at a glance. Especially in the background management system, it is used very frequently.

In this project, a simple visual analysis is mainly carried out for the data of user management module and merchant management module.

4.7.1. User management module (gender proportion analysis of registered users)

import * as echarts from 'echarts';
import React from 'react'
import {getUserList} from '../../api/userlist'


export default class Listtel extends React.Component{

    constructor(){
        super()
        this.state ={    
            boy:0,
            girl:0       
        }
    }

    getAllData(){
        let boy = 0;
        let girl = 0;

        getUserList()
        .then(data=>{
            //Asynchronous
            console.log("echarts Inside data:",data);
            data.forEach((item,idx)=>{
                if(item.sex==="male"){
                    boy ++;
                }else{
                    girl ++;
                }
            })
            this.setState({
                boy:boy,
                girl:girl
            },()=>{
                // Initialize the ecarts instance based on the prepared dom
                var myChart = echarts.init(document.getElementById('listmain'));

                console.log('Statistical boy22',this.state.boy);
                // Draw a chart
                myChart.setOption({
                    title: {
                        text: 'Age analysis of registered users',
                        left:"center",
                        top:8
                    },
                    xAxis: {
                        type: 'category',
                        splitNumber:10,
                        name:"age distribution",
                        data: ['0-10', '10-20', '20-30', '30-40', '40-50', '50-60', '60-70','70-80','80-90','90-100'],
                        axisTick: {
                            length: 10,
                            interval:0,
                            show:true,
                            lineStyle: {
                              type: 'dashed'
                              // ...
                            }
                          }
                    },
                    yAxis: {
                        type: 'value',
                        name:"Number of people",
                        axisTick: {
                            length: 10,
                            lineStyle: {
                              type: 'dashed'
                              // ...
                            }
                          }
                    },
                    series: [{
                        data: [120, 200, 150, 80, 70, 110, 130,45,87,90],
                        type: 'bar',
                        showBackground: true,
                        backgroundStyle: {
                            color: 'rgba(180, 180, 180, 0.2)'
                        }
                    }]
            
                });
            })
        })
        
    }

    componentDidMount(){
        //get data
        this.getAllData()
        
    }

    render(){
        return (
            <div id="listmain" style={{width:"600px",height:"500px"}}>
               
            </div>
        )
    }
}

4.7.2 user management module (age analysis of registered users)

import * as echarts from 'echarts';
import React from 'react'
import {getUserList} from '../../api/userlist'


export default class Listtel extends React.Component{

    constructor(){
        super()
        this.state ={    
            boy:0,
            girl:0       
        }
    }

    getAllData(){
        let boy = 0;
        let girl = 0;

        getUserList()
        .then(data=>{
            //Asynchronous
            console.log("echarts Inside data:",data);
            data.forEach((item,idx)=>{
                if(item.sex==="male"){
                    boy ++;
                }else{
                    girl ++;
                }
            })
            this.setState({
                boy:boy,
                girl:girl
            },()=>{
                // Initialize the ecarts instance based on the prepared dom
                var myChart = echarts.init(document.getElementById('main'));

                console.log('Statistical boy22',this.state.boy);
                // Draw a chart
                myChart.setOption({
                    title: {
                        text: 'Analysis chart of gender proportion of registered users',
                        left:"center",
                        top:8
                    },
                    tooltip: {
                        trigger: 'item'
                    },
                    legend: {
                        top: '8%',
                        left: 'center'
                    },
                    labelLine: {
                        show: true
                    },
                    series: [
                        {
                            name: 'Access source',
                            type: 'pie',
                            radius: ['40%', '70%'],
                            avoidLabelOverlap: false,
                            itemStyle: {
                                borderRadius: 10,
                                borderColor: '#fff',
                                borderWidth: 2
                            },
                            label: {
                                show: false,
                                position: 'center'
                            },
                            emphasis: {
                                label: {
                                    show: true,
                                    fontSize: '40',
                                    fontWeight: 'bold'
                                }
                            },
                            labelLine: {
                                show: false
                            },
                            data: [
                                {value: this.state.boy, name: 'schoolboy'},
                                {value: this.state.girl, name: 'girl student'},
                            ]
                        }
                    ]
                });
            })
        })
        
    }

    componentDidMount(){
        //get data
        this.getAllData()
        
    }
    
    render(){
        return (
            <div id="main" style={{width:"500px",height:"500px"}}>

            </div>
        )
    }
}

design sketch:

4.7.3 merchant management module (analysis of merchant's catering operation)

import * as echarts from 'echarts';
import React from 'react'
import { getmerchanlist } from '../../api/merchanlist'

var legendData = [];
var seriesData = [];

export default class Merchanstati extends React.Component {

    constructor() {
        super()
    }

    //Get all data
    async getAllData() {

        let data = await getmerchanlist()
        data.forEach((item) => {
            legendData.push(item.merchanname)
            seriesData.push({
                name: item.merchanname,
                value: item.cost
            })
        })

        // Initialize the ecarts instance based on the prepared dom
        var myChart = echarts.init(document.getElementById('merchanstati'));

        myChart.setOption({
            title: {
                text: 'Analysis of catering operation of merchants and the proportion of per capita consumption',
                left: 'left'
            },
            tooltip: {
                trigger: 'item',
                formatter: '{a} <br/>{b} : {c} ({d}%)',
            },
            legend: {
                type: 'scroll',
                orient: 'vertical',
                right: 120,
                top: 20,
                bottom: 20,
                data: legendData,

                // selected: legendData.selected
            },
            series: [
                {
                    name: 'full name',
                    type: 'pie',
                    radius: '55%',
                    center: ['40%', '50%'],
                    data: seriesData,
                    emphasis: {
                        itemStyle: {
                            shadowBlur: 10,
                            shadowOffsetX: 0,
                            shadowColor: 'rgba(0, 0, 0, 0.5)'
                        }
                    }
                }
            ]
        })
    }


    componentDidMount(){
        legendData = [];
        seriesData = [];
        this.getAllData()
    }

    render() {
        return (
            <div id="merchanstati" style={{ width: "100%", height: "100%"}}>

            </div>

        )
    }
}

4.7.4 merchant management module (merchant login analysis)

import * as echarts from 'echarts';
import React from 'react'
import { getLoginaccou } from '../../api/loginaccou'

var legendData = [];//Record department classification


export default class Merchanstati extends React.Component {

    constructor() {
        super()
    }

    //Array de duplication
    getOnlyArr(arr) {
        let newarr = []
        for (let i = 0; i < arr.length; i++) {
            if (newarr.indexOf(arr[i]) == -1) {
                newarr.push(arr[i])
            }
        }
        return newarr;
    }

    //Get all data
    async getAllData() {

        let data = await getLoginaccou()
        console.log('data',data);
        data.forEach((item) => {
            legendData.push(item.department)
            // seriesData.push({
            //     name: item.merchanname,
            //     value: item.cost
            // })
        })

        legendData= this.getOnlyArr(legendData)
        console.log('legendData',legendData);

        // Initialize the ecarts instance based on the prepared dom
        var myChart = echarts.init(document.getElementById('merchanlogin'));

        myChart.setOption({
            title: {
                text: 'Merchant Department login analysis'
            },
            tooltip: {
                trigger: 'axis'
            },
            legend: {
                data: ['Department login analysis']
            },
            grid: {
                left: '3%',
                right: '4%',
                bottom: '3%',
                containLabel: true
            },
            toolbox: {
                feature: {
                    saveAsImage: {}
                }
            },
            xAxis: {
                type: 'category',
                boundaryGap: false,
                data: legendData
            },
            yAxis: {
                type: 'value'
            },
            series: [
                {
                    name: 'Department login analysis',
                    type: 'line',
                    stack: 'total',
                    data: [120, 132, 101, 134, 90, 230, 210]
                }
            ]
        })

    }
    componentDidMount() {

        this.getAllData()
    }

    render() {
        return (
            <div id="merchanlogin" style={{ width: "100%", height: "100%" }}>

            </div>

        )
    }
}

The effect of some ecarts charts is not complete, but the idea is the same, but the use of ecarts is not skilled enough.

5, Project summary

1) The third-party component libraries used in this project are basically antd. To be honest, antd is different from vant. If it is used for the first time, it will encounter great resistance. Therefore, it also needs to be used more to increase proficiency;
2) I am not proficient in the use of react, especially in the performance optimization of hooks;
3) . the function implementation is too simple, especially with mock data. It was originally intended to use nodeJS to write the interface;

Source code download address: https://gitee.com/sandas/order-admin
You need to log in to run the project. You can send private letters!

Tags: React

Posted on Tue, 28 Sep 2021 06:01:29 -0400 by tbaink2000