Để hiển thị mỗi nhân viên cấp dưới trên một hàng mới khi hàng cha được mở rộng, chúng ta có thể sử dụng hàm expandedRowRender để tạo một bảng con, nơi mỗi nhân viên cấp dưới sẽ được hiển thị trên một dòng riêng. Bằng cách này, mỗi nhân viên sẽ xuất hiện trên một dòng mới trong khu vực mở rộng, với độ thụt lề chính xác dựa trên cấp bậc của chúng trong cấu trúc hiyrarchi.
1. Cấu Trúc Dữ Liệu (mảng employees)
Mảng employees đại diện cho cấu trúc hệ thống, nơi mỗi nhân viên có thể có các cấp dưới. Mỗi đối tượng nhân viên chứa:
- id: Định danh duy nhất cho nhân viên.
- name: Tên nhân viên.
- position: Chức vụ của nhân viên.
- email: Địa chỉ email của nhân viên.
- subordinates: Mảng chứa các cấp dưới (nhân viên con), mỗi cấp dưới cũng theo cùng một cấu trúc.
javascript
const employees = [
{
id: 1,
name: 'Hồ Bích Thảo',
position: 'CEO',
email: 'thaohb@example.com',
subordinates: [
{
id: 2,
name: 'Nguyễn Văn A',
position: 'Manager',
email: 'vana@example.com',
subordinates: [
{
id: 3,
name: 'Trần Văn B',
position: 'Lead',
email: 'vanb@example.com',
subordinates: [
{
id: 4,
name: 'Lê Thị C',
position: 'Developer',
email: 'thic@example.com',
subordinates: [],
},
],
},
],
},
],
},
];
2. Hàm renderSubordinateRows
Hàm này là một hàm đệ quy sẽ làm phẳng cấu trúc hệ thống của các cấp dưới. Nó duyệt qua từng cấp dưới và tạo một mục trong bảng, thêm một cấp độ để thể hiện vị trí của chúng trong hệ thống (độ sâu của chúng). Hàm sử dụng đệ quy để đi sâu vào mảng subordinates của từng cấp dưới, làm phẳng cấu trúc dữ liệu.
Dưới đây là cách hàm renderSubordinateRows hoạt động:
- Nó xử lý cấp dưới hiện tại bằng cách trả về một đối tượng chứa thông tin (tên, chức vụ, email), cùng với cấp độ (để thể hiện độ thụt lề).
- Với mỗi cấp dưới, hàm sẽ gọi chính nó một cách đệ quy để xử lý các cấp dưới sâu hơn.
javascript
const renderSubordinateRows = (subordinates, level = 1) => {
return subordinates.flatMap((subordinate) => [
{
key: subordinate.id, // Khóa duy nhất cho mỗi hàng
name: subordinate.name,
position: subordinate.position,
email: subordinate.email,
level: level, // Cấp độ thụt lề
},
...renderSubordinateRows(subordinate.subordinates, level + 1), // Đệ quy cho các cấp dưới
]);
};
3. Thành Phần Chính EmployeeTable
Thành phần này sẽ render bảng nhân viên. Có hai phần chính:
- Nhân viên cấp cao nhất (mainData): Chỉ có những nhân viên cấp cao nhất được hiển thị trong bảng chính (dataSource={mainData}). Đây là những nhân viên không có cha (tức là chúng là gốc của hệ thống). Thuộc tính level được thiết lập là 0 cho những nhân viên này, vì họ là cấp độ đầu tiên.
- Hàng Có Thể Mở Rộng: Thuộc tính expandable của thành phần Table từ Ant Design được sử dụng để làm cho mỗi hàng có thể mở rộng. Khi hàng (nhân viên cha) được nhấp vào, nó sẽ hiển thị một bảng con với các cấp dưới. Hàm expandedRowRender sẽ render một bảng lồng vào với mỗi cấp dưới là một hàng riêng biệt, và các cấp dưới được thụt lề dựa trên cấp độ trong hệ thống. Dưới đây là phần chính của thành phần xử lý việc render:
javascript
<Table
dataSource={mainData} // Nhân viên cấp cao nhất
columns={columns}
rowKey="id"
pagination={false}
expandable={{
expandedRowRender: (record) => {
const subordinatesData = renderSubordinateRows(record.subordinates); // Xử lý các cấp dưới
return (
<Table
columns={columns}
dataSource={subordinatesData} // Render cấp dưới dưới dạng hàng riêng biệt
pagination={false}
showHeader={false} // Ẩn tiêu đề cho bảng con
rowKey="key" // Khóa duy nhất cho hàng cấp dưới
/>
);
},
rowExpandable: (record) => record.subordinates.length > 0, // Chỉ mở rộng hàng có cấp dưới
}}
/>
4. Cột và Độ Thụt Lề
Mảng cột xác định cách hiển thị mỗi cột của bảng:
- Tên Nhân Viên: Tên nhân viên được render với độ thụt lề dựa trên thuộc tính level. Càng sâu hơn (con của con), độ thụt lề càng lớn.
- Chức Vụ và Email: Các cột này hiển thị thông tin tương ứng của nhân viên. Hàm render bên trong cột Tên Nhân Viên sử dụng thuộc tính level để điều khiển độ thụt lề:
javascript
{
title: 'Tên Nhân Viên',
dataIndex: 'name',
key: 'name',
render: (text, record) => (
<div style={{ paddingLeft: record.level * 20 }}>
{text}
</div>
),
}
Điều này thêm paddingLeft vào tên mỗi nhân viên dựa trên cấp độ trong cấu trúc, tạo ra sự thụt lề trực quan.
5. Các Cấp Dưới Có Thể Mở Rộng
Khi hàng cha được mở rộng, hàm expandedRowRender được gọi, tạo ra một bảng lồng vào. Bảng lồng này chứa các hàng cho mỗi cấp dưới, được thụt lề chính xác và thuộc tính showHeader={false} ẩn tiêu đề của bảng lồng để duy trì vẻ ngoài gọn gàng.
6. Kết Quả
- Nhân Viên Cấp Cao Nhất: Được render trong các hàng bảng chính.
- Cấp Dưới: Khi nhân viên cha được mở rộng, một bảng con hiển thị các cấp dưới dưới dạng hàng riêng biệt. Mỗi cấp dưới được thụt lề dựa trên cấp độ trong cấu trúc.
- Không Có Nhân Viên Trùng Lặp: Mỗi nhân viên chỉ xuất hiện một lần trong bảng—các nhân viên cấp cao nhất được hiển thị trong bảng chính, và các cấp dưới của họ chỉ được hiển thị trong hàng mở rộng.
Kết Luận
Giải pháp này làm phẳng cấu trúc hệ thống thành định dạng bảng, hiển thị mỗi nhân viên và các cấp dưới của họ trong khi vẫn giữ nguyên cấu trúc một cách trực quan với độ thụt lề. Nó cũng sử dụng các hàng có thể mở rộng của Ant Design để chỉ hiển thị các cấp dưới khi cần, giúp cho giao diện người sử dụng thân thiện và hiệu quả trong việc render UI.
source: viblo