Commit 2e620e26 authored by chenshouchao's avatar chenshouchao

feat: 表格组件重构

parent a4532ce1
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 64</title>
<g id="上线UI" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组-64">
<rect id="底框备份" x="0" y="0" width="16" height="16"></rect>
<polygon id="路径-15备份-5" fill="#8A9099" fill-rule="nonzero" points="7 3 7 13 3 13 3 11.2857143 5.39968807 11.2855021 5.4 3"></polygon>
<polygon id="路径-15备份-6" fill="#1370FF" fill-rule="nonzero" transform="translate(11.000000, 8.000000) scale(-1, -1) translate(-11.000000, -8.000000) " points="13 3 13 13 9 13 9 11.2857143 11.3996881 11.2855021 11.4 3"></polygon>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 65</title>
<g id="上线UI" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组-65">
<rect id="底框" x="0" y="0" width="16" height="16"></rect>
<polygon id="路径-15" fill="#1370FF" fill-rule="nonzero" points="7 3 7 13 3 13 3 11.2857143 5.39968807 11.2855021 5.4 3"></polygon>
<polygon id="路径-15备份" fill="#8A9099" fill-rule="nonzero" transform="translate(11.000000, 8.000000) scale(-1, -1) translate(-11.000000, -8.000000) " points="13 3 13 13 9 13 9 11.2857143 11.3996881 11.2855021 11.4 3"></polygon>
</g>
</g>
</svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>1.Base基础/Icon图标/排序备份 5</title>
<g id="上线UI" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="1.Base基础/Icon图标/排序备份-5">
<rect id="底框" x="0" y="0" width="16" height="16"></rect>
<g id="排序" transform="translate(3.000000, 3.000000)" fill="#8A9099" fill-rule="nonzero">
<polygon id="路径-15" points="4 0 4 10 0 10 0 8.28571429 2.39968807 8.28550207 2.4 0"></polygon>
<polygon id="路径-15备份" transform="translate(8.000000, 5.000000) scale(-1, -1) translate(-8.000000, -5.000000) " points="10 0 10 10 6 10 6 8.28571429 8.39968807 8.28550207 8.4 0"></polygon>
</g>
</g>
</g>
</svg>
\ No newline at end of file
......@@ -11,21 +11,28 @@
box-sizing: border-box;
padding: 2px;
}
.radio {
.radioBox {
position: relative;
min-width: 63px;
height: 28px;
box-sizing: border-box;
font-size: 14px;
color: #565c66;
border-radius: 2px;
line-height: 20px;
padding: 6px 16px;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.radio {
position: relative;
box-sizing: border-box;
font-size: 14px;
color: #565c66;
padding: 6px 16px;
white-space: nowrap;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.radioActive {
......@@ -33,6 +40,8 @@
position: relative;
}
.border {
position: absolute;
right: 0px;
width: 1px;
height: 16px;
background-color: rgba(209, 214, 222, 1);
......
......@@ -67,14 +67,16 @@ const RadioGroupOfButtonStyle = (props: IRadioGroupOfButtonStyleProps) => {
{radioOptions.map((options, index) => {
return (
<>
<div
key={options.value}
className={style.radioBox}
onClick={() => handleRadio(options.value)}
>
<div
className={classnames({
[style.radio]: true,
[style.radioActive]: value === options.value,
})}
onClick={() => handleRadio(options.value)}
style={radioStyle}
>
{options.label}
......@@ -89,7 +91,7 @@ const RadioGroupOfButtonStyle = (props: IRadioGroupOfButtonStyleProps) => {
),
})}
></div>
</>
</div>
);
})}
</div>
......
......@@ -27,6 +27,22 @@ const useMyRouter = () => {
);
permissionStore.restAddRoutes();
if (window.location.host === "localhost:8088") {
menuInfo.res.data.forEach((item: any) => {
if (item.id === 'cadd') {
item.routes.push({
element: 'Demo',
id: "demo",
name: "demo",
order: 1000,
path: "/demo",
show: true,
type: "page",
})
}
});
}
for (let item of menuInfo.res.data) {
for (let route of item.routes) {
route.element = elements[route.element] || NotFound;
......
......@@ -57,6 +57,7 @@ const theme = createTheme({
MuiSvgIcon: {
styleOverrides: {
root: {
color: "rgba(209, 214, 222, 1)",
fontSize: "19px",
},
},
......
......@@ -3,6 +3,13 @@ import Pagination from "@mui/material/Pagination";
import { ThemeProvider, createTheme } from "@mui/material/styles";
const theme = createTheme({
components: {
MuiPagination: {
styleOverrides: {
ul: {
flexWrap: "nowrap",
},
},
},
MuiPaginationItem: {
styleOverrides: {
root: {
......@@ -39,6 +46,11 @@ const MyPagination = (props: IMyPaginationProps) => {
count={count + 1}
shape="rounded"
onChange={handlePageChange}
// sx={{
// padding: "20px 0",
// display: "flex",
// justifyContent: "flex-end",
// }}
/>
</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 { createTheme, ThemeProvider } from "@mui/material";
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>; // 表头配置
key?: string; // 表格数据的key
hasCheckbox?: boolean; // 是否有复选框
selectItems?: Array<any>; // 选中的项
setSelectItems?: any; // 设置选中的项
fixedHead?: boolean; // 是否是固定表头
noDataText?: string; // 无数据提示文案
hasTableFooter?: boolean; // 是否有分页组件
page?: number; // 当前页
pageChange?: any; // 页码改变
count?: number; // 总页数
randerTooterContent?: any; // 分页左侧内容渲染
sortState?: sortState; // 排序状态
setSortState?: any; // 设置排序状态
}
const MyTable = (props: IMyTableProps) => {
// tode loading nodata
const {
rows,
headCells,
key = "id",
hasCheckbox = false,
selectItems = [],
setSelectItems,
fixedHead,
hasTableFooter = false,
page = 0,
pageChange = () => {},
count = 1,
randerTooterContent = null,
sortState,
setSortState,
} = props;
const theme = useMemo(() => {
return createTheme({
components: {
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: {
":hover": {
background: "rgba(245, 246, 247, 1)",
},
"& .MuiTableCell-root": {
"&.MuiTableCell-head": {
fontSize: "12px",
lineHeight: "20px",
color: "rgba(138, 144, 153, 1)",
padding: "12px 16px",
},
"&.MuiTableCell-head:nth-of-type(1)": {
paddingRight: hasCheckbox ? "8px" : "16px",
},
},
"&.MuiTableRow-footer": {
// 分页的那一行不要hover效果
":hover": {
background: "#fff",
},
},
},
},
},
MuiPaper: {
styleOverrides: {
root: {
"&.MuiTableContainer-root": {
boxShadow: "none",
},
},
},
},
MuiTableCell: {
styleOverrides: {
root: {
fontSize: "14px",
lineHeight: "22px",
color: "rgba(86, 92, 102, 1)",
padding: "16px",
borderBottom: "1px solid rgba(240, 242, 245, 1)",
},
},
},
MuiTableBody: {
styleOverrides: {
root: {
"& .MuiTableRow-root:nth-last-of-type(1) .MuiTableCell-root": {
borderBottom: "none",
},
},
},
},
// 复选框样式
MuiSvgIcon: {
styleOverrides: {
root: {
color: "rgba(209, 214, 222, 1)",
fontSize: "18px",
},
},
},
MuiCheckbox: {
styleOverrides: {
root: {
"&.Mui-checked .MuiSvgIcon-root": {
color: "rgba(19, 110, 250, 1)",
},
},
},
},
MuiButtonBase: {
styleOverrides: {
root: {
"&.MuiCheckbox-root": {
padding: 0,
},
},
},
},
},
});
}, [hasCheckbox]);
const onSelectAllClick = useCallback(() => {
return (e: any) => {
if (e.target.checked) {
setSelectItems && setSelectItems(rows.map((row) => row[key]));
} else {
setSelectItems && setSelectItems([]);
}
};
}, [setSelectItems, key, 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 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={onSelectAllClick}
/>
</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(() => {
return (
<TableBody>
{rows.map((row) => (
<TableRow key={row[key]}>
{hasCheckbox && (
<TableCell
align="left"
sx={{ width: "18px", paddingRight: "8px" }}
>
<Checkbox
checked={
selectItems.filter((selectItem) => selectItem === row[key])
.length > 0
}
onChange={(e) => onSelectRowClick(e, row[key])}
/>
</TableCell>
)}
{headCells.map((headCell, index) => (
<TableCell key={index} align="left" width={headCell.width}>
{row[headCell.id]}
</TableCell>
))}
</TableRow>
))}
</TableBody>
);
}, [rows, key, hasCheckbox, selectItems, onSelectRowClick, headCells]);
const randerTableFooter = useMemo(() => {
if (hasTableFooter) {
return (
<div
style={{
padding: "20px 0",
display: "flex",
justifyContent: "space-between",
alignItems: "center",
borderTop: "1px solid rgba(240, 242, 245, 1)",
}}
>
<div>{randerTooterContent}</div>
<MyPagination page={page} pageChange={pageChange} count={count} />
</div>
);
} else {
return null;
}
}, [hasTableFooter, page, count, pageChange, randerTooterContent]);
return (
<ThemeProvider theme={theme}>
{!fixedHead && (
<>
<TableContainer component={Paper}>
<Table>
{randerTableHead}
{randerTableBody}
</Table>
</TableContainer>
{randerTableFooter}
</>
)}
{fixedHead && (
<div
style={{ height: "100%", display: "flex", flexDirection: "column" }}
>
<TableContainer component={Paper}>
<Table>{randerTableHead}</Table>
</TableContainer>
<TableContainer component={Paper} sx={{ flex: 1 }}>
<Table>{randerTableBody}</Table>
</TableContainer>
{randerTableFooter}
</div>
)}
</ThemeProvider>
);
};
export default MyTable;
......@@ -32,6 +32,9 @@ const MenuLayout = observer(() => {
const routerIcon = (id: string, isSelect: boolean) => {
try {
if (id === "demo") {
return require("../../assets/project/PROJECT_SETTING.svg");
}
const result = require(`../../assets/project/${id}${
isSelect ? "_BLUE" : ""
}.svg`);
......
import { useState } from "react";
import MyButton from "@/components/mui/MyButton";
import { sortState } from "@/components/mui/MyTableNew";
import MyTable from "@/components/mui/MyTableNew";
const MyTableDemo = () => {
const [page, setPage] = useState(0);
const [count, setCount] = useState(5);
const pageChange = (value: number) => {
setPage(value - 1);
};
const [sortState, setSortState] = useState<sortState>({
field: "",
order: "",
});
const rows = [
{
a: "as123",
b: "werewrw",
c: "asdfasf",
d: "asdfasdf",
e: "asd4534",
id: "1",
},
{
a: "asdf",
b: "asdg",
c: "asdasdffasf",
d: "sfgh",
e: "asdgshdsdfh534",
id: "2",
},
{
a: "dfgj",
b: "xcvb",
c: "xcvb",
d: "xcvb",
e: "ert",
id: "3",
},
{
a: "xcgh",
b: "sdf",
c: "sdfg",
d: "sdfg",
e: "wertwe",
id: "4",
},
{
a: "as123",
b: "werewrw",
c: "asdfasf",
d: "asdfasdf",
e: "asd4534",
id: "5",
},
{
a: "asdf",
b: "asdg",
c: "asdasdffasf",
d: "sfgh",
e: "asdgshdsdfh534",
id: "6",
},
{
a: "dfgj",
b: "xcvb",
c: "xcvb",
d: "xcvb",
e: "ert",
id: "7",
},
{
a: "xcgh",
b: "sdf",
c: "sdfg",
d: "sdfg",
e: "wertwe",
id: "8",
},
{
a: "as123",
b: "werewrw",
c: "asdfasf",
d: "asdfasdf",
e: "asd4534",
id: "9",
},
{
a: "asdf",
b: "asdg",
c: "asdasdffasf",
d: "sfgh",
e: "asdgshdsdfh534",
id: "10",
},
{
a: "dfgj",
b: "xcvb",
c: "xcvb",
d: "xcvb",
e: "ert",
id: "11",
},
{
a: "xcgh",
b: "sdf",
c: "sdfg",
d: "sdfg",
e: "wertwe",
id: "12",
},
];
const headCells = [
{
id: "a",
label: "属性a",
width: "25%",
},
{
id: "b",
label: "属性b",
width: "25%",
},
{
id: "c",
label: "属性c",
width: "25%",
},
{
id: "d",
label: "属性d",
width: "25%",
},
];
const sortHeadCells = [
{
id: "a",
label: "属性a",
width: "25%",
sort: true,
},
{
id: "b",
label: "属性b",
width: "25%",
sort: true,
},
{
id: "c",
label: "属性c",
width: "25%",
},
{
id: "d",
label: "属性d",
width: "25%",
},
];
const [selectItems, setSelectItems] = useState(["1"]);
const randerButton = () => {
return <MyButton text="下载"></MyButton>;
};
console.log(selectItems);
return (
<div>
<h2>常规表格</h2>
<div>
<MyTable
rows={rows}
headCells={headCells}
selectItems={selectItems}
setSelectItems={setSelectItems}
/>
</div>
<h2>有复选框表格</h2>
<div>
<MyTable
rows={rows}
headCells={headCells}
hasCheckbox={true}
selectItems={selectItems}
setSelectItems={setSelectItems}
/>
</div>
<h2>固定表头(需要指定每列宽度 不然对不齐)表格,内容滚动, </h2>
<div style={{ height: "300px" }}>
<MyTable
rows={rows}
headCells={headCells}
hasCheckbox={true}
selectItems={selectItems}
setSelectItems={setSelectItems}
fixedHead={true}
/>
</div>
<h2>带分页的表格</h2>
<div>
<MyTable
rows={rows}
headCells={headCells}
hasCheckbox={true}
selectItems={selectItems}
setSelectItems={setSelectItems}
hasTableFooter={true}
page={page}
count={count}
pageChange={pageChange}
/>
</div>
<h2>带分页且固定表头(需要指定每列宽度 不然对不齐)的表格</h2>
<div style={{ height: "300px" }}>
<MyTable
rows={rows}
headCells={headCells}
hasCheckbox={true}
selectItems={selectItems}
setSelectItems={setSelectItems}
fixedHead={true}
hasTableFooter={true}
page={page}
count={count}
pageChange={pageChange}
/>
</div>
<h2>表格底部带按钮 有分页才能用</h2>
<div style={{ height: "300px" }}>
<MyTable
rows={rows}
headCells={headCells}
hasCheckbox={true}
selectItems={selectItems}
setSelectItems={setSelectItems}
fixedHead={true}
hasTableFooter={true}
page={page}
count={count}
pageChange={pageChange}
randerTooterContent={randerButton()}
/>
</div>
<h2>
带排序表格(headCells中sort为true, 传入sortState、setSortState字段)
</h2>
<div>
<MyTable
rows={rows}
headCells={sortHeadCells}
selectItems={selectItems}
setSelectItems={setSelectItems}
sortState={sortState}
setSortState={setSortState}
/>
</div>
</div>
);
};
export default MyTableDemo;
.demoBox {
padding: 16px 16px 0;
box-sizing: border-box;
height: 100%;
overflow: auto;
display: flex;
flex-direction: column;
}
.demoContentBox {
padding-top: 16px;
flex: 1;
}
import { useMessage } from "@/components/MySnackbar";
// import usePass from "@/hooks/usePass";
import { operation, route } from "@/router";
import { Button } from "@mui/material";
import { Box } from "@mui/system";
import { useEffect } from "react";
import MyTableDemo from "./MyTableDemo";
import RadioGroupOfButtonStyle from "@/components/CommonComponents/RadioGroupOfButtonStyle";
import { useCallback, useMemo, useState } from "react";
import styles from "./index.module.css";
const Demo = ({
childrenRoutes,
}: {
childrenRoutes?: Array<route | operation>;
}) => {
const message = useMessage();
// const isPass = usePass();
useEffect(() => {
// console.log(isPass("PROJECT_OVERIVEW_CREATE"), "11111111111");
// console.log(isPass("PROJECT_SUMMARY_MEMBER"), "2222222");
// console.log(isPass("test"), "33333");
// eslint-disable-next-line react-hooks/exhaustive-deps
const Demo = () => {
const demoList = useCallback(() => {
return [
{
name: "表格",
element: MyTableDemo,
},
{
name: "box",
element: () => {
return <div>box</div>;
},
},
];
}, []);
const radioOptionsArr = [
{
value: "表格",
label: "表格",
},
{
value: "box",
label: "box",
},
];
const handleRadio = (e: string) => {
setSelectDemo(e);
};
const [selectDemo, setSelectDemo] = useState("表格");
const randerDemo = useMemo(() => {
return demoList().filter((item) => item.name === selectDemo)[0].element;
}, [selectDemo, demoList]);
return (
<Box>
<Box>{JSON.stringify(childrenRoutes)}</Box>
<Box>{JSON.stringify(process.env.NODE_ENV)}</Box>
<Button onClick={() => message.success("测试测试")}>message测试</Button>
</Box>
<div className={styles.demoBox}>
<RadioGroupOfButtonStyle
value={selectDemo}
radioOptions={radioOptionsArr}
handleRadio={handleRadio}
/>
<div className={styles.demoContentBox}>{randerDemo()}</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