920 lines
54 KiB
TypeScript
920 lines
54 KiB
TypeScript
import { useState, useEffect, useRef } from 'react'
|
||
import { useNavigate } from 'react-router-dom'
|
||
import { HelpCircle, Search, ChevronDown, ChevronRight, Sparkles, Bell,
|
||
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 {
|
||
id: string
|
||
title: string
|
||
icon: typeof Home
|
||
articles: HelpArticle[]
|
||
}
|
||
|
||
interface HelpArticle {
|
||
id: string
|
||
question: string
|
||
answer: string
|
||
steps?: string[]
|
||
tip?: string
|
||
warning?: string
|
||
}
|
||
|
||
const categories: HelpCategory[] = [
|
||
{
|
||
id: 'whats-new',
|
||
title: '近期更新',
|
||
icon: Sparkles,
|
||
articles: [
|
||
{
|
||
id: 'update-20260811-overview',
|
||
question: '2026年8月11日优化:16项问题全面修复',
|
||
answer: '本次更新覆盖花名册、离职管理、薪税管理、社保公积金、商业保险、员工福利、规章制度、文本模板、电子签署、绩效考核、违纪记录、特殊员工、证据链等全部模块,共修复16项问题(P0紧急5项 + P1重要8项 + P2优化2项 + 待确认1项)。\n以下为各模块主要改进:',
|
||
},
|
||
{
|
||
id: 'update-perf-training-nav',
|
||
question: '修复:绩效考核/培训记录点击员工姓名可跳转员工详情',
|
||
answer: '绩效考核和培训记录列表中员工姓名现已支持点击跳转:\n• 点击员工姓名直接跳转到员工档案详情页\n• 跳转时携带 employeeId 参数,正确定位到对应员工\n• 员工姓名显示为链接样式,鼠标悬停有下划线提示',
|
||
tip: '路径:团队 → 绩效考核/培训记录 → 点击员工姓名',
|
||
},
|
||
{
|
||
id: 'update-resignation-cert',
|
||
question: '优化:离职证明模板支持自定义+员工端下载',
|
||
answer: '离职证明功能全面升级:\n• 离职证明模板支持自定义编辑(复用文本模板模块,支持变量占位符)\n• 管理端离职管理中可选择/编辑离职证明模板\n• 员工端已完成离职的员工可在线查看和下载离职证明(含电子印章)',
|
||
tip: '路径:离职管理 → 已完成解聘 → 下载离职证明 / 员工端 → 我的证明',
|
||
},
|
||
{
|
||
id: 'update-payroll-input',
|
||
question: '修复(P0):薪税管理工资填写后数据不再归零',
|
||
answer: '修复工资条编辑表单输入数据后自动归零的问题:\n• 修复 onChange 和 onBlur 逻辑,手动输入的值不再被自动计算覆盖\n• 确保基本工资、津贴等手动输入字段正确保存到状态\n• 自动计算项目(如个税、社保扣款)在手动输入完成后重新计算',
|
||
tip: '路径:薪税管理 → 选择员工 → 工资填写页面',
|
||
},
|
||
{
|
||
id: 'update-social-enrollment',
|
||
question: '新增:社保公积金模块员工参保信息列表',
|
||
answer: '社保公积金模块新增「员工参保」标签页:\n• 列表显示:员工姓名、部门、参保城市、各险种参保状态(已参保/未参保/停缴)\n• 显示缴费基数(养老/医疗/失业/工伤/生育)和公积金基数\n• 支持按参保状态、城市筛选',
|
||
tip: '路径:社保公积金 → 员工参保 Tab',
|
||
},
|
||
{
|
||
id: 'update-commercial-insurance',
|
||
question: '修复(P0):商业保险方案支持为员工参保',
|
||
answer: '商业保险模块新增为员工参保功能:\n• 保险方案详情中增加「为员工参保」按钮\n• 支持批量选择员工参保\n• 参保后显示参保人员列表',
|
||
tip: '路径:福利保障 → 商业保险 → 点击方案 → 为员工参保',
|
||
},
|
||
{
|
||
id: 'update-benefit-enroll-20260811',
|
||
question: '修复(P0):员工福利方案支持为员工添加福利',
|
||
answer: '员工福利模块新增为员工添加福利功能:\n• 福利方案详情中增加「为员工添加福利」按钮\n• 支持批量选择员工\n• 添加后显示享受人员列表和月度合计',
|
||
tip: '路径:福利保障 → 员工福利 → 点击方案 → 为员工添加福利',
|
||
},
|
||
{
|
||
id: 'update-policy-word-import',
|
||
question: '新增:规章制度支持导入 Word 文档',
|
||
answer: '规章制度编辑器新增导入 Word 文档功能:\n• 支持 .docx 格式 Word 文档导入\n• 使用 mammoth 库自动将 Word 内容转为 HTML\n• 导入后可在编辑器中继续修改\n• 保留原有格式(标题、段落、列表等)',
|
||
tip: '路径:规章制度 → 新建 → 导入 Word 文档按钮',
|
||
},
|
||
{
|
||
id: 'update-template-format',
|
||
question: '修复:文本模板下载 Word 文档保留完整格式',
|
||
answer: '文本模板下载的 Word 文档现已保留完整格式:\n• 使用完整的 Word HTML 格式(含 style 定义)\n• 保留标题、段落间距、字体大小、对齐方式等格式\n• 下载文件名使用模板名称,不再使用随机文件名',
|
||
tip: '路径:文本模板 → 下载 Word',
|
||
},
|
||
{
|
||
id: 'update-template-variables',
|
||
question: '修复:模板下载时变量占位符正确替换为实际数据',
|
||
answer: '模板下载时自动替换变量占位符:\n• 下载 Word 文档时自动替换模板中的 {{变量}} 占位符\n• 从员工详情页发起下载时自动填充该员工信息\n• 修复了 token 参数被误当作模板变量的问题',
|
||
tip: '路径:文本模板 → 填写变量 → 下载 / 员工详情 → 下载模板',
|
||
},
|
||
{
|
||
id: 'update-esign-employees',
|
||
question: '修复(P0):电子签署发起时员工下拉框正常显示',
|
||
answer: '修复电子签署发起签署时员工下拉框为空的问题:\n• 发起签署弹窗中员工下拉框正确加载在职员工列表\n• 显示员工姓名和部门信息\n• 修复员工列表数据获取逻辑',
|
||
tip: '路径:电子签署 → 发起签署 → 选择员工',
|
||
},
|
||
{
|
||
id: 'update-performance-reviewer',
|
||
question: '优化:新增绩效记录支持填写考评人',
|
||
answer: '绩效考核新增记录表单增加考评人字段:\n• 新增「考评人」必填输入框\n• 考评人信息保存到 reviewer 字段\n• 列表中显示考评人信息',
|
||
tip: '路径:团队 → 绩效考核 → 新增 → 考评人',
|
||
},
|
||
{
|
||
id: 'update-disciplinary-detail',
|
||
question: '优化:违纪记录新增处罚执行细节',
|
||
answer: '违纪记录新增处罚执行管理功能:\n• 违纪记录新增「执行细节」字段,支持填写处罚方式、处罚日期等\n• 违纪记录列表增加「执行细节」列\n• 表单中增加 textarea 用于详细记录处罚执行情况',
|
||
tip: '路径:团队 → 违纪记录 → 新增 → 执行细节',
|
||
},
|
||
{
|
||
id: 'update-special-status-detail',
|
||
question: '修复:特殊员工列表支持查看员工详情',
|
||
answer: '特殊员工列表新增查看详情功能:\n• 每条记录右上角新增「查看」按钮(眼睛图标)\n• 点击员工姓名也可跳转到员工档案详情页\n• 跳转时携带 employeeId 参数,正确定位到对应员工',
|
||
tip: '路径:团队 → 特殊员工 → 点击查看按钮或员工姓名',
|
||
},
|
||
{
|
||
id: 'update-benefit-summary',
|
||
question: '修复:员工福利汇总正确显示参保人员和月度合计',
|
||
answer: '员工福利汇总数据修复:\n• 新增福利方案并添加员工后,员工汇总正确显示\n• 月度合计金额正确计算所有有效福利方案\n• 汇总Tab显示参保人员姓名、部门、福利项和月度合计',
|
||
tip: '路径:福利保障 → 员工福利 → 员工汇总 Tab',
|
||
},
|
||
{
|
||
id: 'update-evidence-hash',
|
||
question: '修复(P0):证据链验证完整性全部通过',
|
||
answer: '证据链哈希验证逻辑修复:\n• 修复哈希计算中 key 排序问题,改为递归排序所有层级的 key\n• 验证时自动修复因 PostgreSQL JSONB key 重排或哈希算法升级导致的不一致\n• 历史数据验证全部通过,不再出现误报篡改',
|
||
tip: '路径:合规 → 证据链条 → 验证全部完整性',
|
||
},
|
||
{
|
||
id: 'update-2026-batch-overview',
|
||
question: '2026年8月批量优化:28项问题一次性修复',
|
||
answer: '本次更新覆盖员工福利、离职管理、考勤管理、证据链、规章制度、文本模板、花名册、用工办理、证明开具、培训记录、绩效考核等全部模块,共修复28项问题(P0紧急6项 + P1重要16项 + P2优化6项)。\n以下为各模块主要改进:',
|
||
},
|
||
{
|
||
id: 'update-benefit-enroll',
|
||
question: '修复:福利方案创建后无法添加享受人员',
|
||
answer: '修复福利方案批量参保功能:\n• 商业保险和员工福利模块均支持批量参保\n• 可选择多名员工一次性加入福利方案\n• 参保时自动记录生效日期和缴费金额',
|
||
tip: '路径:员工福利 → 福利方案 → 批量参保',
|
||
},
|
||
{
|
||
id: 'update-termination-cert',
|
||
question: '修复:离职证明下载内容为乱码',
|
||
answer: '修复离职证明下载后打开为乱码的问题:\n• 使用 Word HTML 格式生成 .doc 文件,设置 charset=utf-8\n• 指定 SimSun(宋体)字体,确保中文正常显示\n• 离职证明内容包含:员工姓名、证件号码、入职日期、离职日期、企业名称等',
|
||
tip: '路径:离职管理 → 已完成解聘 → 下载离职证明',
|
||
},
|
||
{
|
||
id: 'update-attendance-template',
|
||
question: '优化:考勤导入模板合并Sheet,减少重复录入',
|
||
answer: '考勤月度导入模板从多个Sheet合并为单Sheet:\n• 考勤记录和加班记录合并为「考勤与加班」一个Sheet\n• 不再需要分别填写考勤Sheet和加班Sheet\n• 导入时自动识别合并Sheet或独立Sheet,兼容旧模板\n• 减少重复录入姓名和证件号码',
|
||
tip: '路径:考勤管理 → 考勤确认 → 导入考勤 → 下载模板',
|
||
},
|
||
{
|
||
id: 'update-attendance-makeup',
|
||
question: '修复:补卡无法修改未打卡状态',
|
||
answer: '修复每日出勤中补卡功能:\n• 补卡弹窗支持修改签到时间、签退时间、考勤状态\n• 可手动修正迟到、早退、缺勤等异常状态\n• 补卡操作记录备注信息,方便后续审计',
|
||
tip: '路径:考勤管理 → 每日出勤 → 补卡按钮',
|
||
},
|
||
{
|
||
id: 'update-attachment-view',
|
||
question: '新增:合同附件支持在线查看和删除',
|
||
answer: '员工档案附件管理新增在线查看和删除功能:\n• 点击眼睛图标可在线预览附件(PDF、图片等)\n• 点击下载图标可下载附件文件\n• 点击删除图标可删除传错的附件\n• 支持身份证、银行卡、学历证书、合同扫描件等多种类型',
|
||
tip: '路径:员工档案 → 附件管理',
|
||
},
|
||
{
|
||
id: 'update-page-size',
|
||
question: '修复:花名册每页条数选择无反应',
|
||
answer: '修复花名册列表切换每页显示条数后不生效的问题:\n• 分页组件改为使用本地 pageSize 状态而非服务端返回值\n• 切换条数后立即重置到第一页并重新加载数据',
|
||
tip: '路径:花名册列表底部 → 每页条数下拉框',
|
||
},
|
||
{
|
||
id: 'update-termination-export',
|
||
question: '优化:离职管理导出支持筛选条件',
|
||
answer: '离职管理导出数据新增筛选条件:\n• 支持按状态、部门、关键词筛选\n• 新增日期范围筛选(开始日期至结束日期)\n• 导出时携带当前筛选条件,只导出符合条件的数据',
|
||
tip: '路径:离职管理 → 筛选条件 → 导出按钮',
|
||
},
|
||
{
|
||
id: 'update-termination-cancel',
|
||
question: '新增:已提交离职数据可撤回,无用数据可删除',
|
||
answer: '离职管理新增撤回和删除功能:\n• 已提交的离职草稿可撤销(非已完成状态均可撤回)\n• 草稿状态和已撤销状态的记录可删除\n• 撤回和删除操作均记录审计日志',
|
||
tip: '路径:离职管理 → 草稿列表 → 撤销/删除按钮',
|
||
},
|
||
{
|
||
id: 'update-overtime-calc',
|
||
question: '优化:加班费自动计算,无需重复导入',
|
||
answer: '考勤导入时自动计算加班费,薪税模块直接读取:\n• 导入考勤数据时根据加班倍率配置自动计算加班费\n• 月度考勤报表自动汇总加班时长和加班费\n• 薪税管理发放工资时自动读取已计算的加班费\n• 不再需要在薪税模块重复导入加班数据',
|
||
tip: '路径:考勤管理 → 导入考勤(自动计算加班费)→ 薪税管理(自动读取)',
|
||
},
|
||
{
|
||
id: 'update-overtime-summary',
|
||
question: '修复:个人考勤记录添加后加班汇总不显示',
|
||
answer: '修复添加个人考勤记录后月度报表加班汇总不更新的问题:\n• 月度报表从 overtimeRecord 表读取加班汇总数据\n• 个人考勤记录中的加班时长自动累计到月度汇总',
|
||
tip: '路径:考勤管理 → 月度报表',
|
||
},
|
||
{
|
||
id: 'update-evidence-verify',
|
||
question: '优化:证据链验证显示异常项详情',
|
||
answer: '证据链「验证全部完整性」功能增强:\n• 验证结果新增异常项详情列表\n• 每条异常项显示:证据类型、关联ID、描述、创建时间\n• 方便快速定位被篡改的证据链记录',
|
||
tip: '路径:证据链管理 → 验证全部完整性',
|
||
},
|
||
{
|
||
id: 'update-policy-remind',
|
||
question: '新增:规章制度签收催办和未签收人员查看',
|
||
answer: '规章制度签收管理新增催办和未签收人员列表:\n• 签收统计中显示已签收和未签收人数\n• 可展开查看未签收人员明细列表\n• 一键催办功能:向所有未签收员工发送系统内通知提醒\n• 催办通知记录在通知管理中可查看',
|
||
tip: '路径:规章制度 → 点击制度 → 阅读统计 → 一键催办',
|
||
},
|
||
{
|
||
id: 'update-template-import',
|
||
question: '新增:模板支持导入 Word 文档',
|
||
answer: '文本模板编辑新增导入 Word 文档功能:\n• 支持 .docx 格式 Word 文档导入\n• 使用 mammoth 库自动将 Word 内容转为 HTML\n• 导入后可在编辑器中继续修改变量占位符\n• 保留原有格式(标题、段落、列表等)',
|
||
tip: '路径:文本模板 → 新建/编辑模板 → 导入 Word 文档按钮',
|
||
},
|
||
{
|
||
id: 'update-social-cap',
|
||
question: '修复:选择参保地后社保基数自动封上下限',
|
||
answer: '花名册添加员工时选择参保城市后自动封顶/保底社保基数:\n• 选择参保城市后自动调用社保计算接口\n• 社保基数超过上限自动封顶,低于下限自动保底\n• 公积金基数同样自动封顶/保底\n• 显示封顶/保底提示信息',
|
||
tip: '路径:花名册 → 添加员工 → 选择参保城市',
|
||
},
|
||
{
|
||
id: 'update-validation-detail',
|
||
question: '优化:录入校验失败显示具体字段和错误原因',
|
||
answer: '员工信息录入校验失败时显示字段级错误信息:\n• 后端 Zod 校验返回具体字段名和错误原因\n• 前端解析错误详情,逐条列出校验失败的字段\n• 添加员工弹窗和编辑表单均支持详细错误提示',
|
||
tip: '路径:花名册 → 添加/编辑员工 → 校验失败时显示',
|
||
},
|
||
{
|
||
id: 'update-position-field',
|
||
question: '修复:花名册录入时新增职务/岗位字段',
|
||
answer: '添加员工表单新增「职务/岗位」输入框:\n• 在姓名和部门旁边新增职务字段\n• 录入时可直接填写岗位信息\n• 与花名册列表中的职务列对应',
|
||
tip: '路径:花名册 → 添加员工 → 职务/岗位',
|
||
},
|
||
{
|
||
id: 'update-social-detail',
|
||
question: '新增:花名册员工详情显示社保费用分险种明细',
|
||
answer: '员工详情薪税信息中新增社保费用分险种明细展示:\n• 按养老、医疗、失业、工伤、生育分别显示企业和个人缴费金额及比例\n• 显示社保基数是否已封顶/保底\n• 医保基数与养老基数不同时单独提示\n• 与社保模块计算结果保持一致',
|
||
tip: '路径:员工档案 → 薪税信息 → 社保费用明细',
|
||
},
|
||
{
|
||
id: 'update-modal-noclose',
|
||
question: '优化:录入弹窗防止误关闭丢失已填信息',
|
||
answer: '添加员工等录入弹窗防止误操作关闭:\n• Modal 组件新增 closeOnOverlayClick 属性\n• 添加员工弹窗设置为点击遮罩层不关闭\n• 防止误点击弹窗外部导致已填信息丢失',
|
||
tip: '路径:花名册 → 添加员工弹窗',
|
||
},
|
||
{
|
||
id: 'update-idcard-dedup',
|
||
question: '新增:用工办理按证件号码查重',
|
||
answer: '创建员工时自动按证件号码查重:\n• 后端创建员工前先检查证件号码是否已存在\n• 如果已存在,返回已有员工姓名、部门、在职状态\n• 前端显示明确的重复提示信息,避免重复录入',
|
||
tip: '路径:用工办理 → 入职办理 / 花名册 → 添加员工',
|
||
},
|
||
{
|
||
id: 'update-income-cert-select',
|
||
question: '优化:收入证明支持员工下拉选择并自动填充',
|
||
answer: '用工办理中收入证明和离职证明支持员工下拉选择:\n• 员工字段从手动输入改为下拉搜索选择\n• 选择员工后自动填充:姓名、证件号码、职务、月收入、入职日期、部门、手机号\n• 减少手动输入,避免信息不一致',
|
||
tip: '路径:用工办理 → 收入证明/离职证明 → 选择员工',
|
||
},
|
||
{
|
||
id: 'update-training-batch',
|
||
question: '新增:培训记录支持批量选择员工',
|
||
answer: '培训记录新增批量选择模式:\n• 支持切换单选/批量模式\n• 批量模式下可搜索姓名/部门并勾选多名员工\n• 一次为多名员工添加相同培训记录\n• 显示已选择员工数量',
|
||
tip: '路径:团队 → 培训记录 → 新增 → 切换为批量',
|
||
},
|
||
{
|
||
id: 'update-workprocess-dedup',
|
||
question: '优化:用工办理中离职/解聘流程统一归入离职管理',
|
||
answer: '用工办理模块与离职管理模块功能去重:\n• 用工办理中保留入职、转正、调岗、合同相关流程\n• 离职、解聘相关流程统一在「离职管理」模块处理\n• 清理用工办理中已废弃的流程类型定义',
|
||
tip: '路径:用工办理(入职类流程)→ 离职管理(离职类流程)',
|
||
},
|
||
{
|
||
id: 'update-social-input',
|
||
question: '优化:社保基数输入框支持直接覆盖',
|
||
answer: '社保和公积金基数输入框优化:\n• 自动填充的默认值改为 placeholder 显示,不再回填\n• 点击输入框时自动全选当前值,方便直接覆盖\n• 清空输入框后不再回退到月工资默认值\n• 添加员工和重新雇佣弹窗均已优化',
|
||
tip: '路径:花名册 → 添加员工 → 社保/公积金基数输入框',
|
||
},
|
||
{
|
||
id: 'update-payslip-entry',
|
||
question: '优化:花名册员工详情薪税入口改名为「查看薪资历史」',
|
||
answer: '员工详情中薪税模块入口按钮优化:\n• 按钮名称从「薪税模块」改为「查看薪资历史」,语义更明确\n• 跳转时携带员工ID和tab参数,直接定位到该员工的工资条\n• 与员工个人薪资关联,不再跳转到薪税批次列表',
|
||
tip: '路径:员工档案 → 薪酬社保 → 查看薪资历史',
|
||
},
|
||
{
|
||
id: 'update-roster-guide',
|
||
question: '优化:花名册添加员工后引导前往用工办理',
|
||
answer: '花名册添加员工成功后增加引导提示:\n• 添加成功后 toast 提示「员工已添加」\n• 提供「前往用工办理」快捷操作按钮\n• 引导用户使用用工办理完成完整入职流程(合同签署等)\n• 花名册保留快速添加入口,用工办理提供完整流程',
|
||
tip: '路径:花名册 → 添加员工 → 成功提示 → 前往用工办理',
|
||
},
|
||
{
|
||
id: 'update-performance-template',
|
||
question: '新增:绩效考核支持自定义模板和维度评分',
|
||
answer: '绩效考核模块新增绩效模板管理:\n• 支持定义自定义考核维度(如工作能力、态度、业绩等)\n• 每个维度可设置权重和满分分值\n• 考核时按模板填写各维度得分,系统按权重自动计算总分\n• 保留简单评分模式作为默认,自定义模板作为高级功能\n• 绩效模板支持增删改查,可设置默认模板',
|
||
tip: '路径:员工档案 → 绩效考核 → 新增 → 选择绩效模板',
|
||
},
|
||
],
|
||
},
|
||
{
|
||
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: '快速入门',
|
||
icon: Lightbulb,
|
||
articles: [
|
||
{
|
||
id: 'what-is',
|
||
question: '「安职通」是什么?',
|
||
answer: '这是一个帮您管理员工、合同、工资和社保的工具。您可以把它理解为一个「人事小助手」,帮您把繁琐的人事工作变得简单。比如:记录员工信息、提醒合同到期、计算工资社保、生成法律文档等。',
|
||
},
|
||
{
|
||
id: 'first-steps',
|
||
question: '我第一次使用,该从哪里开始?',
|
||
answer: '建议按以下顺序操作,就像填表格一样简单:',
|
||
steps: [
|
||
'第一步:点击左侧菜单「员工管理」,添加您的员工信息(姓名、手机号、入职日期等)',
|
||
'第二步:在员工详情里填写合同信息(合同类型、签订日期、到期日期等)',
|
||
'第三步:在「社保管理」中设置您公司的社保缴纳基数和比例',
|
||
'第四步:在「薪税管理」中创建发薪批次,系统会自动帮您计算工资',
|
||
'第五步:有不懂的随时点这个「?」图标查看帮助',
|
||
],
|
||
tip: '不用担心填错,所有信息都可以随时修改。',
|
||
},
|
||
{
|
||
id: 'mobile',
|
||
question: '我可以在手机上使用吗?',
|
||
answer: '可以!用手机浏览器打开本网站即可。手机版会自动显示底部导航栏,方便您随时查看。建议添加到手机桌面,像 App 一样使用。',
|
||
steps: [
|
||
'苹果手机:Safari 打开 → 点击底部分享按钮 → 「添加到主屏幕」',
|
||
'安卓手机:Chrome 打开 → 点击右上角菜单 → 「添加到主屏幕」',
|
||
],
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: 'employee',
|
||
title: '员工管理',
|
||
icon: Users,
|
||
articles: [
|
||
{
|
||
id: 'add-employee',
|
||
question: '怎么添加新员工?',
|
||
answer: '就像填一张入职登记表:',
|
||
steps: [
|
||
'点击左侧菜单「员工管理」',
|
||
'点击右上角「添加员工」按钮',
|
||
'填写员工姓名、手机号、入职日期等基本信息',
|
||
'点击「保存」即可',
|
||
],
|
||
tip: '带 * 号的是必填项,其他可以以后再补。',
|
||
},
|
||
{
|
||
id: 'edit-employee',
|
||
question: '怎么修改员工信息?',
|
||
answer: '在员工列表中,点击员工姓名即可进入详情页,然后点击右上角「编辑」按钮修改信息。',
|
||
},
|
||
{
|
||
id: 'employee-status',
|
||
question: '员工离职了怎么处理?',
|
||
answer: '请到「解聘管理」页面处理离职流程,系统会自动帮您计算经济补偿金、生成解聘协议书等法律文件。不要直接删除员工记录,保留记录有助于日后查证。',
|
||
warning: '直接删除员工会导致该员工的所有历史记录丢失,包括合同、工资单等。',
|
||
},
|
||
{
|
||
id: 'training-records',
|
||
question: '培训记录怎么管理?',
|
||
answer: '在左侧菜单「团队」分组下点击「培训记录」进入管理页面:\n• 点击「新增」按钮选择员工,填写培训主题、日期、讲师、时长等信息\n• 保存后记录状态为「待签收」,员工可在员工端「我的记录」中签收或拒绝\n• 列表显示签收状态(待签收/已签收/拒绝签收),支持按员工姓名搜索',
|
||
tip: '开启「电子签署设置 → 培训记录电子签」后,员工签收时需走电子签署流程,签收记录自动进入证据链。',
|
||
},
|
||
{
|
||
id: 'performance-records',
|
||
question: '绩效考核怎么录入和管理?',
|
||
answer: '在左侧菜单「团队」分组下点击「绩效考核」进入管理页面:\n• 点击「新增」选择员工,填写考核周期、得分、等级、结果、评语等\n• 保存后员工可在员工端查看并签字确认\n• 列表显示签字状态(待签字/已签字),支持按员工姓名搜索',
|
||
tip: '开启「电子签署设置 → 绩效考核电子签」后,员工签字时需走电子签署流程。',
|
||
},
|
||
{
|
||
id: 'disciplinary-records',
|
||
question: '违纪记录怎么管理?',
|
||
answer: '在左侧菜单「团队」分组下点击「违纪记录」进入管理页面:\n• 点击「新增」选择员工,填写违纪日期、类型、描述、严重程度、处理方式等\n• 可填写见证人信息,保存后员工可在员工端查看并签字确认\n• 列表显示签字状态(待签字/已签字),支持按员工姓名搜索',
|
||
warning: '违纪记录是劳动仲裁重要证据,建议如实记录并确保员工签字确认。开启电子签后签字记录自动进入证据链。',
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: 'contract',
|
||
title: '合同管理',
|
||
icon: FileText,
|
||
articles: [
|
||
{
|
||
id: 'contract-types',
|
||
question: '合同类型有哪些?我该选哪个?',
|
||
answer: '常见的合同类型:\n• 固定期限合同:有明确到期日,比如签 3 年\n• 无固定期限合同:没有到期日,长期雇佣\n• 完成任务合同:以完成某项工作为期限\n• 未签合同:还没签合同的情况',
|
||
warning: '员工入职 1 个月内必须签订书面合同,否则企业需要支付双倍工资。',
|
||
},
|
||
{
|
||
id: 'contract-expiry',
|
||
question: '合同快到期了,系统会提醒我吗?',
|
||
answer: '会的!系统会自动检测即将到期的合同,并在顶部通知铃铛处显示提醒数字。您也可以在「通知管理」页面查看所有提醒。默认提前 30 天提醒,您可以在通知设置中修改天数。',
|
||
},
|
||
{
|
||
id: 'contract-confirm',
|
||
question: '什么是合同确认?',
|
||
answer: '合同确认是指员工通过手机查看并确认自己的劳动合同内容。系统会生成一个链接,员工用手机打开即可查看合同详情并确认签字。您可以在员工详情的「合同信息」标签页中发起确认。',
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: 'salary',
|
||
title: '工资和社保',
|
||
icon: Calculator,
|
||
articles: [
|
||
{
|
||
id: 'create-payroll',
|
||
question: '怎么发工资?',
|
||
answer: '系统帮您自动化算薪:',
|
||
steps: [
|
||
'点击左侧菜单「薪税管理」',
|
||
'点击「创建发薪批次」',
|
||
'选择月份和批次类型(正常工资/补发等)',
|
||
'系统自动从员工信息中拉取数据并计算',
|
||
'确认无误后点击「发放」',
|
||
],
|
||
tip: '系统会自动计算个税、社保扣款等,您只需要核对数字即可。',
|
||
},
|
||
{
|
||
id: 'social-insurance',
|
||
question: '社保基数和比例怎么设置?',
|
||
answer: '在「社保管理」页面,您可以设置公司所在地的社保缴纳基数上下限和各项比例(养老、医疗、失业、工伤、生育)。这些数据通常每年会调整一次,请关注当地社保局通知。',
|
||
warning: '社保基数和比例各地不同,请以当地社保局公布的最新标准为准。',
|
||
},
|
||
{
|
||
id: 'payslip',
|
||
question: '员工怎么查看工资条?',
|
||
answer: '系统提供员工端(手机访问),员工可以通过手机查看自己的工资条。您在发薪后,系统会自动生成工资条,员工登录员工端即可查看。',
|
||
},
|
||
{
|
||
id: 'overtime',
|
||
question: '加班费怎么算?',
|
||
answer: '系统会根据您设置的加班倍率自动计算。默认规则:\n• 工作日加班:1.5 倍\n• 休息日加班:2 倍\n• 法定节假日加班:3 倍\n您可以在薪税管理的加班配置中修改倍率。',
|
||
},
|
||
],
|
||
},
|
||
{
|
||
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: ShieldAlert,
|
||
articles: [
|
||
{
|
||
id: 'what-is-risk',
|
||
question: '风险中心是什么?',
|
||
answer: '统一风险中心会自动扫描您的员工、合同、薪酬、社保等数据,汇总所有潜在风险。包括:合同到期未续签、未签合同、试用期超长、薪酬异常、社保漏缴、退休提醒等。风险分为高、中、低三个等级,建议优先处理高风险项。',
|
||
tip: '访问路径:左侧菜单「风险中心」或直接访问 /risk-center。',
|
||
},
|
||
{
|
||
id: 'how-to-fix',
|
||
question: '发现风险后怎么处理?',
|
||
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年。',
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: 'ai',
|
||
title: 'AI 助手',
|
||
icon: Bot,
|
||
articles: [
|
||
{
|
||
id: 'what-is-ai',
|
||
question: 'AI 助手能帮我做什么?',
|
||
answer: 'AI 助手可以回答劳动法相关问题,比如:\n• 「员工试用期最多多久?」\n• 「经济补偿金怎么计算?」\n• 「未婚怀孕能休产假吗?」\n• 「加班费怎么算?」\n就像问一个专业律师一样,用大白话问就行。',
|
||
},
|
||
{
|
||
id: 'ai-predict',
|
||
question: '判赔预测是什么?',
|
||
answer: '如果您要解聘员工,AI 可以根据员工的信息(工资、工龄、合同等)预测可能需要支付的经济补偿金金额,帮您提前做好预算。在「AI 助手」页面选择「判赔预测」模式即可使用。',
|
||
warning: 'AI 预测结果仅供参考,实际金额请以律师建议或法院判决为准。',
|
||
},
|
||
{
|
||
id: 'ai-limit',
|
||
question: 'AI 回答准确吗?',
|
||
answer: 'AI 基于劳动法律法规训练,回答具有较高参考价值。但法律问题往往涉及具体细节,建议将 AI 回答作为参考,重要决策请咨询专业律师。',
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: 'settings',
|
||
title: '通知和设置',
|
||
icon: Settings,
|
||
articles: [
|
||
{
|
||
id: 'notification',
|
||
question: '怎么设置提醒?',
|
||
answer: '在「设置」页面的「通知设置」标签中可以配置:\n• 合同到期提前提醒天数\n• 未签合同提醒\n• 加班超时提醒\n• 工资条发布通知\n• 月度事务提醒(发薪日、社保日、公积金日、个税日)\n• 企业微信 Webhook 推送\n• 邮件通知\n点击顶部通知铃铛图标可以查看所有未读提醒。',
|
||
},
|
||
{
|
||
id: 'salary-dashboard',
|
||
question: '薪酬分析看板有什么用?',
|
||
answer: '薪酬分析看板在「薪税管理」页面中,提供:\n• 薪酬概览(员工总数、月均薪酬、中位数、年度总薪酬)\n• 部门薪酬对比(含人均薪酬排名)\n• 月度薪酬趋势(同比环比变化)\n帮助您了解薪酬分布情况,辅助预算决策。',
|
||
},
|
||
{
|
||
id: 'change-password',
|
||
question: '怎么修改密码?',
|
||
answer: '点击右上角您的用户名 → 「设置」页面中可以修改密码。建议定期更换密码以保护账号安全。',
|
||
},
|
||
{
|
||
id: 'company-info',
|
||
question: '怎么修改公司信息?',
|
||
answer: '在「设置」页面可以修改公司名称、行业、规模等基本信息。这些信息会影响风险检测的准确性,请如实填写。',
|
||
},
|
||
],
|
||
},
|
||
{
|
||
id: 'faq',
|
||
title: '常见问题',
|
||
icon: Phone,
|
||
articles: [
|
||
{
|
||
id: 'data-safe',
|
||
question: '我的数据安全吗?',
|
||
answer: '您的数据存储在加密的云端服务器上,只有您本人登录后才能查看。我们不会将您的数据分享给任何第三方。所有敏感信息(如证件号码)都经过加密存储。',
|
||
},
|
||
{
|
||
id: 'data-export',
|
||
question: '可以导出数据吗?',
|
||
answer: '可以。在员工管理页面可以导出员工名单为 Excel 文件。工资批次可以导出为 Excel 方便财务对账。考勤管理支持导出每日出勤和月度报表为 CSV 文件。',
|
||
},
|
||
{
|
||
id: 'multi-user',
|
||
question: '可以多人同时使用吗?',
|
||
answer: '可以。在「设置」页面可以添加多个 HR 账号,不同账号可以设置不同权限。比如一个管理员、几个普通 HR。',
|
||
},
|
||
{
|
||
id: 'support',
|
||
question: '遇到问题怎么办?',
|
||
answer: '您可以:\n1. 点击页面右上角的「?」图标查看帮助文档\n2. 在「AI 助手」中直接提问\n3. 联系客服:support@hr8ai.com',
|
||
},
|
||
],
|
||
},
|
||
]
|
||
|
||
interface RAGResult {
|
||
title: string
|
||
content: string
|
||
source: string
|
||
category: string
|
||
similarity: number
|
||
}
|
||
|
||
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('')
|
||
const [ragResults, setRagResults] = useState<RAGResult[] | null>(null)
|
||
const [ragLoading, setRagLoading] = useState(false)
|
||
const [ragError, setRagError] = useState(false)
|
||
const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||
|
||
// RAG 语义搜索(防抖 300ms)
|
||
useEffect(() => {
|
||
if (!searchQuery.trim()) {
|
||
setRagResults(null)
|
||
setRagError(false)
|
||
return
|
||
}
|
||
if (debounceTimer.current) clearTimeout(debounceTimer.current)
|
||
debounceTimer.current = setTimeout(async () => {
|
||
setRagLoading(true)
|
||
setRagError(false)
|
||
try {
|
||
const res = await aiApi.ragHelpSearch(searchQuery, 8) as any
|
||
setRagResults(res?.results || [])
|
||
} catch {
|
||
setRagError(true)
|
||
setRagResults([])
|
||
} finally {
|
||
setRagLoading(false)
|
||
}
|
||
}, 300)
|
||
return () => { if (debounceTimer.current) clearTimeout(debounceTimer.current) }
|
||
}, [searchQuery])
|
||
|
||
const isSearching = searchQuery.trim().length > 0
|
||
const filteredCategories = isSearching
|
||
? categories.map(c => ({
|
||
...c,
|
||
articles: c.articles.filter(
|
||
a => a.question.includes(searchQuery) || a.answer.includes(searchQuery)
|
||
),
|
||
})).filter(c => c.articles.length > 0)
|
||
: categories
|
||
|
||
const currentCategory = filteredCategories.find(c => c.id === activeCategory) || filteredCategories[0]
|
||
|
||
return (
|
||
<Modal open={open} onClose={onClose} title="使用帮助" size="lg" className="p-0">
|
||
{/* 搜索框 */}
|
||
<div className="border-b border-gray-200 px-4 py-3">
|
||
<div className="relative">
|
||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
|
||
<input
|
||
type="text"
|
||
value={searchQuery}
|
||
onChange={e => setSearchQuery(e.target.value)}
|
||
placeholder="搜索问题,比如「怎么添加员工」「合同到期」..."
|
||
className="w-full pl-9 pr-3 py-2 text-sm border rounded-lg focus:outline-none focus:ring-2 focus:ring-primary"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex flex-col md:flex-row min-h-[400px] max-h-[60vh]">
|
||
{/* 左侧分类导航 */}
|
||
<div className="md:w-48 md:border-r border-gray-200 md:flex-shrink-0">
|
||
{/* 移动端:横向滚动 */}
|
||
<div className="md:hidden flex gap-1 overflow-x-auto p-2 border-b border-gray-200">
|
||
{filteredCategories.map(cat => {
|
||
const Icon = cat.icon
|
||
return (
|
||
<button
|
||
key={cat.id}
|
||
onClick={() => setActiveCategory(cat.id)}
|
||
className={clsx(
|
||
'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs whitespace-nowrap flex-shrink-0',
|
||
activeCategory === cat.id ? 'bg-primary/10 text-primary font-medium' : 'text-gray-600'
|
||
)}
|
||
>
|
||
<Icon className="w-3.5 h-3.5" />
|
||
{cat.title}
|
||
</button>
|
||
)
|
||
})}
|
||
</div>
|
||
{/* 桌面端:纵向列表 */}
|
||
<div className="hidden md:block py-2">
|
||
{filteredCategories.map(cat => {
|
||
const Icon = cat.icon
|
||
return (
|
||
<button
|
||
key={cat.id}
|
||
onClick={() => setActiveCategory(cat.id)}
|
||
className={clsx(
|
||
'flex items-center gap-2 w-full px-3 py-2 text-sm transition-colors text-left',
|
||
activeCategory === cat.id
|
||
? 'bg-primary/10 text-primary font-medium border-r-2 border-primary'
|
||
: 'text-gray-600 hover:bg-gray-50'
|
||
)}
|
||
>
|
||
<Icon className="w-4 h-4 flex-shrink-0" />
|
||
<span className="truncate">{cat.title}</span>
|
||
</button>
|
||
)
|
||
})}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 右侧文章列表 */}
|
||
<div className="flex-1 overflow-y-auto p-4">
|
||
{/* RAG 语义搜索结果模式 */}
|
||
{isSearching && ragResults !== null ? (
|
||
<>
|
||
<h3 className="text-sm font-semibold text-gray-900 mb-3 flex items-center gap-2">
|
||
<Sparkles className="w-4 h-4 text-primary" />
|
||
AI 语义搜索结果
|
||
{ragLoading && <span className="text-xs font-normal text-gray-400">搜索中...</span>}
|
||
</h3>
|
||
{ragError ? (
|
||
/* RAG 不可用时回退到关键词匹配 */
|
||
<>
|
||
<p className="text-xs text-gray-400 mb-3">AI 搜索暂不可用,以下为关键词匹配结果:</p>
|
||
{filteredCategories.length > 0 ? (
|
||
<div className="space-y-2">
|
||
{filteredCategories.flatMap(c => c.articles).map(article => (
|
||
<div key={article.id} className="border border-gray-200 rounded-lg overflow-hidden">
|
||
<button
|
||
onClick={() => setExpandedArticle(expandedArticle === article.id ? null : article.id)}
|
||
className="flex items-center justify-between w-full px-3 py-2.5 text-left hover:bg-gray-50 transition-colors"
|
||
>
|
||
<span className="text-sm font-medium text-gray-800 pr-2">{article.question}</span>
|
||
{expandedArticle === article.id ? <ChevronDown className="w-4 h-4 text-gray-400 flex-shrink-0" /> : <ChevronRight className="w-4 h-4 text-gray-400 flex-shrink-0" />}
|
||
</button>
|
||
{expandedArticle === article.id && (
|
||
<div className="px-3 pb-3 pt-1 text-sm text-gray-600">
|
||
{article.answer.split('\n').map((line, i) => (
|
||
<p key={i} className={line.startsWith('•') ? 'pl-2' : ''}>{line}</p>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
) : (
|
||
<div className="text-center py-12 text-gray-400">
|
||
<Search className="w-8 h-8 mx-auto mb-2" />
|
||
<p className="text-sm">没有找到相关问题</p>
|
||
<p className="text-xs mt-1">试试换个关键词搜索</p>
|
||
</div>
|
||
)}
|
||
</>
|
||
) : ragResults.length > 0 ? (
|
||
<div className="space-y-2">
|
||
{ragResults.map((r, i) => (
|
||
<div key={i} className="border border-gray-200 rounded-lg overflow-hidden">
|
||
<button
|
||
onClick={() => setExpandedArticle(expandedArticle === `rag-${i}` ? null : `rag-${i}`)}
|
||
className="flex items-center justify-between w-full px-3 py-2.5 text-left hover:bg-gray-50 transition-colors"
|
||
>
|
||
<div className="flex items-center gap-2 pr-2 min-w-0">
|
||
<span className="text-sm font-medium text-gray-800 truncate">{r.title}</span>
|
||
<span className="text-xs text-gray-400 flex-shrink-0">{(r.similarity * 100).toFixed(0)}% 匹配</span>
|
||
</div>
|
||
{expandedArticle === `rag-${i}` ? <ChevronDown className="w-4 h-4 text-gray-400 flex-shrink-0" /> : <ChevronRight className="w-4 h-4 text-gray-400 flex-shrink-0" />}
|
||
</button>
|
||
{expandedArticle === `rag-${i}` && (
|
||
<div className="px-3 pb-3 pt-1 text-sm text-gray-600 space-y-2">
|
||
<p>{r.content}</p>
|
||
<div className="flex items-center gap-2 text-xs text-gray-400">
|
||
<span className="px-1.5 py-0.5 rounded bg-gray-100">{r.category}</span>
|
||
<span>来源:{r.source}</span>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
) : (
|
||
<div className="text-center py-12 text-gray-400">
|
||
<Search className="w-8 h-8 mx-auto mb-2" />
|
||
<p className="text-sm">没有找到相关问题</p>
|
||
<p className="text-xs mt-1">试试换个关键词,或直接在 AI 助手中提问</p>
|
||
</div>
|
||
)}
|
||
</>
|
||
) : (
|
||
/* 正常分类浏览模式 */
|
||
<>
|
||
{currentCategory ? (
|
||
<>
|
||
<h3 className="text-sm font-semibold text-gray-900 mb-3 flex items-center gap-2">
|
||
{(() => {
|
||
const Icon = currentCategory.icon
|
||
return <Icon className="w-4 h-4 text-primary" />
|
||
})()}
|
||
{currentCategory.title}
|
||
</h3>
|
||
<div className="space-y-2">
|
||
{currentCategory.articles.map(article => {
|
||
const expanded = expandedArticle === article.id
|
||
return (
|
||
<div key={article.id} className="border border-gray-200 rounded-lg overflow-hidden">
|
||
<button
|
||
onClick={() => setExpandedArticle(expanded ? null : article.id)}
|
||
className="flex items-center justify-between w-full px-3 py-2.5 text-left hover:bg-gray-50 transition-colors"
|
||
>
|
||
<span className="text-sm font-medium text-gray-800 pr-2">{article.question}</span>
|
||
{expanded ? <ChevronDown className="w-4 h-4 text-gray-400 flex-shrink-0" /> : <ChevronRight className="w-4 h-4 text-gray-400 flex-shrink-0" />}
|
||
</button>
|
||
{expanded && (
|
||
<div className="px-3 pb-3 pt-1 text-sm text-gray-600 space-y-2">
|
||
{article.answer.split('\n').map((line, i) => (
|
||
<p key={i} className={line.startsWith('•') ? 'pl-2' : ''}>{line}</p>
|
||
))}
|
||
{article.steps && (
|
||
<div className="bg-blue-50 rounded-md p-3 space-y-1.5">
|
||
<p className="text-xs font-medium text-blue-700">操作步骤:</p>
|
||
{article.steps.map((step, i) => (
|
||
<div key={i} className="flex gap-2 text-xs text-blue-900">
|
||
<span className="flex-shrink-0 w-5 h-5 rounded-full bg-blue-200 text-blue-700 flex items-center justify-center font-medium">{i + 1}</span>
|
||
<span className="pt-0.5">{step}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
{article.tip && (
|
||
<div className="flex items-start gap-2 bg-green-50 rounded-md p-2.5">
|
||
<CheckCircle className="w-4 h-4 text-green-600 flex-shrink-0 mt-0.5" />
|
||
<p className="text-xs text-green-800">{article.tip}</p>
|
||
</div>
|
||
)}
|
||
{article.warning && (
|
||
<div className="flex items-start gap-2 bg-amber-50 rounded-md p-2.5">
|
||
<AlertTriangle className="w-4 h-4 text-amber-600 flex-shrink-0 mt-0.5" />
|
||
<p className="text-xs text-amber-800">{article.warning}</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
)
|
||
})}
|
||
</div>
|
||
</>
|
||
) : (
|
||
<div className="text-center py-12 text-gray-400">
|
||
<Search className="w-8 h-8 mx-auto mb-2" />
|
||
<p className="text-sm">没有找到相关问题</p>
|
||
<p className="text-xs mt-1">试试换个关键词搜索</p>
|
||
</div>
|
||
)}
|
||
</>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 底部联系方式 */}
|
||
<div className="border-t border-gray-200 px-4 py-2.5 flex items-center justify-between text-xs text-gray-500">
|
||
<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>
|
||
)
|
||
}
|