874f8c49bd
部署后旧chunk文件名(带hash)被删除,浏览器若缓存了旧index.html 仍会请求旧chunk导致 "Failed to fetch dynamically imported module"。 新增 lazyRetry 包装器,检测到此错误时自动刷新页面获取新index.html。 用 sessionStorage 标记防止无限刷新。 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
67 lines
3.1 KiB
TypeScript
67 lines
3.1 KiB
TypeScript
import { useState, Suspense } from 'react'
|
||
import { Bot, FileSearch, Scale, Sparkles, BookOpen, TrendingUp, Loader2 } from 'lucide-react'
|
||
import PageGuide from '../components/ui/PageGuide'
|
||
import { lazyRetry } from '../lib/lazyRetry'
|
||
|
||
const ChatTab = lazyRetry(() => import('./ai-assistant/ChatTab').then(m => ({ default: m.ChatTab })))
|
||
const PredictTab = lazyRetry(() => import('./ai-assistant/PredictTab').then(m => ({ default: m.PredictTab })))
|
||
const ReviewTab = lazyRetry(() => import('./ai-assistant/ReviewTab').then(m => ({ default: m.ReviewTab })))
|
||
const CaseTab = lazyRetry(() => import('./ai-assistant/CaseTab').then(m => ({ default: m.CaseTab })))
|
||
const KnowledgeTab = lazyRetry(() => import('./ai-assistant/KnowledgeTab').then(m => ({ default: m.KnowledgeTab })))
|
||
const HRReportTab = lazyRetry(() => import('./ai-assistant/HRReportTab').then(m => ({ default: m.HRReportTab })))
|
||
|
||
type Tab = 'chat' | 'predict' | 'review' | 'case' | 'knowledge' | 'hr-report'
|
||
|
||
export default function AIAssistant() {
|
||
const [tab, setTab] = useState<Tab>('chat')
|
||
|
||
const tabs: { key: Tab; label: string; icon: typeof Bot }[] = [
|
||
{ key: 'chat', label: '智能问答', icon: Bot },
|
||
{ key: 'predict', label: '风险预测', icon: Sparkles },
|
||
{ key: 'review', label: '合同审查', icon: FileSearch },
|
||
{ key: 'case', label: '案例匹配', icon: Scale },
|
||
{ key: 'hr-report', label: '人力报告', icon: TrendingUp },
|
||
{ key: 'knowledge', label: '知识库', icon: BookOpen },
|
||
]
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<PageGuide>AI 智能助手,提供用工风险预测、案例分析、合规建议等智能服务。支持自然语言提问,系统自动匹配相关法规和案例。关联:风险预测数据来自花名册和合同信息。</PageGuide>
|
||
<div className="flex items-center gap-2">
|
||
<Bot className="h-5 w-5 text-primary" />
|
||
<div>
|
||
<h1 className="text-base font-semibold">AI 合规顾问</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) => {
|
||
const Icon = t.icon
|
||
return (
|
||
<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'
|
||
}`}
|
||
>
|
||
<Icon className="w-4 h-4" />
|
||
{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 === 'chat' && <ChatTab />}
|
||
{tab === 'predict' && <PredictTab />}
|
||
{tab === 'review' && <ReviewTab />}
|
||
{tab === 'case' && <CaseTab />}
|
||
{tab === 'hr-report' && <HRReportTab />}
|
||
{tab === 'knowledge' && <KnowledgeTab />}
|
||
</Suspense>
|
||
</div>
|
||
)
|
||
}
|