From f845d6b6a41d5d845ad2dd870e616b5feafe390b Mon Sep 17 00:00:00 2001 From: Trae Assistant Date: Mon, 5 Oct 2026 08:35:24 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E5=88=A0=E9=99=A4=20undefined/=E5=A4=B1=E8=B4=A5=E9=97=AE?= =?UTF-8?q?=E9=A2=98=EF=BC=88POST=20=E6=9B=BF=E4=BB=A3=20DELETE=20body?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 批量删除改走 POST /users/batch-remove(稳定保证 body 可被 Express 解析),同时增强 DELETE /batch 兼容 query string 回退 - 前端 usersApi.batchRemove 改用 POST 请求;批量删除函数新增 ok 标志位校验 + ?? 0 防 undefined,catch 分支带前缀明确提示失败原因 - npm run build 通过 --- client/src/api/index.ts | 6 +---- client/src/pages/admin/Users.tsx | 8 ++++-- server/src/routes/users.ts | 43 +++++++++++++++++++++++++++++--- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/client/src/api/index.ts b/client/src/api/index.ts index 6becb56..2b570cb 100644 --- a/client/src/api/index.ts +++ b/client/src/api/index.ts @@ -66,11 +66,7 @@ export const usersApi = { results: Array<{ idx: number; ok: boolean; id?: number; error?: string; item: any }>; }>('/users/batch', { items }).then(r => r.data), batchRemove: (ids: number[]) => - api.request<{ ok: boolean; deleted: number; total: number }>({ - url: '/users/batch', - method: 'DELETE', - data: { ids }, - }).then(r => r.data), + api.post<{ ok: boolean; deleted: number; total: number }>('/users/batch-remove', { ids }).then(r => r.data), }; export const activitiesApi = { diff --git a/client/src/pages/admin/Users.tsx b/client/src/pages/admin/Users.tsx index e244955..0ed9a24 100644 --- a/client/src/pages/admin/Users.tsx +++ b/client/src/pages/admin/Users.tsx @@ -165,11 +165,15 @@ export default function Users() { if (!confirm(`确认删除选中的 ${selectedCount} 名部员?相关的时长登记也会一并删除,此操作不可撤销。`)) return; try { const result = await usersApi.batchRemove(Array.from(selectedIds)); - alert(`批量删除完成:共提交 ${result.total} 条,实际删除 ${result.deleted} 条`); + if (!result || result.ok === false) { + alert('批量删除失败:' + ((result as any)?.error || '未知错误')); + return; + } + alert(`批量删除完成:共提交 ${result.total ?? 0} 条,实际删除 ${result.deleted ?? 0} 条`); clearSelection(); load(); } catch (e) { - alert(errMsg(e)); + alert('批量删除失败:' + errMsg(e)); } }; diff --git a/server/src/routes/users.ts b/server/src/routes/users.ts index 5a87430..87c344f 100644 --- a/server/src/routes/users.ts +++ b/server/src/routes/users.ts @@ -237,9 +237,13 @@ router.post('/batch', authMiddleware, (req: AuthRequest, res: Response) => { }); router.delete('/batch', authMiddleware, (req: AuthRequest, res: Response) => { - const { ids } = req.body || {}; + const ids = Array.isArray((req.body || {}).ids) + ? (req.body as any).ids + : Array.isArray((req.query || {}).ids) + ? [].concat((req.query as any).ids as any) + : null; if (!Array.isArray(ids) || ids.length === 0) { - return res.status(400).json({ error: '请选择要删除的部员' }); + return res.status(400).json({ ok: false, error: '请选择要删除的部员' }); } const db = getDb(); const list: number[] = []; @@ -248,7 +252,7 @@ router.delete('/batch', authMiddleware, (req: AuthRequest, res: Response) => { if (!isNaN(n) && n > 0 && !list.includes(n)) list.push(n); } if (list.length === 0) { - return res.status(400).json({ error: '请选择有效的部员' }); + return res.status(400).json({ ok: false, error: '请选择有效的部员' }); } try { db.execCompat('BEGIN'); @@ -263,7 +267,38 @@ router.delete('/batch', authMiddleware, (req: AuthRequest, res: Response) => { res.json({ ok: true, deleted: count, total: list.length }); } catch (e: any) { try { db.execCompat('ROLLBACK'); } catch {} - res.status(500).json({ error: e?.message || '批量删除失败' }); + res.status(500).json({ ok: false, error: e?.message || '批量删除失败' }); + } +}); + +router.post('/batch-remove', authMiddleware, (req: AuthRequest, res: Response) => { + const { ids } = req.body || {}; + if (!Array.isArray(ids) || ids.length === 0) { + return res.status(400).json({ ok: false, error: '请选择要删除的部员' }); + } + const db = getDb(); + const list: number[] = []; + for (const id of ids) { + const n = Number(id); + if (!isNaN(n) && n > 0 && !list.includes(n)) list.push(n); + } + if (list.length === 0) { + return res.status(400).json({ ok: false, error: '请选择有效的部员' }); + } + try { + db.execCompat('BEGIN'); + const del = db.prepareCompat('DELETE FROM users WHERE id = ?'); + let count = 0; + for (const id of list) { + const r = del.run(id); + count += (r?.changes ?? 0); + } + db.execCompat('COMMIT'); + db.save(); + res.json({ ok: true, deleted: count, total: list.length }); + } catch (e: any) { + try { db.execCompat('ROLLBACK'); } catch {} + res.status(500).json({ ok: false, error: e?.message || '批量删除失败' }); } });