bee-complex-grid
Version:
Complex Grid for React, AdvancedDataGrid
606 lines (562 loc) • 21.9 kB
JavaScript
import React, { Component } from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
import ExportJsonExcel from "./ExportExcel";
import ColumnsDropdown from './ColumnsDropdown';
import { Pagination, Popover, Checkbox, Icon, Radio, Table } from '@tinper/next-ui'
const { multiSelect, filterColumn, dragColumn, sort, sum, bigData, singleSelect } = Table
import i18n from "./i18n";
import { getLangInfo } from './utils'
// import { getComponentLocale } from "bee-locale/build/tool";
// import { getComponentLocale } from "@tinper/next-ui/lib/wui-locale/src/tool"
const propTypes = {
showHeaderMenu: PropTypes.bool,
sheetName: PropTypes.string,
sheetIsRowFilter: PropTypes.bool,
exportData: PropTypes.array,
afterRowLock:PropTypes.func
};
const defaultProps = {
scroll: {
y: true
},
bordered: true,
multiSelect: { type: "checkbox" },
draggable: true,
dragborder: true,
showHeaderMenu: true,
data: [],
exportData: [],
locale: 'zh-cn',
paginationObj: {},
sheetName: "sheet", //导出表格的name
sheetIsRowFilter: false, //是否要设置行样式,是否遍历
columnFilterAble: true,
afterRowLock:()=>{},//表头锁定解锁的回调函数
};
const defualtPaginationParam = { horizontalPosition: "center", verticalPosition: 'bottom', showJump: true, first: true, prev: true, last: true, next: true, maxButtons: 5 };
class Grid extends Component {
constructor(props) {
super(props);
let { paginationObj, filterable } = props;
this.state = {
filterable, //是否默认启用“行过滤”功能,即按条件或值筛选行数据`data`的功能
renderFlag: false, //这个只是一个标记量,用于控制组件是否需要渲染
activePage: paginationObj.activePage,
total: paginationObj.total,
pageItems: paginationObj.items,
dataNum: paginationObj.dataNum,
showMenuKey: '',
selectedRowIndex: props.selectedRowIndex || ''
};
this.local = i18n
this.selectType = 'none'; // 标识单选/多选/无选择列
this.ComplexTable = this.constructGrid(Table);
}
// 根据参数,组装复杂表格
constructGrid = (basicTable) => {
let { props } = this;
let { sort: sortObj } = props;
let ComplexTable = basicTable;
// 大数据渲染
if (props.loadLazy) {
ComplexTable = bigData(ComplexTable);
}
//后端回调方法,用户的sortFun和Grid的有时有冲突,所以重新定义了一个sort,传给Table
if (sortObj) {
sortObj.originSortFun = sortObj.originSortFun
? sortObj.originSortFun
: sortObj.sortFun;
sortObj.sortFun = this.sortFun;
this.sort = sortObj;
}
// 合计
if (props.canSum) {
ComplexTable = sum(ComplexTable);
}
//根据条件生成Grid
ComplexTable = sort(ComplexTable, Icon);
// 1、type: "checkbox" 多选 2、type: "radio" 单选
if (Object.prototype.toString.call(props.multiSelect) === '[object Object]' && 'type' in props.multiSelect) {
if (props.multiSelect.type === "checkbox") { //多选
ComplexTable = multiSelect(ComplexTable, Checkbox);
this.selectType = "multiple";
} else if (props.multiSelect.type === "radio") { //单选
ComplexTable = singleSelect(ComplexTable, Radio);
this.selectType = "single";
}
} else if (typeof props.multiSelect === 'boolean' && !!(props.multiSelect)) { //兼容老版本,设置 true 为多选。
ComplexTable = multiSelect(ComplexTable, Checkbox);
}
// 拖拽
if (props.draggable) {
ComplexTable = dragColumn(ComplexTable);
}
// 过滤
ComplexTable = filterColumn(ComplexTable, Popover);
return ComplexTable;
}
columns = this.props.columns.map(colItem => {
return { ...colItem };
});
componentWillReceiveProps(nextProps) {
const { renderFlag, selectedRowIndex } = this.state;
//分页
if (nextProps.paginationObj && nextProps.paginationObj !== 'none') {
this.setState({
activePage: nextProps.paginationObj.activePage,
total: nextProps.paginationObj.total,
pageItems: nextProps.paginationObj.items,
dataNum: nextProps.paginationObj.dataNum
});
}
if (nextProps.columns && nextProps.columns !== this.columns) {
let newColumns = [],
leftColumns = [],
rightColumns = [],
centerColumns = [];
if (nextProps.noReplaceColumns) {
newColumns = nextProps.columns.map(colItem => {
return { ...colItem };
});
} else {
//先检查nextProps.columns的顺序与this.columns的顺序是否一致,不一致按照this.columns的顺序调整,(主要交换列时当前column会保存列的顺序,而props的顺序还是之前的)
this.columns.forEach((item, index) => {
if (nextProps.columns[index] && nextProps.columns[index].dataIndex !== item.dataIndex) {
let curIndex = -1;
for (
let nextIndex = 0;
nextIndex < nextProps.columns.length;
nextIndex++
) {
if (nextProps.columns[nextIndex].dataIndex == item.dataIndex) {
curIndex = nextIndex;
break;
}
}
nextProps.columns.splice(
index,
0,
nextProps.columns.splice(curIndex, 1)[0]
);
}
});
//将sort、过滤等在组件中维护的状态和传入column合并
nextProps.columns.forEach((nextItem, index) => {
let newItem = {};
this.columns.forEach(item => {
if (nextItem.dataIndex == item.dataIndex || (!item.dataIndex)) {//适配初始item内没有数据的情况
newItem = { ...item, ...nextItem };
//对于解锁的列,再次传入时还是解锁状态
if (!item.fixed) {
newItem.fixed = '';
}
if (item.width && newItem.width !== item.width) {
newItem.width = item.width;
}
if (item.hasOwnProperty('ifshow')) {
newItem.ifshow = item.ifshow;
}
newItem.hasHeaderMenu = false; //重置后的都需要重新渲染表头菜单
}
});
if (newItem.fixed == "left") {
leftColumns.push(newItem);
} else if (newItem.fixed == "right") {
rightColumns.push(newItem);
} else {
centerColumns.push(newItem);
}
});
newColumns = [...leftColumns, ...centerColumns, ...rightColumns];
}
(this.columns = newColumns),
this.setState({
renderFlag: !renderFlag,
filterable: nextProps.filterable
});
}
// 单选行
if ('selectedRowIndex' in nextProps && nextProps.selectedRowIndex !== selectedRowIndex) {
this.setState({
selectedRowIndex: nextProps.selectedRowIndex
})
}
}
/**
* 点击分页回调函数
*/
handleSelectPage = eventKey => {
let { paginationObj = {} } = this.props;
this.setState({
activePage: eventKey
});
paginationObj.freshData && paginationObj.freshData(eventKey);
};
/**
* 设置相关固定Cols
*/
optFixCols = (columns, key) => {
let fixedLeftCols = [];
let fixedRightCols = [];
let nonColums = [];
columns.find(da => {
if (da.key == key) {
da.fixed ? delete da.fixed : (da.fixed = "left");
}
if (da.fixed == "left") {
fixedLeftCols.push(da);
} else if (da.fixed == "right") {
fixedRightCols.push(da);
} else {
nonColums.push(da);
}
});
columns = [...fixedLeftCols, ...nonColums, ...fixedRightCols];
return columns;
};
/**
* 设置隐藏显示Cols
*/
optShowCols = (columns, key) => {
columns.forEach((item, index) => {
if (item.key == key) {
item.ifshow = false;
return;
}
});
return columns;
};
/**
* header菜单点击操作
*/
onMenuSelect = ({ key, item }) => {
let { filterable, renderFlag } = this.state;
const { checkMinSize,afterRowLock } = this.props;
const fieldKey = item.props.data.fieldKey;
let sum = 0;
if (key !== 'rowFilter') {
//显示原则跟table组件同步,至少有一个非固定列显示
this.columns.forEach(da => {
!da.fixed && da.ifshow !== false ? sum++ : "";
});
}
if (key == "fix") {
if (sum <= 1 && !item.props.data.fixed) {
return;
}
this.columns = this.optFixCols(this.columns, fieldKey);
afterRowLock(fieldKey,!item.props.data.fixed,this.columns)
this.setState({
renderFlag: !renderFlag
});
} else if (key == "show") {
if (sum < checkMinSize || sum <= 1) {
return;
}
this.columns = this.optShowCols(this.columns, fieldKey);
this.setState({
renderFlag: !renderFlag
});
} else {
if (typeof this.props.afterRowFilter == "function") {
this.props.afterRowFilter(!filterable);
}
this.setState({ filterable: !filterable });
}
};
/**
* 渲染表头下拉菜单(过滤、隐藏)
* @param {Array} columns 表格列数组
*/
renderColumnsDropdown = (columns) => {
const icon = "uf-arrow-down";
const { showFilterMenu, columnFilterAble } = this.props;
const { filterable } = this.state;
return columns.map((originColumn, index, arr) => {
let column = Object.assign({}, originColumn);
column.hasHeaderMenu = true;
column.title = <ColumnsDropdown originColumn={originColumn} local={this.local} showFilterMenu={showFilterMenu}
onMenuSelect={this.onMenuSelect} allColumns={arr} columnFilterAble={columnFilterAble}
filterable={filterable}
/>;
return column;
});
}
/**
* 表头menu和表格整体过滤时有冲突,因此添加了回调函数
*/
afterFilter = (optData, columns) => {
if (Array.isArray(optData)) {
this.columns.forEach(da => {
optData.forEach(optItem => {
if (da.key == optItem.key) {
da.ifshow = optItem.ifshow;
return true;
}
});
});
} else {
this.columns.find(da => {
if (da.key == optData.key) {
da.ifshow = optData.ifshow;
}
});
}
if (typeof this.props.afterFilter == "function") {
this.props.afterFilter(optData, this.columns);
}
};
/**
* 后端获取数据
*/
sortFun = (sortParam,newData,oldData) => {
let sortObj = {};
sortParam.forEach(item => {
sortObj[item.field] = item;
});
this.columns.forEach(da => {
//保存返回的column状态,没有则终止order状态
if (sortObj[da.dataIndex]) {
da = Object.assign(da, sortObj[da.dataIndex]);
} else {
da.order = "flatscend";
da.orderNum = "";
}
});
//将参数传递给后端排序
if (typeof this.sort.originSortFun == "function") {
this.sort.originSortFun(sortParam, this.columns,newData,oldData);
}
};
/**
*拖拽交互列后记录下当前columns
*/
dragDrop = (event, data, columns) => {
columns.forEach((item, index) => {
if (this.columns[index].dataIndex !== item.dataIndex) {
let curIndex = -1;
for (let nextIndex = 0; nextIndex < this.columns.length; nextIndex++) {
if (this.columns[nextIndex].dataIndex == item.dataIndex) {
curIndex = nextIndex;
break;
}
}
this.columns.splice(index, 0, this.columns.splice(curIndex, 1)[0]);
}
});
if (this.props.onDrop) {
this.props.onDrop(event, data, this.columns);
}
};
/**
* 获取所有列以及table属性值
*/
getColumnsAndTablePros = () => {
const columns = this.columns.slice();
if (this.dragColsData) {
const dragColsKeyArr = Object.keys(this.dragColsData);
dragColsKeyArr.some(itemKey => {
columns.forEach(col => {
if (col.dataIndex == itemKey) {
col.width = this.dragColsData[itemKey].width;
return true;
}
});
});
}
const rs = {
columns: columns,
tablePros: this.props
};
return rs;
};
getItem = da => {
let obj = {};
da.height ? (obj.hpx = da.height) : "";
da.ifshow ? (obj.hidden = true) : false;
da.level ? (obj.level = da.level) : "";
return obj;
};
getRowList = data => {
let rowAttr = [];
data.forEach(da => {
let item = this.getItem(da);
if (item) {
rowAttr.push(item);
}
});
return rowAttr;
};
exportExcel = () => {
let { sheetIsRowFilter, sheetName, sheetHeader: _sheetHeader, exportData, exportFileName } = this.props;
let colsAndTablePros = this.getColumnsAndTablePros();
let sheetHeader = [],
columnAttr = [],
rowAttr = [],
sheetFilter = [];
colsAndTablePros.columns.forEach(column => {
let _show = false, _hidden = false;
if (column.ifshow != undefined && column.ifshow === false) {
_show = true;
}
_hidden = column.exportHidden ? true : _show;
if (!_hidden) {
let _width = String(column.width).indexOf("%") != -1 ? 100 : column.width
columnAttr.push({
wpx: _width
});
let _cloum = column.exportKey ? column.exportKey : column.dataIndex
sheetFilter.push(_cloum);
sheetHeader.push(column.title);
}
});
if (_sheetHeader) {
rowAttr.push(this.getItem(_sheetHeader));
}
if (sheetIsRowFilter) {
this.getRowList(colsAndTablePros.tablePros.data);
}
let option = {
datas: [
{
fileName: exportFileName,
sheetData: exportData,
sheetName,
sheetFilter,
sheetHeader,
columnAttr,
rowAttr
}
]
};
let toExcel = new ExportJsonExcel(option, exportFileName);
toExcel.saveExcel();
};
/**
* 拖拽后计算列宽
*/
afterDragColWidth = colData => {
const { renderFlag } = this.state;
const { rows, cols, currIndex } = colData;
this.columns.forEach(item => {
rows.find((paramItem, paramIndex) => {
if (item.dataIndex == paramItem.dataindex) {
if (paramIndex == currIndex) {
item.width = parseInt(cols[paramIndex].style.width);
} else {
item.width = paramItem.width;
}
}
});
});
this.setState({
renderFlag: !renderFlag
})
};
/**
*
* 重置grid的columns
*/
resetColumns = newColumns => {
const { renderFlag } = this.state;
if (newColumns) {
this.columns = newColumns.map(colItem => {
return { ...colItem };
});
this.setState({
renderFlag: !renderFlag
});
}
};
/**
* 单选回调
*/
getSingleSelectedDataFunc = (record, index) => {
let { getSelectedDataFunc } = this.props;
this.setState({
selectedRowIndex: index
});
getSelectedDataFunc && getSelectedDataFunc(record, index);
}
/**
* 多选回调
*/
getMultiSelectedDataFunc = (selectedList, record, index, newData) => {
let { getSelectedDataFunc } = this.props;
getSelectedDataFunc && getSelectedDataFunc(selectedList, record, index, newData);
}
render() {
const props = this.props;
const ComplexTable = this.ComplexTable;
let { sort = {}, paginationObj, toolBar = {} } = props;
let paginationParam, verticalPosition, horizontalPosition;
// this.local = getComponentLocale(
// this.props,
// this.context,
// "Grid",
// () => i18n
// );
this.local = getLangInfo(this.props.locale, i18n)
if (paginationObj !== 'none') {
paginationParam = { ...defualtPaginationParam, ...paginationObj }
verticalPosition = paginationParam.verticalPosition;
horizontalPosition = paginationParam.horizontalPosition;
delete paginationParam.freshData;
delete paginationParam.horizontalPosition;
delete paginationParam.verticalPosition;
}
//默认固定表头
const scroll = Object.assign({}, { y: true }, props.scroll);
const { filterable, selectedRowIndex } = this.state;
let columns = this.columns.slice();
//是否显示表头菜单、已经显示过的不再显示
if (props.showHeaderMenu) {
columns = this.renderColumnsDropdown(columns);
}
return (
<div className={classNames("bee-complex-grid", props.className)}>
{verticalPosition == "top" && (
<Pagination
{...paginationParam}
className={horizontalPosition}
boundaryLinks
activePage={this.state.activePage}
onSelect={this.handleSelectPage}
items={this.state.pageItems}
total={this.state.total}
/>
)}
<ComplexTable
{...props}
scroll={scroll}
columns={columns}
afterFilter={this.afterFilter}
sort={this.sort}
onDragEnd={this.dragDrop}
afterDragColWidth={this.afterDragColWidth}
filterable={filterable}
selectedRowIndex={selectedRowIndex}
getSelectedDataFunc={this.selectType === 'single' ? this.getSingleSelectedDataFunc : this.getMultiSelectedDataFunc}
/>
{verticalPosition == "bottom" && (
<Pagination
{...paginationParam}
className={horizontalPosition}
boundaryLinks
activePage={this.state.activePage}
onSelect={this.handleSelectPage}
items={this.state.pageItems}
total={this.state.total}
/>
)}
</div>
);
}
}
Grid.propTypes = propTypes;
Grid.defaultProps = defaultProps;
Grid.contextTypes = {
beeLocale: PropTypes.object
};
export default Grid;