fix(acl): role menu list only displays one page (#3775)
* fix(acl): role menu list only displays one page * chore: remove log
This commit is contained in:
parent
9b27fa955a
commit
49d3401379
@ -79,7 +79,6 @@ export const RolesManagement: React.FC = () => {
|
|||||||
resource: 'roles',
|
resource: 'roles',
|
||||||
action: 'list',
|
action: 'list',
|
||||||
params: {
|
params: {
|
||||||
pagination: false,
|
|
||||||
filter: {
|
filter: {
|
||||||
'name.$ne': 'root',
|
'name.$ne': 'root',
|
||||||
},
|
},
|
||||||
|
@ -5,55 +5,98 @@ import {
|
|||||||
useAPIClient,
|
useAPIClient,
|
||||||
useResourceActionContext,
|
useResourceActionContext,
|
||||||
} from '@nocobase/client';
|
} from '@nocobase/client';
|
||||||
import { Menu, Empty, Dropdown, App, Tag, Row, Col } from 'antd';
|
import { Menu, Empty, Dropdown, App, Tag, Row, Col, Spin } from 'antd';
|
||||||
import { TagOutlined, MoreOutlined } from '@ant-design/icons';
|
import { TagOutlined, MoreOutlined } from '@ant-design/icons';
|
||||||
import React, { useContext, useEffect } from 'react';
|
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useACLTranslation } from './locale';
|
import { useACLTranslation } from './locale';
|
||||||
import { Schema } from '@formily/react';
|
import { Schema } from '@formily/react';
|
||||||
import { RolesManagerContext } from './RolesManagerProvider';
|
import { RolesManagerContext } from './RolesManagerProvider';
|
||||||
import { roleEditSchema } from './schemas/roles';
|
import { roleEditSchema } from './schemas/roles';
|
||||||
|
import { useLoadMoreObserver } from './hooks/load-more-observer';
|
||||||
|
|
||||||
export const RolesMenu: React.FC & {
|
export const RolesMenu: React.FC & {
|
||||||
Item: React.FC<{ item: any; onEdit: () => void }>;
|
Item: React.FC<{ item: any; onEdit: () => void }>;
|
||||||
} = () => {
|
} = () => {
|
||||||
const { t } = useACLTranslation();
|
const { t } = useACLTranslation();
|
||||||
const { data } = useResourceActionContext();
|
const { data, run, loading } = useResourceActionContext();
|
||||||
const [visible, setVisible] = React.useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [record, setRecord] = React.useState(null);
|
const [record, setRecord] = useState(null);
|
||||||
const { role, setRole } = useContext(RolesManagerContext);
|
const { role, setRole } = useContext(RolesManagerContext);
|
||||||
const items = (data?.data || []).map((item: any) => ({
|
const [roles, setRoles] = useState([]);
|
||||||
key: item.name,
|
|
||||||
label: (
|
const loadMore = useCallback(() => {
|
||||||
<RolesMenu.Item
|
const meta = data?.meta;
|
||||||
item={item}
|
if (!meta || meta.page >= meta.totalPage) {
|
||||||
onEdit={() => {
|
return;
|
||||||
setVisible(true);
|
}
|
||||||
setRecord(item);
|
run({
|
||||||
}}
|
page: meta.page + 1,
|
||||||
/>
|
});
|
||||||
),
|
}, [data]);
|
||||||
}));
|
const { lastItem, setLastItem } = useLoadMoreObserver({ loadMore });
|
||||||
|
|
||||||
const handleSelect = ({ key }) => {
|
const handleSelect = ({ key }) => {
|
||||||
setRole((data?.data || []).find((item: any) => item.name === key));
|
setRole(roles.find((item: any) => item.name === key));
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data?.data.length) {
|
if (!roles[0]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setRole(data?.data[0]);
|
setRole(roles[0]);
|
||||||
}, [data, setRole]);
|
}, [roles, setRole]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!data?.data?.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ref = React.createRef<any>();
|
||||||
|
setLastItem(ref);
|
||||||
|
|
||||||
|
setRoles((prev) => prev.concat(data.data));
|
||||||
|
}, [data, setLastItem]);
|
||||||
|
|
||||||
|
const items = useMemo(
|
||||||
|
() =>
|
||||||
|
roles.map((item: any, index: number) => ({
|
||||||
|
key: item.name,
|
||||||
|
label:
|
||||||
|
index === roles.length - 1 ? (
|
||||||
|
<div ref={lastItem}>
|
||||||
|
<RolesMenu.Item
|
||||||
|
item={item}
|
||||||
|
onEdit={() => {
|
||||||
|
setVisible(true);
|
||||||
|
setRecord(item);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<RolesMenu.Item
|
||||||
|
item={item}
|
||||||
|
onEdit={() => {
|
||||||
|
setVisible(true);
|
||||||
|
setRecord(item);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
[roles, lastItem],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{items.length ? (
|
{roles.length ? (
|
||||||
<Menu
|
<>
|
||||||
style={{ border: 'none', maxHeight: '65vh', overflowY: 'auto' }}
|
<Menu
|
||||||
items={items}
|
style={{ border: 'none', maxHeight: '65vh', overflowY: 'auto' }}
|
||||||
selectedKeys={[role?.name]}
|
items={items}
|
||||||
onSelect={handleSelect}
|
selectedKeys={[role?.name]}
|
||||||
/>
|
onSelect={handleSelect}
|
||||||
|
/>
|
||||||
|
{loading && <Spin />}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||||
)}
|
)}
|
||||||
@ -92,12 +135,15 @@ RolesMenu.Item = function DepartmentTreeItem({ item, onEdit }) {
|
|||||||
deleteDepartment();
|
deleteDepartment();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const title = Schema.compile(item.title, { t });
|
||||||
return (
|
return (
|
||||||
<Row>
|
<Row>
|
||||||
<Col flex={3} style={{ display: 'inline-flex', alignItems: 'center' }}>
|
<Col flex={3} style={{ display: 'inline-flex', alignItems: 'center' }}>
|
||||||
<span style={{ whiteSpace: 'nowrap', width: '120px', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
<span style={{ whiteSpace: 'nowrap', width: '120px', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||||
<TagOutlined />
|
<TagOutlined />
|
||||||
<span style={{ marginLeft: '10px' }}>{Schema.compile(item.title, { t })}</span>
|
<span style={{ marginLeft: '10px' }} title={title}>
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</Col>
|
</Col>
|
||||||
<Col>
|
<Col>
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
export const useLoadMoreObserver = ({
|
||||||
|
loadMore,
|
||||||
|
}: {
|
||||||
|
loadMore: () => void; // callback
|
||||||
|
}) => {
|
||||||
|
const [lastItem, setLastItem] = useState<React.RefObject<any>>(null);
|
||||||
|
const observer = useRef<IntersectionObserver | null>(null);
|
||||||
|
const observeExposure = useCallback(
|
||||||
|
(lastItem: React.RefObject<any>) => {
|
||||||
|
if (!lastItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
observer.current && observer.current.disconnect();
|
||||||
|
observer.current = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
if (entry.target !== lastItem.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
observer.current && observer.current.unobserve(lastItem.current);
|
||||||
|
loadMore();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
lastItem.current && observer.current && observer.current.observe(lastItem.current);
|
||||||
|
},
|
||||||
|
[loadMore],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
observeExposure(lastItem);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
observer.current && observer.current.disconnect();
|
||||||
|
};
|
||||||
|
}, [lastItem, observeExposure]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
lastItem,
|
||||||
|
setLastItem,
|
||||||
|
};
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user