一步一步搭建react后台系统6之用户列表

一步一步构件react后台系统6 之 用户列表

给后台新增几个接口

  • routes/users.js

获取分页数据

第一次调用接口的时候, 先新增数据, 之后每次从这里面拿出数据。
一般列表接口 都有以下几个变量
这里根据 页码和每页条数拿到请求的数据并返回回去。
total 总条数
page 页码
pageSize 每页条数
data: [] 数据列表


router.get('/userslist', function (ctx, next) {
    // 从上下文中直接获取
    let ctx_query = ctx.query;
    let ctx_querystring = ctx.querystring;
    let page = ctx_query.page
    let data = global.Gdata
    let num = ctx_query.pageSize || 10
    let total = data.length - 1;
    if (data.length === 0) {
        for (let i = 0; i <= 100; i++) {
            data.push({
                key: i,
                username: `John${i}`,
                phone: `1881678249${i}`,
                email: `1031516418${i}@qq.com`,
                description: `My name is John Brown, I am ${i}2 years old, living in New York No. ${i} Lake Park.`,
            });
        }
        total = data.length-1;
    }
    let res_data = []
    let index = page*num/1 - num+1
    res_data = data.slice(index, Math.floor(index) + Math.floor(num))
    console.log(index, index+num);
    console.log(res_data.length)
    if (page <= 0) {
        ctx.body = {
            code: 0,
            data: {
                total: total,
                data: res_data,
                page: ctx_query.page,
                pageSize: num
            },
            message: 'page必须大于等于1'
        }
        return false;
    }
    if (page > Math.ceil(total/num)) {
        ctx.body = {
            code: 0,
            data: {
                total: total,
                data: res_data,
                page: ctx_query.page,
                pageSize: num
            },
            message: 'page必须小于最大页值'
        }
        return false;
    }
    ctx.body = {
        code: 200,
        data: {
            total: total,
            data: res_data,
            page: ctx_query.page,
            pageSize: num
        }
    }
})

修改用户列表


router.put('/userslist', function (ctx, next) {
    let body = ctx.request.body
    let username = body.username;
    let email = body.email;
    let phone = body.phone;
    let data = global.Gdata
    for (var key in data) {
        var value = data[key]
        if (value.username === username) {
            data[key] = {...data[key], ...body}
            ctx.body = {
                code: 200,
                message: "修改成功"
            }
            global.Gdata = data
            return false
        }
    }
    ctx.body = {
        code: 0,
        message: "修改失败"
    }
})

删除用户数据

router.delete('/userslist', function (ctx, next) {
    let body = ctx.request.body
    let username = body.username;
    let email = body.email;
    let phone = body.phone;
    let data = global.Gdata
    for (var key in data) {
        var value = data[key]
        if (value.username === username) {
            data.splice(key, 1)
            ctx.body = {
                code: 200,
                message: "删除成功"
            }
            global.Gdata = data
            return false
        }
    }
    ctx.body = {
        code: 0,
        message: "删除失败"
    }
})

新增用户

router.post('/userslist', function (ctx, next) {
    let body = ctx.request.body
    let username = body.username;
    let email = body.email;
    let phone = body.phone;
    console.log(body)
    global.Gdata.push(body)
    ctx.body = {
        code: 200,
        message: "添加成功"
    }
})
  • app.js

新增全局变量

global.Gdata = [];

新增react用户列表页面。

  • conponents/table/table.js
    这里将用户列表单独提出来了。 如果有哪里不合理的, 以后可以在修改, 当然提出来的好处就是, 一个页面不会有太多内容, 显得臃肿难看

这里拿的同样是ant里面的table表格组件里面的内容, 这里选择的是可以手动开关某些功能的那个表格组件。

import React from 'react'
import { Table, Icon, Switch, Radio, Form, Divider, Button } from 'antd';
import EditableTable from '@/components/table/EditableTable.js'
const FormItem = Form.Item;



const expandedRowRender = record => <p>{record.description}</p>;
const title = () => 'Here is title';
const showHeader = true;
const footer = () => 'Here is footer';
const scroll = { y: 240 };
const pagination = { position: 'bottom' };

