feat(web): components update

This commit is contained in:
zhaoying
2026-03-07 12:18:11 +08:00
parent 4c18f9e858
commit 0b3b241436
44 changed files with 1881 additions and 345 deletions

View File

@@ -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>
);