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,25 +5,74 @@ 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([]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const loadMore = useCallback(() => {
 | 
				
			||||||
 | 
					    const meta = data?.meta;
 | 
				
			||||||
 | 
					    if (!meta || meta.page >= meta.totalPage) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    run({
 | 
				
			||||||
 | 
					      page: meta.page + 1,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }, [data]);
 | 
				
			||||||
 | 
					  const { lastItem, setLastItem } = useLoadMoreObserver({ loadMore });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleSelect = ({ key }) => {
 | 
				
			||||||
 | 
					    setRole(roles.find((item: any) => item.name === key));
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    if (!roles[0]) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    setRole(roles[0]);
 | 
				
			||||||
 | 
					  }, [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,
 | 
					        key: item.name,
 | 
				
			||||||
    label: (
 | 
					        label:
 | 
				
			||||||
 | 
					          index === roles.length - 1 ? (
 | 
				
			||||||
 | 
					            <div ref={lastItem}>
 | 
				
			||||||
 | 
					              <RolesMenu.Item
 | 
				
			||||||
 | 
					                item={item}
 | 
				
			||||||
 | 
					                onEdit={() => {
 | 
				
			||||||
 | 
					                  setVisible(true);
 | 
				
			||||||
 | 
					                  setRecord(item);
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          ) : (
 | 
				
			||||||
            <RolesMenu.Item
 | 
					            <RolesMenu.Item
 | 
				
			||||||
              item={item}
 | 
					              item={item}
 | 
				
			||||||
              onEdit={() => {
 | 
					              onEdit={() => {
 | 
				
			||||||
@ -32,28 +81,22 @@ export const RolesMenu: React.FC & {
 | 
				
			|||||||
              }}
 | 
					              }}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
  }));
 | 
					      })),
 | 
				
			||||||
 | 
					    [roles, lastItem],
 | 
				
			||||||
  const handleSelect = ({ key }) => {
 | 
					  );
 | 
				
			||||||
    setRole((data?.data || []).find((item: any) => item.name === key));
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  useEffect(() => {
 | 
					 | 
				
			||||||
    if (!data?.data.length) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    setRole(data?.data[0]);
 | 
					 | 
				
			||||||
  }, [data, setRole]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <>
 | 
					    <>
 | 
				
			||||||
      {items.length ? (
 | 
					      {roles.length ? (
 | 
				
			||||||
 | 
					        <>
 | 
				
			||||||
          <Menu
 | 
					          <Menu
 | 
				
			||||||
            style={{ border: 'none', maxHeight: '65vh', overflowY: 'auto' }}
 | 
					            style={{ border: 'none', maxHeight: '65vh', overflowY: 'auto' }}
 | 
				
			||||||
            items={items}
 | 
					            items={items}
 | 
				
			||||||
            selectedKeys={[role?.name]}
 | 
					            selectedKeys={[role?.name]}
 | 
				
			||||||
            onSelect={handleSelect}
 | 
					            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