|
|
@@ -179,6 +179,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"
|
|
|
@@ -286,10 +296,17 @@
|
|
|
</el-button>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
+ <ProductLinesBatchSlotFillDialog
|
|
|
+ v-model="batchSlotFillVisible"
|
|
|
+ :empty-line-count="emptyStorageSlotLineCount"
|
|
|
+ :form-used-slot-ids="formUsedStorageSlotIds"
|
|
|
+ :occupied-slot-ids="occupiedSlotIds || []"
|
|
|
+ @filled="onBatchSlotFilled"
|
|
|
+ />
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
- import { Close, Check, MagicStick } from '@element-plus/icons-vue'
|
|
|
+ import { Close, Check, MagicStick, CopyDocument, Grid } from '@element-plus/icons-vue'
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
import { fetchProductionProductTypeList } from '@/api/production'
|
|
|
import { fetchMaxPassageProductSeq, fetchMaxProductSuffixSeq, fetchCheckCellProductCode } from '@/api/cell-product'
|
|
|
@@ -310,6 +327,11 @@
|
|
|
validateUniqueStorageSlotIds
|
|
|
} from '@/utils/storage-slot-options'
|
|
|
import type { StorageSlotSelectOption } from '@/utils/storage-slot-options'
|
|
|
+ import {
|
|
|
+ copyFirstProductLineAttrs,
|
|
|
+ applySlotIdsToEmptyLines
|
|
|
+ } from '@/utils/storage-slot-range-fill'
|
|
|
+ import ProductLinesBatchSlotFillDialog from '@/components/business/product-lines-batch-slot-fill-dialog/index.vue'
|
|
|
|
|
|
defineOptions({ name: 'TaskOutputProductDialog' })
|
|
|
|
|
|
@@ -483,9 +505,47 @@
|
|
|
const submitLoading = ref(false)
|
|
|
const originalProductCode = ref('')
|
|
|
const productLines = ref<ProductLine[]>([createEmptyProductLine()])
|
|
|
+ const batchSlotFillVisible = ref(false)
|
|
|
let lineCodeSyncToken = 0
|
|
|
let skipLineCodeAutoSync = 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 formData = reactive({
|
|
|
cellProductId: '',
|
|
|
productCode: '',
|
|
|
@@ -1012,7 +1072,7 @@
|
|
|
|
|
|
.product-lines-head {
|
|
|
display: flex;
|
|
|
- align-items: baseline;
|
|
|
+ align-items: center;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 8px;
|
|
|
margin-bottom: 10px;
|
|
|
@@ -1024,6 +1084,13 @@
|
|
|
color: var(--mint-text, #334155);
|
|
|
}
|
|
|
|
|
|
+ .product-lines-actions {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+ margin-left: auto;
|
|
|
+ }
|
|
|
+
|
|
|
.product-lines-table {
|
|
|
width: 100%;
|
|
|
}
|