feat: 完成23项系统优化 - 花名册社保状态列/身份证复制/附件类型扩展, 用工办理姓名检索/直接提交/文书查看/批量证明, 风险中心跳转筛选+批量处理, 日历7/15/35天分组+逾期统计, 考勤单条编辑+按人导出, 工资条查看状态+工资流水导出, 辞职申请附件上传, 交接清单PDF下载, 操作完成下一步引导, 休假审批入口, 人效成本分部门
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { HelpCircle, Search, ChevronDown, ChevronRight, Sparkles,
|
||||
Home, Users, FileText, Calculator, Bot,
|
||||
Settings, Lightbulb, AlertTriangle, CheckCircle, Phone } from 'lucide-react'
|
||||
Home, Users, FileText, Calculator, Bot, Calendar,
|
||||
Settings, Lightbulb, AlertTriangle, CheckCircle, Phone, RotateCcw, ShieldAlert, TrendingDown } from 'lucide-react'
|
||||
import Modal from './ui/Modal'
|
||||
import { aiApi } from '../lib/api-services'
|
||||
import { resetOnboarding } from './OnboardingGuide'
|
||||
import clsx from 'clsx'
|
||||
|
||||
interface HelpCategory {
|
||||
@@ -23,6 +25,41 @@ interface HelpArticle {
|
||||
}
|
||||
|
||||
const categories: HelpCategory[] = [
|
||||
{
|
||||
id: 'home',
|
||||
title: '首页',
|
||||
icon: Home,
|
||||
articles: [
|
||||
{
|
||||
id: 'system-intro',
|
||||
question: '本系统能帮企业做什么?',
|
||||
answer: '「企业用工专家」是一站式人力资源管理平台,覆盖员工全生命周期管理,帮助企业高效管理人事业务的同时确保合规运营:\n• 员工管理:入职登记、合同签订、转正调岗、离职解聘\n• 薪税管理:工资计算、个税申报、社保公积金缴纳\n• 考勤管理:排班打卡、加班统计、休假记录、月度报表\n• 合同管理:电子合同、到期提醒、续签流程\n• 风险管控:自动扫描法律风险、合规预警、判赔预测\n• AI 助手:劳动法咨询、智能问答、文档生成',
|
||||
},
|
||||
{
|
||||
id: 'compliance',
|
||||
question: '系统如何保障用工合规?',
|
||||
answer: '系统从以下维度帮助企业实现合规管理:\n• 合同合规:自动提醒合同到期续签,检测未签合同风险(入职1个月内未签合同需支付双倍工资)\n• 薪酬合规:自动计算个税、社保扣款,确保发薪准确无误\n• 考勤合规:记录加班时长,预警超时加班风险,留存考勤证据\n• 解聘合规:自动计算经济补偿金,生成规范解聘协议,降低劳动争议风险\n• 社保合规:跟踪社保缴纳情况,提醒漏缴断缴\n• 风险预警:统一风险中心实时扫描所有数据,按高/中/低分级预警',
|
||||
tip: '建议每周查看风险中心,每月核对薪税和考勤数据,确保合规无遗漏。',
|
||||
},
|
||||
{
|
||||
id: 'workflow',
|
||||
question: '日常人事工作流程是怎样的?',
|
||||
answer: '系统覆盖企业日常人事管理的完整流程:',
|
||||
steps: [
|
||||
'入职:添加员工信息 → 签订合同 → 设置社保 → 安排排班',
|
||||
'日常:考勤打卡 → 加班审批 → 休假管理 → 补卡修正',
|
||||
'月度:导入考勤 → 确认考勤 → 计算工资 → 发放工资条 → 缴纳社保公积金 → 个税申报',
|
||||
'合同:到期提醒 → 续签合同 → 合同确认',
|
||||
'离职:发起解聘 → 计算补偿金 → 生成协议 → 完成离职',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'value',
|
||||
question: '使用系统能带来什么价值?',
|
||||
answer: '• 提效:自动化算薪、考勤统计、合同管理,减少 80% 人工操作\n• 降险:法律风险自动检测预警,避免因疏忽导致的劳动纠纷和罚款\n• 省心:到期提醒、月度任务提醒,不再遗漏关键时间节点\n• 透明:员工可通过手机端查看工资条、合同、考勤记录,信息透明\n• 合规:所有操作留存记录,满足劳动法合规要求,应对审计无忧',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'start',
|
||||
title: '快速入门',
|
||||
@@ -146,21 +183,94 @@ const categories: HelpCategory[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'attendance',
|
||||
title: '考勤管理',
|
||||
icon: Calendar,
|
||||
articles: [
|
||||
{
|
||||
id: 'attendance-overview',
|
||||
question: '考勤管理有哪些功能?',
|
||||
answer: '考勤管理包含 6 个子功能:\n• 考勤确认:导入考勤数据后批量确认并发布给员工\n• 班次管理:设置早班、晚班、弹性班等班次规则\n• 排班:按日期为员工分配班次,支持批量排班\n• 每日出勤:查看当日打卡情况,支持补卡修正\n• 月度报表:汇总月度出勤、迟到、加班数据\n• 休假记录:管理员工请假信息',
|
||||
},
|
||||
{
|
||||
id: 'shift-setup',
|
||||
question: '怎么设置班次?',
|
||||
answer: '在考勤管理「班次管理」标签页中,点击「新增班次」按钮,设置班次名称、上下班时间、弹性时长和休息时长。每个班次可以设置不同颜色方便区分。',
|
||||
tip: '常见班次:早班 08:00-17:00、晚班 14:00-23:00、弹性班 09:00-18:00(弹性30分钟)。',
|
||||
},
|
||||
{
|
||||
id: 'schedule',
|
||||
question: '怎么给员工排班?',
|
||||
answer: '在考勤管理「排班」标签页中:',
|
||||
steps: [
|
||||
'选择日期',
|
||||
'在员工列表中,未排班的员工行内有班次下拉框',
|
||||
'选择班次后点击「排班」按钮即可',
|
||||
'也可以点击「批量排班」按钮,勾选多个员工一次性分配班次',
|
||||
],
|
||||
tip: '支持按姓名或部门搜索,按部门筛选快速定位员工。',
|
||||
},
|
||||
{
|
||||
id: 'attendance-import',
|
||||
question: '怎么导入考勤数据?',
|
||||
answer: '在考勤管理「考勤确认」标签页中,点击「导入考勤」按钮,下载模板填写后上传。系统会自动匹配员工并生成考勤记录。',
|
||||
tip: '身份证号优先匹配,未填时用姓名匹配。',
|
||||
},
|
||||
{
|
||||
id: 'attendance-correct',
|
||||
question: '员工漏打卡了怎么办?',
|
||||
answer: '在「每日出勤」标签页中,找到对应员工,点击「补卡」按钮,手动填写签到/签退时间和状态即可修正记录。',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'risk',
|
||||
title: '风险检测',
|
||||
icon: AlertTriangle,
|
||||
title: '风险中心',
|
||||
icon: ShieldAlert,
|
||||
articles: [
|
||||
{
|
||||
id: 'what-is-risk',
|
||||
question: '风险检测是什么意思?',
|
||||
answer: '系统会自动扫描您的员工和合同数据,发现可能存在的法律风险。比如:合同到期未续签、试用期超长、未签合同等。风险分为高、中、低三个等级,建议优先处理高风险项。',
|
||||
question: '风险中心是什么?',
|
||||
answer: '统一风险中心会自动扫描您的员工、合同、薪酬、社保等数据,汇总所有潜在风险。包括:合同到期未续签、未签合同、试用期超长、薪酬异常、社保漏缴、退休提醒等。风险分为高、中、低三个等级,建议优先处理高风险项。',
|
||||
tip: '访问路径:左侧菜单「风险中心」或直接访问 /risk-center。',
|
||||
},
|
||||
{
|
||||
id: 'how-to-fix',
|
||||
question: '发现风险后怎么处理?',
|
||||
answer: '在首页「总览」页面可以看到风险概览。点击风险项可以跳转到对应员工详情,然后根据系统建议进行处理。处理完成后风险会自动消除。',
|
||||
tip: '建议每周查看一次风险提醒,及时处理避免法律纠纷。',
|
||||
answer: '在风险中心页面,每个风险项右侧有快捷操作按钮(如「续签」「转正」「处理」),点击即可跳转到对应页面处理。处理完成后风险会自动消除。',
|
||||
tip: '建议每周查看一次风险中心,及时处理避免法律纠纷。',
|
||||
},
|
||||
{
|
||||
id: 'risk-types',
|
||||
question: '有哪些类型的风险?',
|
||||
answer: '系统目前检测以下风险类型:\n• 合同风险:到期未续签、未签合同\n• 薪酬风险:薪资异常波动\n• 解聘风险:可能存在劳动争议\n• 月度任务:发薪、社保、公积金、个税等截止日提醒\n• 入职手续:入职材料不完整\n• 退休提醒:员工即将达到退休年龄',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'termination',
|
||||
title: '解聘管理',
|
||||
icon: TrendingDown,
|
||||
articles: [
|
||||
{
|
||||
id: 'termination-process',
|
||||
question: '员工离职怎么处理?',
|
||||
answer: '在「解聘管理」页面处理离职流程:',
|
||||
steps: [
|
||||
'点击「发起解聘」选择员工',
|
||||
'填写解聘原因、离职日期等信息',
|
||||
'系统自动计算经济补偿金',
|
||||
'生成解聘协议书等法律文件',
|
||||
'确认后完成解聘流程',
|
||||
],
|
||||
warning: '不要直接删除员工记录,保留记录有助于日后查证和合规。',
|
||||
},
|
||||
{
|
||||
id: 'compensation',
|
||||
question: '经济补偿金怎么算?',
|
||||
answer: '系统根据员工工龄和月均工资自动计算经济补偿金:\n• 每满一年支付一个月工资\n• 六个月以上不满一年按一年算\n• 不满六个月支付半个月工资\n• 月工资按离职前12个月平均工资计算',
|
||||
tip: '工资高于当地社平工资3倍的,按3倍封顶,最长补偿12年。',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -195,7 +305,12 @@ const categories: HelpCategory[] = [
|
||||
{
|
||||
id: 'notification',
|
||||
question: '怎么设置提醒?',
|
||||
answer: '在「通知管理」页面可以设置各类提醒:\n• 合同到期提前提醒天数\n• 未签合同提醒\n• 试用期到期提醒等\n点击通知铃铛图标可以查看所有未读提醒。',
|
||||
answer: '在「设置」页面的「通知设置」标签中可以配置:\n• 合同到期提前提醒天数\n• 未签合同提醒\n• 加班超时提醒\n• 工资条发布通知\n• 月度事务提醒(发薪日、社保日、公积金日、个税日)\n• 企业微信 Webhook 推送\n• 邮件通知\n点击顶部通知铃铛图标可以查看所有未读提醒。',
|
||||
},
|
||||
{
|
||||
id: 'salary-dashboard',
|
||||
question: '薪酬分析看板有什么用?',
|
||||
answer: '薪酬分析看板在「薪税管理」页面中,提供:\n• 薪酬概览(员工总数、月均薪酬、中位数、年度总薪酬)\n• 部门薪酬对比(含人均薪酬排名)\n• 月度薪酬趋势(同比环比变化)\n帮助您了解薪酬分布情况,辅助预算决策。',
|
||||
},
|
||||
{
|
||||
id: 'change-password',
|
||||
@@ -222,7 +337,7 @@ const categories: HelpCategory[] = [
|
||||
{
|
||||
id: 'data-export',
|
||||
question: '可以导出数据吗?',
|
||||
answer: '可以。在员工管理页面可以导出员工名单为 Excel 文件。工资批次也可以导出为 Excel 方便财务对账。',
|
||||
answer: '可以。在员工管理页面可以导出员工名单为 Excel 文件。工资批次可以导出为 Excel 方便财务对账。考勤管理支持导出每日出勤和月度报表为 CSV 文件。',
|
||||
},
|
||||
{
|
||||
id: 'multi-user',
|
||||
@@ -247,6 +362,7 @@ interface RAGResult {
|
||||
}
|
||||
|
||||
export default function HelpModal({ open, onClose }: { open: boolean; onClose: () => void }) {
|
||||
const navigate = useNavigate()
|
||||
const [activeCategory, setActiveCategory] = useState(categories[0].id)
|
||||
const [expandedArticle, setExpandedArticle] = useState<string | null>(null)
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
@@ -502,10 +618,19 @@ export default function HelpModal({ open, onClose }: { open: boolean; onClose: (
|
||||
|
||||
{/* 底部联系方式 */}
|
||||
<div className="border-t border-gray-200 px-4 py-2.5 flex items-center justify-between text-xs text-gray-500">
|
||||
<span className="flex items-center gap-1.5">
|
||||
<HelpCircle className="w-3.5 h-3.5" />
|
||||
还有问题?在 AI 助手中直接提问
|
||||
</span>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="flex items-center gap-1.5">
|
||||
<HelpCircle className="w-3.5 h-3.5" />
|
||||
还有问题?在 AI 助手中直接提问
|
||||
</span>
|
||||
<button
|
||||
onClick={() => { resetOnboarding(); onClose(); navigate('/'); setTimeout(() => window.location.reload(), 100) }}
|
||||
className="flex items-center gap-1 text-primary hover:underline"
|
||||
>
|
||||
<RotateCcw className="w-3 h-3" />
|
||||
重新显示功能导览
|
||||
</button>
|
||||
</div>
|
||||
<span>support@hr8ai.com</span>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@@ -1,80 +1,144 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { X, ArrowRight } from 'lucide-react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { X, ArrowRight, Home, Users, Calculator, CalendarCheck, ShieldAlert, Bot } from 'lucide-react'
|
||||
|
||||
const STORAGE_KEY = 'hr-onboarding-completed'
|
||||
const STORAGE_KEY = 'hr-onboarding-dismissed'
|
||||
|
||||
const steps = [
|
||||
const modules = [
|
||||
{
|
||||
icon: '🏠',
|
||||
title: '这里看风险',
|
||||
description: '首页展示企业用工风险总览,红色代表高风险项,点击「去处理」直接跳转操作。',
|
||||
icon: Home,
|
||||
color: 'text-blue-600',
|
||||
bg: 'bg-blue-50',
|
||||
title: '工作台',
|
||||
desc: '风险总览、待办事项、日历事件',
|
||||
path: '/',
|
||||
},
|
||||
{
|
||||
icon: '�',
|
||||
title: '这里管花名册',
|
||||
description: '花名册页面管理员工档案、劳动合同、附件,以及违纪、考勤、培训、绩效记录,可生成仲裁证据链。',
|
||||
icon: Users,
|
||||
color: 'text-indigo-600',
|
||||
bg: 'bg-indigo-50',
|
||||
title: '团队管理',
|
||||
desc: '花名册、用工办理、离职管理、特殊状态',
|
||||
path: '/roster',
|
||||
},
|
||||
{
|
||||
icon: '💰',
|
||||
title: '这里算薪税',
|
||||
description: '薪税页面提供加班费、双倍工资、社保公积金计算器和工资条管理,输入参数实时计算。',
|
||||
icon: Calculator,
|
||||
color: 'text-amber-600',
|
||||
bg: 'bg-amber-50',
|
||||
title: '薪酬管理',
|
||||
desc: '发薪批次、工资条、社保公积金、薪酬分析',
|
||||
path: '/money',
|
||||
},
|
||||
{
|
||||
icon: CalendarCheck,
|
||||
color: 'text-green-600',
|
||||
bg: 'bg-green-50',
|
||||
title: '考勤时间',
|
||||
desc: '考勤打卡、排班管理、休假审批',
|
||||
path: '/attendance',
|
||||
},
|
||||
{
|
||||
icon: ShieldAlert,
|
||||
color: 'text-red-600',
|
||||
bg: 'bg-red-50',
|
||||
title: '合规风控',
|
||||
desc: '风险中心、证据链、规章制度、用工体检',
|
||||
path: '/risk-center',
|
||||
},
|
||||
{
|
||||
icon: Bot,
|
||||
color: 'text-purple-600',
|
||||
bg: 'bg-purple-50',
|
||||
title: 'AI 助手',
|
||||
desc: '智能咨询、合同审查、判赔预测、人力分析',
|
||||
path: '/ai-assistant',
|
||||
},
|
||||
]
|
||||
|
||||
export function isOnboardingDismissed() {
|
||||
return localStorage.getItem(STORAGE_KEY) === '1'
|
||||
}
|
||||
|
||||
export function dismissOnboarding() {
|
||||
localStorage.setItem(STORAGE_KEY, '1')
|
||||
}
|
||||
|
||||
export function resetOnboarding() {
|
||||
localStorage.removeItem(STORAGE_KEY)
|
||||
}
|
||||
|
||||
export default function OnboardingGuide() {
|
||||
const [visible, setVisible] = useState(false)
|
||||
const [step, setStep] = useState(0)
|
||||
const [dontShow, setDontShow] = useState(false)
|
||||
const navigate = useNavigate()
|
||||
|
||||
useEffect(() => {
|
||||
const completed = localStorage.getItem(STORAGE_KEY)
|
||||
if (!completed) {
|
||||
if (!isOnboardingDismissed()) {
|
||||
setVisible(true)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const close = () => {
|
||||
localStorage.setItem(STORAGE_KEY, '1')
|
||||
if (dontShow) dismissOnboarding()
|
||||
setVisible(false)
|
||||
}
|
||||
|
||||
const goTo = (path: string) => {
|
||||
if (dontShow) dismissOnboarding()
|
||||
setVisible(false)
|
||||
navigate(path)
|
||||
}
|
||||
|
||||
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">
|
||||
<div className="bg-white rounded-xl shadow-xl max-w-lg w-full mx-4 overflow-hidden">
|
||||
<div className="flex items-center justify-between px-5 py-3 border-b border-gray-100">
|
||||
<h2 className="text-base font-semibold">欢迎使用企业用工专家</h2>
|
||||
<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 className="px-5 py-4">
|
||||
<p className="text-sm text-gray-500 mb-4">系统包含以下 6 大模块,点击任意模块可直接前往体验:</p>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{modules.map((m) => {
|
||||
const Icon = m.icon
|
||||
return (
|
||||
<button
|
||||
key={m.title}
|
||||
onClick={() => goTo(m.path)}
|
||||
className="flex items-start gap-3 p-3 rounded-lg border border-gray-100 hover:border-primary/30 hover:bg-primary/[0.02] transition-all text-left"
|
||||
>
|
||||
<div className={`flex items-center justify-center w-9 h-9 rounded-lg ${m.bg} ${m.color} shrink-0`}>
|
||||
<Icon className="w-4.5 h-4.5" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-sm font-medium text-gray-800">{m.title}</div>
|
||||
<div className="text-xs text-gray-500 mt-0.5 leading-relaxed">{m.desc}</div>
|
||||
</div>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between">
|
||||
{step > 0 ? (
|
||||
<button onClick={() => setStep(step - 1)} className="text-sm text-gray-500">上一步</button>
|
||||
) : <span />}
|
||||
<div className="mt-4 flex items-center justify-between">
|
||||
<label className="flex items-center gap-2 text-sm text-gray-500 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={dontShow}
|
||||
onChange={(e) => setDontShow(e.target.checked)}
|
||||
className="w-4 h-4 rounded border-gray-300 text-primary focus:ring-primary/10"
|
||||
/>
|
||||
不再自动显示
|
||||
</label>
|
||||
<button
|
||||
onClick={() => isLast ? close() : setStep(step + 1)}
|
||||
className="flex items-center gap-1 text-sm font-medium text-primary"
|
||||
onClick={close}
|
||||
className="flex items-center gap-1 px-4 py-2 text-sm font-medium text-white bg-primary rounded-lg hover:bg-primary/90 transition-colors"
|
||||
>
|
||||
{isLast ? '开始使用' : '下一步'}
|
||||
{!isLast && <ArrowRight className="w-4 h-4" />}
|
||||
开始使用
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { Link, useLocation } from 'react-router-dom'
|
||||
import { useState } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import clsx from 'clsx'
|
||||
import {
|
||||
LayoutDashboard, Users, CalendarCheck, UserX,
|
||||
@@ -16,6 +17,7 @@ import {
|
||||
Building2, CalendarDays, ClipboardList, Heart, CalendarClock,
|
||||
} from 'lucide-react'
|
||||
import Logo from '../ui/Logo'
|
||||
import { settingsApi } from '../../lib/api-services'
|
||||
|
||||
interface NavItem {
|
||||
path: string
|
||||
@@ -33,7 +35,7 @@ const navGroups: NavGroup[] = [
|
||||
title: '首页',
|
||||
items: [
|
||||
{ path: '/', label: '工作台', icon: LayoutDashboard },
|
||||
{ path: '/calendar', label: '日历', icon: CalendarDays },
|
||||
{ path: '/calendar', label: '工作日历', icon: CalendarDays },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -49,7 +51,7 @@ const navGroups: NavGroup[] = [
|
||||
title: '薪酬',
|
||||
items: [
|
||||
{ path: '/money', label: '薪税管理', icon: Calculator },
|
||||
{ path: '/social', label: '社保公积金', icon: Shield },
|
||||
{ path: '/social', label: '社公商保', icon: Shield },
|
||||
{ path: '/salary-dashboard', label: '薪酬分析', icon: BarChart3 },
|
||||
],
|
||||
},
|
||||
@@ -64,7 +66,7 @@ const navGroups: NavGroup[] = [
|
||||
title: '合规',
|
||||
items: [
|
||||
{ path: '/risk-center', label: '风险中心', icon: ShieldAlert },
|
||||
{ path: '/evidence', label: '证据链', icon: FileSearch },
|
||||
{ path: '/evidence', label: '证据链条', icon: FileSearch },
|
||||
{ path: '/policies', label: '规章制度', icon: FileText },
|
||||
{ path: '/tools/health-check', label: '用工体检', icon: Stethoscope },
|
||||
{ path: '/tools/medical-period', label: '医疗期', icon: HeartPulse },
|
||||
@@ -79,7 +81,7 @@ const navGroups: NavGroup[] = [
|
||||
{ path: '/notifications', label: '通知管理', icon: Bell },
|
||||
{ path: '/audit', label: '操作日志', icon: ScrollText },
|
||||
{ path: '/company-files', label: '公司文件', icon: Building2 },
|
||||
{ path: '/settings', label: '设置', icon: Settings },
|
||||
{ path: '/settings', label: '系统设置', icon: Settings },
|
||||
],
|
||||
},
|
||||
]
|
||||
@@ -89,13 +91,18 @@ const navGroups: NavGroup[] = [
|
||||
*/
|
||||
export default function SidebarNav({ mobileOpen, onClose }: { mobileOpen: boolean; onClose: () => void }) {
|
||||
const location = useLocation()
|
||||
const { data: orgData } = useQuery<any>({
|
||||
queryKey: ['org-settings'],
|
||||
queryFn: () => settingsApi.org(),
|
||||
staleTime: 300000,
|
||||
})
|
||||
const isActive = (path: string) => {
|
||||
if (path === '/') return location.pathname === '/'
|
||||
return location.pathname.startsWith(path)
|
||||
}
|
||||
const activeGroup = navGroups.find(g => g.items.some(item => isActive(item.path)))
|
||||
const [expandedGroups, setExpandedGroups] = useState<Set<string>>(
|
||||
new Set(activeGroup ? [activeGroup.title] : ['首页'])
|
||||
new Set(navGroups.map(g => g.title))
|
||||
)
|
||||
|
||||
const toggleGroup = (title: string) => {
|
||||
@@ -131,7 +138,7 @@ export default function SidebarNav({ mobileOpen, onClose }: { mobileOpen: boolea
|
||||
{/* Logo 区 */}
|
||||
<div className="h-14 flex items-center gap-2 px-4 border-b border-gray-200 shrink-0">
|
||||
<Logo className="w-5 h-5 text-primary" />
|
||||
<span className="font-bold text-sm text-gray-900">企业用工专家</span>
|
||||
<span className="font-bold text-sm text-gray-900 truncate">{orgData?.name || '企业用工专家'}</span>
|
||||
</div>
|
||||
|
||||
{/* 导航菜单 */}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ChevronDown, Settings as SettingsIcon, Bell, Menu, HelpCircle, Smartpho
|
||||
import { useState } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useAuthStore } from '../../store/authStore'
|
||||
import { dashboardApi, settingsApi } from '../../lib/api-services'
|
||||
import { dashboardApi } from '../../lib/api-services'
|
||||
import Breadcrumb from './Breadcrumb'
|
||||
import HelpModal from '../HelpModal'
|
||||
import PortalQRModal from '../PortalQRModal'
|
||||
@@ -22,11 +22,6 @@ export default function TopNav({ onMenuClick }: { onMenuClick?: () => void }) {
|
||||
queryFn: () => dashboardApi.data(),
|
||||
refetchInterval: 60000,
|
||||
})
|
||||
const { data: orgData } = useQuery<any>({
|
||||
queryKey: ['org-settings'],
|
||||
queryFn: () => settingsApi.org(),
|
||||
staleTime: 300000,
|
||||
})
|
||||
const riskCount = dashboardData?.riskSummary?.pending || 0
|
||||
|
||||
return (
|
||||
@@ -41,12 +36,6 @@ export default function TopNav({ onMenuClick }: { onMenuClick?: () => void }) {
|
||||
>
|
||||
<Menu className="w-5 h-5 text-gray-600" />
|
||||
</button>
|
||||
{orgData?.name && (
|
||||
<span className="hidden sm:inline text-sm font-medium text-gray-700 shrink-0">
|
||||
{orgData.name}
|
||||
</span>
|
||||
)}
|
||||
{orgData?.name && <span className="hidden sm:inline text-gray-300 shrink-0">|</span>}
|
||||
<Breadcrumb />
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user