表格行编辑

This commit is contained in:
梁才军 2019-04-25 17:03:51 +08:00
parent a29aac93af
commit 8141f08a2b
11 changed files with 1195 additions and 331 deletions

BIN
bee-table.rar Normal file

Binary file not shown.

View File

@ -1,62 +1,368 @@
/**
*
* @title 行编辑
* @title 行编辑 - 行内编辑
* @parent 编辑 Editor
* @description 可以对行进行编辑的表格
*
*/
import React, { Component } from "react";
import React, { Component, PureComponent } from "react";
import Table from "../../src";
import { Button, FormControl } from "tinper-bee";
import { Select,Form,FormControl,Button,Icon,Tooltip } from "tinper-bee";
const Option = Select.Option;
import { RefTreeWithInput } from "ref-tree";
class EditableCell extends Component {
function handleFormValueChange(WarpCompProps, field, allFields) {
const { onChange, throwError } = WarpCompProps;
if (field.value === "") return throwError && throwError(true);
throwError && throwError(false);
onChange && onChange(field.value);
}
const StringEditCell = Form.createForm({
onValuesChange: handleFormValueChange
})(PureStringEditCell);
function PureStringEditCell(props) {
const { getFieldProps, getFieldError } = props.form;
const { value, editable, required } = props;
let cls = "editable-cell-input-wrapper";
if (required) cls += " required";
return (
<div className="editable-cell">
{editable ? (
<div className={cls}>
<FormControl
{...getFieldProps("value", {
initialValue: value,
validateTrigger: "onBlur",
rules: [
{
required: true,
message: (
<Tooltip
inverse
className="u-editable-table-tp"
placement="bottom"
overlay={
<div className="tp-content">
{"请输入" + props.colName}
</div>
}
>
<Icon className="uf-exc-t required-icon" />
</Tooltip>
)
}
]
})}
/>
<span className="error">{getFieldError("value")}</span>
</div>
) : (
<div className="editable-cell-text-wrapper">{value || " "}</div>
)}
</div>
);
}
const SELECT_SOURCE = ["男", "女"];
class SelectEditCell extends PureComponent {
constructor(props, context) {
super(props);
this.state = {
value: this.props.value,
editable: false
};
}
handleChange = value => {
this.setState({ value });
if (this.props.onChange) {
this.props.onChange(value);
}
handleSelect = value => {
this.props.onChange && this.props.onChange(value);
};
render() {
const { value, editable } = this.props;
return (
<div className="editable-cell">
<div className="editable-cell-input-wrapper">
{this.props.editable ? (
<FormControl
value={this.state.value}
onChange={this.handleChange}
/>
) : (
this.state.value || " "
)}
</div>
{editable ? (
<div className="editable-cell-input-wrapper">
<Select value={this.props.value} onSelect={this.handleSelect}>
{SELECT_SOURCE.map((item, index) => (
<Option key={index} value={item}>
{item}
</Option>
))}
</Select>
</div>
) : (
<div className="editable-cell-text-wrapper">{value || " "}</div>
)}
</div>
);
}
}
let dataSource = [
{ a: "ASVAL_201903280005", b: "小张", c: "男", d: "财务二科", key: "1" },
{ a: "ASVAL_201903200004", b: "小明", c: "男", d: "财务一科", key: "2" },
{ a: "ASVAL_201903120002", b: "小红", c: "女", d: "财务一科", key: "3" }
];
const option = {
title: "树",
searchable: true,
multiple: false,
param: {
refCode: "neworganizition_tree"
},
checkStrictly: true,
disabled: false,
nodeDisplay: record => {
return record.refname;
},
displayField: record => {
return record.refname;
}, //显示内容的键
valueField: "refpk", //真实 value 的键
refModelUrl: {
treeUrl: "https://mock.yonyoucloud.com/mock/358/blobRefTree",
treeUrl: "/pap_basedoc/common-ref/blobRefTree"
},
matchUrl: "/pap_basedoc/common-ref/matchPKRefJSON",
filterUrl: "/pap_basedoc/common-ref/filterRefJSON",
lazyModal: false,
strictMode: true,
lang: "zh_CN",
treeData: [
{
code: "org1",
children: [
{
code: "bj",
entityType: "mainEntity",
name: "北京总部-简",
pid: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
refcode: "bj",
refpk: "5305416e-e7b4-4051-90bd-12d12942295b",
id: "5305416e-e7b4-4051-90bd-12d12942295b",
isLeaf: "true",
refname: "北京总部-简"
},
{
code: "xd",
entityType: "mainEntity",
name: "新道-简",
pid: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
refcode: "xd",
refpk: "b691afff-ea83-4a3f-affa-beb2be9cba52",
id: "b691afff-ea83-4a3f-affa-beb2be9cba52",
isLeaf: "true",
refname: "新道-简"
},
{
code: "yy3",
entityType: "mainEntity",
name: "test3",
pid: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
refcode: "yy3",
refpk: "e75694d9-7c00-4e9e-9573-d29465ae79a9",
id: "e75694d9-7c00-4e9e-9573-d29465ae79a9",
isLeaf: "true",
refname: "test3"
},
{
code: "yy1",
entityType: "mainEntity",
name: "test1",
pid: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
refcode: "yy1",
refpk: "fd32ceeb-57a8-4f44-816e-fa660f5715ab",
id: "fd32ceeb-57a8-4f44-816e-fa660f5715ab",
isLeaf: "true",
refname: "test1"
},
{
code: "dept2",
children: [
{
code: "cs",
entityType: "subEntity",
organization_id: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
name: "测试部-简",
pid: "0ebbb6d8-250a-4d1d-a019-7ae951629a2c",
refcode: "cs",
refpk: "cc43a66a-438d-4106-937f-bec44406f771",
id: "cc43a66a-438d-4106-937f-bec44406f771",
isLeaf: "true",
refname: "测试部-简"
},
{
code: "qd",
entityType: "subEntity",
organization_id: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
name: "前端部-简",
pid: "0ebbb6d8-250a-4d1d-a019-7ae951629a2c",
refcode: "qd",
refpk: "73a10edd-aae8-4f31-af25-1f48f0a3b344",
id: "73a10edd-aae8-4f31-af25-1f48f0a3b344",
isLeaf: "true",
refname: "前端部-简"
}
],
entityType: "subEntity",
organization_id: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
name: "生产处",
refcode: "dept2",
refpk: "0ebbb6d8-250a-4d1d-a019-7ae951629a2c",
id: "0ebbb6d8-250a-4d1d-a019-7ae951629a2c",
refname: "生产处"
},
{
code: "dept1",
children: [
{
code: "dept1_2",
entityType: "subEntity",
organization_id: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
name: "财务二科",
pid: "95b60f35-ed0b-454e-b948-fb45ae30b911",
refcode: "dept1_2",
refpk: "55b7fff1-6579-4ca9-92b7-3271d288b9f3",
id: "55b7fff1-6579-4ca9-92b7-3271d288b9f3",
isLeaf: "true",
refname: "财务二科"
},
{
code: "dept1_1",
entityType: "subEntity",
organization_id: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
name: "财务一科",
pid: "95b60f35-ed0b-454e-b948-fb45ae30b911",
refcode: "dept1_1",
refpk: "9711d912-3184-4063-90c5-1facc727813c",
id: "9711d912-3184-4063-90c5-1facc727813c",
isLeaf: "true",
refname: "财务一科"
}
],
entityType: "subEntity",
organization_id: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
name: "财务处",
refcode: "dept1",
refpk: "95b60f35-ed0b-454e-b948-fb45ae30b911",
id: "95b60f35-ed0b-454e-b948-fb45ae30b911",
refname: "财务处"
}
],
entityType: "mainEntity",
name: "用友集团",
refcode: "org1",
refpk: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
id: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
refname: "用友集团"
}
]
};
for (let i = 4; i < 10; i++) {
dataSource.push({
const RefEditCell = Form.createForm()(
class RefComponentWarpper extends PureComponent {
constructor(props, context) {
super(props);
}
handleSelect = values => {
this.props.onChange && this.props.onChange(values[0]);
};
render() {
const { getFieldProps, getFieldError } = this.props.form;
const { value, editable, required } = this.props;
let cls = "editable-cell-input-wrapper";
if (required) cls += " required";
return editable ? (
<div className={cls}>
<RefTreeWithInput
{...option}
onSave={this.handleSelect}
getRefTreeData={this.getRefTreeData}
{...getFieldProps("refValue", {
initialValue: JSON.stringify(value),
rules: [
{
message: (
<Tooltip
inverse
className="u-editable-table-tp"
placement="bottom"
overlay={
<div className="tp-content">
{"请输入" + this.props.colName}
</div>
}
>
<Icon className="uf-exc-t required-icon" />
</Tooltip>
),
pattern: /[^{"refname":"","refpk":""}|{"refpk":"","refname":""}]/
}
]
})}
/>
<span className="error">{getFieldError("refValue")}</span>
</div>
) : (
<div className="editable-cell-text-wrapper">{value.name || " "}</div>
);
}
}
);
let dataSource = [
{
a: "ASVAL_201903280005",
b: "小张",
c: "男",
d: {
code: "dept1_2",
entityType: "subEntity",
organization_id: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
name: "财务二科",
pid: "95b60f35-ed0b-454e-b948-fb45ae30b911",
refcode: "dept1_2",
refpk: "55b7fff1-6579-4ca9-92b7-3271d288b9f3",
id: "55b7fff1-6579-4ca9-92b7-3271d288b9f3",
isLeaf: "true",
refname: "财务二科"
},
key: "1"
},
{
a: "ASVAL_201903200004",
b: "小明",
c: "男",
d: {
code: "dept1_2",
entityType: "subEntity",
organization_id: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
name: "财务二科",
pid: "95b60f35-ed0b-454e-b948-fb45ae30b911",
refcode: "dept1_2",
refpk: "55b7fff1-6579-4ca9-92b7-3271d288b9f3",
id: "55b7fff1-6579-4ca9-92b7-3271d288b9f3",
isLeaf: "true",
refname: "财务二科"
},
key: "2"
},
{
a: "ASVAL_201903120002",
b: "小红",
c: "女",
d: "财务一科",
key: i + ""
});
}
d: {
code: "dept1_1",
entityType: "subEntity",
organization_id: "a4cf0601-51e6-4012-9967-b7a64a4b2d47",
name: "财务一科",
pid: "95b60f35-ed0b-454e-b948-fb45ae30b911",
refcode: "dept1_1",
refpk: "9711d912-3184-4063-90c5-1facc727813c",
id: "9711d912-3184-4063-90c5-1facc727813c",
isLeaf: "true",
refname: "财务一科"
},
key: "3"
}
];
class Demo0501 extends Component {
constructor(props, context) {
@ -65,17 +371,20 @@ class Demo0501 extends Component {
{
title: "员工编号",
dataIndex: "a",
key: "a"
key: "a",
},
{
title: "名字",
dataIndex: "b",
key: "b",
render: (text, record, index) => (
<EditableCell
editable={this.state.editingRows.indexOf(index) > -1}
<StringEditCell
colName={"名字"}
editable={this.state.editingRowsMap[index] || false}
required
value={text}
onChange={this.onCellChange(index, "quality")}
onChange={this.onCellChange(index, "b")}
throwError={this.throwError}
/>
)
},
@ -85,70 +394,103 @@ class Demo0501 extends Component {
key: "c",
width: 100,
render: (text, record, index) => (
<EditableCell
editable={this.state.editingRows.indexOf(index) > -1}
<SelectEditCell
editable={this.state.editingRowsMap[index] || false}
value={text}
onChange={this.onCellChange(index, "level")}
onChange={this.onCellChange(index, "c")}
/>
)
},
{
titile: "部门",
title: "部门",
dataIndex: "d",
key: "d",
render: (text, record, index) => text
width: 215,
render: (text, record, index) => (
<RefEditCell
colName={"部门"}
editable={this.state.editingRowsMap[index] || false}
required
value={record.d}
onChange={this.onCellChange(index, "d")}
throwError={this.throwError}
/>
)
},
// 只是用来占位占宽度的
{
key: "placeholder"
}
];
this.state = {
dataSource: dataSource,
editingRows: []
editingRowsMap: {},
currentIndex: null,
errorEditFlag: false
};
this.dataBuffer = {};
this.currentIndex = null;
this.currentRecord = null;
this.__OPTS_BTN_GROUP__ = null;
this.originData = {};
}
createBtn = (text, props, event) => {
let btn = document.createElement("button");
btn.innerText = text;
for (let pKey in props) {
btn.setAttribute(pKey, props[pKey]);
}
for (let eKey in event) {
btn.addEventListener(eKey, event[eKey]);
}
return btn;
};
edit = index => () => {
if (index === null) return;
let editingRows = [...this.state.editingRows];
editingRows.push(index);
this.dataBuffer[index] = Object.assign({}, dataSource[index]);
this.setState({ editingRows });
let editingRowsMap = { ...this.state.editingRowsMap };
editingRowsMap[index] = index.toString();
this.originData[index] = { ...this.state.dataSource[index] };
this.setState({ editingRowsMap });
};
abortEdit = () => {
let editingRows = [...this.state.editingRows];
editingRows.splice(index, 1);
delete this.dataBuffer[index];
this.setState({ editingRows });
abortEdit = index => () => {
let editingRowsMap = { ...this.state.editingRowsMap };
let dataSource = [...this.state.dataSource];
dataSource[index] = this.originData[index];
delete editingRowsMap[index];
delete this.originData[index];
this.setState({ editingRowsMap, dataSource });
};
commitChange = index => () => {
let editingRows = [...this.state.editingRows];
let dataSource = [...this.state.dataSource];
editingRows.splice(editingRows.indexOf(index), 1);
dataSource[index] = this.dataBuffer[index];
delete this.dataBuffer[index];
this.setState({ editingRows, dataSource });
if (this.state.errorEditFlag) return;
let editingRowsMap = { ...this.state.editingRowsMap };
delete editingRowsMap[index];
this.setState({ editingRowsMap });
};
onCellChange = (index, key) => value => {
this.dataBuffer[index][key] = value;
let dataSource = [...this.state.dataSource];
dataSource[index][key] = value;
this.setState({ dataSource });
};
throwError = isError => {
if (isError !== this.state.errorEditFlag)
this.setState({ errorEditFlag: isError });
};
handleRowHover = (index, record) => {
this.currentRecord = record;
this.setState({ currentIndex: index });
};
renderRowHover = () => {
const { currentIndex } = this.state;
return this.state.editingRowsMap[currentIndex] ? (
<div className={"opt-btns"}>
<Button colors="dark" onClick={this.abortEdit(currentIndex)}>
取消
</Button>
<Button colors="primary" onClick={this.commitChange(currentIndex)}>
确认
</Button>
</div>
) : (
<div className={"opt-btns"}>
<Button colors="dark" onClick={this.edit(currentIndex)}>
编辑
</Button>
</div>
);
};
render() {

View File

@ -1,16 +1,72 @@
.demo0501 {
.u-table-row {
height: 58px;
}
.demo0501 .u-table {
.u-row-hover {
.opt-btns {
button {
border-radius: 5px;
&:first-child {
margin-right: 10px;
}
}
}
}
.u-table-row {
td {
padding: 5px 8px;
input {
font-size: 12px;
padding-left: 5px;
}
}
.u-form-control,
.u-select-selection {
height: 30px;
}
}
.editable-cell-text-wrapper {
box-sizing: border-box;
line-height: 20px;
border-radius: 3px;
padding-left: 0
}
.required {
margin-left: 10px;
position: relative;
&::before {
content: " ";
border: 2px solid #F44336;
width: 0;
height: 12px;
position: absolute;
top: 9px;
left: -8px;
}
span.u-input-group {
display: block
}
}
.required-icon {
position: absolute;
top: 2px;
color: #F44336;
font-size: 20px;
}
.ref-input-wrap {
width: 160px !important;
}
}
.u-editable-table-tp {
.tp-content {
color: #F44336;
}
}

View File

@ -1,6 +1,7 @@
/**
*
* @title 单元格编辑
* @parent 编辑 Editor
* @description 可以对单元格进行编辑的表格示例中给出输入框下拉框参照的编辑模式以及两类校验通过对 coloums 配置 render 属性实现渲染不同格式的编辑态单元格
*
*/
@ -17,6 +18,7 @@ class StringEditCell extends Component {
value: this.props.value,
editable: false
};
this.editWarp = React.createRef();
}
commitChange = () => {
@ -38,6 +40,7 @@ class StringEditCell extends Component {
};
handleChange = e => {
if (e.target.value === "") this.editWarp.className += " verify-cell";
this.setState({ value: e.target.value });
};
@ -46,7 +49,7 @@ class StringEditCell extends Component {
return (
<div className="editable-cell">
{editable ? (
<div className="editable-cell-input-wrapper">
<div ref={el => this.editWarp = el} className="editable-cell-input-wrapper">
<input
className={value ? "u-form-control" : "u-form-control error"}
autoFocus
@ -59,10 +62,10 @@ class StringEditCell extends Component {
{value === "" ? (
<Tooltip
inverse
className="tp-0502"
className="u-editable-table-tp"
placement="bottom"
overlay={
<div className="help-tip">
<div className="tp-content">
{"请输入" + this.props.colName}
</div>
}
@ -489,7 +492,7 @@ class Demo0502 extends Component {
render() {
return (
<div className="demo0502">
<div className="demo0502 u-editable-table">
<Table data={this.state.dataSource} columns={this.columns} />
</div>
);

View File

@ -1,21 +1,28 @@
.demo0502 {
.u-editable-table .u-table {
.u-table-row {
td {
padding: 5px 8px;
input.error {
border-color: #F44336;
input {
padding-left: 5px;
font-size: 12px;
&.error {
border-color: #F44336;
}
}
}
.editable-cell {
height: 30px;
}
&-hover {
.editable-cell-text-wrapper {
line-height: 18px;
border: 1px solid #c1c7d0;
line-height: 19px;
}
}
.u-form-control,
.u-select-selection {
height: 30px;
@ -26,11 +33,14 @@
box-sizing: border-box;
line-height: 20px;
border-radius: 3px;
}
&:hover {
line-height: 18px;
border: 1px solid #a5adba;
}
.editable-cell-input-wrapper {
padding-right: 0;
}
.verify-cell {
padding-right: 25px !important;
}
.require {
@ -41,16 +51,8 @@
}
}
.help-tip {
color: #F44336;
}
.tp-0502 {
.tooltip-arrow {
border-bottom-color: #F44336 !important;
.u-editable-table-tp {
.tp-content {
color: #F44336;
}
.tooltip-inner {
border-color: #F44336 !important;
}
}
}

File diff suppressed because one or more lines are too long

112
dist/demo.css vendored
View File

@ -627,6 +627,24 @@
position: absolute;
top: -1000px; }
.u-editable-table .u-table .u-table-row-hover .editable-cell-text-wrapper {
padding-left: 4px;
border: 1px solid #c1c7d0; }
.u-editable-table .u-table .editable-cell-text-wrapper:hover {
padding-left: 4px;
border: 1px solid #a5adba; }
.u-editable-table .u-table .editable-cell-input-wrapper:focus {
outline: none; }
.u-editable-table-tp .tooltip-arrow {
top: 1px !important;
border-bottom-color: #F44336 !important; }
.u-editable-table-tp .tooltip-inner {
border-color: #F44336 !important; }
.selected {
background: #FFF7E7; }
@ -654,52 +672,90 @@
padding-top: 0px;
padding-bottom: 0px; }
.demo0501 .u-table-row {
height: 58px; }
.demo0501 .u-row-hover .opt-btns button {
.demo0501 .u-table .u-row-hover .opt-btns button {
border-radius: 5px; }
.demo0501 .u-row-hover .opt-btns button:first-child {
.demo0501 .u-table .u-row-hover .opt-btns button:first-child {
margin-right: 10px; }
.demo0502 .u-table-row td {
.demo0501 .u-table .u-table-row td {
padding: 5px 8px; }
.demo0502 .u-table-row td input.error {
border-color: #F44336; }
.demo0501 .u-table .u-table-row td input {
font-size: 12px;
padding-left: 5px; }
.demo0502 .u-table-row .editable-cell {
.demo0501 .u-table .u-table-row .u-form-control,
.demo0501 .u-table .u-table-row .u-select-selection {
height: 30px; }
.demo0502 .u-table-row-hover .editable-cell-text-wrapper {
line-height: 18px;
border: 1px solid #c1c7d0; }
.demo0502 .u-table-row .u-form-control,
.demo0502 .u-table-row .u-select-selection {
height: 30px; }
.demo0502 .editable-cell-text-wrapper {
.demo0501 .u-table .editable-cell-text-wrapper {
box-sizing: border-box;
line-height: 20px;
border-radius: 3px; }
.demo0502 .editable-cell-text-wrapper:hover {
line-height: 18px;
border: 1px solid #a5adba; }
border-radius: 3px;
padding-left: 0; }
.demo0502 .require {
.demo0501 .u-table .required {
margin-left: 10px;
position: relative; }
.demo0501 .u-table .required::before {
content: " ";
border: 2px solid #F44336;
width: 0;
height: 12px;
position: absolute;
top: 9px;
left: -8px; }
.demo0501 .u-table .required span.u-input-group {
display: block; }
.demo0501 .u-table .required-icon {
position: absolute;
top: 2px;
color: #F44336;
font-size: 20px; }
.help-tip {
.demo0501 .u-table .ref-input-wrap {
width: 160px !important; }
.u-editable-table-tp .tp-content {
color: #F44336; }
.tp-0502 .tooltip-arrow {
border-bottom-color: #F44336 !important; }
.u-editable-table .u-table .u-table-row td {
padding: 5px 8px; }
.u-editable-table .u-table .u-table-row td input {
padding-left: 5px;
font-size: 12px; }
.u-editable-table .u-table .u-table-row td input.error {
border-color: #F44336; }
.tp-0502 .tooltip-inner {
border-color: #F44336 !important; }
.u-editable-table .u-table .u-table-row .editable-cell {
height: 30px; }
.u-editable-table .u-table .u-table-row-hover .editable-cell-text-wrapper {
line-height: 19px; }
.u-editable-table .u-table .u-table-row .u-form-control,
.u-editable-table .u-table .u-table-row .u-select-selection {
height: 30px; }
.u-editable-table .u-table .editable-cell-text-wrapper {
box-sizing: border-box;
line-height: 20px;
border-radius: 3px; }
.u-editable-table .u-table .editable-cell-input-wrapper {
padding-right: 0; }
.u-editable-table .u-table .verify-cell {
padding-right: 25px !important; }
.u-editable-table .u-table .require {
position: absolute;
top: 2px;
color: #F44336;
font-size: 20px; }
.u-editable-table-tp .tp-content {
color: #F44336; }
th .drop-menu .uf {
font-size: 12px;

2
dist/demo.css.map vendored

File diff suppressed because one or more lines are too long

760
dist/demo.js vendored

File diff suppressed because one or more lines are too long

2
dist/demo.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -959,3 +959,36 @@ $icon-color:#505F79;
position: absolute;
top:-1000px;
}
.u-editable-table .u-table {
.u-table-row-hover {
.editable-cell-text-wrapper {
padding-left: 4px;
border: 1px solid #c1c7d0;
}
}
.editable-cell-text-wrapper {
&:hover {
padding-left: 4px;
border: 1px solid #a5adba;
}
}
.editable-cell-input-wrapper {
&:focus {
outline: none;
}
}
}
.u-editable-table-tp {
.tooltip-arrow {
top: 1px !important;
border-bottom-color: #F44336 !important;
}
.tooltip-inner {
border-color: #F44336 !important;
}
}