diff --git a/backend/src/routes/dashboard.routes.ts b/backend/src/routes/dashboard.routes.ts index 349ee50..d835d79 100644 --- a/backend/src/routes/dashboard.routes.ts +++ b/backend/src/routes/dashboard.routes.ts @@ -239,4 +239,142 @@ router.get('/workforce-stats', authMiddleware, async (req: AuthRequest, res: Res } }) +// 入离职统计看板 — 按月聚合入职和离职人数 +router.get('/turnover-stats', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => { + try { + const orgId = req.user!.orgId + const months = parseInt(req.query.months as string) || 12 + + // 计算最近 N 个月的月份列表 + const now = new Date() + const monthList: string[] = [] + for (let i = months - 1; i >= 0; i--) { + const d = new Date(now.getFullYear(), now.getMonth() - i, 1) + monthList.push(`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`) + } + + // 查询入职数据(按 hireDate 月份分组) + const startDate = new Date(monthList[0] + '-01') + const employees = await prisma.employee.findMany({ + where: { + orgId, + OR: [ + { hireDate: { gte: startDate } }, + { status: 'RESIGNED' }, + ], + }, + select: { id: true, name: true, hireDate: true, status: true, department: true }, + }) + + // 查询离职记录 + const terminations = await prisma.terminationRecord.findMany({ + where: { + orgId, + terminationDate: { gte: startDate }, + }, + select: { employeeId: true, terminationDate: true, type: true, reason: true }, + }) + + // 按月聚合 + const monthlyData = monthList.map(month => { + const monthStart = new Date(month + '-01') + const monthEnd = new Date(monthStart.getFullYear(), monthStart.getMonth() + 1, 1) + + const hired = employees.filter(e => e.hireDate >= monthStart && e.hireDate < monthEnd).length + const left = terminations.filter(t => { + const td = t.terminationDate + return td && td >= monthStart && td < monthEnd + }).length + + return { month, hired, left, net: hired - left } + }) + + // 汇总 + const totalHired = monthlyData.reduce((s, m) => s + m.hired, 0) + const totalLeft = monthlyData.reduce((s, m) => s + m.left, 0) + const currentHeadcount = employees.filter(e => e.status !== 'RESIGNED').length + const avgHeadcount = currentHeadcount // 简化:使用当前人数 + const turnoverRate = avgHeadcount > 0 ? (totalLeft / avgHeadcount * 100).toFixed(1) : '0' + + res.json({ + success: true, + data: { + monthly: monthlyData, + summary: { + totalHired, + totalLeft, + currentHeadcount, + turnoverRate: parseFloat(turnoverRate), + }, + }, + }) + } catch (err) { + next(err) + } +}) + +// 绩效统计看板 — 按周期聚合绩效分布 +router.get('/performance-stats', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => { + try { + const orgId = req.user!.orgId + const period = (req.query.period as string) || new Date().toISOString().slice(0, 4) // 默认当前年 + + const records = await prisma.performanceRecord.findMany({ + where: { + orgId, + period: { startsWith: period }, + }, + include: { employee: { select: { name: true, department: true } } }, + orderBy: { createdAt: 'desc' }, + }) + + // 按等级分布 + const gradeDist: Record = {} + // 按部门分布 + const deptDist: Record = {} + // 按周期分布 + const periodDist: Record = {} + + for (const r of records) { + // 等级分布 + const grade = r.grade || '未评级' + gradeDist[grade] = (gradeDist[grade] || 0) + 1 + + // 部门分布 + const dept = r.employee?.department || '未分配' + if (!deptDist[dept]) deptDist[dept] = { count: 0, avgScore: 0, scores: [] } + deptDist[dept].count++ + if (r.score) deptDist[dept].scores.push(r.score) + + // 周期分布 + if (!periodDist[r.period]) periodDist[r.period] = { count: 0, avgScore: 0, scores: [] } + periodDist[r.period].count++ + if (r.score) periodDist[r.period].scores.push(r.score) + } + + // 计算平均分 + const calcAvg = (d: typeof deptDist) => Object.entries(d).map(([name, v]) => ({ + name, + count: v.count, + avgScore: v.scores.length > 0 ? Math.round(v.scores.reduce((s, x) => s + x, 0) / v.scores.length * 10) / 10 : 0, + })) + + const allScores = records.map(r => r.score).filter(Boolean) as number[] + const overallAvg = allScores.length > 0 ? Math.round(allScores.reduce((s, x) => s + x, 0) / allScores.length * 10) / 10 : 0 + + res.json({ + success: true, + data: { + total: records.length, + overallAvgScore: overallAvg, + gradeDistribution: Object.entries(gradeDist).map(([name, value]) => ({ name, value })), + departmentDistribution: calcAvg(deptDist), + periodDistribution: calcAvg(periodDist), + }, + }) + } catch (err) { + next(err) + } +}) + export default router diff --git a/backend/src/routes/export.routes.ts b/backend/src/routes/export.routes.ts index f543ebe..ae78577 100644 --- a/backend/src/routes/export.routes.ts +++ b/backend/src/routes/export.routes.ts @@ -282,26 +282,65 @@ router.get('/roster', authMiddleware, async (req: AuthRequest, res: Response, ne const workbook = new ExcelJS.Workbook() const ws = workbook.addWorksheet('花名册') ws.columns = [ - { header: '姓名', key: 'name', width: 12 }, - { header: '部门', key: 'department', width: 15 }, - { header: '状态', key: 'status', width: 10 }, + { header: '姓名', key: 'name', width: 10 }, + { header: '部门', key: 'department', width: 12 }, + { header: '性别', key: 'gender', width: 6 }, + { header: '状态', key: 'status', width: 8 }, { header: '入职日期', key: 'hireDate', width: 12 }, + { header: '手机号', key: 'phone', width: 13 }, + { header: '身份证号', key: 'idCardNumber', width: 20 }, + { header: '月工资', key: 'monthlySalary', width: 10 }, + { header: '社保基数', key: 'socialInsBase', width: 10 }, + { header: '公积金基数', key: 'housingFundBase', width: 10 }, + { header: '专项附加扣除', key: 'specialDeduction', width: 12 }, + { header: '参保城市', key: 'city', width: 10 }, + { header: '紧急联系人', key: 'emergencyContact', width: 10 }, + { header: '紧急联系电话', key: 'emergencyPhone', width: 13 }, + { header: '住址', key: 'address', width: 18 }, + { header: '开户行', key: 'bankName', width: 10 }, + { header: '银行账号', key: 'bankAccount', width: 18 }, { header: '合同起始', key: 'contractStart', width: 12 }, { header: '合同结束', key: 'contractEnd', width: 12 }, - { header: '联系方式', key: 'phone', width: 15 }, ] ws.getRow(1).font = { bold: true } + // 是否脱敏(非 ADMIN 用户强制脱敏) + const shouldMask = req.user!.role !== 'ADMIN' + for (const e of employees) { const contract = e.contracts[0] + // 解密敏感字段 + let salary = 0 + try { salary = Number(decrypt(e.monthlySalary)) || 0 } catch { salary = Number(e.monthlySalary) || 0 } + let idCard: string | null = null + try { if (e.idCardNumber) idCard = decrypt(e.idCardNumber) } catch { idCard = e.idCardNumber } + let bankAccount: string | null = null + try { if (e.bankAccount) bankAccount = decrypt(e.bankAccount) } catch { bankAccount = e.bankAccount } + if (shouldMask) { + idCard = maskIdCard(idCard) + bankAccount = maskBankAccount(bankAccount) + if (salary) salary = 0 + } ws.addRow({ name: e.name, department: e.department, + gender: e.gender || '', status: e.status === 'ACTIVE' ? '在职' : e.status === 'RESIGNED' ? '离职' : '预入职', hireDate: e.hireDate?.toISOString().slice(0, 10) || '', + phone: e.phone || '', + idCardNumber: idCard || '', + monthlySalary: salary, + socialInsBase: e.socialInsBase || 0, + housingFundBase: e.housingFundBase || 0, + specialDeduction: e.specialDeduction || 0, + city: e.city || '', + emergencyContact: e.emergencyContact || '', + emergencyPhone: e.emergencyPhone || '', + address: e.address || '', + bankName: e.bankName || '', + bankAccount: bankAccount || '', contractStart: contract?.startDate?.toISOString().slice(0, 10) || '', contractEnd: contract?.endDate?.toISOString().slice(0, 10) || '', - phone: e.phone || '', }) } @@ -386,4 +425,101 @@ router.get('/terminations', authMiddleware, async (req: AuthRequest, res: Respon } }) +// 导出个税申报表 Excel(对齐自然人电子税务局格式) +router.get('/tax-declaration', authMiddleware, requireAdmin, async (req: AuthRequest, res: Response, next) => { + try { + const orgId = req.user!.orgId + const month = (req.query.month as string) || new Date().toISOString().slice(0, 7) + + const entries = await prisma.batchEntry.findMany({ + where: { orgId, batch: { month, status: 'ARCHIVED' } }, + include: { employee: true, batch: true }, + orderBy: { employee: { name: 'asc' } }, + }) + + const workbook = new ExcelJS.Workbook() + const ws = workbook.addWorksheet('个税申报表') + + // 个税申报表列定义(对齐自然人电子税务局模板) + ws.columns = [ + { header: '序号', key: 'seq', width: 6 }, + { header: '姓名', key: 'name', width: 10 }, + { header: '身份证件号码', key: 'idCardNumber', width: 22 }, + { header: '所得项目', key: 'incomeType', width: 16 }, + { header: '本期收入', key: 'totalPay', width: 12 }, + { header: '本期免税收入', key: 'taxFreeIncome', width: 14 }, + { header: '基本减除费用', key: 'basicDeduction', width: 14 }, + { header: '专项扣除合计', key: 'specialDeductionTotal', width: 14 }, + { header: '养老保险', key: 'pensionEmp', width: 10 }, + { header: '医疗保险', key: 'medicalEmp', width: 10 }, + { header: '失业保险', key: 'unemploymentEmp', width: 10 }, + { header: '住房公积金', key: 'housingEmp', width: 12 }, + { header: '专项附加扣除', key: 'specialAdditionalDeduction', width: 14 }, + { header: '其他扣除', key: 'otherDeduction', width: 10 }, + { header: '累计收入额', key: 'ytdIncome', width: 12 }, + { header: '累计减除费用', key: 'ytdBasicDeduction', width: 14 }, + { header: '累计专项扣除', key: 'ytdSpecialDeduction', width: 14 }, + { header: '累计专项附加扣除', key: 'ytdSpecialAdditional', width: 16 }, + { header: '累计应纳税所得额', key: 'ytdTaxableIncome', width: 16 }, + { header: '税率', key: 'taxRate', width: 8 }, + { header: '速算扣除数', key: 'quickDeduction', width: 12 }, + { header: '累计已预扣税额', key: 'ytdTaxDeducted', width: 14 }, + { header: '本期应预扣税额', key: 'tax', width: 14 }, + { header: '备注', key: 'remark', width: 20 }, + ] + ws.getRow(1).font = { bold: true } + + let seq = 0 + for (const e of entries) { + seq++ + // 解密身份证号 + let idCard: string = '' + try { if (e.employee.idCardNumber) idCard = decrypt(e.employee.idCardNumber) || '' } catch { idCard = e.employee.idCardNumber || '' } + + // 从社保中拆分个人部分(简化:使用 socialEmp 总额按比例拆分) + const socialEmp = e.socialEmp || 0 + const pensionEmp = Math.round(socialEmp * 0.56) // 养老约 56% + const medicalEmp = Math.round(socialEmp * 0.36) // 医疗约 36% + const unemploymentEmp = socialEmp - pensionEmp - medicalEmp // 剩余为失业 + + // 累计数据 + const ytdTaxDeducted = (e as any).ytdTaxDeducted || e.tax || 0 + + ws.addRow({ + seq, + name: e.employee.name, + idCardNumber: idCard, + incomeType: '工资薪金所得', + totalPay: e.totalPay || 0, + taxFreeIncome: 0, + basicDeduction: 5000, // 基本减除费用 5000/月 + specialDeductionTotal: socialEmp + (e.housingEmp || 0), + pensionEmp, + medicalEmp, + unemploymentEmp, + housingEmp: e.housingEmp || 0, + specialAdditionalDeduction: e.employee.specialDeduction || 0, + otherDeduction: 0, + ytdIncome: e.totalPay || 0, // 简化:单月累计=本月 + ytdBasicDeduction: 5000, + ytdSpecialDeduction: socialEmp + (e.housingEmp || 0), + ytdSpecialAdditional: e.employee.specialDeduction || 0, + ytdTaxableIncome: Math.max(0, (e.totalPay || 0) - 5000 - socialEmp - (e.housingEmp || 0) - (e.employee.specialDeduction || 0)), + taxRate: '', + quickDeduction: 0, + ytdTaxDeducted, + tax: e.tax || 0, + remark: '', + }) + } + + res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + res.setHeader('Content-Disposition', contentDisposition(`个税申报表-${month}.xlsx`)) + await workbook.xlsx.write(res) + res.end() + } catch (err) { + next(err) + } +}) + export default router diff --git a/backend/src/routes/import.routes.ts b/backend/src/routes/import.routes.ts index 64d7375..550193b 100644 --- a/backend/src/routes/import.routes.ts +++ b/backend/src/routes/import.routes.ts @@ -267,6 +267,16 @@ router.post('/excel', authMiddleware, requireAdmin, upload.single('file'), async if (idCheck.upgraded) idCard = idCheck.upgraded } + // 社保基数:值为 0 或「无」表示不参保 + const socialInsBaseVal = num(getField(r, '社保基数')) + const socialInsOptOut = val(getField(r, '社保基数')) === '无' || val(getField(r, '社保基数')) === '不缴' + const socialInsBase = socialInsOptOut ? 0 : (socialInsBaseVal || num(salary)) + + // 公积金基数:值为 0 或「无」表示不缴纳 + const housingFundBaseVal = num(getField(r, '公积金基数')) + const housingFundOptOut = val(getField(r, '公积金基数')) === '无' || val(getField(r, '公积金基数')) === '不缴' + const housingFundBase = housingFundOptOut ? 0 : (housingFundBaseVal || num(salary)) + const emp = await prisma.employee.create({ data: { orgId, name, department: dept, hireDate, @@ -283,21 +293,27 @@ router.post('/excel', authMiddleware, requireAdmin, upload.single('file'), async address: val(getField(r, '住址')) || null, bankName: val(getField(r, '开户行')) || null, bankAccount: val(getField(r, '银行账号')) ? encrypt(val(getField(r, '银行账号'))) : null, - socialInsBase: num(getField(r, '社保基数')) || num(salary), - housingFundBase: num(getField(r, '公积金基数')) || num(salary), + socialInsBase, + housingFundBase, specialDeduction: num(getField(r, '专项附加扣除')) || 0, city: val(getField(r, '参保城市')) || null, isPregnant: val(getField(r, '孕期')) === '是', isInMedicalPeriod: val(getField(r, '医疗期')) === '是', isWorkInjured: val(getField(r, '工伤')) === '是', - socialInsStartMonth: dateToMonth(hireDate), - housingFundStartMonth: dateToMonth(hireDate), + socialInsStartMonth: socialInsOptOut ? null : dateToMonth(hireDate), + housingFundStartMonth: housingFundOptOut ? null : dateToMonth(hireDate), createdBy: userId, }, }) - await prisma.employeeSocialInsRecord.create({ data: { orgId, employeeId: emp.id, startMonth: dateToMonth(hireDate), endMonth: null, base: num(getField(r, '社保基数')) || num(salary), changeType: 'ONBOARDING', createdBy: userId } }) - await prisma.employeeHousingFundRecord.create({ data: { orgId, employeeId: emp.id, startMonth: dateToMonth(hireDate), endMonth: null, base: num(getField(r, '公积金基数')) || num(salary), changeType: 'ONBOARDING', createdBy: userId } }) + // 仅在未 opt-out 时创建社保记录 + if (!socialInsOptOut) { + await prisma.employeeSocialInsRecord.create({ data: { orgId, employeeId: emp.id, startMonth: dateToMonth(hireDate), endMonth: null, base: socialInsBase, changeType: 'ONBOARDING', createdBy: userId } }) + } + // 仅在未 opt-out 时创建公积金记录 + if (!housingFundOptOut) { + await prisma.employeeHousingFundRecord.create({ data: { orgId, employeeId: emp.id, startMonth: dateToMonth(hireDate), endMonth: null, base: housingFundBase, changeType: 'ONBOARDING', createdBy: userId } }) + } await prisma.salaryChangeRecord.create({ data: { orgId, employeeId: emp.id, oldSalary: 0, newSalary: num(salary), effectiveDate: hireDate, effectiveMonth: dateToMonth(hireDate), endMonth: null, changeType: 'ONBOARDING', createdBy: userId } }) await prisma.employeeDepartmentRecord.create({ data: { orgId, employeeId: emp.id, oldDepartment: '', newDepartment: dept, effectiveMonth: dateToMonth(hireDate), endMonth: null, changeType: 'ONBOARDING', createdBy: userId } }) diff --git a/docs/20260730-系统操作问题与功能对照.md b/docs/20260730-系统操作问题与功能对照.md new file mode 100644 index 0000000..8a6638d --- /dev/null +++ b/docs/20260730-系统操作问题与功能对照.md @@ -0,0 +1,406 @@ +# 企业用工专家系统 — 操作问题与现有功能对照分析 + +> 文档日期:2026-07-31 +> 整理人:用户反馈 + 系统代码核查 + +--- + +## 一、花名册模块 + +### 1.1 合同类型覆盖问题 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 只有标准劳动合同界面,劳务合同、承揽合同、临时工、非全日制等员工无法录入 | 数据库 `ContractType` 枚举支持 5 种:`FIXED`(固定期限)、`UNFIXED`(无固定期限)、`UNSIGNED`(未签)、`LABOR`(劳务)、`INTERNSHIP`(实习) | **后端已支持 LABOR 类型,但前端未暴露**。导入模板合同类型仅映射「固定期限/无固定期限/未签」3 种,缺少「劳务/实习」选项。前端合同添加界面也只展示标准劳动合同选项 | +| — | 前端 `Roster.tsx` 合同管理 Tab 和 `roster/ContractInfo.tsx` 中合同类型选择器未包含 LABOR、INTERNSHIP | **承揽合同、临时工、非全日制** 3 种类型在枚举中完全缺失,需要新增 | + +### 1.2 花名册导入导出错位问题 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 导出只能导出姓名、合同期限等部分基本信息,无法导出全部信息 | `export.routes.ts` 花名册导出仅 7 列:姓名、部门、状态、入职日期、合同起始、合同结束、联系方式 | **导出字段严重不足**。缺少:性别、身份证号、手机号、社保基数、公积金基数、参保城市、银行账户、紧急联系人、学历、出生日期、专项附加扣除等 | +| 导入和导出系统错位 | 导入模板 `import.routes.ts` 支持 19 个字段(姓名、部门、性别、手机号、身份证号、入职日期、月工资、社保基数、公积金基数、专项附加扣除、参保城市、紧急联系人、紧急联系电话、住址、开户行、银行账号、孕期、医疗期、工伤) | **导入 19 字段 vs 导出 7 字段,严重不对齐**。导入的数据无法完整导出回溯 | + +### 1.3 现有功能 + +| 功能项 | 支持情况 | 实现位置 | +|-------|---------|---------| +| 单个员工录入 | ✅ 支持 | `Roster.tsx` → `AddEmployeeModal` | +| 批量导入 | ✅ 支持 Excel 导入 | `import.routes.ts` → `/import/excel` | +| 花名册导出 | ⚠️ 仅 7 列基本信息 | `export.routes.ts` → `/export/roster` | +| 全量数据导出 | ✅ 支持 JSON/Excel 格式 | `export.routes.ts` → `/export/all`(管理员权限) | +| 员工详情 | ✅ 支持 10 个 Tab 页 | `roster/EmployeeProfile.tsx`(基本信息、合同、薪税社保、考勤加班、解聘、违纪、培训、绩效、证据链、变更历史) | +| 批量续签合同 | ✅ 支持 | `Roster.tsx` → `batchRenewMutation` | +| 批量解聘 | ✅ 支持 | `Roster.tsx` → `batchTerminateMutation` | +| 调薪记录 | ✅ 支持 | `roster/modals.tsx` → `SalaryChangeModal` | +| 部门变更 | ✅ 支持 | `roster/modals.tsx` → `DeptChangeModal` | + +--- + +## 二、工资模块 + +### 2.1 计薪方式覆盖问题 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 只有标准工时工资模块,计件、计时方式无法录入 | `BatchEntry` 模型薪资项:`baseSalary`(基本工资)、`overtimePay`(加班费)、`allowance`(津贴)、`bonus`(奖金)、`deduction`(扣款)、`positionSalary`(岗位工资)、`performanceSalary`(绩效工资)、`senioritySalary`(工龄工资)及 4 类补贴 | **缺少计件工资字段**。无 `pieceworkCount`(件数)、`pieceworkUnitPrice`(单价)、`hourlyRate`(时薪)、`workHours`(工时) 等字段 | +| — | `PayrollBatchType` 枚举:`REGULAR`(常规)、`TERMINATION`(离职结算)、`BONUS`(年终奖/奖金)、`SEVERANCE`(补偿金按月) | **缺少计件/计时批次类型** | + +### 2.2 现有功能 + +| 功能项 | 支持情况 | 实现位置 | +|-------|---------|---------| +| 发薪批次管理 | ✅ 支持创建/编辑/归档 | `Money.tsx` → batch tab | +| 薪酬模板 | ✅ 支持自定义薪资项 | `Money.tsx` → template tab | +| 加班费计算 | ✅ 支持工作日/休息日/法定节假日 | `Money.tsx` → overtime tab | +| 工资条管理 | ✅ 支持发布/确认/定时发送 | `Money.tsx` → payslip tab | +| Excel 导入工资 | ✅ 支持 | `import.routes.ts` → `/import/payroll` | +| 工资表导出 | ✅ 支持部门汇总 + 明细 | `Money.tsx` → 导出按钮 | +| 个税预览计算 | ✅ 支持累计预扣法 | `payroll.routes.ts` → `/payroll/tax-preview` | +| 年终奖单独计税 | ✅ 支持 | `payroll.service.ts` → `calcBonusTax` | +| 薪资项手工覆盖 | ✅ 社保/公积金可手动覆盖 | `payroll.service.ts` → `overrideSocial` | + +--- + +## 三、社保模块 + +### 3.1 险种覆盖问题 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 全部是正规五险一金,无社保人员模块 | `SocialInsuranceConfig` 固定 5 险:养老、医疗、失业、工伤、生育 | **缺少「无社保」选项**。员工入职时自动创建社保记录,无法标记为「不参保」 | +| 纯商险或意外险员工无法录入 | `extraInsurances` JSON 字段支持附加险种配置(名称、企业/个人比例、基数类型) | **后端已支持附加险种,但前端仅展示已有配置**。无独立的「商险管理」模块,附加险种只能在社保配置中作为附加项录入 | +| 只有五险或三险且无公积金的员工,系统也会自动匹配公积金 | 入职导入时 `import.routes.ts` 第 300 行**无条件创建** `EmployeeHousingFundRecord`,基数默认等于月工资 | **缺少「是否缴纳公积金」开关**。`EmployeeHousingFundRecord` 无 `status` 或 `optOut` 字段 | +| 基数自动为员工应发工资基数 | `import.routes.ts` 第 287 行:`housingFundBase: num(getField(r, '公积金基数')) || num(salary)` | 未填写公积金基数时自动使用月工资作为基数,**无法设为 0 或不缴** | + +### 3.2 现有功能 + +| 功能项 | 支持情况 | 实现位置 | +|-------|---------|---------| +| 社保配置(五险比例) | ✅ 支持多城市多版本 | `SocialInsurance.tsx` → social tab | +| 公积金配置 | ✅ 支持基本/补充公积金 | `SocialInsurance.tsx` → housing tab | +| 附加险种配置 | ✅ 后端支持 JSON 字段 | `SocialInsuranceConfig.extraInsurances` | +| 社保基数调整 | ✅ 支持批量调基 | `SocialInsurance.tsx` → 调整按钮 | +| 月度社保办理 | ✅ 支持标记已办理 | `SocialInsurance.tsx` → monthly tab | +| 专项附加扣除 | ✅ 支持导入 | `import.routes.ts` → `/import/special-deduction` | +| 版本管理 | ✅ 支持历史版本 | `SocialInsurance.tsx` → 版本管理 | +| 社保导出 | ✅ 含在全量导出中 | `export.routes.ts` → `/export/all` | + +--- + +## 四、考勤模块 + +### 4.1 打卡功能缺失 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 无法实现打卡模块,不如钉钉/企微好用 | 系统无打卡功能,`AttendanceRecord` 模型有 `checkInTime`/`checkOutTime` 字段但无打卡接口 | **完全未实现打卡功能**。系统定位为 HR 管理后台而非考勤终端 | +| 需要借助第三方平台导出数据再手工录入,增加工作量 | 支持 Excel 批量导入考勤记录(`import.routes.ts` → `/import/monthly` 考勤记录 sheet) | **已有导入功能但无第三方系统对接**。缺少钉钉/企微/飞书 API 对接 | + +### 4.2 现有功能 + +| 功能项 | 支持情况 | 实现位置 | +|-------|---------|---------| +| 考勤确认流程 | ✅ 支持员工端确认 | `Attendance.tsx` → confirm tab | +| 班次管理 | ✅ 支持创建/编辑班次 | `Attendance.tsx` → shifts tab | +| 排班管理 | ✅ 支持按日排班 | `Attendance.tsx` → schedule tab | +| 每日出勤记录 | ✅ 支持手动录入/查看 | `Attendance.tsx` → daily tab | +| 月度报表 | ✅ 支持汇总统计 | `Attendance.tsx` → monthly tab | +| 休假记录 | ✅ 支持请假类型管理 | `Attendance.tsx` → leaves tab | +| Excel 批量导入 | ✅ 支持月度导入 | `import.routes.ts` → `/import/monthly` | +| 员工端考勤查看 | ✅ 支持 | `portal/MyAttendance.tsx` | + +--- + +## 五、发薪模块 — 个税自动算税 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 暂时无法实现个税自动算税 | **已实现累计预扣法个税计算**:`payroll.service.ts` → `calcCumulativeTax()`,支持 7 级超额累进税率 | **用户反馈与系统现状不符**。系统已支持个税自动计算,可能在操作时未发现该功能 | +| — | `payroll.routes.ts` → `/payroll/tax-preview` 提供个税预览计算 | 前端 `Money.tsx` 有个税预览功能,支持输入薪资项后实时计算个税 | +| — | 归档批次时自动计算个税并写入 `BatchEntry.tax` | 归档时个税自动计算并锁定 | +| — | 支持年终奖单独计税:`calcBonusTax()` | 年终奖批次类型自动使用单独计税 | +| — | 支持专项附加扣除:`Employee.specialDeduction` | 个税计算时扣除专项附加扣除 | +| — | 个税跳档预警:`payroll.service.ts` 第 544 行 | 归档时检测个税异常波动并提示 | + +### 现有功能 + +| 功能项 | 支持情况 | 说明 | +|-------|---------|------| +| 累计预扣法算税 | ✅ 已实现 | 7 级超额累进,年度累计预扣 | +| 个税实时预览 | ✅ 已实现 | 前端输入薪资项即可预览个税 | +| 年终奖单独计税 | ✅ 已实现 | 除以 12 找税率 + 速算扣除 | +| 专项附加扣除 | ✅ 已实现 | 支持导入和员工端填报 | +| 个税跳档预警 | ✅ 已实现 | 归档时自动检测 | +| YTD 累计数据 | ✅ 已实现 | 从已归档批次获取历史数据 | + +--- + +## 六、发票模块和审批模块 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 系统暂时没有发票模块 | **完全未实现**。无 Invoice 模型、无发票路由、无发票页面 | 需从零设计开发 | +| 系统暂时没有审批模块 | **部分实现**。解聘记录有 `status` 字段(DRAFT/PENDING_APPROVAL/APPROVED/REJECTED/EXECUTING/COMPLETED/CANCELLED),但无通用审批流 | 仅有解聘审批状态流转,无通用审批引擎(请假审批、调薪审批、合同审批等) | + +### 现有相关功能 + +| 功能项 | 支持情况 | 说明 | +|-------|---------|------| +| 解聘审批流 | ⚠️ 有状态字段但无审批界面 | `TerminationRecord.status` 支持 7 种状态,但前端无审批操作入口 | +| 操作日志审计 | ✅ 支持 | `AuditLog` 模型记录所有操作 | +| 合同确认链接 | ✅ 支持 | `ContractConfirmLink` 模型,员工端确认签署 | + +--- + +## 七、功能对照总览 + +| 模块 | 用户反馈问题 | 系统实际状态 | 严重程度 | 修复难度 | +|------|------------|------------|---------|---------| +| 花名册 | 缺少劳务/承揽/临时工/非全合同类型 | 枚举缺 3 种类型,前端缺 2 种展示 | 🔴 高 | 中 | +| 花名册 | 导出字段不全、导入导出错位 | 导出 7 列 vs 导入 19 列 | 🔴 高 | 低 | +| 工资 | 缺少计件/计时计薪方式 | 无相关字段和批次类型 | 🟡 中 | 高 | +| 社保 | 无社保人员无法标记 | 入职自动创建社保记录,无 optOut | 🟡 中 | 中 | +| 社保 | 商险/意外险无独立模块 | 后端有 extraInsurances 字段 | 🟡 中 | 中 | +| 社保 | 无公积金员工自动匹配 | 无条件创建公积金记录 | 🟡 中 | 低 | +| 考勤 | 无打卡功能 | 系统无打卡能力 | 🟡 中 | 高 | +| 考勤 | 第三方数据需手工导入 | 有 Excel 导入但无 API 对接 | 🟢 低 | 中 | +| 发薪 | 个税无法自动算税 | **已实现**,用户可能未发现 | 🟢 低 | — | +| 发票 | 无发票模块 | 完全未实现 | 🟡 中 | 高 | +| 审批 | 无审批模块 | 仅解聘有状态流转 | 🟡 中 | 高 | + +--- + +## 八、建议优先级 + +### P0 — 立即修复(影响日常使用) + +1. **花名册导出字段扩充**:对齐导入的 19 个字段,增加性别、身份证号、社保基数、公积金基数、参保城市、银行账户等 +2. **社保「无社保」选项**:入职时增加「是否参保」开关,不参保则不创建社保记录 +3. **公积金「不缴纳」选项**:入职时增加「是否缴纳公积金」开关,不缴纳则不创建公积金记录 + +### P1 — 近期优化(提升覆盖面) + +4. **合同类型扩充**:新增「承揽合同」「临时工」「非全日制」3 种枚举,前端添加对应选项 +5. **导入模板合同类型对齐**:导入模板增加「劳务」「实习」选项映射 +6. **商险/意外险独立管理**:在社保页面增加「商险管理」Tab,独立于五险一金 + +### P2 — 中期规划(功能扩展) + +7. **计件/计时工资**:新增 `PayrollBatchType.PIECEWORK` 和 `TIMework`,`BatchEntry` 增加件数/单价/时薪字段 +8. **考勤第三方对接**:钉钉/企微/飞书 API 数据同步 +9. **发票模块**:新增 Invoice 模型,支持发票录入、查验、归档 +10. **通用审批引擎**:支持请假、调薪、合同等多场景审批流 + +--- + +## 九、系统现有完整模块清单 + +| 分类 | 模块 | 路由 | 核心功能 | +|------|------|------|---------| +| 工作台 | 总览 | `/` | 数据看板、风险提醒、月度任务、价值报告 | +| 工作台 | 工作日历 | `/calendar` | 月度 HR 事项日历 | +| 员工管理 | 花名册 | `/roster` | 员工 CRUD、批量导入、导出、合同管理、调薪、部门变更 | +| 员工管理 | 用工办理 | `/work-process` | 入职/离职流程管理 | +| 员工管理 | 考勤确认 | `/attendance` | 班次/排班/出勤/月度报表/休假 | +| 员工管理 | 解聘补偿 | `/termination` | 解聘记录、补偿金计算 | +| 员工管理 | 特殊状态 | `/special-status` | 孕期/哺乳期/工伤/医疗期 | +| 薪税社保 | 薪税管理 | `/money` | 发薪批次、薪酬模板、加班费、工资条 | +| 薪税社保 | 社保公积金 | `/social` | 五险配置、公积金配置、基数调整、月度办理 | +| 合规风控 | 证据链 | `/evidence` | 入职/合同/培训/违纪证据管理 | +| 合规风控 | 规章制度 | `/policies` | 制度发布、员工签收 | +| 合规风控 | 用工体检 | `/tools/health-check` | 5 维度合规健康度评分 | +| 合规风控 | 医疗期计算 | `/tools/medical-period` | 医疗期期限计算器 | +| 合规风控 | 年度价值 | `/tools/annual-value` | 年度 HR 价值报告 | +| AI 辅助 | AI 顾问 | `/ai-assistant` | 劳动法 AI 问答 | +| AI 辅助 | 文本模板 | `/templates` | 合同/通知/制度模板管理 | +| 系统 | 通知管理 | `/notifications` | 风险提醒通知配置 | +| 系统 | 操作日志 | `/audit` | 操作审计日志 | +| 系统 | 设置 | `/settings` | 企业信息、导入配置 | +| 员工端 | 门户 | `/portal/*` | 工资条查看、合同确认、考勤确认、制度签收、入职办理 | + +--- + +## 十、绩效分析与统计看板 + +### 10.1 现状 + +| 功能项 | 支持情况 | 实现位置 | 说明 | +|-------|---------|---------|------| +| 绩效记录录入 | ✅ 支持 | `roster/PerformanceInfo.tsx` | 在员工详情中手动新增绩效记录(考核周期、得分、等级 A/B/C/D、评语、改进计划) | +| 绩效员工确认 | ✅ 支持 | `PerformanceRecord.employeeAck` | 员工可确认考核结果 | +| 绩效看板 | ❌ 未实现 | — | 无独立的绩效统计看板页面 | +| 绩效趋势分析 | ❌ 未实现 | — | 无绩效得分趋势、部门对比、等级分布等分析 | +| 绩效与薪酬联动 | ❌ 未实现 | — | 绩效结果未与薪酬调整、奖金分配关联 | + +### 10.2 差距分析与建议 + +| 需求 | 现状 | 建议方案 | 预算评估 | +|------|------|---------|---------| +| 绩效统计看板 | 无 | 新增 `/tools/performance-dashboard` 页面,展示:等级分布饼图、部门绩效对比柱状图、月度/季度趋势折线图、Top/Bottom 员工排名 | 🟢 低 — 前端页面 + 1 个聚合 API,复用现有 `PerformanceRecord` 数据 | +| 绩效与薪酬联动 | 无 | 在发薪批次中增加「绩效奖金」列,自动关联最近一期绩效等级,按规则计算奖金系数 | 🟡 中 — 需扩展 `BatchEntry` 增加绩效奖金字段 + 计算规则配置 | +| 绩效改进追踪 | 有改进计划字段但无追踪 | 在绩效记录中增加「改进目标」「完成度」「复评得分」字段,支持改进闭环 | 🟢 低 — 扩展 `PerformanceRecord` 模型 + 前端展示 | + +### 10.3 现有可复用的看板基础设施 + +| 基础设施 | 实现位置 | 复用方向 | +|---------|---------|---------| +| Dashboard 数据看板 | `Dashboard.tsx` | 绩效看板可复用 Card/图表组件和布局模式 | +| 人员统计 API | `/dashboard/workforce-stats` | 绩效看板可复用聚合统计 API 模式 | +| 年度价值报告 | `/tools/annual-value` | 绩效年度汇总可嵌入年度价值报告 | +| 图表组件 | recharts(已引入) | 直接复用饼图/柱状图/折线图 | + +--- + +## 十一、入离职合同签署线上化 + +### 11.1 现状 + +| 功能项 | 支持情况 | 实现位置 | 说明 | +|-------|---------|---------|------| +| 入职线上化 | ✅ 已实现 | `OnboardingLink` 模型 + `portal.routes.ts` | HR 生成入职链接 → 员工扫码/点击 → 填写个人信息 → 上传附件(身份证、银行卡等)→ HR 审核 | +| 入职附件上传 | ✅ 已实现 | `portal.routes.ts` → `/onboarding/:token/upload` | 支持文件上传到 `/uploads/onboarding/` | +| 入职链接管理 | ✅ 已实现 | `OnboardingLink.status` | PENDING → APPROVED/REJECTED/CANCELLED | +| 合同签署线上化 | ✅ 已实现 | `ContractConfirmLink` 模型 + `portal.routes.ts` | HR 发送合同确认链接 → 员工手机验证码验证 → 确认签署 → 记录 IP/时间/验证码为证据 | +| 合同签署证据链 | ✅ 已实现 | `portal.routes.ts` 第 295 行 | 签署确认时自动创建证据链(IP、UserAgent、短信验证码) | +| 合同电子签署 | ⚠️ 部分 | `LaborContract.signMethod` 支持 `ELECTRONIC` | 有字段标记但未对接第三方电子签平台(如 e签宝、法大大) | +| 离职线上化 | ⚠️ 部分 | `TerminationRecord` 模型 | HR 端可发起离职/解聘流程,但员工端无离职申请入口 | +| 离职确认 | ❌ 未实现 | — | 员工无法在线确认离职/解聘结果 | + +### 11.2 差距分析与建议 + +| 需求 | 现状 | 建议方案 | 预算评估 | +|------|------|---------|---------| +| 入职流程完善 | 已有基础流程 | 补充:入职信息预览 PDF 导出、入职指南推送、入职材料清单勾选 | 🟢 低 — 前端增强 | +| 合同电子签对接 | 有签署确认但非真电子签 | 对接 e签宝/法大大 API,实现具有法律效力的电子签名 | 🟡 中 — API 对接费用 + 开发量 | +| 员工离职申请 | 无 | 员工端门户新增离职申请入口,HR 端审批 | 🟢 低 — 复用现有门户框架 | +| 离职交接清单 | 无 | 离职流程增加交接清单(设备归还、工作交接、文件移交) | 🟢 低 — 前端表单 + 后端模型扩展 | +| 入离职统计看板 | 无 | Dashboard 新增入离职趋势图、离职率分析、入职转化率 | 🟢 低 — 复用 `Employee.hireDate` + `TerminationRecord` 数据 | + +### 11.3 现有入离职线上化流程 + +``` +入职流程(已实现): +HR 创建员工 → 生成入职链接(OnboardingLink) → 员工扫码填写信息 → 上传附件 → HR 审核 → 创建员工档案 + +合同签署流程(已实现): +HR 创建合同(LaborContract) → 生成确认链接(ContractConfirmLink) → 员工手机验证码验证 → 确认签署 → 自动生成证据链 + +离职流程(部分实现): +HR 发起解聘/离职(TerminationRecord) → 计算补偿金 → 记录证据链 → [缺失:员工确认/交接清单] +``` + +--- + +## 十二、薪酬核算线上化 + +### 12.1 现状 + +| 功能项 | 支持情况 | 实现位置 | 说明 | +|-------|---------|---------|------| +| 薪酬核算 | ✅ 已实现 | `payroll.service.ts` → `calcBatchEntry()` | 自动计算:基本工资 + 岗位/绩效/工龄工资 + 各类补贴 + 加班费 - 扣款 → 社保公积金 → 个税 → 实发 | +| 社保公积金自动计算 | ✅ 已实现 | `calcSocialInsurance()` + `calcHousingFund()` | 按城市配置比例自动计算企业和个人部分 | +| 个税自动计算 | ✅ 已实现 | `calcCumulativeTax()` | 累计预扣法,7 级超额累进 | +| 年终奖单独计税 | ✅ 已实现 | `calcBonusTax()` | 除以 12 找税率 + 速算扣除 | +| 多批次发薪 | ✅ 已实现 | `PayrollBatchType` 4 种类型 | 常规/离职结算/年终奖/补偿金按月 | +| 薪酬模板 | ✅ 已实现 | `PayslipItem` 模型 | 支持自定义薪资项和计算公式 | +| 工资条在线发放 | ✅ 已实现 | `Payslip.status` PENDING→PUBLISHED | 支持定时发送、员工端确认 | +| 工资条员工确认 | ✅ 已实现 | `Payslip.confirmedAt` | 员工查看并确认工资条 | +| Excel 导入工资 | ✅ 已实现 | `/import/payroll` | 支持批量导入工资数据 | +| 薪税汇总导出 | ✅ 已实现 | `/export/payroll` | 导出月度薪税汇总 Excel | +| 个税预览 | ✅ 已实现 | `/payroll/tax-preview` | 输入薪资项实时预览个税 | +| 个税跳档预警 | ✅ 已实现 | `payroll.service.ts` 第 544 行 | 归档时检测个税异常波动 | +| 计件/计时工资 | ❌ 未实现 | — | 无相关字段和计算逻辑 | + +### 12.2 差距分析 + +| 需求 | 现状 | 差距 | +|------|------|------| +| 薪酬核算线上化 | **已基本实现** | 用户可能未充分了解现有功能 | +| 计件/计时工资 | 未实现 | 需扩展数据模型和计算逻辑 | +| 薪酬分析看板 | 无 | 缺少薪酬分布、薪酬趋势、部门薪酬对比等分析 | +| 调薪记录追踪 | ✅ 有 `SalaryChangeRecord` | 但缺少调薪审批流和调薪建议 | + +--- + +## 十三、个税申报线上化 + +### 13.1 现状 + +| 功能项 | 支持情况 | 实现位置 | 说明 | +|-------|---------|---------|------| +| 个税计算 | ✅ 已实现 | `calcCumulativeTax()` | 累计预扣法,支持 7 级超额累进 | +| 个税预览 | ✅ 已实现 | `/payroll/tax-preview` | 实时预览个税明细 | +| 个税累计数据 | ✅ 已实现 | `Payslip.ytdTaxDeducted` | 年度累计已扣税额 | +| 个税申报提醒 | ✅ 已实现 | `risk.service.ts` 第 408 行 | 每月个税申报日提醒 | +| 个税申报日配置 | ✅ 已实现 | `NotificationSetting.taxDay` | 可配置每月申报截止日 | +| 个税申报报表 | ❌ 未实现 | — | 无个税申报表导出(自然人电子税务局格式) | +| 个税批量申报 | ❌ 未实现 | — | 未对接税务系统 API | +| 专项附加扣除导入 | ✅ 已实现 | `/import/special-deduction` | 支持批量导入 | + +### 13.2 差距分析与建议 + +| 需求 | 现状 | 建议方案 | 预算评估 | +|------|------|---------|---------| +| 个税申报表导出 | 无 | 生成「个人所得税申报表」Excel,格式对齐自然人电子税务局模板 | 🟢 低 — 后端新增 1 个导出接口 | +| 个税年度汇算提示 | 无 | 每年 3-6 月提示员工进行年度汇算清缴 | 🟢 低 — 定时任务 + 通知 | +| 个税申报数据校验 | 无 | 导出前自动校验:身份证号完整性、累计数据一致性 | 🟢 低 — 校验逻辑 | +| 对接税务系统 | 无 | 调用自然人电子税务局 API 实现在线申报 | 🔴 高 — 需税务系统授权 + API 对接,成本高 | + +### 13.3 个税申报线上化实施路径 + +``` +阶段一(低成本,立即可做): + ✅ 个税自动计算 → 已实现 + ✅ 个税申报日提醒 → 已实现 + 📋 个税申报表 Excel 导出 → 新增导出接口,对齐税务局模板格式 + +阶段二(中等成本): + 📋 个税申报数据校验 → 导出前自动校验数据完整性 + 📋 个税年度汇算提醒 → 每年 3 月推送汇算通知 + +阶段三(高成本,远期规划): + 📋 对接自然人电子税务局 API → 在线批量申报(需获取税务系统授权) +``` + +--- + +## 十四、综合分析与实施建议 + +### 14.1 功能成熟度矩阵 + +| 功能领域 | 计算能力 | 线上化程度 | 分析看板 | 申报对接 | 综合评分 | +|---------|---------|-----------|---------|---------|---------| +| 薪酬核算 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | — | 4/5 | +| 个税 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | — | ⭐ | 3/5 | +| 入职 | ⭐⭐⭐ | ⭐⭐⭐⭐ | — | — | 3.5/5 | +| 合同签署 | ⭐⭐⭐ | ⭐⭐⭐⭐ | — | — | 3.5/5 | +| 离职 | ⭐⭐⭐⭐ | ⭐⭐ | — | — | 3/5 | +| 绩效 | ⭐⭐ | ⭐⭐ | ⭐ | — | 1.5/5 | +| 社保 | ⭐⭐⭐⭐ | ⭐⭐⭐ | — | ⭐ | 3/5 | + +### 14.2 低预算优先实施清单 + +| 优先级 | 功能项 | 预算 | 预期效果 | +|-------|-------|------|---------| +| P0 | 个税申报表 Excel 导出 | 🟢 低 | HR 可直接用导出表去税务局申报,免去手工整理 | +| P0 | 入离职统计看板 | 🟢 低 | Dashboard 新增入离职趋势、离职率,管理层一目了然 | +| P0 | 绩效统计看板 | 🟢 低 | 新增绩效分布、部门对比、趋势分析页面 | +| P1 | 员工离职申请入口 | 🟢 低 | 员工端门户新增离职申请,完善离职线上化 | +| P1 | 薪酬分析看板 | 🟢 低 | 薪酬分布、部门薪酬对比、人均成本趋势 | +| P1 | 离职交接清单 | 🟢 低 | 离职流程增加交接确认步骤 | +| P2 | 合同电子签对接 | 🟡 中 | 对接 e签宝/法大大,实现法律效力电子签 | +| P2 | 个税申报数据校验 | 🟢 低 | 导出前校验数据完整性 | +| P3 | 对接税务系统在线申报 | 🔴 高 | 实现个税全流程线上化 | + +### 14.3 实施建议总结 + +> **核心结论**:系统在薪酬核算和个税计算方面已具备完整的线上化能力,用户反馈"无法实现个税自动算税"可能是操作不熟练导致。当前最大的短板在于**缺乏统计分析看板**和**个税申报报表导出**。 + +**低成本高收益的 3 件事**: + +1. **个税申报表导出**(1-2 天开发量)— 新增 `/export/tax-declaration?month=YYYY-MM` 接口,导出格式对齐自然人电子税务局模板,包含:员工姓名、身份证号、应发工资、社保扣除、公积金扣除、专项附加扣除、应纳税所得额、税率、速算扣除数、已预扣税额、本月应扣税额 +2. **入离职统计看板**(2-3 天开发量)— Dashboard 新增「人员流动」卡片:本月入职人数、离职人数、离职率趋势图(近 12 个月)、部门离职分布 +3. **绩效统计看板**(2-3 天开发量)— 新增 `/tools/performance-dashboard` 页面:绩效等级分布饼图、部门绩效均分对比柱状图、月度/季度趋势折线图、Top 10 / Bottom 10 员工排名 + +以上 3 项均复用现有数据和基础设施,无需新增数据模型,开发成本低,用户感知提升明显。 diff --git a/docs/20260731-Gusto风格UIUX完整优化方案.md b/docs/20260731-Gusto风格UIUX完整优化方案.md new file mode 100644 index 0000000..a66d7fa --- /dev/null +++ b/docs/20260731-Gusto风格UIUX完整优化方案.md @@ -0,0 +1,979 @@ +# TurboHR UI/UX 完整优化方案 + +> 版本:V1.0 +> 日期:2026-07-31 +> 适用范围:企业管理端、员工门户、平台管理端 +> 参考方向:Gusto 的任务驱动体验、温暖可信的视觉语言、员工自助和低门槛业务流程 +> 说明:本方案借鉴 Gusto 的产品原则,不复制其品牌、页面或视觉资产。 + +--- + +## 1. 项目目标 + +TurboHR 已具备员工、合同、考勤、薪酬、社保公积金、离职、制度、证据链、审批、通知、员工门户及 AI 顾问等较完整的后端能力。当前优化重点不应继续堆叠入口,而应将既有能力组织成更容易理解和完成的工作流。 + +本轮优化目标如下: + +1. 将产品从“模块型管理后台”升级为“任务型 HR 工作台”。 +2. 让非专业 HR 用户也能在清晰引导下完成发薪、入职、离职和月度社保等复杂业务。 +3. 建立国际化、可信、温暖但不过度装饰的品牌与视觉体系。 +4. 减少页面跳转、重复录入、操作犹豫和错误返工。 +5. 提高员工自助率,减少 HR 重复答疑和资料收集工作。 +6. 建立可扩展的设计系统,为中英文、多地区和多组织能力预留空间。 + +### 1.1 核心设计原则 + +- **任务优先**:首先告诉用户现在要做什么,而不是先展示系统有哪些模块。 +- **渐进披露**:日常信息优先,高级配置在需要时出现。 +- **一步一决策**:复杂流程每一步只解决一个主要问题。 +- **状态透明**:所有流程都能看见当前状态、负责人、截止时间和下一步。 +- **异常前置**:尽量在提交、归档、发布之前发现问题。 +- **人物优先**:HR 系统中的主体是员工,不是数据表和字段。 +- **一致可预测**:同一类动作、状态和反馈在所有模块中保持一致。 +- **国际框架、本地语义**:交互采用国际通行方式,劳动用工文案保持中国法律语义准确。 + +--- + +## 2. 当前产品诊断 + +### 2.1 信息架构问题 + +当前侧边栏包含工作台、员工管理、薪税社保、合规风控、AI 辅助和系统等多个分组,并且所有分组默认展开。日常高频任务、高级配置、辅助工具和系统管理处于同一层级。 + +主要影响: + +- 新用户无法迅速判断应该从哪里开始。 +- 低频工具持续占用导航空间。 +- 用户需要理解系统模块之间的关系,才能完成真实业务。 +- “考勤确认—加班计算—发薪—工资条发布”等连续工作被人为拆散。 +- 页面入口多,但缺乏统一的“我的工作”视角。 + +### 2.2 页面结构问题 + +- Dashboard 同时承担概览、薪税、风险、待办、成本和人力分析,第一屏焦点不清。 +- Money、Termination、SocialInsurance、AIAssistant 等页面体量过大,局部交互难以保持一致。 +- 页面标题、说明、工具栏、筛选区和数据区缺乏完全统一的结构。 +- 大量操作按钮同时出现,主要动作与次要动作区分不足。 +- 表格能够承载数据,但不够支持重复、高频和批量操作。 +- 页面状态经常表现为模块状态,缺少负责人、截止时间、影响范围和下一步说明。 + +### 2.3 视觉问题 + +- 当前紫色主色与灰白卡片具有较强的通用后台模板感。 +- 品牌名“企业用工专家”描述了功能,但品牌识别度不足。 +- 大量 `text-xs` 导致信息虽然密集,但阅读压力较大。 +- 页面主要依赖卡片和边框分区,缺少明确的视觉层级。 +- 图标、状态标签、按钮密度和留白在不同页面存在差异。 +- `max-w-content` 已配置,但主内容容器未实际限制超宽屏宽度。 + +### 2.4 交互问题 + +- 用户需要跨页面记忆上下文和上一步结果。 +- 部分复杂流程虽有步骤,但缺少持续可见的摘要和风险提示。 +- 批量操作入口常驻,未根据选择状态渐进出现。 +- 错误反馈偏结果型,缺少问题定位与修复建议。 +- AI 顾问与业务上下文分离,用户需要重新描述员工、批次或风险背景。 +- 员工门户功能已覆盖多个场景,但仍更像功能集合,不像统一的员工工作台。 + +--- + +## 3. 目标产品框架 + +### 3.1 目标一级导航 + +| 一级入口 | 主要内容 | 核心用户任务 | +|---|---|---| +| 首页 | 今日任务、审批、风险、截止日、快捷创建、近期动态 | 判断今天先做什么 | +| 团队 | 员工目录、入职、异动、合同、特殊状态、离职 | 管理员工全生命周期 | +| 薪酬 | 发薪、工资条、加班、社保公积金、专项扣除 | 完成每月薪酬工作 | +| 时间 | 考勤、排班、休假、月度确认 | 管理工时和出勤 | +| 合规 | 风险、制度、证据链、用工体检、审计 | 发现并处理用工风险 | + +全局右上区域保留: + +- 全局搜索/命令面板。 +- 快速创建按钮。 +- 待审批入口。 +- 通知入口。 +- 帮助与 AI 助手。 +- 组织及个人菜单。 + +以下内容移入“设置”或“更多”: + +- 文本模板。 +- 数据导入和导出。 +- 用户与权限。 +- 企业信息。 +- 通知规则。 +- 社保和公积金基础配置。 +- 审计配置及平台级管理。 + +### 3.2 导航行为 + +- 默认只展开当前一级导航对应的二级菜单。 +- 记住用户最近访问的页面和分组状态。 +- 当前页面使用底色、左侧标识和文本颜色共同表达,不只依赖颜色。 +- 导航底部提供“设置”和“帮助”,避免与日常模块混排。 +- 桌面端侧边栏宽度建议为 224–240px,可折叠为 64px 图标栏。 +- 移动端不复刻完整侧边栏,使用“首页、团队、待办、我的”四入口底栏。 + +### 3.3 全局搜索和命令面板 + +快捷键建议为 `Cmd/Ctrl + K`,搜索范围包括: + +- 员工姓名、手机号、员工编号。 +- 发薪批次和月份。 +- 合同、制度和证据链。 +- 页面与功能。 +- 可执行命令,例如“新增员工”“创建发薪批次”“发起离职”。 + +搜索结果按“人员、任务、记录、功能”分组,最近使用项优先。敏感信息不应出现在未经授权的搜索结果摘要中。 + +--- + +## 4. 首页:从数据看板升级为任务中心 + +### 4.1 页面目标 + +首页第一屏必须回答: + +1. 今天最需要处理什么? +2. 最近一个重要截止日是什么? +3. 哪些流程或员工存在异常? + +### 4.2 推荐布局 + +#### 顶部区域 + +- 问候语:`早上好,李经理`。 +- 补充信息:`7 月 31 日,星期五 · 上海`。 +- 右侧主按钮:`+ 快速创建`。 +- 可选企业/组织切换器。 + +#### 主任务卡 + +只展示当前最重要的一项任务,例如: + +> 7 月工资等待复核 +> 63 名员工,预计应发 ¥428,560,计划 8 月 5 日发布 +> 3 项异常需要处理 + +主操作:`继续复核`。 +次操作:`查看异常`、`稍后提醒`。 + +主任务的选择规则: + +1. 已逾期事项。 +2. 高风险合规事项。 +3. 需要当前用户审批的事项。 +4. 未来三日内到期事项。 +5. 当前月度流程的下一步。 + +#### 待办队列 + +使用分段控制器切换: + +- 需要我处理。 +- 等待他人。 +- 即将到期。 +- 已完成。 + +每条待办包含:事项、关联员工或批次、截止时间、风险级别、负责人和一个直接操作。 + +#### 人员动态 + +- 待入职。 +- 本周入职。 +- 合同即将到期。 +- 试用期即将结束。 +- 特殊状态提醒。 +- 离职交接中。 + +#### 数据洞察 + +放在第二屏,避免与任务竞争注意力: + +- 本月人力成本。 +- 年度成本趋势。 +- 部门人数变化。 +- 风险分布。 +- 员工完成率和自助率。 + +### 4.3 空状态 + +无待办时不显示空白卡片,应表达积极完成状态: + +> 今天没有待处理事项 +> 下一次发薪准备将在 8 月 1 日开始。 + +可提供 `查看日历` 和 `创建任务` 两个入口。 + +--- + +## 5. 团队与员工体验 + +### 5.1 员工目录 + +默认列表列建议: + +| 列 | 内容 | +|---|---| +| 员工 | 头像、姓名、职位/部门 | +| 状态 | 在职、待入职、试用期、离职处理中、已离职 | +| 入职日期 | 本地化日期和在职时长 | +| 合同 | 合同类型、到期时间或风险 | +| 薪酬状态 | 已配置、缺失、变更待生效 | +| 下一项任务 | 最需要完成的员工相关事项 | +| 操作 | 查看详情和更多菜单 | + +优化要求: + +- 点击整行进入员工详情。 +- 搜索与筛选始终位于同一工具栏。 +- 筛选条件展示为可移除的条件标签。 +- 支持保存视图,例如“合同 30 天内到期”“上海在职员工”。 +- 选中员工后再出现批量操作栏。 +- 批量离职、删除等危险操作必须显示影响范围和不可逆后果。 +- 支持列显示管理、列冻结和舒适/紧凑密度切换。 + +### 5.2 员工 Profile Shell + +员工详情采用统一框架,避免不同档案页面重复搭建标题和导航。 + +头部固定信息: + +- 头像、姓名、职位、部门。 +- 在职状态和关键风险。 +- 联系方式。 +- 入职日期和直属负责人。 +- `编辑资料` 主按钮及更多操作菜单。 + +推荐 Tab: + +1. 概览。 +2. 工作信息。 +3. 薪酬福利。 +4. 考勤休假。 +5. 文件合同。 +6. 绩效与事件。 +7. 历史记录。 + +概览页内容: + +- 待完成事项。 +- 当前合同摘要。 +- 薪酬与参保摘要。 +- 最近考勤异常。 +- 最近文件和记录。 +- 生命周期时间线。 + +隐私要求: + +- 身份证、银行卡、工资默认遮罩。 +- 查看完整敏感信息需要具备权限。 +- 查看和导出敏感数据写入审计日志。 +- 在操作附近说明数据用途,避免用户不清楚为何收集。 + +### 5.3 入职流程 + +统一为五阶段: + +`发送邀请 → 员工填写 → HR 审核 → 合同确认 → 完成配置` + +每位待入职员工显示: + +- 总完成度。 +- 当前阶段。 +- 缺失资料。 +- 员工最后操作时间。 +- 预计入职日期。 +- 可执行动作,如提醒、重新发送、代为补录。 + +完成配置应包含合同、薪酬、社保、班次和系统访问权限,不应只以员工资料提交为终点。 + +### 5.4 离职流程 + +保留现有六步能力,并统一为: + +1. 选择员工和离职类型。 +2. 确认原因与日期。 +3. 合规检查。 +4. 费用结算。 +5. 工作交接和材料。 +6. 复核、审批与执行。 + +桌面端使用“主内容 + 右侧摘要”布局。右侧始终显示: + +- 员工和离职类型。 +- 生效日期。 +- 补偿金额。 +- 风险级别。 +- 缺失材料。 +- 当前审批状态。 + +底部固定操作栏:`保存草稿`、`上一步`、`继续`。最后一步根据权限显示 `提交审批` 或 `执行离职`,避免三个同等权重的终态按钮同时出现。 + +--- + +## 6. 薪酬与发薪体验 + +### 6.1 月度发薪中心 + +将发薪操作组织为一个连续流程: + +`准备数据 → 异常检查 → 复核工资 → 归档 → 发布工资条 → 完成` + +页面顶部显示: + +- 月份和批次名称。 +- 流程步骤。 +- 当前负责人。 +- 计划发布时间。 +- 员工数量和预计总额。 +- 自动保存状态。 + +### 6.2 准备数据 + +集中呈现以下数据源的完成情况: + +- 员工和薪酬结构。 +- 考勤。 +- 加班。 +- 请假。 +- 社保公积金。 +- 专项附加扣除。 +- 奖金、补偿或其他一次性项目。 + +每个数据源显示“已同步、存在异常、尚未导入、无需处理”。支持直接进入问题位置,不要求用户自行寻找模块。 + +### 6.3 异常检查 + +使用后端现有 pre-check 能力构建发薪质量门禁。 + +异常按严重级别分为: + +- 阻断:未配置薪资、关键身份信息缺失、计算失败。 +- 警告:与上月相比变化显著、加班异常、实发为负数。 +- 提示:新员工首月、离职结算、一次性奖金。 + +每条异常需要提供: + +- 员工。 +- 具体字段。 +- 当前值和参考值。 +- 影响。 +- 建议操作。 +- `立即修复` 入口。 + +修复后自动重新检查,并保留处理记录。 + +### 6.4 工资复核表 + +- 固定员工姓名列和实发工资列。 +- 工资项目支持分组展开和折叠。 +- 修改后的单元格明确标识,并支持撤销。 +- 显示与上月的金额和百分比变化。 +- 支持筛选“仅看异常”“仅看已修改”“仅看新增员工”。 +- 表尾固定显示人数、应发、扣款、税额、公司成本和实发合计。 +- 大数据量使用虚拟滚动或服务端分页,避免一次渲染全部员工。 + +### 6.5 归档与发布 + +归档前弹出复核摘要,而不是只询问是否确认: + +- 员工数量。 +- 总金额。 +- 未解决警告。 +- 后续影响。 +- 归档后是否可撤销。 + +归档后将主要操作变为 `发布工资条`。支持立即发布和定时发布,发布完成后展示员工查看/确认进度。 + +--- + +## 7. 时间、考勤和排班 + +### 7.1 模块结构 + +建议二级导航: + +- 月度概览。 +- 每日出勤。 +- 排班。 +- 休假。 +- 规则设置。 + +现有六个平级 Tab 应重新按使用频率分组,高级班次设置放入次级入口。 + +### 7.2 月度确认流程 + +`导入/同步 → 识别异常 → 员工确认 → HR 锁定 → 同步发薪` + +页面显示: + +- 应确认人数。 +- 已确认人数。 +- 未确认人员。 +- 异常考勤数量。 +- 加班总时长。 +- 当前发布状态。 + +“取消发布”属于次要危险操作,应收进更多菜单并说明会对员工端及发薪数据造成的影响。 + +### 7.3 排班 + +- 周视图作为默认视图。 +- 班次用色块与缩写共同表达,不能只依赖颜色。 +- 支持复制上周、按部门批量排班和冲突检测。 +- 在发布排班前检查重复班次、休假冲突和连续工作风险。 + +--- + +## 8. 社保公积金体验 + +### 8.1 日常办理与配置分离 + +日常用户默认进入“月度办理”,配置版本、费率试算和基数调整放入“规则与配置”。 + +月度办理流程: + +`确认增减员 → 检查基数变化 → 生成申报名单 → 导出/办理 → 标记完成` + +每一步显示: + +- 待办数量。 +- 关键变化。 +- 是否存在阻断问题。 +- 上一步完成时间和操作者。 + +### 8.2 配置版本 + +- 使用时间线展示历史版本。 +- 清楚标记当前生效版本和未来版本。 +- 新建版本时显示与当前版本的差异。 +- 应用调基前展示影响人数和公司成本变化。 +- 允许导出受影响员工名单。 + +--- + +## 9. 合规、制度与证据链 + +### 9.1 统一风险中心 + +将风险、用工体检、合同提醒、特殊状态提醒和相关证据入口汇总为统一风险中心。 + +风险列表字段: + +- 风险事项。 +- 关联员工/流程。 +- 风险等级。 +- 截止时间。 +- 负责人。 +- 建议动作。 +- 证据完整度。 +- 处理状态。 + +风险详情使用三栏结构:问题说明、法律/制度依据、处理动作与证据。 + +### 9.2 规章制度 + +制度发布流程: + +`起草 → 审核 → 公示/征求意见 → 发布 → 员工阅读确认 → 归档` + +制度列表优先展示状态、覆盖人数、已读率、未读人员和最近更新时间。员工已读统计应能直接发送提醒。 + +### 9.3 证据链 + +证据链采用时间线表达,按事件展示: + +- 发生时间。 +- 事件类型。 +- 操作者和相关人员。 +- 文件及来源。 +- 是否完整、是否签署或确认。 +- 与制度、审批和员工事件的关联。 + +支持按离职、违纪、考勤、合同等场景生成证据包,并在导出前展示包含内容和敏感信息提示。 + +--- + +## 10. AI 顾问优化 + +### 10.1 从独立聊天升级为上下文助手 + +保留完整 AI 工作区,同时在业务页面提供“询问 AI”入口。系统自动附带当前上下文,例如: + +- 当前员工和已授权档案摘要。 +- 当前发薪批次和异常。 +- 当前离职原因、日期和风险评估。 +- 当前制度文档或证据链。 + +用户不需要重新描述“这个员工是谁、现在在哪一步”。发送前应明确显示即将带入的上下文,并允许取消部分内容。 + +### 10.2 AI 输出结构 + +优先提供结构化结果: + +- 结论。 +- 风险级别。 +- 依据。 +- 建议步骤。 +- 可生成材料。 +- 需要人工确认的部分。 + +AI 建议不能直接执行高风险操作。生成合同、制度或离职材料后,需要进入预览、编辑和确认流程。 + +--- + +## 11. 员工门户 + +### 11.1 目标结构 + +移动端首页优先展示: + +- 我的待办。 +- 最新工资条。 +- 考勤和休假。 +- 合同与文件。 +- 制度阅读确认。 +- 个人资料。 + +底部导航建议:`首页|时间|文件|我的`。 + +### 11.2 员工首页 + +第一屏显示需要员工完成的动作,例如: + +- 确认本月考勤。 +- 阅读新制度。 +- 补充入职材料。 +- 确认合同。 +- 查看工资条。 + +不要首先展示功能说明或宣传内容。每项任务展示截止时间、完成要求和一个明确按钮。 + +### 11.3 自助数据维护 + +在权限允许的前提下支持员工更新: + +- 手机号、地址和紧急联系人。 +- 银行卡。 +- 专项附加扣除。 +- 个人证件和附件。 + +敏感变更进入 HR 审核,并向员工展示“已提交、审核中、已生效、被退回”的完整状态。 + +--- + +## 12. 视觉设计系统 + +### 12.1 品牌气质 + +关键词:可信、温暖、清晰、专业、有人情味、不过度严肃。 + +避免: + +- 通用紫色 SaaS 模板感。 +- 大面积单一米色或深蓝色主题。 +- 过多装饰卡片、渐变球和营销式构图。 +- 超大标题占据工作空间。 +- 用插画替代真实业务信息。 + +### 12.2 建议色彩 + +以下为方向性 Token,最终需通过品牌设计和 WCAG 对比度测试: + +| Token | 建议色值 | 用途 | +|---|---|---| +| `brand-600` | `#C7442E` | 主按钮、当前导航、品牌强调 | +| `brand-700` | `#A93625` | Hover、Active | +| `ink-900` | `#202622` | 主要文字 | +| `ink-600` | `#59615C` | 次要文字 | +| `surface-page` | `#F7F8F6` | 页面背景 | +| `surface-card` | `#FFFFFF` | 内容背景 | +| `border` | `#DDE1DD` | 边框和分隔线 | +| `success-600` | `#237A57` | 成功、安全、完成 | +| `warning-600` | `#A76113` | 警告、临期 | +| `danger-600` | `#B83232` | 高风险、危险操作 | +| `info-600` | `#356A8A` | 信息、进行中 | + +颜色不能单独表达状态。状态组件必须同时包含文字,必要时增加图标。 + +### 12.3 字体 + +建议字体栈: + +```css +font-family: Inter, "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif; +font-variant-numeric: tabular-nums; +``` + +字体层级: + +| 层级 | 字号 | 字重 | 用途 | +|---|---:|---:|---| +| Page title | 24px | 600 | 页面主标题 | +| Section title | 16–18px | 600 | 页面分区 | +| Card title | 14–16px | 600 | 小型内容块 | +| Body | 14px | 400 | 默认正文和表格 | +| Metadata | 12–13px | 400 | 时间、辅助信息 | +| KPI | 24–32px | 600 | 关键数字 | + +不使用随视口宽度缩放字号,不使用负字距。 + +### 12.4 间距和尺寸 + +- 使用 4px 基础单位。 +- 页面水平内边距:移动端 16px,桌面端 24–32px。 +- 主内容最大宽度:1440px;数据密集表格可使用全可用宽度。 +- 常规区块垂直间距:24px。 +- 表单字段间距:16px。 +- 按钮高度:小型 32px,默认 40px,大型 48px。 +- 输入框默认高度:40px。 +- 图标按钮保持稳定的 36×36px 或 40×40px。 +- 卡片圆角不超过 8px。 + +### 12.5 页面标准结构 + +每个管理端页面统一为: + +1. `PageHeader`:标题、说明、状态和主要动作。 +2. `Tabs/SegmentedControl`:只在确有同级视图时使用。 +3. `FilterBar`:搜索、筛选、视图和导出。 +4. `Content`:列表、表格、流程或详情。 +5. `BulkActionBar`:仅选择数据后出现。 +6. `Pagination/Footer`:分页、总数和显示密度。 + +### 12.6 组件补齐 + +建议建立或完善: + +- AppShell。 +- PageHeader。 +- GlobalSearch/CommandPalette。 +- FilterBar 和 FilterChip。 +- DataTable。 +- Stepper。 +- TaskItem。 +- Timeline。 +- StatBlock。 +- InlineAlert。 +- Drawer。 +- SplitPane。 +- StickyActionBar。 +- SensitiveValue。 +- PermissionGate。 +- FormSection。 +- SaveStatus。 +- UndoToast。 + +--- + +## 13. 表单、反馈和可访问性 + +### 13.1 表单 + +- 标签始终可见,placeholder 不代替标签。 +- 必填项统一表达,不在每个标签后堆叠红色星号。 +- 校验错误出现在字段附近,并说明如何修复。 +- 长表单按语义分区,支持保存草稿和离开保护。 +- 对身份证、手机号、银行卡、金额和日期提供格式化输入。 +- 自动保存时显示“保存中/已保存/保存失败”。 + +### 13.2 操作反馈 + +- 成功:Toast,可提供“撤销”。 +- 普通失败:Toast + 字段或区域内错误。 +- 页面加载失败:区域级 Error State 和重试按钮。 +- 危险操作:确认对话框,说明对象、数量、后果及可恢复性。 +- 长耗时任务:显示进度和后台处理状态,不能只让按钮持续转圈。 + +### 13.3 可访问性 + +- 正文和交互文字达到 WCAG AA 对比度。 +- 所有功能支持键盘访问和可见焦点。 +- 图标按钮具有明确的 `aria-label` 和 tooltip。 +- 表格表头、排序状态和选择状态可被辅助技术理解。 +- Modal 打开时锁定焦点,关闭后返回触发元素。 +- 动画遵循 `prefers-reduced-motion`。 +- 移动端触控目标至少 44×44px。 + +--- + +## 14. 国际化与本地化 + +### 14.1 工程要求 + +- 界面文案全部进入 i18n 资源文件。 +- 日期、数字、百分比和货币统一通过 `Intl` 格式化。 +- 内部存储使用 ISO 日期和明确时区。 +- 不在业务组件内拼接中文句子。 +- 布局支持英文文案比中文长 30%–80%。 +- 表格列和按钮不依赖固定中文宽度。 + +### 14.2 语言策略 + +- 第一阶段:简体中文和英文框架。 +- 第二阶段:繁体中文。 +- 第三阶段:根据业务扩展国家/地区包。 + +劳动合同、解聘、社保公积金等功能需要区分“界面语言”和“适用法律地区”。英文界面不意味着将中国法概念简单替换成美国 HR 术语。 + +### 14.3 品牌与文案 + +- 品牌名应简短、可记忆、便于形成中英文组合。 +- “企业用工专家”可保留为价值说明,而非唯一品牌名。 +- 文案使用动作导向表达,例如“复核 7 月工资”,避免“薪税管理功能”。 +- 按钮描述结果,例如“发布工资条”,避免笼统的“确定”。 +- 空状态提供下一步,不使用无帮助的“暂无数据”。 + +--- + +## 15. 后端能力与前端映射 + +| 现有能力 | 推荐前端体验 | 优先级 | +|---|---|---| +| Dashboard todos、风险和合规评分 | 首页任务排序、统一风险中心 | P0 | +| Calendar 和提醒 | 首页截止日、员工时间线、月度日历 | P0 | +| Payroll batch pre-check | 发薪异常质量门禁 | P0 | +| 归档、撤销归档、定时发布 | 发薪 Stepper 和发布中心 | P0 | +| Onboarding link 和资料上传 | 入职进度面板、员工自助资料收集 | P1 | +| Contract confirm | 入职和合同确认闭环 | P1 | +| Termination draft、审批和执行 | 离职流程及审批收件箱 | P0 | +| WorkProcess | 跨模块统一审批和办理中心 | P1 | +| Policy read records | 制度已读率、未读提醒和证据 | P1 | +| Attendance publish/confirm | 月度确认流程和员工响应追踪 | P1 | +| Evidence chain | 员工详情时间线、风险处理证据包 | P1 | +| Audit log | 敏感字段查看和关键操作追踪 | P1 | +| RAG/AI conversation | 上下文 AI、结构化风险建议 | P2 | +| Portal APIs | Employee Hub | P1 | +| Notification settings/logs | 统一通知中心和偏好设置 | P2 | +| Platform org/user APIs | 多组织管理工作台 | P2 | + +### 15.1 建议补充的后端聚合能力 + +尽管现有接口已覆盖大部分功能,前端重构时建议增加少量聚合接口,避免首页和流程页并发请求过多: + +- `GET /workspace/next-actions`:当前用户下一步任务。 +- `GET /workspace/deadlines`:统一截止日和提醒。 +- `GET /employees/:id/summary`:员工 Profile 首屏摘要。 +- `GET /payroll2/batches/:id/workflow`:发薪步骤、异常和完成状态。 +- `GET /approvals/inbox`:跨模块审批收件箱。 +- `GET /search`:全局搜索。 + +聚合接口只负责编排已有业务数据,不重复实现核心计算逻辑。 + +--- + +## 16. 响应式策略 + +### 16.1 桌面端 + +- 适用于完整发薪表、社保办理、配置和批量管理。 +- 1280px 以上使用侧边栏与主内容布局。 +- 超宽屏限制主要内容宽度,数据表格场景可例外铺满。 + +### 16.2 平板 + +- 侧边栏默认折叠。 +- 表格优先保留关键列,其他字段进入详情。 +- 复杂流程保持步骤和摘要,但摘要可折叠。 + +### 16.3 手机端 + +管理员端只优先支持: + +- 查看首页待办。 +- 审批。 +- 查看员工摘要。 +- 处理通知。 +- 查看发薪状态和异常摘要。 + +完整工资表编辑、复杂配置和批量导入应引导用户使用桌面端。员工门户则必须完整支持手机端。 + +--- + +## 17. 技术与组件重构建议 + +### 17.1 页面拆分 + +按领域拆分大型页面: + +```text +pages/payroll/ + PayrollHome.tsx + PayrollWorkflow.tsx + PayrollReviewTable.tsx + PayrollExceptions.tsx + PayrollPublish.tsx + +pages/termination/ + TerminationList.tsx + TerminationWorkflow.tsx + steps/ + components/ + +pages/social/ + MonthlyProcess.tsx + SocialConfig.tsx + HousingFundConfig.tsx + VersionHistory.tsx +``` + +拆分目标不是追求小文件,而是建立清晰的领域边界、复用状态组件和降低单页面变更风险。 + +### 17.2 状态与数据 + +- React Query 的 key 统一工厂化,明确失效规则。 +- 复杂流程使用 reducer 或状态机表达,避免大量彼此关联的 `useState`。 +- 筛选、Tab 和分页写入 URL,支持刷新、分享和返回。 +- 对乐观更新提供撤销机制。 +- 大型表格考虑列配置持久化。 + +### 17.3 设计 Token + +将颜色、圆角、阴影、字号、层级、宽度和动效统一定义为 Token,不继续在页面内直接使用 `bg-[#...]` 等任意值。Tailwind 配置与 CSS 变量应共用同一语义命名。 + +--- + +## 18. 分阶段实施计划 + +### Phase 0:基线和验证,3–5 天 + +- 建立核心任务的可用性基线。 +- 记录发薪、入职和离职的点击数及完成时间。 +- 整理角色和权限矩阵。 +- 确认中英文品牌策略。 +- 建立桌面、平板和手机测试视口。 + +交付物:体验基线、页面清单、角色矩阵、设计原则确认稿。 + +### Phase 1:全局框架,2–3 周 + +- 新导航和 AppShell。 +- PageHeader、FilterBar、DataTable 等基础组件。 +- 新色彩、字体、间距和状态体系。 +- 首页任务中心。 +- 全局宽度和响应式修复。 +- WCAG AA 基础整改。 + +验收:一级导航不超过五项;首页第一屏存在唯一主任务;所有页面使用统一标题结构;1920px 下内容不无边界拉伸。 + +### Phase 2:核心业务流程,3–5 周 + +- 发薪工作流。 +- 离职工作流。 +- 月度社保办理。 +- 月度考勤确认。 +- 员工 Profile Shell。 + +验收:核心任务无需用户跨模块寻找下一步;所有流程能显示当前步骤、阻断项和负责人;危险操作明确说明影响。 + +### Phase 3:员工自助与协作,3–4 周 + +- Employee Hub。 +- 入职进度中心。 +- 审批收件箱。 +- 制度阅读追踪。 +- 通知中心。 + +验收:员工可在手机端完成主要自助任务;HR 能统一查看员工响应状态;审批不再分散在业务页面中。 + +### Phase 4:智能化和国际化,3–4 周 + +- 上下文 AI。 +- 全局搜索和命令面板。 +- 中英文资源化。 +- Intl 日期、数字和货币格式化。 +- 保存视图和个性化工作台。 + +验收:AI 自动识别当前业务上下文;核心页面完成中英文切换;英文文本不出现按钮溢出和布局破坏。 + +--- + +## 19. 成功指标 + +### 19.1 效率指标 + +- 首次创建员工到入职完成的时间。 +- 创建发薪批次到发布工资条的时间。 +- 每次发薪的点击数和页面跳转数。 +- 异常发现到修复的平均时间。 +- 离职流程平均完成时长。 +- 批量操作使用率。 + +### 19.2 质量指标 + +- 发薪归档后撤销率。 +- 工资条发布错误率。 +- 入职资料一次通过率。 +- 逾期合同和未处理高风险数量。 +- 重复导入和数据冲突数量。 +- 高风险操作误触和取消率。 + +### 19.3 体验指标 + +- 首页待办完成率。 +- 员工自助完成率。 +- 员工制度阅读确认率。 +- 移动端任务完成率。 +- 客服/内部咨询数量。 +- SUS 或内部满意度评分。 + +建议目标: + +- 发薪操作时间下降 30%。 +- 核心流程页面跳转下降 40%。 +- 入职资料一次通过率提升至 85% 以上。 +- 员工自助完成率达到 80% 以上。 +- 高风险事项逾期数量下降 50%。 + +--- + +## 20. 页面验收清单 + +每个页面发布前至少验证: + +- 用户能否在 5 秒内理解页面目的。 +- 是否存在唯一且明确的主要动作。 +- 是否说明当前状态和下一步。 +- 是否避免同时展示过多低频操作。 +- 加载、空、错误、无权限和部分数据状态是否完整。 +- 危险操作是否说明对象、数量、后果和可恢复性。 +- 文字和状态对比度是否达到 WCAG AA。 +- 仅使用键盘是否能完成主要操作。 +- 360px、768px、1280px、1440px、1920px 是否无溢出和遮挡。 +- 中英文最长文案是否不破坏布局。 +- 敏感信息是否按权限遮罩并留下审计记录。 +- 页面返回后筛选、分页和滚动位置是否合理保留。 + +--- + +## 21. 建议优先制作的高保真原型 + +为了尽快验证方向,第一轮只需设计以下八个关键页面: + +1. 新首页任务中心。 +2. 新导航和全局搜索。 +3. 员工目录。 +4. 员工概览详情。 +5. 发薪流程总览。 +6. 发薪异常和工资复核表。 +7. 离职流程。 +8. 员工门户首页。 + +上述页面覆盖导航、人物、任务、数据表、复杂流程、风险和移动端,可用于验证整个设计系统,无需一开始重画所有页面。 + +--- + +## 22. 参考资料 + +- Gusto 产品能力总览: +- Gusto Employee Self-Serve: +- Gusto 2025 Feature Showcase: +- Gusto 报表与自定义报告: +- Gusto Money Dashboard: + +这些参考资料体现的共同方向是:减少专业门槛、围绕下一步组织界面、让异常在提交前出现、强化员工自助,并使跨模块数据自然衔接。TurboHR 应保留自身在中国劳动用工合规、证据链和离职处理方面的差异化能力,在此基础上建立国际化产品体验。 + diff --git a/docs/20260731-TurboHR完整优化与功能对照方案.md b/docs/20260731-TurboHR完整优化与功能对照方案.md new file mode 100644 index 0000000..817617c --- /dev/null +++ b/docs/20260731-TurboHR完整优化与功能对照方案.md @@ -0,0 +1,904 @@ +# TurboHR 完整优化与功能对照方案 + +> 版本:V2.0(整合版) +> 日期:2026-07-31 +> 整合来源:UI/UX 完整优化方案 + 系统操作问题与功能对照分析 +> 参考方向:Gusto 的任务驱动体验、温暖可信的视觉语言、员工自助和低门槛业务流程 + +--- + +## 目录 + +- [Part 1:项目概述与诊断](#part-1) +- [Part 2:目标产品框架](#part-2) +- [Part 3:模块设计与功能对照](#part-3) +- [Part 4:视觉设计系统](#part-4) +- [Part 5:工程与技术](#part-5) +- [Part 6:实施计划与指标](#part-6) + +--- + + +# Part 1:项目概述与诊断 + +## 1. 项目目标 + +TurboHR 已具备员工、合同、考勤、薪酬、社保公积金、离职、制度、证据链、审批、通知、员工门户及 AI 顾问等较完整的后端能力。当前优化重点不应继续堆叠入口,而应将既有能力组织成更容易理解和完成的工作流,同时补齐功能短板。 + +本轮优化目标: + +1. 将产品从"模块型管理后台"升级为"任务型 HR 工作台"。 +2. 让非专业 HR 用户也能在清晰引导下完成发薪、入职、离职和月度社保等复杂业务。 +3. 建立可信、温暖但不过度装饰的品牌与视觉体系。 +4. 减少页面跳转、重复录入、操作犹豫和错误返工。 +5. 提高员工自助率,减少 HR 重复答疑和资料收集工作。 +6. 补齐用户反馈的功能短板:合同类型覆盖、导入导出对齐、社保灵活性、绩效分析看板、个税申报报表等。 + +### 1.1 核心设计原则 + +- **任务优先**:首先告诉用户现在要做什么,而不是先展示系统有哪些模块。 +- **渐进披露**:日常信息优先,高级配置在需要时出现。 +- **一步一决策**:复杂流程每一步只解决一个主要问题。 +- **状态透明**:所有流程都能看见当前状态、负责人、截止时间和下一步。 +- **异常前置**:尽量在提交、归档、发布之前发现问题。 +- **人物优先**:HR 系统中的主体是员工,不是数据表和字段。 +- **一致可预测**:同一类动作、状态和反馈在所有模块中保持一致。 +- **国际框架、本地语义**:交互采用国际通行方式,劳动用工文案保持中国法律语义准确。 + +## 2. 当前产品诊断 + +### 2.1 信息架构问题 + +当前侧边栏包含工作台、员工管理、薪税社保、合规风控、AI 辅助和系统等多个分组,且默认展开。日常高频任务、高级配置、辅助工具和系统管理处于同一层级。 + +主要影响:新用户无法迅速判断从哪开始;低频工具占用导航空间;连续工作被拆散;缺乏统一的"我的工作"视角。 + +### 2.2 页面结构问题 + +- Dashboard 同时承担概览、薪税、风险、待办、成本和人力分析,第一屏焦点不清。 +- Money、Termination、SocialInsurance 等页面体量过大,交互不一致。 +- 主要动作与次要动作区分不足。 + +### 2.3 视觉问题 + +- 紫色主色有通用后台模板感;`text-xs` 密度高阅读压力大;缺少明确视觉层级。 + +### 2.4 交互问题 + +- 用户需跨页面记忆上下文;批量操作入口常驻;AI 顾问与业务上下文分离。 + +### 2.5 功能差距总览 + +| 模块 | 用户反馈问题 | 系统实际状态 | 严重程度 | 修复难度 | +|------|------------|------------|---------|---------| +| 花名册 | 缺少劳务/承揽/临时工/非全合同类型 | 枚举缺 3 种,前端缺 2 种展示 | 🔴 高 | 中 | +| 花名册 | 导出字段不全、导入导出错位 | 导出 7 列 vs 导入 19 列 | 🔴 高 | 低 | +| 工资 | 缺少计件/计时计薪方式 | 无相关字段和批次类型 | 🟡 中 | 高 | +| 社保 | 无社保人员无法标记 | 入职自动创建社保记录 | 🟡 中 | 中 | +| 社保 | 商险/意外险无独立模块 | 后端有 extraInsurances 字段 | 🟡 中 | 中 | +| 社保 | 无公积金员工自动匹配 | 无条件创建公积金记录 | 🟡 中 | 低 | +| 考勤 | 无打卡功能 | 系统无打卡能力 | 🟡 中 | 高 | +| 考勤 | 第三方数据需手工导入 | 有 Excel 导入但无 API 对接 | 🟢 低 | 中 | +| 发薪 | 个税无法自动算税 | **已实现**,用户可能未发现 | 🟢 低 | — | +| 发票 | 无发票模块 | 完全未实现 | 🟡 中 | 高 | +| 审批 | 无审批模块 | 仅解聘有状态流转 | 🟡 中 | 高 | +| 绩效 | 无绩效分析看板 | 有绩效记录但无统计分析 | 🟡 中 | 低 | +| 个税 | 无个税申报报表导出 | 有计算但无申报表 | 🟡 中 | 低 | + +### 2.6 功能成熟度矩阵 + +| 功能领域 | 计算能力 | 线上化程度 | 分析看板 | 申报对接 | 综合评分 | +|---------|---------|-----------|---------|---------|---------| +| 薪酬核算 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | — | 4/5 | +| 个税 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | — | ⭐ | 3/5 | +| 入职 | ⭐⭐⭐ | ⭐⭐⭐⭐ | — | — | 3.5/5 | +| 合同签署 | ⭐⭐⭐ | ⭐⭐⭐⭐ | — | — | 3.5/5 | +| 离职 | ⭐⭐⭐⭐ | ⭐⭐ | — | — | 3/5 | +| 绩效 | ⭐⭐ | ⭐⭐ | ⭐ | — | 1.5/5 | +| 社保 | ⭐⭐⭐⭐ | ⭐⭐⭐ | — | ⭐ | 3/5 | + +--- + + +# Part 2:目标产品框架 + +## 3. 目标一级导航 + +| 一级入口 | 主要内容 | 核心用户任务 | +|---|---|---| +| 首页 | 今日任务、审批、风险、截止日、快捷创建、近期动态 | 判断今天先做什么 | +| 团队 | 员工目录、入职、异动、合同、特殊状态、离职 | 管理员工全生命周期 | +| 薪酬 | 发薪、工资条、加班、社保公积金、专项扣除 | 完成每月薪酬工作 | +| 时间 | 考勤、排班、休假、月度确认 | 管理工时和出勤 | +| 合规 | 风险、制度、证据链、用工体检、审计 | 发现并处理用工风险 | + +全局右上区域保留:全局搜索/命令面板、快速创建按钮、待审批入口、通知入口、帮助与 AI 助手、组织及个人菜单。 + +以下内容移入"设置"或"更多":文本模板、数据导入导出、用户与权限、企业信息、通知规则、社保公积金基础配置、审计配置。 + +## 3.1 导航行为 + +- 默认只展开当前一级导航对应的二级菜单。 +- 记住用户最近访问的页面和分组状态。 +- 桌面端侧边栏 224–240px,可折叠为 64px 图标栏。 +- 移动端使用"首页、团队、待办、我的"四入口底栏。 + +## 3.2 全局搜索和命令面板 + +快捷键 `Cmd/Ctrl + K`,搜索范围:员工、发薪批次、合同、页面与功能、可执行命令。结果按"人员、任务、记录、功能"分组,敏感信息不出现在未授权摘要中。 + +--- + + +# Part 3:模块设计与功能对照 + +## 4. 首页:从数据看板升级为任务中心 + +### 4.1 页面目标 + +首页第一屏必须回答:今天最需要处理什么?最近一个重要截止日是什么?哪些流程或员工存在异常? + +### 4.2 推荐布局 + +**顶部区域**:问候语 + 日期 + `+ 快速创建` 主按钮。 + +**主任务卡**:只展示当前最重要的一项任务,主操作 + 次操作。选择规则:已逾期 → 高风险 → 需审批 → 三日内到期 → 月度流程下一步。 + +**待办队列**:分段控制器切换"需要我处理 / 等待他人 / 即将到期 / 已完成"。每条包含事项、关联对象、截止时间、风险级别、负责人、直接操作。 + +**人员动态**:待入职、本周入职、合同即将到期、试用期即将结束、特殊状态、离职交接中。 + +**数据洞察(第二屏)**:本月人力成本、年度成本趋势、部门人数变化、风险分布、员工完成率。**新增**:入离职趋势图(近 12 个月离职率)、绩效概览(等级分布、部门均分)。 + +### 4.3 空状态 + +> 今天没有待处理事项。下一次发薪准备将在 8 月 1 日开始。 + +提供 `查看日历` 和 `创建任务` 入口。 + +--- + +## 5. 团队与员工管理 + +### 5.1 功能对照 + +#### 合同类型覆盖问题 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 只有标准劳动合同界面,劳务/承揽/临时工/非全日制无法录入 | `ContractType` 枚举 5 种:FIXED、UNFIXED、UNSIGNED、LABOR、INTERNSHIP | 后端已支持 LABOR/INTERNSHIP 但前端未暴露;**承揽合同、临时工、非全日制** 3 种完全缺失 | + +#### 花名册导入导出错位 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 导出只能导出部分基本信息 | `export.routes.ts` 仅 7 列 | 导入 19 字段 vs 导出 7 字段,严重不对齐 | + +#### 现有功能 + +| 功能项 | 支持情况 | 实现位置 | +|-------|---------|---------| +| 单个员工录入 | ✅ | `Roster.tsx` → `AddEmployeeModal` | +| 批量导入 | ✅ | `import.routes.ts` → `/import/excel` | +| 花名册导出 | ⚠️ 仅 7 列 | `export.routes.ts` → `/export/roster` | +| 全量数据导出 | ✅ | `export.routes.ts` → `/export/all` | +| 员工详情 | ✅ 10 个 Tab | `roster/EmployeeProfile.tsx` | +| 批量续签/解聘 | ✅ | `Roster.tsx` | +| 调薪/部门变更 | ✅ | `roster/modals.tsx` | + +### 5.2 员工目录优化 + +默认列表列:员工(头像/姓名/职位)、状态、入职日期、合同、薪酬状态、下一项任务、操作。 + +优化要求:点击整行进入详情;搜索与筛选同一工具栏;筛选条件为可移除标签;支持保存视图;选中后才出现批量操作栏;危险操作显示影响范围。 + +### 5.3 员工 Profile Shell + +头部固定:头像、姓名、职位、部门、在职状态、联系方式、入职日期、`编辑资料` 主按钮。 + +推荐 Tab:概览、工作信息、薪酬福利、考勤休假、文件合同、绩效与事件、历史记录。 + +隐私要求:身份证/银行卡/工资默认遮罩;查看敏感信息需权限并写审计日志。 + +### 5.4 入职流程 + +统一五阶段:`发送邀请 → 员工填写 → HR 审核 → 合同确认 → 完成配置` + +| 功能项 | 支持情况 | 说明 | +|-------|---------|------| +| 入职线上化 | ✅ | `OnboardingLink` + `portal.routes.ts`,HR 生成链接 → 员工填写 → 上传附件 → HR 审核 | +| 入职附件上传 | ✅ | `/onboarding/:token/upload` | +| 入职信息 PDF 导出 | ❌ | 建议补充 | +| 入职材料清单勾选 | ❌ | 建议补充 | + +每位待入职员工显示:总完成度、当前阶段、缺失资料、最后操作时间、预计入职日期、可执行动作。 + +### 5.5 合同签署线上化 + +| 功能项 | 支持情况 | 说明 | +|-------|---------|------| +| 合同签署线上化 | ✅ | `ContractConfirmLink`,HR 发送链接 → 员工验证码验证 → 确认签署 | +| 签署证据链 | ✅ | 自动记录 IP/时间/验证码 | +| 第三方电子签 | ⚠️ | 有 ELECTRONIC 字段但未对接 e签宝/法大大 | + +现有流程:`HR 创建合同 → 生成确认链接 → 员工验证码验证 → 确认签署 → 自动生成证据链` + +### 5.6 离职流程 + +统一六步:选择员工 → 确认原因日期 → 合规检查 → 费用结算 → 工作交接 → 复核审批执行。 + +| 功能项 | 支持情况 | 说明 | +|-------|---------|------| +| 离职线上化 | ⚠️ 部分 | HR 端可发起,员工端无离职申请入口 | +| 离职确认 | ❌ | 员工无法在线确认 | +| 离职交接清单 | ❌ | 建议增加 | +| 解聘审批流 | ⚠️ | 有 7 种状态字段但前端无审批界面 | + +桌面端使用"主内容 + 右侧摘要"布局,底部固定操作栏。 + +--- + +## 6. 薪酬与发薪体验 + +### 6.1 功能对照 + +#### 计薪方式覆盖 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 只有标准工时工资,计件/计时无法录入 | `BatchEntry` 有基本工资/加班费/津贴/奖金等 | 缺少计件工资字段(件数/单价/时薪/工时)和对应批次类型 | + +#### 个税自动算税 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 无法实现个税自动算税 | **已实现** `calcCumulativeTax()` 累计预扣法 + `calcBonusTax()` 年终奖单独计税 | 用户反馈与系统现状不符,可能未发现该功能 | + +#### 现有功能清单 + +| 功能项 | 支持情况 | 说明 | +|-------|---------|------| +| 薪酬核算 | ✅ | `calcBatchEntry()` 自动计算全流程 | +| 社保公积金自动计算 | ✅ | 按城市配置比例 | +| 个税自动计算 | ✅ | 累计预扣法,7 级超额累进 | +| 年终奖单独计税 | ✅ | `calcBonusTax()` | +| 多批次发薪 | ✅ | 4 种批次类型 | +| 薪酬模板 | ✅ | `PayslipItem` 自定义薪资项 | +| 工资条在线发放 | ✅ | 支持定时发送、员工端确认 | +| Excel 导入/导出 | ✅ | `/import/payroll` + `/export/payroll` | +| 个税预览 | ✅ | `/payroll/tax-preview` | +| 个税跳档预警 | ✅ | 归档时检测 | +| 薪资项手工覆盖 | ✅ | 社保/公积金可 override | +| 计件/计时工资 | ❌ | 无相关字段 | +| 薪酬分析看板 | ❌ | 缺少薪酬分布/趋势/部门对比 | + +### 6.2 月度发薪中心 + +连续流程:`准备数据 → 异常检查 → 复核工资 → 归档 → 发布工资条 → 完成` + +页面顶部显示:月份、批次名称、流程步骤、当前负责人、计划发布时间、员工数量、预计总额、自动保存状态。 + +### 6.3 准备数据 + +集中呈现数据源完成情况:员工薪酬结构、考勤、加班、请假、社保公积金、专项附加扣除、奖金/补偿。每个数据源显示"已同步/存在异常/尚未导入/无需处理"。 + +### 6.4 异常检查 + +异常分三级:阻断(未配置薪资/计算失败)、警告(变化显著/实发为负)、提示(新员工/离职结算)。每条异常提供员工、字段、当前值/参考值、影响、建议操作、`立即修复` 入口。 + +### 6.5 工资复核表 + +固定姓名列和实发列;工资项目分组展开;修改单元格标识并支持撤销;显示与上月变化;支持筛选"仅看异常/已修改/新增";表尾固定合计;大数据量虚拟滚动。 + +### 6.6 归档与发布 + +归档前弹出复核摘要(人数/总额/未解决警告/后续影响/可撤销性)。归档后主操作变为 `发布工资条`,支持立即/定时发布,展示员工查看确认进度。 + +--- + +## 7. 个税申报线上化 + +### 7.1 现状 + +| 功能项 | 支持情况 | 说明 | +|-------|---------|------| +| 个税计算 | ✅ | 累计预扣法,7 级超额累进 | +| 个税预览 | ✅ | `/payroll/tax-preview` | +| 个税累计数据 | ✅ | `Payslip.ytdTaxDeducted` | +| 个税申报提醒 | ✅ | 每月申报日提醒 | +| 申报日配置 | ✅ | `NotificationSetting.taxDay` | +| 专项附加扣除导入 | ✅ | `/import/special-deduction` | +| 个税申报报表 | ❌ | 无导出功能 | +| 个税批量申报 | ❌ | 未对接税务系统 | + +### 7.2 实施路径 + +``` +阶段一(低成本): + ✅ 个税自动计算 → 已实现 + ✅ 申报日提醒 → 已实现 + 📋 申报表 Excel 导出 → 新增 /export/tax-declaration 接口 + +阶段二(中等成本): + 📋 申报数据校验 → 导出前校验完整性 + 📋 年度汇算提醒 → 每年 3 月推送 + +阶段三(高成本): + 📋 对接自然人电子税务局 API → 在线批量申报 +``` + +--- + +## 8. 时间、考勤和排班 + +### 8.1 功能对照 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 无法实现打卡 | 无打卡接口 | 系统定位为 HR 管理后台非考勤终端 | +| 第三方数据需手工导入 | 有 Excel 导入 | 缺少钉钉/企微/飞书 API 对接 | + +现有功能:考勤确认、班次管理、排班、每日出勤、月度报表、休假记录、Excel 批量导入、员工端考勤查看 — 全部 ✅。 + +### 8.2 模块优化 + +二级导航建议:月度概览、每日出勤、排班、休假、规则设置。高级班次设置放入次级入口。 + +月度确认流程:`导入/同步 → 识别异常 → 员工确认 → HR 锁定 → 同步发薪` + +排班:周视图默认;色块+缩写表达班次;支持复制上周/批量排班/冲突检测。 + +第三方对接建议:钉钉/企微/飞书 API(🟡 中预算)。 + +--- + +## 9. 社保公积金体验 + +### 9.1 功能对照 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 全部正规五险一金,无社保人员模块 | 固定 5 险 | 缺少「无社保」选项,入职自动创建 | +| 纯商险/意外险无法录入 | `extraInsurances` JSON 字段 | 后端支持但前端无独立商险模块 | +| 无公积金员工自动匹配 | 无条件创建公积金记录 | 缺少「是否缴纳公积金」开关 | +| 基数自动为应发工资 | 未填写时默认使用月工资 | 无法设为 0 或不缴 | + +现有功能:社保配置(多城市多版本)、公积金配置、附加险种配置、基数调整、月度办理、专项附加扣除导入、版本管理、社保导出 — 全部 ✅。 + +### 9.2 体验优化 + +日常办理与配置分离:日常用户默认进入"月度办理",配置放入"规则与配置"。 + +月度办理流程:`确认增减员 → 检查基数变化 → 生成申报名单 → 导出/办理 → 标记完成` + +配置版本:时间线展示历史版本;新建版本时显示差异;调基前展示影响人数和成本变化。 + +### 9.3 灵活性改进 + +| 需求 | 建议方案 | 预算 | +|------|---------|------| +| 「无社保」选项 | 入职增加「是否参保」开关 | 🟢 低 | +| 「不缴纳公积金」选项 | 入职增加开关 | 🟢 低 | +| 商险独立管理 | 社保页面增加「商险管理」Tab | 🟡 中 | + +--- + +## 10. 合规、制度与证据链 + +### 10.1 统一风险中心 + +将风险、用工体检、合同提醒、特殊状态汇总为统一风险中心。风险列表字段:事项、关联对象、等级、截止时间、负责人、建议动作、证据完整度、处理状态。详情使用三栏结构:问题说明、法律依据、处理动作与证据。 + +### 10.2 规章制度 + +发布流程:`起草 → 审核 → 公示 → 发布 → 员工阅读确认 → 归档`。列表优先展示状态、覆盖人数、已读率、未读人员。 + +### 10.3 证据链 + +时间线表达,按事件展示时间、类型、操作者、文件、完整性。支持按离职/违纪/考勤/合同等场景生成证据包。 + +--- + +## 11. 绩效分析与统计看板 + +### 11.1 现状 + +| 功能项 | 支持情况 | 说明 | +|-------|---------|------| +| 绩效记录录入 | ✅ | `roster/PerformanceInfo.tsx`,考核周期/得分/等级/评语/改进计划 | +| 绩效员工确认 | ✅ | `PerformanceRecord.employeeAck` | +| 绩效看板 | ❌ | 无独立统计看板 | +| 绩效趋势分析 | ❌ | 无趋势/部门对比/等级分布 | +| 绩效与薪酬联动 | ❌ | 未与奖金分配关联 | + +### 11.2 建议方案 + +| 需求 | 建议方案 | 预算 | +|------|---------|------| +| 绩效统计看板 | 新增 `/tools/performance-dashboard`,等级分布饼图/部门对比柱状图/趋势折线图/Top-Bottom 排名 | 🟢 低 | +| 绩效与薪酬联动 | 发薪批次增加「绩效奖金」列,关联绩效等级计算系数 | 🟡 中 | +| 绩效改进追踪 | 增加改进目标/完成度/复评得分字段 | 🟢 低 | + +### 11.3 可复用基础设施 + +Dashboard 看板组件、`/dashboard/workforce-stats` 聚合 API 模式、年度价值报告、recharts 图表库。 + +--- + +## 12. AI 顾问优化 + +从独立聊天升级为上下文助手:业务页面提供"询问 AI"入口,自动附带当前员工/批次/风险上下文。发送前显示带入的上下文并允许取消。 + +AI 输出结构化:结论、风险级别、依据、建议步骤、可生成材料、需人工确认部分。AI 建议不能直接执行高风险操作。 + +--- + +## 13. 员工门户 + +移动端首页优先展示:我的待办、最新工资条、考勤休假、合同文件、制度阅读确认、个人资料。底部导航:`首页|时间|文件|我的`。 + +第一屏显示需要员工完成的动作,每项展示截止时间、完成要求、明确按钮。 + +自助数据维护:手机号/地址/紧急联系人/银行卡/专项附加扣除/证件附件。敏感变更进入 HR 审核,展示完整状态。 + +--- + +## 14. 发票与审批模块 + +| 用户反馈 | 系统现状 | 差距分析 | +|---------|---------|---------| +| 无发票模块 | 完全未实现 | 需从零开发 | +| 无审批模块 | 仅解聘有 7 种状态字段,无通用审批引擎 | 需从零开发通用审批流 | + +现有相关:解聘审批流(⚠️ 有状态无界面)、操作日志审计(✅)、合同确认链接(✅)。 + +建议:发票模块从零设计(🔴 高);通用审批引擎支持请假/调薪/合同等多场景(🔴 高);解聘审批界面前端补充(🟢 低)。 + +--- + + +# Part 4:视觉设计系统 + +## 15. 品牌气质 + +关键词:可信、温暖、清晰、专业、有人情味。避免通用紫色 SaaS 模板感、大面积单一色调、过多装饰卡片。 + +## 16. 建议色彩 + +| Token | 建议色值 | 用途 | +|---|---|---| +| `brand-600` | `#C7442E` | 主按钮、当前导航、品牌强调 | +| `brand-700` | `#A93625` | Hover、Active | +| `ink-900` | `#202622` | 主要文字 | +| `ink-600` | `#59615C` | 次要文字 | +| `surface-page` | `#F7F8F6` | 页面背景 | +| `surface-card` | `#FFFFFF` | 内容背景 | +| `border` | `#DDE1DD` | 边框和分隔线 | +| `success-600` | `#237A57` | 成功、完成 | +| `warning-600` | `#A76113` | 警告、临期 | +| `danger-600` | `#B83232` | 高风险、危险操作 | +| `info-600` | `#356A8A` | 信息、进行中 | + +颜色不能单独表达状态,必须同时包含文字。 + +## 17. 字体 + +```css +font-family: Inter, "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif; +font-variant-numeric: tabular-nums; +``` + +| 层级 | 字号 | 字重 | 用途 | +|---|---:|---:|---| +| Page title | 24px | 600 | 页面主标题 | +| Section title | 16–18px | 600 | 页面分区 | +| Card title | 14–16px | 600 | 小型内容块 | +| Body | 14px | 400 | 默认正文和表格 | +| Metadata | 12–13px | 400 | 时间、辅助信息 | +| KPI | 24–32px | 600 | 关键数字 | + +## 18. 间距和尺寸 + +4px 基础单位;页面水平内边距移动端 16px / 桌面端 24–32px;主内容最大宽度 1440px;常规区块垂直间距 24px;表单字段间距 16px;按钮高度 32/40/48px;卡片圆角不超过 8px。 + +## 19. 页面标准结构 + +1. `PageHeader`:标题、说明、状态、主要动作。 +2. `Tabs/SegmentedControl`:只在确有同级视图时使用。 +3. `FilterBar`:搜索、筛选、视图、导出。 +4. `Content`:列表、表格、流程或详情。 +5. `BulkActionBar`:仅选择数据后出现。 +6. `Pagination/Footer`:分页、总数、显示密度。 + +## 20. 组件补齐 + +AppShell、PageHeader、GlobalSearch/CommandPalette、FilterBar/FilterChip、DataTable、Stepper、TaskItem、Timeline、StatBlock、InlineAlert、Drawer、SplitPane、StickyActionBar、SensitiveValue、PermissionGate、FormSection、SaveStatus、UndoToast。 + +## 21. 表单、反馈和可访问性 + +**表单**:标签始终可见;校验错误在字段附近说明修复方法;长表单按语义分区支持草稿;格式化输入(身份证/手机/银行卡/金额/日期);自动保存显示状态。 + +**操作反馈**:成功 Toast 可撤销;失败 Toast + 区域错误;页面加载失败 Error State + 重试;危险操作确认对话框说明后果;长耗时任务显示进度。 + +**可访问性**:WCAG AA 对比度;键盘访问和可见焦点;图标按钮 `aria-label`;Modal 焦点锁定;`prefers-reduced-motion`;移动端触控目标 ≥ 44×44px。 + +--- + + +# Part 5:工程与技术 + +## 22. 文案与表达规范 + +- 文案使用动作导向表达("复核 7 月工资"而非"薪税管理功能")。 +- 按钮描述结果("发布工资条"而非"确定")。 +- 空状态提供下一步,不使用无帮助的"暂无数据"。 +- 日期/数字/百分比/货币通过 `Intl` 格式化。 +- 内部存储使用 ISO 日期和明确时区。 + +## 23. 后端能力与前端映射 + +| 现有能力 | 推荐前端体验 | 优先级 | +|---|---|---| +| Dashboard todos、风险评分 | 首页任务排序、统一风险中心 | P0 | +| Calendar 和提醒 | 首页截止日、月度日历 | P0 | +| Payroll batch pre-check | 发薪异常质量门禁 | P0 | +| 归档/撤销/定时发布 | 发薪 Stepper 和发布中心 | P0 | +| Termination draft/审批 | 离职流程及审批收件箱 | P0 | +| 个税计算 + 申报提醒 | 个税申报表导出 | P0 | +| Onboarding link | 入职进度面板 | P1 | +| Contract confirm | 合同确认闭环 | P1 | +| WorkProcess | 跨模块统一审批中心 | P1 | +| Policy read records | 制度已读率、未读提醒 | P1 | +| Attendance publish/confirm | 月度确认流程 | P1 | +| Evidence chain | 员工时间线、证据包 | P1 | +| Audit log | 敏感字段查看追踪 | P1 | +| Portal APIs | Employee Hub | P1 | +| PerformanceRecord | 绩效统计看板 | P1 | +| RAG/AI conversation | 上下文 AI | P2 | +| Notification settings | 统一通知中心 | P2 | +| Platform org/user APIs | 多组织管理工作台 | P2 | + +### 23.1 建议补充的聚合接口 + +- `GET /workspace/next-actions`:当前用户下一步任务。 +- `GET /workspace/deadlines`:统一截止日。 +- `GET /employees/:id/summary`:员工 Profile 首屏摘要。 +- `GET /payroll2/batches/:id/workflow`:发薪步骤和异常。 +- `GET /approvals/inbox`:跨模块审批收件箱。 +- `GET /search`:全局搜索。 +- `GET /performance/dashboard`:绩效统计聚合。 +- `GET /dashboard/turnover`:入离职统计聚合。 +- `GET /export/tax-declaration?month=YYYY-MM`:个税申报表导出。 + +## 24. 响应式策略 + +**桌面端**:1280px+ 侧边栏与主内容布局;超宽屏限制主要内容宽度。 + +**平板**:侧边栏默认折叠;表格保留关键列;复杂流程摘要可折叠。 + +**手机端**:管理员端仅支持查看待办/审批/员工摘要/通知/发薪状态;员工门户完整支持。 + +## 25. 技术重构建议 + +**页面拆分**:按领域拆分大型页面(payroll/termination/social 等子目录)。 + +**状态与数据**:React Query key 工厂化;复杂流程用 reducer/状态机;筛选/Tab/分页写入 URL;乐观更新提供撤销。 + +**设计 Token**:颜色/圆角/阴影/字号/层级统一定义为 Token,不使用 `bg-[#...]` 任意值。 + +--- + + +# Part 6:实施计划与指标 + +## 26. 综合优先级清单 + +### P0 — 立即修复(影响日常使用) + +| 序号 | 功能项 | 预算 | 来源 | +|-----|-------|------|-----| +| 1 | 花名册导出字段扩充(对齐 19 字段) | 🟢 低 | 功能对照 | +| 2 | 社保「无社保」选项 | 🟢 低 | 功能对照 | +| 3 | 公积金「不缴纳」选项 | 🟢 低 | 功能对照 | +| 4 | 个税申报表 Excel 导出 | 🟢 低 | 功能对照 | +| 5 | 入离职统计看板 | 🟢 低 | 功能对照 | +| 6 | 绩效统计看板 | 🟢 低 | 功能对照 | +| 7 | 新导航和 AppShell | 🟡 中 | UI/UX | +| 8 | 首页任务中心 | 🟡 中 | UI/UX | +| 9 | 发薪工作流 Stepper | 🟡 中 | UI/UX | +| 10 | 离职流程及审批 | 🟡 中 | UI/UX | + +### P1 — 近期优化 + +| 序号 | 功能项 | 预算 | 来源 | +|-----|-------|------|-----| +| 11 | 合同类型扩充(承揽/临时工/非全日制) | 🟡 中 | 功能对照 | +| 12 | 导入模板合同类型对齐 | 🟢 低 | 功能对照 | +| 13 | 商险/意外险独立管理 | 🟡 中 | 功能对照 | +| 14 | 员工离职申请入口 | 🟢 低 | 功能对照 | +| 15 | 离职交接清单 | 🟢 低 | 功能对照 | +| 16 | 薪酬分析看板 | 🟢 低 | 功能对照 | +| 17 | 员工 Profile Shell | 🟡 中 | UI/UX | +| 18 | 入职进度面板 | 🟡 中 | UI/UX | +| 19 | 月度社保办理流程 | 🟡 中 | UI/UX | +| 20 | 月度考勤确认流程 | 🟡 中 | UI/UX | +| 21 | 统一风险中心 | 🟡 中 | UI/UX | +| 22 | Employee Hub(员工门户) | 🟡 中 | UI/UX | + +### P2 — 中期规划 + +| 序号 | 功能项 | 预算 | 来源 | +|-----|-------|------|-----| +| 23 | 计件/计时工资 | 🔴 高 | 功能对照 | +| 24 | 考勤第三方对接(钉钉/企微/飞书) | 🟡 中 | 功能对照 | +| 25 | 合同电子签对接(e签宝/法大大) | 🟡 中 | 功能对照 | +| 26 | 个税申报数据校验 | 🟢 低 | 功能对照 | +| 27 | 绩效与薪酬联动 | 🟡 中 | 功能对照 | +| 28 | 上下文 AI | 🟡 中 | UI/UX | +| 29 | 全局搜索和命令面板 | 🟡 中 | UI/UX | + +### P3 — 远期规划 + +| 序号 | 功能项 | 预算 | 来源 | +|-----|-------|------|-----| +| 30 | 发票模块 | 🔴 高 | 功能对照 | +| 31 | 通用审批引擎 | 🔴 高 | 功能对照 | +| 32 | 对接税务系统在线申报 | 🔴 高 | 功能对照 | + +## 27. 分阶段实施计划 + +### Phase 0:基线和验证(3–5 天) + +- 建立核心任务可用性基线(发薪/入职/离职点击数及完成时间)。 +- 整理角色和权限矩阵。 +- 确认品牌策略。 +- 建立桌面/平板/手机测试视口。 + +### Phase 1:全局框架 + P0 功能修复(2–3 周) + +- 新导航和 AppShell。 +- PageHeader、FilterBar、DataTable 基础组件。 +- 新色彩/字体/间距/状态体系。 +- 首页任务中心。 +- 花名册导出扩充、社保/公积金开关、个税申报表导出。 +- 入离职统计看板、绩效统计看板。 +- WCAG AA 基础整改。 + +验收:一级导航不超过五项;首页第一屏存在唯一主任务;导出字段对齐;统计看板上线。 + +### Phase 2:核心业务流程(3–5 周) + +- 发薪工作流(Stepper + 异常门禁 + 复核表 + 归档发布)。 +- 离职工作流(六步 + 右侧摘要 + 审批)。 +- 月度社保办理。 +- 月度考勤确认。 +- 员工 Profile Shell。 +- 合同类型扩充、商险管理。 + +验收:核心任务无需跨模块寻找下一步;所有流程显示当前步骤/阻断项/负责人;危险操作说明影响。 + +### Phase 3:员工自助与协作(3–4 周) + +- Employee Hub。 +- 入职进度中心。 +- 审批收件箱。 +- 制度阅读追踪。 +- 通知中心。 +- 员工离职申请入口。 + +验收:员工可手机端完成主要自助任务;HR 统一查看员工响应状态;审批不再分散。 + +### Phase 4:智能化(3–4 周) + +- 上下文 AI。 +- 全局搜索和命令面板。 +- 保存视图和个性化工作台。 + +验收:AI 自动识别业务上下文;全局搜索覆盖人员、任务、记录和功能。 + +## 27.5 详细 Sprint 实施安排 + +以下将上述 Phase 0–4 细化为 5 个 Sprint(每个 Sprint 2 周),明确每项工作的类型、工作量、涉及文件和执行策略。 + +### 现状判断 + +当前代码基础: + +- **布局**:`AdminLayout`(`SidebarNav` + `TopNav` + `PageContainer` + `MobileTabBar`)已存在但结构简单。 +- **组件**:无 `AppShell`、`PageHeader`、`DataTable`、`FilterBar` 等抽象组件,各页面自行实现。 +- **导航**:6 个分组全部默认展开,未按任务优先级组织。 +- **色彩**:使用 Tailwind `primary`(紫色系),无设计 Token 系统。 +- **页面**:`Dashboard.tsx`、`Money.tsx`、`Roster.tsx` 等均为大文件单页面。 + +### Sprint 1:基础设施 + 快速修复(第 1–2 周) + +**目标**:搭建新框架骨架,同时完成 6 项低成本功能修复,让用户立刻感知改善。 + +| 工作项 | 类型 | 工作量 | 涉及文件 | +|-------|------|-------|---------| +| 设计 Token 系统 | UI/UX | 1 天 | `tailwind.config.ts` + 新建 `src/styles/tokens.css` | +| 新导航 5 分组 | UI/UX | 1 天 | `SidebarNav.tsx` 改造 + `MobileTabBar.tsx` 调整 | +| `AppShell` + `PageHeader` 组件 | UI/UX | 2 天 | 新建 `src/components/layout/AppShell.tsx`、`PageHeader.tsx` | +| `FilterBar` + `DataTable` 组件 | UI/UX | 2 天 | 新建 `src/components/ui/FilterBar.tsx`、`DataTable.tsx` | +| 花名册导出字段扩充 | 功能修复 | 0.5 天 | `export.routes.ts` | +| 社保「无社保」选项 | 功能修复 | 1 天 | `schema.prisma` + `import.routes.ts` + `SocialInsurance.tsx` | +| 公积金「不缴纳」选项 | 功能修复 | 0.5 天 | `import.routes.ts` + `SocialInsurance.tsx` | +| 个税申报表 Excel 导出 | 功能修复 | 1 天 | 新增 `export.routes.ts` 路由 | +| 入离职统计看板 | 功能修复 | 1.5 天 | `Dashboard.tsx` + 新增后端聚合 API | +| 绩效统计看板 | 功能修复 | 1.5 天 | 新建 `PerformanceDashboard.tsx` + 后端 API | + +**执行策略**:功能修复(后端为主)与 UI 基础设施(前端为主)可并行,互不阻塞。 + +**验收标准**: + +- 导航从 6 分组变为 5 分组(首页/团队/薪酬/时间/合规)。 +- 花名册导出包含 19 字段。 +- 社保/公积金可选择不缴纳。 +- 个税申报表可导出 Excel。 +- Dashboard 新增入离职趋势图和绩效分布图。 + +### Sprint 2:首页 + 薪酬流程(第 3–4 周) + +**目标**:首页从数据看板升级为任务中心,发薪流程串联为 Stepper。 + +| 工作项 | 类型 | 工作量 | 涉及文件 | +|-------|------|-------|---------| +| 首页任务中心重构 | UI/UX | 3 天 | `Dashboard.tsx` 重写 + 新增 `/workspace/next-actions` API | +| 待办队列 + 人员动态 | UI/UX | 2 天 | 同上 | +| `Stepper` 组件 | UI/UX | 1 天 | 新建 `src/components/ui/Stepper.tsx` | +| 发薪工作流页面拆分 | UI/UX | 4 天 | `Money.tsx` 拆分为 `PayrollHome/Workflow/ReviewTable/Exceptions/Publish` | +| 发薪异常质量门禁 | UI/UX | 2 天 | 对接已有 `pre-check` API + `InlineAlert` 组件 | + +**执行策略**:先做 `Stepper` 组件,再做首页,最后拆分发薪页面。`Money.tsx` 拆分时保持路由兼容(`/money` 仍可用)。 + +### Sprint 3:团队 + 离职 + 社保流程(第 5–6 周) + +**目标**:员工全生命周期管理体验统一。 + +| 工作项 | 类型 | 工作量 | 涉及文件 | +|-------|------|-------|---------| +| 员工目录优化 | UI/UX | 2 天 | `Roster.tsx` 重构列表部分 | +| 员工 Profile Shell | UI/UX | 3 天 | 新建 `src/pages/roster/EmployeeProfileShell.tsx` | +| 离职工作流重构 | UI/UX | 3 天 | `Termination.tsx` 拆分 + 右侧摘要面板 | +| 合同类型扩充 | 功能 | 1 天 | `schema.prisma` 枚举 + 前端选择器 + 导入模板 | +| 月度社保办理流程 | UI/UX | 2 天 | `SocialInsurance.tsx` 重构月度办理部分 | +| 商险管理 Tab | 功能 | 1 天 | `SocialInsurance.tsx` 新增 Tab | + +### Sprint 4:员工自助 + 考勤 + 合规(第 7–8 周) + +| 工作项 | 类型 | 工作量 | 涉及文件 | +|-------|------|-------|---------| +| Employee Hub 首页 | UI/UX | 2 天 | 新建 `src/pages/portal/EmployeeHome.tsx` | +| 入职进度面板 | UI/UX | 2 天 | 新建 `src/pages/portal/OnboardingProgress.tsx` | +| 员工离职申请入口 | 功能 | 1.5 天 | `portal.routes.ts` + 前端表单 | +| 离职交接清单 | 功能 | 1 天 | `TerminationRecord` 模型扩展 + 前端 | +| 月度考勤确认流程 | UI/UX | 2 天 | `Attendance.tsx` 重构 | +| 统一风险中心 | UI/UX | 2.5 天 | 新建 `src/pages/compliance/RiskCenter.tsx` | +| 薪酬分析看板 | 功能 | 1 天 | 新建 `SalaryDashboard.tsx` + 后端 API | + +### Sprint 5:AI + 搜索 + 收尾(第 9–10 周) + +| 工作项 | 类型 | 工作量 | 涉及文件 | +|-------|------|-------|---------| +| `CommandPalette` 全局搜索 | UI/UX | 3 天 | 新建 `src/components/ui/CommandPalette.tsx` + `/search` API | +| 上下文 AI | UI/UX | 3 天 | 业务页面嵌入"询问 AI"入口 + 上下文传递 | +| 保存视图 | UI/UX | 1.5 天 | FilterBar 扩展 + localStorage 持久化 | +| WCAG AA 整体审查 | UI/UX | 1 天 | 全局对比度/键盘/aria 审查 | +| 页面验收清单走查 | QA | 2 天 | 8 个关键页面逐一验证 | + +### 关键执行原则 + +1. **先后端后前端**:功能修复项(社保开关、导出扩充、个税报表)先改后端 API,前端再对接,避免阻塞。 +2. **大页面拆分保持路由兼容**:`Money.tsx` 拆分后 `/money` 路由保留,内部用子路由导航。 +3. **组件先行**:每个 Sprint 开始先完成该 Sprint 需要的新组件(`Stepper`、`PageHeader` 等),再做页面。 +4. **并行策略**:后端改动(schema 迁移、新 API)与前端组件开发可并行,前端用 mock 数据先行。 +5. **每个 Sprint 末尾部署验证**:在 `https://on.hr8ai.top` 上验证,使用 Playwright 跑关键路径。 + +### 人力建议 + +| 角色 | 人数 | 职责 | +|------|------|------| +| 全栈开发 | 1–2 | 后端 API + Prisma 迁移 + 前端页面 | +| 前端开发 | 1 | 组件库 + 页面重构 | +| 产品/设计 | 0.5 | 原型确认 + 验收 | + +**最小可行配置**:1 名全栈开发 + 按需前端协助,10 周完成全部 P0–P1 内容。如果只做 P0(Sprint 1–2),4 周即可见效。 + +## 28. 成功指标 + +### 28.1 效率指标 + +- 首次创建员工到入职完成的时间。 +- 创建发薪批次到发布工资条的时间。 +- 每次发薪的点击数和页面跳转数。 +- 异常发现到修复的平均时间。 +- 离职流程平均完成时长。 + +### 28.2 质量指标 + +- 发薪归档后撤销率。 +- 工资条发布错误率。 +- 入职资料一次通过率。 +- 逾期合同和未处理高风险数量。 +- 高风险操作误触和取消率。 + +### 28.3 体验指标 + +- 首页待办完成率。 +- 员工自助完成率。 +- 员工制度阅读确认率。 +- 移动端任务完成率。 + +### 28.4 建议目标 + +- 发薪操作时间下降 30%。 +- 核心流程页面跳转下降 40%。 +- 入职资料一次通过率提升至 85%+。 +- 员工自助完成率达到 80%+。 +- 高风险事项逾期数量下降 50%。 + +## 29. 页面验收清单 + +每个页面发布前至少验证: + +- 用户能否在 5 秒内理解页面目的。 +- 是否存在唯一且明确的主要动作。 +- 是否说明当前状态和下一步。 +- 加载/空/错误/无权限状态是否完整。 +- 危险操作是否说明对象/数量/后果/可恢复性。 +- 文字和状态对比度是否达到 WCAG AA。 +- 仅使用键盘是否能完成主要操作。 +- 360px/768px/1280px/1440px/1920px 是否无溢出。 +- 敏感信息是否按权限遮罩并留审计记录。 + +## 30. 建议优先制作的高保真原型 + +第一轮只需设计以下八个关键页面: + +1. 新首页任务中心。 +2. 新导航和全局搜索。 +3. 员工目录。 +4. 员工概览详情。 +5. 发薪流程总览。 +6. 发薪异常和工资复核表。 +7. 离职流程。 +8. 员工门户首页。 + +--- + +## 31. 系统现有完整模块清单 + +| 分类 | 模块 | 路由 | 核心功能 | +|------|------|------|---------| +| 工作台 | 总览 | `/` | 数据看板、风险提醒、月度任务、价值报告 | +| 工作台 | 工作日历 | `/calendar` | 月度 HR 事项日历 | +| 员工管理 | 花名册 | `/roster` | 员工 CRUD、批量导入/导出、合同管理、调薪、部门变更 | +| 员工管理 | 用工办理 | `/work-process` | 入职/离职流程管理 | +| 员工管理 | 考勤确认 | `/attendance` | 班次/排班/出勤/月度报表/休假 | +| 员工管理 | 解聘补偿 | `/termination` | 解聘记录、补偿金计算 | +| 员工管理 | 特殊状态 | `/special-status` | 孕期/哺乳期/工伤/医疗期 | +| 薪税社保 | 薪税管理 | `/money` | 发薪批次、薪酬模板、加班费、工资条 | +| 薪税社保 | 社保公积金 | `/social` | 五险配置、公积金配置、基数调整、月度办理 | +| 合规风控 | 证据链 | `/evidence` | 入职/合同/培训/违纪证据管理 | +| 合规风控 | 规章制度 | `/policies` | 制度发布、员工签收 | +| 合规风控 | 用工体检 | `/tools/health-check` | 5 维度合规健康度评分 | +| 合规风控 | 医疗期计算 | `/tools/medical-period` | 医疗期期限计算器 | +| 合规风控 | 年度价值 | `/tools/annual-value` | 年度 HR 价值报告 | +| AI 辅助 | AI 顾问 | `/ai-assistant` | 劳动法 AI 问答 | +| AI 辅助 | 文本模板 | `/templates` | 合同/通知/制度模板管理 | +| 系统 | 通知管理 | `/notifications` | 风险提醒通知配置 | +| 系统 | 操作日志 | `/audit` | 操作审计日志 | +| 系统 | 设置 | `/settings` | 企业信息、导入配置 | +| 员工端 | 门户 | `/portal/*` | 工资条查看、合同确认、考勤确认、制度签收、入职办理 | + +--- + +## 32. 参考资料 + +- Gusto 产品能力总览: +- Gusto Employee Self-Serve: +- Gusto 2025 Feature Showcase: +- Gusto 报表与自定义报告: +- Gusto Money Dashboard: + +这些参考体现的共同方向:减少专业门槛、围绕下一步组织界面、让异常在提交前出现、强化员工自助、跨模块数据自然衔接。TurboHR 应保留自身在中国劳动用工合规、证据链和离职处理方面的差异化能力,在此基础上建立优秀的产品体验。 diff --git a/frontend/src/components/layout/AppShell.tsx b/frontend/src/components/layout/AppShell.tsx new file mode 100644 index 0000000..e9757e2 --- /dev/null +++ b/frontend/src/components/layout/AppShell.tsx @@ -0,0 +1,54 @@ +/** + * 应用外壳组件 — 统一页面布局结构 + * 包含 PageHeader 区域 + 内容区域,配合 SidebarNav 和 TopNav 使用 + */ +import { ReactNode } from 'react' +import clsx from 'clsx' + +interface AppShellProps { + children: ReactNode + className?: string +} + +/** + * 应用外壳 — 限制内容最大宽度,统一内边距 + */ +export function AppShell({ children, className }: AppShellProps) { + return ( +
+ {children} +
+ ) +} + +interface PageHeaderProps { + title: string + description?: string + actions?: ReactNode + status?: ReactNode + className?: string +} + +/** + * 页面头部 — 统一标题、说明、状态和操作按钮区域 + */ +export function PageHeader({ title, description, actions, status, className }: PageHeaderProps) { + return ( +
+
+
+

{title}

+ {status} +
+ {description && ( +

{description}

+ )} +
+ {actions && ( +
+ {actions} +
+ )} +
+ ) +} diff --git a/frontend/src/components/layout/MobileTabBar.tsx b/frontend/src/components/layout/MobileTabBar.tsx index 260b2f9..5c99602 100644 --- a/frontend/src/components/layout/MobileTabBar.tsx +++ b/frontend/src/components/layout/MobileTabBar.tsx @@ -1,14 +1,13 @@ import { Link, useLocation } from 'react-router-dom' -import { Home, Users, Calculator, UserX, Bot, Shield } from 'lucide-react' +import { Home, Users, Calculator, CalendarCheck, Shield } from 'lucide-react' import clsx from 'clsx' const tabs = [ - { path: '/', label: '总览', icon: Home }, - { path: '/roster', label: '员工', icon: Users }, - { path: '/money', label: '薪税', icon: Calculator }, - { path: '/social', label: '社保', icon: Shield }, - { path: '/termination', label: '解聘', icon: UserX }, - { path: '/ai-assistant', label: 'AI', icon: Bot }, + { path: '/', label: '首页', icon: Home }, + { path: '/roster', label: '团队', icon: Users }, + { path: '/money', label: '薪酬', icon: Calculator }, + { path: '/attendance', label: '时间', icon: CalendarCheck }, + { path: '/evidence', label: '合规', icon: Shield }, ] export default function MobileTabBar() { diff --git a/frontend/src/components/layout/SidebarNav.tsx b/frontend/src/components/layout/SidebarNav.tsx index eb056f6..5825428 100644 --- a/frontend/src/components/layout/SidebarNav.tsx +++ b/frontend/src/components/layout/SidebarNav.tsx @@ -30,31 +30,36 @@ interface NavGroup { const navGroups: NavGroup[] = [ { - title: '工作台', + title: '首页', items: [ - { path: '/', label: '总览', icon: LayoutDashboard }, + { path: '/', label: '工作台', icon: LayoutDashboard }, { path: '/calendar', label: '工作日历', icon: CalendarDays }, ], }, { - title: '员工管理', + title: '团队', items: [ { path: '/roster', label: '花名册', icon: Users }, { path: '/work-process', label: '用工办理', icon: ClipboardList }, - { path: '/attendance', label: '考勤确认', icon: CalendarCheck }, - { path: '/termination', label: '解聘补偿', icon: UserX }, + { path: '/termination', label: '离职管理', icon: UserX }, { path: '/special-status', label: '特殊状态', icon: Heart }, ], }, { - title: '薪税社保', + title: '薪酬', items: [ { path: '/money', label: '薪税管理', icon: Calculator }, { path: '/social', label: '社保公积金', icon: Shield }, ], }, { - title: '合规风控', + title: '时间', + items: [ + { path: '/attendance', label: '考勤排班', icon: CalendarCheck }, + ], + }, + { + title: '合规', items: [ { path: '/evidence', label: '证据链', icon: FileSearch }, { path: '/policies', label: '规章制度', icon: FileText }, @@ -64,15 +69,10 @@ const navGroups: NavGroup[] = [ ], }, { - title: 'AI 辅助', + title: '更多', items: [ { path: '/ai-assistant', label: 'AI 顾问', icon: Bot }, { path: '/templates', label: '文本模板', icon: BookMarked }, - ], - }, - { - title: '系统', - items: [ { path: '/notifications', label: '通知管理', icon: Bell }, { path: '/audit', label: '操作日志', icon: ScrollText }, { path: '/settings', label: '设置', icon: Settings }, @@ -85,7 +85,14 @@ const navGroups: NavGroup[] = [ */ export default function SidebarNav({ mobileOpen, onClose }: { mobileOpen: boolean; onClose: () => void }) { const location = useLocation() - const [expandedGroups, setExpandedGroups] = useState>(new Set(navGroups.map(g => g.title))) + 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>( + new Set(activeGroup ? [activeGroup.title] : ['首页']) + ) const toggleGroup = (title: string) => { setExpandedGroups(prev => { @@ -96,11 +103,6 @@ export default function SidebarNav({ mobileOpen, onClose }: { mobileOpen: boolea }) } - const isActive = (path: string) => { - if (path === '/') return location.pathname === '/' - return location.pathname.startsWith(path) - } - return ( <> {/* 移动端遮罩 */} diff --git a/frontend/src/components/ui/DataTable.tsx b/frontend/src/components/ui/DataTable.tsx new file mode 100644 index 0000000..4aafcd0 --- /dev/null +++ b/frontend/src/components/ui/DataTable.tsx @@ -0,0 +1,127 @@ +/** + * 数据表格组件 — 统一表头、行样式、排序状态和空状态 + * 支持列定义、行点击、选中行、固定列、对齐和空状态 + */ +import { ReactNode } from 'react' +import clsx from 'clsx' +import { ChevronUp, ChevronDown, ChevronsUpDown } from 'lucide-react' + +export interface Column { + key: string + header: string + render?: (row: T) => ReactNode + sortable?: boolean + align?: 'left' | 'center' | 'right' + width?: string + fixed?: 'left' | 'right' +} + +interface DataTableProps { + columns: Column[] + data: T[] + rowKey: (row: T) => string + onRowClick?: (row: T) => void + sortBy?: string + sortOrder?: 'asc' | 'desc' + onSort?: (key: string) => void + emptyState?: ReactNode + dense?: boolean + className?: string +} + +/** + * 数据表格 — 支持排序、行点击、空状态和密度切换 + */ +export function DataTable({ + columns, + data, + rowKey, + onRowClick, + sortBy, + sortOrder, + onSort, + emptyState, + dense, + className, +}: DataTableProps) { + const alignClass = (align?: string) => + align === 'right' ? 'text-right' : align === 'center' ? 'text-center' : 'text-left' + + return ( +
+ + + + {columns.map((col) => ( + + ))} + + + + {data.length === 0 ? ( + + + + ) : ( + data.map((row) => ( + onRowClick(row) : undefined} + className={clsx( + 'border-b border-border-subtle transition-colors', + onRowClick && 'cursor-pointer hover:bg-surface-muted', + )} + > + {columns.map((col) => ( + + ))} + + )) + )} + +
onSort(col.key) : undefined} + > + + {col.header} + {col.sortable && onSort && ( + + {sortBy === col.key ? ( + sortOrder === 'asc' ? ( + + ) : ( + + ) + ) : ( + + )} + + )} + +
+ {emptyState || '暂无数据'} +
+ {col.render ? col.render(row) : (row as any)[col.key]} +
+
+ ) +} diff --git a/frontend/src/components/ui/FilterBar.tsx b/frontend/src/components/ui/FilterBar.tsx new file mode 100644 index 0000000..fe6c5f8 --- /dev/null +++ b/frontend/src/components/ui/FilterBar.tsx @@ -0,0 +1,73 @@ +/** + * 筛选栏组件 — 统一搜索、筛选条件展示和工具按钮 + * 支持搜索框、可移除的筛选条件标签、右侧工具区 + */ +import { ReactNode } from 'react' +import clsx from 'clsx' +import { Search, X } from 'lucide-react' + +interface FilterBarProps { + searchValue?: string + searchPlaceholder?: string + onSearchChange?: (value: string) => void + filters?: ReactNode + activeChips?: Array<{ label: string; onRemove: () => void }> + tools?: ReactNode + className?: string +} + +/** + * 筛选栏 — 搜索 + 筛选条件 + 工具按钮 + */ +export function FilterBar({ + searchValue, + searchPlaceholder = '搜索…', + onSearchChange, + filters, + activeChips, + tools, + className, +}: FilterBarProps) { + return ( +
+ {/* 第一行:搜索 + 筛选器 + 工具 */} +
+ {onSearchChange !== undefined && ( +
+ + onSearchChange(e.target.value)} + placeholder={searchPlaceholder} + className="w-full pl-8 pr-3 py-1.5 rounded-md border border-border-default bg-surface-card text-sm text-ink-900 focus:outline-none focus:ring-2 focus:ring-brand-600/30 focus:border-brand-600 transition-colors" + /> +
+ )} + {filters &&
{filters}
} + {tools &&
{tools}
} +
+ + {/* 第二行:激活的筛选条件标签 */} + {activeChips && activeChips.length > 0 && ( +
+ {activeChips.map((chip, i) => ( + + {chip.label} + + + ))} +
+ )} +
+ ) +} diff --git a/frontend/src/index.css b/frontend/src/index.css index 8cf5137..b44619d 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -4,9 +4,10 @@ @layer base { body { - @apply bg-surface text-gray-900 antialiased; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; - font-size: 16px; + @apply bg-surface-page text-ink-900 antialiased; + font-family: Inter, "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif; + font-variant-numeric: tabular-nums; + font-size: 14px; line-height: 1.5; -webkit-tap-highlight-color: transparent; -webkit-touch-callout: none; @@ -29,22 +30,22 @@ @apply inline-flex items-center justify-center px-3 py-1.5 rounded font-medium text-sm transition-colors disabled:opacity-50 disabled:cursor-not-allowed; } .btn-primary { - @apply btn bg-primary text-white hover:bg-primary-dark; + @apply btn bg-brand-600 text-white hover:bg-brand-700; } .btn-secondary { - @apply btn bg-gray-100 text-gray-700 hover:bg-gray-200; + @apply btn bg-surface-muted text-ink-700 hover:bg-border-subtle; } .btn-danger { - @apply btn bg-danger text-white hover:bg-red-700; + @apply btn bg-danger text-white hover:bg-danger-light; } .card { - @apply bg-white rounded-lg shadow-sm border border-gray-200 p-4; + @apply bg-surface-card rounded-card border border-border-default; } .input { - @apply w-full px-2.5 py-1.5 rounded-md border border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent text-sm; + @apply w-full px-2.5 py-2 rounded-md border border-border-default bg-surface-card text-sm text-ink-900 focus:outline-none focus:ring-2 focus:ring-brand-600/30 focus:border-brand-600 transition-colors; } .label { - @apply block text-sm font-medium text-gray-700 mb-1; + @apply block text-sm font-medium text-ink-700 mb-1; } } diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index a9706e1..30caa25 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -11,6 +11,8 @@ import Button from '../components/ui/Button' import EmptyState from '../components/ui/EmptyState' import Pagination from '../components/ui/Pagination' import type { DashboardData } from '../types' +import TurnoverStats from './dashboard/TurnoverStats' +import PerformanceStats from './dashboard/PerformanceStats' function fmt(n: number) { return `¥${(n || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` @@ -679,6 +681,12 @@ export default function Dashboard() { )} + + {/* 入离职统计 + 绩效统计 */} +
+ + +
)} diff --git a/frontend/src/pages/dashboard/PerformanceStats.tsx b/frontend/src/pages/dashboard/PerformanceStats.tsx new file mode 100644 index 0000000..477a938 --- /dev/null +++ b/frontend/src/pages/dashboard/PerformanceStats.tsx @@ -0,0 +1,122 @@ +/** + * 绩效统计看板 — 展示绩效等级分布和部门/周期对比 + */ +import { useState } from 'react' +import { useQuery } from '@tanstack/react-query' +import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, BarChart, Bar, XAxis, YAxis, CartesianGrid, Legend } from 'recharts' +import { Award, TrendingUp } from 'lucide-react' +import api from '../../lib/api' + +const GRADE_COLORS: Record = { + 'A': '#237A57', + 'B': '#356A8A', + 'C': '#A76113', + 'D': '#B83232', + 'S': '#C7442E', + '未评级': '#9CA39B', +} + +export default function PerformanceStats() { + const [period, setPeriod] = useState(new Date().getFullYear().toString()) + + const { data, isLoading } = useQuery({ + queryKey: ['performance-stats', period], + queryFn: async () => { + const res = await api.get(`/dashboard/performance-stats?period=${period}`) + return res.data.data + }, + }) + + if (isLoading) { + return ( +
+
+
+
+
+
+ ) + } + + if (!data || data.total === 0) { + return ( +
+

绩效统计

+
暂无数据
+
+ ) + } + + return ( +
+
+

绩效统计

+ +
+ + {/* 汇总 */} +
+
+ 参评人数 + + {data.total} + +
+
+ 平均分 + + {data.overallAvgScore} + +
+
+ +
+ {/* 等级分布饼图 */} +
+

等级分布

+ + + `${name}: ${value}`} + labelLine={false} + > + {data.gradeDistribution.map((entry: any, i: number) => ( + + ))} + + + + +
+ + {/* 部门平均分柱状图 */} +
+

部门平均分

+ + + + + + + + + +
+
+
+ ) +} diff --git a/frontend/src/pages/dashboard/TurnoverStats.tsx b/frontend/src/pages/dashboard/TurnoverStats.tsx new file mode 100644 index 0000000..d3ce265 --- /dev/null +++ b/frontend/src/pages/dashboard/TurnoverStats.tsx @@ -0,0 +1,93 @@ +/** + * 入离职统计看板 — 展示按月入职/离职趋势和汇总数据 + */ +import { useQuery } from '@tanstack/react-query' +import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts' +import { UserPlus, UserMinus, Users, TrendingDown } from 'lucide-react' +import api from '../../lib/api' + +export default function TurnoverStats() { + const { data, isLoading } = useQuery({ + queryKey: ['turnover-stats'], + queryFn: async () => { + const res = await api.get('/dashboard/turnover-stats?months=12') + return res.data.data + }, + }) + + if (isLoading) { + return ( +
+
+
+
+
+
+ ) + } + + if (!data || data.monthly.length === 0) { + return ( +
+

入离职统计

+
暂无数据
+
+ ) + } + + const { monthly, summary } = data + + return ( +
+

入离职趋势(近 12 个月)

+ + {/* 汇总卡片 */} +
+
+ 入职人数 + + {summary.totalHired} + +
+
+ 离职人数 + + {summary.totalLeft} + +
+
+ 当前人数 + + {summary.currentHeadcount} + +
+
+ 离职率 + + {summary.turnoverRate}% + +
+
+ + {/* 趋势图 */} + + + + v.slice(5)} + /> + + [v, ''] as [any, any]} + /> + + + + + +
+ ) +} diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts index a394b2b..9f805f5 100644 --- a/frontend/tailwind.config.ts +++ b/frontend/tailwind.config.ts @@ -6,18 +6,82 @@ export default { theme: { extend: { colors: { - primary: { - DEFAULT: '#4F46E5', - light: '#6366F1', - dark: '#4338CA', + // 品牌色 — 暖红橙,可信温暖 + brand: { + 50: '#FBF0EE', + 100: '#F5D9D4', + 200: '#EBB3A9', + 300: '#E08D7E', + 400: '#D66853', + 500: '#C7442E', + 600: '#C7442E', + 700: '#A93625', + 800: '#8B2B1E', + 900: '#6D2118', }, - danger: '#DC2626', - warning: '#F59E0B', - safe: '#16A34A', - surface: '#F8FAFC', + // 兼容旧 primary 引用,指向 brand-600 + primary: { + DEFAULT: '#C7442E', + light: '#D66853', + dark: '#A93625', + }, + // 文字色 + ink: { + 900: '#202622', + 700: '#3A4239', + 600: '#59615C', + 500: '#7A8278', + 400: '#9CA39B', + }, + // 表面色 + surface: { + page: '#F7F8F6', + card: '#FFFFFF', + muted: '#F0F2EF', + }, + // 状态色 + success: { + DEFAULT: '#237A57', + light: '#3A9B72', + bg: '#E6F4ED', + }, + warning: { + DEFAULT: '#A76113', + light: '#C98A30', + bg: '#FBF0E0', + }, + danger: { + DEFAULT: '#B83232', + light: '#D94848', + bg: '#FCEAEA', + }, + info: { + DEFAULT: '#356A8A', + light: '#4F8AAB', + bg: '#E8F0F5', + }, + // 边框 + border: { + DEFAULT: '#DDE1DD', + strong: '#C4C9C4', + subtle: '#ECEEEC', + }, + }, + fontFamily: { + sans: ['Inter', '"Noto Sans SC"', '"PingFang SC"', '"Microsoft YaHei"', 'sans-serif'], + }, + fontSize: { + // 使用 tabular-nums 对齐数字 + 'kpi': ['28px', { lineHeight: '36px', fontWeight: '600' }], }, maxWidth: { - content: '1280px', + content: '1440px', + }, + borderRadius: { + card: '8px', + }, + spacing: { + '18': '72px', }, }, },