diff --git a/README.md b/README.md index 9e3545d..d5adeb3 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ $ npm start * addEmptyRowFromTop(): 从顶部添加一个空的新行。 * addRow(rowData): 以指定数据添加一个新行。 * addRowFromTop(rowData): 以指定数据从顶部添加一个新行。 +* addSubRow(rowData, jsxid, cb): 树形表格模式下从底部插入一个子树节点行 +* addSubRomFromTop(rowData, jsxid, cb): 树形表格模式下从顶部插入一个子树节点行 * delRow(rowData): 删除一个新行。 * editRow(rowData): 使指定的行切换到编辑模式。 * editAllRow(): 使所有行切换到编辑模式。 diff --git a/demo/TreeGridDemo.jsx b/demo/TreeGridDemo.jsx index 6104ae1..5a029f8 100644 --- a/demo/TreeGridDemo.jsx +++ b/demo/TreeGridDemo.jsx @@ -8,97 +8,257 @@ import React from 'react'; import Table from '../src'; +import Constants from 'uxcore-const'; + import NattyFetch from 'natty-fetch'; const urlPrefix = window.urlPrefix || 'http://30.9.174.1:3000/'; function loadTreeData(rowData) { - const request = NattyFetch.create({ - method: 'GET', - url: `${urlPrefix}demo/rowData.json`, - data: '', - Promise, - }); - return request(); + // const request = NattyFetch.create({ + // method: 'GET', + // url: `${urlPrefix}demo/rowData.json`, + // data: '', + // Promise, + // }); + // return request(); + return new Promise(resolve => { + setTimeout(() => { + resolve({ + data: [ + { + "id": `${setTimeout(0)}`, + "radio": true, + "grade": "2grade2", + "email": "2email2", + "firstName": "2firstName2", + "lastName": "2lastName2", + "birthDate": "2birthDate2", + "country": "2country2", + "city": "2city2", + "data": [] + }, + { + "id": `${setTimeout(0)}`, + "check": true, + "grade": "2grade3", + "email": "2email3", + "firstName": "2firstName3", + "lastName": "2lastName3", + "birthDate": "2birthDate3", + "country": "2country3", + "city": "2city3" + } + ] + }) + }, 500) + }) } class Demo extends React.Component { constructor(props) { super(props); this.state = { + columns: [ + { + dataKey: 'country', + title: '国家', + width: '200px', + ordered: true, + align: 'left', + type: 'money', + delimiter: ',', + fixed: true, + }, + { + dataKey: 'city', + title: '城市', + width: '150px', + }, + { + dataKey: 'firstName', + title: 'FristName', + }, + { + dataKey: 'lastName', + title: 'LastName', + }, + { + dataKey: 'email', + title: 'Email', + width: '200px', + ordered: true, + }, + { + title: '操作', + type: 'action', + rightFixed: true, + width: '300px', + collapseNum: 5, + actions: [ + { + title: '新增', + callback: (rowData) => { + this.table.addSubRow({ + "id": `${setTimeout(0)}`, + "check": true, + "grade": "2grade3", + "email": "2email", + "firstName": "2firstName3", + "lastName": "2lastName3", + "birthDate": "2birthDate3", + "country": `country_${setTimeout(0)}`, + "city": "2city3" + }, rowData.jsxid, () => { + console.log(this.table.getData()) + }) + } + }, + { + title: '上移', + callback: (rowData) => { + this.table.moveRowUp(rowData); + }, + }, + { + title: '删除', + callback: (rowData) => { + this.table.delRow(rowData); + }, + }, + { + title: '下移', + callback: (rowData) => { this.table.moveRowDown(rowData); }, + }, + ], + }, + ] }; } - render() { - const columns = [ - { - dataKey: 'id', - title: 'ID', - width: '50px', - hidden: true, - }, - { - dataKey: 'country', - title: '国家', - width: '200px', - ordered: true, - align: 'left', - type: 'money', - // fixed: true, - delimiter: ',', - }, - { - dataKey: 'city', - title: '城市', - width: '150px', - }, - { - dataKey: 'firstName', - title: 'FristName', - }, - { - dataKey: 'lastName', - title: 'LastName', - }, - { - dataKey: 'email', - title: 'Email', - width: '200px', - ordered: true, - }, - { - title: '操作', - type: 'action', - width: '200px', - actions: [ - { - title: '上移', - callback: (rowData) => { - this.table.moveRowUp(rowData); - }, - }, - { - title: '删除', - callback: (rowData) => { - this.table.delRow(rowData); - }, - }, - { - title: '下移', - callback: (rowData) => { this.table.moveRowDown(rowData); }, - }, - ], - }, - ]; - const renderProps = { - height: '400px', + height: '600px', width: '1000px', showSearch: true, levels: 0, fetchUrl: `${urlPrefix}demo/data.json`, loadTreeData, - jsxcolumns: columns, + processData(data) { + data.data.map(item => { + item.data = [] + }); + return data + }, + useListActionBar: true, + showColumnPickerCheckAll: true, + showColumnPicker: true, + actionBar: { + columnsOrder: { + iconName: 'huxiangguanzhu', + // keepActiveInCustomView: true, + title: '列排序', + includeActionColumn: false, // 优先级低于fixed和rightFixed + onChange(dragInfo, data) { + console.log(data) + } + }, + buttons: [ + { + title: '新增一行', + callback: () => { + this.table.addRowFromTop( + { + "id": `${setTimeout(0)}`, + "check": true, + "grade": "2grade3", + "email": "2email", + "firstName": "2firstName3", + "lastName": "2lastName3", + "birthDate": "2birthDate3", + "country": `country_${setTimeout(0)}`, + "city": "2city3" + } + ) + } + }, + { + title: '变更columns', + callback: () => { + this.setState({ + columns: [ + { + dataKey: 'id', + title: 'ID', + width: '50px', + hidden: true, + }, + { + dataKey: 'country', + title: '国家', + width: '200px', + ordered: true, + align: 'left', + type: 'money', + delimiter: ',', + }, + { + title: '操作', + type: 'action', + width: '300px', + collapseNum: 5, + actions: [ + { + title: '新增', + callback: (rowData) => { + this.table.addSubRow({ + "id": `${setTimeout(0)}`, + "check": true, + "grade": "2grade3", + "email": "2email", + "firstName": "2firstName3", + "lastName": "2lastName3", + "birthDate": "2birthDate3", + "country": `country_${setTimeout(0)}`, + "city": "2city3" + }, rowData.jsxid, () => { + console.log(this.table.getData()) + }) + } + }, + { + title: '上移', + callback: (rowData) => { + this.table.moveRowUp(rowData); + }, + }, + { + title: '编辑', + callback: (rowData) => { + this.table.editRow(rowData); + }, + mode: Constants.MODE.VIEW, + }, + { + title: '删除', + callback: (rowData) => { + this.table.delRow(rowData); + }, + }, + { + title: '下移', + callback: (rowData) => { this.table.moveRowDown(rowData); }, + }, + ], + }, + ] + }, () => { + this.table.checkRightFixed(true) + }) + } + } + ] + }, + jsxcolumns: this.state.columns, renderModel: 'tree', toggleTreeExpandOnRowClick: true, rowSelection: { @@ -111,7 +271,7 @@ class Demo extends React.Component { this.table = c; }, }; - return (); + return (
{this.table = c}} {...renderProps} className={'kuma-uxtable-border-line'} />); } } diff --git a/demo/index.jsx b/demo/index.jsx index e440879..80f5d2a 100644 --- a/demo/index.jsx +++ b/demo/index.jsx @@ -17,9 +17,9 @@ import Demo5 from './TableGroup'; import Demo6 from './TableRowGroup'; import '../style'; -ReactDOM.render(, document.getElementById('UXCoreDemo')); +// ReactDOM.render(, document.getElementById('UXCoreDemo')); // ReactDOM.render(, document.getElementById('UXCoreDemo2')); -// ReactDOM.render(, document.getElementById('UXCoreDemo3')); +ReactDOM.render(, document.getElementById('UXCoreDemo3')); // ReactDOM.render(, document.getElementById('UXCoreDemo4')); // ReactDOM.render(, document.getElementById('UXCoreDemo5')); // ReactDOM.render(, document.getElementById('UXCoreDemo6')); diff --git a/doc/README_EN.md b/doc/README_EN.md index cfe5fda..83bc2da 100644 --- a/doc/README_EN.md +++ b/doc/README_EN.md @@ -213,6 +213,10 @@ let columns = [ ## API +* addSubRow(rowData, jsxid, cb): add an sub row with specified data in tree table from bottom +* addSubRowFromTop(rowData, jsxid, cb): add an sub row with specified data in tree table from top + + ### Row Editing * getData(): return cellData & do Validation diff --git a/src/ActionBar/ColumnOrder.jsx b/src/ActionBar/ColumnOrder.jsx index ff851fb..ba4c5ea 100644 --- a/src/ActionBar/ColumnOrder.jsx +++ b/src/ActionBar/ColumnOrder.jsx @@ -23,13 +23,13 @@ class ColumnOrder extends React.Component { this.state = { value: props.defaultValue, preColumns: props.columns, - checkAbleColumns: getColumnsInfo(props.columns, props.includeActionColumn), + checkAbleColumns: getColumnsInfo(props.columns, props.includeActionColumn, true), } } static getDerivedStateFromProps = (props, state) => { if (props.columns !== state.preColumns) { return { - checkAbleColumns: getColumnsInfo(props.columns, props.includeActionColumn), + checkAbleColumns: getColumnsInfo(props.columns, props.includeActionColumn, true), preColumns: props.columns }; } @@ -76,7 +76,8 @@ class ColumnOrder extends React.Component { } render() { const p = this.props; - const disabled = !p.keepActiveInCustomView && !p.isTableView + const { checkAbleColumns } = this.state; + const disabled = !p.keepActiveInCustomView && !p.isTableView || !checkAbleColumns.columns.length; return ( } diff --git a/src/ActionBar/ColumnPickerNew.jsx b/src/ActionBar/ColumnPickerNew.jsx index 430710e..dbb7638 100644 --- a/src/ActionBar/ColumnPickerNew.jsx +++ b/src/ActionBar/ColumnPickerNew.jsx @@ -300,7 +300,8 @@ class ColumnPicker extends React.Component { renderListActionBar() { const me = this; const p = me.props; - const disabled = !p.keepActiveInCustomView && !p.isTableView; + const { columnsInfo } = this.state + const disabled = !p.keepActiveInCustomView && !p.isTableView || !columnsInfo.columns.length return ( {me.renderIndent()} diff --git a/src/innerMethods.js b/src/innerMethods.js index ba56de2..02fce36 100644 --- a/src/innerMethods.js +++ b/src/innerMethods.js @@ -58,7 +58,7 @@ function addValuesInData(objAux, operation) { * @param objAux {Array or Object} datum or data need to be inserted */ -function insertRecords(obj, reverse, cb) { +function insertRecords(obj, reverse, cb, targetJsxId) { if (typeof obj !== 'object') return; const me = this; let objAux = deepcopy(obj); @@ -66,11 +66,16 @@ function insertRecords(obj, reverse, cb) { objAux = [objAux]; } objAux = me.addJSXIdsForRecord(objAux); - const content = util.mergeData(me.state.data, objAux, reverse); - updateTreeId(content.data); - me.data = content; + const { data, expandedKey } = util.mergeData(me.state.data, objAux, reverse, targetJsxId); + updateTreeId(data.data); + me.data = data; + let expandedKeys = [...this.state.expandedKeys]; + if (expandedKey >= 0) { + expandedKeys.push(expandedKey) + } me.setState({ - data: content, + data: data, + expandedKeys }, () => { if (cb) { cb(); diff --git a/src/methods.js b/src/methods.js index c47e980..badbe65 100644 --- a/src/methods.js +++ b/src/methods.js @@ -18,6 +18,18 @@ function addRowFromTop(rowData, cb) { this.insertRecords(rowData, true, cb); } +function addSubRow(rowData, jsxid, cb) { + if (jsxid >= 0 && this.props.renderModel === 'tree') { + this.insertRecords(rowData, false, cb, jsxid) + } +} + +function addSubRowFromTop(rowData, jsxid, cb) { + if (jsxid >= 0 && this.props.renderModel === 'tree') { + this.insertRecords(rowData, true, cb, jsxid) + } +} + function resetRow(rowData, cb) { const me = this; let updateData = {}; @@ -321,6 +333,8 @@ export default { addEmptyRowFromTop, addRow, addRowFromTop, + addSubRow, + addSubRowFromTop, resetRow, resetAllRow, delRow, diff --git a/src/style/ActionBar.less b/src/style/ActionBar.less index ce9471f..3c555c2 100644 --- a/src/style/ActionBar.less +++ b/src/style/ActionBar.less @@ -146,6 +146,9 @@ line-height: 32px; margin-right: 20px; position: relative; + &:first-child { + margin-right: 2px; + } } .order-title, .column-order-title, diff --git a/src/style/Main.less b/src/style/Main.less index e74d8bb..7617866 100644 --- a/src/style/Main.less +++ b/src/style/Main.less @@ -182,6 +182,7 @@ display: inline-block; text-align: left; overflow: hidden; + text-overflow: ellipsis; height: 100%; padding: 0 20px 0 20px; vertical-align: top; @@ -189,7 +190,7 @@ word-break: break-all; height: 50px; line-height: 50px; - &>div { + & > div { display: inline-block; overflow: hidden; text-overflow: ellipsis; @@ -557,4 +558,4 @@ .kuma-button-group-separated { line-height: 1.5; } -} \ No newline at end of file +} diff --git a/src/util.js b/src/util.js index 8eea43f..0a4e70d 100644 --- a/src/util.js +++ b/src/util.js @@ -84,8 +84,44 @@ const arrayConcat = (oldArr, newArr, reverse) => { return resArr; }; -const mergeData = (data, obj, reverse) => { - const newData = deepcopy(data); +const getFindRowData = ()=> { + let ret = null + const findRowData = (data, jsxId) => { + if (!data || !data.length || jsxId === undefined) { + return + } + for (let i = 0, len = data.length; i < len; i++) { + const item = data[i] + if (item.jsxid === jsxId) { + ret = item + break + } + if (item.data && item.data.length) { + findRowData(item.data, jsxId) + } + } + return ret + }; + return findRowData +}; + +const mergeData = (data, obj, reverse, targetId) => { + let newData = deepcopy(data); + let expandedKey + if (targetId >= 0) { + const findRowData = getFindRowData() + let ret = findRowData(newData.data, targetId) + if (ret) { + if (ret.data && ret.data.length) { + ret.data = arrayConcat(ret.data, obj, reverse) + } else { + ret.data = obj + } + expandedKey = ret.jsxid + } + return { data: newData, expandedKey } + } + // code compatible if (newData.datas) { newData.datas = arrayConcat(newData.datas, obj, reverse); @@ -93,7 +129,7 @@ const mergeData = (data, obj, reverse) => { newData.data = arrayConcat(newData.data, obj, reverse); } newData.totalCount += 1; - return newData; + return { data: newData, expandedKey }; }; /* eslint-disable no-param-reassign */ @@ -300,7 +336,7 @@ const dropFunc = (obj) => { return obj; }; -const getColumnsInfo = (columns, includeActionColumn) => { +const getColumnsInfo = (columns, includeActionColumn, excludeHiddenColumn) => { const blackList = {'jsxchecked': 1, 'jsxtreeIcon': 1, 'jsxwhite': 1}; let columnsKey = []; let actionColumn = null; @@ -310,7 +346,7 @@ const getColumnsInfo = (columns, includeActionColumn) => { let fixedColumns = []; return { columns: columns.filter((column, index) => { - if (column.dataKey in blackList) { + if (column.dataKey in blackList || excludeHiddenColumn && column.hidden) { otherColumns.push(column) return false } diff --git a/tests/tree.spec.jsx b/tests/tree.spec.jsx index d4882dc..a92ccfd 100644 --- a/tests/tree.spec.jsx +++ b/tests/tree.spec.jsx @@ -237,4 +237,76 @@ describe('Tree', () => { wrapper.find('.kuma-icon.kuma-icon-triangle-right').at(4).simulate('click'); expect(wrapper.find('.kuma-uxtable-row').length).to.be(rowLength + 1); }); + + it('api addSubRow & addSubRowFromTop', (done) => { + wrapper = mount( +
+ ); + const table = wrapper.instance() + expect(table.state.expandedKeys).to.have.length(0) + table.addSubRow({ + id: '9999', + radio: true, + grade: '2grade2', + email: '2email2', + firstName: '2firstName2', + lastName: '2lastName2', + birthDate: '2birthDate2', + country: '2country2', + city: '2city2' + }, 0, () => { + const data = table.getData().data.data; + expect(data[0].data).to.have.length(4); + expect(data[0].data[3].id).to.be('9999'); + expect(data[0].data[3].__treeId__).to.be('0-3'); + expect(table.state.expandedKeys.includes(0)) + done(); + }); + table.addSubRowFromTop({ + id: '0000', + radio: true, + grade: '2grade2', + email: '2email2', + firstName: '2firstName2', + lastName: '2lastName2', + birthDate: '2birthDate2', + country: '2country2', + city: '2city2' + }, 0, () => { + const data = table.getData().data.data; + expect(data[0].data).to.have.length(5); + expect(data[0].data[4].id).to.be('0000'); + expect(data[0].data[4].__treeId__).to.be('0-0'); + done(); + }); + + // not tree table + let wrapper = mount( +
+ ); + wrapper.instance().addSubRow({ + id: '9999', + radio: true, + grade: '2grade2', + email: '2email2', + firstName: '2firstName2', + lastName: '2lastName2', + birthDate: '2birthDate2', + country: '2country2', + city: '2city2' + }, 0, () => { + const data = wrapper.instance().getData().data.data; + expect(data[0].data).to.have.length(3); + done(); + }); + }); });