1 | import React, { Component } from 'react';
|
2 | import PropTypes from 'prop-types';
|
3 | import objectPath from 'object-path';
|
4 | import i18n from './lib/i18n';
|
5 | import { getComponentLocale } from 'bee-locale/build/tool';
|
6 | import { formatMoney } from './lib/utils';
|
7 | import Dropdown from 'bee-dropdown';
|
8 | import Menu from 'bee-menus';
|
9 | const { Item } = Menu;
|
10 | const propTypes = {
|
11 | record: PropTypes.object,
|
12 | clsPrefix: PropTypes.string,
|
13 | index: PropTypes.number,
|
14 | indent: PropTypes.number,
|
15 | indentSize: PropTypes.number,
|
16 | column: PropTypes.object,
|
17 | expandIcon: PropTypes.node,
|
18 | onPaste:PropTypes.func
|
19 | };
|
20 |
|
21 | class TableCell extends Component{
|
22 | constructor(props){
|
23 | super(props);
|
24 | this.isInvalidRenderCellText = this.isInvalidRenderCellText.bind(this);
|
25 | this.handleClick = this.handleClick.bind(this);
|
26 | this.state = {
|
27 | showDropdowm: false
|
28 | }
|
29 | }
|
30 | isInvalidRenderCellText(text) {
|
31 | return text && !React.isValidElement(text) &&
|
32 | Object.prototype.toString.call(text) === '[object Object]';
|
33 | }
|
34 | handleClick(e) {
|
35 | const { record, column: { onCellClick } } = this.props;
|
36 | if (onCellClick) {
|
37 | onCellClick(record, e);
|
38 | }
|
39 | }
|
40 |
|
41 |
|
42 | renderLinkType = ( data, record, index, config={}) => {
|
43 | const { url, urlIndex, linkType, className, underline, descIndex, desc, linkColor } = config;
|
44 | let linkUrl = '';
|
45 | if(url){
|
46 | linkUrl = url(data, record, index);
|
47 | }
|
48 | else if(urlIndex){
|
49 | linkUrl = record[urlIndex];
|
50 | }
|
51 | if(linkUrl){
|
52 | let link = () => {
|
53 | window.open(linkUrl,linkType || '_blank');
|
54 | }
|
55 | let cls = 'u-table-link u-table-fieldtype ';
|
56 | if(className){
|
57 | cls += `${className} `;
|
58 | }
|
59 | if(underline){
|
60 | cls += 'u-table-link-underline ';
|
61 | }
|
62 | let title = '';
|
63 |
|
64 | if(desc === true){
|
65 | title = linkUrl;
|
66 | }
|
67 | else if( typeof desc === 'string'){
|
68 | title = desc;
|
69 | }
|
70 | else if( typeof desc === 'function'){
|
71 | title = desc(data, record, index);
|
72 | }
|
73 | else if(descIndex){
|
74 | title = record[descIndex];
|
75 | }
|
76 | return <span onClick={link} className={cls} style={{color:linkColor || ''}} title={title}>{data}</span>
|
77 | }
|
78 | return data;
|
79 | }
|
80 |
|
81 |
|
82 | renderBoolType = ( data, config={} ) => {
|
83 | let locale = getComponentLocale(this.props, this.context, 'Table', () => i18n);
|
84 | let boolConfig = {...{ trueText: locale['bool_true'], falseText: locale['bool_false']},...config};
|
85 | if(typeof data === 'string'){
|
86 | if(data === 'false' || data === '0'){
|
87 | return boolConfig.falseText;
|
88 | }
|
89 | }
|
90 | else if(!data){
|
91 | return boolConfig.falseText;
|
92 | }
|
93 | return boolConfig.trueText;
|
94 | }
|
95 |
|
96 |
|
97 | renderNumber = (data, config={}, width=200) => {
|
98 | const { precision, thousand, makeUp, preSymbol, nextSymbol } = config;
|
99 | let number = formatMoney(data, precision, thousand);
|
100 | if(makeUp === false && number.indexOf('.') !== -1) {
|
101 | number = number.replace(/0*$/,'').replace(/\.$/,'');
|
102 | }
|
103 | let numberWidth = parseInt(width) - 16;
|
104 | let res = <span className='u-table-currency-number' >{number}</span>;
|
105 | let pre = preSymbol ? <span className='u-table-currency-pre'>{preSymbol}</span> : null;
|
106 | let next = nextSymbol ? <span className='u-table-currency-next'>{nextSymbol}</span> : null;
|
107 | let title = '';
|
108 | title += typeof preSymbol === 'string' ? preSymbol : '';
|
109 | title += number;
|
110 | title += typeof nextSymbol === 'string' ? nextSymbol : '';
|
111 | return <span className='u-table-currency u-table-fieldtype' style={{width:numberWidth}} title={title}>
|
112 | {pre}
|
113 | {res}
|
114 | {next}
|
115 | </span>;
|
116 | }
|
117 |
|
118 |
|
119 | renderDate = ( data, config={}) => {
|
120 | const { moment, format } = config;
|
121 | if(!moment)return data;
|
122 | return moment(data).format(format || 'YYYY-MM-DD');
|
123 | }
|
124 |
|
125 |
|
126 | renderSelect = ( data, config={}) => {
|
127 | if(config.options){
|
128 | data = config.options[data] || config.defaultShow;
|
129 | }
|
130 | return data;
|
131 | }
|
132 |
|
133 |
|
134 |
|
135 | renderColumnMenu = (colMenu, text, record, index) => {
|
136 | if (!colMenu) return null;
|
137 | let { menu, trigger = 'hover', className = '', icon = <i className='uf uf-3dot-h' />, iconSize = 21 } = colMenu;
|
138 | let items = [];
|
139 | items = menu.map((item) => {
|
140 | return <Item key={item.key} onClick={() => { this.onClickColMenu(item.callback, text, record, index) }}>
|
141 | {item.icon}
|
142 | {item.text}
|
143 | </Item>
|
144 | })
|
145 | if (items.length === 0) return null;
|
146 | className += ' u-table-inline-op-dropdowm'
|
147 | let menus = <Menu className={className}>{items}</Menu>;
|
148 | let top = `calc(50% - ${iconSize / 2}px)`;
|
149 | let visibility = this.state.showDropdowm ? 'visible' : '';
|
150 | let iconClassName = `u-table-inline-op-icon u-table-inline-op-icon-hover`;
|
151 | return <Dropdown
|
152 | trigger={[trigger]}
|
153 | overlay={menus}
|
154 | animation="slide-up"
|
155 | onVisibleChange={this.changeShowDropdowm}
|
156 | >
|
157 | {<span className={iconClassName} style={{ fontSize: iconSize, top: top, visibility: visibility }}>{icon}</span>}
|
158 | </Dropdown>
|
159 | }
|
160 |
|
161 | // 下拉按钮状态改变,点击后保持图标常驻
|
162 | changeShowDropdowm = (val) => {
|
163 | this.setState({
|
164 | showDropdowm: val
|
165 | })
|
166 | }
|
167 |
|
168 | // 菜单点击事件
|
169 | onClickColMenu = (callback, text, record, index) => {
|
170 | if (callback) {
|
171 | callback(text, record, index);
|
172 | }
|
173 | this.setState({
|
174 | showDropdowm: false,
|
175 | })
|
176 | }
|
177 | onPaste=(e)=>{
|
178 | let { index:row,onPaste,fixed,col } = this.props
|
179 | let position = {
|
180 | row,
|
181 | col,
|
182 | fixed:!!fixed
|
183 | }
|
184 | onPaste(e,position)
|
185 | }
|
186 |
|
187 | onCellMouseOver = (e)=> {
|
188 | const {column} = this.props
|
189 | this.props.stopRowDrag(column.notRowDrag)
|
190 | }
|
191 |
|
192 | render() {
|
193 | const { record, indentSize, clsPrefix, indent,
|
194 | index, expandIcon, column ,fixed,showSum, bodyDisplayInRow,lazyStartIndex,lazyEndIndex, getCellClassName} = this.props;
|
195 | const { dataIndex, render, fieldType, linkConfig, fontColor, bgColor,...other } = column;
|
196 | let {className = ''} = column;
|
197 |
|
198 | let text = objectPath.get(record, dataIndex);
|
199 | let tdProps;
|
200 | let colSpan;
|
201 | let rowSpan,title;
|
202 |
|
203 | if (render && !showSum) {
|
204 | text = render(text, record, index,{
|
205 | dataIndex, render, fieldType, linkConfig, fontColor, bgColor,...other
|
206 | });
|
207 | if (this.isInvalidRenderCellText(text)) {
|
208 | tdProps = text.props || {};
|
209 | rowSpan = (tdProps.rowSpan>lazyEndIndex && lazyEndIndex>5)?lazyEndIndex-index:tdProps.rowSpan;
|
210 | colSpan = tdProps.colSpan;
|
211 | text = text.children;
|
212 | }
|
213 | }
|
214 |
|
215 | let colMenu = this.renderColumnMenu(column.cellMenu, text, record, index);
|
216 | // 根据 fieldType 来渲染数据
|
217 | if(!render){
|
218 | switch(column.fieldType){
|
219 | case 'link':{
|
220 | text = this.renderLinkType(text, record, index, column.linkConfig);
|
221 | break;
|
222 | }
|
223 | case 'bool':{
|
224 | text = this.renderBoolType(text, column.boolConfig);
|
225 | break;
|
226 | }
|
227 | case 'currency':{
|
228 | let config = {
|
229 | precision: 2, // 精度值,需要大于0
|
230 | thousand: true, // 是否显示千分符号
|
231 | makeUp: true, // 末位是否补零
|
232 | preSymbol: '', // 前置符号
|
233 | nextSymbol: '', // 后置符号
|
234 | }
|
235 | text = this.renderNumber(text, {...config,...column.currencyConfig}, column.width);
|
236 | break;
|
237 | }
|
238 | case 'number':{
|
239 | let config = {
|
240 | precision: 0, // 精度值,需要大于0
|
241 | thousand: true, // 是否显示千分符号
|
242 | makeUp: false, // 末位是否补零
|
243 | preSymbol: '', // 前置符号
|
244 | nextSymbol: '', // 后置符号
|
245 | }
|
246 | text = this.renderNumber(text, {...config,...column.numberConfig}, column.width);
|
247 | break;
|
248 | }
|
249 | case 'date':{
|
250 | text = this.renderDate(text, column.dateConfig);
|
251 | break;
|
252 | }
|
253 | case 'select':{
|
254 | text = this.renderSelect(text, column.selectConfig);
|
255 | break;
|
256 | }
|
257 | default : {
|
258 | break;
|
259 | }
|
260 | }
|
261 | }
|
262 |
|
263 | if (this.isInvalidRenderCellText(text)) {
|
264 | text = null;
|
265 | }
|
266 |
|
267 | const indentText = expandIcon ? (
|
268 | <span
|
269 | style={{ paddingLeft: `${indentSize * indent}px` }}
|
270 | className={`${clsPrefix}-indent indent-level-${indent}`}
|
271 | />
|
272 | ) : null;
|
273 |
|
274 | if ((lazyStartIndex !==index) &&(rowSpan === 0 || colSpan === 0) ) {
|
275 | return null;
|
276 | }
|
277 | if(tdProps && tdProps.mergeEndIndex && index<tdProps.mergeEndIndex && rowSpan === 0){
|
278 | rowSpan = tdProps.mergeEndIndex - index;
|
279 | text = ''
|
280 | }
|
281 | //不是固定表格并且当前列是固定,则隐藏当前列
|
282 | if(column.fixed && !fixed){
|
283 | className = className+` ${clsPrefix}-fixed-columns-in-body`;
|
284 | }
|
285 | if(column.contentAlign){
|
286 | className = className+` text-${column.contentAlign}`;
|
287 | }
|
288 | else if(column.textAlign){
|
289 | className = className+` text-${column.textAlign}`;
|
290 | }
|
291 | if((typeof text == 'string' || typeof text === 'number') && bodyDisplayInRow){
|
292 | title = text
|
293 | }
|
294 | if(expandIcon && expandIcon.props.expandable){
|
295 | className = className+` ${clsPrefix}-has-expandIcon`
|
296 | }
|
297 | if(colMenu){
|
298 | className += ' u-table-inline-icon'
|
299 | }
|
300 |
|
301 | if(typeof getCellClassName == 'function') {
|
302 | const selfClassName = getCellClassName(record, index, column) || ''
|
303 | className += ` ${selfClassName}`
|
304 | }
|
305 |
|
306 | if(colSpan==0)return null;
|
307 | return <td
|
308 | draggable={column.draggable}
|
309 | colSpan={colSpan}
|
310 | rowSpan={rowSpan}
|
311 | className={className}
|
312 | onClick={this.handleClick}
|
313 | title={title}
|
314 | onPaste={this.onPaste}
|
315 | onMouseOver={this.onCellMouseOver}
|
316 | style={{maxWidth:column.width, color:fontColor, backgroundColor:bgColor, ...column.style}}>
|
317 | {indentText}
|
318 | {expandIcon}
|
319 | {text}
|
320 | {colMenu}
|
321 | </td>
|
322 | }
|
323 | };
|
324 |
|
325 | TableCell.propTypes = propTypes;
|
326 |
|
327 | export default TableCell;
|