umi+dva+antd后台管理系统(6)---table数据的改查

数据的增删改查!源码在这儿MyGithub,觉得有帮助的话留颗小星星哦~

1. 修改

效果:
点击编辑按钮路由跳转修改页,并且参数传递,商品信息自动填在另外一个表单中

  • 先制作一个简单的修改页表单
    umi+dva+antd后台管理系统(6)---table数据的改查_第1张图片

用户名/自动填充,类别/选择,数量/数字输入框,图片/上传。
可以看出,这些都是可变的,id是不可变的,id才是我们要实现此功能的切入点 ,可以分为以下几个步骤:

  1. 点击按钮,获取id,跳转至编辑页
  2. 编辑页根据id,发请求获取到该商品信息并自动填充
  3. 点击编辑页提交按钮,发送修改请求,修改成功~
具体怎么修改呢,一步一步进阶把~
**初级**
  1. 只要点击,就传过来id存着
  • page:

    import router from 'umi/router';
    
      function onClick(e) {
      console.log(e);
      if (isLogined()) {
        console.log(e);
        dispatch({
          type: 'products/editor',
          payload: {
            id: e,
          },
        });
        router.replace("/editor")
      }
    }
    
    <Popconfirm title="Edit?" onConfirm={() => onClick(record._id)}>
                  <Button
                    type="primary"
                    style={{ width: '35%', textAlign: 'center' }}
                    icon="edit"
                  ></Button>
    </Popconfirm>
    
    
    
  • model: id传过来拉,另一个页面可以取啦

reducers:
editor(state,{payload}){
     console.log("我来咯",payload)
      return {...state,...payload}
    }
**中级**

那么我们的思路就可以变成:
1. 点击按钮,根据id发起一个获取详情请求,获取此商品的详细信息,
2. 在编辑页接收这个id和详细信息,填进表单
3. 点击submit,发起一个修改请求,修改商品信息

  1. 定义请求
//点击编辑按钮,通过id获取详情
export const Detail=(id)=>{
  return instance.GET("/api/crud/"+id)
}
  1. 定义model
effects:
//点击编辑按钮,通过id获取详情
*detail({payload},{call,put}){
      const result=yield call(Detail,payload.id)
      yield put({
        type:'editor',
        payload:{
          product:result.data
        }
      })
    }
 reducers:
 //获取详情之后,保存详情
 editor(state,{payload}){
      console.log("我准备好了",payload)
        return {...state,...payload}
    }
  1. 商品管理页没改还是测试时的代码
	import router from 'umi/router';

	  function onClick(e) {
      console.log(e);
      if (isLogined()) {
        console.log(e);
        //点击请求数据并存在redux里等着被处理
        dispatch({
          type: 'products/editor',
          payload: {
            id: e,
          },
        });
        //点击跳转咯
        router.replace("/editor")
      }
    }
    
   <Popconfirm title="Edit?" onConfirm={() => onClick(record._id)}>
                  <Button
                    type="primary"
                    style={{ width: '35%', textAlign: 'center' }}
                    icon="edit"
                  ></Button>
   </Popconfirm>
  1. 编辑页得通过connect拿到数据,才能去使用待被处理的数据

希望展示的效果是,名字自动填充,点击提交信息更改成功.

  • 拿到传过来的数据
import { connect } from 'dva';

 componentDidMount(){
 //因为这是编辑页,所以我把数据直接放在这了~
    const { product} = this.props;
    console.log(product)

  }

//和之前一样,表单需要 Form.create !!!
const editor = Form.create({ name: 'validate_other' })(edit);
const mapStateToProps = state => state.products;
export default connect(mapStateToProps)(editor);
  • 使用拿过来的数据
//直接用set把值填进去,再发请求修改商品信息就完事了
componentDidMount(){
    console.log(this.props)
    const { product} = this.props;
   //填值
    this.props.form.setFieldsValue({
      proName: product.data.title
    });
  }
**终级**

注意!!!!!

注意,通过状态管理是取巧,但是不对,class跨组件传参正确的是通过props!!超简单!!

这 样 才 是 正 常 的 c l a s s 组 件 跨 页 面 传 参 : \color{red}{这样才是正常的class组件跨页面传参:} class