class TableComponent extends React.Component {
    constructor(props){
        super(props)
        let defaultState = {  // 定义默认值, 这里都是某些开关属性,是要注入到table组件内部的属性。
            showEditTable: false,
            bordered: false,
            loading: false,
            pagination,
            size: 'default',
            expandedRowRender: undefined,
            title: undefined,
            showHeader,
            footer,
            rowSelection: undefined,
            scroll: undefined,
            hasData: true,
            deleteIndex: -1,
            editingKey: undefined
        }
        this.state = {...defaultState, ...this.props}  // 因为当前页面同样是组件, 因此父组件可能会传递参数过来, 这里使用父组件传递的参数覆盖当前默认参数。
    }

    handleToggle = (prop) => {  // 都是table组件以及写好的方法
        return (enable) => {
            this.setState({ [prop]: enable });
        };
    }

    handleSizeChange = (e) => { // 都是table组件以及写好的方法
        this.setState({ size: e.target.value });
    }

    handleExpandChange = (enable) => { // 都是table组件以及写好的方法
        this.setState({ expandedRowRender: enable ? expandedRowRender : undefined });
    }

    handleTitleChange = (enable) => { // 都是table组件以及写好的方法
        this.setState({ title: enable ? title : undefined });
    }

    handleHeaderChange = (enable) => { // 都是table组件以及写好的方法
        this.setState({ showHeader: enable ? showHeader : false });
    }

    handleFooterChange = (enable) => { // 都是table组件以及写好的方法
        this.setState({ footer: enable ? footer : undefined });
    }

    handleRowSelectionChange = (enable) => { // 都是table组件以及写好的方法
        this.setState({ rowSelection: enable ? {} : undefined });
    }

    handleScollChange = (enable) => { // 都是table组件以及写好的方法
        this.setState({ scroll: enable ? scroll : undefined });
    }

    handleDataChange = (hasData) => { // 都是table组件以及写好的方法
        this.setState({ hasData });
    }

    handlePaginationChange = (e) => { // 都是table组件以及写好的方法
        const { value } = e.target;
        this.setState({
            pagination: value === 'none' ? false : { position: value },
        });
    }
    handleAdd = () => {  // 自定义方法,  新增数据方法。
        typeof this.props.handleAdd === 'function' && this.props.handleAdd()
        let { data } = this.props;
        this.setState({
            data: data
        });
    };

    render() {
        const state = {...this.state, ...this.props} // 合并state 与 props方法, 这样可以监听props变化
        let { data,  columns, showEditTable, deleteIndex, editingKey, handleAdd} = state
        return (
            <div>
                {showEditTable && <div className="components-table-demo-control-bar">  // 判断是否暂时手动开关table功能。
                    <Form layout="inline">
                        <FormItem label="Bordered">
                            <Switch checked={state.bordered} onChange={this.handleToggle('bordered')} />
                        </FormItem>
                        <FormItem label="loading">
                            <Switch checked={state.loading} onChange={this.handleToggle('loading')} />
                        </FormItem>
                        <FormItem label="Title">
                            <Switch checked={!!state.title} onChange={this.handleTitleChange} />
                        </FormItem>
                        <FormItem label="Column Header">
                            <Switch checked={!!state.showHeader} onChange={this.handleHeaderChange} />
                        </FormItem>
                        <FormItem label="Footer">
                            <Switch checked={!!state.footer} onChange={this.handleFooterChange} />
                        </FormItem>
                        <FormItem label="Expandable">
                            <Switch checked={!!state.expandedRowRender} onChange={this.handleExpandChange} />
                        </FormItem>
                        <FormItem label="Checkbox">
                            <Switch checked={!!state.rowSelection} onChange={this.handleRowSelectionChange} />
                        </FormItem>
                        <FormItem label="Fixed Header">
                            <Switch checked={!!state.scroll} onChange={this.handleScollChange} />
                        </FormItem>
                        <FormItem label="Has Data">
                            <Switch checked={!!state.hasData} onChange={this.handleDataChange} />
                        </FormItem>
                        <FormItem label="Size">
                            <Radio.Group size="default" value={state.size} onChange={this.handleSizeChange}>
                                <Radio.Button value="default">Default</Radio.Button>
                                <Radio.Button value="middle">Middle</Radio.Button>
                                <Radio.Button value="small">Small</Radio.Button>
                            </Radio.Group>
                        </FormItem>
                        <FormItem label="Pagination">
                            <Radio.Group
                                value={state.pagination ? state.pagination.position : 'none'}
                                onChange={this.handlePaginationChange}
                            >
                                <Radio.Button value="top">Top</Radio.Button>
                                <Radio.Button value="bottom">Bottom</Radio.Button>
                                <Radio.Button value="both">Both</Radio.Button>
                                <Radio.Button value="none">None</Radio.Button>
                            </Radio.Group>
                        </FormItem>
                    </Form>
                </div>}
                {typeof handleAdd === 'function' && <Button className="editable-add-btn mb-s" onClick={this.handleAdd}>Add</Button>} // 新增数据按钮
                <EditableTable {...state} columns={columns} data={state.hasData ? data : null} rowClassName={(record, index) => {  // 列表组件 将state数据全部传入子组件, 设置动画, 注入数据等操作。
                    if (deleteIndex === record.key) return 'animated zoomOutLeft min-black';
                    return 'animated fadeInRight';
                }}></EditableTable>
            </div>
        );
    }
}

