0df8aa77d9
- 员工花名册管理(加密存储、导入导出) - 薪酬管理(发薪批次、薪酬模版、加班费计算、工资条) - 社保公积金(多城市配置、版本管理、基数调整) - 解聘管理(6步流程、证据链、工作交接) - AI 助手(合同审查、风险预测、RAG 知识库) - Dashboard 仪表盘 - 设置与通知
85 lines
2.8 KiB
TypeScript
85 lines
2.8 KiB
TypeScript
import { useState, useEffect } from 'react'
|
|
import { X, ArrowRight } from 'lucide-react'
|
|
|
|
const STORAGE_KEY = 'hr-onboarding-completed'
|
|
|
|
const steps = [
|
|
{
|
|
icon: '🏠',
|
|
title: '这里看风险',
|
|
description: '首页展示企业用工风险总览,红色代表高风险项,点击「去处理」直接跳转操作。',
|
|
},
|
|
{
|
|
icon: '�',
|
|
title: '这里管花名册',
|
|
description: '花名册页面管理员工档案、劳动合同、附件,以及违纪、考勤、培训、绩效记录,可生成仲裁证据链。',
|
|
},
|
|
{
|
|
icon: '💰',
|
|
title: '这里算薪税',
|
|
description: '薪税页面提供加班费、双倍工资、社保公积金计算器和工资条管理,输入参数实时计算。',
|
|
},
|
|
]
|
|
|
|
export default function OnboardingGuide() {
|
|
const [visible, setVisible] = useState(false)
|
|
const [step, setStep] = useState(0)
|
|
|
|
useEffect(() => {
|
|
const completed = localStorage.getItem(STORAGE_KEY)
|
|
if (!completed) {
|
|
setVisible(true)
|
|
}
|
|
}, [])
|
|
|
|
const close = () => {
|
|
localStorage.setItem(STORAGE_KEY, '1')
|
|
setVisible(false)
|
|
}
|
|
|
|
if (!visible) return null
|
|
|
|
const current = steps[step]
|
|
const isLast = step === steps.length - 1
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40">
|
|
<div className="bg-white rounded-xl shadow-xl max-w-sm w-full mx-4 overflow-hidden">
|
|
<div className="flex justify-end p-2">
|
|
<button onClick={close} className="text-gray-400 hover:text-gray-600">
|
|
<X className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
<div className="px-6 pb-6">
|
|
<div className="text-5xl text-center mb-4">{current.icon}</div>
|
|
<h2 className="text-lg font-semibold text-center mb-2">{current.title}</h2>
|
|
<p className="text-sm text-gray-600 text-center mb-6">{current.description}</p>
|
|
|
|
{/* 进度指示器 */}
|
|
<div className="flex justify-center gap-1.5 mb-6">
|
|
{steps.map((_, i) => (
|
|
<div
|
|
key={i}
|
|
className={`h-1.5 rounded-full transition-all ${i === step ? 'w-6 bg-primary' : 'w-1.5 bg-gray-300'}`}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
<div className="flex justify-between">
|
|
{step > 0 ? (
|
|
<button onClick={() => setStep(step - 1)} className="text-sm text-gray-500">上一步</button>
|
|
) : <span />}
|
|
<button
|
|
onClick={() => isLast ? close() : setStep(step + 1)}
|
|
className="flex items-center gap-1 text-sm font-medium text-primary"
|
|
>
|
|
{isLast ? '开始使用' : '下一步'}
|
|
{!isLast && <ArrowRight className="w-4 h-4" />}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|