From 934cde0034ed2d579e33c156ff68b9cf608cbe9f Mon Sep 17 00:00:00 2001 From: liyongde <1419499670@qq.com> Date: Mon, 17 Aug 2026 21:32:15 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=98=B2=E6=AD=A2=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E6=98=A0=E5=B0=84=E4=B8=A2=E5=A4=B1=E8=BE=93?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/batch-form-model.test.ts | 104 +++++++++++++++++- .../modules/batch-form-model.ts | 43 ++++++-- 2 files changed, 138 insertions(+), 9 deletions(-) diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/lms/subpackagerelation/modules/batch-form-model.test.ts b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/lms/subpackagerelation/modules/batch-form-model.test.ts index a2efd358..414bd57a 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/lms/subpackagerelation/modules/batch-form-model.test.ts +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/lms/subpackagerelation/modules/batch-form-model.test.ts @@ -74,6 +74,18 @@ describe('batch-form-model', () => { }); }); + it('reports a required field when its runtime value is missing', () => { + const row = createBatchRow(); + const incompleteRow = { + ...row, + containerName: undefined, + } as unknown as typeof row; + + expect(validateBatchRows([incompleteRow]).get(0)).toMatchObject({ + containerName: '子卷号不能为空', + }); + }); + it('does not report an error for a complete row', () => { const row = Object.assign(createBatchRow(), { packageBoxSn: 'BOX-001', @@ -95,13 +107,13 @@ describe('batch-form-model', () => { failures: [ { clientKey: rows[2]!.clientKey, - rowIndex: 2, + rowIndex: 3, errorCode: 'INVALID', message: '第三行失败', }, { clientKey: rows[0]!.clientKey, - rowIndex: 0, + rowIndex: 1, errorCode: 'DUPLICATE', message: '第一行失败', }, @@ -117,6 +129,94 @@ describe('batch-form-model', () => { ); }); + it('falls back to one-based row index when the failure client key is unknown', () => { + const rows = [createBatchRow(), createBatchRow()]; + const result = applyBatchResult(rows, { + successCount: 1, + failureCount: 1, + failures: [ + { + clientKey: 'unknown-key', + rowIndex: 2, + errorCode: 'INVALID', + message: '第二行失败', + }, + ], + }); + + expect(result.rows).toEqual([rows[1]]); + expect(result.rowErrors).toEqual( + new Map([[rows[1]!.clientKey, '第二行失败']]), + ); + }); + + it('keeps every row when a known client key conflicts with row index', () => { + const rows = [createBatchRow(), createBatchRow()]; + const result = applyBatchResult(rows, { + successCount: 1, + failureCount: 1, + failures: [ + { + clientKey: rows[0]!.clientKey, + rowIndex: 2, + errorCode: 'CONFLICT', + message: '响应冲突', + }, + ], + }); + const generalError = '批量保存结果无法匹配,请核对后重试'; + + expect(result.rows).toEqual(rows); + expect(result.rowErrors).toEqual( + new Map(rows.map((row) => [row.clientKey, generalError])), + ); + }); + + it('keeps every row when an unknown client key has an invalid row index', () => { + const rows = [createBatchRow(), createBatchRow()]; + const result = applyBatchResult(rows, { + successCount: 1, + failureCount: 1, + failures: [ + { + clientKey: 'unknown-key', + rowIndex: 3, + errorCode: 'INVALID_INDEX', + message: '无法定位', + }, + ], + }); + const generalError = '批量保存结果无法匹配,请核对后重试'; + + expect(result.rows).toEqual(rows); + expect(result.rowErrors).toEqual( + new Map(rows.map((row) => [row.clientKey, generalError])), + ); + }); + + it('keeps every row when input client keys are duplicated', () => { + const rows = [createBatchRow(), createBatchRow()]; + rows[1]!.clientKey = rows[0]!.clientKey; + const result = applyBatchResult(rows, { + successCount: 1, + failureCount: 1, + failures: [ + { + clientKey: rows[0]!.clientKey, + rowIndex: 1, + errorCode: 'INVALID', + message: '第一行失败', + }, + ], + }); + const generalError = '批量保存结果无法匹配,请核对后重试'; + + expect(result.rows).toEqual(rows); + expect(result.rowErrors).toEqual( + new Map(rows.map((row) => [row.clientKey, generalError])), + ); + }); + it('returns no rows or errors when every row succeeds', () => { const result = applyBatchResult([createBatchRow()], { successCount: 1, diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/lms/subpackagerelation/modules/batch-form-model.ts b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/lms/subpackagerelation/modules/batch-form-model.ts index 1260cd3b..550737d5 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/lms/subpackagerelation/modules/batch-form-model.ts +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/lms/subpackagerelation/modules/batch-form-model.ts @@ -64,7 +64,7 @@ export function validateBatchRows( rows.forEach((row, index) => { const errors: BatchFieldErrors = {}; requiredFields.forEach(([field, message]) => { - if (row[field]?.trim().length === 0) { + if (!row[field]?.trim()) { errors[field] = message; } }); @@ -80,12 +80,41 @@ export function applyBatchResult( rows: BatchFormRow[], result: LmsSubPackageRelationApi.BatchCreateResult, ): { rows: BatchFormRow[]; rowErrors: Map } { - const failedClientKeys = new Set( - result.failures.map((failure) => failure.clientKey), - ); - const rowErrors = new Map( - result.failures.map((failure) => [failure.clientKey, failure.message]), - ); + if (result.failures.length === 0) { + return { rows: [], rowErrors: new Map() }; + } + + const generalError = '批量保存结果无法匹配,请核对后重试'; + const rowByClientKey = new Map(); + for (const row of rows) { + if (rowByClientKey.has(row.clientKey)) { + return { + rows, + rowErrors: new Map(rows.map((item) => [item.clientKey, generalError])), + }; + } + rowByClientKey.set(row.clientKey, row); + } + + const failedClientKeys = new Set(); + const rowErrors = new Map(); + for (const failure of result.failures) { + const rowByKey = rowByClientKey.get(failure.clientKey); + const rowByIndex = rows[failure.rowIndex - 1]; + if ( + (rowByKey && rowByIndex?.clientKey !== rowByKey.clientKey) || + (!rowByKey && !rowByIndex) + ) { + return { + rows, + rowErrors: new Map(rows.map((item) => [item.clientKey, generalError])), + }; + } + + const matchedRow = rowByKey ?? rowByIndex!; + failedClientKeys.add(matchedRow.clientKey); + rowErrors.set(matchedRow.clientKey, failure.message); + } return { rows: rows.filter((row) => failedClientKeys.has(row.clientKey)),