export default TableComponent
  • components/table/EditableTable

表格组件,
这个组件使用的是table中可编辑组件

import React from 'react'
import { Table, Input, InputNumber, Popconfirm, Form } from 'antd';
import EditableContext from '@/components/table/EditableContext'
import EditableFormRow from '@/components/table/EditableFormRow'
import EditableCell from '@/components/table/EditableCell'

class EditableTable extends React.Component {
    constructor(props) {
        super(props);
        this.state = { ...this.props };  // 接收到父组件传递过来的数据, 并给state继承
    }

    isEditing = (record, editingKey) => {   // table可编辑组件中自带方法
        return record.key === editingKey;
    };

    edit(key) { // table可编辑组件中自带方法
        this.setState({ editingKey: key });
    }

    save(form, key) {  // table可编辑组件中自带方法
        form.validateFields((error, row) => {
            if (error) {
                return;
            }
            const newData = [...this.state.data];
            const index = newData.findIndex(item => key === item.key);
            if (index > -1) {
                const item = newData[index];
                newData.splice(index, 1, {
                    ...item,
                    ...row,
                });
                this.setState({ data: newData, editingKey: '' });
            } else {
                newData.push(row);
                this.setState({ data: newData, editingKey: '' });
            }
        });
    }

    cancel = () => { // table可编辑组件中自带方法
        this.setState({ editingKey: '' });
    };

    render() {
        const components = {  // table可编辑组件中自带
            body: {
                row: EditableFormRow,
                cell: EditableCell,
            },
        };
        let state = {...this.state, ...this.props}  // 合并数据
        let { data,  columns, rowClassName, handleAdd, editingKey} = state
        columns = columns.map((col) => {  // 根据表格头内的数据, 判断当前列是否可编辑。
            if (!col.editable) {
                return col;
            }
            return {
                ...col,
                onCell: record =>{
                    return ({
                    record,
                    inputType: col.dataIndex === 'phone' ? 'number' : 'text',
                    dataIndex: col.dataIndex,
                    title: col.title,
                    editing: this.isEditing(record, editingKey),
                })},
            };
        });
        // 将父组件注入的数据全部注入到table 这样就可以通过父组件来控制当前组件的子组件功能的开关了。
        return (
            <Table
                {...state}
                components={components}
                dataSource={data}
                rowClassName={rowClassName}
                columns={columns}
            />
        );
    }
}

export default EditableTable
  • components/table/EditableFormRow

ant可编辑组件中自带的, 经过拆分放在这个文件夹内

import React from 'react'
import EditableContext from '@/components/table/EditableContext'
import { Table, Input, InputNumber, Popconfirm, Form } from 'antd';

const EditableRow = ({ form, index, ...props }) => (
    <EditableContext.Provider value={form}>
        <tr {...props} />
    </EditableContext.Provider>
);

