feat(web-admin): enable copy-first and batch slot fill on task output dialog

Speed up multi-line cell product inbound entry in production task execution.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-07-28 13:35:59 +08:00
1 parent 0c24165922
commit 3079d5da7e
1 file changed
+69 -2
@@ -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%;
}