開發中有這樣的需求,須要對單元格中每一項進行校驗,並提供新增行和刪除行的操做。 css
由於 Form 組件能夠很方便的對數據進行校驗,因此就想到了套用 From 組件和 Table 組件來實現這個需求。能實現需求中的全部功能點,可是方案並不完美,小夥伴們能夠借鑑一下👉連接在這裏。react
效果圖在這裏git
import React from "react";
import ReactDOM from "react-dom";
import PropTypes from "prop-types";
import "antd/dist/antd.css";
import { Form, Input, Table, Select, AutoComplete, Icon } from "antd";
import "./map.css";
class SetMapping extends React.Component {
static propTypes = {
form: PropTypes.shape({}).isRequired,
};
state = {
mappingState: [],
appnameList: [],
tagList: []
};
componentDidMount() {}
// 這裏判斷下 IP 是否是有效的,以及是否重複
syslogIPValidator = () => ({
validator: (rule, value, callback) => {
if (value && value.trim() !== "") {
const reg = /^(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)$/i;
if (!reg.test(value.trim())) {
callback("IP地址格式錯誤");
} else if (this.checkIfExistedIP(value)) {
callback("IP地址重複");
} else {
callback();
}
}
}
});
checkIfExistedIP = value => {
const { form } = this.props;
const { getFieldValue } = form;
const mapping = getFieldValue("mapping") || [];
const mappingArray = mapping ? mapping.filter(one => one.ip === value) : [];
if (mappingArray.length > 1) {
return true;
}
return false;
};
// 添加一行數據
addOneMapping = () => {
const { form } = this.props;
const { getFieldsValue, validateFields } = form;
const { mappingState } = this.state;
const mapping = getFieldsValue().mapping || mappingState;
// 若是表格中有數據-->檢測下以前的有沒有填寫完整
if (mapping && mapping.length !== 0) {
validateFields(["mapping"], { force: true }, errors => {
if (!errors) {
mapping.push({
ip: "",
appname: "",
tag: "",
charset: ""
});
this.setState({ mappingState: mapping });
}
});
} else {
mapping.push({
ip: "",
appname: "",
tag: "",
charset: ""
});
this.setState({ mappingState: mapping });
}
};
// 移除某一項
// mapping 爲 form 的屬性名
// mappingState 爲用來渲染 table 的數據源
// 因此刪除時,須要兩個一塊兒修改
removeOneMapping = index => {
const { form } = this.props;
const { getFieldValue } = form;
const mapping = getFieldValue("mapping");
mapping.splice(index, 1);
this.setState({
mappingState: mapping
});
form.setFieldsValue({
mapping
});
};
render() {
const { mappingState } = this.state;
const { form, appnameList, tagList } = this.props;
const { getFieldDecorator } = form;
const PLACEHOLDER = "請輸入";
const tableTitle = [
{
title: "ip",
dataIndex: "ip",
key: "ip",
width: 205,
render: (ip, one, index) => (
<Form.Item>
{getFieldDecorator(`mapping[${index}].ip`, {
initialValue: ip || "",
validateFirst: true,
validateTrigger: ["onChange", "onFocus"],
rules: [
{
required: true,
message: "請輸入IP地址"
},
this.syslogIPValidator()
]
})(
<Input
size="small"
style={{ width: "170px" }}
autoComplete="off"
placeholder={PLACEHOLDER}
/>
)}
</Form.Item>
)
},
{
title: "appname",
dataIndex: "appname",
key: "appname",
width: 205,
render: (appname, one, index) => (
<Form.Item>
{getFieldDecorator(`mapping[${index}].appname`, {
initialValue: appname || "",
validateFirst: true,
validateTrigger: ["onChange", "onBlur"],
rules: [
{
required: true,
message: "請輸入 appname"
}
]
})(
<AutoComplete
size="small"
dataSource={appnameList}
style={{ width: "170px" }}
placeholder={PLACEHOLDER}
filterOption={(inputValue, option) =>
option.props.children
.toUpperCase()
.indexOf(inputValue.toUpperCase()) !== -1
}
/>
)}
</Form.Item>
)
},
{
title: "tag",
dataIndex: "tag",
key: "tag",
width: 205,
render: (tag, one, index) => (
<Form.Item>
{getFieldDecorator(`mapping[${index}].tag`, {
initialValue: tag || "",
validateFirst: true,
validateTrigger: ["onChange", "onBlur"],
rules: [
{
required: true,
message: "請輸入 tag"
}
]
})(
<AutoComplete
size="small"
dataSource={tagList}
style={{ width: "170px" }}
placeholder={PLACEHOLDER}
filterOption={(inputValue, option) =>
option.props.children
.toUpperCase()
.indexOf(inputValue.toUpperCase()) !== -1
}
/>
)}
</Form.Item>
)
},
{
title: "charset",
dataIndex: "charset",
key: "charset",
width: 110,
render: (charset, one, index) => (
<Form.Item>
{getFieldDecorator(`mapping[${index}].charset`, {
initialValue: charset || "utf-8"
})(
<Select size="small">
<Select.Option value="utf-8" style={{ fontSize: 12 }}>
utf-8
</Select.Option>
<Select.Option value="gbk" style={{ fontSize: 12 }}>
gbk
</Select.Option>
</Select>
)}
</Form.Item>
)
},
{
title: "操做",
key: "operation",
width: 60,
render: (operation, one, index) => (
<Icon
style={{ height: "42px" }}
type="delete"
onClick={() => this.removeOneMapping(index)}
/>
)
}
];
return (
<div className="setMapping">
<Table
bordered
size="small"
className="tableBody"
rowKey={(record, index) => index}
columns={tableTitle}
dataSource={mappingState}
pagination={false}
/>
<span className="addNew" onClick={this.addOneMapping}>
添加新映射
</span>
</div>
);
}
}
const SetMappingForm = Form.create({ name: "register" })(SetMapping);
ReactDOM.render(<SetMappingForm />, document.getElementById("root"));
複製代碼
另外 AntDesign 官方也有相似的實現,你們能夠研究下,👉連接在這裏 。另外這個問題在 github 中也有討論,其餘小夥伴的方法,👉連接在這裏github