const EditableFormRow = Form.create()(EditableRow);
export default EditableFormRow
  • components/table/EditableCell

ant可编辑组件中自带的, 经过拆分放在这个文件夹内

import React from 'react'
import { Table, Input, InputNumber, Popconfirm, Form } from 'antd';
import EditableContext from '@/components/table/EditableContext'

const FormItem = Form.Item;

class EditableCell extends React.Component {
    getInput = () => {  // 自带方法   判断数据类型, 返回输入组件
        if (this.props.inputType === 'number') {
            return <InputNumber />;
        }
        return <Input />;
    };

    render() {
        let {
            editing,
            dataIndex,
            title,
            inputType,
            record,
            index,
            ...restProps
        } = this.props;
        return (
            <EditableContext.Consumer>
                {(form) => {
                    const { getFieldDecorator } = form;
                    return (
                        <td {...restProps}>
                            {editing ? (
                                <FormItem style={{ margin: 0 }}>
                                    {getFieldDecorator(dataIndex, {  // 注意,  dataIndex 必须存在, 刻在cloums表格头数据内设置。
                                        initialValue: record[dataIndex],
                                    })(this.getInput())}
                                </FormItem>
                            ) : restProps.children}
                        </td>
                    );
                }}
            </EditableContext.Consumer>
        );
    }
}

export default EditableCell
  • components/table/editableContext
    ant可编辑组件自带, 经过拆分放到该文件夹。
import React from 'react'
const EditableContext = React.createContext();
export default EditableContext
  • components/table/mix

这个文件在table组建中, 是非常重要的一个文件, 里面放着一些数据, 主要用来操作表格。

记住以下几点

- methods内的方法, 全部注入到react组件的this内。
- state内的方法, 全部注入到react组件的this.state内。
- 里面的方法, 可在react组件重写, 以达到需要的功能
import React from 'react'
import { Icon, Popconfirm, Divider } from 'antd';
import EditableContext from '@/components/table/EditableContext'
import { editUserList } from '@/api/request.js'

