chore: add frontend/src/components/layout/BottomTab.tsx

This commit is contained in:
2026-04-29 10:53:27 +08:00
parent 4f041ea1f3
commit e17041d542
@@ -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;