chore: add frontend/src/components/layout/BottomTab.tsx
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
import React from 'react';
|
||||
import { NavLink, useLocation } from 'react-router-dom';
|
||||
|
||||
const BottomTab: React.FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
const navItems = [
|
||||
{ path: '/', label: '首页', icon: HomeIcon },
|
||||
{ path: '/record', label: '记账', icon: RecordIcon },
|
||||
{ path: '/budget', label: '预算', icon: BudgetIcon },
|
||||
{ path: '/statistics', label: '统计', icon: StatisticsIcon },
|
||||
];
|
||||
|
||||
return (
|
||||
<nav className="bottom-tab-bar" role="navigation" aria-label="底部导航">
|
||||
{navItems.map((item) => (
|
||||
<NavLink
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
className={({ isActive }) =>
|
||||
`tab-item ${isActive ? 'active' : ''}`
|
||||
}
|
||||
aria-current={location.pathname === item.path ? 'page' : undefined}
|
||||
>
|
||||
<item.icon />
|
||||
<span>{item.label}</span>
|
||||
</NavLink>
|
||||
))}
|
||||
|
||||
<style>{`
|
||||
:root {
|
||||
--primary: #0052ff;
|
||||
--surface: #ffffff;
|
||||
--border: #e5e5e5;
|
||||
--text-secondary: #737373;
|
||||
}
|
||||
|
||||
.bottom-tab-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 64px;
|
||||
background: var(--surface);
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.bottom-tab-bar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
function HomeIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" width="24" height="24">
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
function RecordIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" width="24" height="24">
|
||||
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function BudgetIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" width="24" height="24">
|
||||
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"/>
|
||||
<line x1="8" y1="21" x2="16" y2="21"/>
|
||||
<line x1="12" y1="17" x2="12" y2="21"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function StatisticsIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" width="24" height="24">
|
||||
<line x1="18" y1="20" x2="18" y2="10"/>
|
||||
<line x1="12" y1="20" x2="12" y2="4"/>
|
||||
<line x1="6" y1="20" x2="6" y2="14"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default BottomTab;
|
||||
Reference in New Issue
Block a user