fix: 提成奖金、证据链条、风险中心姓名显示身份证号
后端: - commission-bonus.service.ts: listByMonth 查询加上 idCardNumber 并解密 - evidence.service.ts: getEvidenceList 加上 idCardNumber 并解密返回 employeeIdCardNumber - roster.routes.ts: 证据链接口返回 employee.idCardNumber,Excel 导出加上身份证号 前端: - CommissionBonus.tsx: 列表姓名下显示身份证号,下拉选项和编辑弹窗也加上 - EvidenceChain.tsx: 两处姓名显示加上身份证号,文本导出加上身份证号 - Evidence.tsx: 证据链列表页姓名后显示身份证号
This commit is contained in:
@@ -638,6 +638,7 @@ router.get('/:id/evidence-chain', authMiddleware, async (req: AuthRequest, res,
|
||||
status: employee.terminations.some((t) => t.status === 'COMPLETED' && t.terminationDate <= new Date()) ? 'RESIGNED' : 'ACTIVE',
|
||||
gender: employee.gender,
|
||||
phone: employee.phone,
|
||||
idCardNumber: safeDecryptStr(employee.idCardNumber),
|
||||
},
|
||||
evidence,
|
||||
risks,
|
||||
@@ -691,6 +692,7 @@ router.get('/:id/evidence-chain/export', authMiddleware, async (req: AuthRequest
|
||||
const empStatus = employee.terminations.some((t) => t.status === 'COMPLETED' && t.terminationDate <= today) ? '离职' : '在职'
|
||||
wsInfo.addRows([
|
||||
{ label: '姓名', value: empName },
|
||||
{ label: '身份证号', value: safeDecryptStr(employee.idCardNumber) || '' },
|
||||
{ label: '部门', value: empDept },
|
||||
{ label: '入职日期', value: hireDate },
|
||||
{ label: '状态', value: empStatus },
|
||||
|
||||
@@ -13,12 +13,18 @@ export async function listByMonth(orgId: string, month: string) {
|
||||
where: { orgId, month },
|
||||
include: {
|
||||
employee: {
|
||||
select: { id: true, name: true, department: true, idCardHash: true },
|
||||
select: { id: true, name: true, department: true, idCardHash: true, idCardNumber: true },
|
||||
},
|
||||
},
|
||||
orderBy: [{ employee: { department: 'asc' } }, { employee: { name: 'asc' } }],
|
||||
})
|
||||
return records
|
||||
return records.map(r => ({
|
||||
...r,
|
||||
employee: r.employee ? {
|
||||
...r.employee,
|
||||
idCardNumber: (() => { try { const v = r.employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(),
|
||||
} : r.employee,
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import prisma from '../lib/prisma'
|
||||
import { sha256 } from '../lib/crypto'
|
||||
import { sha256, decrypt } from '../lib/crypto'
|
||||
|
||||
/**
|
||||
* 递归排序对象/数组中所有 key,确保 JSON.stringify 结果一致
|
||||
@@ -165,7 +165,7 @@ export async function getEvidenceList(orgId: string, category?: string, page: nu
|
||||
prisma.evidenceChain.count({ where }),
|
||||
prisma.evidenceChain.findMany({
|
||||
where,
|
||||
include: { employee: { select: { id: true, name: true, department: true } } },
|
||||
include: { employee: { select: { id: true, name: true, department: true, idCardNumber: true } } },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
@@ -187,6 +187,7 @@ export async function getEvidenceList(orgId: string, category?: string, page: nu
|
||||
employeeId: r.employeeId,
|
||||
employeeName: r.employee?.name || null,
|
||||
employeeDept: r.employee?.department || null,
|
||||
employeeIdCardNumber: (() => { try { const v = r.employee?.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(),
|
||||
eventCount: events.length,
|
||||
firstAction: firstEvent?.action || null,
|
||||
lastAction: lastEvent?.action || null,
|
||||
|
||||
@@ -171,7 +171,10 @@ export default function CommissionBonus() {
|
||||
<tr><td colSpan={5} className="px-4 py-8 text-center text-gray-400">{month} 无提成奖金记录</td></tr>
|
||||
) : paged.map((r: any) => (
|
||||
<tr key={r.id} className="hover:bg-gray-50">
|
||||
<td className="px-4 py-2.5 text-sm text-gray-900">{r.employee?.name || '-'}</td>
|
||||
<td className="px-4 py-2.5 text-sm text-gray-900">
|
||||
{r.employee?.name || '-'}
|
||||
{r.employee?.idCardNumber && <div className="text-xs text-gray-400 font-mono">{r.employee.idCardNumber}</div>}
|
||||
</td>
|
||||
<td className="px-4 py-2.5 text-sm text-gray-500">{r.employee?.department || '-'}</td>
|
||||
<td className={`px-4 py-2.5 text-sm text-right font-medium ${r.amount >= 0 ? 'text-safe' : 'text-danger'}`}>
|
||||
{r.amount >= 0 ? '+' : ''}{fmt(r.amount)}
|
||||
@@ -241,7 +244,7 @@ function AddModal({ employees, month, onClose, onSubmit, saving }: {
|
||||
<Select value={employeeId} onChange={(e) => setEmployeeId(e.target.value)}>
|
||||
<option value="">请选择员工</option>
|
||||
{employees.map((e: any) => (
|
||||
<option key={e.id} value={e.id}>{e.name}({e.department})</option>
|
||||
<option key={e.id} value={e.id}>{e.name}({e.department}){e.idCardNumber ? ` ${e.idCardNumber}` : ''}</option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
@@ -281,7 +284,7 @@ function EditModal({ record, onClose, onSubmit, saving }: {
|
||||
const [remark, setRemark] = useState(record.remark || '')
|
||||
|
||||
return (
|
||||
<Modal open onClose={onClose} title={`编辑 - ${record.employee?.name || ''}`} size="md">
|
||||
<Modal open onClose={onClose} title={`编辑 - ${record.employee?.name || ''}${record.employee?.idCardNumber ? `(${record.employee.idCardNumber})` : ''}`} size="md">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label>金额 *(正=奖金,负=扣款)</Label>
|
||||
|
||||
@@ -121,7 +121,7 @@ export default function Evidence() {
|
||||
</div>
|
||||
{item.employeeName && (
|
||||
<div className="text-xs text-gray-500 mt-0.5">
|
||||
员工:{item.employeeName}{item.employeeDept ? ` · ${item.employeeDept}` : ''}
|
||||
员工:{item.employeeName}{item.employeeDept ? ` · ${item.employeeDept}` : ''}{item.employeeIdCardNumber && <span className="ml-1 font-mono text-gray-400">{item.employeeIdCardNumber}</span>}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-xs text-gray-400 mt-0.5 flex items-center gap-2">
|
||||
|
||||
@@ -48,6 +48,7 @@ export default function EvidenceChain({ employeeId }: { employeeId: string }) {
|
||||
<h2 className="text-xs font-medium flex items-center gap-2"><Scale className="w-4 h-4" />仲裁证据链</h2>
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
{data.employee?.name || '未知'} · {data.employee?.department || '未知'} · 入职{data.employee?.hireDate ? data.employee.hireDate.toString().slice(0, 10) : '未知'}
|
||||
{data.employee?.idCardNumber && <span className="ml-1 font-mono text-gray-400">{data.employee.idCardNumber}</span>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -104,6 +105,7 @@ export default function EvidenceChain({ employeeId }: { employeeId: string }) {
|
||||
<h2 className="text-xs font-medium flex items-center gap-2"><Scale className="w-4 h-4" />仲裁证据链</h2>
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
{data.employee.name} · {data.employee.department} · 入职{data.employee.hireDate}
|
||||
{data.employee.idCardNumber && <span className="ml-1 font-mono text-gray-400">{data.employee.idCardNumber}</span>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
@@ -228,6 +230,7 @@ export function generateEvidenceText(data: any): string {
|
||||
lines.push('========================================')
|
||||
lines.push('')
|
||||
lines.push(`员工姓名:${data.employee.name}`)
|
||||
if (data.employee.idCardNumber) lines.push(`身份证号:${data.employee.idCardNumber}`)
|
||||
lines.push(`部门:${data.employee.department}`)
|
||||
lines.push(`入职日期:${data.employee.hireDate}`)
|
||||
lines.push(`状态:${data.employee.status === 'ACTIVE' ? '在职' : '离职'}`)
|
||||
|
||||
Reference in New Issue
Block a user