let tableMixNativ = {
    state: {  // 将表格默认属性提取出来,
        showEditTable: false,  // 自定义属性, 是否显示可编辑表格功能。
        bordered: false,
        loading: false,
        pagination:  {},
        size: 'default',
        expandedRowRender: undefined,
        title: undefined,
        showHeader: true,
        footer: undefined,
        rowSelection: undefined,
        scroll: undefined,
        hasData: true,
        columns: [],
        data: [],
        handleAdd: false,
        editingKey: undefined,  // 当前正在编辑的表格行的key,
        modelVisible: false  //  新增数据弹出框是否显示
    },
    methods: {
        pagination () {  // 获取分页数据。 里面返回分页的一些数据
            return (
                { position: 'bottom',
                    onShowSizeChange: (current, pageSize) => {
                    },
                    onChange: (page, pageSize) => {
                    },
                    total: 0,
                    showSizeChanger: true,
                    pageSize: 10,
                    hideOnSinglePage: false,
                    showSizeChanger: true,
                    defaultCurrent: 1,
                    current: 1
                }
            )},
        columns () { // 获取表格头数据,
            let self = this;
            return ([{
                title: '用户名称',
                dataIndex: 'username',
                key: 'username',
                width: 150,
                editable: true,
                render: text => <a href="javascript:;">{text}</a>,
            }, {
                title: '手机号码',
                dataIndex: 'phone',
                key: 'phone',
                width: 150,
                editable: true,
            }, {
                title: '用户邮箱',
                dataIndex: 'email',
                key: 'email',
                editable: true,
            }, {
                title: '操作',
                key: 'action',
                dataIndex: 'action',
                width: 360,
                editable: false,
                render: function (text, record, index){
                    let _self = self
                    if (_self.isEditing(record)) {  // 根据是否正在编辑中的行,  显示对应的组件
                        return (
                            <span>
                  <EditableContext.Consumer>
                    {form => (
                        <a
                            href="javascript:;"
                            onClick={() => _self.save(form, record.key)}
                            style={{ marginRight: 8 }}
                        >
                            保存
                        </a>
                    )}
                  </EditableContext.Consumer>
                  <Popconfirm
                      title="是否取消?"
                      onConfirm={() => _self.cancel(record.key)}
                  >
                    <a>取消</a>
                  </Popconfirm>
                </span>
                        )
                    }
                    return (
                        <span>
        <a href="javascript:;" ><Icon type="show" />查看详情</a>
        <Divider type="vertical" />
        <a href="javascript:;" onClick={_self.handleEdit(record)}><Icon type="edit" />编辑</a>
        <Divider type="vertical" />
        <a href="javascript:;">
        <Popconfirm title="确认删除?" cancelText="取消" okText="确认" onConfirm={() => _self.onDelete(record, index)}>
        <Icon type="delete" /> 删除
        </Popconfirm>
        </a>
        </span>
                    )},
            }])
        },
        handleEdit (record) {return () => {  // 设置编辑中的行,存储取来。
            this.setState({ editingKey: record.key });
        }},
        save(form, key) {  // 编辑完后,保存数据
            form.validateFields(async (error, row) => {
                if (error) {
                    return;
                }
                this.setState({ editingKey: '' });
            });
        },
        cancel () {  // 取消编辑
            this.setState({ editingKey: '' });
        },
        onDelete (record, index) {  // 删除数据
            console.log(this)
            const data = [...this.state.data];
            data.splice(index, 1);
            this.setState({ deleteIndex: record.key});
            setTimeout(() => {
                this.setState({ data })
            }, 500);
        },
        isEditing (record) { // 是否正在编辑中
            return record.key === this.state.editingKey;
        },
        handleAdd () { // 添加数据
            let {data} = this.state
            let num = Math.floor(Math.random()*10000 - 1000)
            data.push({
                key: num,
                username: `add ${num}`,
                phone: 18817844785,
                email: "1031516418@qq.com"
            })
            this.setState((preState) => ({
                data: data
            }))
        },
        setThis (a, b) { // 设置当前对象的this
            var obj = a.methods
            for (var key in obj) {
                var value = obj[key]
                if (typeof value === 'function') {
                    if (!b[key]) {  // 当React组件没有这个方法, 则继承
                        b[key] = value.bind(b)
                    }
                }
            }
            tableMixNativ.state.pagination = b.pagination(b)  // 提取数据注入到当前对象的state内。
            tableMixNativ.state.columns = b.columns(b)    // 提取数据注入到当前对象的state内。
            tableMixNativ.state.handleAdd = b.handleAdd     // 修改state, 继承自react组件
            Object.setPrototypeOf(a, b)
        }
    }
}


export const tableMix =  {...tableMixNativ}
  • views/users/index.js

这里就是调用各个组件, 并传递组件的数据。
这里需要重写继承的属性和方法, 以达到自己想要的功能。
通过modelVisible, modelLoading 控制添加组件的请求状态和显示状态

import React from 'react'
import TableComponent from '@/components/table/table.js'
import { Icon, Popconfirm, Divider } from 'antd';
import { getUserList, editUserList, deleteUserList, addUsersList } from '@/api/request.js'  // 请求数据的方法
import { tableMix } from "@/components/table/mix";
import AddModel from '@/views/users/add'

class Users extends React.Component {
    constructor (props) {
        super(props)
        tableMix.methods.setThis(tableMix, this) // 修改 tableMix 的数据。
        this.state = {
            ...tableMix.state
        }
    }
    componentDidMount () {
        // 重写分页方法。
        let { pagination } = this.state
        pagination.onShowSizeChange = (current, pageSize) => {
            console.log(current, pageSize);
            pagination.current = current
            pagination.pageSize = pageSize
            this.handleUserList({
                page: pagination.current,
                pageSize: pagination.pageSize
            })
            this.setState({
                pagination: pagination
            })
        }
        pagination.onChange = (page, pageSize) => {
            pagination.current = page
            pagination.pageSize = pageSize
            this.onGetUserList()
            this.setState({
                pagination: pagination
            })
        }
        // 修改state.handleAdd属性方法。
        this.state.handleAdd = this.handleAdd
        this.setState({
            pagination: pagination
        })
        this.onGetUserList()
    }
    handleAdd  = async () => {
        this.setState({
            modelVisible: true
        })
    }
    // 调用新增数据接口。 修改model状态
    addUserList = async (data) => {
        let res = await addUsersList(data)
        this.setState({
            modelLoading: false,
            modelVisible: false
        })
        if (res.code === 200) {
            this.onGetUserList()
        }
    }
    // 封装提取数据接口的方法。
    handleUserList = async (obj = {page: 1}) => {
        this.setState((prevState ) => ({
            loading: true
        }))
        let data = await getUserList({
            ...obj
        })
        let { pagination } = this.state
        pagination.pageSize = obj.pageSize ? obj.pageSize : pagination.pageSize
        pagination.total = data.data.total || 0
        this.setState((prevState ) => ({
            data: data.data ? data.data.data : [],
            loading: false,
            pagination: pagination
        }))
    }

