《React后台管理系统实战 :四》产品分类管理页:添加产品分类、修改(更新)产品分类

一、静态页面

目录结构

F:\Test\react-demo\admin-client\src\pages\admin\category

    add-cate-form.jsx
    index.jsx
    index.less
    update-cate-form.jsx

1. pages/category/index.jsx

import React,{Component} from 'react'
import {
    Button,
    Card, //引入卡片
    Table, //引入表格
    Icon, } from 'antd';    
import LinkButton from '../../../components/link-button' //引入自定义的链接样式按钮组件

export default class Category extends Component{
    state={}

    render(){
        //卡片标题
        const title='产品分类管理'
        //卡片右侧添加按键
        const extra=(
            <Button type='primary'>
                <Icon type='plus'/>
                添加
            </Button>
        )
        
        // 表格数据源示例
         const dataSource = [
		        {
		        "parentId": "0",
		        "_id": "5c2ed631f352726338607046",
		        "name": "分类001",
		        "__v": 0
		      },
		      {
		        "parentId": "0",
		        "_id": "5c2ed647f352726338607047",
		        "name": "分类2",
		        "__v": 0
		      },
		      {
		        "parentId": "0",
		        "_id": "5c2ed64cf352726338607048",
		        "name": "1分类3",
		        "__v": 0
		      }
           ];

          //表格列名
          const columns = [
            {
              title: '分类名',
              dataIndex: 'name',
              key: 'name',
            },
            {
              title: '操作',
              width:'29%',
              render: () => ( //向操作列所有行输出修改及查看分类两个按钮
                  <span>
                      <LinkButton>修改分类</LinkButton>
                      <LinkButton>查看子分类</LinkButton>
                  </span>
              ),
            },
            
          ];

        return(
            <div className='category'>
             {/*卡片样式组件*/}
                <Card title={title} extra={extra} >
                	{/*表格组件、边框、key为数据源的_id、数据源、列名定义*/}
                    <Table 
                    bordered
                    rowKey='_id'
                    dataSource={dataSource} 
                    columns={columns} />
                </Card>
            </div>
        )
    }
}

在这里插入图片描述

二、动态:产品分类数据请求

1.编写产品分类相关接口src/api/index.js

【1】获取产品一级/二级分类列表接口
【2】添加产品分类接口
【3】修改产品分类接口

import ajax from './ajax'
import jsonp from 'jsonp'
import {message} from 'antd' //借用antd返回信息组件
// const BASE = 'http://localhost:5000'
const BASE = ''

//导出一个函数,第1种写法
//登录接口函数
// export function reqLogin(username,password){
//     return ajax('login',{username,password},'POST')
// }

//导出一个函数,第2种写法
// 登录接口函数
export const reqLogin=(username,password)=>ajax(BASE+'login',{username,password},'POST')


//【1】获取产品一级/二级分类列表接口
export const reqCategorys=(parentId)=>ajax(BASE+'/manage/category/list',{parentId})
//【2】添加产品分类接口
export const reqAddCategory=(parentId,categoryName)=>ajax(BASE+'/manage/category/add',{parentId,categoryName},'POST')
//【3】修改产品分类接口
export const reqUpdateCategory=({parentId,categoryName})=>ajax(BASE+'/manage/category/update',{parentId,categoryName},'POST')


// 天气接口
export const reqWeather=(city) => {    
    const url = `http://api.map.baidu.com/telematics/v3/weather?location=${city}&output=json&ak=3p49MVra6urFRGOT9s8UBWr2`
    //返回一个promise函数
    return new Promise((resolve,reject) => {
        //发送一个jsonp请求
        jsonp(url,{},(err,data) => {
            //输出请求的数据到控制台
            console.log('jsonp()', err, data)
            //如果请求成功
            if(!err && data.status==='success'){
                //从数据中解构取出图片、天气
                const {dayPictureUrl,weather}=data.results[0].weather_data[0]
                //异步返回图片、天气给调用函数者
                resolve({dayPictureUrl,weather})
            }else{//如果请求失败
                message.error('天气信息获取失败')
            }
        })
    })
}
//reqWeather('上海')

2. 获取数据category/index.jsx

import React,{Component} from 'react'
import './index.less'
import {
    Button,
    Card,
    Table,
    Icon, 
    message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys} from '../../../api/' //【1】获取api接口函数

export default class Category extends Component{
    state={
        categorys:[] //【2】存放api接口获取的分类列表
    }

    //【3】异步请求一级分类列表
    getCategorys = async (parentId)=>{
        const result=await reqCategorys('0') //请求分类数据并赋值给result
        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
            const categorys=result.data //把返回数据赋值给categorys
            console.log(result.data) //测试输出返回的数据
            this.setState({
                categorys //把返回的数据赋值给state里
            })
        }else{
            message.error('获取分类失败')
        }
    }

    //【4】初始化表格column列名函数    
    initColumn=()=>{
        //表格列名
        this.columns = [
          {
            title: '分类名子',
            key: 'name',
            dataIndex: 'name',
          },
          {
            title: '操作',
            width:'29%',
            render: () => (
                <span>
                    <LinkButton>修改分类</LinkButton>
                    <LinkButton>查看子分类</LinkButton>
                </span>
            ),
          },
          
        ];
    }

    // 【5】页面完成加载后运行,用于异步加载等函数存放
    componentDidMount(){
        this.getCategorys() //获取表格数据源
    }

    // 【6】页面将要加载运行:用于页面渲染前的数据准备
    componentWillMount(){
        this.initColumn() //准备表格列名相关数据
    }
    


    render(){
        //卡片标题
        const title='产品分类管理'
        //卡片右侧添加按键
        const extra=(
            <Button type='primary'>
                <Icon type='plus'/>
                添加
            </Button>
        )

        // 【7】对state里数据解构
        const {categorys}=this.state
        
        return(
            <div className='category'>
                   {/*卡片样式组件*/}             
                <Card title={title} extra={extra} >
                    {/*
                    【8】表格组件、边框、key为数据源的_id、数据源、列名定义
                    */}
                    <Table 
                    bordered
                    rowKey='_id'
                    dataSource={categorys} 
                    columns={this.columns} />
                </Card>
            </div>
        )
    }
}

效果同一.1

3. 优化:加载数据动画、分页显示

【1】控制是否显示加载动画
【2】设置加载中动画状态显示
【3】数据加载完成,取消loading动画显示
【4】对state里数据解构
【5】分页配置:每页显示数量,显示快速跳转
【6】加载动画、

import React,{Component} from 'react'
import './index.less'
import {
    Button,
    Card,
    Table,
    Icon, 
    message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys} from '../../../api/' //获取api接口函数

export default class Category extends Component{
    state={
        loading:false, //【1】控制是否显示加载动画
        categorys:[] //存放api接口获取的分类列表
    }

    //异步请求一级分类列表
    getCategorys = async (parentId)=>{
        this.setState({loading:true}) //【2】设置加载中动画状态显示
        const result=await reqCategorys('0') //请求分类数据并赋值给result
        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
            const categorys=result.data //把返回数据赋值给categorys
            console.log(result.data) //测试输出返回的数据
            this.setState({
                categorys, //把返回的数据赋值给state里
                loading:false, //【3】数据加载完成,取消loading动画显示
            })
        }else{
            message.error('获取分类失败')
        }
    }

    //初始化表格column列名函数    
    initColumn=()=>{
        //表格列名
        this.columns = [
          {
            title: '分类名子',
            key: 'name',
            dataIndex: 'name',
          },
          {
            title: '操作',
            width:'29%',
            render: () => (
                <span>
                    <LinkButton>修改分类</LinkButton>
                    <LinkButton>查看子分类</LinkButton>
                </span>
            ),
          },         
        ];
    }

    // 页面完成加载后运行,用于异步加载等函数存放
    componentDidMount(){
        this.getCategorys() //获取表格数据源
    }

    // 页面将要加载运行:用于页面渲染前的数据准备
    componentWillMount(){
        this.initColumn() //准备表格列名相关数据
    }
    


    render(){
        //卡片标题
        const title='产品分类管理'
        //卡片右侧添加按键
        const extra=(
            <Button type='primary'>
                <Icon type='plus'/>
                添加
            </Button>
        )

        // 【4】对state里数据解构
        const {categorys,loading}=this.state
        
        return(
            <div className='category'>
                   {/*卡片样式组件*/}             
                <Card title={title} extra={extra} >
                    {/*
                    表格组件、边框、key为数据源的_id、
                    数据源、列名定义、
                    【5】分页配置:每页显示数量,显示快速跳转
                    【6】加载动画、                    
                    */}
                    <Table 
                    bordered
                    rowKey='_id'
                    dataSource={categorys} 
                    columns={this.columns} 
                    loading={loading}
                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
                    />
                </Card>
            </div>
        )
    }
}

效果:加载时显示加载中动画、每页显示5条数据(默认十条)

三、二级产品分类及面包屑

3.1 点一级分类进入对应子分类列表,

3.2 头部加面包屑,在子页面显示对应首页分类名

【1】初始为0即请求一级产品分类列表
【2】当前子分类的对应父分类名
【3】子分类列表数据
【4】parentId等于传进来的参数或state里的值
【5】把0改为从state内动态获取,请求分类数据并赋值给result
【6】如果parentId=0则是一级列表,执行:
【7】否则是二级列表,执行:
【8】数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
【9.0】参数:当前条目的数据对象,返回需要显示的界面标签
【9.1】添加事件监听点击时调用显示函数
因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
【10】显示一级分类对应二级产品分类函数
【11】先更新状state的parentId为对应新分类的id
【12】setState是异步执行,并不会马上更新完状态,
因此需在其内部写(在状态更新且重新render()后执行)

