|
|
@@ -471,6 +471,16 @@
|
|
|
<div class="product-lines-head">
|
|
|
<span class="product-lines-title">入库明细</span>
|
|
|
<span class="form-label-tip">{{ productLinesHint }}</span>
|
|
|
+ <div class="product-lines-actions">
|
|
|
+ <el-button class="btn-ghost" @click="handleCopyFirstLineAttrs">
|
|
|
+ <el-icon><CopyDocument /></el-icon>
|
|
|
+ 复制首行属性
|
|
|
+ </el-button>
|
|
|
+ <el-button class="btn-ghost" @click="openBatchSlotFill">
|
|
|
+ <el-icon><Grid /></el-icon>
|
|
|
+ 批量填储物格
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<el-table
|
|
|
:data="productLines"
|
|
|
@@ -568,6 +578,13 @@
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
|
|
|
+ <ProductLinesBatchSlotFillDialog
|
|
|
+ v-model="batchSlotFillVisible"
|
|
|
+ :empty-line-count="emptyStorageSlotLineCount"
|
|
|
+ :form-used-slot-ids="formUsedStorageSlotIds"
|
|
|
+ @filled="onBatchSlotFilled"
|
|
|
+ />
|
|
|
+
|
|
|
<!-- 图片预览弹窗 -->
|
|
|
<el-dialog
|
|
|
v-model="previewVisible"
|
|
|
@@ -757,10 +774,13 @@
|
|
|
Box,
|
|
|
Delete,
|
|
|
Download,
|
|
|
- Loading
|
|
|
+ Loading,
|
|
|
+ CopyDocument,
|
|
|
+ Grid
|
|
|
} from '@element-plus/icons-vue'
|
|
|
import SystemViewDialog from '@/components/business/system-view-dialog/index.vue'
|
|
|
import type { ViewSection } from '@/components/business/system-view-dialog/index.vue'
|
|
|
+ import ProductLinesBatchSlotFillDialog from '@/components/business/product-lines-batch-slot-fill-dialog/index.vue'
|
|
|
import QrcodeVue from 'qrcode.vue'
|
|
|
import SourceChainGraph from './SourceChainGraph.vue'
|
|
|
import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading'
|
|
|
@@ -787,6 +807,10 @@
|
|
|
filterStorageSlotSelectOptions,
|
|
|
validateUniqueStorageSlotIds
|
|
|
} from '@/utils/storage-slot-options'
|
|
|
+ import {
|
|
|
+ copyFirstProductLineAttrs,
|
|
|
+ applySlotIdsToEmptyLines
|
|
|
+ } from '@/utils/storage-slot-range-fill'
|
|
|
import { cellProductTypeNeedsPassage } from '@/utils/cell-product-type'
|
|
|
import {
|
|
|
EXPECTED_CELL_COUNT_LABEL,
|
|
|
@@ -969,6 +993,44 @@
|
|
|
})
|
|
|
|
|
|
const productLines = ref<ProductLine[]>([createEmptyProductLine()])
|
|
|
+ const batchSlotFillVisible = ref(false)
|
|
|
+
|
|
|
+ const emptyStorageSlotLineCount = computed(
|
|
|
+ () => productLines.value.filter((l) => !String(l.storageSlotId || '').trim()).length
|
|
|
+ )
|
|
|
+
|
|
|
+ const formUsedStorageSlotIds = computed(() =>
|
|
|
+ productLines.value.map((l) => l.storageSlotId).filter((id) => String(id || '').trim())
|
|
|
+ )
|
|
|
+
|
|
|
+ function handleCopyFirstLineAttrs() {
|
|
|
+ const r = copyFirstProductLineAttrs(productLines.value)
|
|
|
+ if (!r.ok) {
|
|
|
+ ElMessage.warning(r.message)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ElMessage.success(`已将首行属性复制到其余 ${r.filledCount} 行`)
|
|
|
+ }
|
|
|
+
|
|
|
+ function openBatchSlotFill() {
|
|
|
+ if (emptyStorageSlotLineCount.value <= 0) {
|
|
|
+ ElMessage.warning('没有可填充的空行')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ batchSlotFillVisible.value = true
|
|
|
+ }
|
|
|
+
|
|
|
+ function onBatchSlotFilled(payload: {
|
|
|
+ slotIds: string[]
|
|
|
+ options: Array<{ id: string; name: string; facilityNodeId?: string }>
|
|
|
+ }) {
|
|
|
+ const n = applySlotIdsToEmptyLines(productLines.value, payload.slotIds)
|
|
|
+ storageSlotOptions.value = ensureStorageSlotsInOptions(
|
|
|
+ storageSlotOptions.value,
|
|
|
+ payload.options
|
|
|
+ )
|
|
|
+ ElMessage.success(`已为 ${n} 行填充储物格`)
|
|
|
+ }
|
|
|
|
|
|
const formRules = {
|
|
|
productCode: [{ required: true, message: '请输入产品编码', trigger: 'blur' }],
|
|
|
@@ -1874,7 +1936,7 @@
|
|
|
|
|
|
.product-lines-head {
|
|
|
display: flex;
|
|
|
- align-items: baseline;
|
|
|
+ align-items: center;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 8px;
|
|
|
margin-bottom: 10px;
|
|
|
@@ -1886,6 +1948,13 @@
|
|
|
color: var(--mint-text, #334155);
|
|
|
}
|
|
|
|
|
|
+ .product-lines-actions {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+ margin-left: auto;
|
|
|
+ }
|
|
|
+
|
|
|
.product-lines-table {
|
|
|
width: 100%;
|
|
|
}
|