From cdafc0f73cdaf3bffeb8a33d362552e435cb5125 Mon Sep 17 00:00:00 2001 From: zhangwenshuai Date: Mon, 23 Mar 2020 02:33:49 +0800 Subject: [PATCH] update --- src/apollo-table/component/Cell.less | 89 +---- src/apollo-table/component/Cell.tsx | 323 ++++----------- src/apollo-table/component/Table.less | 117 +----- src/apollo-table/component/Table.tsx | 373 +++--------------- .../component/base/_utils/setFormatter.tsx | 9 +- src/apollo-table/component/base/config.tsx | 3 +- .../component/base/detail/index.tsx | 9 +- .../component/base/edit/editInterface.tsx | 13 +- .../component/base/edit/textarea/index.tsx | 44 ++- .../component/base/edit/textarea/styles.less | 22 +- src/apollo-table/component/base/index.tsx | 62 ++- src/apollo-table/component/index.tsx | 24 +- src/apollo-table/component/interface.tsx | 97 ++--- src/apollo-table/utils/utils.tsx | 65 ++- src/test/demo1.less | 3 + src/test/demo1.tsx | 23 +- 16 files changed, 375 insertions(+), 901 deletions(-) create mode 100644 src/test/demo1.less diff --git a/src/apollo-table/component/Cell.less b/src/apollo-table/component/Cell.less index df64046..d7dc642 100644 --- a/src/apollo-table/component/Cell.less +++ b/src/apollo-table/component/Cell.less @@ -1,79 +1,20 @@ @import '../../common'; -.cell { - max-width: 100%; - min-width: 100%; +.detailCell { + display: flex; + align-items: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: 100%; height: 100%; - - &.disabled { - background: @disabledColor; - } - - .cellContent { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - width: 100%; - height: 100%; - font-size: 14px; - } + font-size: @textFontGen; + padding: 0 10px; } - -.editPop { - width: 300px; - - :global(.ant-popover-inner){ - box-shadow: none; - } - - :global(.ant-popover-inner-content) { - padding: 0; - border: 1px solid @primaryColor; - border-radius: 2px; - } -} - -.menuPop { - width: 110px; - - :global(.ant-popover-inner-content) { - padding: @paddingSmX 0; - border-radius: @borderRadius; - background: #333333; - } - - .menuContainer { - .splitLine { - height: 1px; - background: white; - opacity: 0.2; - } - - .menu { - display: flex; - height: 28px; - padding: 0 @paddingGen; - align-items: center; - cursor: pointer; - - &:hover { - background: rgba(216, 216, 216, 0.1); - } - - .menuIcon { - width: 12px; - margin-right: @marginSmX; - color: white; - font-size: @textFontGen; - display: flex; - } - - .menuTitle { - font-size: @textFontGen; - font-family: PingFangSC-Regular, PingFang SC; - font-weight: 400; - color: white; - } - } - } +.editCell { + position: absolute; + z-index: 1; + width: 100%; + top: 0; + left: 0; } diff --git a/src/apollo-table/component/Cell.tsx b/src/apollo-table/component/Cell.tsx index db01894..6f25299 100644 --- a/src/apollo-table/component/Cell.tsx +++ b/src/apollo-table/component/Cell.tsx @@ -1,272 +1,113 @@ -import React from 'react'; -import { message, Popover, Modal } from 'antd'; +import React, { useState } from 'react'; +import { message } from 'antd'; import { config } from './config'; -import style from './Cell.less'; +import s from './Cell.less'; +import { CellProps } from './interface'; import _ from 'lodash'; -import { CellProps, CellState } from './interface'; -import EditCell from './EditCell'; import FormHelper from '../utils/formHelper'; -import insertMenu from '../assets/insertMenu.png'; -import copyMenu from '../assets/copyMenu.png'; -import extendMenu from '../assets/extendMenu.png'; -import delMenu from '../assets/delMenu.png'; +interface CellData { + text: string; + value: any; +} +const Cell = (props: CellProps) => { + const { columnConfig, cellClassName, cellStyle, rowData, cellData, emitChangeCell } = props; + const { width, columnType, columnName, requiredFlag } = columnConfig || {}; -export default class Cell extends React.Component { - static getDerivedStateFromProps(nextProps: CellProps, prevState: CellState) { - const { prevProps } = prevState; - let nextState: CellState = { - ...prevState, - prevProps: nextProps, - editValue: nextProps.cellData, - }; - if (JSON.stringify(prevProps.cellData) !== JSON.stringify(nextProps.cellData)) { - nextState.editValue = nextProps.cellData; - } - return nextState; - } - constructor(props: CellProps) { - super(props); - const { cellData } = props; - this.state = { - prevProps: props, - isEditVisible: false, - isMenuVisible: false, - editValue: cellData, - originValue: [], - }; + if (config[String(columnType)] === undefined) { + return null; } + const Comp = config[String(columnType)].component; + const [status, setStatus] = useState('detail'); - handleVisibleChange = (visible: boolean) => { - if (!visible) { - this.emitChange(); - } - this.setState({ isEditVisible: visible, isMenuVisible: false }); + const changeCellData = (changedValue) => { + console.log('onChange', changedValue); }; - menuVisibleChange = (visible: boolean) => { - this.setState({ isMenuVisible: visible, isEditVisible: false }); - }; - handleChange = ({ newValue, originValue, emitFlag }: any) => { - this.setState( - { - editValue: newValue, - originValue, - }, - () => { - if (emitFlag) { - this.emitChange(); - } - }, - ); - }; - - emitChange = () => { - let { originValue, editValue } = this.state; - let { cellData } = this.props; - if (_.isEqual(cellData, editValue)) { + const emitChangeCellData = (changedValue, optionValue) => { + console.log('onEmitChange', changedValue); + const temp: Array = []; + cellData.map((item) => { + temp.push({ text: item.text, value: item.value }); + }); + if (_.isEqual(temp, changedValue)) { return; } + changeValue(changedValue, optionValue); + }; - const { - updateData, - rowData, - columnConfig, - columnConfig: { key }, - } = this.props; + const changeValue = async (changedValue, optionValue) => { + // 校验必填项 + if (requiredFlag) { + if (!changedValue || changedValue.length === 0) { + message.error('该字段为必填项'); + return; + } + if (changedValue.length === 1) { + const data = changedValue[0]; + if (!data.value && !data.text) { + message.error('该字段为必填项'); + return; + } + } + } const extraData = FormHelper.changeTableData({ item: columnConfig, - changedKey: key, - changedValue: editValue, - originValue, + changedKey: columnName, + changedValue, + originValue: optionValue, }); extraData.push({ - columnCode: key, - cellValueList: editValue, + columnCode: columnName, + cellValueList: changedValue, }); - if (typeof updateData === 'function') { - updateData({ + if (typeof emitChangeCell === 'function') { + emitChangeCell({ rowId: rowData.id, value: extraData, }); } - this.setState({ - editValue: [], - originValue: [], - }); - }; - - copy = (record: any) => { - const { showForm } = this.props; - if (typeof showForm === 'function') { - showForm({ rowData: record.rowData }); - } }; - delData = (record: any) => { - const { delData, getData, hasGroup, tableId } = this.props; - // 拆分情况需检查数据是否原始数据 - if (hasGroup) { - if (record.id === record.groupId) { - Modal.warning({ title: '删除', content: '不能删除原始拆分数据' }); - return; - } - } - Modal.confirm({ - title: '确认要删除该条数据吗?', - autoFocusButton: null, - onOk: async () => { - if (typeof delData === 'function') { - const response = await delData({ tableId, data: { id: record.id } }); - if (response && response.success) { - message.success('删除成功'); - if (typeof getData === 'function') { - getData({ isClean: true }); - } - } - } - }, - }); - }; - - clickExtraMenu = (menu: any, record: any) => { - if (menu.check && typeof menu.check === 'function') { - menu.check({ data: [record] }, this.menuClickFunc.bind(this, menu, record)); - } else { - this.menuClickFunc(menu, record); - } - }; - - menuClickFunc = async (menu: any, record: any) => { - const { flush } = this.props; - if (typeof menu.onClick === 'function') { - await menu.onClick({ data: [record], flush }); - } - if (!menu.noNeedFlush) { - if (typeof flush === 'function') { - flush(); - } - } - }; - - renderPopEdit = ({ columnAttrObj, component }: any) => { - let { columnConfig, rowData, cellData, columns, tableId } = this.props; - let { editValue } = this.state; - return ( - - ); - }; - - renderPopMenu = () => { - let { cellData, showForm, extraMenu, noAdd, noDel, hasGroup } = this.props; + const renderDetailCell = () => { return ( -
- {!noAdd && ( -
- 插入 - 插入 -
- )} - {!noAdd && ( -
- 复制 - 复制 -
- )} -
- 展开 - 展开 -
- {!noDel && (!hasGroup || (hasGroup && cellData.id !== cellData.groupId)) && ( -
- 删除 - 删除 -
- )} - {extraMenu && extraMenu.length > 0 && ( - -
- {extraMenu.map((item: any, i: number) => { - if (typeof item.hidden === 'function' && item.hidden({ record: cellData, menu: item })) { - return; - } - return ( -
- {item.icon && - (typeof item.icon === 'string' ? ( - - ) : ( - {item.icon} - ))} - {item.label} -
- ); - })} - - )} +
{ + setStatus('edit'); + }} + > +
); }; - renderCellContent = () => { - let { columnConfig, cellData, noEdit, tableId } = this.props; - let { isEditVisible, isMenuVisible } = this.state; - - const { readOnlyFlag, columnType } = columnConfig || {}; - let { columnAttrObj } = columnConfig || {}; - if (config[String(columnType)] === undefined) { - return false; - } - columnAttrObj = columnAttrObj || {}; - columnAttrObj.disabled = readOnlyFlag || noEdit || false; - const { component } = config[String(columnType)]; + const renderEditCell = () => { return ( - { + setStatus('detail'); + }} > - -
- -
-
-
+ +
); }; - render() { - const { columnConfig, overlayClassName } = this.props; - const { width } = columnConfig || {}; - return ( -
- {this.renderCellContent()} -
- ); - } -} + return ( +
+ {status === 'detail' && renderDetailCell()} + {status === 'edit' && renderEditCell()} +
+ ); +}; + +export default Cell; diff --git a/src/apollo-table/component/Table.less b/src/apollo-table/component/Table.less index 5815db7..6b72e72 100644 --- a/src/apollo-table/component/Table.less +++ b/src/apollo-table/component/Table.less @@ -1,115 +1,5 @@ @import '../../common'; -.tableCell { - &.sortCol { - background: @hoverColor; - } -} - -.indexCell { - padding: 0 10px; - display: flex; - justify-content: flex-start; - align-items: center; - height: 100%; - - &.disabled { - background: @disabledColor; - - &:hover { - box-shadow: none; - } - } - - &:hover { - box-shadow: inset 0 0 0 1px @operateColor; - - .showFormBtnCls { - .showFormBtn { - display: initial; - } - } - - .unchecked { - display: initial; - } - - .no { - display: none; - } - - .addCls { - .addIcon { - display: initial; - } - } - } - - .cellNo { - display: flex; - align-items: center; - min-width: 50px; - } - - .cellContent { - flex: 1; - overflow: hidden; - height: 100%; - display: flex; - align-items: center; - .firstCell { - min-width: 0; - max-width: 80%; - } - .empty { - min-width: 80%; - } - .historyCount { - margin: @marginSmX; - cursor: pointer; - .countNo { - color: @primaryColor; - } - } - } - .addCls { - width: 20px; - text-align: center; - cursor: pointer; - height: 16px; - .addIcon { - display: none; - color: @primaryColor; - } - } - - .unchecked { - display: none; - } - - .no { - width: 17px; - display: block; - text-align: center; - white-space: nowrap; - } - - .showFormBtnCls { - margin: 0 5px; - width: 18px; - cursor: pointer; - - .showFormBtn { - display: none; - width: 18px; - border-radius: 50%; - &:hover { - background: #d0f0fd; - } - } - } -} - .GridRow { position: relative; display: flex; @@ -171,11 +61,6 @@ width: 100%; outline: none; } -.bodyRow { - width: 100%; - box-sizing: border-box; -} .bodyCell { - width: 100%; - height: 100%; + font-size: 20px; } diff --git a/src/apollo-table/component/Table.tsx b/src/apollo-table/component/Table.tsx index 53d7092..bcb2b42 100644 --- a/src/apollo-table/component/Table.tsx +++ b/src/apollo-table/component/Table.tsx @@ -11,6 +11,9 @@ import Cell from './Cell'; import extendIcon from '../assets/extend.png'; import IconFont from './base/extra/iconFont'; import scrollbarSize from 'dom-helpers/scrollbarSize'; +import { getMergeClassName, getMergeStyle } from '@/apollo-table/utils/utils'; + +const borderStyle = '1px solid #e8e8e8'; export default class AirTable extends Component { private indexCount: any; @@ -18,35 +21,26 @@ export default class AirTable extends Component { memoizeLeftCount: Function; memoizeData: Function; static getDerivedStateFromProps(nextProps: TableProps, prevState: TableState) { - const { prevProps } = prevState; + const { columns, dataSource } = prevState; let nextState: TableState = { ...prevState, - prevProps: nextProps, }; - if (JSON.stringify(prevProps.dataSource) !== JSON.stringify(nextProps.dataSource)) { + if (JSON.stringify(dataSource) !== JSON.stringify(nextProps.dataSource)) { nextState.dataSource = nextProps.dataSource; } - if (JSON.stringify(prevProps.columns) !== JSON.stringify(nextProps.columns)) { + if (JSON.stringify(columns) !== JSON.stringify(nextProps.columns)) { nextState.columns = nextProps.columns; } - if (JSON.stringify(prevProps.checked) !== JSON.stringify(nextProps.checked)) { - nextState.checked = nextProps.checked; - } - nextState.tableHeight = document.body.clientHeight; - nextState.tableWidth = document.body.clientWidth; return nextState; } constructor(props: TableProps) { super(props); - const { columns, dataSource, tableId, checked = [] } = props; + const { columns, dataSource, width, height } = props; this.state = { columns, dataSource, - checked, - tableId, - prevProps: props, - tableWidth: 0, - tableHeight: 0, + tableWidth: width || document.body.clientWidth, + tableHeight: height || document.body.clientHeight - 40, }; this.config = { overscanColumnCount: 5, @@ -61,10 +55,13 @@ export default class AirTable extends Component { componentDidMount() { window.addEventListener('resize', this.resize, false); } + componentWillUnmount() { + window.removeEventListener('resize', this.resize, false); + } //重置窗口高度 resize = () => { this.setState({ - tableHeight: document.body.clientHeight, + tableHeight: document.body.clientHeight - 40, tableWidth: document.body.clientWidth, }); }; @@ -104,86 +101,11 @@ export default class AirTable extends Component { }); return cloneData; }; - // 复选框选择 - changeCheckbox = (record: any, flag: boolean) => { - const { changeChecked, hasGroup } = this.props; - const { checked, dataSource } = this.state; - let temp = (checked && checked.slice()) || []; - if (flag) { - if (hasGroup) { - const selected = dataSource.filter((item: any) => { - return item.groupId === record.groupId; - }); - temp = temp.concat(selected); - } else { - temp.push(record); - } - } else { - if (hasGroup) { - temp = temp.filter((item: any) => { - return item.groupId !== record.groupId; - }); - } - const index = temp.findIndex((item: any) => { - return item.id === record.id; - }); - temp.splice(index, 1); - } - if (typeof changeChecked === 'function') { - changeChecked({ checked: temp }); - } - }; - // 检测当前分组是否全部选中 - getCheckedAll = () => { - const { checked, dataSource } = this.state; - const temp = (checked && checked.slice()) || []; - const result = temp.filter((v: any) => { - return dataSource.some((item: any) => { - return item.id === v.id; - }); - }); - return result.length === dataSource.length && result.length !== 0; - }; - // 全选/反选 - changeCheckboxAll = () => { - const { checked, dataSource } = this.state; - const { changeChecked } = this.props; - const temp = (checked && checked.slice()) || []; - const flag = this.getCheckedAll(); - let result: any[] = []; - if (flag) { - result = temp.concat(dataSource).filter((v: any) => { - return ( - temp.some((item: any) => { - return item.id === v.id; - }) && - !dataSource.some((item: any) => { - return item.id === v.id; - }) - ); - }); - } else { - result = temp.concat( - dataSource.filter((v: any) => { - return !temp.some((item: any) => { - return item.id === v.id; - }); - }), - ); - } - if (typeof changeChecked === 'function') { - changeChecked({ checked: result }); - } - }; // 下拉加载 - onScrollRow = ({ clientHeight, scrollTop }: any) => { + onScroll = ({ clientHeight, scrollHeight, scrollTop }: any) => { const height = clientHeight + scrollTop || 0; - const { dataSource = [] } = this.state; - const { rowHeight } = this.config; - const { onScroll } = this.props; - const offset = 100; - if (dataSource.length === 0) return; - if (height + offset >= dataSource.length * rowHeight && typeof onScroll === 'function') { + const { onScroll, distanceToLoad = 100 } = this.props; + if (height + distanceToLoad >= scrollHeight && typeof onScroll === 'function') { onScroll(); } }; @@ -192,253 +114,86 @@ export default class AirTable extends Component { const { columns } = this.state; if (columns.length === 0) return null; const { columnType, columnChsName, width, columnAttrObj } = columns[columnIndex] || {}; - if (columnIndex === 0) { - return ( -
-
- -
-
- -
-
- ); - } return (
); }; - // 渲染首列复选框 - renderCheckbox = (record: any, rowIndex: number) => { - const { checked } = this.state; - const { hasGroup } = this.props; - const flag = - checked && - checked.some((item: any) => { - return item.id === record.id; - }); - let checkbox = flag ? ( - - ) : ( - - -
{record.index || rowIndex + 1}
-
- ); - if (hasGroup) { - if ( - this.indexCount[record.groupId][Math.floor((this.indexCount[record.groupId].length - 1) / 2)] !== - record.id - ) { - // 填充透明的复选框使样式保持一致 - checkbox = ; - } - } - return checkbox; - }; - renderBodyRow = (data: any[], { columnIndex, key, rowIndex, style }: any) => { - const { rowClassName, rowStyle, rowBorder = false } = this.props; - const rowClass = typeof rowClassName === 'function' ? rowClassName({ index: rowIndex }) : rowClassName; - const rowStyleObject = typeof rowStyle === 'function' ? rowStyle({ index: rowIndex }) : rowStyle; + const { rowClassName, rowStyle, bordered = false } = this.props; // 默认边框覆盖掉传过来的样式 const rowBorderStyle: any = { borderLeft: 'none', - borderRight: '1px solid #e8e8e8', + borderRight: 'none', borderTop: 'none', - borderBottom: '1px solid #e8e8e8', + borderBottom: borderStyle, }; - if (rowIndex === 0) { - rowBorderStyle.borderTop = '1px solid #e8e8e8'; - } - if (columnIndex === 0) { - rowBorderStyle.borderLeft = '1px solid #e8e8e8'; - } - // border需要单独设置 - if (typeof rowBorder === 'string') { - rowBorderStyle.borderTop = rowBorder; - rowBorderStyle.borderBottom = rowBorder; + if (bordered) { + rowBorderStyle.borderRight = borderStyle; if (columnIndex === 0) { - rowBorderStyle.borderLeft = rowBorder; + rowBorderStyle.borderLeft = borderStyle; } - if (columnIndex === data.length - 1) { - rowBorderStyle.borderRight = rowBorder; - } - } - let className = styles.bodyRow; - if (rowClass) { - className = `${className} ${rowClass}`; } - return ( -
- {this.renderBodyCellContainer(data, { columnIndex, key, rowIndex, style })} -
- ); + const className = getMergeClassName(styles.bodyCell, rowClassName, { rowIndex }); + const rowStyleObject = getMergeStyle(style, rowStyle, { rowIndex }); + const mergedRowStyle = { + ...rowStyleObject, + ...rowBorderStyle, + }; + return this.renderBodyCellContainer(data, { + columnIndex, + key, + rowIndex, + rowClassName: className, + rowStyle: mergedRowStyle, + }); }; - renderBodyCellContainer = (data: any[], { columnIndex, key, rowIndex, style }: any) => { + renderBodyCellContainer = (data: any[], { columnIndex, key, rowIndex, rowClassName, rowStyle }: any) => { const { columns } = this.state; const columnConfig = columns[columnIndex]; const { cellClassName, cellStyle } = columnConfig; - const cellClass = typeof cellClassName === 'function' ? cellClassName({ index: columnIndex }) : cellClassName; - const cellStyleObject = typeof cellStyle === 'function' ? cellStyle({ index: columnIndex }) : cellStyle; - let className = styles.bodyCell; - if (cellClass) { - className = `${className} ${cellClass}`; - } - return ( -
- {this.renderBodyCell(data, { columnIndex, key, rowIndex, style })} -
- ); + const className = getMergeClassName(rowClassName, cellClassName, { columnIndex }); + const mergedCellStyle = getMergeStyle(rowStyle, cellStyle, { columnIndex }); + return this.renderBodyCell(data, { + columnIndex, + key, + rowIndex, + cellClassName: className, + cellStyle: mergedCellStyle, + }); }; //渲染数据 - renderBodyCell = (data: any[], { columnIndex, key, rowIndex }: any) => { + renderBodyCell = (data: any[], { columnIndex, key, rowIndex, cellClassName, cellStyle }: any) => { const { columns, dataSource } = this.state; - const { - updateData, - hasGroup, - showForm = () => {}, - delData, - getData, - extraMenu, - noAdd, - noEdit, - noDel, - tableId, - flush, - showHistory = () => {}, - } = this.props; + const { emitChangeCell } = this.props; if (columns.length === 0 || data.length === 0) { return; } - const filterRowData: any = data[rowIndex]; // 过滤掉隐藏项并追加行号的行数据,只在渲染逻辑中使用 const rowData: any = dataSource[rowIndex]; // 原始行数据,真实使用的数据 const columnConfig: any = columns[columnIndex]; // 列属性 const columnData: any = rowData.rowData[columnIndex]; // 列数据 const cellData: any = columnData.cellValueList; // 列数据 - let className = styles.indexCell; - if (columnIndex === 0) { - return ( -
-
- {this.renderCheckbox(filterRowData, rowIndex)} -
- edit -
-
-
- 0 ? styles.firstCell : styles.empty} - columns={columns} - columnConfig={columnConfig} - rowData={rowData} - cellData={cellData} - updateData={updateData} - showForm={showForm} - delData={delData} - hasGroup={hasGroup} - getData={getData} - extraMenu={extraMenu} - noAdd={noAdd} - noEdit={noEdit} - noDel={noDel} - tableId={tableId} - flush={flush} - /> - {(tableId === 1 || tableId === 14) && ( -
- {filterRowData.groupHistoryCount && filterRowData.groupHistoryCount > 1 && ( - - {`(${ - filterRowData.groupHistoryCount > 99 - ? '99+' - : filterRowData.groupHistoryCount - })`} - - )} -
- )} -
- {(tableId === 1 || tableId === 14) && ( -
- -
- )} -
- ); - } return ( -
-
- -
-
+ ); }; render() { - const { columns, dataSource, checked, tableHeight, tableWidth } = this.state; - const { changeChecked, operateConfig = {}, hasGroup } = this.props; - const { groupConfig } = operateConfig; + const { columns, dataSource, tableHeight = 0, tableWidth = 0 } = this.state; const { overscanColumnCount, overscanRowCount, columnWidth, rowHeight } = this.config; let totalHeight = tableHeight; let colWidth = columnWidth; @@ -490,9 +245,6 @@ export default class AirTable extends Component { rowHeight={rowHeight} rowCount={1} height={rowHeight} - checked={checked} - changeChecked={changeChecked} - groupConfig={groupConfig} dataSource={dataSource} cellRenderer={this.renderHeaderCell} /> @@ -520,10 +272,8 @@ export default class AirTable extends Component { rowCount={rowCount} height={totalHeight - scrollbarSize()} scrollTop={scrollTop} - checked={checked} dataSource={dataSource} columns={columns} - hasGroup={hasGroup} />
} @@ -560,7 +310,6 @@ export default class AirTable extends Component { height={rowHeight} scrollLeft={scrollLeft} cellRenderer={this.renderHeaderCell} - checked={checked} /> } @@ -585,12 +334,10 @@ export default class AirTable extends Component { height={totalHeight} onScroll={(...arg: Array) => { onScroll(...arg); - this.onScrollRow(arg[0]); + this.onScroll(arg[0]); }} cellRenderer={this.renderBodyRow.bind(this, data)} columns={columns} - checked={checked} - hasGroup={hasGroup} /> } diff --git a/src/apollo-table/component/base/_utils/setFormatter.tsx b/src/apollo-table/component/base/_utils/setFormatter.tsx index 7ebe4c5..c76f6a6 100644 --- a/src/apollo-table/component/base/_utils/setFormatter.tsx +++ b/src/apollo-table/component/base/_utils/setFormatter.tsx @@ -1,6 +1,7 @@ export const formatStr = 'YYYY-MM-DD HH:mm:ss'; export const emptyModel = [{ text: '', value: '' }]; +const emptyFormat = (value) => {}; export const SetFormatter = { INPUT: (event) => { const node = event.currentTarget || {}; @@ -16,9 +17,11 @@ export const SetFormatter = { })); } }, - TEXTAREA: (event) => { - const node = event.currentTarget || {}; - return [{ value: node.value, text: node.value }]; + TEXTAREA: (value) => { + if (!value) { + return emptyModel; + } + return [{ value: value, text: value }]; }, UPLOAD: (val) => { if (!val) return null; diff --git a/src/apollo-table/component/base/config.tsx b/src/apollo-table/component/base/config.tsx index 5cc5cd1..4802eb0 100644 --- a/src/apollo-table/component/base/config.tsx +++ b/src/apollo-table/component/base/config.tsx @@ -25,7 +25,8 @@ export const config: any = { getFormatter: GetFormatter['TEXTAREA'], setFormatter: SetFormatter['TEXTAREA'], componentAttr: { - autosize: { minRows: 3 }, + autoSize: true, + emitTrigger: 'onBlur', }, detail: require('./detail/textarea').default, }, diff --git a/src/apollo-table/component/base/detail/index.tsx b/src/apollo-table/component/base/detail/index.tsx index 22f9b80..66d6dd9 100644 --- a/src/apollo-table/component/base/detail/index.tsx +++ b/src/apollo-table/component/base/detail/index.tsx @@ -26,14 +26,7 @@ export const getDetail = (type: string) => { const transferColumn = transferAttr(columnType, newProps); return ( -
- -
+ ); } }; diff --git a/src/apollo-table/component/base/edit/editInterface.tsx b/src/apollo-table/component/base/edit/editInterface.tsx index fe63905..ad3b76d 100644 --- a/src/apollo-table/component/base/edit/editInterface.tsx +++ b/src/apollo-table/component/base/edit/editInterface.tsx @@ -1,11 +1,9 @@ import { DatePickerProps } from 'antd/es/date-picker/interface'; +import { TextAreaProps } from 'antd/es/input'; export interface CommonProps { - value: any[] | undefined; - placeholder?: string; - formatter?: Function; - onChange: any; - disabled?: boolean; + onEmitChange?: Function; + emitTrigger?: string; } export interface InputProps extends CommonProps { maxLength: number; @@ -18,9 +16,8 @@ export interface LinkState { prevProps: LinkProps; value: any[]; } -export interface TextAreaProps extends CommonProps { - maxLength: number; - autosize?: boolean | any; +export interface ApolloTextAreaProps extends TextAreaProps, CommonProps { + value: string | undefined; } export interface SearchProps extends CommonProps { type: string; diff --git a/src/apollo-table/component/base/edit/textarea/index.tsx b/src/apollo-table/component/base/edit/textarea/index.tsx index 11577b6..7d8c4c9 100644 --- a/src/apollo-table/component/base/edit/textarea/index.tsx +++ b/src/apollo-table/component/base/edit/textarea/index.tsx @@ -1,17 +1,41 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Input } from 'antd'; import styles from './styles.less'; -import { TextAreaProps } from '../editInterface'; +import { antiAssign } from '@/apollo-table/utils/utils'; +import { ApolloTextAreaProps } from '../editInterface'; -export default function(props: TextAreaProps) { - const { maxLength, value, disabled, placeholder, autosize } = props; - const selfProps = { value, disabled, placeholder, autosize }; +const TextArea = (props: ApolloTextAreaProps) => { + const { maxLength, onChange, emitTrigger, onEmitChange } = props; + const selfProps = antiAssign(props, ['value', 'onChange', 'emitTrigger', 'onEmitChange']); + const [value, setValue] = useState(props.value); + useEffect(() => { + setValue(props.value); + }, [props.value]); + const changeValue = (e) => { + setValue(e.target.value); + if (typeof onChange === 'function') { + onChange(e.target.value); + } + if (!emitTrigger || emitTrigger === 'onChange') { + emitChange(e); + } + }; + + const emitChange = (e) => { + if (typeof onEmitChange === 'function') { + onEmitChange(e.target.value); + } + }; + if (emitTrigger === 'onBlur') { + selfProps.onBlur = emitChange; + } return (
- - {maxLength ? ( - {`已输入${(value || '').length || 0}/${maxLength}`} - ) : null} + + {maxLength && ( + {`已输入${(value || '').length}/${maxLength}`} + )}
); -} +}; +export default TextArea; diff --git a/src/apollo-table/component/base/edit/textarea/styles.less b/src/apollo-table/component/base/edit/textarea/styles.less index bf760a5..1934300 100644 --- a/src/apollo-table/component/base/edit/textarea/styles.less +++ b/src/apollo-table/component/base/edit/textarea/styles.less @@ -1,18 +1,18 @@ -.container{ +.container { position: relative; - top:0; + top: 0; left: 0; - .input{ + .input { padding-bottom: 18px; } - textarea{ + textarea { padding-bottom: 18px; } -.wordNumber{ - position: absolute; - right:12px; - bottom: 2px; - color:#E1E1E1FF; - font-size: 12px; -} + .wordNumber { + position: absolute; + right: 12px; + bottom: 2px; + color: #e1e1e1ff; + font-size: 12px; + } } diff --git a/src/apollo-table/component/base/index.tsx b/src/apollo-table/component/base/index.tsx index f9dabc8..56bafa9 100644 --- a/src/apollo-table/component/base/index.tsx +++ b/src/apollo-table/component/base/index.tsx @@ -17,11 +17,8 @@ export const getComponent = (type: string) => { console.error(type + '组件未定义'); return; } - const setFormat = (columnConfig: any, values: any[]) => { + const setFormat = (columnConfig: any, value: any[]) => { const { columnAttrObj, columnType } = columnConfig || {}; - if (!values || values.length <= 0) return undefined; - const value = values[0] || undefined; - if (!value) return undefined; const transferColumn = transferAttr(columnType, { ...(NodeObj.componentAttr || {}), ...(columnAttrObj || {}), @@ -30,67 +27,59 @@ export const getComponent = (type: string) => { return value; }; - const getFormat = (columnConfig: any, values: any) => { + const getFormat = (columnConfig: any, value: any) => { const { columnAttrObj, columnType } = columnConfig || {}; - if (!values || !Array.isArray(values) || values.length <= 0) return undefined; - const newValue = values.filter((item) => { - return item.value || item.value === 0 || item.label || item.text; - }); + if (!value || !Array.isArray(value) || value.length <= 0) return undefined; const transferColumn = transferAttr(columnType, { ...(NodeObj.componentAttr || {}), ...(columnAttrObj || {}), }); if (NodeObj.getFormatter) { - return NodeObj.getFormatter(newValue, transferColumn); + return NodeObj.getFormatter(value, transferColumn); } - return newValue; + return value; }; const com: any = class Base extends React.Component { static getDerivedStateFromProps(nextProps: BaseComponentProps, prevState: BaseComponentState) { - const { prevProps } = prevState; + const { columnConfig } = prevState; let nextState: BaseComponentState = { ...prevState, - prevProps: nextProps, - columnConfig: nextProps.columnConfig, - value: getFormat(nextProps.columnConfig, nextProps.value), }; - if (JSON.stringify(prevProps.columnConfig) !== JSON.stringify(nextProps.columnConfig)) { + if (JSON.stringify(columnConfig) !== JSON.stringify(nextProps.columnConfig)) { nextState.columnConfig = nextProps.columnConfig; } - if (JSON.stringify(nextProps.value) !== JSON.stringify(nextProps.value)) { - nextState.value = getFormat(nextProps.columnConfig, nextProps.value); - } return nextState; } constructor(props: BaseComponentProps) { super(props); + const { columnConfig } = props; this.state = { - prevProps: props, - value: undefined, - columnConfig: {}, + columnConfig, }; } - onChange = ({ changedValue, isBlur }: any) => { + onChange = (changedValue: any) => { const { columnConfig } = this.state; - const { onChange, changeParams } = this.props; + const { onChange } = this.props; let value = setFormat(columnConfig, changedValue); - // 置空默认逻辑 - if (value && value.length === 0) { - value = emptyModel; - } if (typeof onChange === 'function') { onChange(value); } - if (typeof changeParams === 'function') { - changeParams({ value, changedValue, isBlur }); + }; + // 触发修改回调 + onEmitChange = (changedValue: any) => { + const { columnConfig } = this.state; + const { onEmitChange } = this.props; + let value = setFormat(columnConfig, changedValue); + if (typeof onEmitChange === 'function') { + onEmitChange(value,changedValue); } }; render() { - const { ...others } = this.props; - const { columnConfig, value } = this.state; + const { value, ...others } = this.props; + const { columnConfig } = this.state; const { columnAttrObj, columnType } = columnConfig || {}; let transferColumn = transferAttr(columnType, { ...(NodeObj.componentAttr || {}), @@ -100,7 +89,14 @@ export const getComponent = (type: string) => { ...others, ...(transferColumn || {}), }; - return ; + return ( + + ); } }; com.Detail = getDetail(type); diff --git a/src/apollo-table/component/index.tsx b/src/apollo-table/component/index.tsx index 1dc0bbd..066ccfe 100644 --- a/src/apollo-table/component/index.tsx +++ b/src/apollo-table/component/index.tsx @@ -5,32 +5,26 @@ import Table from './Table'; class AirTable extends React.Component { static getDerivedStateFromProps(nextProps: CommonProps, prevState: CommonState) { - const { prevProps } = prevState; + const { columns, dataSource } = prevState; let nextState: CommonState = { ...prevState, - prevProps: nextProps, }; - if (JSON.stringify(prevProps.dataSource) !== JSON.stringify(nextProps.dataSource)) { + if (JSON.stringify(dataSource) !== JSON.stringify(nextProps.dataSource)) { nextState.dataSource = nextProps.dataSource; } - if (JSON.stringify(prevProps.columns) !== JSON.stringify(nextProps.columns)) { + if (JSON.stringify(columns) !== JSON.stringify(nextProps.columns)) { nextState.columns = nextProps.columns; } - if (prevProps.tableId !== nextProps.tableId) { - nextState.tableId = nextProps.tableId; - } return nextState; } constructor(props: CommonProps) { super(props); - const { columns, dataSource, tableId } = props; + const { columns, dataSource } = props; this.state = { - prevProps: props, columns, dataSource, - tableId, }; } @@ -42,20 +36,20 @@ class AirTable extends React.Component { }; render() { - const { columns, dataSource, tableId } = this.state; - const { rowStyle, rowClassName } = this.props; - const operateConfig = {}; + const { columns, dataSource } = this.state; + const { rowStyle, rowClassName, distanceToLoad, emitChangeCell, bordered } = this.props; return (
diff --git a/src/apollo-table/component/interface.tsx b/src/apollo-table/component/interface.tsx index c212cc1..c137253 100644 --- a/src/apollo-table/component/interface.tsx +++ b/src/apollo-table/component/interface.tsx @@ -1,54 +1,46 @@ import React from 'react'; -export interface CommonProps { +export interface OperateConfigProps { + menusGroup?: Object[]; + buttonsGroup?: Object[]; + moreGroup?: Object[]; +} + +export interface LoadConfigProps { + onScroll?: Function; + distanceToLoad?: number; +} +export interface EmitChangeProps { + emitChangeCell: Function; + emitChangeRow?: Function; + emitChangeColumns?: Function; + emitChangeOperate?: Function; +} +export interface TableProps extends LoadConfigProps { columns: any[]; dataSource: any[]; - tableId: number; - checked?: any[]; - columnWidth?: number; rowClassName?: string | Function; rowStyle?: Object | Function; rowRenderer?: Function; - onScroll?:Function; + bordered?: boolean; + height?: number; + width?: number; + emitChangeCell: Function; } - -export interface CommonState { - prevProps: CommonProps; +export interface TableState { columns: any[]; dataSource: any[]; - tableId: number; - checked?: any[]; - columnWidth?: number; + tableHeight?: number; + tableWidth?: number; } - -export interface TableProps extends CommonProps { - onScroll: Function; - operateConfig: any; - changeChecked?: Function; - hasGroup?: boolean; - updateData?: Function; - showForm?: Function; - showHistory?: Function; - delData?: Function; - getData?: Function; - flush?: Function; - extraMenu?: any; - noAdd?: boolean; - noEdit?: boolean; - noDel?: boolean; - rowClassName?: string | Function; - rowStyle?: Object | Function; - rowRenderer?: Function; - rowBorder?:string; +export interface CommonProps extends TableProps { + operateConfig?: OperateConfigProps; } -export interface TableState extends CommonState { - prevProps: TableProps; - tableHeight: number; - tableWidth: number; -} +export interface CommonState extends TableState {} + export interface RowRendererParams { - rowClassName?: string|Function; + rowClassName?: string | Function; columns: Array; rowIndex: number; isScrolling: boolean; @@ -74,39 +66,24 @@ export interface BaseComponentProps { columnConfig: any; formatter?: Function; onChange?: Function; - changeParams?: Function; + onEmitChange?: Function; } export interface BaseComponentState { - prevProps: BaseComponentProps; - value: any[] | undefined; + // value: any[] | undefined; columnConfig: any; } export interface CellProps { - columns: any[]; + rowIndex: number; + columnIndex: number; columnConfig: any; rowData: any; cellData: any; - tableId: number; - changeChecked?: Function; - hasGroup?: boolean; - updateData?: Function; - showForm?: any; - showHistory?: Function; - delData?: Function; - getData?: Function; - flush?: Function; - extraMenu?: any; - noAdd?: boolean; - noEdit?: boolean; - noDel?: boolean; - overlayClassName?: string; + emitChangeCell: Function; + cellClassName?: string; + cellStyle: object; } export interface CellState { - prevProps: CellProps; - editValue: any[]; - originValue: any[]; - isEditVisible: boolean; - isMenuVisible: boolean; + status: string; } export interface EditCellProps { columns: any[]; diff --git a/src/apollo-table/utils/utils.tsx b/src/apollo-table/utils/utils.tsx index e959fac..74db715 100644 --- a/src/apollo-table/utils/utils.tsx +++ b/src/apollo-table/utils/utils.tsx @@ -27,7 +27,7 @@ export function getStrLeng(str: string) { * @returns {string} 返回随机字符串 */ -export function getRandom(len:number = 6, type?:string, extra?:string) { +export function getRandom(len: number = 6, type?: string, extra?: string) { let num = '0123456789'; let letter = 'abcdefghigklmnopqrstuvwxyz'; let upLetter = letter.toUpperCase(); @@ -54,7 +54,68 @@ export function getRandom(len:number = 6, type?:string, extra?:string) { str += extra; } for (let i = 0; i < len; i++) { - result += str[Math.floor(Math.random() * str.length)] + result += str[Math.floor(Math.random() * str.length)]; } return result; } + +/** + * 提出原对象中指定属性 + * @param src 原对象 + * @param anti 属性名称数组 + */ +export function antiAssign(src: object, anti: string[]) { + const result = _.assign({}, src); + Object.keys(result).map((key) => { + if (anti.includes(key)) { + delete result[key]; + } + }); + return result; +} + +/** + * 合并样式class + * @param className 原样式名 + * @param mergeClassName 合并样式名或方法 + * @param mergeData 合并样式方法的参数 + */ +export function getMergeClassName( + className: string | undefined, + mergeClassName: string | Function | undefined, + mergeData?: any, +): string { + if (!className) { + className = ''; + } + if (!mergeClassName) { + mergeClassName = ''; + } + if (typeof mergeClassName === 'function') { + className = `${className} ${mergeClassName(mergeData)}`; + } else { + className = `${className} ${mergeClassName}`; + } + return className; +} + +/** + * 合并样式style + * @param style 原样式 + * @param mergeStyle 合并样式对象或方法 + * @param mergeData 合并样式方法的参数 + */ +export function getMergeStyle(style: any, mergeStyle: object | Function | undefined, mergeData: any) { + if (!style) { + style = {}; + } + if (!mergeStyle) { + mergeStyle = {}; + } + if (typeof mergeStyle === 'function') { + _.assign(style, mergeStyle(mergeData)); + } else { + _.assign(style, mergeStyle); + } + return style; +} diff --git a/src/test/demo1.less b/src/test/demo1.less new file mode 100644 index 0000000..ec93097 --- /dev/null +++ b/src/test/demo1.less @@ -0,0 +1,3 @@ +.row{ + color: green; +} diff --git a/src/test/demo1.tsx b/src/test/demo1.tsx index ec34f94..26a078a 100644 --- a/src/test/demo1.tsx +++ b/src/test/demo1.tsx @@ -1,12 +1,14 @@ import React, { useState, useEffect, useCallback } from 'react'; import _ from 'lodash'; import ApolloTable from '../apollo-table'; -import { getColumnConfig, getDataSource } from './serives'; +import { getColumnConfig, getDataSource, addOrUpdateDataSource } from './serives'; import { message } from 'antd'; +import s from './demo1.less'; -const rowStyle = ({ index, record }) => { +const rowStyle = ({ rowIndex }) => { const style: any = {}; - if (index % 3 === 0) { + if (rowIndex % 3 === 0) { + style.fontSize = '20px'; style.background = 'red'; } return style; @@ -41,10 +43,11 @@ const Demo1 = (props) => { hasNextPage: false, nextPage: 1, }); + const [flush,setFlush]=useState(false); useEffect(() => { const getDataList = async () => { const data = { - pageNum: pageConfig.nextPage, + pageNum: pageConfig.pageNum, pageSize: pageConfig.pageSize, }; const res = await getDataSource({ tableId, data }); @@ -59,7 +62,7 @@ const Demo1 = (props) => { }, [pageConfig.pageNum]); const fetchData = useCallback(async (isClean: boolean) => { const data = { - pageNum: isClean ? 1 : pageConfig.nextPage, + pageNum: isClean ? 1 : pageConfig.pageNum, pageSize: pageConfig.pageSize, }; const res = await getDataSource({ tableId, data }); @@ -76,13 +79,21 @@ const Demo1 = (props) => { } }; const debounceScroll = _.debounce(onScroll, 400); + const emitChangeCell = async ({ rowId, value }) => { + const res = await addOrUpdateDataSource({ data: { id: rowId, value } }); + if (res && res.success) { + fetchData(true); + } + }; return ( ); }; -- 2.21.0