    onGetUserList () {
        this.handleUserList({
            page: this.state.pagination.current,
            pageSize: this.state.pagination.pageSize
        })
    }
    // 重写修改保存方法
    save(form, key) {
        form.validateFields(async (error, row) => {
            if (error) {
                return;
            }
            let data = await editUserList(row)
            if (data.code === 200) {
                this.onGetUserList()
            }
            this.setState({ editingKey: '' });
        });
    }
    // 重写删除方法
    async onDelete (record, index) {
        let data = await deleteUserList(record)
        if (data.code === 200) {
            this.setState({ deleteIndex: record.key});
            this.onGetUserList()
        }
    }
    render () {
        let state = this.state
        let { modelVisible, modelLoading } = state
        return (
            <div>
                <AddModel modelVisible={modelVisible} modelLoading={modelLoading} addUserList={this.addUserList}></AddModel>  // 显示新增数据组件
                <TableComponent {...state}></TableComponent>  // table组件
            </div>
        )
    }
}
export default Users
  • views/users/add.js

这里是新增数据组件

请注意下马几个react声明周期

    // componentWillReceiveProps  props 更新时调用
    //  是否重新渲染, 当state 与 props改变时调用
    // shouldComponentUpdate(nextProps, nextState)

    // 组件更新时调用
    // componentWillUpdata(nextProps, nextState)
    // 组件更新完毕后调用
    // componentDidUpdate()
    // 组件卸载时调用
    // componentWillUnmount()
    // 组件渲染之后调用,只调用一次。
    // componentDidMount
import React from 'react'
import { Modal, Button, Form, Input, Tooltip, Icon, Cascader, Select, Row, Col, Checkbox, AutoComplete } from 'antd';
import { validatorPhone } from '@/utils/index'

const FormItem = Form.Item;
const Option = Select.Option;
const AutoCompleteOption = AutoComplete.Option;


class MyForm extends React.Component {
    constructor (props) {
        super(props)
        this.state = {
            loading: this.props.loading
        }
    }
    handleSubmit = (e) => {
        e.preventDefault();
        this.props.form.validateFieldsAndScroll((err, values) => {
            if (!err) {
                console.log('Received values of form: ', values);
                this.props.addUserList(values)  // 通过验证后, 调用父组件传递的调用数据接口。
                this.setState({  // 修改组件状态
                    loading: true
                })
            }
        });
    }
    // 当props更新, 修改props属性。
    componentWillReceiveProps (nextProps) {
        this.setState({
            loading: nextProps.loading
        })
        if (!nextProps.visible) {  // 当组件隐藏, 重置表单
            this.props.form.resetFields()
        }
    }
    render () {
        const { getFieldDecorator } = this.props.form;
        const formItemLayout = {
            labelCol: {
                xs: { span: 24 },
                sm: { span: 8 },
            },
            wrapperCol: {
                xs: { span: 24 },
                sm: { span: 16 },
            },
        };
        const tailFormItemLayout = {
            wrapperCol: {
                xs: {
                    span: 24,
                    offset: 0,
                },
                sm: {
                    span: 16,
                    offset: 8,
                },
            },
        };
        let {loading} = this.state
        return (
            <Form onSubmit={this.handleSubmit}>
                <FormItem
                    {...formItemLayout}
                    label="用户名称"
                >
                    {getFieldDecorator('username', {
                        rules: [{
                             message: '请输入用户名称',
                        }, {
                            required: true, message: '请输入用户名称',
                        }],
                    })(
                        <Input />
                    )}
                </FormItem>
                <FormItem
                    {...formItemLayout}
                    label="用户邮箱"
                >
                    {getFieldDecorator('email', {
                        rules: [{
                            type: 'email', message: '请输入用户邮箱',
                        }, {
                            required: true, message: '请输入用户邮箱',
                        }],
                    })(
                        <Input />
                    )}
                </FormItem>
                <FormItem
                    {...formItemLayout}
                    label="手机号码"
                >
                    {getFieldDecorator('phone', {
                        rules: [{
                            validator: validatorPhone.bind(this)()
                        }, {
                            required: true, message: '请输入手机号码',
                        }],
                    })(
                        <Input />
                    )}
                </FormItem>
                <FormItem {...tailFormItemLayout}>
                    <Button type="primary" htmlType="submit" loading={loading}>提交</Button>
                </FormItem>
            </Form>
        )
    }
}
const ModelForm = Form.create()(MyForm)  // form表单与model分开。


