fix: 子卷包装关系新增判断erp卷号是否存在

This commit is contained in:
2026-08-18 13:57:14 +08:00
parent 68a7112921
commit d3feb61e62
5 changed files with 260 additions and 73 deletions

View File

@@ -19,6 +19,7 @@ import {
message,
Segmented,
Select,
Table,
} from 'antdv-next';
import { batchCreateSubPackageRelation } from '#/api/lms/subpackagerelation';
@@ -43,6 +44,31 @@ const viewOptions = [
{ label: '▤ 卡片', value: 'card' },
{ label: '▦ 表格', value: 'table' },
];
const batchTableColumns = [
{ title: '序号', key: 'sequence', width: 70, fixed: 'left' as const },
{ title: '木箱唯一码', key: 'packageBoxSn', width: 200 },
{ title: '子卷号', key: 'containerName', width: 200 },
{ title: '木箱类型', key: 'boxType', width: 190 },
{ title: '木箱长', key: 'boxLength', width: 130 },
{ title: '木箱宽', key: 'boxWidth', width: 130 },
{ title: '木箱高', key: 'boxHigh', width: 130 },
{
title: '保质期(天)',
key: 'qualityGuaranPeriod',
width: 170,
},
{ title: '入库日期', key: 'dateOfFgInbound', width: 180 },
{ title: '状态', key: 'status', width: 140 },
{ title: '操作', key: 'operations', width: 150, fixed: 'right' as const },
];
const requiredColumnKeys = new Set([
'boxType',
'containerName',
'dateOfFgInbound',
'packageBoxSn',
'qualityGuaranPeriod',
'status',
]);
const statusOptions = computed(() =>
getDictOptions(DICT_TYPE.SUB_PACKAGE_STATUS).map((item) => ({
@@ -50,6 +76,11 @@ const statusOptions = computed(() =>
value: String(item.value),
})),
);
const errorRowKeys = computed(() =>
rows.value
.filter((row) => rowErrors.value.has(row.clientKey))
.map((row) => row.clientKey),
);
function reset() {
rows.value = [createBatchRow()];
@@ -222,34 +253,37 @@ const [Modal, modalApi] = useVbenModal({
</Alert>
<div v-if="viewMode === 'table'" class="table-wrap">
<table class="batch-table">
<thead>
<tr>
<th>序号</th><th><i>*</i> 木箱唯一码</th><th><i>*</i> 子卷号</th><th><i>*</i> 木箱类型</th>
<th>木箱长</th><th>木箱宽</th><th>木箱高</th>
<th><i>*</i> 保质期</th><th><i>*</i> 入库日期</th>
<th><i>*</i> 状态</th><th>操作</th>
</tr>
</thead>
<tbody>
<template v-for="(row, index) in rows" :key="row.clientKey">
<tr>
<td class="sequence">{{ index + 1 }}</td>
<td><Input v-model:value="row.packageBoxSn" :class="{ invalid: fieldError(index, 'packageBoxSn') }" placeholder="请输入木箱唯一码" @update:value="editField(index, 'packageBoxSn')" /><small>{{ fieldError(index, 'packageBoxSn') }}</small></td>
<td><Input v-model:value="row.containerName" :class="{ invalid: fieldError(index, 'containerName') }" placeholder="请输入子卷号" @update:value="editField(index, 'containerName')" /><small>{{ fieldError(index, 'containerName') }}</small></td>
<td><Input :value="row.boxType" readonly role="button" tabindex="0" aria-label="选择木箱类型" :class="{ invalid: fieldError(index, 'boxType') }" placeholder="请选择木箱类型" @click="selectBoxType(row.clientKey)" @keydown.enter="selectBoxType(row.clientKey)" @keydown.space.prevent="selectBoxType(row.clientKey)" /><small>{{ fieldError(index, 'boxType') }}</small></td>
<td><Input v-model:value="row.boxLength" readonly placeholder="自动带出" /></td>
<td><Input v-model:value="row.boxWidth" readonly placeholder="自动带出" /></td>
<td><Input v-model:value="row.boxHigh" readonly placeholder="自动带出" /></td>
<td><Input v-model:value="row.qualityGuaranPeriod" type="number" :class="{ invalid: fieldError(index, 'qualityGuaranPeriod') }" placeholder="例如90" @update:value="editField(index, 'qualityGuaranPeriod')"><template #suffix><span class="input-unit"></span></template></Input><small>{{ fieldError(index, 'qualityGuaranPeriod') }}</small></td>
<td><DatePicker v-model:value="row.dateOfFgInbound" value-format="YYYY-MM-DD" format="YYYY-MM-DD" class="w-full" :class="{ invalid: fieldError(index, 'dateOfFgInbound') }" placeholder="请选择入库日期" @change="editField(index, 'dateOfFgInbound')" /><small>{{ fieldError(index, 'dateOfFgInbound') }}</small></td>
<td><Select v-model:value="row.status" :options="statusOptions" :class="{ invalid: fieldError(index, 'status') }" @change="editField(index, 'status')" /><small>{{ fieldError(index, 'status') }}</small></td>
<td class="operations"><Button type="link" @click="duplicateRow(index)"><template #icon><IconifyIcon icon="ant-design:copy-outlined" /></template>复制</Button><Button type="link" danger @click="removeRow(index)"><template #icon><IconifyIcon icon="ant-design:delete-outlined" /></template>删除</Button></td>
</tr>
<tr v-if="rowErrors.get(row.clientKey)" class="row-error"><td colspan="11"> {{ index + 1 }} {{ rowErrors.get(row.clientKey) }}</td></tr>
</template>
</tbody>
</table>
<Table
class="batch-table"
bordered
:columns="batchTableColumns"
:data-source="rows"
:expandable="{ showExpandColumn: false }"
:expanded-row-keys="errorRowKeys"
:pagination="false"
row-key="clientKey"
:scroll="{ x: 1690, y: 430 }"
>
<template #headerCell="{ column }">
<i v-if="requiredColumnKeys.has(String(column.key))">*</i> {{ column.title }}
</template>
<template #bodyCell="{ column, record: row, index }">
<span v-if="column.key === 'sequence'" class="sequence">{{ index + 1 }}</span>
<template v-else-if="column.key === 'packageBoxSn'"><Input v-model:value="row.packageBoxSn" :class="{ invalid: fieldError(index, 'packageBoxSn') }" placeholder="请输入木箱唯一码" @update:value="editField(index, 'packageBoxSn')" /><small>{{ fieldError(index, 'packageBoxSn') }}</small></template>
<template v-else-if="column.key === 'containerName'"><Input v-model:value="row.containerName" :class="{ invalid: fieldError(index, 'containerName') }" placeholder="请输入子卷号" @update:value="editField(index, 'containerName')" /><small>{{ fieldError(index, 'containerName') }}</small></template>
<template v-else-if="column.key === 'boxType'"><Input :value="row.boxType" readonly role="button" tabindex="0" aria-label="选择木箱类型" :class="{ invalid: fieldError(index, 'boxType') }" placeholder="请选择木箱类型" @click="selectBoxType(row.clientKey)" @keydown.enter="selectBoxType(row.clientKey)" @keydown.space.prevent="selectBoxType(row.clientKey)" /><small>{{ fieldError(index, 'boxType') }}</small></template>
<Input v-else-if="column.key === 'boxLength'" v-model:value="row.boxLength" readonly placeholder="自动带出" />
<Input v-else-if="column.key === 'boxWidth'" v-model:value="row.boxWidth" readonly placeholder="自动带出" />
<Input v-else-if="column.key === 'boxHigh'" v-model:value="row.boxHigh" readonly placeholder="自动带出" />
<template v-else-if="column.key === 'qualityGuaranPeriod'"><Input v-model:value="row.qualityGuaranPeriod" type="number" :class="{ invalid: fieldError(index, 'qualityGuaranPeriod') }" placeholder="例如90" @update:value="editField(index, 'qualityGuaranPeriod')"><template #suffix><span class="input-unit"></span></template></Input><small>{{ fieldError(index, 'qualityGuaranPeriod') }}</small></template>
<template v-else-if="column.key === 'dateOfFgInbound'"><DatePicker v-model:value="row.dateOfFgInbound" value-format="YYYY-MM-DD" format="YYYY-MM-DD" class="w-full" :class="{ invalid: fieldError(index, 'dateOfFgInbound') }" placeholder="请选择入库日期" @change="editField(index, 'dateOfFgInbound')" /><small>{{ fieldError(index, 'dateOfFgInbound') }}</small></template>
<template v-else-if="column.key === 'status'"><Select v-model:value="row.status" class="w-full" :options="statusOptions" :class="{ invalid: fieldError(index, 'status') }" @change="editField(index, 'status')" /><small>{{ fieldError(index, 'status') }}</small></template>
<div v-else-if="column.key === 'operations'" class="operations"><Button type="link" @click="duplicateRow(index)"><template #icon><IconifyIcon icon="ant-design:copy-outlined" /></template>复制</Button><Button type="link" danger @click="removeRow(index)"><template #icon><IconifyIcon icon="ant-design:delete-outlined" /></template>删除</Button></div>
</template>
<template #expandedRowRender="{ record, index }">
<div class="row-error"> {{ index + 1 }} {{ rowErrors.get(record.clientKey) }}</div>
</template>
</Table>
</div>
<div v-else class="card-list">
@@ -359,7 +393,7 @@ const [Modal, modalApi] = useVbenModal({
.table-wrap {
flex: 1;
min-height: 420px;
overflow: auto;
overflow: hidden;
background: hsl(var(--background));
border: 1px solid hsl(var(--border));
border-radius: 10px;
@@ -367,42 +401,31 @@ const [Modal, modalApi] = useVbenModal({
.batch-table {
width: 100%;
min-width: 1780px;
border-collapse: separate;
border-spacing: 0;
}
.batch-table th,
.batch-table td {
padding: 10px;
vertical-align: top;
background: hsl(var(--background));
border-right: 1px solid hsl(var(--border));
border-bottom: 1px solid hsl(var(--border));
.batch-table :deep(.ant-table) {
border-radius: 10px;
}
.batch-table th {
position: sticky;
top: 0;
z-index: 3;
.batch-table :deep(.ant-table-thead > tr > th) {
height: 48px;
color: #4b5870;
font-weight: 600;
text-align: left;
white-space: nowrap;
background: #f3f6fb;
vertical-align: middle;
}
.batch-table th i {
.batch-table :deep(.ant-table-tbody > tr > td) {
padding: 10px;
vertical-align: top;
background: hsl(var(--background));
}
.batch-table i {
color: #ff4d4f;
font-style: normal;
}
.batch-table td {
min-width: 145px;
}
.batch-table :deep(.ant-input),
.batch-table :deep(.ant-input-affix-wrapper),
.batch-table :deep(.ant-picker),
@@ -429,34 +452,16 @@ const [Modal, modalApi] = useVbenModal({
}
.batch-table .sequence {
position: sticky;
left: 0;
z-index: 2;
min-width: 64px;
display: block;
color: #788196;
text-align: center;
}
.batch-table th:first-child {
left: 0;
z-index: 5;
min-width: 64px;
text-align: center;
}
.batch-table th:last-child,
.batch-table .operations {
position: sticky;
right: 0;
z-index: 2;
min-width: 128px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
white-space: nowrap;
box-shadow: -5px 0 12px rgb(21 35 62 / 6%);
}
.batch-table th:last-child {
z-index: 5;
}
.batch-table small {
@@ -474,7 +479,12 @@ const [Modal, modalApi] = useVbenModal({
border-color: #ff4d4f;
}
.row-error td {
.batch-table :deep(.ant-table-expanded-row > td) {
padding: 0 !important;
}
.row-error {
padding: 9px 16px;
color: #ff4d4f;
background: #fff2f0;
}