feat(web-admin): enable copy-first and batch slot fill on cell product create

Keep inbound product-lines UX consistent with task output dialog.

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