feat: 附件管理拆为独立tab + 员工端附件上传

管理端:
- 花名册详情新增「附件资料」独立tab(人事信息分组下)
- 从 BasicInfo 移除附件管理代码到独立 AttachmentsTab 组件
- shared.ts 添加 attachments tab 类型和计数映射

员工端:
- 后端新增 GET/POST/DELETE /portal/attachments 接口
- 前端 api-services 添加 portalDelete + 附件 API 方法
- MyProfile 新增附件资料卡片,支持上传/查看/删除附件
- 附件类型:身份证/银行卡/学历证书/职业资格证书/合同/照片/其他
This commit is contained in:
freedakgmail
2026-08-17 19:27:45 +08:00
parent 7b2f722984
commit 007109e425
7 changed files with 321 additions and 50 deletions
+42 -1
View File
@@ -1027,7 +1027,48 @@ router.put('/profile', portalAuth, async (req: any, res, next) => {
} catch (err) { next(err) }
})
// ========== 员工端:离职申请 ==========
// ========== 员工端:附件管理 ==========
// 查看自己的附件列表
router.get('/attachments', portalAuth, async (req: any, res, next) => {
try {
const { id: employeeId, orgId } = req.employee
const attachments = await prisma.employeeAttachment.findMany({
where: { employeeId, orgId },
orderBy: { createdAt: 'desc' },
})
res.json({ success: true, data: attachments })
} catch (err) { next(err) }
})
// 上传附件(文件URL由前端 FileReader 转为 base64 后传入)
router.post('/attachments', portalAuth, async (req: any, res, next) => {
try {
const { id: employeeId, orgId } = req.employee
const { fileName, fileType, fileUrl, fileSize } = req.body
if (!fileName || !fileUrl) {
return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '文件名和文件内容不能为空' } })
}
const attachment = await prisma.employeeAttachment.create({
data: { orgId, employeeId, fileName, fileType: fileType || 'OTHER', fileUrl, fileSize: fileSize || 0 },
})
res.json({ success: true, data: attachment })
} catch (err) { next(err) }
})
// 删除自己的附件
router.delete('/attachments/:id', portalAuth, async (req: any, res, next) => {
try {
const { id: employeeId, orgId } = req.employee
const attachment = await prisma.employeeAttachment.findFirst({
where: { id: req.params.id, employeeId, orgId },
})
if (!attachment) {
return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '附件不存在' } })
}
await prisma.employeeAttachment.delete({ where: { id: attachment.id } })
res.json({ success: true })
} catch (err) { next(err) }
})
// 提交离职申请
router.post('/resignation/submit', portalAuth, async (req: any, res, next) => {
try {