UNPKG

ac-gridcn

Version:
939 lines (896 loc) 34 kB
import React, { Component,Fragment } from "react"; import BeeGrid from "bee-complex-grid"; import Btns from 'ac-btns'; import ButtonGroup from 'bee-button-group'; import cloneDeep from 'lodash.clonedeep'; import Icon from 'bee-icon'; import Modal from 'bee-modal'; import isequal from 'lodash.isequal'; import RowFieldModel from './FieldModel'; //文本输入组件 import TextField from './RowField/TextField'; //下拉选择组件 import SelectField from './RowField/SelectField'; //数值选择组件 import NumberField from './RowField/NumberField'; //年份选择组件 import YearField from './RowField/YearField'; //日期组件 import DateField from './RowField/DateField'; import AcTips from 'ac-tips'; import classnames from 'classnames'; import { gridDefalutProps,paginationDefaultProps } from './defaultProps' const defaultProps = { data: [], excludeKeys:[], delRow:()=>{},//删除回调 getSelectedDataFunc:()=>{},//选中回调 save:()=>{},//保存回调 clsfix:'ac-gridcn', onChange:()=>{},//数据改变回调 hideSave:false,//是否隐藏保存按钮 isEdit:false,//是否需要表格编辑 powerBtns:['addRow','update','delRow','copyRow','export','min','max','cancel','save','copyToEnd'], forcePowerBtns:['cancel','save'],//不受按钮权限控制的按钮 }; class Grid extends Component { constructor(props) { super(props); this.state={ copying:false,//是否正在拷贝 open:props.defaultOpen!=undefined?props.defaultValue:true,//默认展开收起 isMax:false,//是否最大化了 columns:props.columns, data:props.data, defaultValueKeyValue:{},//每个单元格的默认值 isMax:false,//是否最大化了 selectData:[],//选中的数据 allEditing:false,//是否正在修改所有数据 adding:false,//是否正在新增 addNum:0,//新增的条数 canExport:false, pasting:false,//正在粘贴 rowField:{ show:false, } } this.oldColumns = props.columns; this.selectList = [];//选中的数据 this.allData = [];//表格所有数据 this.errors = {};//整个表格的校验错误信息 this.selectKeyData = {};//存select类型字段 key:data(下拉列表) } /** *获取保存的column和table上的属性 * */ getColumnsAndTablePros = () => { return this.grid.getColumnsAndTablePros(); }; /** * * 重置grid的columns */ resetColumns = () => { this.grid.resetColumns(this.oldColumns); }; exportExcel = () => { this.grid.exportExcel(); }; getValue=(text,props)=>{ let { renderType,fieldProps } = props; let { data=[],defaultValue } = fieldProps; let value = defaultValue!=undefined?defaultValue:''; if(renderType&&renderType=='select'){ data.forEach(item => { if(item.value==text){ value = item.key } }); }else{ value = text; } return value; } componentWillMount(){ this.setColumn(this.props.columns) this.setData(this.props.data,this.props.exportData) } componentWillReceiveProps(nextProps){ if('data' in nextProps&&(!isequal(nextProps.data,this.state.data))){ this.setData(nextProps.data,nextProps.exportData); } } setColumn=(cl)=>{ let columns = cloneDeep(cl); let defaultValueKeyValue = {}; columns.forEach(item => { let { renderType,//渲染类型 input/inputNumber/select/datepicker/year fieldProps={},//传给`field`的属性 dataIndex, render:oldRender, component,//参照组件 ...other } = item; if(!oldRender)oldRender=text=>text; if(renderType){ if(item.required){ item.className="required" } if(fieldProps.defaultValue!=undefined){ defaultValueKeyValue[dataIndex]=fieldProps.defaultValue; }else{ defaultValueKeyValue[dataIndex]=''; } switch(renderType){ case 'input': item.render=(text,record,index)=>{ return ( record._edit?<TextField {...other} fieldProps={fieldProps} index = {index} value = {oldRender&&oldRender(text,record,index)} field = {item.dataIndex} onChange = {this.onChange} status = {record._status} onValidate={this.onValidate} />:<div>{oldRender&&oldRender(text,record,index)}</div> ) } break; case 'inputNumber': item.render=(text,record,index)=>{ let value = text; return ( record._edit?<NumberField {...other} fieldProps={fieldProps} index = {index} value = {value} field = {item.dataIndex} onChange = {this.onChange} status = {record._status} onValidate={this.onValidate} />:<div>{oldRender&&oldRender(value,record,index)}</div> ) } break; case 'select': item.render=(text,record,index)=>{ let value = this.getValue(text,item); if(index==0&&(!this.selectKeyData[item.dataIndex])){ this.selectKeyData[item.dataIndex] = fieldProps.data; } return ( record._edit?<SelectField {...other} fieldProps={fieldProps} index = {index} value = {text+''} field = {item.dataIndex} onChange = {this.onChange} status = {record._status} onValidate={this.onValidate} />:<div>{oldRender&&oldRender(value,record,index)}</div> ) } break; case 'datepicker': item.render=(text,record,index)=>{ return ( record._edit?<DateField {...other} fieldProps={fieldProps} index = {index} value = {oldRender&&oldRender(text,record,index)} field = {item.dataIndex} onChange = {this.onChange} status = {record._status} onValidate={this.onValidate} />:<div>{oldRender&&oldRender(text,record,index)}</div> ) } break; case 'year': item.render=(text,record,index)=>{ return ( record._edit?<YearField {...other} fieldProps={fieldProps} index = {index} value = {oldRender&&oldRender(text,record,index)} field = {item.dataIndex} onChange = {this.onChange} status = {record._status} onValidate={this.onValidate} />:<div>{oldRender&&oldRender(text,record,index)}</div> ) } break; case 'refer': item.render=(text,record,index)=>{ let displayName = fieldProps['displayname'];//'name'; displayName = displayName?displayName:'name'; if(fieldProps&&fieldProps.displayName)name=fieldProps.displayName; let value = text; if(text && record._edit === false){ value = text instanceof Array?text.map(da=>{return da[displayName]}):null; value = value ?value.join(","):null; value = !value && text instanceof Object?text[displayName]:value; } return ( record._edit?<span> { React.cloneElement(component,{ ...other, ...fieldProps, index : index, value , field :item.dataIndex, onChange :this.onChange, status :record._status, onValidate:this.onValidate, text:item.listKey?record[item.listKey]:value, rowFieldPop:this.props.rowFieldPop }) } </span>:<div>{item.listKey?record[item.listKey]:value}</div> ) } //参照需要根据valueField 来显示内容 if(fieldProps.defaultValue!=undefined && this.props.rowFieldPop){ defaultValueKeyValue[dataIndex]=fieldProps.defaultValue[fieldProps.valueField]; } break; } } }); this.setState({ columns, defaultValueKeyValue }) this.oldColumns = columns; } setData=(da,exportData)=>{ let data = cloneDeep(da); let selectData = []; data.forEach((item,index)=>{ item._index = index; if(item._checked)selectData.push(item) }) this.allData = data; this.selectList = selectData; this.setState({ data, selectData, canExport:false, },()=>{ if(exportData&&(isequal(this.props.exportData,exportData))){ }else if(exportData&&(!isequal(this.props.exportData,exportData))){ this.getExportData(exportData) }else if(!exportData){ this.getExportData(data) } }) } onValidate=(filed,errors,index)=>{ if(filed=='_delete'){ delete this.errors[index] } let current = this.errors[index]||{}; if(errors){ current[filed] = errors[filed][0].message; }else{ delete current[filed]; } if(Object.keys(current).length==0){ delete this.errors[index]; }else{ this.errors[index] = current; } } validate = ()=>{ if(Object.keys(this.errors).length){ return this.errors; }else{ return null; } } //校验选中数据 validateSelect = () =>{ if(Object.keys(this.errors).length){ let newError = {}; this.selectList.forEach(item=>{ if(this.errors[item._index]){ newError[item._index] = this.errors[item._index] } }) if(Object.keys(newError).length){ return newError; }else{ return null } }else{ return null; } } onChange=(field, value, index)=>{ if(!isequal(this.allData[index][field],value)){ this.allData[index]._checked = true; this.allData[index][field] = value; let selectList = []; this.allData.forEach(item=>{ if(item._checked)selectList.push(item) }) this.setState({ data:this.allData, selectData:selectList }) this.props.onChange(this.allData, field, value, index); } } //增行 addRow=()=>{ const {rowFieldPop,rowFieldRow,rowFieldDialog} = this.props; let defaultValueKeyValue = this.state.defaultValueKeyValue; let data = cloneDeep(this.state.data); let item = cloneDeep(defaultValueKeyValue); item._edit = true; item._status = 'edit'; item._checked = true; if(rowFieldPop){ this.setState({ rowField:{ show:true, title:'增行', columns:this.props.columns, itemDate:item, clsfix:this.props.clsfix, rowFieldRow:rowFieldRow?rowFieldRow:3, rowFieldDialog, // className:this.props.className, cancel:(_item)=>{this.rowFieldCancel(_item,data)} } }) return; } data.unshift(item); let selectList = []; data.forEach((item,index)=>{ if(item._checked)selectList.push(item); item._index = index; }) this.setState({ data, adding:true, addNum:this.state.addNum+1, selectData:selectList }) this.selectList = selectList; this.allData = data; this.props.onChange(data) } //弹框后新增 rowFieldCancel =(_item,data)=>{ this.state.rowField.show = false; this.setState({ rowField:{...rowField} }) if(!_item)return; let {selectData,addNum,rowField} = this.state; _item._edit = true; _item._status = 'edit'; _item._checked = true; selectData.push(_item); if(_item){ data.unshift(_item); } this.setState({ data, selectData, adding:true, addNum:addNum+1, rowField:{...rowField} }) this.props.onChange(data) this.allData = data; } //取消新增 cancelAdd=()=>{ Modal.confirm({ // title:'温馨提示', keyword:'警告', content:"数据未保存,确定离开 ?", onOk:()=> { let data = cloneDeep(this.state.data); data.splice(0,this.state.addNum); for(let i = 0;i<this.state.addNum;i++)delete this.errors[i] this.setState({ data, adding:false, addNum:0, selectData:[] }) this.selectList = []; this.props.onChange(data) }, onCancel:()=>{ }, confirmType:'two' }) } //修改 updateAll=()=>{ let data = cloneDeep(this.state.data); data.forEach(item=>{ item._edit = true;//是否编辑态 item._status = 'edit';//是否编辑态,用于显示是否编辑过 item._checked = false; }) this.setState({ data, allEditing:true, selectData:[] }) // this.props.onChange(data) this.allData = data; } //删除行 delRow=()=>{ if(this.selectList.length<=0){ AcTips.create({ type:'warning', content:"请先选择数据" }) }else{ Modal.confirm({ // title:'温馨提示', keyword:'删除', content:"单据删除后将不能恢复。", onOk:()=> { let data = cloneDeep(this.state.data); this.selectList.forEach((item,index)=>{ data.splice(item._index-index,1); this.onValidate('_delete','',item._index) }) data = this.resetChecked(data,true); this.allData = data; this.props.onChange(data) this.setState({ data },()=>{ this.props.delRow(this.selectList,data); }) }, onCancel:()=>{ }, confirmType:'two' }) } } //复制行 copyRow=()=>{ if(this.selectList.length<=0){ AcTips.create({ type:'warning', content:"请先选择数据" }) }else{ let copyData = []; let data = cloneDeep(this.state.data); data.forEach(item=>{ if(item._checked)copyData.push(item) }) this.setState({ copying:true, selectData:copyData }) } } //保存数据 save=()=>{ let selectList = []; this.allData.forEach(item=>{ if(item._checked)selectList.push(item) }) if(selectList.length<=0){ AcTips.create({ type:'warning', content:"请先选择数据" }) }else if(this.validate()){ AcTips.create({ type:'warning', content:"数据校验失败" }) console.log(this.errors) }else{ let saveData = this.props.save(selectList); if(saveData !== false) { let data = cloneDeep(this.state.data); data.forEach(item=>{ item._edit = false;//是否编辑态 item._status = '';//是否编辑态,用于显示是否编辑过 item._checked = false; }) this.setState({ data, adding:false, allEditing:false, selectData:[], addNum:0, pasting:false }) this.allData = data; } // this.props.onChange(data) } } //取消复制 cancelCopy=()=>{ this.setState({ copying:false, selectData:[] }) } //粘贴至末行 copyToEnd=()=>{ let { data } = this.state; let selectData = this.selectList; selectData.forEach((item,index)=>{ item._edit = true; item._status = 'edit'; item._checked = true; item._needChecked = true; this.props.excludeKeys.forEach(it=>{ delete item[it]; }) }) data = data.concat(selectData); data = this.resetChecked(data,true) this.setState({ data, copying:false, selectData, pasting:true, pasteOldData:this.state.data }) this.props.onChange(data) this.allData = data; } //粘贴至此处 copyToHere=()=>{ let currentIndex = this.currentIndex;//从0开始 let data = cloneDeep(this.state.data); let selectData = this.selectList; selectData.forEach((item,index)=>{ item._edit = true; item._status = 'edit'; item._checked = true; item._needChecked = true; this.props.excludeKeys.forEach(it=>{ delete item[it]; }) }) data.splice(currentIndex,0,...selectData); data = this.resetChecked(data,true) this.setState({ data, copying:false, pasting:true, pasteOldData:this.state.data }) this.props.onChange(data) this.allData = data; } //取消粘贴 cancelPaste=()=>{ let data = this.state.pasteOldData; this.setState({ data, copying:false, pasting:false }) this.allData = data; } //最大化、最小化 max=()=>{ if(!this.state.isMax){ window.scrollTo(0,0) } this.setState({ isMax:!this.state.isMax }) } //修改取消 cancelEdit=()=>{ Modal.confirm({ // title:'温馨提示', keyword:'警告', content:"数据未保存,确定离开?", onOk:()=> { // let data = cloneDeep(this.state.data); let data = cloneDeep(this.props.data); // 取消时取props中的data重置数据 data.forEach(item=>{ item._edit = false;//是否编辑态 item._status = '';//是否编辑态,用于显示是否编辑过 item._checked = false; }) this.setState({ data, allEditing:false, selectData:[], errors:{} }) // this.props.onChange(data) this.allData = data; this.errors = {}; this.selectList = []; }, onCancel:()=>{ }, confirmType:'two' }) } //全不选 resetChecked=(dataValue,needIndex)=>{ let data = cloneDeep(dataValue); data.forEach((item,index)=>{ if(item._needChecked){ delete item._needChecked; }else{ item._checked=false; } if(needIndex)item._index = index }) // this.props.onChange(data) return data; } //行hover onRowHover = (index,record) => { this.currentIndex = index; } //粘贴至此处按钮 hoverContent=()=>{ if(this.state.copying){ return <Btns btns={{ copyToHere:{ onClick: this.copyToHere } }}/> }else{ return '' } } //数据选择回调 getSelectedDataFunc=(selectList,record,index,newData)=>{ this.selectList = selectList; let data = cloneDeep(this.state.data) if (index != undefined) { data[index]['_checked'] = !data[index]['_checked']; } else {//点击了全选 if (selectList.length > 0) {//全选 data.map(item => { if (!item['_disabled']) { item['_checked'] = true } }); } else {//反选 data.map(item => { if (!item['_disabled']) { item['_checked'] = false } }); } } this.setState({ data:data, selectData:selectList }) this.allData = data; this.props.getSelectedDataFunc(selectList,record,index,newData); } //打开关闭 open=()=>{ this.setState({ open:!this.state.open }) } //编辑表格导出数据select类型单独处理 getExportData=(data)=>{ let exportData = cloneDeep(data); exportData.forEach(item=>{ for(let attr in this.selectKeyData){ item[attr] = this.getValue(item[attr],{ renderType:'select', fieldProps:{ data:this.selectKeyData[attr] } }) } }) this.exportData = exportData; this.setState({ canExport:true }) } renderDom=()=>{ let { copying,isMax,columns,data,allEditing,adding,open,selectData,canExport,pasting } = this.state; const { clsfix,paginationObj, exportData,disabled,title,hideSave, isEdit,powerBtns,forcePowerBtns, ...otherProps } = this.props; let _paginationObj ='none'; if(paginationObj!='none'){ _paginationObj = {...paginationDefaultProps, ...paginationObj}; _paginationObj.disabled = paginationObj.disabled !== undefined ? paginationObj.disabled : (data.length === 0||allEditing||copying||adding); if((data.length === 0||allEditing||copying||adding)){ _paginationObj.disabled = true; } } let btns1 = {}; let btnSave = {}; btns1= { addRow:{ onClick:this.addRow, disabled:copying||allEditing||pasting||disabled }, update:{ onClick:this.updateAll, disabled:data.length==0||copying||allEditing||adding||pasting||disabled }, delRow:{ onClick:this.delRow, disabled:pasting||copying||selectData.length==0||disabled }, copyRow:{ onClick:this.copyRow, disabled:copying||adding||pasting||allEditing||selectData.length==0||disabled } } let btnsObj = { min:{ onClick:this.max } }; if(!isMax){ delete btnsObj.min; btnsObj.max = { onClick:this.max }; } if(allEditing){ if(!hideSave){ btnSave.save = { onClick:this.save, disabled:selectData.length==0||disabled } } btnSave.cancel = { onClick:this.cancelEdit } }else if(adding){ if(!hideSave){ btnSave.save = { onClick:this.save, disabled:selectData.length==0||disabled } } btnSave.cancel = { onClick:this.cancelAdd } }else if(copying){ delete btns1.copyRow; btns1.copyToEnd = { onClick:this.copyToEnd } btnSave = { cancel:{ onClick:this.cancelCopy } } }else if(pasting){ if(!hideSave){ btnSave.save = { onClick:this.save, disabled:selectData.length==0||disabled } } btnSave.cancel = { onClick:this.cancelPaste } } let gridOptions={ syncHover:true, autoCheckedByClickRows:false, multiSelect:{ type:"checkbox" }, showFilterMenu:false, ...otherProps, data:data, columns:columns, exportData:this.exportData, paginationObj:_paginationObj, ref:el => this.grid = el, hoverContent:this.hoverContent, getSelectedDataFunc:this.getSelectedDataFunc, onRowHover:this.onRowHover, } gridOptions = Object.assign(gridDefalutProps,gridOptions); return ( <Fragment> <div className={`${clsfix} ${disabled?'disabled':''} ${gridOptions.headerScroll?'header-scroll':''} ${isMax?'max':''} ${adding||allEditing||copying||pasting?'isEdit':''}`}> { typeof title=='string'?<div className={`${clsfix}-panel ${open?'':'close'}`}> <span onClick={this.open} className={`${clsfix}-panel-header`}> <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?<div className={`${clsfix}-panel-btns`}> <ButtonGroup> <Btns btns={btns1} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/> </ButtonGroup> <Btns btns={btnSave} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/> <Btns btns={{ export: { onClick: () => { this.grid.exportExcel(); }, disabled:(!canExport)||allEditing||adding||disabled }, }} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/> <Btns btns={btnsObj} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/> </div>:'' } </div>: <div className={`${clsfix}-panel`}> <div></div> <div className='ac-gridcn-panel-btns'> <ButtonGroup> <Btns btns={btns1} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/> </ButtonGroup> <Btns btns={btnSave} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/> <Btns btns={{ export: { onClick: () => { this.grid.exportExcel(); }, disabled:(!canExport)||allEditing||adding||disabled }, }} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/> <Btns btns={btnsObj} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/> </div> </div> } { typeof title=='string'?<div className={`${clsfix}-inner ${open?'show':'hide'} ${isMax?'max':''}`}> <BeeGrid {...gridOptions}/> </div>:<BeeGrid {...gridOptions}/> } </div> </Fragment> ); } render() { const {rowField} = this.state; return ( <span> { this.state.isMax?ReactDOM.createPortal(this.renderDom(),document.querySelector('body')):this.renderDom() } { rowField.show?<RowFieldModel {...rowField} />:null } </span> ) } } Grid.defaultProps = defaultProps; export default Grid;