This commit is contained in:
YI FANG
2025-11-26 09:50:49 +08:00
commit 8155c9f95d
43 changed files with 7687 additions and 0 deletions

120
src/layouts/MainLayout.tsx Normal file
View File

@@ -0,0 +1,120 @@
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import { useEffect, useMemo, useState } from 'react';
import type { QuickActionType } from '../data/mockData';
import { EXAM_CLIENTS } from '../data/mockData';
import { QuickActionModal } from '../components/modals/QuickActionModal';
import { LoginModal } from '../components/modals/LoginModal';
import { Sidebar, type SectionKey } from '../components/layout/Sidebar';
import { TopBar } from '../components/layout/TopBar';
export interface MainLayoutContext {
search: string;
setSearch: (value: string) => void;
}
const sectionToRoute: Record<SectionKey, string> = {
home: '/home',
exam: '/exam',
booking: '/booking',
support: '/support',
};
const routeToSection = Object.entries(sectionToRoute).reduce<Record<string, SectionKey>>(
(acc, [section, route]) => {
acc[route] = section as SectionKey;
return acc;
},
{},
);
export const MainLayout = () => {
const [search, setSearch] = useState('');
const [quickAction, setQuickAction] = useState<QuickActionType>('none');
const [noteText, setNoteText] = useState('');
const [loginModalOpen, setLoginModalOpen] = useState(false);
const [operatorName, setOperatorName] = useState<string>('');
const [mealDoneIds, setMealDoneIds] = useState<string[]>(
EXAM_CLIENTS.filter((c) => c.status === '用餐').map((c) => c.id),
);
const totalExamCount = EXAM_CLIENTS.length;
const mealCount = mealDoneIds.length;
const notMealCount = totalExamCount - mealCount;
const navigate = useNavigate();
const location = useLocation();
const activeSection: SectionKey = useMemo(() => {
const matched = Object.entries(routeToSection).find(([path]) => location.pathname.startsWith(path));
return (matched?.[1] || 'home') as SectionKey;
}, [location.pathname]);
const handleNavigate = (section: SectionKey) => {
navigate(sectionToRoute[section]);
};
const handleMealDone = (id: string) => {
setMealDoneIds((prev) => (prev.includes(id) ? prev : prev.concat(id)));
};
const handleLoginSuccess = (phone: string) => {
// 实际项目中应该从后端获取用户信息
// 这里暂时使用手机号后4位作为操作员名称
const displayName = phone.slice(-4);
setOperatorName(displayName);
// 可以存储到 localStorage 或状态管理中
localStorage.setItem('operatorPhone', phone);
localStorage.setItem('operatorName', displayName);
};
// 初始化时检查是否有已登录的操作员
useEffect(() => {
const savedName = localStorage.getItem('operatorName');
if (savedName) {
setOperatorName(savedName);
}
}, []);
return (
<div className='min-h-screen bg-gray-50 text-gray-900 grid grid-cols-[240px_1fr]'>
<Sidebar active={activeSection} onNavigate={handleNavigate} onQuickAction={setQuickAction} />
<div className='flex flex-col min-h-screen'>
<TopBar
search={search}
onSearch={setSearch}
enableSearch={activeSection === 'exam'}
operatorName={operatorName}
onLoginClick={() => setLoginModalOpen(true)}
/>
<main className='p-6 space-y-6 flex-1 overflow-auto'>
<Outlet context={{ search, setSearch }} />
</main>
</div>
{loginModalOpen && (
<LoginModal
onClose={() => setLoginModalOpen(false)}
onLoginSuccess={handleLoginSuccess}
/>
)}
{quickAction !== 'none' && (
<QuickActionModal
action={quickAction}
noteText={noteText}
onNoteChange={setNoteText}
onClose={() => setQuickAction('none')}
totalExamCount={totalExamCount}
mealCount={mealCount}
notMealCount={notMealCount}
mealDoneIds={mealDoneIds}
onMealDone={handleMealDone}
/>
)}
</div>
);
};