第一个页面传:
function onClick(e) {
     // 点击跳转页面并把id传过去
      if (isLogined()) {
        router.replace('/editor?id='+e);
      }

第二个页面收:

  componentDidMount(){
    // 挂载完成的this.props里面的location的search属性就有id啦
    console.log(this.props)
    const { product} = this.props;
    console.log(product)
    this.props.form.setFieldsValue({
      proName: product.data.title
    });
  }

正常情况下通过url传参,有了id,就可以先请求详细数据,再求修改数据啦!

效果1:点击商品名输入框,商品名称自动填充,那我们需要id获取商品详情

state:
 state: {
    id: {},
    product: {}
  }
  
effects:
 *detail({ payload }, { call, put }) {
      const result = yield call(Detail, payload.id);
      yield put({
        type: 'editor',
        payload: {
          product: result.data,
        },
      });
    }
--------------------------------------------------------------------------
//此处挂载完成应该发一个请求此商品详情的请求,看效果二~

  getTitle = e => {
    console.log('详情后的', this.props);
    const { product } = this.props;
    this.props.form.setFieldsValue({
      title: product.data.title,
    });
  }

 <Input onFocus={this.getTitle} />

效果2:选择种类,所有种类都显示,这个当然不能写死,也需要发请求

  componentDidMount() {
    // 挂载完成的this.props里面的location的search属性就有啦
    console.log('传来的props', this.props);
    const searchParams = new URLSearchParams(this.props.location.search);
    const id = searchParams.get('id');
    this.props.dispatch({
      type: 'editor/detail',
      payload: { id },
    });
    this.props.dispatch({
      type: 'editor/category',
    });
  }
-----------------------------------------------------------
 <Form.Item label="种类" hasFeedback>
          {getFieldDecorator('name', {
            rules: [{ required: true, message: '必选' }],
          })(
            <Select placeholder="Please select a category" onFocus={this.getMenu}>
              {menu.map(item => (
                <Select.Option key={item._id} value={item.name}>
                  {item.name}
                </Select.Option>
              ))}
            </Select>,
          )}
        </Form.Item>

效果3:价格,很简单,不写了
效果4:图片上传

  constructor(props) {
    super(props);
    this.state = {
      title: '',
      name: '',
      OriginPrice: '',
      upload: '',
      imgUrl: '',
    };
  }
  
 normFile = e => {
    console.log('Upload event:', e);
    if (Array.isArray(e)) {
      return e;
    }
    return e.file && e.fileList;
  };
  
  uploadHandle = e => {
    console.log(e);
    if (e.file.status === 'done') {
      console.log(e.file.response.file);
      this.setState({
        imgUrl: 'http://192.168.11.46:1314' + e.file.response.file,
      });
    }
  }
  
return:
 <Form.Item label="上传图片" extra="">
          {getFieldDecorator('upload', {
            valuePropName: 'fileList',
            getValueFromEvent: this.normFile,
          })(
            <Upload
              name="logo"
              onChange={this.uploadHandle.bind(this)}
               // eslint-disable-next-line
              name="avatar"
              //地址写在这
              action="http://192.168.11.46:1314/upload"
              listType="picture"
            >
              <Button>
                <Icon type="upload" /> 上传图片
              </Button>
            </Upload>,
          )}
        </Form.Item>

这里是单个图片上传,我没懂antd的图片上传捷径是什么,所以自己添了个imgUrl用,多个图片上传见table数据-----
这里又是表单:所以,connect的时候又是Form.create():

const editor = Form.create({ name: 'validate_other' })(edit);

const mapStateToProps = state => state.editor;
export default connect(mapStateToProps)(editor);

2. 查询

效果:模糊搜索
看代码注释把。

import React from 'react';
import { AutoComplete, Button, Input, Icon, Card } from 'antd';
import { connect } from 'dva';
import router from 'umi/router';


const { Meta } = Card;
class search extends React.Component {
//value是表单所有的值,dataSource是搜索时的select的提示数据源
//proInfo是搜索出来的商品信息,flag是控制select的显示隐藏
  constructor(props) {
    super(props);
    this.state = {
      value: '',
      dataSource: [],
      proInfo: [],
      flag: true,
    };
  }

  componentDidMount() {
  //挂载完成的时候加载了全部数据
    this.props.dispatch({
      type: 'search/loadData',
      payload: {
        products: [],
      },
    });
  }

  onSelect = value => {
  //回车或者点击select里的提示商品时,展示商品
    this.props.products.forEach(item => {
      if (item.title.indexOf(value) > -1) {
        this.setState({
          proInfo: [item],
          flag: false,
        });
      }
    });
  };

  onSearch = searchText => {
  //1. 根据输入框内容,根据商品名对比做过滤,过滤后的商品信息存进新数组result
    const result = [];

    this.props.products.forEach(item => {
      if (searchText) {
        if (item.title.indexOf(searchText) > -1) {
          return result.push(item);
        }
      }
    });
  //2. 遍历result把商品名取出来放进新数组data并赋值给dataSource
    let data = [];
    result.map(item => {
      return data.push(item.title);
    });

    this.setState({
      dataSource: !searchText ? [] : data,
      searchData: result,
      flag: true,
    });
  };


  zoom = () => {
  //点击放大镜搜索时,proInfo要展示的商品等于搜索出的数据
    this.setState(
      {
        proInfo: this.state.searchData,
        flag: false,
      },

    );
  };

  onClickHandle=(e)=>{
  //点击编辑图标,url传参+页面跳转
   console.log(e.currentTarget.getAttribute('data-id'))
   let id=e.currentTarget.getAttribute('data-id')
   router.replace('/editor?id='+id)
}


  render() {
    console.log(this.props);
    // const {products} = this.props
    // console.log(products)

    const { dataSource, proInfo } = this.state;
    return (
      <div>
        <AutoComplete
          dataSource={dataSource}
          size="large"
          style={{ width: '40%', marginLeft: '27%' }}
          onSelect={this.onSelect}
          onSearch={this.onSearch}
          placeholder="input here"
          open={this.state.flag}
          getPopupContainer={() => document.getElementById('area')}
        >
          <Input
            size="large"
            onChange={this.onChange}
            suffix={
              <Button
                className="search-btn"
                style={{ marginRight: -12 }}
                size="large"
                type="primary"
                id="area"
                onClick={this.zoom}
              >
                <Icon type="search"/>
              </Button>
            }
          />
        </AutoComplete>

        {proInfo.map(item => {
          return (
            <Card
              hoverable
              style={{
                width: '90%',
                height: '30%',
                marginTop: '5%',
                marginLeft: '5%',
                display: 'flex',
                justifyContent: 'space-around',
                textAlign:"center"
              }}
              cover={<img alt="example" src={item.imgUrl} style={{ width: '100px',height:"100%",marginLeft:"1px"}} />}
              key={item._id}
              actions={[
                <div style={{ display: 'flex', textAlign: 'left', width: '100%' }}>
                  <Icon type="edit" key="edit" style={{ fontSize: '30px', marginTop: '45%' }}  data-id={item._id} onClick={this.onClickHandle}/>
                </div>,
              ]}
            >
              <Meta
                title={item.title}
                description={'剩余数量:' + item.Num + '   ,现价:' + item.OriginPrice}
              />
            </Card>
          );
        })}
      </div>
    );
  }
}

const mapStateToProps = state => state.search;
export default connect(mapStateToProps)(search);

!! 虽然效果实现了,但是总觉得,挂载完成的时候数据全请求出来,怪怪的,为了一个提示,感觉浪费很多资源,若是有人看到这个博客,并且有更好的思路请告诉俺~

照例,先来看看样式

可编行表格:
umi+dva+antd后台管理系统(6)---table数据的改查_第2张图片

效果如图所示,那么antd的代码怎么改着使用呢

这里奉上俺的代码,在注释说说我是怎么改的~

import React from 'react';
import { Table, Input, InputNumber, Popconfirm, Form } from 'antd';
import { connect } from 'dva';
/* eslint-disable */

const EditableContext = React.createContext();
//默认数据data删除掉
class EditableCell extends React.Component {
  getInput = () => {
    if (this.props.inputType === 'number') {
      return <InputNumber />;
    }
    return <Input />;
  };

  renderCell = ({ getFieldDecorator }) => {
    const {
      editing,
      dataIndex,
      title,
      inputType,
      record,
      index,
      children,
      ...restProps
    } = this.props;
    return (
      <td {...restProps}>
        {editing ? (
          <Form.Item style={{ margin: 0 }}>
            {getFieldDecorator(dataIndex, {
              rules: [
                {
                  required: true,
                  message: `Please Input ${name}!`,
                },
              ],
              initialValue: record[dataIndex],
            })(this.getInput())}
          </Form.Item>
        ) : (
          children
        )}
      </td>
    );
  };

  render() {
    return <EditableContext.Consumer>{this.renderCell}</EditableContext.Consumer>;
  }
}

class EditableTable extends React.Component {
//此处有改动,state初始值加了我需要的name,colums也改了
  constructor(props) {
    super(props);
    this.state = { editingKey: '', name: '' };
    this.columns = [
      {
        title: 'name',
        dataIndex: 'name',
        width: '50%',
        editable: true,
      },
      {
        title: 'operation',
        dataIndex: 'operation',
        render: (text, record) => {
          const { editingKey } = this.state;
          const editable = this.isEditing(record);
          return editable ? (
            <span>
              <EditableContext.Consumer>
                {form => (
                  <a
                    onClick={() => {
                    //此处传去的form和record._id极其有用!!!
                      this.save(form, record._id);
                    }}
                    style={{ marginRight: 8 }}
                  >
                    Save
                  </a>
                )}
              </EditableContext.Consumer>
              <Popconfirm title="Sure to cancel?" onConfirm={() => this.cancel(record._id)}>
                <a>Cancel</a>
              </Popconfirm>
            </span>
          ) : (
            <a disabled={editingKey !== ''} onClick={() => this.edit(record._id)}>
              编辑
            </a>
          );
        },
      },
    ];
  }

  componentDidMount() {
  //和之前一样,界面挂载完成数据请求完毕
    this.props.dispatch({
      type: 'category/loadData',
      paload: {},
    });
  }

  isEditing = record => record._id === this.state.editingKey;

  cancel = () => {
  //之前我发完请求修改之后,可编辑的表格依旧存在,我就在想为啥取消就能恢复?
  //于是把这段代码copy过去一试,果然有用!
    this.setState({ editingKey: '' });
  };

  save(form, key) {
  //刚刚传来极大作用的参数,你们可以自行clg一下看看这是啥
    form.validateFields((error, row) => {
      if (error) {
        return;
      }
      //这不就是我们需要编辑传来的id和新值吗!!!
      console.log(row, key, row.name);
      //那就发请求就完事啦!
      this.props.dispatch({
        type: 'category/cate',
        payload: {
          id: key,
          name: row.name,
        },
      });
      //之前修改保存后恢复不了原样?少了这行!
      this.setState({ editingKey: '' });
      this.props.dispatch({
        type:'category/loadData',
      })
    });
  }

 
  edit(key) {
    this.setState({ editingKey: key });
  }

  render() {
    const components = {
      body: {
        cell: EditableCell,
      },
    };

    const columns = this.columns.map(col => {
      if (!col.editable) {
        return col;
      }
      return {
        ...col,
        onCell: record => ({
          record,
          inputType: col.dataIndex === 'age' ? 'number' : 'text',
          dataIndex: col.dataIndex,
          title: col.name,
          editing: this.isEditing(record),
        }),
      };
    });

    return (
      <EditableContext.Provider value={this.props.form}>
        <Table
          components={components}
          bordered
          //数据源,改了
          dataSource={this.props.category}
          columns={columns}
          //rowKey,加了
          rowKey={record => record._id}
          rowClassName="editable-row"
          pagination={{
            onChange: this.cancel,
          }}
        />
      </EditableContext.Provider>
    );
  }
}

//依旧是我们的表单Form.create和HOC---connect
const EditableFormTable = Form.create()(EditableTable);
const mapStateToProps = state => state.category;
export default connect(mapStateToProps)(EditableFormTable);

不知道这样把解释打在注释里到底会不会有人看,但是这样才详细啊~

我的model,和你们好像帮助不大,但是为了完整性,我还是发发!

import { categories,Cate } from '../services/admin';

export default {
  namespace: 'category',
  state: {
    data: [],
    name:[]
  },
  reducers: {
    save({ state }, { payload }) {
      return { ...state, ...payload };
    },
  },
  effects: {
    *loadData({ payload }, { call, put }) {
      const result = yield call(categories);
      yield put({
        type: 'save',
        payload: {
          category: result.data,
        },
      });
    },
    *cate({payload},{call,put}){
      console.log(payload)
      yield call(Cate,payload);
    }
  },
};

OVER~ 祈福过完年能找个好工作!!

你可能感兴趣的:(MangoStore)