5196d7c80a
- portalAxios 401 响应拦截器:清除 token 并跳转 /portal/login - PortalLayoutWrapper:检查 token 是否存在,不存在则跳转登录页 - 排除 /portal/login 和 /portal/auto-login 页面避免重复跳转 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
59 lines
2.8 KiB
TypeScript
59 lines
2.8 KiB
TypeScript
import { useState, Suspense } from 'react'
|
||
import { useSearchParams } from 'react-router-dom'
|
||
import { Layers, Wallet, LayoutTemplate, Receipt, Loader2 } from 'lucide-react'
|
||
import PageGuide from '../components/ui/PageGuide'
|
||
import { lazyRetry } from '../lib/lazyRetry'
|
||
|
||
const BatchManager = lazyRetry(() => import('./money/BatchTab').then(m => ({ default: m.BatchManager })))
|
||
const TemplateManager = lazyRetry(() => import('./money/TemplateTab').then(m => ({ default: m.TemplateManager })))
|
||
const PayslipManager = lazyRetry(() => import('./money/PayslipTab').then(m => ({ default: m.PayslipManager })))
|
||
|
||
type Tab = 'batch' | 'template' | 'payslip'
|
||
|
||
export default function Money() {
|
||
const [searchParams] = useSearchParams()
|
||
const initialEmployeeId = searchParams.get('employeeId') || ''
|
||
const initialTab = (searchParams.get('tab') as Tab) || (initialEmployeeId ? 'payslip' : 'batch')
|
||
const [tab, setTab] = useState<Tab>(initialTab)
|
||
|
||
const tabs: { key: Tab; label: string; icon: React.ReactNode }[] = [
|
||
{ key: 'batch', label: '发薪批次', icon: <Layers className="w-4 h-4" /> },
|
||
{ key: 'template', label: '薪酬模版', icon: <LayoutTemplate className="w-4 h-4" /> },
|
||
{ key: 'payslip', label: '工资条管理', icon: <Receipt className="w-4 h-4" /> },
|
||
]
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<PageGuide>薪酬管理,支持工资条发放、批量算薪和工资模板配置。流程:①配置模板 → ②导入考勤数据 → ③批量算薪 → ④生成工资条。关联:工资数据来自花名册薪酬信息,考勤数据来自考勤管理,加班费在「加班费计算」模块单独计算后导入。</PageGuide>
|
||
<div className="flex items-center gap-2">
|
||
<Wallet className="w-5 h-5 text-primary" />
|
||
<div>
|
||
<h1 className="text-base font-semibold">薪税管理</h1>
|
||
<p className="mt-1 text-sm text-gray-500">统一管理发薪批次、工资条及薪酬模板</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex gap-1 border-b overflow-x-auto">
|
||
{tabs.map((t) => (
|
||
<button
|
||
key={t.key}
|
||
onClick={() => setTab(t.key)}
|
||
className={`flex items-center gap-1.5 px-4 py-2 text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${
|
||
tab === t.key ? 'border-primary text-primary' : 'border-transparent text-gray-500 hover:text-gray-700'
|
||
}`}
|
||
>
|
||
{t.icon}
|
||
{t.label}
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
<Suspense fallback={<div className="flex items-center justify-center py-12"><Loader2 className="w-5 h-5 animate-spin text-gray-400" /></div>}>
|
||
{tab === 'batch' && <BatchManager />}
|
||
{tab === 'template' && <TemplateManager />}
|
||
{tab === 'payslip' && <PayslipManager filterEmployeeId={initialEmployeeId} />}
|
||
</Suspense>
|
||
</div>
|
||
)
|
||
}
|