前言
react的语法上就是比vue麻烦不少,既然要开手动挡,那就开吧,一个基础的demo
效果图
列表
新增弹窗
编辑弹框
新增一条数据后的效果
代码
根组件 index.jsx
import React, { Component,createRef} from 'react'
import withRouter from '../../utils/withRouter'
import GlobalFormCom from './globalForm'
import GlobalTable from './globalTable'
import './index.less'
import { cloneDeep } from 'lodash'
import { Input, Select , Button , Modal } from 'antd';
import PubSub from 'pubsub-js'
export default withRouter(class index extends Component {
state = {
searchForm:{
searchName:'王惊涛',
selectValue:'jsCoder'
},
modalInfo:{
isModalOpen:false,
modalTitle:'新建数据',
},
doneType:'add',
addFormData:{},
editFormData:{},
editRecord:{}
}
searchDom = {
width: '200px',
marginRight:'16px'
}
selectOptions = [
{
label:'前端',
value:'jsCoder'
},
{
label:'后端',
value:'pythonCoder'
}
]
changeNameInput = (e,name) => {
this.setValue('searchForm',this.state.searchForm,name,e.target.value)
}
changeSelectValue = (e,name) =>{
this.setValue('searchForm',this.state.searchForm,name,e)
}
setValue = (name,data,key,value,fn)=>{
let _data = cloneDeep(data)
_data[key] = value
this.setState({[name]:_data},()=>{
if(fn){
fn()
}
})
}
openModal = (flag) =>{
this.setState({doneType:flag})
let fn = ()=>{
this.setValue('modalInfo',this.state.modalInfo,'modalTitle','新建数据')
}
this.setValue('modalInfo',this.state.modalInfo,'isModalOpen',true,fn)
}
editHandler = (editData)=>{
this.setState({doneType:'edit'},()=>{
this.setValue('modalInfo',this.state.modalInfo,'modalTitle','编辑数据')
})
let data = cloneDeep(editData)
this.setState({editRecord:data})
this.setValue('modalInfo',this.state.modalInfo,'isModalOpen',true,()=>{
setTimeout(()=>{
PubSub.publish('editForm',this.state.editRecord)
})
})
}
handleOk = ()=>{
// this.handleCancel()
if(this.state.doneType === 'add'){
PubSub.publish('getFormData','add')
}else if(this.state.doneType === 'edit'){
PubSub.publish('getFormData','edit')
}
}
handleCancel = ()=>{
let fn = ()=>{
}
this.setValue('modalInfo',this.state.modalInfo,'isModalOpen',false,fn)
}
globalFormStyle = ()=>{
if(this.state.isModalOpen === false){
return {
display:none
}
}
}
sendFormData = (formData,flag)=>{
if(flag === 'add'){
this.setState({addFormData:formData},()=>{
PubSub.publish('addTableData',this.state.addFormData)
})
}else if(flag === 'edit'){
this.setState({editFormData:formData},()=>{
PubSub.publish('editTableData',this.state.editFormData)
})
}
}
reqList = ()=>{
setTimeout(()=>{
PubSub.publish('reqList')
})
}
render() {
return (
<div className='content'>
<div className='topSearch'>
<span className='labelSpan'>姓名:</span>
<Input placeholder="输入姓名" onChange={(e)=>this.changeNameInput(e,'searchName')} value={this.state.searchForm.searchName} style={this.searchDom} />
<span className='labelSpan'>类型:</span>
<Select
defaultValue={this.state.searchForm.selectValue}
style={this.searchDom}
allowClear
options={this.selectOptions}
onChange={(e)=>this.changeSelectValue(e,'selectValue')}
/>
<Button type='primary' onClick={this.reqList}>数据重置</Button>
<Button type='primary' className='addBtn' onClick={()=>this.openModal('add')}>新增</Button>
</div>
<div className='tableBox'>
<GlobalTable addFormData={this.state.addFormData} closeModal={this.handleCancel} editHandler={this.editHandler}></GlobalTable>
</div>
<div className='footer'>
</div>
<Modal title={this.state.modalInfo.modalTitle} open={this.state.modalInfo.isModalOpen} onOk={this.handleOk} onCancel={this.handleCancel} width="800px" okText="确定" cancelText="取消" destroyOnClose>
<GlobalFormCom sendFormData={this.sendFormData}></GlobalFormCom>
</Modal>
</div>
)
}
})
表格组件 globalTable.jsx
import React, { Component } from 'react'
import withRouter from '../../utils/withRouter'
import { Table, Tag, Space, Button } from 'antd';
const { Column, ColumnGroup } = Table;
import PubSub from 'pubsub-js';
import { cloneDeep } from 'lodash';
export default withRouter(class globalTable extends Component {
state = {
data: []
}
genderMap = {
man: '男',
woman: '女'
}
workMap = {
pythonCoder: '后端',
jsCoder: '前端'
}
componentDidMount() {
this.reqList()
PubSub.unsubscribe('addTableData')
PubSub.subscribe('addTableData',(msg,data)=>{
this.setTableHandler(data,'add')
})
PubSub.unsubscribe('editTableData')
PubSub.subscribe('editTableData',(msg,data)=>{
this.changeTableRow(data)
})
PubSub.unsubscribe('reqList')
PubSub.subscribe('reqList',()=>{
this.reqList()
})
}
changeTableRow = (rowData)=>{
let cloneTableData = cloneDeep(this.state.data)
let index = cloneTableData.findIndex(val=>val.name === rowData.name)
cloneTableData[index] = rowData
this.setState({data:cloneTableData})
this.props['closeModal']()
}
setTableHandler = (data,type)=>{
let tableData = cloneDeep(this.state.data)
if(type === 'add'){
tableData.push(data)
this.setState({data:tableData},()=>{
this.props['closeModal']()
})
}
}
actionBtn = {
marginRight:'12px'
}
editHandler =(record)=>{
this.props.editHandler(record)
}
deleteHandler = (record)=>{
let index = this.state.data.findIndex(item=>item.name === record.name)
if(index !== -1){
let tableData = cloneDeep(this.state.data)
tableData.splice(index,1)
this.setState({data:tableData})
}
}
reqList = ()=>{
this.setState({
data:[{
name: '马师',
gender: 'woman',
work: 'jsCoder',
birthDate: '1995-01-04',
desc: '描述文字'
}]
})
}
render() {
return (
<div>
<Table dataSource={this.state.data} rowKey="name" pagination={false}>
<Column title="姓名" dataIndex="name" key="name" width={120}></Column>
<Column title="性别" dataIndex="gender" key="gender" width={80} render={(text, record) => {
return <span>{this.genderMap[text]}</span>
}}></Column>
<Column title="工作" dataIndex="work" key="work" width={200} render={(text => (
<span>{this.workMap[text]}</span>
))}></Column>
<Column title="出生日期" dataIndex="birthDate" key="birthDate" width={200}></Column>
<Column title="描述" dataIndex="desc" key="desc"></Column>
<Column title="操作" key="action" width={280} render={(text,record) => (
<div>
<Button type='primary' style={this.actionBtn} onClick={()=>this.editHandler(record)} >编辑</Button>
<Button style={this.actionBtn} onClick={()=>this.deleteHandler(record)}>删除</Button>
</div>
)}></Column>
</Table>
</div>
)
}
})
表单组件 globalForm.jsx
import React, { Component, forwardRef, createRef } from 'react'
import withRouter from '../../utils/withRouter'
import { InboxOutlined, UploadOutlined } from '@ant-design/icons';
import PubSub from 'pubsub-js';
import {
Input,
Form,
Radio,
Select,
DatePicker
} from 'antd';
import dayjs from 'dayjs';
const { Option } = Select;
const { TextArea } = Input;
const formItemLayout = {
labelCol: {
span: 6,
},
wrapperCol: {
span: 14,
},
};
const normFile = (e) => {
if (Array.isArray(e)) {
return e;
}
return e?.fileList;
};
const onFinish = (values) => {
};
const GlobalForm = withRouter(class index extends Component {
state = {
formData: {
name: '',
gender: true,
work: null,
desc: '',
birthDate: null,
}
}
workList = [
{
label: '前端',
value: 'jsCoder'
},
{
label: '后端',
value: 'pythonCoder'
}
]
changeFormItem = (e, name) => {
switch (name) {
case 'work':
this.setFormValue(e, name)
case 'birthDate':
this.setFormValue(e, name)
break
default:
this.setFormValue(e.target.value, name)
}
}
setFormValue = (value, key, fn) => {
let _formData = this.state.formData
_formData[key] = value
this.setState({ formData: _formData }, () => {
if (fn) {
fn()
}
})
}
changeDate = (date, dateString) => {
this.setFormValue(dateString, 'birthDate')
}
componentDidMount() {
PubSub.unsubscribe('getFormData')
PubSub.subscribe('getFormData', (msg, data) => {
if (data === 'add') {
this.formRef.current.validateFields().then((value) => {
this.props.sendFormData(this.state.formData, 'add')
}).catch((error) => {
})
} else if (data === 'edit') {
this.formRef.current.validateFields().then((value) => {
this.props.sendFormData(this.state.formData, 'edit')
}).catch((error) => {
})
}
})
PubSub.unsubscribe('editForm')
PubSub.subscribe('editForm', (msg, data) => {
data.birthDate = dayjs(data.birthDate)
this.setState({ formData: data }, () => {
for (let item in data) {
this.formRef.current.setFieldValue(item, data[item])
}
})
})
}
componentDidUpdate() {
}
componentWillUnmount() {
PubSub.unsubscribe('getFormData')
PubSub.unsubscribe('editForm')
}
formRef = createRef()
render() {
return (
<div>
<Form
ref={this.formRef}
name="validate_other"
{...formItemLayout}
onFinish={onFinish}
initialValues={this.state.formData}
style={{
maxWidth: 600,
}}
>
<Form.Item
name="name"
label="姓名"
hasFeedback
rules={[
{
required: true,
message: '请输入名字',
},
]}
>
<Input placeholder="请输入姓名" onChange={(e) => this.changeFormItem(e, 'name')} value={this.state.formData.name}></Input>
</Form.Item>
<Form.Item name="gender" label="性别" rules={[
{
required: true,
message: '请选择性别',
},
]}>
<Radio.Group value={this.state.formData.gender} onChange={(e) => this.changeFormItem(e, 'gender')}>
<Radio value="man">男</Radio>
<Radio value="woman">女</Radio>
</Radio.Group>
</Form.Item>
<Form.Item name="work" label="工作" rules={[
{
required: true,
message: '请选择工作',
},
]}>
<Select value={this.state.formData.work} onChange={(e) => this.changeFormItem(e, 'work')} options={this.workList}>
</Select>
</Form.Item>
<Form.Item name="birthDate" label="出生日期" rules={[
{
required: true,
message: '请选择出生日期',
},
]}>
<DatePicker showTime onChange={(date, dateString) => this.changeDate(date, dateString)} defaultValue={dayjs(this.state.formData.birthDate, 'YYYY/MM/DD')} format='YYYY/MM/DD' />
</Form.Item>
<Form.Item name="desc" label="描述">
<TextArea
showCount
maxLength={100}
style={{
height: 120,
marginBottom: 24,
}}
value={this.state.formData.desc}
onChange={(e) => this.changeFormItem(e, 'desc')}
placeholder="请输入描述内容"
/>
</Form.Item>
</Form>
</div>
)
}
})
export default forwardRef((props, ref) => {
return <GlobalForm {...props} forwardRef={ref}></GlobalForm>
})
公共外套组件 withRouter.jsx
import {
useLocation,
useNavigate,
useParams,
} from "react-router-dom";
function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
return ComponentWithRouterProp;
}
export default withRouter