Commit 56d3e5bd authored by chenshouchao's avatar chenshouchao

feat: 完成虚拟表格组件

parent cbeee6c2
.VTHeader {
font-size: 12px;
line-height: 20px;
color: rgba(138, 144, 153, 1);
padding: 12px 16px;
white-space: nowrap;
font-weight: 400;
box-sizing: border-box;
display: flex;
align-items: center;
}
.VTHeaderRow {
background-color: rgba(247, 248, 250, 1);
......@@ -18,6 +19,10 @@
background-color: rgba(245, 246, 247, 1);
}
}
.VTActiveRow {
@extend .VTRow;
background-color: rgba(237, 244, 255, 1);
}
.VTRowColumn {
text-align: left;
box-sizing: border-box;
......
import React from "react";
import { useCallback, useEffect, useState, useRef, useMemo } from "react";
import { Column, Table, AutoSizer } from "react-virtualized";
import { useCallback, useEffect, useState, useRef } from "react";
import { Column, Table } from "react-virtualized";
import style from "./index.module.scss";
import "react-virtualized/styles.css"; // only needs to be imported once
import Checkbox from "@mui/material/Checkbox";
import MyCircularProgress from "@/components/mui/MyCircularProgress";
import { createTheme, ThemeProvider } from "@mui/material";
import NoData from "@/components/BusinessComponents/NoData";
import ascIcon from "@/assets/project/ascIcon.svg";
import descIcon from "@/assets/project/descIcon.svg";
import sortIcon from "@/assets/project/sort.svg";
type Order = "ASC" | "DESC"; // 升序为asc,降序为desc。
......@@ -20,24 +26,51 @@ interface IVirtuallyTableProps {
hasCheckbox?: boolean; // 是否有复选框
selectItems?: Array<any>; // 选中的项
setSelectItems?: any; // 设置选中的项
// fixedHead?: boolean; // 是否是固定表头
noDataText?: string; // 无数据提示文案
// hasTableFooter?: boolean; // 是否有分页组件
// page?: number; // 当前页
// pageChange?: any; // 页码改变
// count?: number; // 总页数
// totalElements?: number; // 数据总量 不止是列表渲染的长度
sortState?: sortState; // 排序状态
setSortState?: any; // 设置排序状态
// paginationType?: "simple" | "complex"; // 分页组件的类型 simple简洁式 complex复杂、带每页数量切换、总数等
// rowsPerPage?: number; // 每页多少条数据
// handleChangeRowsPerPage?: any; // 每页多少条数据变化
nodataText?: any; // 无数据文案
handleRow?: any; // 点击一行
activeId?: string; // 选中的一行的id
disableFn?: any; // 禁用时根据disableFn来判断是否禁用
}
const theme = createTheme({
components: {
// 复选框样式
MuiSvgIcon: {
styleOverrides: {
root: {
color: "rgba(209, 214, 222, 1)",
fontSize: "18px",
},
},
},
MuiCheckbox: {
styleOverrides: {
root: {
color: "rgba(209, 214, 222, 1)",
"&.MuiCheckbox-indeterminate .MuiSvgIcon-root": {
color: "rgba(19, 112, 255, 1)",
},
"&.Mui-checked .MuiSvgIcon-root": {
color: "rgba(19, 112, 255, 1)",
},
},
},
},
MuiButtonBase: {
styleOverrides: {
root: {
"&.MuiCheckbox-root": {
padding: 0,
},
},
},
},
},
});
const VirtuallyTable = (props: IVirtuallyTableProps) => {
const {
rows,
......@@ -74,53 +107,218 @@ const VirtuallyTable = (props: IVirtuallyTableProps) => {
getTableWidthHeight();
};
const onSelectAllClick = useCallback(
(e: any) => {
if (e.target.checked) {
setSelectItems && setSelectItems(rows.map((row) => row[tableKey]));
} else {
setSelectItems && setSelectItems([]);
}
},
[setSelectItems, tableKey, rows]
);
const onSelectRowClick = useCallback(
(e: any, itemValue: string) => {
if (e.target.checked) {
setSelectItems && setSelectItems([...selectItems, itemValue]);
} else {
const selectItemIndex = selectItems.indexOf(itemValue);
const newSelectItems = [
...selectItems.slice(0, selectItemIndex),
...selectItems.slice(selectItemIndex + 1, selectItems.length),
];
setSelectItems && setSelectItems(newSelectItems);
}
},
[selectItems, setSelectItems]
);
const handleSort = useCallback(
(field: string) => {
if (sortState?.field === field) {
if (sortState?.order === "ASC") {
setSortState({
field,
order: "DESC",
});
} else if (sortState?.order === "DESC") {
setSortState({
field,
order: "ASC",
});
} else {
setSortState({
field,
order: "DESC",
});
}
} else {
setSortState({
field,
order: "DESC",
});
}
},
[sortState, setSortState]
);
const handleRowFn = useCallback(
(row: any) => {
if (!disableFn) {
handleRow && handleRow(row);
} else {
!disableFn(row) && handleRow && handleRow(row);
}
},
[disableFn, handleRow]
);
return (
<div
ref={virtuallyTableBoxRef}
style={{ width: "100%", height: "100%", position: "relative" }}
>
<MyCircularProgress loading={loading} />
{width && height && (
<Table
ref={virtuallyTableRef}
width={width}
height={height}
headerHeight={59}
rowHeight={54}
rowCount={rows.length}
rowGetter={({ index }: any) => rows[index]}
headerClassName={style.VTHeader}
rowClassName={({ index }: any) => {
if (index < 0) {
return style.VTHeaderRow;
} else {
return style.VTRow;
}
}}
>
{headCells.map((headCell) => {
console.log(headCell.cellRenderer);
return (
<Column
key={headCell.id}
label={headCell.label}
dataKey={headCell.id}
width={headCell.width}
flexGrow={headCell.flexGrow || 0}
cellRenderer={
headCell.cellRenderer
? headCell.cellRenderer
: (data: any) => {
return data.cellData;
}
<ThemeProvider theme={theme}>
<div
ref={virtuallyTableBoxRef}
style={{ width: "100%", height: "100%", position: "relative" }}
>
<MyCircularProgress loading={loading} />
{width && height && (
<Table
ref={virtuallyTableRef}
width={width}
height={height}
headerHeight={59}
rowHeight={54}
rowCount={rows.length}
rowGetter={({ index }: any) => rows[index]}
headerClassName={style.VTHeader}
onRowClick={(data: any) => {
handleRowFn(data.rowData);
}}
rowClassName={({ index }: any) => {
if (index < 0) {
return style.VTHeaderRow;
} else {
if (rows[index][tableKey] === activeId) {
return style.VTActiveRow;
} else {
return style.VTRow;
}
}
}}
rowStyle={({ index }: any) => {
if (index !== -1) {
return {
background:
activeId === rows[index][tableKey]
? "rgba(237, 244, 255, 1)"
: "",
cursor: disableFn && disableFn(rows[index]) ? "no-drop" : "",
opacity: disableFn && disableFn(rows[index]) ? "0.3" : "",
};
}
}}
>
{hasCheckbox && (
<Column
dataKey="checkbox"
headerRenderer={() => {
return (
<Checkbox
indeterminate={
selectItems.length > 0 &&
selectItems.length < rows.length
}
checked={
rows.length > 0 && selectItems.length === rows.length
}
onChange={(e) => onSelectAllClick(e)}
/>
);
}}
headerStyle={{ margin: 0 }}
style={{ margin: 0 }}
width={50}
cellRenderer={(data: any) => {
return (
<Checkbox
checked={
selectItems.filter(
(selectItem) => selectItem === data.rowData[tableKey]
).length > 0
}
onChange={(e) =>
onSelectRowClick(e, data.rowData[tableKey])
}
/>
);
}}
className={style.VTRowColumn}
/>
);
})}
</Table>
)}
</div>
)}
{headCells.map((headCell) => {
return (
<Column
key={headCell.id}
// label={headCell.label}
headerRenderer={(data: any) => {
if (headCell.sort) {
return (
<div>
<span>{headCell.label}</span>
<img
src={
sortState?.field === headCell.id
? sortState?.order === "ASC"
? ascIcon
: sortState?.order === "DESC"
? descIcon
: sortIcon
: sortIcon
}
alt=""
onClick={() => handleSort(headCell.id)}
style={{
marginLeft: "8px",
cursor: "pointer",
position: "relative",
top: "3px",
}}
/>
</div>
);
} else {
return headCell.label;
}
}}
dataKey={headCell.id}
width={headCell.width}
flexGrow={headCell.flexGrow || 0}
cellRenderer={
headCell.cellRenderer
? headCell.cellRenderer
: (data: any) => {
return data.cellData;
}
}
className={style.VTRowColumn}
/>
);
})}
</Table>
)}
{rows.length === 0 && (
<NoData
text={nodataText}
noDataBoxStyle={{
position: "absolute",
bottom: 0,
width: `${width}px`,
height: `${height - 59}px`,
}}
/>
)}
</div>
</ThemeProvider>
);
};
......
import { useCallback, useMemo } from "react";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Paper from "@mui/material/Paper";
import Checkbox from "@mui/material/Checkbox";
import MyPagination from "@/components/mui/MyPagination";
import ascIcon from "@/assets/project/ascIcon.svg";
import descIcon from "@/assets/project/descIcon.svg";
import sortIcon from "@/assets/project/sort.svg";
import CircularProgress from "@mui/material/CircularProgress";
import { createTheme, ThemeProvider } from "@mui/material";
import MyTooltip from "./MyTooltip";
import noFile from "@/assets/project/noFile.svg";
type Order = "ASC" | "DESC"; // 升序为asc,降序为desc。
export type sortState = {
field: string | null | undefined | ""; // 根据哪个属性来排序
order: Order | null | undefined | "";
};
interface IMyTableProps {
rows: Array<any>; // 表格数据
headCells: Array<any>; // 表头配置
tableKey?: string; // 表格数据的key
loading?: boolean; // 是否正在加载数据
hasCheckbox?: boolean; // 是否有复选框
selectItems?: Array<any>; // 选中的项
setSelectItems?: any; // 设置选中的项
fixedHead?: boolean; // 是否是固定表头
noDataText?: string; // 无数据提示文案
hasTableFooter?: boolean; // 是否有分页组件
page?: number; // 当前页
pageChange?: any; // 页码改变
count?: number; // 总页数
totalElements?: number; // 数据总量 不止是列表渲染的长度
sortState?: sortState; // 排序状态
setSortState?: any; // 设置排序状态
paginationType?: "simple" | "complex"; // 分页组件的类型 simple简洁式 complex复杂、带每页数量切换、总数等
rowsPerPage?: number; // 每页多少条数据
handleChangeRowsPerPage?: any; // 每页多少条数据变化
nodataText?: any; // 无数据文案
handleRow?: any; // 点击一行
activeId?: string; // 选中的一行的id
disableFn?: any; // 禁用时根据disableFn来判断是否禁用
}
const MyMuiTable = (props: IMyTableProps) => {
const {
rows,
headCells,
tableKey = "id",
loading = false,
hasCheckbox = false,
selectItems = [],
setSelectItems,
fixedHead,
hasTableFooter = false,
page = 0,
pageChange = () => {},
count = 1,
sortState,
setSortState,
paginationType = "simple",
rowsPerPage = 10,
handleChangeRowsPerPage,
totalElements = 0,
nodataText,
handleRow,
activeId,
disableFn,
} = props;
const theme = useMemo(() => {
return createTheme({
components: {
MuiTable: {
styleOverrides: {
root: {
// minHeight: "100px",
},
},
},
MuiTableHead: {
styleOverrides: {
root: {
background: "rgba(247, 248, 250, 1)",
"& .MuiTableRow-head": {
borderRadius: "4px 4px 0 0",
borderBottom: "1px solid rgba(240, 242, 245, 1)",
},
},
},
},
MuiTableRow: {
styleOverrides: {
root: {
opacity: loading ? 0.5 : 1,
":hover": {
background: "rgba(245, 246, 247, 1)",
},
"& .MuiTableCell-root": {
"&.MuiTableCell-head": {
fontSize: "12px",
lineHeight: "20px",
color: "rgba(138, 144, 153, 1)",
padding: "12px 16px",
whiteSpace: "nowrap",
},
"&.MuiTableCell-head:nth-of-type(1)": {
paddingRight: hasCheckbox ? "8px" : "16px",
},
},
"&.MuiTableRow-footer": {
// 分页的那一行不要hover效果
":hover": {
background: "#fff",
},
},
},
},
},
MuiPaper: {
styleOverrides: {
root: {
// minHeight: "200px",
position: "relative",
"&.MuiTableContainer-root": {
boxShadow: "none",
},
},
},
},
MuiTableCell: {
styleOverrides: {
root: {
boxSizing: "border-box",
fontSize: "14px",
lineHeight: "22px",
color: "rgba(30, 38, 51, 1)",
padding: "16px",
borderBottom: "1px solid rgba(240, 242, 245, 1)",
},
},
},
MuiTableBody: {
styleOverrides: {
root: {
minHeight: "200px",
position: "relative",
"& .MuiTableRow-root:nth-last-of-type(1) .MuiTableCell-root": {
// 有分页的话 表格最后一行就不要下边框
borderBottom: hasTableFooter
? "none"
: "1px solid rgba(240, 242, 245, 1)",
},
},
},
},
// 复选框样式
MuiSvgIcon: {
styleOverrides: {
root: {
color: "rgba(209, 214, 222, 1)",
fontSize: "18px",
},
},
},
MuiCheckbox: {
styleOverrides: {
root: {
color: "rgba(209, 214, 222, 1)",
"&.MuiCheckbox-indeterminate .MuiSvgIcon-root": {
color: "rgba(19, 112, 255, 1)",
},
"&.Mui-checked .MuiSvgIcon-root": {
color: "rgba(19, 112, 255, 1)",
},
},
},
},
MuiButtonBase: {
styleOverrides: {
root: {
"&.MuiCheckbox-root": {
padding: 0,
},
},
},
},
MuiCircularProgress: {
styleOverrides: {
root: {
position: "absolute",
top: 0,
bottom: 0,
left: 0,
right: 0,
margin: "auto",
zIndex: 1,
},
},
},
},
});
}, [hasCheckbox, hasTableFooter, loading]);
const onSelectAllClick = useCallback(
(e: any) => {
if (e.target.checked) {
setSelectItems && setSelectItems(rows.map((row) => row[tableKey]));
} else {
setSelectItems && setSelectItems([]);
}
},
[setSelectItems, tableKey, rows]
);
const onSelectRowClick = useCallback(
(e: any, itemValue: string) => {
if (e.target.checked) {
setSelectItems && setSelectItems([...selectItems, itemValue]);
} else {
const selectItemIndex = selectItems.indexOf(itemValue);
const newSelectItems = [
...selectItems.slice(0, selectItemIndex),
...selectItems.slice(selectItemIndex + 1, selectItems.length),
];
setSelectItems && setSelectItems(newSelectItems);
}
},
[selectItems, setSelectItems]
);
const handleRowFn = useCallback(
(row: any) => {
if (!disableFn) {
handleRow && handleRow(row);
} else {
!disableFn(row) && handleRow && handleRow(row);
}
},
[disableFn, handleRow]
);
const handleSort = useCallback(
(field: string) => {
if (sortState?.field === field) {
if (sortState?.order === "ASC") {
setSortState({
field,
order: "DESC",
});
} else if (sortState?.order === "DESC") {
setSortState({
field,
order: "ASC",
});
} else {
setSortState({
field,
order: "DESC",
});
}
} else {
setSortState({
field,
order: "DESC",
});
}
},
[sortState, setSortState]
);
const randerTableHead = useMemo(() => {
return (
<TableHead>
<TableRow>
{hasCheckbox && (
<TableCell align="left" sx={{ width: "18px", paddingRight: "8px" }}>
<Checkbox
indeterminate={
selectItems.length > 0 && selectItems.length < rows.length
}
checked={rows.length > 0 && selectItems.length === rows.length}
onChange={(e) => onSelectAllClick(e)}
/>
</TableCell>
)}
{headCells.map((headCell, index) => (
<TableCell key={index} align="left" width={headCell.width}>
{headCell.sort && (
<>
<span>{headCell.label}</span>
<img
src={
sortState?.field === headCell.id
? sortState?.order === "ASC"
? ascIcon
: sortState?.order === "DESC"
? descIcon
: sortIcon
: sortIcon
}
alt=""
onClick={() => handleSort(headCell.id)}
style={{
marginLeft: "8px",
cursor: "pointer",
position: "relative",
top: "3px",
}}
/>
</>
)}
{!headCell.sort && headCell.label}
</TableCell>
))}
</TableRow>
</TableHead>
);
}, [
hasCheckbox,
headCells,
selectItems,
onSelectAllClick,
rows.length,
handleSort,
sortState,
]);
const randerTableBody = useMemo(() => {
if (rows.length === 0) {
if (loading) {
return (
<tbody>
<tr>
<td></td>
</tr>
</tbody>
);
}
return (
<tbody>
<tr>
<td>
<img
src={noFile}
alt=""
style={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -100%)",
}}
/>
<span
style={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, 8px)",
fontSize: "14px",
lineHeight: "22px",
color: "#8a9099",
}}
>
{nodataText || "暂无数据"}
</span>
</td>
</tr>
</tbody>
);
}
return (
<TableBody>
{rows.map((row, rowIndex) => (
<TableRow
key={row[tableKey] || rowIndex}
onClick={() => handleRowFn(row)}
sx={{
background:
activeId === row[tableKey] ? "rgba(237, 244, 255, 1)" : "",
cursor: disableFn && disableFn(row) ? "no-drop" : "",
opacity: disableFn && disableFn(row) ? "0.3" : "",
}}
>
{hasCheckbox && (
<TableCell
align="left"
sx={{ width: "18px", paddingRight: "8px" }}
>
<Checkbox
checked={
selectItems.filter(
(selectItem) => selectItem === row[tableKey]
).length > 0
}
onChange={(e) => onSelectRowClick(e, row[tableKey])}
/>
</TableCell>
)}
{headCells.map((headCell, index) => (
<TableCell key={index} align="left" width={headCell.width}>
{headCell.showOverflowTooltip && (
<MyTooltip title={row[headCell.id]}>
<div
style={{
width: Number(headCell.width) - 32 || "",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}
>
{row[headCell.id]}
</div>
</MyTooltip>
)}
{!headCell.showOverflowTooltip && (
<div
style={{
width: headCell.width ? Number(headCell.width) - 32 : "",
}}
>
{row[headCell.id]}
</div>
)}
</TableCell>
))}
</TableRow>
))}
</TableBody>
);
}, [
rows,
tableKey,
hasCheckbox,
selectItems,
onSelectRowClick,
headCells,
nodataText,
loading,
handleRowFn,
activeId,
disableFn,
]);
const randerTableFooter = useMemo(() => {
if (hasTableFooter) {
return (
<div
style={{
padding: "20px 0",
display: "flex",
justifyContent: "flex-end",
alignItems: "center",
borderTop: "1px solid rgba(240, 242, 245, 1)",
}}
>
<MyPagination
page={page}
pageChange={pageChange}
count={count}
totalElements={totalElements}
type={paginationType}
rowsPerPage={rowsPerPage}
handleChangeRowsPerPage={handleChangeRowsPerPage}
/>
</div>
);
} else {
return null;
}
}, [
hasTableFooter,
page,
count,
pageChange,
paginationType,
rowsPerPage,
handleChangeRowsPerPage,
totalElements,
]);
const randerCircularProgress = useMemo(() => {
if (loading) {
return <CircularProgress></CircularProgress>;
} else {
return null;
}
}, [loading]);
return (
<ThemeProvider theme={theme}>
{!fixedHead && (
<>
<TableContainer component={Paper} sx={{ minHeight: "200px" }}>
{randerCircularProgress}
<Table>
{randerTableHead}
{randerTableBody}
</Table>
</TableContainer>
{randerTableFooter}
</>
)}
{fixedHead && (
<div
style={{
height: "100%",
display: "flex",
flexDirection: "column",
position: "relative",
}}
>
<TableContainer
component={Paper}
sx={{
minHeight: "45px",
overflowY: "scroll",
background: "rgba(247, 248, 250, 1)",
}}
>
<Table sx={{}}>{randerTableHead}</Table>
</TableContainer>
<TableContainer
component={Paper}
sx={{ flex: 1, minHeight: "200px", overflowY: "scroll" }}
>
<Table>{randerTableBody}</Table>
</TableContainer>
{randerTableFooter}
{randerCircularProgress}
</div>
)}
</ThemeProvider>
);
};
export default MyMuiTable;
......@@ -14,6 +14,8 @@ import sortIcon from "@/assets/project/sort.svg";
import CircularProgress from "@mui/material/CircularProgress";
import { createTheme, ThemeProvider } from "@mui/material";
import MyTooltip from "./MyTooltip";
import MyMuiTable from "./MyMuiTable";
import VirtuallyTable from "../CommonComponents/VirtuallyTable";
import noFile from "@/assets/project/noFile.svg";
type Order = "ASC" | "DESC"; // 升序为asc,降序为desc。
......@@ -24,6 +26,7 @@ export type sortState = {
};
interface IMyTableProps {
isVirtuallyTable?: boolean; // 是不是虚拟表格
rows: Array<any>; // 表格数据
headCells: Array<any>; // 表头配置
tableKey?: string; // 表格数据的key
......@@ -51,6 +54,7 @@ interface IMyTableProps {
const MyTable = (props: IMyTableProps) => {
const {
isVirtuallyTable = false,
rows,
headCells,
tableKey = "id",
......@@ -75,473 +79,52 @@ const MyTable = (props: IMyTableProps) => {
disableFn,
} = props;
const theme = useMemo(() => {
return createTheme({
components: {
MuiTable: {
styleOverrides: {
root: {
// minHeight: "100px",
},
},
},
MuiTableHead: {
styleOverrides: {
root: {
background: "rgba(247, 248, 250, 1)",
"& .MuiTableRow-head": {
borderRadius: "4px 4px 0 0",
borderBottom: "1px solid rgba(240, 242, 245, 1)",
},
},
},
},
MuiTableRow: {
styleOverrides: {
root: {
opacity: loading ? 0.5 : 1,
":hover": {
background: "rgba(245, 246, 247, 1)",
},
"& .MuiTableCell-root": {
"&.MuiTableCell-head": {
fontSize: "12px",
lineHeight: "20px",
color: "rgba(138, 144, 153, 1)",
padding: "12px 16px",
whiteSpace: "nowrap",
},
"&.MuiTableCell-head:nth-of-type(1)": {
paddingRight: hasCheckbox ? "8px" : "16px",
},
},
"&.MuiTableRow-footer": {
// 分页的那一行不要hover效果
":hover": {
background: "#fff",
},
},
},
},
},
MuiPaper: {
styleOverrides: {
root: {
// minHeight: "200px",
position: "relative",
"&.MuiTableContainer-root": {
boxShadow: "none",
},
},
},
},
MuiTableCell: {
styleOverrides: {
root: {
boxSizing: "border-box",
fontSize: "14px",
lineHeight: "22px",
color: "rgba(30, 38, 51, 1)",
padding: "16px",
borderBottom: "1px solid rgba(240, 242, 245, 1)",
},
},
},
MuiTableBody: {
styleOverrides: {
root: {
minHeight: "200px",
position: "relative",
"& .MuiTableRow-root:nth-last-of-type(1) .MuiTableCell-root": {
// 有分页的话 表格最后一行就不要下边框
borderBottom: hasTableFooter
? "none"
: "1px solid rgba(240, 242, 245, 1)",
},
},
},
},
// 复选框样式
MuiSvgIcon: {
styleOverrides: {
root: {
color: "rgba(209, 214, 222, 1)",
fontSize: "18px",
},
},
},
MuiCheckbox: {
styleOverrides: {
root: {
color: "rgba(209, 214, 222, 1)",
"&.MuiCheckbox-indeterminate .MuiSvgIcon-root": {
color: "rgba(19, 112, 255, 1)",
},
"&.Mui-checked .MuiSvgIcon-root": {
color: "rgba(19, 112, 255, 1)",
},
},
},
},
MuiButtonBase: {
styleOverrides: {
root: {
"&.MuiCheckbox-root": {
padding: 0,
},
},
},
},
MuiCircularProgress: {
styleOverrides: {
root: {
position: "absolute",
top: 0,
bottom: 0,
left: 0,
right: 0,
margin: "auto",
zIndex: 1,
},
},
},
},
});
}, [hasCheckbox, hasTableFooter, loading]);
const onSelectAllClick = useCallback(
(e: any) => {
if (e.target.checked) {
setSelectItems && setSelectItems(rows.map((row) => row[tableKey]));
} else {
setSelectItems && setSelectItems([]);
}
},
[setSelectItems, tableKey, rows]
);
const onSelectRowClick = useCallback(
(e: any, itemValue: string) => {
if (e.target.checked) {
setSelectItems && setSelectItems([...selectItems, itemValue]);
} else {
const selectItemIndex = selectItems.indexOf(itemValue);
const newSelectItems = [
...selectItems.slice(0, selectItemIndex),
...selectItems.slice(selectItemIndex + 1, selectItems.length),
];
setSelectItems && setSelectItems(newSelectItems);
}
},
[selectItems, setSelectItems]
);
const handleRowFn = useCallback(
(row: any) => {
if (!disableFn) {
handleRow && handleRow(row);
} else {
!disableFn(row) && handleRow && handleRow(row);
}
},
[disableFn, handleRow]
);
const handleSort = useCallback(
(field: string) => {
if (sortState?.field === field) {
if (sortState?.order === "ASC") {
setSortState({
field,
order: "DESC",
});
} else if (sortState?.order === "DESC") {
setSortState({
field,
order: "ASC",
});
} else {
setSortState({
field,
order: "DESC",
});
}
} else {
setSortState({
field,
order: "DESC",
});
}
},
[sortState, setSortState]
);
const randerTableHead = useMemo(() => {
if (!isVirtuallyTable) {
return (
<TableHead>
<TableRow>
{hasCheckbox && (
<TableCell align="left" sx={{ width: "18px", paddingRight: "8px" }}>
<Checkbox
indeterminate={
selectItems.length > 0 && selectItems.length < rows.length
}
checked={rows.length > 0 && selectItems.length === rows.length}
onChange={(e) => onSelectAllClick(e)}
/>
</TableCell>
)}
{headCells.map((headCell, index) => (
<TableCell key={index} align="left" width={headCell.width}>
{headCell.sort && (
<>
<span>{headCell.label}</span>
<img
src={
sortState?.field === headCell.id
? sortState?.order === "ASC"
? ascIcon
: sortState?.order === "DESC"
? descIcon
: sortIcon
: sortIcon
}
alt=""
onClick={() => handleSort(headCell.id)}
style={{
marginLeft: "8px",
cursor: "pointer",
position: "relative",
top: "3px",
}}
/>
</>
)}
{!headCell.sort && headCell.label}
</TableCell>
))}
</TableRow>
</TableHead>
<MyMuiTable
rows={rows}
headCells={headCells}
tableKey={tableKey}
loading={loading}
hasCheckbox={hasCheckbox}
selectItems={selectItems}
setSelectItems={setSelectItems}
fixedHead={fixedHead}
hasTableFooter={hasTableFooter}
page={page}
pageChange={pageChange}
count={count}
sortState={sortState}
setSortState={setSortState}
paginationType={paginationType}
rowsPerPage={rowsPerPage}
handleChangeRowsPerPage={handleChangeRowsPerPage}
totalElements={totalElements}
nodataText={nodataText}
handleRow={handleRow}
activeId={activeId}
disableFn={disableFn}
></MyMuiTable>
);
}, [
hasCheckbox,
headCells,
selectItems,
onSelectAllClick,
rows.length,
handleSort,
sortState,
]);
const randerTableBody = useMemo(() => {
if (rows.length === 0) {
if (loading) {
return (
<tbody>
<tr>
<td></td>
</tr>
</tbody>
);
}
return (
<tbody>
<tr>
<td>
<img
src={noFile}
alt=""
style={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -100%)",
}}
/>
<span
style={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, 8px)",
fontSize: "14px",
lineHeight: "22px",
color: "#8a9099",
}}
>
{nodataText || "暂无数据"}
</span>
</td>
</tr>
</tbody>
);
}
} else {
return (
<TableBody>
{rows.map((row, rowIndex) => (
<TableRow
key={row[tableKey] || rowIndex}
// onClick={() => handleRow && handleRow(row)}
onClick={() => handleRowFn(row)}
sx={{
background:
activeId === row[tableKey] ? "rgba(237, 244, 255, 1)" : "",
cursor: disableFn && disableFn(row) ? "no-drop" : "",
opacity: disableFn && disableFn(row) ? "0.3" : "",
}}
>
{hasCheckbox && (
<TableCell
align="left"
sx={{ width: "18px", paddingRight: "8px" }}
>
<Checkbox
checked={
selectItems.filter(
(selectItem) => selectItem === row[tableKey]
).length > 0
}
onChange={(e) => onSelectRowClick(e, row[tableKey])}
/>
</TableCell>
)}
{headCells.map((headCell, index) => (
<TableCell key={index} align="left" width={headCell.width}>
{headCell.showOverflowTooltip && (
<MyTooltip title={row[headCell.id]}>
<div
style={{
width: Number(headCell.width) - 32 || "",
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}
>
{row[headCell.id]}
</div>
</MyTooltip>
)}
{!headCell.showOverflowTooltip && (
<div
style={{
width: headCell.width ? Number(headCell.width) - 32 : "",
}}
>
{row[headCell.id]}
</div>
)}
</TableCell>
))}
</TableRow>
))}
</TableBody>
<VirtuallyTable
rows={rows}
headCells={headCells}
tableKey={tableKey}
loading={loading}
hasCheckbox={hasCheckbox}
selectItems={selectItems}
setSelectItems={setSelectItems}
sortState={sortState}
setSortState={setSortState}
nodataText={nodataText}
handleRow={handleRow}
activeId={activeId}
disableFn={disableFn}
></VirtuallyTable>
);
}, [
rows,
tableKey,
hasCheckbox,
selectItems,
onSelectRowClick,
headCells,
nodataText,
loading,
handleRowFn,
activeId,
disableFn,
]);
const randerTableFooter = useMemo(() => {
if (hasTableFooter) {
return (
<div
style={{
padding: "20px 0",
display: "flex",
justifyContent: "flex-end",
alignItems: "center",
borderTop: "1px solid rgba(240, 242, 245, 1)",
}}
>
<MyPagination
page={page}
pageChange={pageChange}
count={count}
totalElements={totalElements}
type={paginationType}
rowsPerPage={rowsPerPage}
handleChangeRowsPerPage={handleChangeRowsPerPage}
/>
</div>
);
} else {
return null;
}
}, [
hasTableFooter,
page,
count,
pageChange,
paginationType,
rowsPerPage,
handleChangeRowsPerPage,
totalElements,
]);
const randerCircularProgress = useMemo(() => {
if (loading) {
return <CircularProgress></CircularProgress>;
} else {
return null;
}
}, [loading]);
return (
<ThemeProvider theme={theme}>
{!fixedHead && (
<>
<TableContainer component={Paper} sx={{ minHeight: "200px" }}>
{randerCircularProgress}
<Table>
{randerTableHead}
{randerTableBody}
</Table>
</TableContainer>
{randerTableFooter}
</>
)}
{fixedHead && (
<div
style={{
height: "100%",
display: "flex",
flexDirection: "column",
position: "relative",
}}
>
<TableContainer
component={Paper}
sx={{
minHeight: "45px",
overflowY: "scroll",
background: "rgba(247, 248, 250, 1)",
}}
>
<Table sx={{}}>{randerTableHead}</Table>
</TableContainer>
<TableContainer
component={Paper}
sx={{ flex: 1, minHeight: "200px", overflowY: "scroll" }}
>
<Table>{randerTableBody}</Table>
</TableContainer>
{randerTableFooter}
{randerCircularProgress}
</div>
)}
</ThemeProvider>
);
}
};
export default MyTable;
......@@ -174,7 +174,7 @@ const AddMember = observer((props: IProps) => {
setProjectMember(e.target.value);
}
}}
placeholder="搜索项目成员"
placeholder="按回车搜索项目成员"
sx={{ mb: 2 }}
/>
<div style={{ height: "320px" }}>
......
......@@ -203,7 +203,7 @@ const OperatorList = observer((props: IOperatorListProps) => {
setKeyword(e.target.value);
}}
value={keyword}
placeholder="输入关键词搜索"
placeholder="输入关键词按回车搜索"
onKeyUp={handleEnterCode}
size="medium"
sx={{ height: 32, width: "100%" }}
......
import { useCallback, useEffect, useState, useRef, useMemo } from "react";
import VirtuallyList from "@/components/CommonComponents/VirtuallyList";
import VirtuallyTable from "@/components/CommonComponents/VirtuallyTable";
import MyButton from "@/components/mui/MyButton";
......@@ -46,7 +47,7 @@ const VirtuallyListDemo = () => {
c: "asdfasf",
d: "asdfasdf",
e: "asd4534",
id: "1",
id: "a1",
},
];
for (let i = 0; i < 10000; i++) {
......@@ -56,7 +57,7 @@ const VirtuallyListDemo = () => {
c: "sdfg",
d: "sdfg",
e: "wertwe",
id: "12" + i,
id: String(i),
});
}
const buttonHeadCells = [
......@@ -99,6 +100,8 @@ const VirtuallyListDemo = () => {
},
];
const [selectItems, setSelectItems] = useState([]);
return (
<div>
<div>
......@@ -106,21 +109,12 @@ const VirtuallyListDemo = () => {
</div>
<div style={{ height: "600px" }}>
<VirtuallyTable
// rows={rows}
rows={rows.map((row) => {
return {
...row,
// caozuo: (
// <MyButton
// text="删除"
// onClick={() => {
// // handleDelete(row.id);
// }}
// ></MyButton>
// ),
};
})}
selectItems={selectItems}
setSelectItems={setSelectItems}
rows={rows}
// rows={[]}
headCells={buttonHeadCells}
activeId="8"
/>
</div>
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment