feat(web): 实现自定义用户菜单并优化侧边栏

- 新增用户菜单获取和解析逻辑
- 动态生成侧边栏菜单项,支持自定义图标
- 优化菜单项样式和交互
- 添加图标选择组件
main
Tuzki 5 months ago
parent 225a963955
commit 1997ff9dd4
  1. 6
      web/client/api/index.ts
  2. 12
      web/client/api/tools/interceptors.ts
  3. 4
      web/client/api/user/index.ts
  4. 6
      web/components/IconSelect/IconSelect.css
  5. 113
      web/components/IconSelect/IconSelect.tsx
  6. 200
      web/components/layout/side-bar.tsx
  7. 26
      web/hooks/useUserMenu.ts
  8. 17
      web/locales/en/common.ts
  9. 19
      web/locales/zh/common.ts
  10. 157
      web/new-components/layout/Construct.tsx
  11. 2
      web/new-components/layout/Sider.tsx
  12. 657
      web/new-components/layout/SystemConstruct.tsx
  13. 7
      web/new-components/layout/UserBar.tsx
  14. 24
      web/next.config.js
  15. 26
      web/pages/_app.tsx
  16. 19
      web/pages/login.tsx
  17. 25
      web/pages/style.css
  18. 17
      web/pages/system/menu/index.tsx
  19. 3
      web/pages/system/organization/index.tsx
  20. 0
      web/public/menus/construct.svg
  21. 0
      web/public/menus/construct_active.svg
  22. 0
      web/public/menus/system.svg
  23. 0
      web/public/menus/system_active.svg
  24. 1
      web/utils/constants/storage.ts

