feat: 统一页面布局与字体样式

This commit is contained in:
selfrelease
2026-07-24 17:30:36 +08:00
parent 4ae24990a7
commit a28b065ab8
25 changed files with 2032 additions and 259 deletions
+9 -1
View File
@@ -9,7 +9,15 @@ import { apiLimiter } from './middleware/rateLimit'
const app = express()
app.use(helmet())
app.use(compression())
app.use(compression({
filter: (req, res) => {
// SSE 流式响应不压缩,避免缓冲导致前端一次性收到所有数据
if (req.headers['accept'] === 'text/event-stream' || res.getHeader('Content-Type') === 'text/event-stream') {
return false
}
return compression.filter(req, res)
},
}))
app.use(
cors({
origin: process.env.CORS_ORIGIN || 'http://localhost:5173',
+3 -3
View File
@@ -1,7 +1,7 @@
import app from './app'
const PORT = process.env.PORT || 3000
const PORT = Number(process.env.PORT) || 3000
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`)
app.listen(PORT, '::', () => {
console.log(`Server running on http://[::]:${PORT}`)
})
+6
View File
@@ -109,12 +109,18 @@ router.post('/chat-stream', authMiddleware, async (req: AuthRequest, res, next)
res.setHeader('Content-Type', 'text/event-stream')
res.setHeader('Cache-Control', 'no-cache')
res.setHeader('Connection', 'keep-alive')
res.setHeader('X-Accel-Buffering', 'no')
res.flushHeaders()
let usageRecorded = false
try {
for await (const delta of chatStream(messages, orgContext)) {
res.write(`data: ${JSON.stringify({ delta })}\n\n`)
if (typeof (res as any).flush === 'function') (res as any).flush()
}
res.write('data: [DONE]\n\n')
} catch (streamErr: any) {
res.write(`data: ${JSON.stringify({ error: streamErr.message || 'AI 服务异常' })}\n\n`)
res.write('data: [DONE]\n\n')
} finally {
if (!usageRecorded) {
await recordUsage(req.user!.orgId, req.user!.id, 'chat')
+13 -1
View File
@@ -205,7 +205,7 @@ router.get('/:id/evidence-chain', authMiddleware, async (req: AuthRequest, res,
employee.contracts.forEach((c) => {
evidence.push({
category: '劳动关系',
title: `劳动合同(${c.contractType === 'FIXED' ? '固定期限' : c.contractType === 'UNFIXED' ? '无固定期限' : '未签订'}`,
title: `合同(${({ FIXED: '固定期限', UNFIXED: '固定期限', LABOR: '劳务协议', INTERNSHIP: '实习协议', UNSIGNED: '未签订' } as Record<string, string>)[c.contractType] || '未签订'}`,
date: c.signDate ? c.signDate.toISOString().slice(0, 10) : c.startDate.toISOString().slice(0, 10),
description: `合同期限:${c.startDate.toISOString().slice(0, 10)}${c.endDate ? c.endDate.toISOString().slice(0, 10) : '无固定期限'},试用期${c.probationMonths}个月,试用期工资¥${c.probationSalary}`,
evidenceType: 'CONTRACT',
@@ -788,4 +788,16 @@ router.get('/contracts/expiring', authMiddleware, async (req: AuthRequest, res,
} catch (err) { next(err) }
})
// 合同类型列表(供前端动态获取)
router.get('/contract-types', authMiddleware, (_req: AuthRequest, res) => {
const types = [
{ value: 'FIXED', label: '劳动合同-固定期', hasEndDate: true },
{ value: 'UNFIXED', label: '劳动合同-无固定期', hasEndDate: false },
{ value: 'LABOR', label: '劳务协议', hasEndDate: false },
{ value: 'INTERNSHIP', label: '实习协议', hasEndDate: false },
{ value: 'UNSIGNED', label: '未签合同', hasEndDate: false },
]
res.json({ success: true, data: types })
})
export default router
+8 -1
View File
@@ -26,7 +26,14 @@ export function getContractStatus(contract: {
hireDate: Date
}): { status: string; statusText: string; riskLevel: 'high' | 'medium' | 'low' | 'safe' } {
const today = new Date()
const typeLabel = contract.contractType === 'FIXED' ? '固定期限' : contract.contractType === 'UNFIXED' ? '无固定期限' : ''
const typeLabelMap: Record<string, string> = {
FIXED: '固定期限',
UNFIXED: '无固定期限',
LABOR: '劳务协议',
INTERNSHIP: '实习协议',
UNSIGNED: '',
}
const typeLabel = typeLabelMap[contract.contractType] || ''
if (!contract.signDate || contract.contractType === 'UNSIGNED') {
const days = daysBetween(today, contract.hireDate)