ac-grids
Version:
AcGrids ui component for react
444 lines (416 loc) • 14.3 kB
JavaScript
import React, {Component} from "react";
import PropTypes from 'prop-types';
import AcGird from "./AcGrids";
import RenderColumn from './RenderColumn';
import isequal from 'lodash.isequal';
import cloneDeep from 'lodash.clonedeep';
import ReactDOM from 'react-dom'
import Icon from 'bee-icon';
import ButtonGroup from 'bee-button-group';
import Tooltip from 'bee-tooltip';
import Btns from 'ac-btns';
const propTypes = {
onChange:PropTypes.func,//数据改变回调
clsfix:PropTypes.string,
onOpenChange:PropTypes.func,//展开收起回调
title:PropTypes.string,
disabled:PropTypes.bool,//是否可编辑
onDel:PropTypes.func,//删除的回调
defaultOpen:PropTypes.bool,//默认是否打开
showIndex:PropTypes.bool,//是否显示序号列
excludeKeys:PropTypes.array,//粘贴时不需要粘贴的key值合计
}
const defaultProps = {
title:'标题',
clsfix:'ac-edit-grid',
data: [],
columns:[],
onChange:()=>{},
onOpenChange:()=>{},
onDel:()=>{},
showIndex:true,
excludeKeys:[],
getSelectedDataFunc:()=>{},
};
class EditGrid extends Component {
constructor(props) {
super(props);
this.state={
columns:props.columns,
data:props.data||[],
selectData:[],//选中的数据
selectDataIds:[],//记录选中数据的id,id在这里生成,componentWillReceiveProps更新data时,设置选中的数据
copying:false,//是否正在拷贝
open:props.defaultOpen||true,//默认展开收起
isMax:false,//是否最大化了
defaultValueKeyValue:{},//带默认值的key,value键值对
}
this.selectDataId = 1;
this.errors = {};
}
componentWillMount(){
this.setDataColumn(this.props.disabled,this.state.columns,this.state.data)
}
onValidate=(errors,filed,fileds,index)=>{
let current = this.errors[index]||{};
if(errors){
current[filed] = errors[0].message;
}else{
delete current[filed];
}
this.errors[index] = current;
this.props.onValidate&&this.props.onValidate(this.errors);
}
validate = ()=>{
if(Object.keys(this.errors).length){
return this.errors;
}else{
return null;
}
}
setDataColumn=(disabled,col,da)=>{
let columns = cloneDeep(col);
let defaultValueKeyValue = {};
columns.forEach(item => {
item.oldRender = item.render;
if(item.renderType||item.customizeRender){
if(!disabled){
if(item.required){
item.title=<span className={`${this.props.clsfix}-column-title-required`}>{item.title}</span>
}
}
if(item.filedProps&&(item.filedProps.defaultValue!=undefined))defaultValueKeyValue[item.dataIndex]=item.filedProps.defaultValue;
item.render=(text,record,index)=>{
return <RenderColumn
valueField={item.valueField}
config={item.config}
textAlign={item.textAlign}
renderType={item.renderType}
index={index}
dataIndex={item.dataIndex}
value={text}
options={item.options}
onChange={this.onChange}
validate={item.validate}
required={item.required}
pattern={item.pattern}
patternMessage={item.patternMessage}
disabled={disabled?true:item.disabled}
customizeRender={item.customizeRender}
onValidate={this.onValidate}
filedProps={item.filedProps}
record={record}
/>
}
}else{
if(typeof item.oldRender == 'function'&&((item.oldRender.toString().indexOf('colSpan')!=-1)||(item.oldRender.toString().indexOf('rowSpan')!=-1))){
item.render = item.oldRender
}else{
item.render=(text,record,index)=>{
let value = typeof item.oldRender =='function'?item.oldRender(text,record,index):text;
let placement = 'left';
if(item.textAlign)placement=item.textAlign=='center'?'bottom':item.textAlign;
return <Tooltip overlay={value} inverse placement={placement}>
<span className='ac-grid-cell'>{value}</span>
</Tooltip>
}
}
}
});
this.setState({
columns,
defaultValueKeyValue
})
//给data加index
let data = cloneDeep(da);
if(data[0]&&data[0].index==1){
}else{
data.forEach((item,index)=>{
item.index=index+1
})
this.setState({
data
})
}
}
onChange=(index,key,value)=>{
//改变data
let data = cloneDeep(this.state.data);
data[index][key]=value;
this.setState({
data:data
})
this.props.onChange(data,{
index,
key,
value
});
}
//选中数据的回调
getSelectedDataFunc=(selectData, record, index, newData)=>{
let data = this.resetChecked(this.state.data)
let selectDataIds = []
selectData.forEach((item)=>{
if(data[item.index-1]){
data[item.index-1]._checked=!data[item.index-1]._checked;
}
let id = 'selectDataId'+this.selectDataId;
data.selectDataId = id;
selectDataIds.push(id);
this.selectDataId++;
})
this.setState({
selectDataIds,
selectData,
data
})
this.props.onChange(data);
this.props.getSelectedDataFunc(selectData, record, index, newData);
}
resetChecked=(dataValue)=>{
let data = cloneDeep(dataValue);
data.forEach((item,index)=>{
item._checked=false
item.index=index+1,
item.key=index+1+''
})
return data
}
componentWillReceiveProps(nextProps){
if(!isequal(nextProps.data,this.state.data)){
let selectDataIds = this.state.selectDataIds;
nextProps.data.forEach((item,index)=>{
item.index=index+1;
if(selectDataIds.indexOf(item.selectDataId)!=-1)item._checked=true;
})
this.setState({
data:nextProps.data
})
}
if('disabled' in nextProps||(!isequal(nextProps.columns,this.state.columns))){
this.setDataColumn(nextProps.disabled,nextProps.columns,nextProps.data)
}
}
//打开关闭
open=()=>{
this.props.onOpenChange(!this.state.open)
this.setState({
open:!this.state.open
})
}
//增行
addRow=()=>{
let defaultValueKeyValue = this.state.defaultValueKeyValue;
let data = cloneDeep(this.state.data);
let length = data.length;
let obj = cloneDeep(data[0]||{});
for(let attr in obj){
if(attr=='index'){
obj.index=length+1;
}else if(attr=='key'){
obj.key=`${length+1}`;
}else{
obj[attr] = ''
}
}
for(let attr in defaultValueKeyValue ){
obj[attr]=defaultValueKeyValue[attr];
}
data.push(obj)
this.props.onChange(data);
this.setState({
data
})
}
//删行
delRow=()=>{
let {selectData} = this.state;
let data = cloneDeep(this.state.data)
data.splice(selectData[0].index-1,selectData.length);
data = this.resetChecked(data)
this.setState({
data,
selectData:[]
})
this.props.onChange(data)
this.props.onDel(selectData)
}
//复制行
copyRow=()=>{
let copyData = [];
let data = cloneDeep(this.state.data);
data.forEach(item=>{
if(item._checked)copyData.push(item)
})
this.setState({
copying:true,
selectData:copyData
})
}
//粘贴至末行
copyToEnd=()=>{
let {selectData,data} = this.state;
selectData.forEach((item,index)=>{
item.index = data.length+index+1;
item.key = data.length+index+1+'';
item._checked = false;
this.props.excludeKeys.forEach(it=>{
delete item[it];
})
})
data = data.concat(selectData);
data = this.resetChecked(data)
this.setState({
data,
copying:false,
selectData:[]
})
this.props.onChange(data)
}
//取消复制
cancelCopy=()=>{
this.setState({
copying:false,
selectData:[]
})
}
//最大化
max=()=>{
this.setState({
isMax:!this.state.isMax
})
}
//行hover
onRowHover = (index,record) => {
this.currentIndex = index;
}
//粘贴至此处按钮
hoverContent=()=>{
if(this.state.copying){
return <span onClick={this.copyToHere} className='copy-to-here'>粘贴至此</span>
}else{
return ''
}
}
//粘贴至此处
copyToHere=()=>{
let index = this.currentIndex;
let data = cloneDeep(this.state.data);
let selectData = this.state.selectData;
selectData.forEach((item,i)=>{
item._checked=false
item.index = i+index+1;
item.key = i+index+1+'';
})
data.splice(index,0,...selectData);
data = this.resetChecked(data)
this.setState({
data,
copying:false,
selectData:[]
})
this.props.onChange(data)
}
renderDom=()=>{
const { exportData, clsfix,title, data:propsData,columns:cl,disabled, ...otherProps } = this.props;
let { data,open,columns,copying,isMax } = this.state;
let _exportData = exportData || data;
let btnsObj = {}
if(isMax){
btnsObj= {
addRow:{
onClick:this.addRow,
disabled:disabled
},
delRow:{
onClick:this.delRow,
disabled:this.state.selectData==0||disabled
},
copyRow:{
onClick:this.copyRow,
disabled:this.state.selectData==0||disabled
},
min:{
onClick:this.max
}
}
}else{
btnsObj= {
addRow:{
onClick:this.addRow,
disabled:disabled
},
delRow:{
onClick:this.delRow,
disabled:this.state.selectData==0||disabled
},
copyRow:{
onClick:this.copyRow,
disabled:this.state.selectData==0||disabled
},
max:{
onClick:this.max
}
}
}
if(copying){
btnsObj={
copyToEnd:{
onClick:this.copyToEnd
},
cancel:{
onClick:this.cancelCopy
}
}
}
return (
<div className={`${clsfix} ${disabled?'disabled':''} ${isMax?'max':''}`}>
<div className={`${clsfix}-panel ${open?'':'close'}`}>
<span onClick={this.open}>
<span className={`${clsfix}-panel-icon`}>
{
open?<Icon type='uf-triangle-down'/>:<Icon type='uf-triangle-right'/>
}
</span>
<span className={`${clsfix}-panel-title`}>
{title}
</span>
</span>
{
open?<span className={`${clsfix}-panel-btns`}>
<ButtonGroup>
<Btns btns={btnsObj}/>
</ButtonGroup>
</span>:''
}
</div>
<div className={`${clsfix}-inner ${open?'show':'hide'} ${isMax?'max':''}`}>
<AcGird
{...otherProps}
noReplaceColumns={true}
columns = {columns}
data={data}
exportData={_exportData}
paginationObj='none'
getSelectedDataFunc={this.getSelectedDataFunc}
hoverContent={this.hoverContent}
onRowHover={this.onRowHover}
autoCheckedByClickRows = {false}
/>
</div>
</div>
)
}
render() {
return (
<span>
{
this.state.isMax?ReactDOM.createPortal(this.renderDom(),document.querySelector('body')):this.renderDom()
}
</span>
)
}
}
EditGrid.defaultProps = defaultProps;
EditGrid.propTypes = propTypes;
export default EditGrid;