From 9e2c5d04d356da44c2f1ab4439e19c7af2a475dd Mon Sep 17 00:00:00 2001 From: liyongde <1419499670@qq.com> Date: Mon, 17 Aug 2026 21:26:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=8C=85=E8=A3=85?= =?UTF-8?q?=E5=85=B3=E7=B3=BB=E6=89=B9=E9=87=8F=E5=BD=95=E5=85=A5=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/api/lms/subpackagerelation/index.ts | 39 ++++++ .../modules/batch-form-model.test.ts | 129 ++++++++++++++++++ .../modules/batch-form-model.ts | 94 +++++++++++++ .../packages/constants/src/dict-enum.ts | 1 + 4 files changed, 263 insertions(+) create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/lms/subpackagerelation/modules/batch-form-model.test.ts create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/lms/subpackagerelation/modules/batch-form-model.ts diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/lms/subpackagerelation/index.ts b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/lms/subpackagerelation/index.ts index ad10fc8a..816b54b7 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/lms/subpackagerelation/index.ts +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/lms/subpackagerelation/index.ts @@ -3,6 +3,35 @@ import type { PageParam, PageResult } from '@vben/request'; import { requestClient } from '#/api/request'; export namespace LmsSubPackageRelationApi { + /** 批量新增子卷包装关系项 */ + export interface BatchCreateItem { + clientKey: string; + packageBoxSn: string; + boxType: string; + boxLength?: string; + boxWidth?: string; + boxHigh?: string; + qualityGuaranPeriod: string; + dateOfFgInbound: string; + containerName: string; + status: string; + } + + /** 批量新增失败信息 */ + export interface BatchCreateFailure { + clientKey: string; + rowIndex: number; + errorCode: string; + message: string; + } + + /** 批量新增结果 */ + export interface BatchCreateResult { + successCount: number; + failureCount: number; + failures: BatchCreateFailure[]; + } + /** 子卷包装关系信息 */ export interface SubPackageRelation { id: number; // 子卷包装标识 @@ -55,6 +84,16 @@ export namespace LmsSubPackageRelationApi { } } +/** 批量新增子卷包装关系 */ +export function batchCreateSubPackageRelation( + items: LmsSubPackageRelationApi.BatchCreateItem[], +) { + return requestClient.post( + '/lms/sub-package-relation/batch-create', + { items }, + ); +} + /** 查询子卷包装关系分页 */ export function getSubPackageRelationPage(params: PageParam) { return requestClient.get>( 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 new file mode 100644 index 00000000..a2efd358 --- /dev/null +++ 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 @@ -0,0 +1,129 @@ +import { describe, expect, it } from 'vitest'; + +import { + applyBatchResult, + copyBatchRow, + createBatchRow, + validateBatchRows, +} from './batch-form-model'; + +describe('batch-form-model', () => { + it('creates an empty row with default status and a unique client key', () => { + const first = createBatchRow(); + const second = createBatchRow(); + + expect(first).toEqual({ + clientKey: expect.any(String), + packageBoxSn: '', + boxType: '', + boxLength: '', + boxWidth: '', + boxHigh: '', + qualityGuaranPeriod: '', + dateOfFgInbound: '', + containerName: '', + status: '0', + }); + expect(first.clientKey).not.toBe(''); + expect(second.clientKey).not.toBe(first.clientKey); + }); + + it('copies reusable fields while clearing identifiers and runtime state', () => { + const source = Object.assign(createBatchRow(), { + packageBoxSn: 'BOX-001', + boxType: 'TYPE-A', + boxLength: '100', + boxWidth: '80', + boxHigh: '60', + qualityGuaranPeriod: '2027-01-01', + dateOfFgInbound: '2026-01-01', + containerName: 'ROLL-001', + status: '1', + rowError: '运行时错误', + }); + + const copied = copyBatchRow(source); + + expect(copied).toMatchObject({ + packageBoxSn: '', + boxType: 'TYPE-A', + boxLength: '100', + boxWidth: '80', + boxHigh: '60', + qualityGuaranPeriod: '2027-01-01', + dateOfFgInbound: '2026-01-01', + containerName: '', + status: '1', + }); + expect(copied.clientKey).not.toBe(source.clientKey); + expect(copied).not.toHaveProperty('rowError'); + }); + + it('reports all required fields when values are empty or whitespace', () => { + const row = createBatchRow(); + row.packageBoxSn = ' '; + row.status = '\t'; + + expect(validateBatchRows([row]).get(0)).toEqual({ + packageBoxSn: '木箱唯一码不能为空', + boxType: '木箱类型不能为空', + qualityGuaranPeriod: '保质期不能为空', + dateOfFgInbound: '入库日期不能为空', + containerName: '子卷号不能为空', + status: '状态不能为空', + }); + }); + + it('does not report an error for a complete row', () => { + const row = Object.assign(createBatchRow(), { + packageBoxSn: 'BOX-001', + boxType: 'TYPE-A', + qualityGuaranPeriod: '2027-01-01', + dateOfFgInbound: '2026-01-01', + containerName: 'ROLL-001', + status: '0', + }); + + expect(validateBatchRows([row])).toEqual(new Map()); + }); + + it('keeps failed rows in original order and maps server messages by client key', () => { + const rows = [createBatchRow(), createBatchRow(), createBatchRow()]; + const result = applyBatchResult(rows, { + successCount: 1, + failureCount: 2, + failures: [ + { + clientKey: rows[2]!.clientKey, + rowIndex: 2, + errorCode: 'INVALID', + message: '第三行失败', + }, + { + clientKey: rows[0]!.clientKey, + rowIndex: 0, + errorCode: 'DUPLICATE', + message: '第一行失败', + }, + ], + }); + + expect(result.rows).toEqual([rows[0], rows[2]]); + expect(result.rowErrors).toEqual( + new Map([ + [rows[2]!.clientKey, '第三行失败'], + [rows[0]!.clientKey, '第一行失败'], + ]), + ); + }); + + it('returns no rows or errors when every row succeeds', () => { + const result = applyBatchResult([createBatchRow()], { + successCount: 1, + failureCount: 0, + failures: [], + }); + + expect(result).toEqual({ rows: [], rowErrors: new Map() }); + }); +}); 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 new file mode 100644 index 00000000..1260cd3b --- /dev/null +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/lms/subpackagerelation/modules/batch-form-model.ts @@ -0,0 +1,94 @@ +import type { LmsSubPackageRelationApi } from '#/api/lms/subpackagerelation'; + +export interface BatchFormRow + extends LmsSubPackageRelationApi.BatchCreateItem {} + +export type BatchFieldErrors = Partial< + Record +>; + +let fallbackSequence = 0; + +function createClientKey() { + const randomUUID = globalThis.crypto?.randomUUID; + if (randomUUID) { + return randomUUID.call(globalThis.crypto); + } + fallbackSequence += 1; + return `${Date.now()}-${fallbackSequence}`; +} + +export function createBatchRow(): BatchFormRow { + return { + clientKey: createClientKey(), + packageBoxSn: '', + boxType: '', + boxLength: '', + boxWidth: '', + boxHigh: '', + qualityGuaranPeriod: '', + dateOfFgInbound: '', + containerName: '', + status: '0', + }; +} + +export function copyBatchRow(source: BatchFormRow): BatchFormRow { + return { + clientKey: createClientKey(), + packageBoxSn: '', + boxType: source.boxType, + boxLength: source.boxLength, + boxWidth: source.boxWidth, + boxHigh: source.boxHigh, + qualityGuaranPeriod: source.qualityGuaranPeriod, + dateOfFgInbound: source.dateOfFgInbound, + containerName: '', + status: source.status, + }; +} + +export function validateBatchRows( + rows: BatchFormRow[], +): Map { + const result = new Map(); + const requiredFields: [keyof BatchFormRow, string][] = [ + ['packageBoxSn', '木箱唯一码不能为空'], + ['boxType', '木箱类型不能为空'], + ['qualityGuaranPeriod', '保质期不能为空'], + ['dateOfFgInbound', '入库日期不能为空'], + ['containerName', '子卷号不能为空'], + ['status', '状态不能为空'], + ]; + + rows.forEach((row, index) => { + const errors: BatchFieldErrors = {}; + requiredFields.forEach(([field, message]) => { + if (row[field]?.trim().length === 0) { + errors[field] = message; + } + }); + if (Object.keys(errors).length > 0) { + result.set(index, errors); + } + }); + + return result; +} + +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]), + ); + + return { + rows: rows.filter((row) => failedClientKeys.has(row.clientKey)), + rowErrors, + }; +} diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/packages/constants/src/dict-enum.ts b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/packages/constants/src/dict-enum.ts index 15e61b4d..2e3d6635 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/packages/constants/src/dict-enum.ts +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/packages/constants/src/dict-enum.ts @@ -316,6 +316,7 @@ const LMS_DICT = { BOX_POINT_TYPE: 'box_point_type', // 装箱点位类型 BOX_POINT_STATUS: 'box_point_status', // 装箱点位状态 BOX_POINT_DEPTH: 'box_point_depth', // 装箱点位深浅 + SUB_PACKAGE_STATUS: 'sub_package_status', // 子卷包装关系状态 } as const; /** 字典类型枚举 - 统一导出 */