import React from 'react';
|
import {
|
Title,
|
useTranslate,
|
useNotify,
|
useRedirect,
|
useRefresh,
|
useDelete,
|
} from 'react-admin';
|
import { styled } from '@mui/material/styles';
|
import {
|
Box,
|
Collapse,
|
IconButton,
|
Table,
|
TableBody,
|
TableCell,
|
TableContainer,
|
TableHead,
|
TableRow,
|
Paper,
|
Card,
|
Typography,
|
TextField,
|
Tooltip,
|
Button,
|
Chip,
|
} from '@mui/material';
|
import { Add, Edit, Delete } from '@mui/icons-material';
|
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
import request from '@/utils/request';
|
import DeptEdit from './MenuEdit';
|
import * as Icons from '@mui/icons-material';
|
|
|
const RESOURCE = 'menu';
|
const TITLE = 'menu.menu';
|
|
const columns = [
|
{
|
id: 'name',
|
label: 'table.field.menu.name',
|
minWidth: 200,
|
},
|
{
|
id: 'icon',
|
label: 'table.field.menu.icon',
|
minWidth: 80,
|
format: (iconStr) => {
|
if (iconStr) {
|
const IconComponent = getIconComponent(iconStr);
|
if (IconComponent) {
|
return <IconComponent />;
|
}
|
}
|
}
|
},
|
{
|
id: 'route',
|
label: 'table.field.menu.route',
|
minWidth: 80,
|
},
|
{
|
id: 'component',
|
label: 'table.field.menu.component',
|
minWidth: 100,
|
},
|
{
|
id: 'authority',
|
label: 'table.field.menu.authority',
|
minWidth: 100,
|
},
|
{
|
id: 'type',
|
label: 'table.field.menu.type',
|
minWidth: 100,
|
format: (val) => {
|
if (Number(val) === 0) {
|
return <Chip variant='outlined' label="Menu" color="primary" size='small' />;
|
} else {
|
return <Chip variant='outlined' label="Button" color="default" size='small' />;
|
}
|
}
|
},
|
{
|
id: 'sort',
|
label: 'table.field.menu.sort',
|
minWidth: 80,
|
format: (val) => {
|
return <Typography variant='body2' sx={{ fontWeight: 'bold' }}>{val}</Typography>
|
}
|
},
|
// {
|
// id: 'updateTime',
|
// label: 'common.field.updateTime',
|
// minWidth: 120,
|
// format: (val) => {
|
// return new Date(val).toLocaleString();
|
// }
|
// },
|
{
|
id: 'id',
|
label: 'common.field.id',
|
minWidth: 80,
|
},
|
{
|
id: 'actions',
|
label: 'common.field.opt',
|
minWidth: 100,
|
},
|
];
|
|
const getIconComponent = (iconStr) => {
|
return Icons[iconStr] || null;
|
};
|
|
const StyledTableRow = styled(TableRow)(({ theme }) => ({
|
'& .MuiButtonBase-root': {
|
padding: '0px 8px'
|
}
|
}));
|
|
const StyledTableCell = styled(TableCell)(({ theme }) => ({
|
overflow: 'hidden',
|
textOverflow: 'ellipsis',
|
whiteSpace: 'nowrap',
|
maxWidth: 600,
|
}));
|
|
const TreeTableRow = (props) => {
|
const { row, depth = 0, openNodes, setOpenNodes, onEdit, onDelete } = props;
|
const translate = useTranslate();
|
|
const toggleNode = (id) => {
|
setOpenNodes(prevState => ({ ...prevState, [id]: !prevState[id] }));
|
};
|
|
const isOpen = openNodes[row.id] || false;
|
|
return (
|
<React.Fragment>
|
<StyledTableRow hover tabIndex={-1} key={row.id}>
|
<StyledTableCell sx={{ padding: 0 }}>
|
{row.children && (
|
<IconButton
|
aria-label="expand row"
|
size="small"
|
onClick={() => toggleNode(row.id)}
|
>
|
{isOpen ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
|
</IconButton>
|
)}
|
</StyledTableCell>
|
{columns.map((column, idx) => {
|
if (column.id !== 'actions') {
|
let value = row[column.id];
|
if (column.id === 'name' && row['type'] === 0) {
|
value = translate(value);
|
}
|
return (
|
<StyledTableCell
|
key={column.id}
|
align={column.align || 'left'}
|
style={{
|
paddingLeft: idx === 0 && (depth * 16 + 16),
|
opacity: column.id === 'icon' && .6
|
}}
|
onClick={() => toggleNode(row.id)}
|
>
|
{column.format ? column.format(value) : value}
|
</StyledTableCell>
|
)
|
}
|
})}
|
<StyledTableCell>
|
<Tooltip title="Edit">
|
<IconButton onClick={() => onEdit(row)}>
|
<Edit />
|
</IconButton>
|
</Tooltip>
|
<Tooltip title="Delete">
|
<IconButton onClick={() => onDelete(row)}>
|
<Delete />
|
</IconButton>
|
</Tooltip>
|
</StyledTableCell>
|
</StyledTableRow>
|
{row.children && isOpen && (
|
row.children.map((child) => (
|
<TreeTableRow
|
key={child.id}
|
row={child}
|
depth={depth + 1}
|
onEdit={onEdit}
|
onDelete={onDelete}
|
openNodes={openNodes}
|
setOpenNodes={setOpenNodes}
|
/>
|
))
|
)}
|
</React.Fragment>
|
);
|
};
|
|
const MenuList = () => {
|
const translate = useTranslate();
|
const notify = useNotify();
|
const redirect = useRedirect();
|
const refresh = useRefresh();
|
const [deleteOne] = useDelete();
|
|
const [treeData, setTreeData] = React.useState([]);
|
const [filter, setFilter] = React.useState("");
|
const [createDialog, setCreateDialog] = React.useState(false);
|
const [editRecord, setEditRecord] = React.useState(null);
|
const [openNodes, setOpenNodes] = React.useState({});
|
const [expandAll, setExpandAll] = React.useState(false);
|
|
const http = async () => {
|
const res = await request.post(RESOURCE + '/tree', {
|
condition: filter
|
});
|
if (res?.data?.code === 200) {
|
setTreeData(res.data.data);
|
} else {
|
notify(res.data.msg);
|
}
|
}
|
|
React.useEffect(() => {
|
http();
|
}, [filter]);
|
|
const handleRefresh = () => {
|
http();
|
};
|
|
const handleAdd = () => {
|
setEditRecord(null);
|
setCreateDialog(true);
|
};
|
|
const handleEdit = (node) => {
|
setEditRecord(node);
|
setCreateDialog(true);
|
};
|
|
const handleDelete = (node) => {
|
if (window.confirm(translate('ra.message.delete_content'))) {
|
deleteOne(
|
RESOURCE,
|
{ id: node.id },
|
{
|
onSuccess: () => {
|
handleRefresh();
|
notify('Department deleted successfully', { type: 'info', messageArgs: { _: 'Department deleted successfully' } });
|
},
|
onError: (error) => {
|
notify(`Error: ${error.message}`, { type: 'warning', messageArgs: { _: `Error: ${error.message}` } });
|
},
|
}
|
);
|
}
|
};
|
|
const toggleExpandAll = () => {
|
setExpandAll(prevExpandAll => {
|
const newExpandAll = !prevExpandAll;
|
const newOpenNodes = {};
|
const updateOpenNodes = (nodes) => {
|
nodes.forEach(node => {
|
newOpenNodes[node.id] = newExpandAll;
|
if (node.children) {
|
updateOpenNodes(node.children);
|
}
|
});
|
};
|
updateOpenNodes(treeData);
|
setOpenNodes(newOpenNodes);
|
return newExpandAll;
|
});
|
};
|
|
return (
|
<div>
|
<DeptEdit
|
editRecord={editRecord}
|
open={createDialog}
|
setOpen={setCreateDialog}
|
callback={() => {
|
handleRefresh();
|
}}
|
resource={RESOURCE}
|
/>
|
<Title title={TITLE} />
|
<Box sx={{ mt: 2, mr: 3, mb: 1, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
<Box width={300} >
|
<Button
|
variant="outlined"
|
color="primary"
|
startIcon={expandAll ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
|
onClick={toggleExpandAll}
|
sx={{ ml: 1 }}
|
>
|
{expandAll ? translate('common.action.collapseAll') : translate('common.action.expandAll')}
|
</Button>
|
{/* <TextField
|
label="Search"
|
value={filter}
|
onChange={({ target }) => {
|
setFilter(target.value)
|
}}
|
variant="filled"
|
size="small"
|
margin="dense"
|
fullWidth
|
/> */}
|
</Box>
|
<Box>
|
<Button
|
variant="outlined"
|
color="primary"
|
startIcon={<RefreshIcon />}
|
onClick={handleRefresh}
|
sx={{ ml: 1 }}
|
>
|
{translate('ra.action.refresh')}
|
</Button>
|
<Button
|
variant="outlined"
|
color="primary"
|
startIcon={<Add />}
|
onClick={handleAdd}
|
sx={{ ml: 1 }}
|
>
|
{translate('ra.action.add')}
|
</Button>
|
</Box>
|
</Box>
|
<Card>
|
<TableContainer component={Paper}>
|
<Table size="small">
|
<TableHead>
|
<TableRow>
|
<StyledTableCell sx={{ padding: 0, width: 60 }} />
|
{columns.map((column, idx) => (
|
<StyledTableCell
|
key={idx}
|
align={column.align || 'left'}
|
style={{
|
minWidth: column.minWidth
|
}}
|
>
|
{translate(column.label)}
|
</StyledTableCell>
|
))}
|
</TableRow>
|
</TableHead>
|
<TableBody>
|
{treeData && treeData.length > 0 && (
|
treeData.map((row) => (
|
<TreeTableRow
|
key={row.id}
|
row={row}
|
onEdit={handleEdit}
|
onDelete={handleDelete}
|
openNodes={openNodes}
|
setOpenNodes={setOpenNodes}
|
/>
|
))
|
)}
|
</TableBody>
|
</Table>
|
</TableContainer>
|
</Card>
|
</div>
|
);
|
};
|
|
export default MenuList;
|