fix(front): restore grouped sidebar with IM Bot section (channels + wechat-bot); normalize brand color #5B5FED
This commit is contained in:
1 parent
9c684076f3
commit
2847289921
2 files changed
+125
-36
No files matched your search
@@ -1,16 +1,18 @@
|
||||
/* ===== Front Layout ===== */
|
||||
/* ===== Front Layout V3 - Light Theme ===== */
|
||||
.front-layout {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background: #F5F7FC;
|
||||
color: #1A1F36;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.front-sidebar {
|
||||
width: var(--sidebar-width);
|
||||
min-width: var(--sidebar-width);
|
||||
background: var(--color-sidebar-bg);
|
||||
border-right: 1px solid var(--color-border-light);
|
||||
background: linear-gradient(180deg, #FFFFFF 0%, #FAFBFE 100%);
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.08);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: width var(--transition-slow), min-width var(--transition-slow);
|
||||
@@ -29,7 +31,7 @@
|
||||
justify-content: space-between;
|
||||
padding: var(--space-lg) var(--space-lg);
|
||||
height: var(--header-height);
|
||||
border-bottom: 1px solid var(--color-border-light);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.sidebar-logo {
|
||||
@@ -41,7 +43,7 @@
|
||||
.sidebar-brand {
|
||||
font-size: var(--text-lg);
|
||||
font-weight: 700;
|
||||
color: var(--color-text-primary);
|
||||
color: #1A1F36;
|
||||
letter-spacing: -0.02em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -53,13 +55,14 @@
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--color-text-tertiary);
|
||||
color: #8892B0;
|
||||
transition: all var(--transition-fast);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.collapse-btn:hover {
|
||||
background: var(--color-bg-hover);
|
||||
color: var(--color-text-primary);
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
color: #8892B0;
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
@@ -68,7 +71,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: var(--space-sm) var(--space-sm);
|
||||
padding: var(--space-sm);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -77,14 +80,23 @@
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
.nav-group-title {
|
||||
padding: 14px var(--space-md) 6px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-md);
|
||||
padding: var(--space-md) var(--space-md);
|
||||
padding: 10px var(--space-md);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--color-text-secondary);
|
||||
color: #8892B0;
|
||||
text-decoration: none;
|
||||
font-size: var(--text-base);
|
||||
font-weight: 500;
|
||||
@@ -93,13 +105,13 @@
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: var(--color-sidebar-hover);
|
||||
color: var(--color-text-primary);
|
||||
background: rgba(91, 95, 237, 0.06);
|
||||
color: #1A1F36;
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: var(--color-sidebar-active);
|
||||
color: var(--color-accent);
|
||||
background: rgba(91, 95, 237, 0.12);
|
||||
color: #5B5FED;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
@@ -115,10 +127,30 @@
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Active dot indicator */
|
||||
.nav-active-dot {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-item.active .nav-active-dot {
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: #5B5FED;
|
||||
box-shadow: 0 0 8px rgba(91, 95, 237, 0.5);
|
||||
}
|
||||
|
||||
/* Sidebar Footer */
|
||||
.sidebar-footer {
|
||||
padding: var(--space-md);
|
||||
border-top: 1px solid var(--color-border-light);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.sidebar-footer .nav-item {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.user-info-mini {
|
||||
@@ -126,13 +158,14 @@
|
||||
align-items: center;
|
||||
gap: var(--space-md);
|
||||
padding: var(--space-sm);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.user-avatar-sm {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: var(--radius-full);
|
||||
background: linear-gradient(135deg, var(--color-accent), #8185F5);
|
||||
background: linear-gradient(135deg, #5B5FED, #4A4ECC);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -151,16 +184,21 @@
|
||||
.user-name {
|
||||
font-size: var(--text-sm);
|
||||
font-weight: 600;
|
||||
color: var(--color-text-primary);
|
||||
color: #1A1F36;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.user-plan {
|
||||
font-size: var(--text-xs);
|
||||
color: var(--color-accent);
|
||||
color: #5B5FED;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.logout-btn:hover {
|
||||
color: #EF4444 !important;
|
||||
background: rgba(239, 68, 68, 0.1) !important;
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
.front-main {
|
||||
flex: 1;
|
||||
@@ -168,6 +206,12 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
background: #F5F7FC;
|
||||
}
|
||||
|
||||
/* Forward-slash route for apps - hidden default route */
|
||||
.front-main > * {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@@ -187,3 +231,11 @@
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Home & Admin links */
|
||||
.nav-home-link {
|
||||
margin-bottom: 4px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
@@ -3,15 +3,31 @@ import { Outlet, NavLink, useNavigate } from 'react-router-dom';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
import './FrontLayout.css';
|
||||
|
||||
const navItems = [
|
||||
const navGroups = [
|
||||
{
|
||||
title: '智能体',
|
||||
items: [
|
||||
{ path: '/chat', icon: 'chat', label: 'AI聊天' },
|
||||
{ path: '/channels', icon: 'channel', label: '频道' },
|
||||
{ path: '/knowledge', icon: 'knowledge', label: '知识库' },
|
||||
{ path: '/market', icon: 'market', label: '应用市场' },
|
||||
{ path: '/market', icon: 'market', label: '技能市场' },
|
||||
{ path: '/evolution', icon: 'evolution', label: 'AI自进化' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'IM Bot',
|
||||
items: [
|
||||
{ path: '/channels', icon: 'channel', label: '频道管理' },
|
||||
{ path: '/wechat-bot', icon: 'wechat', label: '微信机器人' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '资源中心',
|
||||
items: [
|
||||
{ path: '/assets', icon: 'assets', label: '资产文件' },
|
||||
{ path: '/token', icon: 'token', label: 'Token驾驶舱' },
|
||||
{ path: '/pricing', icon: 'pricing', label: '付费中心' },
|
||||
{ path: '/assets', icon: 'assets', label: '资产' },
|
||||
{ path: '/evolution', icon: 'evolution', label: 'AI进化' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const bottomItems = [
|
||||
@@ -24,12 +40,14 @@ const iconPaths: Record<string, JSX.Element> = {
|
||||
channel: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>,
|
||||
knowledge: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>,
|
||||
market: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="7" height="7" rx="1.2"/><rect x="14" y="3" width="7" height="7" rx="1.2"/><rect x="3" y="14" width="7" height="7" rx="1.2"/><rect x="14" y="14" width="7" height="7" rx="1.2"/></svg>,
|
||||
apps: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="2" width="9" height="9" rx="2"/><rect x="13" y="2" width="9" height="9" rx="2"/><rect x="2" y="13" width="9" height="9" rx="2"/><rect x="13" y="13" width="5" height="5" rx="1.5"/><path d="M18 18v3"/><path d="M19.5 16.5v6"/></svg>,
|
||||
token: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>,
|
||||
pricing: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><path d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8"/><path d="M12 18V6"/></svg>,
|
||||
profile: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>,
|
||||
notify: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>,
|
||||
assets: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>,
|
||||
evolution: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>,
|
||||
wechat: <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/><circle cx="9" cy="11" r="1"/><circle cx="15" cy="11" r="1"/></svg>,
|
||||
};
|
||||
|
||||
const FrontLayout: React.FC = () => {
|
||||
@@ -46,49 +64,71 @@ const FrontLayout: React.FC = () => {
|
||||
<div className="front-layout">
|
||||
{/* Sidebar */}
|
||||
<aside className={`front-sidebar ${collapsed ? 'collapsed' : ''}`}>
|
||||
{/* Header */}
|
||||
<div className="sidebar-header">
|
||||
<div className="sidebar-logo">
|
||||
<svg width="28" height="28" viewBox="0 0 32 32" fill="none">
|
||||
<rect width="32" height="32" rx="8" fill="#5B5FED"/>
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none">
|
||||
<defs>
|
||||
<linearGradient id="flGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stopColor="#5B5FED"/>
|
||||
<stop offset="100%" stopColor="#4A4ECC"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="32" height="32" rx="8" fill="url(#flGrad)"/>
|
||||
<path d="M8 20V12l4 4 4-4v8" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" fill="none"/>
|
||||
<path d="M6 22h20" stroke="white" strokeWidth="1.5" strokeLinecap="round" opacity="0.5"/>
|
||||
</svg>
|
||||
{!collapsed && <span className="sidebar-brand">Qihu-AIFlow</span>}
|
||||
{!collapsed && <span className="sidebar-brand">七虎AI</span>}
|
||||
</div>
|
||||
<button className="collapse-btn" onClick={() => setCollapsed(!collapsed)}>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/>
|
||||
{collapsed ? <><polyline points="13 17 18 12 13 7"/><polyline points="6 17 11 12 6 7"/></> : <><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></>}
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="sidebar-nav">
|
||||
<div className="nav-section">
|
||||
{navItems.map(item => (
|
||||
<NavLink to="/" className={({ isActive }) => `nav-item nav-home-link`} end>
|
||||
<span className="nav-item-icon">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
|
||||
</span>
|
||||
{!collapsed && <span className="nav-item-label">返回首页</span>}
|
||||
</NavLink>
|
||||
{navGroups.map(group => (
|
||||
<div className="nav-section" key={group.title}>
|
||||
{!collapsed && <div className="nav-group-title">{group.title}</div>}
|
||||
{group.items.map(item => (
|
||||
<NavLink
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}
|
||||
title={collapsed ? item.label : undefined}
|
||||
>
|
||||
<span className="nav-icon">{iconPaths[item.icon]}</span>
|
||||
{!collapsed && <span className="nav-label">{item.label}</span>}
|
||||
{!collapsed && <span className="nav-active-dot" />}
|
||||
</NavLink>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
<div className="nav-section nav-bottom">
|
||||
{bottomItems.map(item => (
|
||||
<NavLink
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}
|
||||
title={collapsed ? item.label : undefined}
|
||||
>
|
||||
<span className="nav-icon">{iconPaths[item.icon]}</span>
|
||||
{!collapsed && <span className="nav-label">{item.label}</span>}
|
||||
{!collapsed && <span className="nav-active-dot" />}
|
||||
</NavLink>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="sidebar-footer">
|
||||
<div className="user-info-mini">
|
||||
<div className="user-avatar-sm">
|
||||
@@ -105,17 +145,14 @@ const FrontLayout: React.FC = () => {
|
||||
<NavLink
|
||||
to="/admin"
|
||||
className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}
|
||||
title="返回后台"
|
||||
title="管理后台"
|
||||
>
|
||||
<span className="nav-icon">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
||||
<rect x="3" y="3" width="7" height="7" rx="1.2"/>
|
||||
<rect x="14" y="3" width="7" height="7" rx="1.2"/>
|
||||
<rect x="3" y="14" width="7" height="7" rx="1.2"/>
|
||||
<rect x="14" y="14" width="7" height="7" rx="1.2"/>
|
||||
<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
||||
</svg>
|
||||
</span>
|
||||
{!collapsed && <span className="nav-label">返回后台</span>}
|
||||
{!collapsed && <span className="nav-label">管理后台</span>}
|
||||
</NavLink>
|
||||
)}
|
||||
<button
|
||||
|
||||
Reference in new issue
Block a user