数据的增删改查!源码在这儿MyGithub,觉得有帮助的话留颗小星星哦~
效果:
点击编辑按钮路由跳转修改页,并且参数传递,商品信息自动填在另外一个表单中
用户名/自动填充,类别/选择,数量/数字输入框,图片/上传。
可以看出,这些都是可变的,id是不可变的,id才是我们要实现此功能的切入点 ,可以分为以下几个步骤:
- 点击按钮,获取id,跳转至编辑页
- 编辑页根据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,发起一个修改请求,修改商品信息
//点击编辑按钮,通过id获取详情
export const Detail=(id)=>{
return instance.GET("/api/crud/"+id)
}
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}
}
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>
希望展示的效果是,名字自动填充,点击提交信息更改成功.
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!!超简单!!
第一个页面传:
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
});
}
效果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);
效果:模糊搜索
看代码注释把。
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);
!! 虽然效果实现了,但是总觉得,挂载完成的时候数据全请求出来,怪怪的,为了一个提示,感觉浪费很多资源,若是有人看到这个博客,并且有更好的思路请告诉俺~
照例,先来看看样式
这里奉上俺的代码,在注释说说我是怎么改的~
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~ 祈福过完年能找个好工作!!