【20】卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
【21】显示一级分类函数,设置id状态即可

import React,{Component} from 'react'
import './index.less'
import {
    Button,
    Card,
    Table,
    Icon, 
    message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys,reqAddCategory} from '../../../api/' //获取api接口函数

export default class Category extends Component{
    state={
        loading:false, //控制是否显示加载动画
        parentId:'0', //【1】初始为0即请求一级产品分类列表        
        categorys:[], //存放api接口获取的分类列表
        parentName:'', //【2】当前子分类的对应父分类名
        subCategorys:[], //【3】子分类列表数据

    }

   
    //异步请求一级分类列表
    getCategorys = async (parentId)=>{
        this.setState({loading:true}) //设置加载中动画状态显示
        parentId=parentId || this.state.parentId //【4】parentId等于传进来的参数或state里的值
        const result=await reqCategorys(parentId) //【5】把0改为从state内动态获取,请求分类数据并赋值给result
        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
            console.log(result.data) //测试输出返回的数据
            const categorys=result.data //把返回数据赋值给categorys

            //【6】如果parentId=0则是一级列表,执行:
            if(parentId==='0'){ 
                this.setState({
                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }else{//【7】否则是二级列表,执行:
                this.setState({
                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }
            
        }else{
            message.error('获取分类列表失败')
        }
    }

    //【10】显示一级分类对应二级产品分类函数
    showSubCategory=(category)=>{
        //【11】先更新状state的parentId为对应新分类的id
        this.setState({
            parentId:category._id,
            parentName:category.name
        },()=>{/*【12】setState是异步执行,并不会马上更新完状态,
            因此需在其内部写(在状态更新且重新render()后执行)*/
            console.log('parentId',this.state.parentId)
            this.getCategorys()//获取二级分类列表
        })

    }
    //【21】显示一级分类函数,设置id状态即可
    showCategorys=()=>{
        this.setState({
            parentId:'0',
            parentName:'',
            subCategorys:[],
        })
    }
    //初始化表格column列名函数    
    initColumn=()=>{
        //表格列名
        this.columns = [
          {
            title: '分类名',
            key: 'name',
            dataIndex: 'name',
          },
          {
            title: '操作',
            width:'29%',           
            render: (categoryObj) => (//【9.0】参数:当前条目的数据对象,返回需要显示的界面标签
                <span>
                    <LinkButton>修改分类</LinkButton>
                    {/*
                    【9.1】添加事件监听点击时调用显示函数
                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
                    */}
                    {this.state.parentId==='0'?<LinkButton onClick={()=>this.showSubCategory(categoryOjb)}>查看子分类</LinkButton>:null}
                </span>
            ),
          },
          
        ];
    }

    //添加分类
    addCate= async (parentId,categoryName)=>{
        const result = await reqAddCategory(parentId,categoryName)
        if(result.status===0){
            message.success('分类数据添加成功')
        }else{
            message.error('添加分类失败')
        }
    }

    // 页面完成加载后运行,用于异步加载等函数存放
    componentDidMount(){
        this.getCategorys() //获取表格数据源
    }

    // 页面将要加载运行:用于页面渲染前的数据准备
    componentWillMount(){
        this.initColumn() //准备表格列名相关数据
        //this.addCate('5e41578325a557082c18f43b','洗衣机')
    }
    
    render(){
        // 对state里数据解构
        const {categorys,subCategorys, parentId,parentName,loading}=this.state

        //【20】卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
        const title= parentId==='0'?'一级分类列表':(
            <span>
                <LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>  
            <span>{parentName}</span>
            </span>
        )
        //卡片右侧添加按键
        const extra=(
            <Button type='primary'>
                <Icon type='plus'/>
                添加
            </Button>
        )


        
        return(
            <div className='category'>
                   {/*卡片样式组件*/}             
                <Card title={title} extra={extra} >
                    {/*
                    表格组件、边框、key为数据源的_id、
                    【8】数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
                    列名定义、
                    一页显示数据条数,显示快速跳转
                    */}
                    <Table 
                    bordered
                    rowKey='_id'
                    dataSource={parentId==='0'? categorys:subCategorys} 
                    columns={this.columns} 
                    loading={loading}
                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
                    />
                </Card>
            </div>
        )
    }
}

效果:http://localhost:3000/category

在这里插入图片描述

四、添加分类、更新分类基础布局

4.1引进对话框、添加函数框架

【1】引入对话框
【2】添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
【3】对state里数据解构:
【4】添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
【5】添加监听函数:addCate,updateCate,handleCancel
【6】添加分类函数
【7】更新分类函数updateCate
【8】取消对话框函数handleCancel
【9】添加监听updateCate
【10】卡片右侧添加按键:添加监听

import React,{Component} from 'react'
import './index.less'
import {
    Button,
    Card,
    Table,
    Icon, 
    Modal, //【1】引入对话框
    message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys,reqAddCategory} from '../../../api/' //获取api接口函数

export default class Category extends Component{
    state={
        loading:false, //控制是否显示加载动画
        parentId:'0', //初始为0即请求一级产品分类列表        
        categorys:[], //存放api接口获取的分类列表
        parentName:'', //当前子分类的对应父分类名
        subCategorys:[], //子分类列表数据
        showStatus:0, //【2】添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
    }
   
    //异步请求一级分类列表
    getCategorys = async (parentId)=>{
        this.setState({loading:true}) //设置加载中动画状态显示
        parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
        const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
            console.log(result.data) //测试输出返回的数据
            const categorys=result.data //把返回数据赋值给categorys

            //如果parentId=0则是一级列表,执行:
            if(parentId==='0'){ 
                this.setState({
                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }else{//否则是二级列表,执行:
                this.setState({
                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }
            
        }else{
            message.error('获取分类列表失败')
        }
    }

    //显示一级分类对应二级产品分类函数
    showSubCategory=(category)=>{
        //先更新状state的parentId为对应新分类的id
        this.setState({
            parentId:category._id,
            parentName:category.name
        },()=>{/*setState是异步执行,并不会马上更新完状态,
            因此需在其内部写(在状态更新且重新render()后执行)*/
            console.log('parentId',this.state.parentId)
            this.getCategorys()//获取二级分类列表
        })

    }

    //显示一级分类函数,设置id状态即可
    showCategorys=()=>{
        this.setState({
            parentId:'0',
            parentName:'',
            subCategorys:[],
        })
    }

    //初始化表格column列名函数    
    initColumn=()=>{
        //表格列名
        this.columns = [
          {
            title: '分类名',
            key: 'name',
            dataIndex: 'name',
          },
          {
            title: '操作',
            width:'29%',           
            render: (categoryObj) => (//参数:当前条目数据对象,用于返回需要显示的界面标签
                <span>
                    {/*【9】添加监听showUpdateCate */}
                    <LinkButton onClick={this.showUpdateCate}>修改分类</LinkButton>
                    {/*
                    添加事件监听点击时调用显示函数
                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
                    */}
                    {this.state.parentId==='0'?<LinkButton onClick={()=>this.showSubCategory(categoryOjb)}>查看子分类</LinkButton>:null}
                </span>
            ),
          },
          
        ];
    }


    //【6】显示添加分类
    showAddCate=()=>{
        this.setState({
            showStatus:1
        })        
    }
    //【7】显示更新分类showUp,
    showUpdateCate=()=>{
        this.setState({
            showStatus:2
        })
    }
    //【8】取消对话框函数handleCancel
    handleCancel=()=>{
        this.setState({
            showStatus:0
        })
    }

    // 页面完成加载后运行,用于异步加载等函数存放
    componentDidMount(){
        this.getCategorys() //获取表格数据源
    }

    // 页面将要加载运行:用于页面渲染前的数据准备
    componentWillMount(){
        this.initColumn() //准备表格列名相关数据
        //this.addCate('5e41578325a557082c18f43b','洗衣机')
    }
    
    render(){
        // 【3】对state里数据解构:
        const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state

        //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
        const title= parentId==='0'?'一级分类列表':(
            <span>
                <LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>  
            <span>{parentName}</span>
            </span>
        )
        //【10】卡片右侧添加按键:添加监听
        const extra=(
            <Button type='primary' onClick={this.addCate}>
                <Icon type='plus'/>
                添加
            </Button>
        )
       
        return(
            <div className='category'>
                   {/*卡片样式组件*/}             
                <Card title={title} extra={extra} >
                    {/*
                    表格组件、边框、key为数据源的_id、
                    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
                    列名定义、
                    一页显示数据条数,显示快速跳转
                    */}
                    <Table 
                    bordered
                    rowKey='_id'
                    dataSource={parentId==='0'? categorys:subCategorys} 
                    columns={this.columns} 
                    loading={loading}
                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
                    />


                    {/*【4】添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
                    【5】添加监听函数:addCate,updateCate,handleCancel */}
                    <Modal
                    title="添加分类"
                    visible={showStatus===1}
                    onOk={this.addCate}
                    onCancel={this.handleCancel}
                    >
                    <p>添加分类</p>
                    </Modal>

                    <Modal
                    title="修改分类"
                    visible={showStatus===2}
                    onOk={this.updateCate}
                    onCancel={this.handleCancel}
                    >
                    <p>修改分类</p>
                    </Modal>

                </Card>
            </div>
        )
    }
}

4.2编写添加分类界面pages/category/add-cate-form

import React,{Component} from 'react'
import {
    Form,
    Select,
    Input
} from 'antd'

const Item=Form.Item
const Option=Select.Option

class AddCateForm extends Component{

    render(){
        return(
            <Form>
                <Item>
                    <span>所属分类:</span>
                    <Select>
                        <Option value='1'>一级分类</Option>
                    </Select>                    
                </Item>
                
                <Item>
                    <span>添加子分类:</span>
                    <Input type='text' placeholder='请输入子分类名称' />
                </Item>

            </Form>
        )
    }
}
export default AddCateForm;

pages/category/index.jsx引入刚写的组件

【20】添加分类表单
【21】使用<AddCateForm组件

import AddCateForm from './add-cate-form';//【20】添加分类表单

......

{/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
                    添加监听函数:addCate,updateCate,handleCancel 
                    【21】使用<AddCateForm组件
                    */}
                    <Modal
                    title="添加分类"
                    visible={showStatus===1}
                    onOk={this.addCate}
                    onCancel={this.handleCancel}
                    >
                    <AddCateForm />
                    </Modal>

4.4 表单返回数值获取知识点

官网文档:https://ant.design/components/form-cn/#Form.create(options)

improt {Form} from 'antd'
const {categorys, parentId} = this.props
const { getFieldDecorator } = this.props.form

{
    getFieldDecorator('parentId',{
        initialValue:parentId
    })(//要获取表单值的标签)
}



{
    getFieldDecorator('categoryName',{
        rules:[
            {required:true,message:'分类名称必须输入'}
        ]
    })(       
        <Input type='text' placeholder='请输入子分类名称' />
    )
}
exprot default Form.create()(组件名)

完整代码add-cate-form.jsx

import React,{Component} from 'react'
import {
    Form,
    Select,
    Input
} from 'antd'

const Item=Form.Item
const Option=Select.Option

class AddCateForm extends Component{

    render(){
        const {categorys, parentId} = this.props
        const { getFieldDecorator } = this.props.form
        return(
            <Form>
                <Item>
                    <span>所属分类:</span>
                    {
                        getFieldDecorator('parentId',{
                            initialValue:parentId
                        })(                            
                            <Select>
                                <Option value='1'>一级分类</Option>
                            </Select>
                          )
                    }
                    
                </Item>

                <Item>
                    <span>添加子分类:</span>
                    {
                        getFieldDecorator('categoryName',{
                            rules:[
                                {required:true,message:'分类名称必须输入'}
                            ]
                        })(
                            
                            <Input type='text' placeholder='请输入子分类名称' />
                        )
                    }

                </Item>

            </Form>
        )
    }
}
export default Form.create()(AddCateForm);

4.5编写修改分类界面pages/category/update-cate-form.jsx

到此和4.4代码相同,换个类名即可

import React,{Component} from 'react'
import {
    Form,
    Select,
    Input
} from 'antd'

const Item=Form.Item
const Option=Select.Option

class UpdateCateForm extends Component{

    render(){
        const {categorys, parentId} = this.props
        const { getFieldDecorator } = this.props.form
        return(
            <Form>
                <Item>
                    <span>所属分类:</span>
                    {
                        getFieldDecorator('parentId',{
                            initialValue:parentId
                        })(                            
                            <Select>
                                <Option value='1'>一级分类</Option>
                            </Select>
                          )
                    }
                    
                </Item>

                <Item>
                    <span>添加子分类:</span>
                    {
                        getFieldDecorator('categoryName',{
                            rules:[
                                {required:true,message:'分类名称必须输入'}
                            ]
                        })(
                            
                            <Input type='text' placeholder='请输入子分类名称' />
                        )
                    }

                </Item>

            </Form>
        )
    }
}
export default Form.create()(UpdateCateForm);

4.6效果:http://localhost:3000/category

在这里插入图片描述

五、修改产品分类功能实现

1.点击修改分类时自动显示对应的分类名pages/category/index.jsx

【1】在updateCateForm组件加一个参数categoryName用于传给子组件, 实现更新时显示当前条目的产品分类名称
【2】把当前条目的数据对象传递给updateCate函数
【3】接收[2]传来的对应条目数据对象
【4】接收参数赋值到当前函数
【5】把4步收到的参数赋值给categoryObj
【6】转到update-cate-form.jsx内接收传过来的参数categoryName

import React,{Component} from 'react'
import './index.less'
import {
    Button,
    Card,
    Table,
    Icon, 
    Modal, //引入对话框
    message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys,reqAddCategory} from '../../../api/' //获取api接口函数
import AddCateForm from './add-cate-form'; //添加分类表单
import UpdateCateForm from './update-cate-form'; //导入更新分类的表单

export default class Category extends Component{
    state={
        loading:false, //控制是否显示加载动画
        parentId:'0', //初始为0即请求一级产品分类列表        
        categorys:[], //存放api接口获取的分类列表
        parentName:'', //当前子分类的对应父分类名
        subCategorys:[], //子分类列表数据
        showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
    }

   
    //异步请求一级分类列表
    getCategorys = async (parentId)=>{
        this.setState({loading:true}) //设置加载中动画状态显示
        parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
        const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
            console.log(result.data) //测试输出返回的数据
            const categorys=result.data //把返回数据赋值给categorys

            //如果parentId=0则是一级列表,执行:
            if(parentId==='0'){ 
                this.setState({
                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }else{//否则是二级列表,执行:
                this.setState({
                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }
            
        }else{
            message.error('获取分类列表失败')
        }
    }

    //显示一级分类对应二级产品分类函数
    showSubCategory=(category)=>{
        //先更新状state的parentId为对应新分类的id
        this.setState({
            parentId:category._id,
            parentName:category.name
        },()=>{/*setState是异步执行,并不会马上更新完状态,
            因此需在其内部写(在状态更新且重新render()后执行)*/
            console.log('parentId',this.state.parentId)
            this.getCategorys()//获取二级分类列表
        })

    }

    //显示一级分类函数,设置id状态即可
    showCategorys=()=>{
        this.setState({
            parentId:'0',
            parentName:'',
            subCategorys:[],
        })
    }

    //初始化表格column列名函数    
    initColumn=()=>{
        //表格列名
        this.columns = [
          {
            title: '分类名',
            key: 'name',
            dataIndex: 'name',
          },
          {
            title: '操作',
            width:'29%',           
            render: (categoryObj) => (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
                <span>
                    {/*【2】把当前条目的数据对象传递给showUpdateCate函数 */}
                    <LinkButton onClick={()=>this.showUpdateCate(categoryObj)}>修改分类</LinkButton>
                    {/*
                    添加事件监听点击时调用显示函数
                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
                    */}
                    {this.state.parentId==='0'?<LinkButton onClick={()=>{this.showSubCategory(categoryObj)}}>查看子分类</LinkButton>:null}
                </span>
            ),
          },
          
        ];
    }


    //添加分类函数
    addCate= async (parentId,categoryName)=>{
        this.setState({
            showStatus:1
        })        
        // const result = await reqAddCategory(parentId,categoryName)
        // if(result.status===0){
        //     message.success('分类数据添加成功')
        // }else{
        //     message.error('添加分类失败')
        // }
    }

    //更新分类函数showUpdateCate,【3】接收[2]传来的对应条目数据对象
    showUpdateCate=(categoryObj)=>{
        //【4】接收参数赋值到当前函数
        this.categoryObj=categoryObj
        this.setState({
            showStatus:2
        })
    }

    //取消对话框函数handleCancel
    handleCancel=()=>{
        this.setState({
            showStatus:0
        })
    }

    // 页面完成加载后运行,用于异步加载等函数存放
    componentDidMount(){
        this.getCategorys() //获取表格数据源
    }

    // 页面将要加载运行:用于页面渲染前的数据准备
    componentWillMount(){
        this.initColumn() //准备表格列名相关数据
        //this.addCate('5e41578325a557082c18f43b','洗衣机')
    }
    
    render(){
        // 对state里数据解构:
        const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
        //【5】把4步收到的参数赋值给categoryObj
        const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象

        //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
        const title= parentId==='0'?'一级分类列表':(
            <span>
                <LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>  
            <span>{parentName}</span>
            </span>
        )
        //卡片右侧添加按键:添加监听
        const extra=(
            <Button type='primary' onClick={this.addCate}>
                <Icon type='plus'/>
                添加
            </Button>
        )
       
        return(
            <div className='category'>
                   {/*卡片样式组件*/}             
                <Card title={title} extra={extra} >
                    {/*
                    表格组件、边框、key为数据源的_id、
                    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
                    列名定义、
                    一页显示数据条数,显示快速跳转
                    */}
                    <Table 
                    bordered
                    rowKey='_id'
                    dataSource={parentId==='0'? categorys:subCategorys} 
                    columns={this.columns} 
                    loading={loading}
                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
                    />


                    {/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
                    添加监听函数:addCate,updateCate,handleCancel 
                    使用<AddCateForm组件
                    
                    */}
                    <Modal
                    title="添加分类"
                    visible={showStatus===1}
                    onOk={this.addCate}
                    onCancel={this.handleCancel}
                    >
                    <AddCateForm />
                    </Modal>

                    {/*
                    【1】在updateCateForm组件加一个参数categoryName用于传给子组件,
                    实现更新时显示当前条目的产品分类名称
                    【6】转到update-cate-form.jsx内接收传过来的参数categoryName
                     */}
                    <Modal
                    title="修改分类"
                    visible={showStatus===2}
                    onOk={this.updateCate}
                    onCancel={this.handleCancel}
                    >
                    <UpdateCateForm categoryName={categoryOjb.name}/>
                    </Modal>

                </Card>
            </div>
        )
    }
}

2.接收父组件传过来的属性值,实现显示对应条目的类名update-cate-from.jsx

【1】接收父组件传值组件
【2】把从父组件接收过来的属性参数接收过来
【3】把categoryName解构出来
【4】文本框默认值为父组件传过来的对应条目数据的名字

import React,{Component} from 'react'
import {
    Form,
    Select,
    Input
} from 'antd'
import PropTypes from 'prop-types' //【1】接收父组件传值组件

const Item=Form.Item
const Option=Select.Option

class UpdateCateForm extends Component{
    //【2】把从父组件接收过来的属性参数接收过来
    static propTypes={
        categoryName:PropTypes.string.isRequired,
    }

    render(){
        //【3】把categoryName解构出来
        const {categoryName} = this.props
        const { getFieldDecorator } = this.props.form
        return(
            <Form>
               <Item>
                    {/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
                    <span>修改分类名:</span>
                    {
                        getFieldDecorator('categoryName',{
                            //【4】文本框默认值为父组件传过来的对应条目数据的名字
                            initialValue:categoryName,
                            rules:[
                                {required:true,message:'分类名称必须输入'}
                            ]
                        })(
                            
                            <Input type='text' placeholder='请输入子分类名称' />
                        )
                    }

                </Item>

            </Form>
        )
    }
}
export default Form.create()(UpdateCateForm);

效果:

在这里插入图片描述

3.执行修改产品分类

  1. 知识点,子向父组件传值
  2. form.resetFields()清除缓存

3.1 pages/category/index.jsx

【0】onOk点执行updateCate函数执行分类名修改
【1】执行修改分类(点对话框的ok按钮执行此函数)
【2】从子组件update-cate-form.jsx组件获取要修改的分类名
【3】接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)子组件把form对象传来之前,将其赋值到this.from里
【4】下接update-cate-form.jsx
【5】更新函数:重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
【5】取消修改函数:重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据

import React,{Component} from 'react'
import './index.less'
import {
    Button,
    Card,
    Table,
    Icon, 
    Modal, //引入对话框
    message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys,reqAddCategory,reqUpdateCategory} from '../../../api/' //获取api接口请求函数
import AddCateForm from './add-cate-form'; //添加分类表单
import UpdateCateForm from './update-cate-form'; //导入更新分类的表单

export default class Category extends Component{
    state={
        loading:false, //控制是否显示加载动画
        parentId:'0', //初始为0即请求一级产品分类列表        
        categorys:[], //存放api接口获取的分类列表
        parentName:'', //当前子分类的对应父分类名
        subCategorys:[], //子分类列表数据
        showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
    }

   
    //异步请求一级分类列表
    getCategorys = async (parentId)=>{
        this.setState({loading:true}) //设置加载中动画状态显示
        parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
        const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
            console.log(result.data) //测试输出返回的数据
            const categorys=result.data //把返回数据赋值给categorys

            //如果parentId=0则是一级列表,执行:
            if(parentId==='0'){ 
                this.setState({
                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }else{//否则是二级列表,执行:
                this.setState({
                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }
            
        }else{
            message.error('获取分类列表失败')
        }
    }

    //显示一级分类对应二级产品分类函数
    showSubCategory=(category)=>{
        //先更新状state的parentId为对应新分类的id
        this.setState({
            parentId:category._id,
            parentName:category.name
        },()=>{/*setState是异步执行,并不会马上更新完状态,
            因此需在其内部写(在状态更新且重新render()后执行)*/
            console.log('parentId',this.state.parentId)
            this.getCategorys()//获取二级分类列表
        })

    }

    //显示一级分类函数,设置id状态即可
    showCategorys=()=>{
        this.setState({
            parentId:'0',
            parentName:'',
            subCategorys:[],
        })
    }

    //初始化表格column列名函数    
    initColumn=()=>{
        //表格列名
        this.columns = [
          {
            title: '分类名',
            key: 'name',
            dataIndex: 'name',
          },
          {
            title: '操作',
            width:'29%',           
            render: (categoryObj) => (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
                <span>
                    {/*把当前条目的数据对象传递给updateCate函数 */}
                    <LinkButton onClick={()=>this.showUpdateCate(categoryObj)}>修改分类</LinkButton>
                    {/*
                    添加事件监听点击时调用显示函数
                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
                    */}
                    {this.state.parentId==='0'?<LinkButton onClick={()=>{this.showSubCategory(categoryObj)}}>查看子分类</LinkButton>:null}
                </span>
            ),
          },
          
        ];
    }


    //显示添加分类函数
    showAddCate= async (parentId,categoryName)=>{
        this.setState({
            showStatus:1
        })        
    }

    //更新分类函数updateCate,接收[2]传来的对应条目数据对象
    showUpdateCate=(categoryObj)=>{
        //接收参数赋值到当前函数
        this.categoryObj=categoryObj
        this.setState({
            showStatus:2
        })
    }

    //取消对话框函数handleCancel
    handleCancel=()=>{
        //【6】重置所有表单数据,防止上一条数据修改后点取消,下一条使用缓存,点其它条目时展示上一条修改的数据
        this.form.resetFields()
        this.setState({
            showStatus:0
        })
    }


    //执行添加分类
    AddCate= async (parentId,categoryName)=>{
     
        const result = await reqAddCategory(parentId,categoryName)
        if(result.status===0){
            message.success('产品分类添加成功')
        }else{
            message.error('产品分类添加失败')
        }
    }


    //【1】执行修改分类(点对话框的ok按钮执行此函数)
    updateCate= async ()=>{
        //1.点ok后隐藏对话框
        this.setState({showStatus:0})
        //2.准备数据
        const categoryId=this.categoryObj._id 
        //【2】从子组件update-cate-form.jsx组件获取要修改的分类名
        const categoryName=this.form.getFieldValue('categoryName') //取this的form对象
        // console.log('categoryId:',categoryId)
        // console.log('categoryName:',categoryName)
        //【5】重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
        this.form.resetFields()
        //3.发送请求更新分类
        const result = await reqUpdateCategory({categoryId,categoryName})
        
        if(result.status===0){
            message.success('产品分类修改名称成功')
            //4.重新显示修改名称后的分类列表
            this.getCategorys()
        }else{
            message.error('产品分类修改名称失败')
        }
    }

    // 页面完成加载后运行,用于异步加载等函数存放
    componentDidMount(){
        this.getCategorys() //获取表格数据源
    }

    // 页面将要加载运行:用于页面渲染前的数据准备
    componentWillMount(){
        this.initColumn() //准备表格列名相关数据
        //this.addCate('5e41578325a557082c18f43b','洗衣机')
    }
    
    render(){
        // 对state里数据解构:
        const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
        //把4步收到的参数赋值给categoryObj
        const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象

        //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
        const title= parentId==='0'?'一级分类列表':(
            <span>
                <LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>  
            <span>{parentName}</span>
            </span>
        )
        //卡片右侧添加按键:添加监听
        const extra=(
            <Button type='primary' onClick={this.showAddCate}>
                <Icon type='plus'/>
                添加
            </Button>
        )
       
        return(
            <div className='category'>
                   {/*卡片样式组件*/}             
                <Card title={title} extra={extra} >
                    {/*
                    表格组件、边框、key为数据源的_id、
                    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
                    列名定义、
                    一页显示数据条数,显示快速跳转
                    */}
                    <Table 
                    bordered
                    rowKey='_id'
                    dataSource={parentId==='0'? categorys:subCategorys} 
                    columns={this.columns} 
                    loading={loading}
                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
                    />


                    {/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
                    添加监听函数:addCate,updateCate,handleCancel 
                    使用<AddCateForm组件
                    
                    */}
                    <Modal
                    title="添加分类"
                    visible={showStatus===1}
                    onOk={this.addCate}
                    onCancel={this.handleCancel}
                    >
                    <AddCateForm />
                    </Modal>

                    {/*
                    在updateCateForm组件加一个参数categoryName用于传给子组件,
                    实现更新时显示当前条目的产品分类名称
                    转到update-cate-form.jsx内接收传过来的参数categoryName
                    【0】onOk点执行updateCate函数执行分类名修改
                     */}
                    <Modal
                    title="修改分类"
                    visible={showStatus===2}
                    onOk={this.updateCate}
                    onCancel={this.handleCancel}
                    >
                    {/*【3】接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)
                    子组件把form对象传来之前,将其赋值到this.from里
                    【4】下接update-cate-form.jsx*/}
                    <UpdateCateForm 
                    categoryName={categoryOjb.name}
                    setForm={(form)=>{this.form=form}}
                    />
                    </Modal>

                </Card>
            </div>
        )
    }
}

3.2 update-cate-form.jsx

【1】设置setForm类型为函数且必须
【2】在此组件渲染之前调用一次setForm函数,把form传到父组件去

import React,{Component} from 'react'
import {
    Form,
    Select,
    Input
} from 'antd'
import PropTypes from 'prop-types' //接收父组件传值组件

const Item=Form.Item
const Option=Select.Option

class UpdateCateForm extends Component{
    //把从父组件接收过来的属性参数接收过来
    static propTypes={
        categoryName:PropTypes.string.isRequired,
        //【1】设置setForm类型为函数且必须
        setForm:PropTypes.func.isRequired, 
    }

    //【2】在此组件渲染之前调用一次setForm函数,把form传到父组件去
    componentWillMount(){
        //将form对象通过setForm函数传给父组件
        this.props.setForm(this.props.form)
    }

    render(){
        //把categoryName解构出来
        const {categoryName} = this.props
        const { getFieldDecorator } = this.props.form
        return(
            <Form>
                {/*<Item>
                    <span>所属分类:</span>
                    {
                        getFieldDecorator('parentId',{
                            initialValue:parentId
                        })(                            
                            <Select>
                                <Option value='1'>一级分类</Option>
                            </Select>
                          )
                    }
                    
                </Item>*/}

                <Item>
                    {/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
                    <span>修改分类名:</span>
                    {
                        getFieldDecorator('categoryName',{
                            //文本框默认值为父组件传过来的对应条目数据的名字
                            initialValue:categoryName,
                            rules:[
                                {required:true,message:'分类名称必须输入'}
                            ]
                        })(
                            
                            <Input type='text' placeholder='请输入子分类名称' />
                        )
                    }

                </Item>

            </Form>
        )
    }
}
export default Form.create()(UpdateCateForm);

效果:同上,但修改一个条目后,再点下g 个条目不会显示之前修改的那个数据

在这里插入图片描述

六、添加产品分类功能实现

6.1添加分类组件完善add-cate-form.jsx

【1】引入父子传值模块
【2】引入父组件的传过来的相关属性
【3】到父组件category/index.jsx下把categorys[],parentId,from对象传过来
【4】取出父组件传过来的categorys,parentId
【5】令inintalValue=parentId(实现在子分类点添加分类时一级分类自动显示对应分类)、把一级分类动态写入(实现自动调取所有一级分类)、
【6】把当前组件的form作为参数运行一下父组件传过来的[接收子组件form对象函数],从而实现父组件也有form对象
【7】回到父组件实现功能

import React,{Component} from 'react'
import {
    Form,
    Select,
    Input
} from 'antd';
import PropTypes from 'prop-types' //【1】引入父子传值模块

const Item=Form.Item
const Option=Select.Option

class AddCateForm extends Component{
    //【2】引入父组件的相关信息
    static propTypes={
        categorys:PropTypes.array.isRequired, //父组件的一级分类列表 
        parentId:PropTypes.string.isRequired, //父组件传过来的当前产品分类的parentId
        setForm:PropTypes.func.isRequired,//用来接收父组件传过来的接收子组件form对象的函数
    }
    //【6】把当前组件的form作为参数运行一下父组件传过来的[接收子组件form对象函数],从而实现父组件也有form对象
    componentWillMount(){
        this.props.setForm(this.props.form)
    }

    render(){
        //【3】到父组件category/index.jsx下把categorys[],parentId,from对象传过来
        //【4】取出父组件传过来的categorys,parentId
        const {categorys, parentId} = this.props
        const { getFieldDecorator } = this.props.form
        return(
            <Form>
                <Item>
                    <span>所属分类:</span>
                    {
                    /*【5】令inintalValue=parentId(实现在子分类点添加分类时一级分类自动显示对应分类)、
                    把一级分类动态写入(实现自动调取所有一级分类)、【6】回到父组件实现功能*/
                        getFieldDecorator('parentId',{
                            initialValue:parentId
                        })(                            
                            <Select>
                                <Option value='0'>一级分类</Option>
                                {
                                    categorys.map(c=> <Option value={c._id}>{c.name}</Option>)
                                }
                            </Select>
                          )
                    }
                    
                </Item>

                <Item>
                    <span>添加子分类:</span>
                    {
                        getFieldDecorator('categoryName',{
                            rules:[
                                {required:true,message:'分类名称必须输入'}
                            ]
                        })(
                            
                            <Input type='text' placeholder='请输入子分类名称' />
                        )
                    }

                </Item>

            </Form>
        )
    }
}
export default Form.create()(AddCateForm);

6.2 添加分类功能实现:category/index.jsx

【1】把categorys,和parentId、接收子组件from对象的函数、传到子组件add-cate-form.jsx里面
【2】执行添加分类:
1.获取表单数据
2.清除表单数据
3.如果添加成功:
4.隐藏对话框,提示添加成功
5.重新加载产品分类
6.添加失败

import React,{Component} from 'react'
import './index.less'
import {
    Button,
    Card,
    Table,
    Icon, 
    Modal, //引入对话框
    message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys,reqAddCategory,reqUpdateCategory} from '../../../api/' //获取api接口请求函数
import AddCateForm from './add-cate-form'; //添加分类表单
import UpdateCateForm from './update-cate-form'; //导入更新分类的表单

export default class Category extends Component{
    state={
        loading:false, //控制是否显示加载动画
        parentId:'0', //初始为0即请求一级产品分类列表        
        categorys:[], //存放api接口获取的分类列表
        parentName:'', //当前子分类的对应父分类名
        subCategorys:[], //子分类列表数据
        showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
    }

   
    //异步请求一级分类列表
    getCategorys = async (parentId)=>{
        this.setState({loading:true}) //设置加载中动画状态显示
        parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
        const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
            console.log(result.data) //测试输出返回的数据
            const categorys=result.data //把返回数据赋值给categorys

            //如果parentId=0则是一级列表,执行:
            if(parentId==='0'){ 
                this.setState({
                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }else{//否则是二级列表,执行:
                this.setState({
                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }
            
        }else{
            message.error('获取分类列表失败')
        }
    }

    //显示一级分类对应二级产品分类函数
    showSubCategory=(category)=>{
        //先更新状state的parentId为对应新分类的id
        this.setState({
            parentId:category._id,
            parentName:category.name
        },()=>{/*setState是异步执行,并不会马上更新完状态,
            因此需在其内部写(在状态更新且重新render()后执行)*/
            console.log('parentId',this.state.parentId)
            this.getCategorys()//获取二级分类列表
        })

    }

    //显示一级分类函数,设置id状态即可
    showCategorys=()=>{
        this.setState({
            parentId:'0',
            parentName:'',
            subCategorys:[],
        })
    }

    //初始化表格column列名函数    
    initColumn=()=>{
        //表格列名
        this.columns = [
          {
            title: '分类名',
            key: 'name',
            dataIndex: 'name',
          },
          {
            title: '操作',
            width:'29%',           
            render: (categoryObj) => (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
                <span>
                    {/*把当前条目的数据对象传递给updateCate函数 */}
                    <LinkButton onClick={()=>this.showUpdateCate(categoryObj)}>修改分类</LinkButton>
                    {/*
                    添加事件监听点击时调用显示函数
                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
                    */}
                    {this.state.parentId==='0'?<LinkButton onClick={()=>{this.showSubCategory(categoryObj)}}>查看子分类</LinkButton>:null}
                </span>
            ),
          },
          
        ];
    }


    //显示添加分类函数
    showAddCate= async (parentId,categoryName)=>{
        this.setState({
            showStatus:1
        })        
    }

    //更新分类函数updateCate,接收[2]传来的对应条目数据对象
    showUpdateCate=(categoryObj)=>{
        //接收参数赋值到当前函数
        this.categoryObj=categoryObj
        this.setState({
            showStatus:2
        })
    }

    //取消对话框函数handleCancel
    handleCancel=()=>{
        //重置所有表单数据,防止上一条数据修改后点取消,下一条使用缓存,点其它条目时展示上一条修改的数据
        this.form.resetFields()
        this.setState({
            showStatus:0
        })
    }


    //【2】执行添加分类:
    addCate= async ()=>{
        //1.获取表单数据
        const {parentId,categoryName}=this.form.getFieldsValue()
        //2.清除表单数据
        this.form.resetFields()
        const result = await reqAddCategory(parentId,categoryName)
        if(result.status===0){//3.如果添加成功:            
            //4.隐藏对话框,提示添加成功
            this.setState({showStatus:0})
            message.success('产品分类添加成功')
            //5.重新加载产品分类
            this.getCategorys()
        }else{
            message.error('产品分类添加失败')
        }
    }


    //执行修改分类(点对话框的ok按钮执行此函数)
    updateCate= async ()=>{
        //1.点ok后隐藏对话框
        this.setState({showStatus:0})
        //2.准备数据
        const categoryId=this.categoryObj._id 
        //从子组件update-cate-form.jsx组件获取要修改的分类名
        const categoryName=this.form.getFieldValue('categoryName') //取this的form对象
        // console.log('categoryId:',categoryId)
        // console.log('categoryName:',categoryName)
        //重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
        this.form.resetFields()
        //3.发送请求更新分类
        const result = await reqUpdateCategory({categoryId,categoryName})
        
        if(result.status===0){
            message.success('产品分类修改名称成功')
            //4.重新显示修改名称后的分类列表
            this.getCategorys()
        }else{
            message.error('产品分类修改名称失败')
        }
    }

    // 页面完成加载后运行,用于异步加载等函数存放
    componentDidMount(){
        this.getCategorys() //获取表格数据源
    }

    // 页面将要加载运行:用于页面渲染前的数据准备
    componentWillMount(){
        this.initColumn() //准备表格列名相关数据
        //this.addCate('5e41578325a557082c18f43b','洗衣机')
    }
    
    render(){
        // 对state里数据解构:
        const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
        //把4步收到的参数赋值给categoryObj
        const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象

        //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
        const title= parentId==='0'?'一级分类列表':(
            <span>
                <LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>  
            <span>{parentName}</span>
            </span>
        )
        //卡片右侧添加按键:添加监听
        const extra=(
            <Button type='primary' onClick={this.showAddCate}>
                <Icon type='plus'/>
                添加
            </Button>
        )
       
        return(
            <div className='category'>
                   {/*卡片样式组件*/}             
                <Card title={title} extra={extra} >
                    {/*
                    表格组件、边框、key为数据源的_id、
                    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
                    列名定义、
                    一页显示数据条数,显示快速跳转
                    */}
                    <Table 
                    bordered
                    rowKey='_id'
                    dataSource={parentId==='0'? categorys:subCategorys} 
                    columns={this.columns} 
                    loading={loading}
                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
                    />


                    {/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
                    添加监听函数:addCate,updateCate,handleCancel 
                    使用<AddCateForm组件
                    
                    */}
                    <Modal
                    title="添加分类"
                    visible={showStatus===1}
                    onOk={this.addCate}
                    onCancel={this.handleCancel}
                    >
                        {/**【1】把categorys,和parentId、接收子组件from对象的函数、传到子组件add-cate-form.jsx里面 */}
                    <AddCateForm 
                    categorys={categorys} 
                    parentId={parentId}
                    setForm={(form)=>{this.form=form}}
                     />
                    </Modal>

                    {/*
                    在updateCateForm组件加一个参数categoryName用于传给子组件,
                    实现更新时显示当前条目的产品分类名称
                    转到update-cate-form.jsx内接收传过来的参数categoryName
                    onOk点执行updateCate函数执行分类名修改
                     */}
                    <Modal
                    title="修改分类"
                    visible={showStatus===2}
                    onOk={this.updateCate}
                    onCancel={this.handleCancel}
                    >
                    {/*接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)
                    子组件把form对象传来之前,将其赋值到this.from里
                    下接update-cate-form.jsx*/}
                    <UpdateCateForm 
                    categoryName={categoryOjb.name}
                    setForm={(form)=>{this.form=form}}
                    />
                    </Modal>

                </Card>
            </div>
        )
    }
}

效果:http://localhost:3000/category

点【添加分类】成功添加分类,关闭对话框、显示添加成功、重新加载显示分类
在这里插入图片描述

6.3功能完善

问题:

  1. 在2级分类添加其它一级分类、一级分类对应子分类时,点过去看不到其对应分类(原因是添加后没请求2级分类)
  2. 解决,加个判断,在二级分类下添加一级分类/其它子分类,只请求分类列表,但不改变state.parentId即可。

【1】如果新添加的分类就是当前分类下的子分类(当前表单显示的parentId=状态中的parentId):
【2】如果添加的是一级分类(parentId===0),则需获取一级分类,但无需显示
【3】正常要重新设置state里的parentId=0然后再请求一次,但这样会造成跳转到一级分类
因此把parentId直接做为参数传到getCategorys()里,这样就不会跳转显示一级分类,还是在二级分类里了

import React,{Component} from 'react'
import './index.less'
import {
    Button,
    Card,
    Table,
    Icon, 
    Modal, //引入对话框
    message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys,reqAddCategory,reqUpdateCategory} from '../../../api/' //获取api接口请求函数
import AddCateForm from './add-cate-form'; //添加分类表单
import UpdateCateForm from './update-cate-form'; //导入更新分类的表单

export default class Category extends Component{
    state={
        loading:false, //控制是否显示加载动画
        parentId:'0', //初始为0即请求一级产品分类列表        
        categorys:[], //存放api接口获取的分类列表
        parentName:'', //当前子分类的对应父分类名
        subCategorys:[], //子分类列表数据
        showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
    }

   
    //异步请求一级分类列表
    getCategorys = async (parentId)=>{
        this.setState({loading:true}) //设置加载中动画状态显示
        parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
        const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
            console.log(result.data) //测试输出返回的数据
            const categorys=result.data //把返回数据赋值给categorys

            //如果parentId=0则是一级列表,执行:
            if(parentId==='0'){ 
                this.setState({
                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }else{//否则是二级列表,执行:
                this.setState({
                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }
            
        }else{
            message.error('获取分类列表失败')
        }
    }

    //显示一级分类对应二级产品分类函数
    showSubCategory=(category)=>{
        //先更新状state的parentId为对应新分类的id
        this.setState({
            parentId:category._id,
            parentName:category.name
        },()=>{/*setState是异步执行,并不会马上更新完状态,
            因此需在其内部写(在状态更新且重新render()后执行)*/
            console.log('parentId',this.state.parentId)
            this.getCategorys()//获取二级分类列表
        })

    }

    //显示一级分类函数,设置id状态即可
    showCategorys=()=>{
        this.setState({
            parentId:'0',
            parentName:'',
            subCategorys:[],
        })
    }

    //初始化表格column列名函数    
    initColumn=()=>{
        //表格列名
        this.columns = [
          {
            title: '分类名',
            key: 'name',
            dataIndex: 'name',
          },
          {
            title: '操作',
            width:'29%',           
            render: (categoryObj) => (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
                <span>
                    {/*把当前条目的数据对象传递给updateCate函数 */}
                    <LinkButton onClick={()=>this.showUpdateCate(categoryObj)}>修改分类</LinkButton>
                    {/*
                    添加事件监听点击时调用显示函数
                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
                    */}
                    {this.state.parentId==='0'?<LinkButton onClick={()=>{this.showSubCategory(categoryObj)}}>查看子分类</LinkButton>:null}
                </span>
            ),
          },
          
        ];
    }


    //显示添加分类函数
    showAddCate= async (parentId,categoryName)=>{
        this.setState({
            showStatus:1
        })        
    }

    //更新分类函数updateCate,接收[2]传来的对应条目数据对象
    showUpdateCate=(categoryObj)=>{
        //接收参数赋值到当前函数
        this.categoryObj=categoryObj
        this.setState({
            showStatus:2
        })
    }

    //取消对话框函数handleCancel
    handleCancel=()=>{
        //重置所有表单数据,防止上一条数据修改后点取消,下一条使用缓存,点其它条目时展示上一条修改的数据
        this.form.resetFields()
        this.setState({
            showStatus:0
        })
    }


    //执行添加分类:
    addCate= async ()=>{
        //1.获取表单数据
        const {parentId,categoryName}=this.form.getFieldsValue()
        //2.清除表单数据
        this.form.resetFields()
        const result = await reqAddCategory(parentId,categoryName)
        if(result.status===0){//3.如果添加成功:
            // 【1】如果新添加的分类就是当前分类下的子分类(当前表单显示的parentId=状态中的parentId):
            if(parentId===this.state.parentId){
                //隐藏对话框,提示添加成功
                this.setState({showStatus:0})
                message.success('产品分类添加成功')
                //重新加载请求并展示添加之后的产品分类
                this.getCategorys()
            }else if(parentId==='0'){//【2】如果添加的是一级分类(parentId===0),则需获取一级分类,但无需显示
                //【3】正常要重新设置state里的parentId=0然后再请求一次,但这样会造成跳转到一级分类
                //因此把parentId直接做为参数传到getCategorys()里,这样就不会跳转显示一级分类,还是在二级分类里了
                this.getCategorys('0')
                //隐藏对话框,提示添加成功
                this.setState({showStatus:0})
                message.success('产品分类添加成功')
            }else{//否则(添加其它分类下的子分类)
                message.error('不能添加其它分类的子分类!')
            }
            
        }else{//6.添加失败:
            message.error('产品分类添加失败')
        }
    }


    //执行修改分类(点对话框的ok按钮执行此函数)
    updateCate= async ()=>{
        //1.点ok后隐藏对话框
        this.setState({showStatus:0})
        //2.准备数据
        const categoryId=this.categoryObj._id 
        //从子组件update-cate-form.jsx组件获取要修改的分类名
        const categoryName=this.form.getFieldValue('categoryName') //取this的form对象
        // console.log('categoryId:',categoryId)
        // console.log('categoryName:',categoryName)
        //重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
        this.form.resetFields()
        //3.发送请求更新分类
        const result = await reqUpdateCategory({categoryId,categoryName})
        
        if(result.status===0){
            message.success('产品分类修改名称成功')
            //4.重新显示修改名称后的分类列表
            this.getCategorys()
        }else{
            message.error('产品分类修改名称失败')
        }
    }

    // 页面完成加载后运行,用于异步加载等函数存放
    componentDidMount(){
        this.getCategorys() //获取表格数据源
    }

    // 页面将要加载运行:用于页面渲染前的数据准备
    componentWillMount(){
        this.initColumn() //准备表格列名相关数据
        //this.addCate('5e41578325a557082c18f43b','洗衣机')
    }
    
    render(){
        // 对state里数据解构:
        const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
        //把4步收到的参数赋值给categoryObj
        const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象

        //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
        const title= parentId==='0'?'一级分类列表':(
            <span>
                <LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>  
            <span>{parentName}</span>
            </span>
        )
        //卡片右侧添加按键:添加监听
        const extra=(
            <Button type='primary' onClick={this.showAddCate}>
                <Icon type='plus'/>
                添加
            </Button>
        )
       
        return(
            <div className='category'>
                   {/*卡片样式组件*/}             
                <Card title={title} extra={extra} >
                    {/*
                    表格组件、边框、key为数据源的_id、
                    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
                    列名定义、
                    一页显示数据条数,显示快速跳转
                    */}
                    <Table 
                    bordered
                    rowKey='_id'
                    dataSource={parentId==='0'? categorys:subCategorys} 
                    columns={this.columns} 
                    loading={loading}
                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
                    />


                    {/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
                    添加监听函数:addCate,updateCate,handleCancel 
                    使用<AddCateForm组件
                    
                    */}
                    <Modal
                    title="添加分类"
                    visible={showStatus===1}
                    onOk={this.addCate}
                    onCancel={this.handleCancel}
                    >
                    {/**把categorys,和parentId、接收子组件from对象的函数、传到子组件add-cate-form.jsx里面 */}
                    <AddCateForm 
                    categorys={categorys} 
                    parentId={parentId}
                    setForm={(form)=>{this.form=form}}
                     />
                    </Modal>

                    {/*
                    在updateCateForm组件加一个参数categoryName用于传给子组件,
                    实现更新时显示当前条目的产品分类名称
                    转到update-cate-form.jsx内接收传过来的参数categoryName
                    onOk点执行updateCate函数执行分类名修改
                     */}
                    <Modal
                    title="修改分类"
                    visible={showStatus===2}
                    onOk={this.updateCate}
                    onCancel={this.handleCancel}
                    >
                    {/*接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)
                    子组件把form对象传来之前,将其赋值到this.from里
                    下接update-cate-form.jsx*/}
                    <UpdateCateForm 
                    categoryName={categoryOjb.name}
                    setForm={(form)=>{this.form=form}}
                    />
                    </Modal>

                </Card>
            </div>
        )
    }
}

6.4添加表单验证:更新表单验证

antd表单及规则编写

【1】在字段装饰器加入规则【2】到父组件内写验证

<Item>
   <span>添加子分类:</span>
   {
       getFieldDecorator('categoryName',{
           //【1】在字段装饰器加入规则【2】到父组件内写验证
           rules:[
               {required:true,message:'分类名称必须输入'}
           ]
       })(
           
           <Input type='text' placeholder='请输入子分类名称' />
       )
   }
</Item>

表单验证函数

//antd的表单验证函数结构
this.form.validateFields((err,values)=>{
//如果没有错误
    if(!err){
		//写点ok提交数据要执行的代码
    }
})

表单验证完整函数

//执行添加分类:
    addCate= ()=>{
        //【1】antd表单验证函数
        this.form.validateFields(async (err,values)=>{
            if(!err){//【2】把所有提交数据要执行的代码都放入表单验证无错误之后
                //1.获取表单数据
                // const {parentId,categoryName}=this.form.getFieldsValue()
                //【3】注释旧上一行,改成从values里解构需要的数据
                const {parentId,categoryName}=values
                //2.清除表单数据
                this.form.resetFields()
                const result = await reqAddCategory(parentId,categoryName)
                if(result.status===0){//3.如果添加成功:
                    // 如果新添加的分类就是当前分类下的子分类(当前表单显示的parentId=状态中的parentId):
                    if(parentId===this.state.parentId){
                        //隐藏对话框,提示添加成功
                        this.setState({showStatus:0})
                        message.success('产品分类添加成功')
                        //重新加载请求并展示添加之后的产品分类
                        this.getCategorys()
                    }else if(parentId==='0'){//如果添加的是一级分类(parentId===0),则需获取一级分类,但无需显示
                        //正常要重新设置state里的parentId=0然后再请求一次,但这样会造成跳转到一级分类
                        //因此把parentId直接做为参数传到getCategorys()里,这样就不会跳转显示一级分类,还是在二级分类里了
                        this.getCategorys('0')
                        //隐藏对话框,提示添加成功
                        this.setState({showStatus:0})
                        message.success('产品分类添加成功')
                    }else{
                        message.error('不能添加其它分类的子分类!')
                    }
                    
                }else{//6.添加失败:
                    message.error('产品分类添加失败')
                }
            }
        })      
    }

6.5更新产品分类的表单验证

表单部分update-cate-form.jsx

<Item>
                    {/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
                    <span>修改分类名:</span>
                    {
                        getFieldDecorator('categoryName',{
                            //文本框默认值为父组件传过来的对应条目数据的名字
                            initialValue:categoryName,
                            //【1】加入规则【2】到父组件内写验证
                            rules:[
                                {required:true,message:'分类名称必须输入'}
                            ]
                        })(
                            
                            <Input type='text' placeholder='请输入子分类名称' />
                        )
                    }

                </Item>

验证部分category/index.jsx:

//执行修改分类(点对话框的ok按钮执行此函数)
    updateCate= ()=>{
        //【1】表单的验证函数
        this.form.validateFields(async(err,values)=>{
            //【2】如果没错
            if(!err){
                //1.点ok后隐藏对话框
                this.setState({showStatus:0})
                //2.准备数据
                const categoryId=this.categoryObj._id 
                //从子组件update-cate-form.jsx组件获取要修改的分类名
                //const categoryName=this.form.getFieldValue('categoryName') //取this的form对象
                //【3】注释上一行,改成如下从values解构
                const {categoryName}=values
                // console.log('categoryId:',categoryId)
                // console.log('categoryName:',categoryName)
                //重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
                this.form.resetFields()
                //3.发送请求更新分类
                const result = await reqUpdateCategory({categoryId,categoryName})
                
                if(result.status===0){
                    message.success('产品分类修改名称成功')
                    //4.重新显示修改名称后的分类列表
                    this.getCategorys()
                }else{
                    message.error('产品分类修改名称失败')
                }
            }
        })
    
        
    }

6.4-6.5完整代码

index.jsx

import React,{Component} from 'react'
import './index.less'
import {
    Button,
    Card,
    Table,
    Icon, 
    Modal, //引入对话框
    message,} from 'antd';
import LinkButton from '../../../components/link-button'
import {reqCategorys,reqAddCategory,reqUpdateCategory} from '../../../api/' //获取api接口请求函数
import AddCateForm from './add-cate-form'; //添加分类表单
import UpdateCateForm from './update-cate-form'; //导入更新分类的表单

export default class Category extends Component{
    state={
        loading:false, //控制是否显示加载动画
        parentId:'0', //初始为0即请求一级产品分类列表        
        categorys:[], //存放api接口获取的分类列表
        parentName:'', //当前子分类的对应父分类名
        subCategorys:[], //子分类列表数据
        showStatus:0, //添加分类、更新分类显示状态,0:都不显示,1:显示添加,2:显示更新
    }

   
    //异步请求一级分类列表
    getCategorys = async (parentId)=>{
        this.setState({loading:true}) //设置加载中动画状态显示
        parentId=parentId || this.state.parentId //parentId等于传进来的参数或state里的值
        const result=await reqCategorys(parentId) //把0改为从state内动态获取,请求分类数据并赋值给result
        if(result.status===0){ //如果返回的status=0,说明返回成功,执行:
            console.log(result.data) //测试输出返回的数据
            const categorys=result.data //把返回数据赋值给categorys

            //如果parentId=0则是一级列表,执行:
            if(parentId==='0'){ 
                this.setState({
                    categorys, //(因为名称和state标签名相同,所以用简写)把返回的一级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }else{//否则是二级列表,执行:
                this.setState({
                    subCategorys:categorys, //把返回的二级产品分类数据,赋值给state里
                    loading:false, //数据加载完成,取消loading动画显示
                })
            }
            
        }else{
            message.error('获取分类列表失败')
        }
    }

    //显示一级分类对应二级产品分类函数
    showSubCategory=(category)=>{
        //先更新状state的parentId为对应新分类的id
        this.setState({
            parentId:category._id,
            parentName:category.name
        },()=>{/*setState是异步执行,并不会马上更新完状态,
            因此需在其内部写(在状态更新且重新render()后执行)*/
            console.log('parentId',this.state.parentId)
            this.getCategorys()//获取二级分类列表
        })

    }

    //显示一级分类函数,设置id状态即可
    showCategorys=()=>{
        this.setState({
            parentId:'0',
            parentName:'',
            subCategorys:[],
        })
    }

    //初始化表格column列名函数    
    initColumn=()=>{
        //表格列名
        this.columns = [
          {
            title: '分类名',
            key: 'name',
            dataIndex: 'name',
          },
          {
            title: '操作',
            width:'29%',           
            render: (categoryObj) => (//category代表当前条目对象(名字随意),用于返回需要显示的界面标签
                <span>
                    {/*把当前条目的数据对象传递给updateCate函数 */}
                    <LinkButton onClick={()=>this.showUpdateCate(categoryObj)}>修改分类</LinkButton>
                    {/*
                    添加事件监听点击时调用显示函数
                    因为如果加括号及参数就会自动执行函数,而不是点击后才执行,所以用一个匿名函数把它包起来
                    如果parentId=0即是父级列表则显示查看子分类按钮,否则什么也不显示
                    */}
                    {this.state.parentId==='0'?<LinkButton onClick={()=>{this.showSubCategory(categoryObj)}}>查看子分类</LinkButton>:null}
                </span>
            ),
          },
          
        ];
    }


    //显示添加分类函数
    showAddCate= async (parentId,categoryName)=>{
        this.setState({
            showStatus:1
        })        
    }

    //更新分类函数updateCate,接收[2]传来的对应条目数据对象
    showUpdateCate=(categoryObj)=>{
        //接收参数赋值到当前函数
        this.categoryObj=categoryObj
        this.setState({
            showStatus:2
        })
    }

    //取消对话框函数handleCancel
    handleCancel=()=>{
        //重置所有表单数据,防止上一条数据修改后点取消,下一条使用缓存,点其它条目时展示上一条修改的数据
        this.form.resetFields()
        this.setState({
            showStatus:0
        })
    }


    //执行添加分类:
    addCate= ()=>{
        //【1】antd表单验证函数
        this.form.validateFields(async (err,values)=>{
            if(!err){//【2】把所有提交数据要执行的代码都放入表单验证无错误之后
                //1.获取表单数据
                // const {parentId,categoryName}=this.form.getFieldsValue()
                //【3】注释旧上一行,改成从values里解构需要的数据
                const {parentId,categoryName}=values
                //2.清除表单数据
                this.form.resetFields()
                const result = await reqAddCategory(parentId,categoryName)
                if(result.status===0){//3.如果添加成功:
                    // 如果新添加的分类就是当前分类下的子分类(当前表单显示的parentId=状态中的parentId):
                    if(parentId===this.state.parentId){
                        //隐藏对话框,提示添加成功
                        this.setState({showStatus:0})
                        message.success('产品分类添加成功')
                        //重新加载请求并展示添加之后的产品分类
                        this.getCategorys()
                    }else if(parentId==='0'){//如果添加的是一级分类(parentId===0),则需获取一级分类,但无需显示
                        //正常要重新设置state里的parentId=0然后再请求一次,但这样会造成跳转到一级分类
                        //因此把parentId直接做为参数传到getCategorys()里,这样就不会跳转显示一级分类,还是在二级分类里了
                        this.getCategorys('0')
                        //隐藏对话框,提示添加成功
                        this.setState({showStatus:0})
                        message.success('产品分类添加成功')
                    }else{
                        message.error('不能添加其它分类的子分类!')
                    }
                    
                }else{//6.添加失败:
                    message.error('产品分类添加失败')
                }
            }
        })
        
    }


    //执行修改分类(点对话框的ok按钮执行此函数)
    updateCate= ()=>{
        //【1】表单的验证函数
        this.form.validateFields(async(err,values)=>{
            //【2】如果没错
            if(!err){
                //1.点ok后隐藏对话框
                this.setState({showStatus:0})
                //2.准备数据
                const categoryId=this.categoryObj._id 
                //从子组件update-cate-form.jsx组件获取要修改的分类名
                //const categoryName=this.form.getFieldValue('categoryName') //取this的form对象
                //【3】注释上一行,改成如下从values解构
                const {categoryName}=values
                // console.log('categoryId:',categoryId)
                // console.log('categoryName:',categoryName)
                //重置所有表单数据,防止使用缓存,造成点其它条目时展示上一条修改的数据
                this.form.resetFields()
                //3.发送请求更新分类
                const result = await reqUpdateCategory({categoryId,categoryName})
                
                if(result.status===0){
                    message.success('产品分类修改名称成功')
                    //4.重新显示修改名称后的分类列表
                    this.getCategorys()
                }else{
                    message.error('产品分类修改名称失败')
                }
            }
        })
    
        
    }

    // 页面完成加载后运行,用于异步加载等函数存放
    componentDidMount(){
        this.getCategorys() //获取表格数据源
    }

    // 页面将要加载运行:用于页面渲染前的数据准备
    componentWillMount(){
        this.initColumn() //准备表格列名相关数据
        //this.addCate('5e41578325a557082c18f43b','洗衣机')
    }
    
    render(){
        // 对state里数据解构:
        const {categorys,subCategorys, parentId,parentName,loading,showStatus}=this.state
        //把4步收到的参数赋值给categoryObj
        const categoryOjb = this.categoryObj || {} // 如果还没有,则指定一个空对象

        //卡片标题,如果是一及分类显示 一级分类列表,否则显示一级分类+链接+对应的一级分类名
        const title= parentId==='0'?'一级分类列表':(
            <span>
                <LinkButton onClick={this.showCategorys}>一级分类列表</LinkButton> >>  
            <span>{parentName}</span>
            </span>
        )
        //卡片右侧添加按键:添加监听
        const extra=(
            <Button type='primary' onClick={this.showAddCate}>
                <Icon type='plus'/>
                添加
            </Button>
        )
       
        return(
            <div className='category'>
                   {/*卡片样式组件*/}             
                <Card title={title} extra={extra} >
                    {/*
                    表格组件、边框、key为数据源的_id、
                    数据源、如果parentId为0设置一级分类列表为数据源、否则二级分类为列表源
                    列名定义、
                    一页显示数据条数,显示快速跳转
                    */}
                    <Table 
                    bordered
                    rowKey='_id'
                    dataSource={parentId==='0'? categorys:subCategorys} 
                    columns={this.columns} 
                    loading={loading}
                    pagination={{defaultPageSize: 5, showQuickJumper: true}}
                    />


                    {/*添加对话框:0:都不显示,1:显示添加分类,2:显示更新分类
                    添加监听函数:addCate,updateCate,handleCancel 
                    使用<AddCateForm组件
                    
                    */}
                    <Modal
                    title="添加分类"
                    visible={showStatus===1}
                    onOk={this.addCate}
                    onCancel={this.handleCancel}
                    >
                    {/**把categorys,和parentId、接收子组件from对象的函数、传到子组件add-cate-form.jsx里面 */}
                    <AddCateForm 
                    categorys={categorys} 
                    parentId={parentId}
                    setForm={(form)=>{this.form=form}}
                     />
                    </Modal>

                    {/*
                    在updateCateForm组件加一个参数categoryName用于传给子组件,
                    实现更新时显示当前条目的产品分类名称
                    转到update-cate-form.jsx内接收传过来的参数categoryName
                    onOk点执行updateCate函数执行分类名修改
                     */}
                    <Modal
                    title="修改分类"
                    visible={showStatus===2}
                    onOk={this.updateCate}
                    onCancel={this.handleCancel}
                    >
                    {/*接收子组件传来的form对象(向子组件传递带参数的函数,子组件调用它,再把from对象通过参数传回来)
                    子组件把form对象传来之前,将其赋值到this.from里
                    下接update-cate-form.jsx*/}
                    <UpdateCateForm 
                    categoryName={categoryOjb.name}
                    setForm={(form)=>{this.form=form}}
                    />
                    </Modal>

                </Card>
            </div>
        )
    }
}

update-cate-fomr.jsx

import React,{Component} from 'react'
import {
    Form,
    Select,
    Input
} from 'antd'
import PropTypes from 'prop-types' //接收父组件传值组件

const Item=Form.Item
const Option=Select.Option

class UpdateCateForm extends Component{
    //把从父组件接收过来的属性参数接收过来
    static propTypes={
        categoryName:PropTypes.string.isRequired,
        //设置setForm类型为函数且必须
        setForm:PropTypes.func.isRequired, 
    }

    //在此组件渲染之前调用一次setForm函数,把form传到父组件去
    componentWillMount(){
        //将form对象通过setForm函数传给父组件
        this.props.setForm(this.props.form)
    }

    render(){
        //把categoryName解构出来
        const {categoryName} = this.props
        const { getFieldDecorator } = this.props.form
        return(
            <Form>
                {/*<Item>
                    <span>所属分类:</span>
                    {
                        getFieldDecorator('parentId',{
                            initialValue:parentId
                        })(                            
                            <Select>
                                <Option value='1'>一级分类</Option>
                            </Select>
                          )
                    }
                    
                </Item>*/}

                <Item>
                    {/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
                    <span>修改分类名:</span>
                    {
                        getFieldDecorator('categoryName',{
                            //文本框默认值为父组件传过来的对应条目数据的名字
                            initialValue:categoryName,
                            //【1】加入规则【2】到父组件内写验证
                            rules:[
                                {required:true,message:'分类名称必须输入'}
                            ]
                        })(
                            
                            <Input type='text' placeholder='请输入子分类名称' />
                        )
                    }

                </Item>

            </Form>
        )
    }
}
export default Form.create()(UpdateCateForm);

add-cate-form.jsx

import React,{Component} from 'react'
import {
    Form,
    Select,
    Input
} from 'antd'
import PropTypes from 'prop-types' //接收父组件传值组件

const Item=Form.Item
const Option=Select.Option

class UpdateCateForm extends Component{
    //把从父组件接收过来的属性参数接收过来
    static propTypes={
        categoryName:PropTypes.string.isRequired,
        //设置setForm类型为函数且必须
        setForm:PropTypes.func.isRequired, 
    }

    //在此组件渲染之前调用一次setForm函数,把form传到父组件去
    componentWillMount(){
        //将form对象通过setForm函数传给父组件
        this.props.setForm(this.props.form)
    }

    render(){
        //把categoryName解构出来
        const {categoryName} = this.props
        const { getFieldDecorator } = this.props.form
        return(
            <Form>
                {/*<Item>
                    <span>所属分类:</span>
                    {
                        getFieldDecorator('parentId',{
                            initialValue:parentId
                        })(                            
                            <Select>
                                <Option value='1'>一级分类</Option>
                            </Select>
                          )
                    }
                    
                </Item>*/}

                <Item>
                    {/**因为getFiledDecorator接收的标签必须为Form标签,因此span必须拿出来 */}
                    <span>修改分类名:</span>
                    {
                        getFieldDecorator('categoryName',{
                            //文本框默认值为父组件传过来的对应条目数据的名字
                            initialValue:categoryName,
                            //【1】加入规则【2】到父组件内写验证
                            rules:[
                                {required:true,message:'分类名称必须输入'}
                            ]
                        })(
                            
                            <Input type='text' placeholder='请输入子分类名称' />
                        )
                    }

                </Item>

            </Form>
        )
    }
}
export default Form.create()(UpdateCateForm);

效果:http://localhost:3000/category

在这里插入图片描述
在这里插入图片描述

posted @ 2020-02-15 15:04  晨光曦微  阅读(589)  评论(0编辑  收藏  举报