您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關(guān)React Ant Design實(shí)現(xiàn)樹(shù)形表格的復(fù)雜增刪改,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。
樹(shù)形表格的顯示
在antd中對(duì)于表格的key值有著嚴(yán)格的控制,每一個(gè)row都必須有一個(gè)獨(dú)一無(wú)二的key值,可以是數(shù)字也可以是字符串。這一點(diǎn)和我曾經(jīng)使用過(guò)得iview有著很大的區(qū)別。react使用key來(lái)代表每一行是為了避免重新渲染的問(wèn)題,這個(gè)優(yōu)化也在實(shí)際的開(kāi)發(fā)中帶來(lái)了不少的問(wèn)題。比如新建行時(shí)需要自定義新key。
下面直接上一下代碼及代碼效果,這是一個(gè)三級(jí)的樹(shù)形表格,且其中包含二級(jí)標(biāo)題。
最終效果 ​
colums標(biāo)題: 簡(jiǎn)易版標(biāo)題,隨著功能的增加,我們將增加colums的復(fù)雜度。
let columns = [ { title: '題目', dataIndex: 'text' }, { title: '類(lèi)型', children: [ { title: '一級(jí)', dataIndex: 'text1' }, { title: '二級(jí)', dataIndex: 'text2', }] }, { title: '內(nèi)容', dataIndex: 'content' }, { title: '答案', dataIndex: 'answer', }, { title: '類(lèi)型', dataIndex: 'mark_type', className: 'line' }, { title: '版本', dataIndex: 'version', className: 'line' }, { title: '一級(jí)內(nèi)容點(diǎn)', dataIndex: 'value1', className: 'line' }, { title: '二級(jí)內(nèi)容點(diǎn)', dataIndex: 'value2', className: 'line' }, { title: '操作', key: 'action', width: 205 } ];
data數(shù)據(jù):
let data = [{ "key": 1, "text": "題目一", "children": [{ "key": 11, "text1": "數(shù)學(xué)一", "children":[] }, { "key": 12, "text1": "語(yǔ)文一", "value1": "語(yǔ)文", "children": [{ "key": 121, "value2": "選擇", "text2": "選擇題", "content": "題目?jī)?nèi)容", "answer": "A", "mark_type": "1", "version": "1" },{ "key": 122, "value2":"填空", "text2": "填空題", "content": "題目?jī)?nèi)容", "answer": "梅花", "mark_type": "1", "version": "1" },{ "key": 123, "value2": "閱讀", "text2": "閱讀題", "content": "題目?jī)?nèi)容", "answer": "野蠻生長(zhǎng)", "mark_type": "1", "version": "1" },{ "key": 124, "value2": "文言文", "text2": "文言文", "content": "題目?jī)?nèi)容", "answer": "滕王閣序", "mark_type": "1", "version": "1" }], }], }, { "key": 2, "text": "題目二", "children": [ { "key": 21, "text1": "英語(yǔ)一", "value1": "英語(yǔ)", "children": [{ "key": 211, "value2": "完型", "text2": "完形填空", "content": "題目?jī)?nèi)容", "answer": "ABC", "mark_type": "2", "version": "1" },{ "key": 212, "value2": "一級(jí)代碼", "text2": "選擇", "content": "題目?jī)?nèi)容", "answer": "D", "mark_type": "2", "version": "1" }], }], }];
增加子項(xiàng)數(shù)據(jù)
增加子項(xiàng)數(shù)據(jù)使用操作中的增加按鈕進(jìn)行增加,增加按鈕設(shè)置為圖形,更為形象具體清晰化。
//button樣式使用antd自帶icon樣式 <Button type="primary" shape="circle" icon="plus" size={'small'} onClick={this.handleAdd.bind(this, record)} />
注意事項(xiàng):
1、對(duì)于最子項(xiàng)來(lái)說(shuō)沒(méi)有增加子項(xiàng)選擇,需要對(duì)不同數(shù)據(jù)行進(jìn)行不同處理
2、不同數(shù)據(jù)行新增數(shù)據(jù)的內(nèi)容不同,字段也不同
3、新增之后需要點(diǎn)擊確認(rèn)或者取消
4、加入數(shù)據(jù)點(diǎn)擊確認(rèn)之后需要添加于當(dāng)前key下的children中
本想使用antd自帶的editable屬性,但是這個(gè)屬性不支持二級(jí)標(biāo)題的編輯,所以只有自己寫(xiě)render
這個(gè)問(wèn)題,在7102年就提出來(lái)了,但是已經(jīng)8102年了快9102年都沒(méi)有更新。
新增一級(jí)類(lèi)型題目樣式
新增二級(jí)類(lèi)型題目樣式
這里就部分render代碼:
//分級(jí)標(biāo)題 { title: '類(lèi)型', children: [{ title: '一級(jí)', dataIndex: 'text1', render: (text, record) => { if (this.state.isEditing && this.state.editingOneKey === record.key) return <Input defaultValue={text} onChange={(e) => { this.changeEdit(e, record.key, 'text1') }} /> return text } }, { title: '二級(jí)', dataIndex: 'text2', render: (text, record) => { if (this.state.isEditing && this.state.editingTwoKey === record.key) return <Input defaultValue={text} onChange={(e) => { this.changeEdit(e, record.key, 'text2') }} /> return text } }] } //select選擇 { title: '內(nèi)容', dataIndex:'content', render:(text, record) => { if (text === 0) text = '內(nèi)容一'; if (text === 1) text = '內(nèi)容二'; if (this.state.isEditing && this.state.editingTwoKey === record.key) { return <Select defaultValue={text} onChange={(e) => { this.changeEdit(e, record.key, 'flag') }} getPopupContainer={triggerNode => triggerNode.parentNode}> <Option value='0'>有效</Option> <Option value='1'>無(wú)效</Option> </Select> } return text } }
注意展開(kāi)
如果對(duì)一行未展開(kāi)的行下新增數(shù)據(jù),那么無(wú)法看到打開(kāi)的編輯狀態(tài)是一個(gè)非常糟糕的問(wèn)題。所以我們需要在新增子項(xiàng)的時(shí)候自動(dòng)展開(kāi)父項(xiàng)。
//steate初始化 expandedRows: [] //展開(kāi)行 //render 初始化 <Table bordered expandedRowKeys={this.state.expandedRows} onExpandedRowsChange={this.changeExpandedRows.bind(this)} /> //自動(dòng)展開(kāi)變化,獲取當(dāng)前展開(kāi)行 changeExpandedRows = (expandedRows) => { this.setState({ expandedRows }) }; //增加函數(shù)中 let rows = this.state.expandedRows; rows.push(record.key); this.setState({ expandedRows: rows });
刪除行數(shù)據(jù)
刪除在antd中相較比較簡(jiǎn)單,因?yàn)閍ntd的table每行都有key屬性,key是唯一且必須的屬性,所以只要過(guò)濾掉包含目的key的數(shù)據(jù)即可視為刪除。因?yàn)檫@是樹(shù)形數(shù)據(jù),普通的遍歷無(wú)法進(jìn)行操作,所以使用深度優(yōu)先遍歷來(lái)進(jìn)行數(shù)據(jù)處理,也可以使用廣度優(yōu)先,根據(jù)數(shù)據(jù)來(lái)變更。
handleDelete = (key) => { let data = this.state.data; data = dsFilter(data, key); this.setState({ data }); function dsFilter(dealData, dealKey) { for (let i = 0; i < dealData.length; i++) { if (dealData[i].children && dealData[i].children.length > 0) { dealData[i].children = dsFilter(dealData[i].children, dealKey); } } return dealData.filter(item => item.key !== dealKey); } };
修改某些字段
修改和新增的colums相同,不過(guò)修改需要保存之前的值。需要在input或select中設(shè)置defaultValue={text}來(lái)保證初始值相同
保存
無(wú)論是新增修改還是刪除,都需要進(jìn)行保存。這里使用✓和✗ 來(lái)代表確定和取消。
同時(shí)需要注意,如果在一行的編輯中點(diǎn)擊其他行的操作,需要將之前行的操作視為取消。
<div> <Button shape="circle" icon="check" size={'small'} style={{ backgroundColor: '#65BF34', color: '#FFF', border: 'none' }} onClick={this.saveEdit.bind(this, record.key)} /> <Button shape="circle" icon="close" size={'small'} style={{ backgroundColor: '#FF3333', color: '#FFF', border: 'none' }} onClick={this.cancelEdit.bind(this, record.key)} /> </div>
修改順序
修改順序就是修改數(shù)據(jù)在數(shù)組中的順序,使用簡(jiǎn)單的temp進(jìn)行交換即可。
shiftUp = (key) => { let data = this.state.data; data = dsShift(data, key); this.setState({ data }); function dsShift(dealData, dealKey) { for (let i = 0; i < dealData.length; i++) { if (dealData[i].children && dealData[i].children.length > 0) { dealData[i].children = dsShift(dealData[i].children, dealKey); } if (dealData[i].key === dealKey) { if (i === 0) { message.warning('該行已置頂!'); break; } let temp = dealData[i - 1]; dealData[i - 1] = dealData[i]; dealData[i] = temp; break; } } return dealData; } }; shiftDown = (key) => { let data = this.state.data; data = dsShift(data, key); this.setState({ data }); function dsShift(dealData, dealKey) { for (let i = 0; i < dealData.length; i++) { if (dealData[i].children && dealData[i].children.length > 0) { dealData[i].children = dsShift(dealData[i].children, dealKey); } if (dealData[i].key === dealKey) { if (i === dealData.length - 1) { message.warning('該行已置尾!'); break; } let temp = dealData[i + 1]; dealData[i + 1] = dealData[i]; dealData[i] = temp; break; } } return dealData; } };
總結(jié)
在進(jìn)行表格數(shù)據(jù)操作中,需要進(jìn)行變量的保存,這里就沒(méi)有多寫(xiě)了??偟膩?lái)說(shuō)antd是個(gè)好組件,大部分功能都很齊全,但是很多細(xì)節(jié)還是需要自己進(jìn)行完善。
補(bǔ)充知識(shí):Antd(Ant-design),嵌套子表格(expandedRowRender)的異步獲取數(shù)據(jù)
使用阿里的ant-design開(kāi)源框架,要在表格里面嵌套子表格,需要在用戶點(diǎn)擊父表格的一行數(shù)據(jù)后,獲取該行的key,然后去異步請(qǐng)求后臺(tái)的數(shù)據(jù)用來(lái)填充子表格的內(nèi)容。
如果這樣寫(xiě)(省略無(wú)關(guān)代碼):
expandedRowRender = (record) => { dispatch({ type: 'flow/getPlanList', payload: { contractId: record.contract_id, // 該參數(shù)是從父表格帶過(guò)來(lái)的key }, callback: () => { const { flow: { data }, } = this.props; this.setState({ secData: data.list, }); console.log("返回?cái)?shù)據(jù)(PlanList):" + JSON.stringify(this.state.secData)); } }); return ( <Table columns={secColumns} dataSource={this.state.secData} pagination={false} /> ); }; render() { return( <Card> {this.renderForm()} <div> <Table expandedRowRender={this.expandedRowRender} loading={loading} rowSelection={rowSelection} dataSource={list} columns={columns} pagination={paginationProps} scroll={{ x: 2500}} size = 'middle' expandRowByClick={true} onSelect={this.seFn} /> </div> </Card> ) }
則會(huì)出現(xiàn)不斷的發(fā)起請(qǐng)求的現(xiàn)象:
這是因?yàn)?,expandedRowRender 實(shí)際上是在 Table 組件的 render 方法中調(diào)用的,React render 中用 dispatch 會(huì)造成重復(fù)調(diào)用的問(wèn)題,dispatch -> setState -> render -> dispatch -> setState -> render,循環(huán)往復(fù)。所以應(yīng)該把 dispatch 放在 onExpand 中。
onExpand = (expanded, record) => { const { dispatch } = this.props; dispatch({ type: 'flow/getPlanList', payload: { contractId: record.contract_id, }, callback: () => { const { flow: { data }, } = this.props; this.setState({ secData: data.list , }); console.log("返回?cái)?shù)據(jù)(PlanList):" + JSON.stringify(this.state.secData)); } }); }
但是單純的這樣做,又會(huì)帶來(lái)新的問(wèn)題,就是子表格的所有數(shù)據(jù)都變成了相同的。
本人的解決辦法是使用鍵值對(duì)。
onExpand = (expanded, record) => { const { dispatch } = this.props; if (expanded === false) { // 因?yàn)槿绻粩嗟奶砑渔I值對(duì),會(huì)造成數(shù)據(jù)過(guò)于龐大,浪費(fèi)資源, // 因此在每次合并的時(shí)候講相應(yīng)鍵值下的數(shù)據(jù)清空 console.log("合并!"); this.setState({ subTabData: { ...this.state.subTabData, [record.contract_id]: [] , } }); } else { console.log("展開(kāi)!"); dispatch({ type: 'flow/getPlanList', payload: { contractId: record.contract_id, }, callback: () => { const { flow: { data }, } = this.props; this.setState({ subTabData: { ...this.state.subTabData, [record.contract_id]: data.list , } }); console.log("返回?cái)?shù)據(jù)(PlanList):" + JSON.stringify(this.state.subTabData)); } }); } }
以上就是React Ant Design實(shí)現(xiàn)樹(shù)形表格的復(fù)雜增刪改,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。