zoukankan      html  css  js  c++  java
  • 《React后台管理系统实战 :四》产品分类管理页:添加产品分类、修改(更新)产品分类

    一、静态页面

    目录结构

    F:Test eact-demoadmin-clientsrcpagesadmincategory

        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: '操作',
                  '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: '操作',
                '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: '操作',
                '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: '操作',
                '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: '操作',
                '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: '操作',
                '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: '操作',
                '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: '操作',
                '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: '操作',
                '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: '操作',
                '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

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

  • 相关阅读:
    sql sever 数据字典语法
    端口使用情况
    koa中间件说明
    FLIP动画思想
    跨域下载文件显示文件名
    post方法打开新页面并提交参数
    常用快捷键
    cnpm与npm安装的包不一样
    chrome devTools变量不提示,断点点击去不掉问题
    未修改的模块失效排查方法
  • 原文地址:https://www.cnblogs.com/chenxi188/p/12312179.html
Copyright © 2011-2022 走看看