@ -1,5 +1,5 @@
import { getUserId } from '@/utils';
import { HEADER_USER_ID_KEY, STORAGE_USERINFO_KEY } from '@/utils/constants/index';
import { HEADER_USER_ID_KEY, STORAGE_USERINFO_KEY,USER_MENU } from '@/utils/constants/index';
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
// 定义 API 响应数据的类型
@ -65,10 +65,10 @@ ins.interceptors.response.use(
response => response,
(error: AxiosError<ResponseType>) => {
// 仅处理业务逻辑错误
if (error.response?.data?.err_code === 'E0003') {
debugger
if (error.response?.data?.err_code === '401') {
// 清理认证信息
localStorage.removeItem(STORAGE_USERINFO_KEY);
localStorage.removeItem(USER_MENU);
// 安全的环境判断(避免服务端渲染报错)
if (typeof window !== 'undefined' && !window.location.pathname.startsWith('/login')) {

@ -33,7 +33,17 @@ export const apiInterceptors = async <T = any, D = any>(
error = err;
}
const errMsg = err instanceof AxiosError ? (JSON.parse(err.request.response)?.err_msg ?? '接口异常') : err.message;
let errMsg = '接口异常';
if (err instanceof AxiosError && err.request.response) {
try {
const parsedResponse = JSON.parse(err.request.response);
errMsg = parsedResponse?.err_msg ?? '接口异常';
} catch (parseError) {
console.error('JSON 解析失败', parseError);
}
} else {
errMsg = err.message;
}
notification.error({
message: '请求异常',

@ -19,3 +19,7 @@ export const doLogin = (data: Record<string, any>) => {
data,
);
};
//获取用户菜单
export const getUserMenu = () => {
return POST<Record<string, any>, any>(`/api/menu/user_menu_auth_tree`);
};

@ -0,0 +1,6 @@
/* 可选,自定义下拉样式 */
.ant-select-item-option-content {
display: flex;
align-items: center;
gap: 8px;
}

File diff suppressed because one or more lines are too long

@ -1,7 +1,8 @@
import { ChatContext } from '@/app/chat-context';
import UserBar from '@/new-components/layout/UserBar';
import { DarkSvg, SunnySvg } from '@/components/icons';
import { STORAGE_LANG_KEY, STORAGE_THEME_KEY, STORAGE_USERINFO_KEY } from '@/utils/constants/index';
import { GlobalOutlined, MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
import Icon, { GlobalOutlined, MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
import { Popover, Tooltip } from 'antd';
import { ItemType } from 'antd/es/menu/hooks/useItems';
import cls from 'classnames';
@ -12,6 +13,8 @@ import Link from 'next/link';
import { useRouter } from 'next/router';
import { ReactNode, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { USER_MENU } from '@/utils/constants/index';
type SettingItem = {
key: string;
name: string;
@ -40,17 +43,27 @@ type RouteItem = {
// }
function smallMenuItemStyle(active?: boolean) {
return `flex items-center justify-center mx-auto rounded w-14 h-14 text-xl hover:bg-[#F1F5F9] dark:hover:bg-theme-dark transition-colors cursor-pointer ${
active ? 'bg-[#F1F5F9] dark:bg-theme-dark' : ''
}`;
return `flex items-center justify-center mx-auto rounded w-14 h-14 text-xl hover:bg-[#F1F5F9] dark:hover:bg-theme-dark transition-colors cursor-pointer ${active ? 'bg-[#F1F5F9] dark:bg-theme-dark' : ''
}`;
}
function SideBar() {
// const { chatId, scene, isMenuExpand, refreshDialogList, setIsMenuExpand, setAgent, mode, setMode, adminList } =
// useContext(ChatContext);
const [userMenus, setUserMenus] = useState<any[]>([]);
useEffect(() => {
try {
const data = localStorage.getItem(USER_MENU);
const parsed = data ? JSON.parse(data) : {};
setUserMenus(parsed.menus || []);
} catch (error) {
console.error('Failed to parse user menus from localStorage', error);
setUserMenus([]);
}
}, []);
const { isMenuExpand, setIsMenuExpand, mode, setMode, adminList } = useContext(ChatContext);
const { pathname } = useRouter();
const { t, i18n } = useTranslation();
const [logo, setLogo] = useState<string>('/logo_zh_latest.png');
const [logo, setLogo] = useState<string>('/LOGO.png');
const hasAdmin = useMemo(() => {
const { user_id } = JSON.parse(localStorage.getItem(STORAGE_USERINFO_KEY) || '{}');
@ -244,87 +257,100 @@ function SideBar() {
}, [t, mode, handleToggleTheme, i18n, handleChangeLang, isMenuExpand, handleToggleMenu, setMode]);
const functions = useMemo(() => {
const items: RouteItem[] = [
{
key: 'chat',
name: t('chat_online'),
icon: (
<Image
key='image_chat'
src={pathname === '/chat' ? '/menus/chat_active.svg' : '/menus/chat.svg'}
alt='chat_image'
width={20}
height={20}
/>
),
path: '/chat',
isActive: pathname.startsWith('/chat'),
},
{
key: 'explore',
name: t('explore'),
isActive: pathname === '/',
icon: (
<Image
key='image_explore'
src={pathname === '/' ? '/menus/explore_active.svg' : '/menus/explore.svg'}
alt='construct_image'
width={20}
height={20}
/>
),
path: '/',
},
{
key: 'construct',
name: t('construct'),
isActive: pathname.startsWith('/construct'),
icon: (
<Image
key='image_construct'
src={pathname.startsWith('/construct') ? '/menus/app_active.svg' : '/menus/app.svg'}
alt='construct_image'
width={20}
height={20}
/>
),
path: '/construct/app',
},
{
key: 'system_management',
name: t('system_management'),
isActive: pathname.startsWith('/system'),
const items: RouteItem[] = (userMenus || [])
.filter((menu) => menu.visible === 1) // 过滤可见项
.map((menu) => {
const isActive = pathname === menu.path || (menu.path !== '/' && pathname.startsWith(`/${menu.component_name}`));
let iconSrc = '/menus/default.svg'; // 默认图标
if (isActive) {
iconSrc = `/menus/${menu.component_name}_active.svg`; // 动态 active 状态图标
} else {
iconSrc = `/menus/${menu.component_name}.svg`;
}
return {
key: menu.component_name,
name: t(menu.component_name),
path: menu.path,
isActive,
icon: (
<Image
key='image_construct'
src={pathname.startsWith('/system') ? '/menus/setting_active.svg' : '/menus/setting.svg'}
alt='construct_image'
key={`image_${menu.component_name}`}
src={iconSrc}
alt={`${menu.name}_image`}
width={20}
height={20}
/>
),
path: '/system/organization',
},
];
// if (hasAdmin) {
// items.push({
// key: 'evaluation',
// name: '场景评测',
// icon: (
// <Image
// key='image_construct'
// src={pathname.startsWith('/evaluation') ? '/menus/app_active.svg' : '/menus/app.svg'}
// alt='construct_image'
// width={20}
// height={20}
// />
// ),
// path: '/evaluation',
// isActive: pathname === '/evaluation',
// });
// }
return items;
}, [t, pathname, hasAdmin]);
};
});
return items;
// const items: RouteItem[] = [
// {
// key: 'chat',
// name: t('chat_online'),
// icon: (
// <Image
// key='image_chat'
// src={pathname === '/chat' ? '/menus/chat_active.svg' : '/menus/chat.svg'}
// alt='chat_image'
// width={20}
// height={20}
// />
// ),
// path: '/chat',
// isActive: pathname.startsWith('/chat'),
// },
// {
// key: 'explore',
// name: t('explore'),
// isActive: pathname === '/',
// icon: (
// <Image
// key='image_explore'
// src={pathname === '/' ? '/menus/explore_active.svg' : '/menus/explore.svg'}
// alt='construct_image'
// width={20}
// height={20}
// />
// ),
// path: '/',
// },
// {
// key: 'construct',
// name: t('construct'),
// isActive: pathname.startsWith('/construct'),
// icon: (
// <Image
// key='image_construct'
// src={pathname.startsWith('/construct') ? '/menus/app_active.svg' : '/menus/app.svg'}
// alt='construct_image'
// width={20}
// height={20}
// />
// ),
// path: '/construct/app',
// },
// {
// key: 'system',
// name: t('system'),
// isActive: pathname.startsWith('/system'),
// icon: (
// <Image
// key='image_construct'
// src={pathname.startsWith('/system') ? '/menus/setting_active.svg' : '/menus/setting.svg'}
// alt='construct_image'
// width={20}
// height={20}
// />
// ),
// path: '/system/organization',
// },
// ];
// return items;
}, [t, pathname, hasAdmin,userMenus]);
// TODO: unused function
// const dropDownRoutes: ItemType[] = useMemo(() => {
@ -421,16 +447,16 @@ function SideBar() {
}, []);
useEffect(() => {
setLogo(mode === 'dark' ? '/logo_s_latest.png' : '/logo_zh_latest.png');
setLogo(mode === 'dark' ? '/LOGO.png' : '/LOGO.png');
}, [mode]);
if (!isMenuExpand) {
return (
<div
className='flex flex-col justify-between pt-4 h-screen bg-bar dark:bg-[#232734] animate-fade animate-duration-300'
// onMouseEnter={() => {
// setIsMenuExpand(true);
// }}
// onMouseEnter={() => {
// setIsMenuExpand(true);
// }}
>
<div>
<Link href='/' className='flex justify-center items-center pb-4'>
@ -463,9 +489,9 @@ function SideBar() {
return (
<div
className='flex flex-col justify-between h-screen px-4 pt-4 bg-bar bg-[#F9FBFF] dark:bg-[#232734] animate-fade animate-duration-300'
// onMouseLeave={() => {
// setIsMenuExpand(false);
// }}
// onMouseLeave={() => {
// setIsMenuExpand(false);
// }}
>
<div>
{/* LOGO */}

@ -0,0 +1,26 @@
// hooks/useUserMenu.ts
import { useState, useEffect } from 'react';
import { USER_MENU } from '@/utils/constants/index';
export const useUserMenu = () => {
const [userMenus, setUserMenus] = useState<any[]>([]);
useEffect(() => {
const loadMenus = () => {
try {
const data = localStorage.getItem(USER_MENU);
const parsed = data ? JSON.parse(data) : {};
setUserMenus(parsed.menus || []);
} catch (e) {
setUserMenus([]);
}
};
loadMenus();
window.addEventListener('storage', loadMenus);
return () => window.removeEventListener('storage', loadMenus);
}, []);
return userMenus;
};

@ -1,5 +1,6 @@
export const CommonEn = {
Knowledge_Space: 'Knowledge',
knowledge: 'Knowledge',
space: 'space',
Vector: 'Vector',
Owner: 'Owner',
@ -71,6 +72,7 @@ export const CommonEn = {
chunk_overlap: 'chunk_overlap',
The_amount_of_overlap: 'The amount of overlap between adjacent data chunks',
Prompt: 'Prompt',
prompt: 'Prompt',
scene: 'scene',
A_contextual_parameter:
'A contextual parameter used to define the setting or environment in which the prompt is being used',
@ -97,6 +99,7 @@ export const CommonEn = {
Remark: 'Remark',
Edit: 'Edit',
Database: 'Database',
database: 'Database',
Data_Source: 'Data Center',
Close_Sidebar: 'Fold',
Show_Sidebar: 'UnFold',
@ -108,6 +111,7 @@ export const CommonEn = {
create_knowledge: 'Create Knowledge',
path: 'Path',
model_manage: 'Models',
models: 'Models',
create_model: 'Create Model',
model_select_tips: 'Please select a model',
language_select_tips: 'Please select a language',
@ -215,6 +219,7 @@ export const CommonEn = {
sdk_insert: 'SDK Insert',
my_apps: 'My Apps',
awel_flow: 'AWEL Flow',
flow: 'AWEL Flow',
save: 'Save',
add_node: 'Add Node',
no_node: 'No Node',
@ -261,7 +266,7 @@ export const CommonEn = {
copy_url: 'Click the Copy Share link',
double_click_open: 'Double click on Nail nail to open',
construct: ' Construct App',
chat_online: 'Chat',
chat: 'Chat',
recommend_apps: 'Recommend',
all_apps: 'All',
latest_apps: 'Latest',
@ -358,11 +363,11 @@ export const CommonEn = {
resources: 'Resources',
app: 'App',
please_select_resource_type: 'Please select resource type',
user_information_management: 'userInformationManagement', //用户管理
institutional_framework: 'institutionalFramework', //机构框架
role_management: 'roleManagement', //角色管理
menu_management: 'menuManagement', //菜单管理
system_management: 'systemManagement', //系统管理
user: 'userInformationManagement', //用户管理
organization: 'organizationFramework', //机构框架
role: 'roleManagement', //角色管理
menu: 'menuManagement', //菜单管理
system: 'systemManagement', //系统管理
department_name: 'departmentName',
principal: 'Principal',
sort: 'Sort',

@ -8,6 +8,7 @@ interface Resources {
export const CommonZh: Resources['translation'] = {
Knowledge_Space: '知识库',
knowledge: '知识库',
space: '知识库',
Vector: '向量',
Owner: '创建人',
@ -105,6 +106,7 @@ export const CommonZh: Resources['translation'] = {
Remark: '备注',
Edit: '编辑',
Database: '数据库',
database: '数据库',
Data_Source: '数据中心',
Close_Sidebar: '收起',
Show_Sidebar: '展开',
@ -116,6 +118,7 @@ export const CommonZh: Resources['translation'] = {
create_flow: '创建工作流',
path: '路径',
model_manage: '模型管理',
models: '模型管理',
create_model: '创建模型',
model_select_tips: '请选择一个模型',
submit: '提交',
@ -141,6 +144,7 @@ export const CommonZh: Resources['translation'] = {
try_again: '刷新重试',
no_data: '暂无数据',
Prompt: '提示词',
prompt: '提示词',
Open_Sidebar: '展开',
verify: '确认',
cancel: '取消',
@ -221,7 +225,8 @@ export const CommonZh: Resources['translation'] = {
Discover_more: '发现更多',
sdk_insert: 'SDK接入',
my_apps: '我的应用',
awel_flow: 'AWEL 工作流',
awel_flow: '工作流',
flow: '工作流',
save: '保存',
add_node: '添加节点',
no_node: '没有可编排节点',
@ -269,7 +274,7 @@ export const CommonZh: Resources['translation'] = {
copy_url: '单击复制分享链接',
double_click_open: '双击钉钉打开',
construct: '应用管理',
chat_online: '在线对话',
chat: '在线对话',
recommend_apps: '热门推荐',
all_apps: '全部应用',
latest_apps: '最新应用',
@ -363,11 +368,11 @@ export const CommonZh: Resources['translation'] = {
resources: '资源',
app: '应用',
please_select_resource_type: '请选择资源类型',
user_information_management: '用户管理',
institutional_framework: '机构管理',
role_management: '角色管理',
menu_management: '菜单管理',
system_management: '系统管理',
user: '用户管理',
organization: '机构管理',
role: '角色管理',
menu: '菜单管理',
system: '系统管理',
department_name: '部门名称',
principal: '负责人',
sort: '排序',

@ -11,67 +11,76 @@ import { t } from 'i18next';
import { useRouter } from 'next/router';
import React from 'react';
import './style.css';
import { USER_MENU } from '@/utils/constants/index';
import * as Icons from '@ant-design/icons';
const renderIcon = (iconName?: string) => {
if (!iconName || iconName === '#' || !Icons[iconName]) return null;
const IconComponent = Icons[iconName as keyof typeof Icons];
return <IconComponent />;
};
function ConstructLayout({ children }: { children: React.ReactNode }) {
const items = [
{
key: 'app',
name: t('App'),
path: '/app',
icon: <AppstoreOutlined />,
// operations: (
// <Button
// className='border-none text-white bg-button-gradient h-full flex items-center'
// icon={<PlusOutlined className='text-base' />}
// // onClick={handleCreate}
// >
// {t('create_app')}
// </Button>
// ),
},
{
key: 'flow',
name: t('awel_flow'),
icon: <ForkOutlined />,
path: '/flow',
},
{
key: 'models',
name: t('model_manage'),
path: '/models',
icon: <Icon component={ModelSvg} />,
},
{
key: 'database',
name: t('Database'),
icon: <ConsoleSqlOutlined />,
path: '/database',
},
{
key: 'knowledge',
name: t('Knowledge_Space'),
icon: <PartitionOutlined />,
path: '/knowledge',
},
// {
// key: 'agent',
// name: t('Plugins'),
// path: '/agent',
// icon: <BuildOutlined />,
// },
{
key: 'prompt',
name: t('Prompt'),
icon: <MessageOutlined />,
path: '/prompt',
},
// {
// key: 'dbgpts',
// name: t('dbgpts_community'),
// path: '/dbgpts',
// icon: <BuildOutlined />,
// },
];
const data = localStorage.getItem(USER_MENU);
const parsed = data ? JSON.parse(data).menus.filter((item: any) => item.component_name == 'construct')[0].children : [];
console.log('parsed', parsed);
// const items = [
// {
// key: 'app',
// name: t('App'),
// path: '/app',
// icon: <AppstoreOutlined />,
// // operations: (
// // <Button
// // className='border-none text-white bg-button-gradient h-full flex items-center'
// // icon={<PlusOutlined className='text-base' />}
// // // onClick={handleCreate}
// // >
// // {t('create_app')}
// // </Button>
// // ),
// },
// {
// key: 'flow',
// name: t('awel_flow'),
// icon: <ForkOutlined />,
// path: '/flow',
// },
// {
// key: 'models',
// name: t('model_manage'),
// path: '/models',
// icon: <Icon component={ModelSvg} />,
// },
// {
// key: 'database',
// name: t('Database'),
// icon: <ConsoleSqlOutlined />,
// path: '/database',
// },
// {
// key: 'knowledge',
// name: t('Knowledge_Space'),
// icon: <PartitionOutlined />,
// path: '/knowledge',
// },
// // {
// // key: 'agent',
// // name: t('Plugins'),
// // path: '/agent',
// // icon: <BuildOutlined />,
// // },
// {
// key: 'prompt',
// name: t('Prompt'),
// icon: <MessageOutlined />,
// path: '/prompt',
// },
// // {
// // key: 'dbgpts',
// // name: t('dbgpts_community'),
// // path: '/dbgpts',
// // icon: <BuildOutlined />,
// // },
// ];
const router = useRouter();
const activeKey = router.pathname.split('/')[2];
// const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; // unused
@ -109,26 +118,28 @@ function ConstructLayout({ children }: { children: React.ReactNode }) {
size='small'
indicator={{ size: origin => origin - 60, align: 'center' }}
activeKey={activeKey}
items={items.map(items => {
items={parsed.map((item: any) => {
return {
key: items.key,
label: items.name,
key: item.component_name,
label: t(item.component_name),
children: children,
icon: items.icon,
icon: item.component_name === 'models'
? <Icon component={ModelSvg} />
: renderIcon(item.icon)
};
})}
onTabClick={key => {
router.push(`/construct/${key}`);
}}
// tabBarExtraContent={
// <Button
// className='border-none text-white bg-button-gradient h-full flex items-center'
// icon={<PlusOutlined className='text-base' />}
// // onClick={handleCreate}
// >
// {t('create_app')}
// </Button>
// }
// tabBarExtraContent={
// <Button
// className='border-none text-white bg-button-gradient h-full flex items-center'
// icon={<PlusOutlined className='text-base' />}
// // onClick={handleCreate}
// >
// {t('create_app')}
// </Button>
// }
/>
</ConfigProvider>
</div>

@ -86,7 +86,7 @@ const Sider: React.FC = () => {
) : (
<>
<Link href='/' className='flex items-center justify-center p-2 pb-4'>
<Image src='/logo_zh_latest.png' alt='DB-GPT' width={180} height={40} />
<Image src='/LOGO.png' alt='DB-GPT' width={180} height={40} />
</Link>
<div></div>
<div className='flex flex-col'>

@ -3,38 +3,668 @@ import { ConfigProvider, Tabs } from 'antd';
import { t } from 'i18next';
import { useRouter } from 'next/router';
import React from 'react';
import { USER_MENU } from '@/utils/constants/index';
import './style.css';
import * as Icons from '@ant-design/icons';
const IconFont = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/c/font_4926951_si989wen79f.js',
});
const renderIcon = (iconName?: string) => {
if (!iconName || iconName === '#' || !Icons[iconName]) return null;
const IconComponent = Icons[iconName as keyof typeof Icons];
return <IconComponent />;
};
// const iconMap = {
// AlertOutlined: <AlertOutlined />,
// AlibabaOutlined: <AlibabaOutlined />,
// AlignCenterOutlined: <AlignCenterOutlined />,
// AlignLeftOutlined: <AlignLeftOutlined />,
// AlignRightOutlined: <AlignRightOutlined />,
// AlipayCircleOutlined: <AlipayCircleOutlined />,
// AlipayOutlined: <AlipayOutlined />,
// AlipaySquareOutlined: <AlipaySquareOutlined />,
// AndroidOutlined: <AndroidOutlined />,
// AntCloudOutlined: <AntCloudOutlined />,
// AntDesignOutlined: <AntDesignOutlined />,
// ApartmentOutlined: <ApartmentOutlined />,
// ApiOutlined: <ApiOutlined />,
// ApiTwoTone: <ApiTwoTone />,
// AppleOutlined: <AppleOutlined />,
// AppstoreAddOutlined: <AppstoreAddOutlined />,
// AppstoreOutlined: <AppstoreOutlined />,
// AppstoreTwoTone: <AppstoreTwoTone />,
// AreaChartOutlined: <AreaChartOutlined />,
// ArrowDownOutlined: <ArrowDownOutlined />,
// ArrowLeftOutlined: <ArrowLeftOutlined />,
// ArrowRightOutlined: <ArrowRightOutlined />,
// ArrowUpOutlined: <ArrowUpOutlined />,
// ArrowsAltOutlined: <ArrowsAltOutlined />,
// AudioOutlined: <AudioOutlined />,
// AudioMutedOutlined: <AudioMutedOutlined />,
// AudioTwoTone: <AudioTwoTone />,
// AuditOutlined: <AuditOutlined />,
// BackwardOutlined: <BackwardOutlined />,
// BaiduOutlined: <BaiduOutlined />,
// BankOutlined: <BankOutlined />,
// BankTwoTone: <BankTwoTone />,
// BarChartOutlined: <BarChartOutlined />,
// BarcodeOutlined: <BarcodeOutlined />,
// BarsOutlined: <BarsOutlined />,
// BehanceCircleOutlined: <BehanceCircleOutlined />,
// BehanceOutlined: <BehanceOutlined />,
// BehanceSquareOutlined: <BehanceSquareOutlined />,
// BellOutlined: <BellOutlined />,
// BellTwoTone: <BellTwoTone />,
// BgColorsOutlined: <BgColorsOutlined />,
// BilibiliOutlined: <BilibiliOutlined />,
// BlockOutlined: <BlockOutlined />,
// BoldOutlined: <BoldOutlined />,
// BookOutlined: <BookOutlined />,
// BookTwoTone: <BookTwoTone />,
// BorderBottomOutlined: <BorderBottomOutlined />,
// BorderHorizontalOutlined: <BorderHorizontalOutlined />,
// BorderInnerOutlined: <BorderInnerOutlined />,
// BorderLeftOutlined: <BorderLeftOutlined />,
// BorderOuterOutlined: <BorderOuterOutlined />,
// BorderOutlined: <BorderOutlined />,
// BorderRightOutlined: <BorderRightOutlined />,
// BorderTopOutlined: <BorderTopOutlined />,
// BorderVerticleOutlined: <BorderVerticleOutlined />,
// BorderlessTableOutlined: <BorderlessTableOutlined />,
// BoxPlotOutlined: <BoxPlotOutlined />,
// BranchesOutlined: <BranchesOutlined />,
// BugOutlined: <BugOutlined />,
// BugTwoTone: <BugTwoTone />,
// BuildOutlined: <BuildOutlined />,
// BuildTwoTone: <BuildTwoTone />,
// BulbOutlined: <BulbOutlined />,
// BulbTwoTone: <BulbTwoTone />,
// CalculatorOutlined: <CalculatorOutlined />,
// CalculatorTwoTone: <CalculatorTwoTone />,
// CalendarOutlined: <CalendarOutlined />,
// CalendarTwoTone: <CalendarTwoTone />,
// CameraOutlined: <CameraOutlined />,
// CameraTwoTone: <CameraTwoTone />,
// CarOutlined: <CarOutlined />,
// CarTwoTone: <CarTwoTone />,
// CaretDownOutlined: <CaretDownOutlined />,
// CaretLeftOutlined: <CaretLeftOutlined />,
// CaretRightOutlined: <CaretRightOutlined />,
// CaretUpOutlined: <CaretUpOutlined />,
// CarryOutOutlined: <CarryOutOutlined />,
// CarryOutTwoTone: <CarryOutTwoTone />,
// CheckOutlined: <CheckOutlined />,
// CheckSquareOutlined: <CheckSquareOutlined />,
// CheckSquareTwoTone: <CheckSquareTwoTone />,
// ChromeOutlined: <ChromeOutlined />,
// CiCircleOutlined: <CiCircleOutlined />,
// CiOutlined: <CiOutlined />,
// CiTwoTone: <CiTwoTone />,
// ClearOutlined: <ClearOutlined />,
// ClockCircleOutlined: <ClockCircleOutlined />,
// ClockCircleTwoTone: <ClockCircleTwoTone />,
// CloseOutlined: <CloseOutlined />,
// CloseSquareOutlined: <CloseSquareOutlined />,
// CloseSquareTwoTone: <CloseSquareTwoTone />,
// CloudDownloadOutlined: <CloudDownloadOutlined />,
// CloudOutlined: <CloudOutlined />,
// CloudServerOutlined: <CloudServerOutlined />,
// CloudSyncOutlined: <CloudSyncOutlined />,
// CloudTwoTone: <CloudTwoTone />,
// CloudUploadOutlined: <CloudUploadOutlined />,
// ClusterOutlined: <ClusterOutlined />,
// CodeOutlined: <CodeOutlined />,
// CodeSandboxCircleOutlined: <CodeSandboxCircleOutlined />,
// CodeSandboxOutlined: <CodeSandboxOutlined />,
// CodeSandboxSquareOutlined: <CodeSandboxSquareOutlined />,
// CodeTwoTone: <CodeTwoTone />,
// CodepenCircleOutlined: <CodepenCircleOutlined />,
// CodepenOutlined: <CodepenOutlined />,
// CodepenSquareOutlined: <CodepenSquareOutlined />,
// CoffeeOutlined: <CoffeeOutlined />,
// ColumnHeightOutlined: <ColumnHeightOutlined />,
// ColumnWidthOutlined: <ColumnWidthOutlined />,
// CommentOutlined: <CommentOutlined />,
// CompassOutlined: <CompassOutlined />,
// CompassTwoTone: <CompassTwoTone />,
// CompressOutlined: <CompressOutlined />,
// ConsoleSqlOutlined: <ConsoleSqlOutlined />,
// ContactsOutlined: <ContactsOutlined />,
// ContactsTwoTone: <ContactsTwoTone />,
// ContainerOutlined: <ContainerOutlined />,
// ContainerTwoTone: <ContainerTwoTone />,
// ControlOutlined: <ControlOutlined />,
// ControlTwoTone: <ControlTwoTone />,
// CopyOutlined: <CopyOutlined />,
// CopyTwoTone: <CopyTwoTone />,
// CopyrightCircleOutlined: <CopyrightCircleOutlined />,
// CopyrightOutlined: <CopyrightOutlined />,
// CopyrightTwoTone: <CopyrightTwoTone />,
// CreditCardOutlined: <CreditCardOutlined />,
// CreditCardTwoTone: <CreditCardTwoTone />,
// CrownOutlined: <CrownOutlined />,
// CrownTwoTone: <CrownTwoTone />,
// CustomerServiceOutlined: <CustomerServiceOutlined />,
// CustomerServiceTwoTone: <CustomerServiceTwoTone />,
// DashOutlined: <DashOutlined />,
// DashboardOutlined: <DashboardOutlined />,
// DashboardTwoTone: <DashboardTwoTone />,
// DatabaseOutlined: <DatabaseOutlined />,
// DatabaseTwoTone: <DatabaseTwoTone />,
// DeleteColumnOutlined: <DeleteColumnOutlined />,
// DeleteOutlined: <DeleteOutlined />,
// DeleteRowOutlined: <DeleteRowOutlined />,
// DeleteTwoTone: <DeleteTwoTone />,
// DeliveredProcedureOutlined: <DeliveredProcedureOutlined />,
// DeploymentUnitOutlined: <DeploymentUnitOutlined />,
// DesktopOutlined: <DesktopOutlined />,
// DiffOutlined: <DiffOutlined />,
// DiffTwoTone: <DiffTwoTone />,
// DingdingOutlined: <DingdingOutlined />,
// DingtalkCircleOutlined: <DingtalkCircleOutlined />,
// DingtalkOutlined: <DingtalkOutlined />,
// DingtalkSquareOutlined: <DingtalkSquareOutlined />,
// DisconnectOutlined: <DisconnectOutlined />,
// DiscordOutlined: <DiscordOutlined />,
// DislikeOutlined: <DislikeOutlined />,
// DislikeTwoTone: <DislikeTwoTone />,
// DockerOutlined: <DockerOutlined />,
// DollarCircleOutlined: <DollarCircleOutlined />,
// DollarCircleTwoTone: <DollarCircleTwoTone />,
// DollarOutlined: <DollarOutlined />,
// DollarTwoTone: <DollarTwoTone />,
// DotChartOutlined: <DotChartOutlined />,
// DotNetOutlined: <DotNetOutlined />,
// DoubleLeftOutlined: <DoubleLeftOutlined />,
// DoubleRightOutlined: <DoubleRightOutlined />,
// DownCircleOutlined: <DownCircleOutlined />,
// DownCircleTwoTone: <DownCircleTwoTone />,
// DownOutlined: <DownOutlined />,
// DownSquareOutlined: <DownSquareOutlined />,
// DownSquareTwoTone: <DownSquareTwoTone />,
// DownloadOutlined: <DownloadOutlined />,
// DragOutlined: <DragOutlined />,
// DribbbleCircleOutlined: <DribbbleCircleOutlined />,
// DribbbleOutlined: <DribbbleOutlined />,
// DribbbleSquareOutlined: <DribbbleSquareOutlined />,
// DropboxCircleOutlined: <DropboxCircleOutlined />,
// DropboxOutlined: <DropboxOutlined />,
// DropboxSquareOutlined: <DropboxSquareOutlined />,
// EditOutlined: <EditOutlined />,
// EditTwoTone: <EditTwoTone />,
// EllipsisOutlined: <EllipsisOutlined />,
// EnterOutlined: <EnterOutlined />,
// EnvironmentOutlined: <EnvironmentOutlined />,
// EnvironmentTwoTone: <EnvironmentTwoTone />,
// EuroCircleOutlined: <EuroCircleOutlined />,
// EuroCircleTwoTone: <EuroCircleTwoTone />,
// EuroOutlined: <EuroOutlined />,
// EuroTwoTone: <EuroTwoTone />,
// ExceptionOutlined: <ExceptionOutlined />,
// ExclamationCircleOutlined: <ExclamationCircleOutlined />,
// ExclamationCircleTwoTone: <ExclamationCircleTwoTone />,
// ExclamationOutlined: <ExclamationOutlined />,
// ExpandAltOutlined: <ExpandAltOutlined />,
// ExpandOutlined: <ExpandOutlined />,
// ExperimentOutlined: <ExperimentOutlined />,
// ExperimentTwoTone: <ExperimentTwoTone />,
// ExportOutlined: <ExportOutlined />,
// EyeInvisibleOutlined: <EyeInvisibleOutlined />,
// EyeInvisibleTwoTone: <EyeInvisibleTwoTone />,
// EyeOutlined: <EyeOutlined />,
// EyeTwoTone: <EyeTwoTone />,
// FacebookOutlined: <FacebookOutlined />,
// FallOutlined: <FallOutlined />,
// FastBackwardOutlined: <FastBackwardOutlined />,
// FastForwardOutlined: <FastForwardOutlined />,
// FieldBinaryOutlined: <FieldBinaryOutlined />,
// FieldNumberOutlined: <FieldNumberOutlined />,
// FieldStringOutlined: <FieldStringOutlined />,
// FieldTimeOutlined: <FieldTimeOutlined />,
// FileAddOutlined: <FileAddOutlined />,
// FileAddTwoTone: <FileAddTwoTone />,
// FileDoneOutlined: <FileDoneOutlined />,
// FileExcelOutlined: <FileExcelOutlined />,
// FileExcelTwoTone: <FileExcelTwoTone />,
// FileExclamationOutlined: <FileExclamationOutlined />,
// FileExclamationTwoTone: <FileExclamationTwoTone />,
// FileGifOutlined: <FileGifOutlined />,
// FileImageOutlined: <FileImageOutlined />,
// FileImageTwoTone: <FileImageTwoTone />,
// FileJpgOutlined: <FileJpgOutlined />,
// FileMarkdownOutlined: <FileMarkdownOutlined />,
// FileMarkdownTwoTone: <FileMarkdownTwoTone />,
// FileOutlined: <FileOutlined />,
// FilePdfOutlined: <FilePdfOutlined />,
// FilePdfTwoTone: <FilePdfTwoTone />,
// FilePptOutlined: <FilePptOutlined />,
// FilePptTwoTone: <FilePptTwoTone />,
// FileProtectOutlined: <FileProtectOutlined />,
// FileSearchOutlined: <FileSearchOutlined />,
// FileSyncOutlined: <FileSyncOutlined />,
// FileTextOutlined: <FileTextOutlined />,
// FileTextTwoTone: <FileTextTwoTone />,
// FileTwoTone: <FileTwoTone />,
// FileUnknownOutlined: <FileUnknownOutlined />,
// FileUnknownTwoTone: <FileUnknownTwoTone />,
// FileWordOutlined: <FileWordOutlined />,
// FileWordTwoTone: <FileWordTwoTone />,
// FileZipOutlined: <FileZipOutlined />,
// FileZipTwoTone: <FileZipTwoTone />,
// FilterOutlined: <FilterOutlined />,
// FilterTwoTone: <FilterTwoTone />,
// FireOutlined: <FireOutlined />,
// FireTwoTone: <FireTwoTone />,
// FlagOutlined: <FlagOutlined />,
// FlagTwoTone: <FlagTwoTone />,
// FolderAddOutlined: <FolderAddOutlined />,
// FolderAddTwoTone: <FolderAddTwoTone />,
// FolderOpenOutlined: <FolderOpenOutlined />,
// FolderOpenTwoTone: <FolderOpenTwoTone />,
// FolderOutlined: <FolderOutlined />,
// FolderTwoTone: <FolderTwoTone />,
// FolderViewOutlined: <FolderViewOutlined />,
// FontColorsOutlined: <FontColorsOutlined />,
// FontSizeOutlined: <FontSizeOutlined />,
// ForkOutlined: <ForkOutlined />,
// FormOutlined: <FormOutlined />,
// FormatPainterOutlined: <FormatPainterOutlined />,
// ForwardOutlined: <ForwardOutlined />,
// FrownOutlined: <FrownOutlined />,
// FrownTwoTone: <FrownTwoTone />,
// FullscreenExitOutlined: <FullscreenExitOutlined />,
// FullscreenOutlined: <FullscreenOutlined />,
// FunctionOutlined: <FunctionOutlined />,
// FundOutlined: <FundOutlined />,
// FundProjectionScreenOutlined: <FundProjectionScreenOutlined />,
// FundTwoTone: <FundTwoTone />,
// FundViewOutlined: <FundViewOutlined />,
// FunnelPlotOutlined: <FunnelPlotOutlined />,
// FunnelPlotTwoTone: <FunnelPlotTwoTone />,
// GatewayOutlined: <GatewayOutlined />,
// GifOutlined: <GifOutlined />,
// GiftOutlined: <GiftOutlined />,
// GiftTwoTone: <GiftTwoTone />,
// GithubOutlined: <GithubOutlined />,
// GitlabOutlined: <GitlabOutlined />,
// GlobalOutlined: <GlobalOutlined />,
// GoldOutlined: <GoldOutlined />,
// GoldTwoTone: <GoldTwoTone />,
// GoldenOutlined: <GoldenOutlined />,
// GoogleCircleOutlined: <GoogleCircleOutlined />,
// GoogleOutlined: <GoogleOutlined />,
// GooglePlusCircleOutlined: <GooglePlusCircleOutlined />,
// GooglePlusOutlined: <GooglePlusOutlined />,
// GooglePlusSquareOutlined: <GooglePlusSquareOutlined />,
// GoogleSquareOutlined: <GoogleSquareOutlined />,
// GroupOutlined: <GroupOutlined />,
// HarmonyOSOutlined: <HarmonyOSOutlined />,
// HddOutlined: <HddOutlined />,
// HddTwoTone: <HddTwoTone />,
// HeartOutlined: <HeartOutlined />,
// HeartTwoTone: <HeartTwoTone />,
// HeatMapOutlined: <HeatMapOutlined />,
// HighlightOutlined: <HighlightOutlined />,
// HighlightTwoTone: <HighlightTwoTone />,
// HistoryOutlined: <HistoryOutlined />,
// HolderOutlined: <HolderOutlined />,
// HomeOutlined: <HomeOutlined />,
// HomeTwoTone: <HomeTwoTone />,
// HourglassOutlined: <HourglassOutlined />,
// HourglassTwoTone: <HourglassTwoTone />,
// Html5Outlined: <Html5Outlined />,
// Html5TwoTone: <Html5TwoTone />,
// IdcardOutlined: <IdcardOutlined />,
// IdcardTwoTone: <IdcardTwoTone />,
// IeCircleOutlined: <IeCircleOutlined />,
// IeOutlined: <IeOutlined />,
// IeSquareOutlined: <IeSquareOutlined />,
// ImportOutlined: <ImportOutlined />,
// InboxOutlined: <InboxOutlined />,
// InfoCircleOutlined: <InfoCircleOutlined />,
// InfoCircleTwoTone: <InfoCircleTwoTone />,
// InfoOutlined: <InfoOutlined />,
// InsertRowAboveOutlined: <InsertRowAboveOutlined />,
// InsertRowBelowOutlined: <InsertRowBelowOutlined />,
// InsertRowLeftOutlined: <InsertRowLeftOutlined />,
// InsertRowRightOutlined: <InsertRowRightOutlined />,
// InstagramOutlined: <InstagramOutlined />,
// InsuranceOutlined: <InsuranceOutlined />,
// InsuranceTwoTone: <InsuranceTwoTone />,
// InteractionOutlined: <InteractionOutlined />,
// InteractionTwoTone: <InteractionTwoTone />,
// IssuesCloseOutlined: <IssuesCloseOutlined />,
// ItalicOutlined: <ItalicOutlined />,
// JavaOutlined: <JavaOutlined />,
// JavaScriptOutlined: <JavaScriptOutlined />,
// KeyOutlined: <KeyOutlined />,
// KubernetesOutlined: <KubernetesOutlined />,
// LaptopOutlined: <LaptopOutlined />,
// LayoutOutlined: <LayoutOutlined />,
// LayoutTwoTone: <LayoutTwoTone />,
// LeftCircleOutlined: <LeftCircleOutlined />,
// LeftCircleTwoTone: <LeftCircleTwoTone />,
// LeftOutlined: <LeftOutlined />,
// LeftSquareOutlined: <LeftSquareOutlined />,
// LeftSquareTwoTone: <LeftSquareTwoTone />,
// LikeOutlined: <LikeOutlined />,
// LikeTwoTone: <LikeTwoTone />,
// LineChartOutlined: <LineChartOutlined />,
// LineHeightOutlined: <LineHeightOutlined />,
// LineOutlined: <LineOutlined />,
// LinkOutlined: <LinkOutlined />,
// LinkedinOutlined: <LinkedinOutlined />,
// LinuxOutlined: <LinuxOutlined />,
// Loading3QuartersOutlined: <Loading3QuartersOutlined />,
// LoadingOutlined: <LoadingOutlined />,
// LockOutlined: <LockOutlined />,
// LockTwoTone: <LockTwoTone />,
// LoginOutlined: <LoginOutlined />,
// LogoutOutlined: <LogoutOutlined />,
// MacCommandOutlined: <MacCommandOutlined />,
// MailOutlined: <MailOutlined />,
// MailTwoTone: <MailTwoTone />,
// ManOutlined: <ManOutlined />,
// MedicineBoxOutlined: <MedicineBoxOutlined />,
// MedicineBoxTwoTone: <MedicineBoxTwoTone />,
// MediumCircleOutlined: <MediumCircleOutlined />,
// MediumOutlined: <MediumOutlined />,
// MediumSquareOutlined: <MediumSquareOutlined />,
// MediumWorkmarkOutlined: <MediumWorkmarkOutlined />,
// MehOutlined: <MehOutlined />,
// MehTwoTone: <MehTwoTone />,
// MenuFoldOutlined: <MenuFoldOutlined />,
// MenuOutlined: <MenuOutlined />,
// MenuUnfoldOutlined: <MenuUnfoldOutlined />,
// MergeCellsOutlined: <MergeCellsOutlined />,
// MergeOutlined: <MergeOutlined />,
// MessageOutlined: <MessageOutlined />,
// MessageTwoTone: <MessageTwoTone />,
// MinusCircleOutlined: <MinusCircleOutlined />,
// MinusCircleTwoTone: <MinusCircleTwoTone />,
// MinusOutlined: <MinusOutlined />,
// MinusSquareOutlined: <MinusSquareOutlined />,
// MinusSquareTwoTone: <MinusSquareTwoTone />,
// MobileOutlined: <MobileOutlined />,
// MobileTwoTone: <MobileTwoTone />,
// MoneyCollectOutlined: <MoneyCollectOutlined />,
// MoneyCollectTwoTone: <MoneyCollectTwoTone />,
// MonitorOutlined: <MonitorOutlined />,
// MoonOutlined: <MoonOutlined />,
// MoreOutlined: <MoreOutlined />,
// MutedOutlined: <MutedOutlined />,
// NodeCollapseOutlined: <NodeCollapseOutlined />,
// NodeExpandOutlined: <NodeExpandOutlined />,
// NodeIndexOutlined: <NodeIndexOutlined />,
// NotificationOutlined: <NotificationOutlined />,
// NotificationTwoTone: <NotificationTwoTone />,
// NumberOutlined: <NumberOutlined />,
// OneToOneOutlined: <OneToOneOutlined />,
// OpenAIOutlined: <OpenAIOutlined />,
// OrderedListOutlined: <OrderedListOutlined />,
// PaperClipOutlined: <PaperClipOutlined />,
// PartitionOutlined: <PartitionOutlined />,
// PauseCircleOutlined: <PauseCircleOutlined />,
// PauseCircleTwoTone: <PauseCircleTwoTone />,
// PauseOutlined: <PauseOutlined />,
// PayCircleOutlined: <PayCircleOutlined />,
// PercentageOutlined: <PercentageOutlined />,
// PhoneOutlined: <PhoneOutlined />,
// PhoneTwoTone: <PhoneTwoTone />,
// PicCenterOutlined: <PicCenterOutlined />,
// PicLeftOutlined: <PicLeftOutlined />,
// PicRightOutlined: <PicRightOutlined />,
// PictureOutlined: <PictureOutlined />,
// PictureTwoTone: <PictureTwoTone />,
// PieChartOutlined: <PieChartOutlined />,
// PieChartTwoTone: <PieChartTwoTone />,
// PinterestOutlined: <PinterestOutlined />,
// PlayCircleOutlined: <PlayCircleOutlined />,
// PlayCircleTwoTone: <PlayCircleTwoTone />,
// PlaySquareOutlined: <PlaySquareOutlined />,
// PlaySquareTwoTone: <PlaySquareTwoTone />,
// PlusCircleOutlined: <PlusCircleOutlined />,
// PlusCircleTwoTone: <PlusCircleTwoTone />,
// PlusOutlined: <PlusOutlined />,
// PlusSquareOutlined: <PlusSquareOutlined />,
// PlusSquareTwoTone: <PlusSquareTwoTone />,
// PoundCircleOutlined: <PoundCircleOutlined />,
// PoundCircleTwoTone: <PoundCircleTwoTone />,
// PoundOutlined: <PoundOutlined />,
// PoweroffOutlined: <PoweroffOutlined />,
// PrinterOutlined: <PrinterOutlined />,
// PrinterTwoTone: <PrinterTwoTone />,
// ProductOutlined: <ProductOutlined />,
// ProfileOutlined: <ProfileOutlined />,
// ProfileTwoTone: <ProfileTwoTone />,
// ProjectOutlined: <ProjectOutlined />,
// ProjectTwoTone: <ProjectTwoTone />,
// PropertySafetyOutlined: <PropertySafetyOutlined />,
// PropertySafetyTwoTone: <PropertySafetyTwoTone />,
// PullRequestOutlined: <PullRequestOutlined />,
// PushpinOutlined: <PushpinOutlined />,
// PushpinTwoTone: <PushpinTwoTone />,
// PythonOutlined: <PythonOutlined />,
// QqCircleOutlined: <QqCircleOutlined />,
// QqOutlined: <QqOutlined />,
// QqSquareOutlined: <QqSquareOutlined />,
// QrcodeOutlined: <QrcodeOutlined />,
// QuestionCircleOutlined: <QuestionCircleOutlined />,
// QuestionCircleTwoTone: <QuestionCircleTwoTone />,
// QuestionOutlined: <QuestionOutlined />,
// RadarChartOutlined: <RadarChartOutlined />,
// RadiusBottomleftOutlined: <RadiusBottomleftOutlined />,
// RadiusBottomrightOutlined: <RadiusBottomrightOutlined />,
// RadiusSettingOutlined: <RadiusSettingOutlined />,
// RadiusUpleftOutlined: <RadiusUpleftOutlined />,
// RadiusUprightOutlined: <RadiusUprightOutlined />,
// ReadOutlined: <ReadOutlined />,
// ReconciliationOutlined: <ReconciliationOutlined />,
// ReconciliationTwoTone: <ReconciliationTwoTone />,
// RedEnvelopeOutlined: <RedEnvelopeOutlined />,
// RedEnvelopeTwoTone: <RedEnvelopeTwoTone />,
// RedditCircleOutlined: <RedditCircleOutlined />,
// RedditOutlined: <RedditOutlined />,
// RedditSquareOutlined: <RedditSquareOutlined />,
// RedoOutlined: <RedoOutlined />,
// ReloadOutlined: <ReloadOutlined />,
// RestOutlined: <RestOutlined />,
// RestTwoTone: <RestTwoTone />,
// RetweetOutlined: <RetweetOutlined />,
// RightCircleOutlined: <RightCircleOutlined />,
// RightCircleTwoTone: <RightCircleTwoTone />,
// RightOutlined: <RightOutlined />,
// RightSquareOutlined: <RightSquareOutlined />,
// RightSquareTwoTone: <RightSquareTwoTone />,
// RiseOutlined: <RiseOutlined />,
// RobotOutlined: <RobotOutlined />,
// RocketOutlined: <RocketOutlined />,
// RocketTwoTone: <RocketTwoTone />,
// RollbackOutlined: <RollbackOutlined />,
// RotateLeftOutlined: <RotateLeftOutlined />,
// RotateRightOutlined: <RotateRightOutlined />,
// RubyOutlined: <RubyOutlined />,
// SafetyCertificateOutlined: <SafetyCertificateOutlined />,
// SafetyCertificateTwoTone: <SafetyCertificateTwoTone />,
// SafetyOutlined: <SafetyOutlined />,
// SaveOutlined: <SaveOutlined />,
// SaveTwoTone: <SaveTwoTone />,
// ScanOutlined: <ScanOutlined />,
// ScheduleOutlined: <ScheduleOutlined />,
// ScheduleTwoTone: <ScheduleTwoTone />,
// ScissorOutlined: <ScissorOutlined />,
// SearchOutlined: <SearchOutlined />,
// SecurityScanOutlined: <SecurityScanOutlined />,
// SecurityScanTwoTone: <SecurityScanTwoTone />,
// SelectOutlined: <SelectOutlined />,
// SendOutlined: <SendOutlined />,
// SettingOutlined: <SettingOutlined />,
// SettingTwoTone: <SettingTwoTone />,
// ShakeOutlined: <ShakeOutlined />,
// ShareAltOutlined: <ShareAltOutlined />,
// ShopOutlined: <ShopOutlined />,
// ShopTwoTone: <ShopTwoTone />,
// ShoppingCartOutlined: <ShoppingCartOutlined />,
// ShoppingOutlined: <ShoppingOutlined />,
// ShoppingTwoTone: <ShoppingTwoTone />,
// ShrinkOutlined: <ShrinkOutlined />,
// SignalOutlined: <SignalOutlined />,
// SignatureOutlined: <SignatureOutlined />,
// SisternodeOutlined: <SisternodeOutlined />,
// SketchCircleOutlined: <SketchCircleOutlined />,
// SketchOutlined: <SketchOutlined />,
// SketchSquareOutlined: <SketchSquareOutlined />,
// SkinOutlined: <SkinOutlined />,
// SkinTwoTone: <SkinTwoTone />,
// SkypeOutlined: <SkypeOutlined />,
// SlackCircleOutlined: <SlackCircleOutlined />,
// SlackOutlined: <SlackOutlined />,
// SlackSquareOutlined: <SlackSquareOutlined />,
// SlidersOutlined: <SlidersOutlined />,
// SlidersTwoTone: <SlidersTwoTone />,
// SmallDashOutlined: <SmallDashOutlined />,
// SmileOutlined: <SmileOutlined />,
// SmileTwoTone: <SmileTwoTone />,
// SnippetsOutlined: <SnippetsOutlined />,
// SnippetsTwoTone: <SnippetsTwoTone />,
// SolutionOutlined: <SolutionOutlined />,
// SortAscendingOutlined: <SortAscendingOutlined />,
// SortDescendingOutlined: <SortDescendingOutlined />,
// SoundOutlined: <SoundOutlined />,
// SoundTwoTone: <SoundTwoTone />,
// SplitCellsOutlined: <SplitCellsOutlined />,
// SpotifyOutlined: <SpotifyOutlined />,
// StarOutlined: <StarOutlined />,
// StarTwoTone: <StarTwoTone />,
// StepBackwardOutlined: <StepBackwardOutlined />,
// StepForwardOutlined: <StepForwardOutlined />,
// StockOutlined: <StockOutlined />,
// StopOutlined: <StopOutlined />,
// StopTwoTone: <StopTwoTone />,
// StrikethroughOutlined: <StrikethroughOutlined />,
// SubnodeOutlined: <SubnodeOutlined />,
// SunOutlined: <SunOutlined />,
// SwapLeftOutlined: <SwapLeftOutlined />,
// SwapOutlined: <SwapOutlined />,
// SwapRightOutlined: <SwapRightOutlined />,
// SwitcherOutlined: <SwitcherOutlined />,
// SwitcherTwoTone: <SwitcherTwoTone />,
// SyncOutlined: <SyncOutlined />,
// TableOutlined: <TableOutlined />,
// TabletOutlined: <TabletOutlined />,
// TabletTwoTone: <TabletTwoTone />,
// TagOutlined: <TagOutlined />,
// TagTwoTone: <TagTwoTone />,
// TagsOutlined: <TagsOutlined />,
// TagsTwoTone: <TagsTwoTone />,
// TaobaoCircleOutlined: <TaobaoCircleOutlined />,
// TaobaoOutlined: <TaobaoOutlined />,
// TaobaoSquareOutlined: <TaobaoSquareOutlined />,
// TeamOutlined: <TeamOutlined />,
// ThunderboltOutlined: <ThunderboltOutlined />,
// ThunderboltTwoTone: <ThunderboltTwoTone />,
// TikTokOutlined: <TikTokOutlined />,
// ToTopOutlined: <ToTopOutlined />,
// ToolOutlined: <ToolOutlined />,
// ToolTwoTone: <ToolTwoTone />,
// TrademarkCircleOutlined: <TrademarkCircleOutlined />,
// TrademarkCircleTwoTone: <TrademarkCircleTwoTone />,
// TrademarkOutlined: <TrademarkOutlined />,
// TransactionOutlined: <TransactionOutlined />,
// TranslationOutlined: <TranslationOutlined />,
// TrophyOutlined: <TrophyOutlined />,
// TrophyTwoTone: <TrophyTwoTone />,
// TruckOutlined: <TruckOutlined />,
// TwitchOutlined: <TwitchOutlined />,
// TwitterCircleOutlined: <TwitterCircleOutlined />,
// TwitterOutlined: <TwitterOutlined />,
// TwitterSquareOutlined: <TwitterSquareOutlined />,
// UnderlineOutlined: <UnderlineOutlined />,
// UndoOutlined: <UndoOutlined />,
// UngroupOutlined: <UngroupOutlined />,
// UnlockOutlined: <UnlockOutlined />,
// UnlockTwoTone: <UnlockTwoTone />,
// UnorderedListOutlined: <UnorderedListOutlined />,
// UpCircleOutlined: <UpCircleOutlined />,
// UpCircleTwoTone: <UpCircleTwoTone />,
// UpOutlined: <UpOutlined />,
// UpSquareOutlined: <UpSquareOutlined />,
// UpSquareTwoTone: <UpSquareTwoTone />,
// UploadOutlined: <UploadOutlined />,
// UsbOutlined: <UsbOutlined />,
// UsbTwoTone: <UsbTwoTone />,
// UserAddOutlined: <UserAddOutlined />,
// UserDeleteOutlined: <UserDeleteOutlined />,
// UserOutlined: <UserOutlined />,
// UserSwitchOutlined: <UserSwitchOutlined />,
// UsergroupAddOutlined: <UsergroupAddOutlined />,
// UsergroupDeleteOutlined: <UsergroupDeleteOutlined />,
// VerifiedOutlined: <VerifiedOutlined />,
// VerticalAlignBottomOutlined: <VerticalAlignBottomOutlined />,
// VerticalAlignMiddleOutlined: <VerticalAlignMiddleOutlined />,
// VerticalAlignTopOutlined: <VerticalAlignTopOutlined />,
// VerticalLeftOutlined: <VerticalLeftOutlined />,
// VerticalRightOutlined: <VerticalRightOutlined />,
// VideoCameraAddOutlined: <VideoCameraAddOutlined />,
// VideoCameraOutlined: <VideoCameraOutlined />,
// VideoCameraTwoTone: <VideoCameraTwoTone />,
// WalletOutlined: <WalletOutlined />,
// WalletTwoTone: <WalletTwoTone />,
// WarningOutlined: <WarningOutlined />,
// WarningTwoTone: <WarningTwoTone />,
// WechatOutlined: <WechatOutlined />,
// WechatWorkOutlined: <WechatWorkOutlined />,
// WeiboCircleOutlined: <WeiboCircleOutlined />,
// WeiboOutlined: <WeiboOutlined />,
// WeiboSquareOutlined: <WeiboSquareOutlined />,
// WhatsAppOutlined: <WhatsAppOutlined />,
// WifiOutlined: <WifiOutlined />,
// WindowsOutlined: <WindowsOutlined />,
// WomanOutlined: <WomanOutlined />,
// XOutlined: <XOutlined />,
// YahooOutlined: <YahooOutlined />,
// YoutubeOutlined: <YoutubeOutlined />,
// YuqueOutlined: <YuqueOutlined />,
// ZhihuCircleOutlined: <ZhihuCircleOutlined />,
// ZhihuOutlined: <ZhihuOutlined />,
// ZhihuSquareOutlined: <ZhihuSquareOutlined />,
// ZoomInOutlined: <ZoomInOutlined />,
// ZoomOutOutlined: <ZoomOutOutlined />
// };
function SystemConstructLayout({ children }: { children: React.ReactNode }) {
const data = localStorage.getItem(USER_MENU);
const parsed = data ? JSON.parse(data).menus.filter((item: any) => item.component_name == 'system')[0].children : [];
console.log('parsed', parsed);
const items = [
//菜单
{
key: 'menu',
name: t('menu_management'),
name: t('menu'),
path: '/menu',
icon: <MenuFoldOutlined />,
},
//角色
{
key: 'role',
name: t('role_management'),
name: t('role'),
icon: <UsergroupDeleteOutlined />,
path: '/role',
},
//机构
{
key: 'organization',
name: t('institutional_framework'),
name: t('institutional'),
path: '/organization',
icon: <IconFont type='icon-company' />,
},
//用户
{
key: 'user',
name: t('user_information_management'),
name: t('user'),
icon: <UserOutlined />,
path: '/user',
},
@ -76,26 +706,19 @@ function SystemConstructLayout({ children }: { children: React.ReactNode }) {
size='small'
indicator={{ size: origin => origin - 60, align: 'center' }}
activeKey={activeKey}
items={items.map(items => {
items={parsed.map((item: any) => {
return {
key: items.key,
label: items.name,
key: item.component_name,
label: t(item.component_name),
children: children,
icon: items.icon,
icon: item.component_name === 'organization'
? <IconFont type="icon-company" />
: renderIcon(item.icon)
};
})}
onTabClick={key => {
router.push(`/system/${key}`);
}}
// tabBarExtraContent={
// <Button
// className='border-none text-white bg-button-gradient h-full flex items-center'
// icon={<PlusOutlined className='text-base' />}
// // onClick={handleCreate}
// >
// {t('create_app')}
// </Button>
// }
/>
</ConfigProvider>
</div>

@ -1,6 +1,6 @@
import { UserInfoResponse } from '@/types/userinfo';
import { STORAGE_USERINFO_KEY } from '@/utils/constants/index';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { STORAGE_USERINFO_KEY,USER_MENU } from '@/utils/constants/index';
import { ExclamationCircleOutlined ,LogoutOutlined} from '@ant-design/icons';
import { Avatar, Modal } from 'antd';
import cls from 'classnames';
import { useEffect, useState } from 'react';
@ -34,6 +34,7 @@ function UserBar({ onlyAvatar = false }) {
// TODO: delete unused function
const logout = () => {
localStorage.removeItem(STORAGE_USERINFO_KEY);
localStorage.removeItem(USER_MENU);
window.location.href = `/login`;
};
@ -63,7 +64,7 @@ function UserBar({ onlyAvatar = false }) {
hidden: onlyAvatar,
})}
>
退
<LogoutOutlined />
</div>
</div>
</div>

@ -32,7 +32,7 @@ const nextConfig = {
],
webpack: (config, { isServer }) => {
config.resolve.fallback = { fs: false };
if (!isServer) {
if (!isServer&& process.env.NODE_ENV === 'production') {
config.plugins.push(
new CopyPlugin({
patterns: [
@ -51,6 +51,7 @@ const nextConfig = {
new MonacoWebpackPlugin({
// 你可以在这里配置插件的选项,例如:
languages: ["sql"],
features: [],
filename: "static/[name].worker.js",
})
);
@ -68,14 +69,19 @@ const nextConfig = {
},
};
const withTM = require("next-transpile-modules")([
"@berryv/g2-react",
"@antv/g2",
"react-syntax-highlighter",
"@antv/g6",
"@antv/graphin",
"@antv/gpt-vis",
]);
const dev = process.env.NODE_ENV === 'development';
const withTM = require("next-transpile-modules")(
dev
? [] // 开发时不转译任何模块(可测试性能)
: [
"@berryv/g2-react",
"@antv/g2",
"react-syntax-highlighter",
"@antv/g6",
"@antv/graphin",
"@antv/gpt-vis",
]
);
module.exports = withTM({
...nextConfig,

@ -7,9 +7,11 @@ import zhCN from 'antd/locale/zh_CN';
import classNames from 'classnames';
import type { AppProps } from 'next/app';
import Head from 'next/head';
import { useRequest } from 'ahooks';
import { useRouter } from 'next/router';
import React, { useContext, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { getUserMenu } from '@/client/api';
import '../app/i18n';
import '../nprogress.css';
import '../styles/globals.css';
@ -111,9 +113,33 @@ function LayoutWrapper({ children }: { children: React.ReactNode }) {
// setIsLogin(true);
// }
};
const { pathname } = useRouter();
const { run: getUserMenuFun } = useRequest(
async () => {
if(pathname.startsWith('/login')) return;
return await getUserMenu(); // 直接传递表单对象
},
{
manual: false,
onSuccess: (res) => {
if(!res) return;
const { data } = res;
console.log('getUserMenu', data);
localStorage.setItem('menu', JSON.stringify(data.data));
// localStorage.setItem('menu', JSON.stringify(menuRes.data.data));
// console.log('menuRes', menuRes);
},
onError: (error) => {
console.log('error', error);
},
debounceWait: 500,
},
);
// 在组件挂载时调用handleAuth函数
useEffect(() => {
handleAuth();
getUserMenuFun();
}, [router.pathname]);
if (!authChecked) {
// 增加加载状态

@ -1,5 +1,5 @@
// login.tsx 最简测试版本
import { doLogin } from '@/client/api';
import { doLogin, getUserMenu } from '@/client/api';
import { STORAGE_USERINFO_KEY, STORAGE_USERINFO_VALID_TIME_KEY } from '@/utils/constants/index';
import { useRequest } from 'ahooks';
import { Button, Form, Image, Input, Spin, notification } from 'antd';
@ -15,8 +15,7 @@ const LoginPage: FC = () => {
},
{
manual: true,
onSuccess: (res, params) => {
console.log('doLoginFn', res.data);
onSuccess: async (res, params) => {
const [formData] = params;
if (!res.data.success) {
notification.error({
@ -24,14 +23,28 @@ const LoginPage: FC = () => {
});
return;
}
const user = {
token: res.data.data,
user_channel: `CJY`,
user_no: `001`,
nick_name: formData.username,
};
localStorage.setItem(STORAGE_USERINFO_KEY, JSON.stringify(user));
localStorage.setItem(STORAGE_USERINFO_VALID_TIME_KEY, Date.now().toString());
// 获取用户菜单
const menuRes = await getUserMenu();
if (!menuRes.data.success) {
notification.error({
message: '获取菜单失败',
});
return;
}
localStorage.setItem('menu', JSON.stringify(menuRes.data.data));
router.push('/');
},
debounceWait: 500,

@ -1,29 +1,4 @@
/* 在全局 CSS 文件中 */
/* 选择 Ant Design Tabs 组件的 tab 栏 */
.ant-tabs-nav {
@apply bg-transparent bg-opacity-0 backdrop-filter backdrop-blur-sm dark:border-[#6f7f95] dark:bg-opacity-60 h-11 px-6 absolute w-full top-0 z-50 !important;
}
.ant-tabs-nav::before {
@apply dark:border-[#6f7f95] border-0 !important;
}
.ant-tabs-tab {
@apply dark:text-slate-400;
}
/* 选择被激活的 Ant Design Tabs 组件的 tab */
.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn {
@apply dark:text-white;
}
.ant-tabs-ink-bar {
@apply dark:bg-white;
}
:where(.css-dev-only-do-not-override-1cxt1bj).ant-tabs .ant-tabs-tab-btn .ant-tabs-tab-icon:not(:last-child) {
@apply mr-1
}
.ant-segmented {
background-color: transparent !important;

@ -27,6 +27,7 @@ import {
import type { ColumnsType } from 'antd/es/table';
import { TFunction } from 'i18next';
import { useEffect, useState } from 'react';
import IconSelect from '@/components/IconSelect/IconSelect';
import { useTranslation } from 'react-i18next';
import styles from './index.module.scss';
import cls from 'classnames';
@ -116,7 +117,11 @@ const Prompt = () => {
},
];
};
const [selectedIcon, setSelectedIcon] = useState<string>('');
const handleIconChange = (value: string) => {
console.log('选中的图标:', value);
setSelectedIcon(value);
};
// 仅用于递归构建子节点,不添加“根目录”
const buildMenuTreeOptionsRecursive = (list: Menu[]): any[] => {
return list.map(item => ({
@ -332,7 +337,13 @@ const Prompt = () => {
<Form.Item label='排序' name='sort' rules={[{ required: true }]}>
<InputNumber min={0} style={{ width: '100%' }} />
</Form.Item>
<Form.Item
label="菜单图标"
name="icon"
rules={[{ message: '请选择图标' }]}
>
<IconSelect value={selectedIcon} onChange={handleIconChange} />
</Form.Item>
<Form.Item label='父级菜单' name='parent_id' rules={[{ required: true }]}>
<TreeSelect treeData={buildMenuTreeOptions(menuList)} placeholder='请选择上级菜单' treeDefaultExpandAll />
</Form.Item>
@ -413,7 +424,7 @@ const Prompt = () => {
</Modal>
<Table
className={cls('m-table')}
className={cls('m-table')}
columns={getMenuColumns(t)}
dataSource={menuList}
rowKey='id'

@ -103,6 +103,9 @@ const Prompt = () => {
dataIndex: 'status',
// width: '10%',
key: 'status',
render: (status: number) => (
<span >{status == 1? '关闭' : '开启'}</span>
),
},
{
title: t('create_time'),

Before

Width:  |  Height:  |  Size: 970 B

After

Width:  |  Height:  |  Size: 970 B

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -8,6 +8,7 @@ export const STORAGE_INIT_MESSAGE_KET = '__db_gpt_im_key';
export const STORAGE_TOKEN_KET = '__db_gpt_tk_key';
/** UserInfo */
export const STORAGE_USERINFO_KEY = '__db_gpt_uinfo_key';
export const USER_MENU = 'menu';
/** UserInfoValidTime */
export const STORAGE_USERINFO_VALID_TIME_KEY = '__db_gpt_uinfo_vt_key';

Loading…
Cancel
Save