class AddModel extends React.Component {
    state = {
        loading: this.props.modelVisible,
        visible: this.props.modelVisible,
    }

    showModal = () => {
        this.setState({
            visible: true,
        });
    }

    handleOk = () => {  // 当提交的时候, 显示loading
        this.setState({ loading: true });
        // setTimeout(() => {
        //     this.setState({ loading: false, visible: false });
        // }, 3000);
    }

    handleCancel = () => {
        this.setState({ visible: false });
    }
    // props 更新时调用, 这样实时监听父组件传递过来的属性, 然后传递给子组件。
    componentWillReceiveProps (nextProps) {
        this.setState({
            visible: nextProps.modelVisible,
            loading: nextProps.modelLoading
        })
    }
    render() {
        const { visible, loading } = this.state;
        let { addUserList } = this.props
        console.log(loading)
        return (
            <div>
                <Modal
                    visible={visible}
                    title="Title"
                    onOk={this.handleOk}
                    onCancel={this.handleCancel}
                    footer={null}  // 设置为null 不显示footer
                >
                    <ModelForm loading={loading} visible={visible} addUserList={addUserList} ></ModelForm>
                </Modal>
            </div>
        );
    }
}
export default AddModel

添加数据请求方法

像之前把请求直接写在页面中, 是一个不太好的行为,
如果我们把接口地址全部提取出来, 放到一个文件夹内, 这样方便修改与后面的维护

  • api/api.js
export const usersList = '/users/userslist';
export const usersListPut = '/users/userslist';
export const usersListDelete = '/users/userslist';
export const usersListPost = '/users/userslist';

  • api/request.js

把请求的方法全部放入到这个文件内。
好处

- 不用每个页面都引入 http.js   api.js文件了
- 方便统一管理
- 里面的功能单一, 几乎不用怎么修改, 哪怕是修改提交数据, 也是在页面中修改。
- 后期借口多了,  会发现很多重复的, 可以再次提取封装。 (当前接口请求少, 不用理会, 可后期封装)
import instance from './http.js'
import { usersList, usersListPut, usersListDelete, usersListPost } from "./api";

export const getUserList = (params) => {
    return instance.get(usersList, {
        params: params
    })
}
export const editUserList = (data) => {
    return instance.put(usersListPut, data)
}
export const deleteUserList = (data) => {
    return instance.delete(usersListDelete, {
        data
    })
}

export const addUsersList = (data) => {
    return instance.post(usersListPost, data)
}

引入资源文件。

这个表格使用了动画,因此引入文件

  • app.css
@import './assets/css/animate.css';

修改路由配置

import React from 'react'
import Login from '@/views/login/index'
import Index from '@/views/index/index'
import User from '@/views/users/index.js'  // 引入文件


