// Copyright 2021 99cloud // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import React from 'react'; import classnames from 'classnames'; import PropTypes from 'prop-types'; import { get, isString, isEmpty, isEqual, has } from 'lodash'; import { Table, Typography } from 'antd'; import { getColumnSorter, getSortOrder, updateColumnSort, checkIsStatusColumn, getStatusRender, getRender, getNameRender, getNameRenderByRouter, getValueMapRender, getUnitRender, getProjectRender, } from 'utils/table'; import { getNoValue } from 'utils/index'; import styles from './index.less'; const { Paragraph } = Typography; export default class SimpleTable extends React.Component { static propTypes = { data: PropTypes.array.isRequired, filters: PropTypes.object, searchFilters: PropTypes.array, columns: PropTypes.array.isRequired, className: PropTypes.string, onChange: PropTypes.func, isLoading: PropTypes.bool, rowSelection: PropTypes.object, pagination: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), filterByBackend: PropTypes.bool, // eslint-disable-next-line react/no-unused-prop-types isSortByBack: PropTypes.bool, // eslint-disable-next-line react/no-unused-prop-types defaultSortKey: PropTypes.string, // eslint-disable-next-line react/no-unused-prop-types defaultSortOrder: PropTypes.string, onRow: PropTypes.func, childrenColumnName: PropTypes.string, }; static defaultProps = { filters: {}, searchFilters: [], isLoading: false, rowSelection: null, pagination: {}, filterByBackend: false, isSortByBack: false, defaultSortKey: '', defaultSortOrder: '', }; handleChange = (pagination, filters, sorter, extra) => { const { onChange } = this.props; onChange && onChange(pagination, filters, sorter, extra); }; getBaseColumns = (columns) => columns.map((column) => { const { sortable, dataIndex, valueRender, sorter, sortOrder, render, isStatus, isName, isPrice, isLink, routeName, linkPrefix, valueMap, unit, copyable, ...rest } = column; if (column.key === 'operation') { return column; } const newSorter = getColumnSorter(column, this.props); const newSortOrder = sortOrder || newSorter ? getSortOrder(dataIndex, this.props) : null; let newRender = render || getRender(valueRender); if (valueMap) { newRender = getValueMapRender(column); } if (unit) { newRender = getUnitRender(column); } if (checkIsStatusColumn(dataIndex, isStatus)) { newRender = getStatusRender(newRender); } if (dataIndex === 'description') { newRender = this.getNoValueRender(newRender); } if (dataIndex === 'project_name') { newRender = getProjectRender(newRender); } if ((dataIndex === 'name' && routeName) || isLink) { const { rowKey } = this.props; newRender = getNameRenderByRouter(newRender, column, rowKey); } if ((dataIndex === 'name' && linkPrefix) || isName) { newRender = getNameRender(newRender, column); } if (dataIndex === 'cost' || isPrice) { newRender = this.getPriceRender(newRender, column); } if (copyable) { newRender = (value) => { if (value && value !== '-') { return {value}; } return '-'; }; } const newColumn = { ...rest, dataIndex, align: column.align || 'left', }; if (newSorter) { newColumn.sorter = newSorter; } if (sortOrder) { newColumn.sortOrder = newSortOrder; } if (newRender) { newColumn.render = newRender; } updateColumnSort(newColumn, this.props); return newColumn; }); getNoValueRender = (render) => { if (render) { return render; } return (value) => getNoValue(value); }; getLinkUrl = (prefix, id) => { if (!prefix) { return null; } if (prefix[prefix.length - 1] === '/') { return `${prefix}${id}`; } return `${prefix}/${id}`; }; getColumns = () => { const { columns } = this.props; const baseColumns = this.getBaseColumns(columns); return baseColumns; }; // eslint-disable-next-line no-unused-vars getPriceRender = (render, column) => { if (render) { return render; } return (value) => { const valueStr = isString(value) ? value : (value || 0).toFixed(2); return {valueStr}; }; }; getDataSource = () => { const { data, filters, filterByBackend } = this.props; if (filterByBackend) { return data; } const tmpData = data.map((it) => { if (it.key) { return it; } return { ...it, key: it.id, }; }); if (!filters || isEmpty(filters)) { return tmpData; } return tmpData.filter((it) => this.filterData(it, filters)); }; onRow = (record, index) => { const { rowSelection, onRow } = this.props; if (onRow) { return onRow(record, index); } return { onClick: () => { const { selectedRowKeys = [], onChange, type, getCheckboxProps, } = rowSelection || {}; if (getCheckboxProps) { const { disabled } = getCheckboxProps(record); if (disabled) { return; } } const idx = selectedRowKeys.indexOf(record.key); if (type === 'checkbox') { const newKeys = [...selectedRowKeys]; if (idx > -1) { newKeys.splice(idx, 1); } else { newKeys.push(record.key); } onChange(newKeys); } else if (type === 'radio') { onChange([record.key]); } }, }; }; getPagination(dataSource) { const { pagination } = this.props; return ( pagination && { ...pagination, total: dataSource.length, } ); } filterData = (data, filters) => { const { searchFilters } = this.props; const failed = Object.keys(filters).find((key) => { const value = get(data, key); const filterValue = filters[key]; const { filterFunc } = searchFilters.find((i) => i.name === key); if (filterFunc) { return !filterFunc(value, filterValue, data); } const isInclude = this.checkFilterInclude(key); if (isString(value) && isString(filterValue)) { if (isInclude) { return value.toLowerCase().indexOf(filterValue.toLowerCase()) < 0; } return value.toLowerCase() !== filterValue.toLowerCase(); } return !isEqual(value, filterValue); }); return !failed; }; checkFilterInclude = (key) => { const { searchFilters } = this.props; const item = searchFilters.find((it) => it.name === key); if (has(item, 'include')) { return item.include; } if (has(item, 'options')) { return false; } return true; }; render() { const { className, isLoading, rowSelection, footer, childrenColumnName } = this.props; const currentColumns = this.getColumns(); const dataSource = this.getDataSource(); return ( ); } }