UNPKG

30.6 kBJavaScriptView Raw
1import React, { Component,Fragment } from "react";
2import BeeGrid from "bee-complex-grid";
3import Btns from 'ac-btns';
4import ButtonGroup from 'bee-button-group';
5import cloneDeep from 'lodash.clonedeep';
6import Icon from 'bee-icon';
7import Modal from 'bee-modal';
8import isequal from 'lodash.isequal';
9//文本输入组件
10import TextField from './RowField/TextField';
11//下拉选择组件
12import SelectField from './RowField/SelectField';
13//数值选择组件
14import NumberField from './RowField/NumberField';
15//年份选择组件
16import YearField from './RowField/YearField';
17//日期组件
18import DateField from './RowField/DateField';
19
20import AcTips from 'ac-tips';
21
22import classnames from 'classnames';
23
24import { gridDefalutProps,paginationDefaultProps } from './defaultProps'
25
26
27
28const defaultProps = {
29 data: [],
30 excludeKeys:[],
31 delRow:()=>{},//删除回调
32 getSelectedDataFunc:()=>{},//选中回调
33 save:()=>{},//保存回调
34 clsfix:'ac-gridcn',
35 onChange:()=>{},//数据改变回调
36 hideSave:false,//是否隐藏保存按钮
37 isEdit:false,//是否需要表格编辑
38 powerBtns:['addRow','update','delRow','copyRow','export','min','max','cancel','save','copyToEnd'],
39 forcePowerBtns:['cancel','save'],//不受按钮权限控制的按钮
40};
41
42class Grid extends Component {
43 constructor(props) {
44 super(props);
45 this.state={
46 copying:false,//是否正在拷贝
47 open:props.defaultOpen!=undefined?props.defaultValue:true,//默认展开收起
48 isMax:false,//是否最大化了
49 columns:props.columns,
50 data:props.data,
51 defaultValueKeyValue:{},//每个单元格的默认值
52 isMax:false,//是否最大化了
53 selectData:[],//选中的数据
54 allEditing:false,//是否正在修改所有数据
55 adding:false,//是否正在新增
56 addNum:0,//新增的条数
57 canExport:false,
58 pasting:false,//正在粘贴
59 }
60 this.oldColumns = props.columns;
61 this.selectList = [];//选中的数据
62 this.allData = [];//表格所有数据
63 this.errors = {};//整个表格的校验错误信息
64 this.selectKeyData = {};//存select类型字段 key:data(下拉列表)
65 }
66
67 /**
68 *获取保存的column和table上的属性
69 *
70 */
71 getColumnsAndTablePros = () => {
72 return this.grid.getColumnsAndTablePros();
73 };
74 /**
75 *
76 * 重置grid的columns
77 */
78 resetColumns = () => {
79 this.grid.resetColumns(this.oldColumns);
80 };
81
82 exportExcel = () => {
83 this.grid.exportExcel();
84 };
85 getValue=(text,props)=>{
86 let { renderType,fieldProps } = props;
87 let { data=[],defaultValue } = fieldProps;
88 let value = defaultValue!=undefined?defaultValue:'';
89 if(renderType&&renderType=='select'){
90 data.forEach(item => {
91 if(item.value==text){
92 value = item.key
93 }
94 });
95 }else{
96 value = text;
97 }
98 return value;
99 }
100
101 componentWillMount(){
102 this.setColumn(this.props.columns)
103 this.setData(this.props.data,this.props.exportData)
104 }
105 componentWillReceiveProps(nextProps){
106 if('data' in nextProps&&(!isequal(nextProps.data,this.state.data))){
107 this.setData(nextProps.data,nextProps.exportData);
108 }
109 }
110 setColumn=(cl)=>{
111 let columns = cloneDeep(cl);
112 let defaultValueKeyValue = {};
113 columns.forEach(item => {
114 let {
115 renderType,//渲染类型 input/inputNumber/select/datepicker/year
116 fieldProps={},//传给`field`的属性
117 dataIndex,
118 render:oldRender,
119 component,//参照组件
120 ...other
121 } = item;
122 if(!oldRender)oldRender=text=>text;
123 if(renderType){
124 if(item.required){
125 item.className="required"
126 }
127 if(fieldProps.defaultValue!=undefined){
128 defaultValueKeyValue[dataIndex]=fieldProps.defaultValue;
129 }else{
130 defaultValueKeyValue[dataIndex]='';
131 }
132 switch(renderType){
133 case 'input':
134 item.render=(text,record,index)=>{
135 return (
136 record._edit?<TextField
137 {...other}
138 fieldProps={fieldProps}
139 index = {index}
140 value = {oldRender&&oldRender(text,record,index)}
141 field = {item.dataIndex}
142 onChange = {this.onChange}
143 status = {record._status}
144 onValidate={this.onValidate}
145 />:<div>{oldRender&&oldRender(text,record,index)}</div>
146 )
147 }
148 break;
149 case 'inputNumber':
150 item.render=(text,record,index)=>{
151 let value = text;
152 return (
153 record._edit?<NumberField
154 {...other}
155 fieldProps={fieldProps}
156 index = {index}
157 value = {value}
158 field = {item.dataIndex}
159 onChange = {this.onChange}
160 status = {record._status}
161 onValidate={this.onValidate}
162 />:<div>{oldRender&&oldRender(value,record,index)}</div>
163 )
164 }
165 break;
166 case 'select':
167 item.render=(text,record,index)=>{
168 let value = this.getValue(text,item);
169 if(index==0&&(!this.selectKeyData[item.dataIndex])){
170 this.selectKeyData[item.dataIndex] = fieldProps.data;
171 }
172 return (
173 record._edit?<SelectField
174 {...other}
175 fieldProps={fieldProps}
176 index = {index}
177 value = {text+''}
178 field = {item.dataIndex}
179 onChange = {this.onChange}
180 status = {record._status}
181 onValidate={this.onValidate}
182 />:<div>{oldRender&&oldRender(value,record,index)}</div>
183 )
184 }
185 break;
186 case 'datepicker':
187 item.render=(text,record,index)=>{
188 return (
189 record._edit?<DateField
190 {...other}
191 fieldProps={fieldProps}
192 index = {index}
193 value = {oldRender&&oldRender(text,record,index)}
194 field = {item.dataIndex}
195 onChange = {this.onChange}
196 status = {record._status}
197 onValidate={this.onValidate}
198 />:<div>{oldRender&&oldRender(text,record,index)}</div>
199 )
200 }
201 break;
202 case 'year':
203 item.render=(text,record,index)=>{
204 return (
205 record._edit?<YearField
206 {...other}
207 fieldProps={fieldProps}
208 index = {index}
209 value = {oldRender&&oldRender(text,record,index)}
210 field = {item.dataIndex}
211 onChange = {this.onChange}
212 status = {record._status}
213 onValidate={this.onValidate}
214 />:<div>{oldRender&&oldRender(text,record,index)}</div>
215 )
216 }
217 break;
218 case 'refer':
219 item.render=(text,record,index)=>{
220 let displayName = 'name';
221 if(fieldProps&&fieldProps.displayName)name=fieldProps.displayName;
222 let value = oldRender&&oldRender(text,record,index);
223 if(text&&(typeof text == 'object')&&(!record._edit)){
224 value = oldRender&&oldRender(text[displayName],record,index);
225 }
226 return (
227 record._edit?<span>
228 {
229 React.cloneElement(component,{
230 ...other,
231 ...fieldProps,
232 index : index,
233 value ,
234 field :item.dataIndex,
235 onChange :this.onChange,
236 status :record._status,
237 onValidate:this.onValidate,
238 text:item.listKey?record[item.listKey]:value
239 })
240 }
241 </span>:<div>{item.listKey?record[item.listKey]:value}</div>
242 )
243 }
244 break;
245 }
246 }
247 });
248 this.setState({
249 columns,
250 defaultValueKeyValue
251 })
252 this.oldColumns = columns;
253 }
254 setData=(da,exportData)=>{
255 let data = cloneDeep(da);
256 let selectData = [];
257 data.forEach((item,index)=>{
258 item._index = index;
259 if(item._checked)selectData.push(item)
260 })
261 this.allData = data;
262 this.selectList = selectData;
263 this.setState({
264 data,
265 selectData,
266 canExport:false,
267 },()=>{
268 if(exportData&&(isequal(this.props.exportData,exportData))){
269
270 }else if(exportData&&(!isequal(this.props.exportData,exportData))){
271 this.getExportData(exportData)
272 }else if(!exportData){
273 this.getExportData(data)
274 }
275
276 })
277
278 }
279
280 onValidate=(filed,errors,index)=>{
281 if(filed=='_delete'){
282 delete this.errors[index]
283 }
284 let current = this.errors[index]||{};
285 if(errors){
286 current[filed] = errors[filed][0].message;
287 }else{
288 delete current[filed];
289 }
290 if(Object.keys(current).length==0){
291 delete this.errors[index];
292 }else{
293 this.errors[index] = current;
294 }
295 }
296 validate = ()=>{
297 if(Object.keys(this.errors).length){
298 return this.errors;
299 }else{
300 return null;
301 }
302 }
303 //校验选中数据
304 validateSelect = () =>{
305 if(Object.keys(this.errors).length){
306 let newError = {};
307 this.selectList.forEach(item=>{
308 if(this.errors[item._index]){
309 newError[item._index] = this.errors[item._index]
310 }
311 })
312 if(Object.keys(newError).length){
313 return newError;
314 }else{
315 return null
316 }
317 }else{
318 return null;
319 }
320 }
321
322 onChange=(field, value, index)=>{
323 if(!isequal(this.allData[index][field],value)){
324 this.allData[index]._checked = true;
325 this.allData[index][field] = value;
326 let selectList = [];
327 this.allData.forEach(item=>{
328 if(item._checked)selectList.push(item)
329 })
330 this.setState({
331 data:this.allData,
332 selectData:selectList
333 })
334 this.props.onChange(this.allData);
335 }
336 }
337 //增行
338 addRow=()=>{
339 let defaultValueKeyValue = this.state.defaultValueKeyValue;
340 let data = cloneDeep(this.state.data);
341 let item = cloneDeep(defaultValueKeyValue);
342 item._edit = true;
343 item._status = 'edit';
344 item._checked = true;
345 data.unshift(item);
346 let selectList = [];
347 data.forEach((item,index)=>{
348 if(item._checked)selectList.push(item);
349 item._index = index;
350 })
351 this.setState({
352 data,
353 adding:true,
354 addNum:this.state.addNum+1,
355 selectData:selectList
356 })
357 this.selectList = selectList;
358 this.allData = data;
359 this.props.onChange(data)
360 }
361
362 //取消新增
363 cancelAdd=()=>{
364 Modal.confirm({
365 title:'温馨提示',
366 keyword:'警告',
367 content:"数据未保存,确定离开 ?",
368 onOk:()=> {
369 let data = cloneDeep(this.state.data);
370 data.splice(0,this.state.addNum);
371 for(let i = 0;i<this.state.addNum;i++)delete this.errors[i]
372 this.setState({
373 data,
374 adding:false,
375 addNum:0,
376 selectData:[]
377 })
378 this.selectList = [];
379 this.props.onChange(data)
380 },
381 onCancel:()=>{
382 },
383 confirmType:'two'
384 })
385 }
386 //修改
387 updateAll=()=>{
388 let data = cloneDeep(this.state.data);
389 data.forEach(item=>{
390 item._edit = true;//是否编辑态
391 item._status = 'edit';//是否编辑态,用于显示是否编辑过
392 item._checked = false;
393 })
394 this.setState({
395 data,
396 allEditing:true,
397 selectData:[]
398 })
399 // this.props.onChange(data)
400 this.allData = data;
401 }
402
403 //删除行
404 delRow=()=>{
405 if(this.selectList.length<=0){
406 AcTips.create({
407 type:'warning',
408 content:"请先选择数据"
409 })
410 }else{
411 Modal.confirm({
412 title:'温馨提示',
413 keyword:'删除',
414 content:"单据删除后将不能恢复。",
415 onOk:()=> {
416 let data = cloneDeep(this.state.data);
417 this.selectList.forEach((item,index)=>{
418 data.splice(item._index-index,1);
419 this.onValidate('_delete','',item._index)
420 })
421
422 data = this.resetChecked(data,true);
423 this.allData = data;
424 this.props.onChange(data)
425 this.setState({
426 data
427 },()=>{
428 this.props.delRow(this.selectList,data);
429 })
430 },
431 onCancel:()=>{
432 },
433 confirmType:'two'
434 })
435 }
436 }
437
438 //复制行
439 copyRow=()=>{
440 if(this.selectList.length<=0){
441 AcTips.create({
442 type:'warning',
443 content:"请先选择数据"
444 })
445 }else{
446 let copyData = [];
447 let data = cloneDeep(this.state.data);
448 data.forEach(item=>{
449 if(item._checked)copyData.push(item)
450 })
451 this.setState({
452 copying:true,
453 selectData:copyData
454 })
455 }
456
457 }
458
459 //保存数据
460 save=()=>{
461 let selectList = [];
462 this.allData.forEach(item=>{
463 if(item._checked)selectList.push(item)
464 })
465 if(selectList.length<=0){
466 AcTips.create({
467 type:'warning',
468 content:"请先选择数据"
469 })
470 }else if(this.validate()){
471 AcTips.create({
472 type:'warning',
473 content:"数据校验失败"
474 })
475 console.log(this.errors)
476 }else{
477 let data = cloneDeep(this.state.data);
478 data.forEach(item=>{
479 item._edit = false;//是否编辑态
480 item._status = '';//是否编辑态,用于显示是否编辑过
481 item._checked = false;
482 })
483 this.setState({
484 data,
485 adding:false,
486 allEditing:false,
487 selectData:[],
488 pasting:false
489 })
490 // this.props.onChange(data)
491 this.allData = data;
492 this.props.save(selectList);
493 }
494 }
495
496 //取消复制
497 cancelCopy=()=>{
498 this.setState({
499 copying:false,
500 selectData:[]
501 })
502 }
503 //粘贴至末行
504 copyToEnd=()=>{
505 let { data } = this.state;
506 let selectData = this.selectList;
507 selectData.forEach((item,index)=>{
508 item._edit = true;
509 item._status = 'edit';
510 item._checked = true;
511 item._needChecked = true;
512 this.props.excludeKeys.forEach(it=>{
513 delete item[it];
514 })
515 })
516 data = data.concat(selectData);
517 data = this.resetChecked(data,true)
518 this.setState({
519 data,
520 copying:false,
521 selectData,
522 pasting:true,
523 pasteOldData:this.state.data
524 })
525 this.props.onChange(data)
526 this.allData = data;
527 }
528
529 //粘贴至此处
530 copyToHere=()=>{
531 let currentIndex = this.currentIndex;//从0开始
532 let data = cloneDeep(this.state.data);
533 let selectData = this.selectList;
534 selectData.forEach((item,index)=>{
535 item._edit = true;
536 item._status = 'edit';
537 item._checked = true;
538 item._needChecked = true;
539 this.props.excludeKeys.forEach(it=>{
540 delete item[it];
541 })
542 })
543 data.splice(currentIndex,0,...selectData);
544 data = this.resetChecked(data,true)
545 this.setState({
546 data,
547 copying:false,
548 pasting:true,
549 pasteOldData:this.state.data
550 })
551 this.props.onChange(data)
552 this.allData = data;
553 }
554
555 //取消粘贴
556 cancelPaste=()=>{
557 let data = this.state.pasteOldData;
558 this.setState({
559 data,
560 copying:false,
561 pasting:false
562 })
563 this.allData = data;
564 }
565
566 //最大化、最小化
567 max=()=>{
568 if(!this.state.isMax){
569 window.scrollTo(0,0)
570 }
571 this.setState({
572 isMax:!this.state.isMax
573 })
574 }
575
576 //修改取消
577 cancelEdit=()=>{
578 Modal.confirm({
579 title:'温馨提示',
580 keyword:'警告',
581 content:"数据未保存,确定离开?",
582 onOk:()=> {
583 let data = cloneDeep(this.state.data);
584 data.forEach(item=>{
585 item._edit = false;//是否编辑态
586 item._status = '';//是否编辑态,用于显示是否编辑过
587 item._checked = false;
588 })
589 this.setState({
590 data,
591 allEditing:false,
592 selectData:[],
593 errors:{}
594 })
595 // this.props.onChange(data)
596 this.allData = data;
597 this.errors = {};
598 this.selectList = [];
599 },
600 onCancel:()=>{
601 },
602 confirmType:'two'
603 })
604 }
605 //全不选
606 resetChecked=(dataValue,needIndex)=>{
607 let data = cloneDeep(dataValue);
608 data.forEach((item,index)=>{
609 if(item._needChecked){
610 delete item._needChecked;
611 }else{
612 item._checked=false;
613 }
614 if(needIndex)item._index = index
615 })
616 // this.props.onChange(data)
617 return data;
618 }
619
620 //行hover
621 onRowHover = (index,record) => {
622 this.currentIndex = index;
623 }
624
625 //粘贴至此处按钮
626 hoverContent=()=>{
627 if(this.state.copying){
628 return <Btns btns={{
629 copyToHere:{
630 onClick: this.copyToHere
631 }
632 }}/>
633 }else{
634 return ''
635 }
636 }
637
638 //数据选择回调
639 getSelectedDataFunc=(selectList,record,index,newData)=>{
640 this.selectList = selectList;
641 let data = cloneDeep(this.state.data)
642 if (index != undefined) {
643 data[index]['_checked'] = !data[index]['_checked'];
644 } else {//点击了全选
645 if (selectList.length > 0) {//全选
646 data.map(item => {
647 if (!item['_disabled']) {
648 item['_checked'] = true
649 }
650 });
651 } else {//反选
652 data.map(item => {
653 if (!item['_disabled']) {
654 item['_checked'] = false
655 }
656 });
657 }
658 }
659 this.setState({
660 data:data,
661 selectData:selectList
662 })
663 this.allData = data;
664 this.props.getSelectedDataFunc(selectList,record,index,newData);
665 }
666 //打开关闭
667 open=()=>{
668 this.setState({
669 open:!this.state.open
670 })
671 }
672
673 //编辑表格导出数据select类型单独处理
674 getExportData=(data)=>{
675 let exportData = cloneDeep(data);
676 exportData.forEach(item=>{
677 for(let attr in this.selectKeyData){
678 item[attr] = this.getValue(item[attr],{
679 renderType:'select',
680 fieldProps:{
681 data:this.selectKeyData[attr]
682 }
683 })
684 }
685 })
686 this.exportData = exportData;
687 this.setState({
688 canExport:true
689 })
690 }
691
692
693
694 renderDom=()=>{
695 let { copying,isMax,columns,data,allEditing,adding,open,selectData,canExport,pasting } = this.state;
696 const { clsfix,paginationObj, exportData,disabled,title,hideSave, isEdit,powerBtns,forcePowerBtns, ...otherProps } = this.props;
697 let _paginationObj ='none';
698 if(paginationObj!='none'){
699 _paginationObj = {...paginationDefaultProps, ...paginationObj};
700 _paginationObj.disabled = paginationObj.disabled !== undefined
701 ? paginationObj.disabled
702 : (data.length === 0||allEditing||copying||adding);
703
704 if((data.length === 0||allEditing||copying||adding)){
705 _paginationObj.disabled = true;
706 }
707 }
708 let btns1 = {};
709 let btnSave = {};
710 btns1= {
711 addRow:{
712 onClick:this.addRow,
713 disabled:copying||allEditing||pasting||disabled
714 },
715 update:{
716 onClick:this.updateAll,
717 disabled:data.length==0||copying||allEditing||adding||pasting||disabled
718 },
719 delRow:{
720 onClick:this.delRow,
721 disabled:pasting||copying||selectData.length==0||disabled
722 },
723 copyRow:{
724 onClick:this.copyRow,
725 disabled:copying||adding||pasting||allEditing||selectData.length==0||disabled
726 }
727 }
728 let btnsObj = {
729 min:{
730 onClick:this.max
731 }
732 };
733 if(!isMax){
734 delete btnsObj.min;
735 btnsObj.max = {
736 onClick:this.max
737 };
738 }
739 if(allEditing){
740 if(!hideSave){
741 btnSave.save = {
742 onClick:this.save,
743 disabled:selectData.length==0||disabled
744 }
745 }
746 btnSave.cancel = {
747 onClick:this.cancelEdit
748 }
749 }else if(adding){
750 if(!hideSave){
751 btnSave.save = {
752 onClick:this.save,
753 disabled:selectData.length==0||disabled
754 }
755 }
756 btnSave.cancel = {
757 onClick:this.cancelAdd
758 }
759 }else if(copying){
760 delete btns1.copyRow;
761 btns1.copyToEnd = {
762 onClick:this.copyToEnd
763 }
764 btnSave = {
765 cancel:{
766 onClick:this.cancelCopy
767 }
768 }
769 }else if(pasting){
770 if(!hideSave){
771 btnSave.save = {
772 onClick:this.save,
773 disabled:selectData.length==0||disabled
774 }
775 }
776 btnSave.cancel = {
777 onClick:this.cancelPaste
778 }
779 }
780 let gridOptions={
781 syncHover:true,
782 autoCheckedByClickRows:false,
783 multiSelect:{ type:"checkbox" },
784 showFilterMenu:false,
785 ...otherProps,
786 data:data,
787 columns:columns,
788 exportData:this.exportData,
789 paginationObj:_paginationObj,
790 ref:el => this.grid = el,
791 hoverContent:this.hoverContent,
792 getSelectedDataFunc:this.getSelectedDataFunc,
793 onRowHover:this.onRowHover,
794 }
795 gridOptions = Object.assign(gridDefalutProps,gridOptions);
796 return (
797 <Fragment>
798 <div className={`${clsfix} ${disabled?'disabled':''} ${gridOptions.headerScroll?'header-scroll':''} ${isMax?'max':''} ${adding||allEditing||copying||pasting?'isEdit':''}`}>
799 {
800 typeof title=='string'?<div className={`${clsfix}-panel ${open?'':'close'}`}>
801 <span onClick={this.open} className={`${clsfix}-panel-header`}>
802 <span className={`${clsfix}-panel-icon`}>
803 {
804 open?<Icon type='uf-triangle-down'/>:<Icon type='uf-triangle-right'/>
805 }
806 </span>
807 <span className={`${clsfix}-panel-title`}>
808 {title}
809 </span>
810 </span>
811 {
812 open?<div className={`${clsfix}-panel-btns`}>
813 <ButtonGroup>
814 <Btns btns={btns1} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/>
815 </ButtonGroup>
816 <Btns btns={btnSave} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/>
817 <Btns btns={{
818 export: {
819 onClick: () => {
820 this.grid.exportExcel();
821 },
822 disabled:(!canExport)||allEditing||adding||disabled
823 },
824 }} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/>
825 <Btns btns={btnsObj} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/>
826 </div>:''
827 }
828
829 </div>:
830 <div className={`${clsfix}-panel`}>
831 <div></div>
832 <div className='ac-gridcn-panel-btns'>
833 <ButtonGroup>
834 <Btns btns={btns1} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/>
835 </ButtonGroup>
836 <Btns btns={btnSave} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/>
837
838 <Btns btns={{
839 export: {
840 onClick: () => {
841 this.grid.exportExcel();
842 },
843 disabled:(!canExport)||allEditing||adding||disabled
844 },
845 }} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/>
846 <Btns btns={btnsObj} powerBtns={powerBtns} forcePowerBtns={forcePowerBtns}/>
847 </div>
848 </div>
849 }
850 {
851 typeof title=='string'?<div className={`${clsfix}-inner ${open?'show':'hide'} ${isMax?'max':''}`}>
852 <BeeGrid {...gridOptions}/>
853 </div>:<BeeGrid {...gridOptions}/>
854 }
855 </div>
856 </Fragment>
857 );
858 }
859
860 render() {
861 return (
862 <span>
863 {
864 this.state.isMax?ReactDOM.createPortal(this.renderDom(),document.querySelector('body')):this.renderDom()
865 }
866
867 </span>
868 )
869
870 }
871}
872
873Grid.defaultProps = defaultProps;
874export default Grid;
\No newline at end of file