bee-table/src/TableHeader.js

550 lines
19 KiB
JavaScript
Raw Normal View History

import React, { Component } from "react";
2018-12-01 17:51:01 +08:00
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";
import shallowequal from "shallowequal";
import { throttle, debounce } from "throttle-debounce";
2018-11-30 16:14:29 +08:00
import { tryParseInt, ObjectAssign ,Event} from "./utils";
import FilterType from "./FilterType";
2016-12-26 16:52:39 +08:00
2017-01-11 17:01:50 +08:00
const propTypes = {
clsPrefix: PropTypes.string,
rowStyle: PropTypes.object,
rows: PropTypes.array
};
2017-01-11 17:01:50 +08:00
const grap = 16; //偏移数值
2018-06-25 00:39:18 +08:00
class TableHeader extends Component {
constructor(props) {
2018-05-07 10:58:24 +08:00
super(props);
2018-05-09 14:51:01 +08:00
this.currentObj = null;
2018-05-13 15:02:53 +08:00
//拖拽宽度处理
if (!props.dragborder) return;
2018-05-11 09:29:43 +08:00
this.border = false;
2018-05-13 16:28:08 +08:00
this.theadKey = new Date().getTime();
2018-05-11 09:29:43 +08:00
this.drag = {
initPageLeftX: 0,
initLeft: 0,
x: 0,
2018-12-01 17:51:01 +08:00
width: 0,
option:''
};
2018-12-03 11:17:39 +08:00
this.minWidth = 80;//确定最小宽度就是80
2018-11-30 16:14:29 +08:00
this.table = null;
2018-06-25 00:39:18 +08:00
let _row = [];
this.props.rows[0] &&
this.props.rows[0].forEach(item => {
let newItem = item.key != "checkbox" ? ObjectAssign(item) : item;
_row.push(newItem);
});
this.drag.data = _row; //JSON.parse(JSON.stringify(this.props.rows[0]));
}
static defaultProps = {
contentWidthDiff: 0
};
2018-12-01 13:45:06 +08:00
2018-11-30 16:14:29 +08:00
/**
* 动态绑定th line 事件
* type 为false 为增加事件
* eventSource 为false th 内部的div增加事件
*/
2018-12-01 13:45:06 +08:00
thEventListen(events,type,eventSource){
2018-11-30 16:14:29 +08:00
let {ths,cols} = this.table;
for (let index = 0; index < ths.length; index++) {
const element = ths[index];//.getAttribute('data-type');
if(!element.getAttribute('data-th-fixed')){
2018-12-01 17:51:01 +08:00
const colLine = element.children.length > 1?element.lastElementChild:element.children[0];
// const colLine = element.children[0];
2018-11-30 16:14:29 +08:00
for (let i = 0; i < events.length; i++) {
const _event = events[i];
2018-12-01 13:45:06 +08:00
let _dataSource = eventSource?element:colLine;
if(type === "remove"){
_dataSource.removeEventListener(_event.key,_event.fun);
2018-11-30 16:14:29 +08:00
}else{
2018-12-01 13:45:06 +08:00
_dataSource.addEventListener(_event.key,_event.fun);
2018-11-30 16:14:29 +08:00
}
}
}
}
2018-11-30 16:14:29 +08:00
}
2018-11-30 16:14:29 +08:00
bodyEventListen(events,type){
for (let i = 0; i < events.length; i++) {
const _event = events[i];
if(type == "remove"){
document.removeEventListener(_event.key,_event.fun);
}else{
document.addEventListener(_event.key,_event.fun);
}
}
2017-01-11 17:01:50 +08:00
}
2018-05-09 14:51:01 +08:00
2018-11-30 16:14:29 +08:00
componentDidUpdate(){
this.initTable();
this.initEvent();
}
2018-05-11 09:29:43 +08:00
2018-11-30 16:14:29 +08:00
componentDidMount(){
this.initTable();
this.initEvent();
}
/**
* 拖拽列宽的事件处理
*/
initEvent(){
let events = [
{key:'mouseup', fun:this.onLineMouseUp},
{key:'mousemove', fun:this.onLineMouseMove}
];
2018-12-01 13:45:06 +08:00
2018-12-01 17:51:01 +08:00
if(this.props.dragborder){
this.thEventListen(events,'',true);//表示把事件添加到th元素上
this.thEventListen([{key:'mousedown',fun:this.onLineMouseDown}]);//表示把事件添加到竖线
this.bodyEventListen([{key:'mouseup',fun:this.bodyonLineMouseMove}]);
}
2018-12-01 17:51:01 +08:00
if(!this.props.draggable)return;
2018-12-01 13:45:06 +08:00
//拖拽交换列事件
this.thEventListen([{key:'mousedown',fun:this.dragAbleMouseDown}],'',true);//表示把事件添加到竖线
}
2018-12-01 13:45:06 +08:00
/**
* 移除拖拽宽度的事件
*/
removeDragBorderEvent(){
let events = [
{key:'mouseup', fun:this.onLineMouseUp},
{key:'mousemove', fun:this.onLineMouseMove}
];
this.thEventListen(events,'remove',true);//表示把事件添加到th元素上
this.thEventListen([{key:'mousedown',fun:this.onLineMouseDown}],'remove');//表示把事件添加到竖线
this.bodyEventListen([{key:'mouseup',fun:this.bodyonLineMouseMove}],'remove');
2018-11-30 16:14:29 +08:00
}
2018-11-30 16:14:29 +08:00
/**
* 获取table的属性存放在this.table (公用方法)
*/
initTable(){
2018-12-01 17:51:01 +08:00
if(!this.props.dragborder && !this.props.draggable)return;
2018-11-30 16:14:29 +08:00
let el = ReactDOM.findDOMNode(this);
let tableDome = el.parentNode;
let table = {};
if(tableDome && tableDome.nodeName && tableDome.nodeName.toUpperCase() == "TABLE"){
table.table = tableDome;
table.cols = tableDome.getElementsByTagName("col");
table.ths = tableDome.getElementsByTagName("th");
}
2018-11-30 16:14:29 +08:00
this.table = table;
2018-12-01 17:51:01 +08:00
if(!this.props.dragborder)return;
if(document.getElementById("u-table-drag-thead-" + this.theadKey)){
//hao 固定列table
this.fixedTable = {};
let _fixedParentContext = document.getElementById("u-table-drag-thead-" + this.theadKey).parentNode;
let siblingDom = _fixedParentContext.parentNode.nextElementSibling;
if (siblingDom) {
let fixedTable = siblingDom.querySelector("table");
this.fixedTable.table = fixedTable
this.fixedTable.cols = fixedTable.getElementsByTagName("col");
// this.fixedTable.ths = fixedTable.tableDome.getElementsByTagName("th");
}
}
2018-11-30 16:14:29 +08:00
}
2018-12-01 17:51:01 +08:00
//---拖拽列宽代码逻辑----start-----
2018-11-30 16:14:29 +08:00
onLineMouseMove = (e) => {
const { clsPrefix ,dragborder,contentDomWidth,scrollbarWidth,contentTable,headerScroll} = this.props;
2018-11-30 16:14:29 +08:00
Event.stopPropagation(e);
let event = Event.getEvent(e);
2018-12-01 17:51:01 +08:00
if (!this.props.dragborder) return;
2018-11-30 16:14:29 +08:00
if(this.drag.option != "border"){
return false;
}
//移动改变宽度
let currentCols = this.table.cols[this.drag.currIndex];
let diff = (event.x - this.drag.oldLeft);
let newWidth = this.drag.oldWidth + diff;
2018-12-03 11:17:39 +08:00
// if(newWidth > this.drag.minWidth){
if(newWidth > this.minWidth){
2018-11-30 16:14:29 +08:00
currentCols.style.width = newWidth +'px';
//hao 支持固定表头拖拽 修改表体的width
if(this.fixedTable.cols){
this.fixedTable.cols[this.drag.currIndex].style.width = newWidth + "px";
}
//表头滚动条处理
if(headerScroll){
let oldTableWidth = parseInt(this.table.table.style.width ?this.table.table.style.width:this.table.table.scrollWidth);
const newTableWidth = oldTableWidth + diff ;
this.table.table.style.width = newTableWidth;//改变table的width
let showScroll = contentDomWidth - newTableWidth - scrollbarWidth ;
// if(bordered){
// showScroll = showScroll -1;
// }
const fixedLeftHeaderTable = contentTable.querySelector('.u-table-fixed-left .u-table-header') ;
const fixedRighHeadertTable = contentTable.querySelector('.u-table-fixed-right .u-table-header');
const contentTableHeader = contentTable.querySelector('.u-table-scroll .u-table-header');
if(showScroll < 0){
//找到固定列表格设置表头的marginBottom值为scrollbarWidth;
contentTableHeader.style.overflowX = 'scroll';
fixedLeftHeaderTable && (fixedLeftHeaderTable.style.marginBottom = scrollbarWidth + "px");
fixedRighHeadertTable && (fixedRighHeadertTable.style.marginBottom = scrollbarWidth + "px");
}else{
contentTableHeader.style.overflowX = 'hidden';
fixedLeftHeaderTable && (fixedLeftHeaderTable.style.marginBottom = '0px');
fixedRighHeadertTable && (fixedRighHeadertTable.style.marginBottom = '0px');
}
}
2018-11-30 16:14:29 +08:00
}
};
2018-11-30 16:14:29 +08:00
onLineMouseDown = (e) => {
Event.stopPropagation(e);
let event = Event.getEvent(e);
const { clsPrefix, contentTable } = this.props;
2018-12-01 17:51:01 +08:00
if (!this.props.dragborder) return;
2018-11-30 16:14:29 +08:00
let currentIndex = parseInt(Event.getTarget(event).getAttribute("data-line-index"));
let defaultWidth = Event.getTarget(event).getAttribute("data-th-width");
let currentObj = this.table.cols[currentIndex];
this.drag.option = "border";//拖拽操作
this.drag.currIndex = currentIndex;
this.drag.oldLeft = event.x;
this.drag.oldWidth = parseInt((currentObj).style.width);
this.drag.minWidth = currentObj.style.minWidth != ""?parseInt(currentObj.style.minWidth):defaultWidth;
};
2018-11-30 16:14:29 +08:00
onLineMouseUp = (event) => {
2018-12-02 16:53:06 +08:00
let {rows} = this.props;
let data = {rows:rows[0],cols:this.table.cols,currIndex:this.drag.currIndex};
this.props.afterDragColWidth && this.props.afterDragColWidth(data);
2018-11-30 16:14:29 +08:00
this.clearDragBorder(event);
};
2018-11-30 16:14:29 +08:00
bodyonLineMouseMove = (event) => {
this.clearDragBorder(event);
};
2018-11-30 16:14:29 +08:00
clearDragBorder(){
2018-12-03 09:34:30 +08:00
// if (!this.props.dragborder || !this.props.draggable) return;
if(!this.drag)return;
2018-11-30 16:14:29 +08:00
this.drag = {
option:""
};
2018-12-03 09:34:30 +08:00
if (!this.props.draggable){
this.removeDragAbleEvent();
}
2018-11-30 16:14:29 +08:00
}
2018-06-25 00:40:21 +08:00
2018-12-01 17:51:01 +08:00
//---拖拽列宽代码逻辑----start-----
2018-12-01 13:45:06 +08:00
dragAbleMouseDown = (e) => {
Event.stopPropagation(e);
let event = Event.getEvent(e);
2018-12-01 17:51:01 +08:00
if (!this.props.draggable) return;
2018-12-01 13:45:06 +08:00
event.target.setAttribute('draggable',true);//添加交换列效果
this.drag.option = 'dragAble';
this.removeDragBorderEvent();//清理掉拖拽列宽的事件
this.addDragAbleEvent(); //添加拖拽交换列的事件
}
2018-11-30 16:14:29 +08:00
/**
* 拖拽交换列的事件处理
*/
2018-12-01 13:45:06 +08:00
addDragAbleEvent (){
2018-11-30 16:14:29 +08:00
let events = [
2018-12-01 13:45:06 +08:00
{key:'dragstart',fun:this.onDragStart},//用户开始拖动元素时触发
{key:'dragover', fun:this.onDragOver},//当某被拖动的对象在另一对象容器范围内拖动时触发此事件
{key:'drop', fun:this.onDrop}, //在一个拖动过程中,释放鼠标键时触发此事件
2018-12-02 17:19:38 +08:00
// {key:'dragenter', fun:this.onDragEnter} //当被鼠标拖动的对象进入其容器范围内时触发此事件
2018-11-30 16:14:29 +08:00
];
2018-12-01 13:45:06 +08:00
this.thEventListen(events,'',true);
2018-11-30 16:14:29 +08:00
// this.bodyEventListen([{key:'mouseup',fun:this.bodyonDragMouseMove}]);
}
2018-12-01 17:51:01 +08:00
removeDragAbleEvent(){
let events = [
{key:'dragstart',fun:this.onDragStart},
{key:'dragover', fun:this.onDragOver},
{key:'drop', fun:this.onDrop},
{key:'dragenter', fun:this.onDragEnter}
];
this.thEventListen(events,'remove',true);
}
2018-12-01 13:45:06 +08:00
onDragStart = (e) => {
let event = Event.getEvent(e);
2018-12-01 17:51:01 +08:00
if (!this.props.draggable) return;
2018-12-01 13:45:06 +08:00
if(this.drag.option === 'border'){return;}
let currentIndex = parseInt(Event.getTarget(event).getAttribute("data-line-index"));
let currentKey = event.target.getAttribute('data-line-key');
2018-05-09 14:51:01 +08:00
event.dataTransfer.effectAllowed = "move";
2018-12-01 13:45:06 +08:00
event.dataTransfer.setData("Text", currentKey);
this.currentObj = this.props.rows[0][currentIndex];
2018-05-09 14:51:01 +08:00
event.dataTransfer.setDragImage(event.target, 0, 0);
};
2018-06-25 00:39:18 +08:00
2018-12-01 13:45:06 +08:00
onDragOver = (e) => {
2018-05-09 14:51:01 +08:00
event.preventDefault();
};
2018-11-22 23:09:53 +08:00
2018-12-01 13:45:06 +08:00
/**
* 当被鼠标拖动的对象进入其容器范围内时触发此事件目标事件
* @memberof TableHeader
*/
2018-12-02 17:19:38 +08:00
// onDragEnter = (e) => {
// if (!this.props.draggable) return;
// if(this.drag.option === 'border'){return;}
// let data = this.getCurrentEventData(e);
// if (!this.currentObj || this.currentObj.key == data.key) return;
// };
2018-11-22 23:09:53 +08:00
2018-12-01 13:45:06 +08:00
/**
* 在一个拖动过程中释放鼠标键时触发此事件目标事件
* @memberof TableHeader
*/
onDrop = (e) => {
2018-12-01 17:51:01 +08:00
if (!this.props.draggable) return;
2018-12-01 13:45:06 +08:00
if(this.drag.option === 'border'){return;}
let data = this.getCurrentEventData(e);
2018-12-03 11:17:39 +08:00
if(!data)return;
if (!this.currentObj || this.currentObj.key == data.key) return;
2018-12-01 17:51:01 +08:00
if(!this.props.onDrop)return;
2018-12-01 13:45:06 +08:00
this.props.onDrop(event,{dragSource:this.currentObj,dragTarg:data});
};
2018-09-19 10:46:14 +08:00
2018-12-01 13:45:06 +08:00
getCurrentEventData(e){
let event = Event.getEvent(e);
let key = event.target.getAttribute('data-line-key');
let data = this.props.rows[0].find(da=>da.key == key);
if(data){
return data;
}else{
console.log(" getCurrentEventData data is null ");
return null;
}
}
2018-12-01 17:51:01 +08:00
//---拖拽列交换----end-----
/**
* 过滤输入后或下拉条件的回调函数
*/
handlerFilterChange = (key, value, condition) => {
let { onFilterChange } = this.props;
if (onFilterChange) {
onFilterChange(key, value, condition);
}
};
/**
* 过滤行清除回调
*/
handlerFilterClear = (field) => {
let { onFilterClear } = this.props;
if (onFilterClear) {
onFilterClear(field);
}
}
/**
* 过滤渲染的组件类型
*/
filterRenderType = (type, dataIndex, index) => {
const { clsPrefix, rows, filterDelay, locale } = this.props;
switch (type) {
//文本输入
case "text":
return (
<FilterType
locale={locale}//多语
rendertype={type}//渲染类型
clsPrefix={clsPrefix}//css前缀
className={`${clsPrefix} filter-text`}
dataIndex={dataIndex}//字段
onFilterChange={this.handlerFilterChange}//输入框回调
onFilterClear={this.handlerFilterClear}//清除回调
filterDropdown={rows[1][index]["filterdropdown"]}//是否显示下拉条件
2018-11-26 19:26:52 +08:00
filterDropdownType={rows[1][index]["filterdropdowntype"]}//下拉的条件类型为string,number
/>
);
//数值输入
2018-11-26 19:26:52 +08:00
case "number":
return (
<FilterType
locale={locale}
rendertype={type}
clsPrefix={clsPrefix}
className={`${clsPrefix} filter-text`}
dataIndex={dataIndex}//字段
onFilterChange={debounce(filterDelay || 300, this.handlerFilterChange)}//输入框回调并且函数防抖动
onFilterClear={this.handlerFilterClear}//清除回调
filterDropdown={rows[1][index]["filterdropdown"]}
filterDropdownType={rows[1][index]["filterdropdowntype"]}//下拉的条件类型为string,number
/>
);
//下拉框选择
case "dropdown":
let selectDataSource = [];
//处理没有输入数据源的时候,系统自动查找自带的数据筛选后注入
if (rows.length > 0 && (rows[1][index]["filterdropdownauto"] || "auto") == "auto") {
let hash = {};
//处理下拉重复对象组装dropdown
selectDataSource = Array.from(rows[1][0].datasource, x => ({
key: x[dataIndex],
value: x[dataIndex]
}));
selectDataSource = selectDataSource.reduceRight((item, next) => {
hash[next.key] ? "" : (hash[next.key] = true && item.push(next));
return item;
}, []);
} else {
//从外部数据源加载系统数据
selectDataSource = rows[1][index]["filterdropdowndata"];
}
return (
<FilterType
locale={locale}
rendertype={type}
className={`${clsPrefix} filter-dropdown`}
data={selectDataSource}
dataIndex={dataIndex}//字段
onFilterChange={this.handlerFilterChange}//输入框回调
onFilterClear={this.handlerFilterClear}//清除回调
filterDropdown={rows[1][index]["filterdropdown"]}
onFocus={rows[1][index]["filterdropdownfocus"]}
filterDropdownType={rows[1][index]["filterdropdowntype"]}//下拉的条件类型为string,number
/>
);
//日期
case "date":
return (
<FilterType
locale={locale}
rendertype={type}
className={`filter-date`}
onClick={() => { }}
format={rows[1][index]["format"] || "YYYY-MM-DD"}
dataIndex={dataIndex}//字段
onFilterChange={this.handlerFilterChange}//输入框回调
onFilterClear={this.handlerFilterClear}//清除回调
filterDropdown={rows[1][index]["filterdropdown"]}
filterDropdownType={rows[1][index]["filterdropdowntype"]}//下拉的条件类型为string,number
/>
);
//日期范围
case "daterange":
return (
<FilterType
locale={locale}
rendertype={type}
className={`filter-date`}
onClick={() => { }}
format={rows[1][index]["format"] || "YYYY-MM-DD"}
dataIndex={dataIndex}//字段
onFilterChange={this.handlerFilterChange}//输入框回调
onFilterClear={this.handlerFilterClear}//清除回调
filterDropdown={rows[1][index]["filterdropdown"]}
filterDropdownType={rows[1][index]["filterdropdowntype"]}//下拉的条件类型为string,number
/>
);
default:
//不匹配类型默认文本输入
return <div />;
}
};
2018-11-27 14:32:30 +08:00
2018-11-30 16:14:29 +08:00
render() {
const {
clsPrefix,
rowStyle,
onDragStart,
onDragOver,
onDrop,
draggable,
dragborder,
rows,
filterable,
onFilterRowsChange,
onMouseDown,
onMouseMove,
onMouseUp,
onMouseOut,
contentWidthDiff,
fixed,
2018-11-21 22:38:32 +08:00
lastShowIndex,
contentTable
} = this.props;
let attr = dragborder ? { id: `u-table-drag-thead-${this.theadKey}` } : {};
2016-12-26 16:52:39 +08:00
return (
2018-12-01 17:51:01 +08:00
<thead className={`${clsPrefix}-thead`} {...attr} data-theader-fixed='scroll' >
{rows.map((row, index) => (
<tr key={index} style={rowStyle} className={(filterable && index == rows.length - 1)?'filterable':''}>
2018-11-30 16:14:29 +08:00
{row.map((da, columIndex, arr) => {
let thHover = da.drgHover
? ` ${clsPrefix}-thead th-drag-hover`
: "";
delete da.drgHover;
let fixedStyle = "";
let canDotDrag = "";
if (!fixed && da.fixed) {
fixedStyle = `${clsPrefix}-row-fixed-columns-in-body`;
}
2018-11-27 14:32:30 +08:00
2018-11-30 16:14:29 +08:00
if (lastShowIndex == columIndex) {
canDotDrag = "th-can-not-drag";
}
if (filterable && index == rows.length - 1) {
da.children = this.filterRenderType(
da["filtertype"],
da.dataindex,
2018-11-30 16:14:29 +08:00
columIndex
);
delete da.filterdropdownfocus;
}
2018-12-01 17:51:01 +08:00
let thDefaultObj = {};
let thClassName = `${da.className}`;
2018-12-01 17:51:01 +08:00
if(draggable){
thClassName += `${clsPrefix}-thead th-drag ${thHover} `;
}
2018-12-01 17:51:01 +08:00
if(dragborder){
thClassName += `${clsPrefix}-thead-th ${canDotDrag}`;
2018-11-27 17:11:15 +08:00
}
2018-12-01 17:51:01 +08:00
thClassName += `${fixedStyle}`;
if(!da.fixed){
2018-12-01 17:51:01 +08:00
return (<th key={Math.random()+new Date().getTime()} className={thClassName} data-th-fixed={da.fixed}
2018-11-30 16:14:29 +08:00
data-line-key={da.key} data-line-index={columIndex} data-th-width={da.width} >
2018-12-01 17:51:01 +08:00
{da.children}
{
dragborder ? <div ref={el => (this.gap = el)} data-line-key={da.key}
data-line-index={columIndex} data-th-width={da.width}
data-type="online" className = {`${clsPrefix}-thead-th-drag-gap`}>
<div id='th-online' className='online' data-line-key={da.key} data-line-index={columIndex} data-th-width={da.width} /></div>:""
}
</th>)
}else{
thDefaultObj = {
...da,
className:`${da.className} ${fixedStyle}`,
2018-11-30 16:14:29 +08:00
key:columIndex
};
da.onClick ?thDefaultObj.onClick = (e)=>{da.onClick(da, e)}:"";
2018-12-01 13:45:06 +08:00
return (<th {...thDefaultObj} data-th-fixed={da.fixed} />)
}
})}
</tr>
))}
2016-12-26 16:52:39 +08:00
</thead>
);
2017-01-11 17:01:50 +08:00
}
}
2017-01-11 17:01:50 +08:00
TableHeader.propTypes = propTypes;
export default TableHeader;