Просмотр исходного кода

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>
zlwu 3 месяцев назад
Родитель
Сommit
d620d5d857
1 измененных файлов с 71 добавлено и 2 удалено
  1. 71 2
      web-admin/src/views/production/cell-product/index.vue

+ 71 - 2
web-admin/src/views/production/cell-product/index.vue

@@ -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%;
   }