init
This commit is contained in:
120
src/layouts/MainLayout.tsx
Normal file
120
src/layouts/MainLayout.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user