feat(web): components update
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-02 15:25:31
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-04 13:49:16
|
||||
* @Last Modified time: 2026-02-24 11:32:15
|
||||
*/
|
||||
/**
|
||||
* SiderMenu Component
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
import { useState, useEffect, type FC } from 'react';
|
||||
import { Menu as AntMenu, Layout } from 'antd';
|
||||
import { Menu as AntMenu, Layout, Flex } from 'antd';
|
||||
import { UserOutlined } from '@ant-design/icons';
|
||||
import type { MenuProps } from 'antd';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
@@ -29,47 +29,49 @@ import clsx from 'clsx';
|
||||
import { useMenu, type MenuItem } from '@/store/menu';
|
||||
import styles from './index.module.css'
|
||||
import logo from '@/assets/images/logo.png'
|
||||
import menuFold from '@/assets/images/menuFold.png'
|
||||
import menuUnfold from '@/assets/images/menuUnfold.png'
|
||||
import { useUser } from '@/store/user';
|
||||
import logout from '@/assets/images/logout.svg'
|
||||
|
||||
// Import SVG files
|
||||
import dashboardIcon from '@/assets/images/menu/dashboard.svg';
|
||||
import dashboardActiveIcon from '@/assets/images/menu/dashboard_active.svg';
|
||||
import dashboardIcon from '@/assets/images/menuNew/dashboard.svg';
|
||||
import dashboardActiveIcon from '@/assets/images/menuNew/dashboard_active.svg';
|
||||
|
||||
import modelIcon from '@/assets/images/menu/model.svg';
|
||||
import modelActiveIcon from '@/assets/images/menu/model_active.svg';
|
||||
import memoryIcon from '@/assets/images/menu/memory.svg';
|
||||
import memoryActiveIcon from '@/assets/images/menu/memory_active.svg';
|
||||
import spaceIcon from '@/assets/images/menu/space.svg';
|
||||
import spaceActiveIcon from '@/assets/images/menu/space_active.svg';
|
||||
import userIcon from '@/assets/images/menu/user.svg';
|
||||
import userActiveIcon from '@/assets/images/menu/user_active.svg';
|
||||
import userMemoryIcon from '@/assets/images/menu/userMemory.svg';
|
||||
import userMemoryActiveIcon from '@/assets/images/menu/userMemory_active.svg';
|
||||
import applicationIcon from '@/assets/images/menu/application.svg';
|
||||
import applicationActiveIcon from '@/assets/images/menu/application_active.svg';
|
||||
import knowledgeIcon from '@/assets/images/menu/knowledge.svg';
|
||||
import knowledgeActiveIcon from '@/assets/images/menu/knowledge_active.svg';
|
||||
import memoryConversationIcon from '@/assets/images/menu/memoryConversation.svg';
|
||||
import memoryConversationActiveIcon from '@/assets/images/menu/memoryConversation_active.svg';
|
||||
import memberIcon from '@/assets/images/menu/member.svg';
|
||||
import memberActiveIcon from '@/assets/images/menu/member_active.svg';
|
||||
|
||||
import applicationIcon from '@/assets/images/menuNew/application.svg';
|
||||
import applicationActiveIcon from '@/assets/images/menuNew/application_active.svg';
|
||||
import knowledgeIcon from '@/assets/images/menuNew/knowledge.svg';
|
||||
import knowledgeActiveIcon from '@/assets/images/menuNew/knowledge_active.svg';
|
||||
import memoryIcon from '@/assets/images/menuNew/memory.svg';
|
||||
import memoryActiveIcon from '@/assets/images/menuNew/memory_active.svg';
|
||||
import userMemoryIcon from '@/assets/images/menuNew/userMemory.svg';
|
||||
import userMemoryActiveIcon from '@/assets/images/menuNew/userMemory_active.svg';
|
||||
import memoryConversationIcon from '@/assets/images/menuNew/memoryConversation.svg';
|
||||
import memoryConversationActiveIcon from '@/assets/images/menuNew/memoryConversation_active.svg';
|
||||
import apiKeyIcon from '@/assets/images/menuNew/apiKey.svg';
|
||||
import apiKeyActiveIcon from '@/assets/images/menuNew/apiKey_active.svg';
|
||||
import memberIcon from '@/assets/images/menuNew/member.svg';
|
||||
import memberActiveIcon from '@/assets/images/menuNew/member_active.svg';
|
||||
import ontologyIcon from '@/assets/images/menuNew/ontology.svg'
|
||||
import ontologyActiveIcon from '@/assets/images/menuNew/ontology_active.svg'
|
||||
|
||||
import spaceConfigIcon from '@/assets/images/menuNew/spaceConfig.svg'
|
||||
import spaceConfigActiveIcon from '@/assets/images/menuNew/spaceConfig_active.svg'
|
||||
import promptIcon from '@/assets/images/menuNew/prompt.svg'
|
||||
import promptActiveIcon from '@/assets/images/menuNew/prompt_active.svg'
|
||||
|
||||
import toolIcon from '@/assets/images/menu/tool.png';
|
||||
import toolActiveIcon from '@/assets/images/menu/tool_active.png';
|
||||
import apiKeyIcon from '@/assets/images/menu/apiKey.png';
|
||||
import apiKeyActiveIcon from '@/assets/images/menu/apiKey_active.png';
|
||||
import pricingIcon from '@/assets/images/menu/pricing.svg'
|
||||
import pricingActiveIcon from '@/assets/images/menu/pricing_active.svg'
|
||||
import spaceConfigIcon from '@/assets/images/menu/spaceConfig.svg'
|
||||
import spaceConfigActiveIcon from '@/assets/images/menu/spaceConfig_active.svg'
|
||||
import ontologyIcon from '@/assets/images/menu/ontology.svg'
|
||||
import ontologyActiveIcon from '@/assets/images/menu/ontology_active.svg'
|
||||
import promptIcon from '@/assets/images/menu/prompt.svg'
|
||||
import promptActiveIcon from '@/assets/images/menu/prompt_active.svg'
|
||||
import skillsIcon from '@/assets/images/menu/skills.svg'
|
||||
import skillsActiveIcon from '@/assets/images/menu/skills_active.svg'
|
||||
|
||||
|
||||
/** Icon path mapping table for menu items (normal and active states) */
|
||||
const iconPathMap: Record<string, string> = {
|
||||
'dashboard': dashboardIcon,
|
||||
@@ -145,43 +147,57 @@ const Menu: FC<{
|
||||
|
||||
/** Convert custom menu format to Ant Design Menu items format */
|
||||
const generateMenuItems = (menuList: MenuItem[]): MenuProps['items'] => {
|
||||
const items: MenuProps['items'] = [];
|
||||
const filteredMenus = menuList.filter(menu => menu.display);
|
||||
|
||||
return menuList.filter(menu => menu.display).map((menu) => {
|
||||
filteredMenus.forEach((menu, index) => {
|
||||
const iconKey = selectedKeys.includes(menu.path || '') ? `${menu.code}Active` : menu.code;
|
||||
const iconSrc = iconPathMap[iconKey as keyof typeof iconPathMap];
|
||||
const subs = (menu.subs || []).filter(sub => sub.display);
|
||||
|
||||
/** Leaf node - menu item without children */
|
||||
if (!subs || subs.length === 0) {
|
||||
if (!menu.path) return null;
|
||||
|
||||
return {
|
||||
key: menu.path,
|
||||
title: menu.i18nKey ? t(menu.i18nKey) : menu.label,
|
||||
label: (
|
||||
<span data-menu-id={menu.path}>
|
||||
{menu.i18nKey ? t(menu.i18nKey) : menu.label}
|
||||
</span>
|
||||
),
|
||||
if (menu.path) {
|
||||
items.push({
|
||||
key: menu.path,
|
||||
title: menu.i18nKey ? t(menu.i18nKey) : menu.label,
|
||||
label: (
|
||||
<span data-menu-id={menu.path}>
|
||||
{menu.i18nKey ? t(menu.i18nKey) : menu.label}
|
||||
</span>
|
||||
),
|
||||
icon: iconSrc ? <img
|
||||
src={iconSrc}
|
||||
className="rb:w-4 rb:h-4"
|
||||
alt={iconSrc}
|
||||
/> : null,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
/** Node with submenu - menu item with children */
|
||||
const menuLabel = collapsed && menu.type === 'group'? '': menu.i18nKey ? t(menu.i18nKey) : menu.label;
|
||||
const children = generateMenuItems(subs) || [];
|
||||
items.push({
|
||||
key: `submenu-${menu.id}`,
|
||||
...(menu.type === 'group' ? { type: 'group' as const } : {}),
|
||||
title: menuLabel,
|
||||
label: menuLabel,
|
||||
icon: iconSrc ? <img
|
||||
src={iconSrc}
|
||||
className="rb:w-4 rb:h-4 rb:mr-2"
|
||||
/> : null,
|
||||
};
|
||||
className="rb:w-4 rb:h-4"
|
||||
alt={iconSrc}
|
||||
/> : <UserOutlined/>,
|
||||
children,
|
||||
});
|
||||
}
|
||||
|
||||
/** Node with submenu - menu item with children */
|
||||
const menuLabel = menu.i18nKey ? t(menu.i18nKey) : menu.label;
|
||||
return {
|
||||
key: `submenu-${menu.id}`,
|
||||
title: menuLabel,
|
||||
label: menuLabel,
|
||||
icon: iconSrc ? <img
|
||||
src={iconSrc}
|
||||
className="rb:w-4 rb:h-4 rb:mr-2"
|
||||
/> : <UserOutlined/>,
|
||||
children: generateMenuItems(subs),
|
||||
};
|
||||
}).filter(Boolean);
|
||||
/** Add divider after group items (except the last one) */
|
||||
if (menu.type === 'group' && index < filteredMenus.length - 1) {
|
||||
items.push({ type: 'divider', key: `divider-${menu.id}` });
|
||||
}
|
||||
});
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
/** Generate menu items from configuration */
|
||||
@@ -252,20 +268,26 @@ const Menu: FC<{
|
||||
'rb:flex rb:items-center rb:text-[14px]! rb:py-2!': !collapsed && source === 'space' && user.current_workspace_name,
|
||||
})}>
|
||||
{!collapsed && source === 'space' && user.current_workspace_name
|
||||
? <div className="rb:w-43.75 rb:text-center">
|
||||
<div className="rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap">{user.current_workspace_name}</div>
|
||||
<span className="rb:text-[12px] rb:text-[#5B6167] rb:leading-4 rb:font-regular">
|
||||
{t(`space.${storageType}`)}
|
||||
</span>
|
||||
</div>
|
||||
: !collapsed
|
||||
? <div className="rb:flex">
|
||||
<img src={logo} className={styles.logo} />
|
||||
{t('title')}
|
||||
? <Flex gap={9}>
|
||||
<Flex align="center" justify="center" className="rb:size-10 rb:rounded-xl rb:bg-[#171719] rb:text-white rb:text-[18px] rb:font-medium">{user.current_workspace_name[0]}</Flex>
|
||||
<div className="rb:w-32">
|
||||
<div className="rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap rb:font-medium rb:text-[16px] rb:leading-5.5">{user.current_workspace_name}</div>
|
||||
<span className="rb:text-[14px] rb:text-[#5B6167] rb:leading-5 rb:font-regular">
|
||||
{t(`space.${storageType}`)}
|
||||
</span>
|
||||
</div>
|
||||
</Flex>
|
||||
: !collapsed
|
||||
? <Flex>
|
||||
<img src={logo} className={styles.logo}
|
||||
alt={logo} />
|
||||
{t('title')}
|
||||
</Flex>
|
||||
: null
|
||||
}
|
||||
<img src={collapsed ? menuUnfold : menuFold} className={styles.menuIcon} onClick={toggleSider} />
|
||||
<div className={clsx("rb:cursor-pointer rb:size-5 rb:bg-cover rb:bg-[url('@/assets/images/menuNew/menuFold.svg')]", {
|
||||
'rb:rotate-180': collapsed
|
||||
})} onClick={toggleSider}></div>
|
||||
</div>
|
||||
{/* Main navigation menu */}
|
||||
<AntMenu
|
||||
@@ -276,18 +298,21 @@ const Menu: FC<{
|
||||
onClick={handleMenuClick}
|
||||
items={menuItems}
|
||||
inlineCollapsed={collapsed}
|
||||
inlineIndent={13}
|
||||
inlineIndent={10}
|
||||
className="rb:max-h-[calc(100vh-136px)] rb:overflow-y-auto"
|
||||
/>
|
||||
{/* Return to space button for superusers */}
|
||||
{user?.is_superuser && source === 'space' &&
|
||||
<div
|
||||
<Flex
|
||||
gap={8}
|
||||
align="center"
|
||||
justify="start"
|
||||
onClick={goToSpace}
|
||||
className="rb:pl-6.25 rb:flex rb:items-center rb:justify-start rb:absolute rb:bottom-8 rb:w-full rb:text-[12px] rb:text-[#5B6167] rb:hover:text-[#212332] rb:leading-4 rb:font-regular rb:text-center rb:mt-6 rb:cursor-pointer"
|
||||
className="rb-border-t rb:pt-5! rb:pb-2.5! rb:absolute rb:bottom-2.5 rb:right-5 rb:left-5 rb:text-[13px] rb:text-[#5B6167] rb:hover:text-[#212332] rb:leading-4.5 rb:font-regular rb:text-center rb:mt-2.25 rb:cursor-pointer"
|
||||
>
|
||||
<img src={logout} className="rb:w-4 rb:h-4 rb:mr-4" />
|
||||
<div className="rb:cursor-pointer rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/logout.svg')]"></div>
|
||||
{collapsed ? null : t('common.returnToSpace')}
|
||||
</div>
|
||||
</Flex>
|
||||
}
|
||||
</Sider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user