export const menus = [    // 菜单相关路由
    { path: '/index/UI', name: 'UI', icon:'video-camera', component: Ui , routes: [
            {path: '/index/UI/users', name: '用户列表', icon: 'video-camera', component: User },  // 其他不动, 把这个路由修改成用户列表即可
            {path: '/index/UI/Icon', name: '图标', icon: 'video-camera', component: Icon }
        ]
    },
    { path: '/index/animation', name: '动画', icon: 'video-camera', component: Animation },
    { path: '/index/form', name: '表格', icon: 'video-camera', component: From },
]

完成

这里, 我们的table封装就弄好了, 虽然看起来文件很多, 但是用起来却很方便, 各种功能不用再次写一遍, 只需要重写需要修改的方法便可以达到再次利用。 后期主要修改clomus与data数据, 里面的各种功能都可通过开关控制。 是不是很方便?

验证是否table组件是否可重用

  • views/users/index2.js
    复制index.js内容到index2内
    添加this.cloums方法, 这个方法是从Mix拷贝过来的, 添加 {
    title: '用户描述',
    dataIndex: 'description',
    key: 'description',
    editable: true,
    }, 表格头

我们后台生成数据的时候, 是带有description属性的。 我们这不做多修改, 添加这个数据九号。

columns () {
        let self = this;
        return ([{
            title: '用户名称',
            dataIndex: 'username',
            key: 'username',
            width: 150,
            editable: true,
            render: text => <a href="javascript:;">{text}</a>,
        }, {
            title: '手机号码',
            dataIndex: 'phone',
            key: 'phone',
            width: 150,
            editable: true,
        }, {
            title: '用户邮箱',
            dataIndex: 'email',
            key: 'email',
            editable: true,
        },
            {
                title: '用户描述',
                dataIndex: 'description',
                key: 'description',
                editable: true,
            },
            {
            title: '操作',
            key: 'action',
            dataIndex: 'action',
            width: 360,
            editable: false,
            render: function (text, record, index){
                let _self = self
                if (_self.isEditing(record)) {
                    return (
                        <span>
                  <EditableContext.Consumer>
                    {form => (
                        <a
                            href="javascript:;"
                            onClick={() => _self.save(form, record.key)}
                            style={{ marginRight: 8 }}
                        >
                            保存
                        </a>
                    )}
                  </EditableContext.Consumer>
                  <Popconfirm
                      title="是否取消?"
                      onConfirm={() => _self.cancel(record.key)}
                  >
                    <a>取消</a>
                  </Popconfirm>
                </span>
                    )
                }
                return (
                    <span>
        <a href="javascript:;" ><Icon type="show" />查看详情</a>
        <Divider type="vertical" />
        <a href="javascript:;" onClick={_self.handleEdit(record)}><Icon type="edit" />编辑</a>
        <Divider type="vertical" />
        <a href="javascript:;">
        <Popconfirm title="确认删除?" cancelText="取消" okText="确认" onConfirm={() => _self.onDelete(record, index)}>
        <Icon type="delete" /> 删除
        </Popconfirm>
        </a>
        </span>
                )},
        }])
    }

继承的时候, 重写数据。

       this.state = {
            ...tableMix.state, columns: this.columns()
        }
  • route/index.js

把 index 换成index2 , 无缝对接。

import User from '@/views/users/index2.js'

而添加功能, 需要自己添加多一个Input, 其他可不变。 这里就不进行演示了。

github地址
git版本验证table组件是可重用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,971评论 25 707
  • ‌龙珠Z外传 ~赛亚人绝灭计划~ 平台:FC 类型:RPG 比较矛盾的游戏,不能一概论之,把各个部分拆解了一个一个...
    realnabarl阅读 1,777评论 0 1
  • 2020年的清晨一觉睡醒,我头顶的“鸡毛”已经蓬松成一个鸟巢。 头皮瘙痒,我想去理发。 可是我害怕。 我挠了整整一...
    段学生阅读 211评论 0 0
  • ———泛滥有余,治理不足 我们这里没有公交,然交通却是发达的很。不管你去哪里,率先看见的不一定...
    南有乔木刈阅读 370评论 0 3
  • 我惬意的躺在热腾腾的浴池里,任池水浸润着肌肤,任羽灵轻柔的小手抚过我的背脊,手枕在脑后,我就这么半躺着,仰头闭目,...
    考拉凶猛阅读 264评论 0 0