fix: 部员详情参与活动数据没对齐和补录时间显示问题
Deploy to Server / deploy (push) Successful in 1m45s

- 详情 activities 排序改为按登记时间倒序,新增 reg_at 字段
- formatDateRange 同一天合并显示,字符数减半避免被截断
- DataTable 给活动名/时间/时长列显式宽度,时间列加 pl-3 留白防切
This commit is contained in:
Trae Assistant
2026-10-05 14:15:50 +08:00
parent cc7857e009
commit 0d1622d455
3 changed files with 41 additions and 11 deletions
+33 -6
View File
@@ -747,23 +747,50 @@ export default function Users() {
</div>
<div>
<h3 className="font-semibold mb-2 text-gray-800">
参与的活动({detail.activities.length} 次)
参与的活动({detail.activities.length} 次)<span className="ml-2 text-xs font-normal muted">按登记时间倒序,最新补录/登记在最上方</span>
</h3>
<DataTable
columns={[
{ key: 'title', title: '活动名称', render: (r: any) => <span className="font-medium">{r.title}</span> },
{ key: 'time', title: '时间', render: (r: any) => formatDateRange(r.start_time, r.end_time) },
{
key: 'title',
title: '活动名称',
width: '30%',
render: (r: any) => (
<div className="font-medium leading-snug break-words max-w-[280px]">{r.title}</div>
),
},
{
key: 'time',
title: '活动时间',
width: '240px',
render: (r: any) => (
<div className="text-xs font-mono tracking-wide leading-snug whitespace-nowrap pl-3 pr-2 text-gray-700 text-left">
{formatDateRange(r.start_time, r.end_time)}
</div>
),
},
{
key: 'hours',
title: '时长',
width: '100px',
render: (r: any) => (
<span className="font-semibold text-primary-600">{Number(r.hours || 0).toFixed(1)}h</span>
<span className="font-semibold text-primary-600 text-center block w-full text-sm">
{Number(r.hours || 0).toFixed(1)}h
</span>
),
},
{
key: 'remark',
title: '备注',
render: (r: any) => (
<div className="leading-relaxed break-words text-gray-700 pr-2">
{r.remark || <span className="muted">—</span>}
</div>
),
},
{ key: 'remark', title: '备注', render: (r: any) => r.remark || '—' },
]}
data={detail.activities}
rowKey={(r: any) => r.id}
rowKey={(r: any) => `${r.id}-${r.reg_at || r.hours}-${r.remark || ''}`}
/>
</div>
</div>
+6 -3
View File
@@ -271,9 +271,12 @@ export async function exportTableToImage(el: HTMLElement, filename: string) {
}
export function formatDateRange(s: string, e: string) {
const a = s?.replace('T', ' ')?.slice(0, 16);
const b = e?.replace('T', ' ')?.slice(0, 16);
return `${a || ''} ~ ${b || ''}`;
const a = s?.replace('T', ' ')?.slice(0, 16) || '';
const b = e?.replace('T', ' ')?.slice(0, 16) || '';
if (a && b && a.slice(0, 10) === b.slice(0, 10)) {
return `${a} ~ ${b.slice(11)}`;
}
return `${a} ~ ${b}`;
}
export function formatCnDateTime(s?: string | null, withSeconds = false): string {
+2 -2
View File
@@ -76,11 +76,11 @@ router.get('/:id', (req: Request, res: Response) => {
if (!raw) return res.status(404).json({ error: '部员不存在' });
const user = enrichUser(raw);
const activities = db.prepareCompat(`
SELECT a.id, a.title, a.start_time, a.end_time, h.hours, h.remark
SELECT a.id, a.title, a.start_time, a.end_time, h.hours, h.remark, h.created_at AS reg_at
FROM hours h
JOIN activities a ON h.activity_id = a.id
WHERE h.user_id = ?
ORDER BY a.start_time DESC
ORDER BY h.created_at DESC, a.start_time DESC
`).all(id);
res.json({ ...user, activities });
});