bee-table
Version:
Table ui component for react
341 lines (316 loc) • 11.1 kB
JavaScript
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import objectPath from 'object-path';
import i18n from './lib/i18n';
import { getComponentLocale } from 'bee-locale/build/tool';
import { formatMoney } from './lib/utils';
import Dropdown from 'bee-dropdown';
import Menu from 'bee-menus';
const prefix = 'u';
const { Item } = Menu;
const propTypes = {
record: PropTypes.object,
clsPrefix: PropTypes.string,
index: PropTypes.number,
indent: PropTypes.number,
indentSize: PropTypes.number,
column: PropTypes.object,
expandIcon: PropTypes.node,
onPaste:PropTypes.func,
fixed:PropTypes.bool,
showSum:PropTypes.bool,
col:PropTypes.any,
bodyDisplayInRow:PropTypes.any,
stopRowDrag:PropTypes.func,
lazyStartIndex:PropTypes.number,
lazyEndIndex:PropTypes.number,
getCellClassName:PropTypes.string,
};
class TableCell extends Component{
constructor(props){
super(props);
this.isInvalidRenderCellText = this.isInvalidRenderCellText.bind(this);
this.handleClick = this.handleClick.bind(this);
this.state = {
showDropdowm: false
}
}
isInvalidRenderCellText(text) {
return text && !React.isValidElement(text) &&
Object.prototype.toString.call(text) === '[object Object]';
}
handleClick(e) {
const { record, column: { onCellClick } } = this.props;
if (onCellClick) {
onCellClick(record, e);
}
}
// 渲染链接类型
renderLinkType = ( data, record, index, config={}) => {
const { url, urlIndex, linkType, className, underline, descIndex, desc, linkColor } = config;
let linkUrl = '';
if(url){
linkUrl = url(data, record, index);
}
else if(urlIndex){
linkUrl = record[urlIndex];
}
if(linkUrl){
let link = () => {
window.open(linkUrl,linkType || '_blank');
}
let cls = `${prefix}-table-link ${prefix}-table-fieldtype `;
if(className){
cls += `${className} `;
}
if(underline){
cls += `${prefix}-table-link-underline `;
}
let title = '';
if(desc === true){
title = linkUrl;
}
else if( typeof desc === 'string'){
title = desc;
}
else if( typeof desc === 'function'){
title = desc(data, record, index);
}
else if(descIndex){
title = record[descIndex];
}
return <span onClick={link} className={cls} style={{color:linkColor || ''}} title={title}>{data}</span>
}
return data;
}
// 渲染布尔类型
renderBoolType = ( data, config={} ) => {
let locale = getComponentLocale(this.props, this.context, 'Table', () => i18n);
let boolConfig = {...{ trueText: locale['bool_true'], falseText: locale['bool_false']},...config};
if(typeof data === 'string'){
if(data === 'false' || data === '0'){
return boolConfig.falseText;
}
}
else if(!data){
return boolConfig.falseText;
}
return boolConfig.trueText;
}
// 渲染整数/货币类型
renderNumber = (data, config={}, width=200) => {
const { precision, thousand, makeUp, preSymbol, nextSymbol } = config;
let number = formatMoney(data, precision, thousand);
if(makeUp === false && number.indexOf('.') !== -1) {
number = number.replace(/0*$/,'').replace(/\.$/,'');
}
let numberWidth = parseInt(width) - 16; // 减去默认的左右padding共计16px
let res = <span className={`${prefix}-table-currency-number`} >{number}</span>;
let pre = preSymbol ? <span className={`${prefix}-table-currency-pre`}>{preSymbol}</span> : null;
let next = nextSymbol ? <span className={`${prefix}-table-currency-next`}>{nextSymbol}</span> : null;
let title = '';
title += typeof preSymbol === 'string' ? preSymbol : '';
title += number;
title += typeof nextSymbol === 'string' ? nextSymbol : '';
return <span className={`${prefix}-table-currency ${prefix}-table-fieldtype`} style={{width:numberWidth}} title={title}>
{pre}
{res}
{next}
</span>;
}
// 渲染时间类型-l
renderDate = ( data, config={}) => {
const { moment, format } = config;
if(!moment)return data;
return moment(data).format(format || 'YYYY-MM-DD');
}
// 渲染下拉类型,主要为编辑表格铺垫
renderSelect = ( data, config={}) => {
if(config.options){
data = config.options[data] || config.defaultShow;
}
return data;
}
// 渲染行内菜单
renderColumnMenu = (colMenu, text, record, index) => {
if (!colMenu) return null;
let { menu, trigger = 'hover', className = '', icon = <i className='uf uf-3dot-h' />, iconSize = 21 } = colMenu;
let items = [];
items = menu.map((item) => {
return <Item key={item.key} onClick={() => { this.onClickColMenu(item.callback, text, record, index) }}>
{item.icon}
{item.text}
</Item>
})
if (items.length === 0) return null;
className += ` ${prefix}-table-inline-op-dropdowm`
let menus = <Menu className={className}>{items}</Menu>;
let top = `calc(50% - ${iconSize / 2}px)`;
let visibility = this.state.showDropdowm ? 'visible' : '';
let iconClassName = `${prefix}-table-inline-op-icon ${prefix}-table-inline-op-icon-hover`;
return <Dropdown
trigger={[trigger]}
overlay={menus}
animation="slide-up"
onVisibleChange={this.changeShowDropdowm}
>
{<span className={iconClassName} style={{ fontSize: iconSize, top: top, visibility: visibility }}>{icon}</span>}
</Dropdown>
}
// 下拉按钮状态改变,点击后保持图标常驻
changeShowDropdowm = (val) => {
this.setState({
showDropdowm: val
})
}
// 菜单点击事件
onClickColMenu = (callback, text, record, index) => {
if (callback) {
callback(text, record, index);
}
this.setState({
showDropdowm: false,
})
}
onPaste=(e)=>{
let { index:row,onPaste,fixed,col } = this.props
let position = {
row,
col,
fixed:!!fixed
}
onPaste(e,position)
}
onCellMouseOver = (e)=> {
const {column} = this.props
this.props.stopRowDrag(column.notRowDrag)
}
render() {
const { record, indentSize, clsPrefix, indent,
index, expandIcon, column ,fixed,showSum, bodyDisplayInRow,lazyStartIndex,lazyEndIndex, getCellClassName} = this.props;
const { dataIndex, render, fieldType, linkConfig, fontColor, bgColor,...other } = column;
let {className = ''} = column;
let text = objectPath.get(record, dataIndex);
let tdProps;
let colSpan;
let rowSpan,title;
let colMenu = this.renderColumnMenu(column.cellMenu, text, record, index);
// 如果是固定列在主表格上就渲染null
if(column.fixed && !fixed){
text = null
}else {
if (render && !showSum) {
text = render(text, record, index,{
dataIndex, render, fieldType, linkConfig, fontColor, bgColor,...other
});
if (this.isInvalidRenderCellText(text)) {
tdProps = text.props || {};
rowSpan = (tdProps.rowSpan>lazyEndIndex && lazyEndIndex>5)?lazyEndIndex-index:tdProps.rowSpan;
colSpan = tdProps.colSpan;
text = text.children;
}
}
// 根据 fieldType 来渲染数据
if(!render){
switch(column.fieldType){
case 'link':{
text = this.renderLinkType(text, record, index, column.linkConfig);
break;
}
case 'bool':{
text = this.renderBoolType(text, column.boolConfig);
break;
}
case 'currency':{
let config = {
precision: 2, // 精度值,需要大于0
thousand: true, // 是否显示千分符号
makeUp: true, // 末位是否补零
preSymbol: '', // 前置符号
nextSymbol: '', // 后置符号
}
text = this.renderNumber(text, {...config,...column.currencyConfig}, column.width);
break;
}
case 'number':{
let config = {
precision: 0, // 精度值,需要大于0
thousand: true, // 是否显示千分符号
makeUp: false, // 末位是否补零
preSymbol: '', // 前置符号
nextSymbol: '', // 后置符号
}
text = this.renderNumber(text, {...config,...column.numberConfig}, column.width);
break;
}
case 'date':{
text = this.renderDate(text, column.dateConfig);
break;
}
case 'select':{
text = this.renderSelect(text, column.selectConfig);
break;
}
default : {
break;
}
}
}
}
if (this.isInvalidRenderCellText(text)) {
text = null;
}
const indentText = expandIcon ? (
<span
style={{ paddingLeft: `${indentSize * indent}px` }}
className={`${clsPrefix}-indent indent-level-${indent}`}
/>
) : null;
if ((lazyStartIndex !==index) &&(rowSpan === 0 || colSpan === 0) ) {
return null;
}
if(tdProps && tdProps.mergeEndIndex && index<tdProps.mergeEndIndex && rowSpan === 0){
rowSpan = tdProps.mergeEndIndex - index;
text = ''
}
//注意:中间表格区域不需要渲染出固定列的单元格,以节省多余的性能消耗
if(!fixed&&column.fixed)return null;
if(column.contentAlign){
className = className+` text-${column.contentAlign}`;
}
else if(column.textAlign){
className = className+` text-${column.textAlign}`;
}
if((typeof text == 'string' || typeof text === 'number') && bodyDisplayInRow){
title = text
}
if(expandIcon && expandIcon.props.expandable){
className = className+` ${clsPrefix}-has-expandIcon`
}
if(colMenu){
className += ` ${prefix}-table-inline-icon`
}
if(typeof getCellClassName == 'function') {
const selfClassName = getCellClassName(record, index, column) || ''
className += ` ${selfClassName}`
}
if(colSpan==0)return null;
return <td
draggable={column.draggable}
colSpan={colSpan}
rowSpan={rowSpan}
className={className}
onClick={this.handleClick}
title={title}
onPaste={this.onPaste}
onMouseOver={this.onCellMouseOver}
style={{maxWidth:column.width, color:fontColor, backgroundColor:bgColor, ...column.style}}>
{indentText}
{expandIcon}
{text}
{colMenu}
</td>
}
};
TableCell.propTypes = propTypes;
export default TableCell;