feat: 工作日历/考勤管理重构/AI人力报告/工作台员工分布/筛选优化/导入导出增强
- 新增工作日历页面(月历视图、事件管理、自定义事件) - 考勤管理重构为6 Tab模块(班次/排班/每日出勤/月度报表/休假记录) - AI顾问新增人力报告Tab,支持流式生成+Word导出 - 工作台总览新增员工分布统计(性别/年龄/学历/司龄饼图)+部门成本拆分 - 花名册/合同/解聘补偿新增部门和状态筛选 - 薪税管理新增工资表导入模板下载、银行代发CSV导出 - 社保公积金支持多公积金账户类型显示 - 数据导出新增花名册/解聘记录导出,中文文件名编码修复 - 数据导入新增模板下载(员工/增减员/工资表)+错误日志导出 - 移除工作台日历卡片(已迁移至独立工作日历页面) - 新增20260728/20260729更新测试指导文档
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import { Router, Response, NextFunction } from 'express'
|
||||
import prisma from '../lib/prisma'
|
||||
import { authMiddleware, AuthRequest } from '../middleware/auth'
|
||||
import { z } from 'zod'
|
||||
|
||||
const router = Router()
|
||||
|
||||
const createEventSchema = z.object({
|
||||
title: z.string().min(1).max(100),
|
||||
date: z.string(), // ISO date string
|
||||
endDate: z.string().optional(),
|
||||
type: z.enum(['CUSTOM', 'MEETING', 'TEAM_BUILDING', 'TRAINING', 'INTERVIEW']).default('CUSTOM'),
|
||||
priority: z.enum(['high', 'medium', 'low']).default('medium'),
|
||||
location: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
employeeId: z.string().optional(),
|
||||
})
|
||||
|
||||
// 获取当月日历事件(自定义 + 系统自动)
|
||||
router.get('/', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const orgId = req.user!.orgId
|
||||
const month = (req.query.month as string) || new Date().toISOString().slice(0, 7)
|
||||
const typeFilter = req.query.type as string | undefined
|
||||
|
||||
const [year, mon] = month.split('-').map(Number)
|
||||
const monthStart = new Date(year, mon - 1, 1)
|
||||
const monthEnd = new Date(year, mon, 0, 23, 59, 59)
|
||||
|
||||
const where: any = {
|
||||
orgId,
|
||||
date: { gte: monthStart, lte: monthEnd },
|
||||
}
|
||||
if (typeFilter) where.type = typeFilter
|
||||
|
||||
const events = await prisma.calendarEvent.findMany({
|
||||
where,
|
||||
include: { employee: { select: { id: true, name: true } } },
|
||||
orderBy: { date: 'asc' },
|
||||
})
|
||||
|
||||
res.json({ success: true, data: events })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 获取事件详情
|
||||
router.get('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const event = await prisma.calendarEvent.findFirst({
|
||||
where: { id: req.params.id, orgId: req.user!.orgId },
|
||||
include: { employee: { select: { id: true, name: true } } },
|
||||
})
|
||||
if (!event) return res.status(404).json({ success: false, message: '事件不存在' })
|
||||
res.json({ success: true, data: event })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 创建日历事件
|
||||
router.post('/', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const data = createEventSchema.parse(req.body)
|
||||
const event = await prisma.calendarEvent.create({
|
||||
data: {
|
||||
orgId: req.user!.orgId,
|
||||
title: data.title,
|
||||
date: new Date(data.date),
|
||||
endDate: data.endDate ? new Date(data.endDate) : null,
|
||||
type: data.type,
|
||||
priority: data.priority,
|
||||
location: data.location || null,
|
||||
description: data.description || null,
|
||||
employeeId: data.employeeId || null,
|
||||
createdBy: req.user!.id,
|
||||
},
|
||||
})
|
||||
res.json({ success: true, data: event })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 更新日历事件
|
||||
router.patch('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const updateSchema = createEventSchema.partial()
|
||||
const data = updateSchema.parse(req.body)
|
||||
const updateData: any = { ...data }
|
||||
if (data.date) updateData.date = new Date(data.date)
|
||||
if (data.endDate) updateData.endDate = new Date(data.endDate)
|
||||
if (data.endDate === undefined) delete updateData.endDate
|
||||
|
||||
const event = await prisma.calendarEvent.updateMany({
|
||||
where: { id: req.params.id, orgId: req.user!.orgId },
|
||||
data: updateData,
|
||||
})
|
||||
if (event.count === 0) return res.status(404).json({ success: false, message: '事件不存在' })
|
||||
res.json({ success: true })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
// 删除日历事件
|
||||
router.delete('/:id', authMiddleware, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const result = await prisma.calendarEvent.deleteMany({
|
||||
where: { id: req.params.id, orgId: req.user!.orgId },
|
||||
})
|
||||
if (result.count === 0) return res.status(404).json({ success: false, message: '事件不存在' })
|
||||
res.json({ success: true })
|
||||
} catch (err) {
|
||||
next(err)
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
Reference in New Issue
Block a user