Explorar el Código

feat(web-admin): align archive brand qty entry with chips UI

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu hace 3 meses
padre
commit
aea4caed5b
Se han modificado 1 ficheros con 42 adiciones y 45 borrados
  1. 42 45
      web-admin/src/components/business/pl-plan-archive-dialog/index.vue

+ 42 - 45
web-admin/src/components/business/pl-plan-archive-dialog/index.vue

@@ -32,33 +32,17 @@
                 <span class="pl-archive-brand-qty-title">{{ row.productName }} · 按品牌填量</span>
                 <el-tag v-if="row.brandMode === 'required'" size="small" type="warning">指定品牌</el-tag>
                 <el-tag v-else size="small" type="info">不限品牌</el-tag>
-                <span
-                  v-if="!isBrandQtySumMatch(row)"
-                  class="pl-archive-brand-qty-warn"
-                >
-                  各品牌合计须等于实际数量({{ Number(row.actualQty ?? 0) }})
-                </span>
               </div>
-              <el-table :data="row.brandQtyRows || []" stripe size="small" class="list-table">
-                <el-table-column label="品牌" min-width="140" show-overflow-tooltip>
-                  <template #default="{ row: brandRow }">
-                    {{ brandRow.brandName || '—' }}
-                  </template>
-                </el-table-column>
-                <el-table-column label="数量" min-width="140" align="right">
-                  <template #default="{ row: brandRow }">
-                    <el-input-number
-                      v-model="brandRow.qty"
-                      :min="0"
-                      :precision="3"
-                      :controls="false"
-                      :disabled="row.brandMode === 'required'"
-                      style="width: 100%"
-                      @change="() => onBrandQtyChange(row)"
-                    />
-                  </template>
-                </el-table-column>
-              </el-table>
+              <BrandQtyChipsField
+                v-model="row.brandQtyRows"
+                :brand-mode="archiveBrandMode(row)"
+                :required-brand-id="row.brandId"
+                :brand-options="row.brandOptions || []"
+                :unit="row.unit || '吨'"
+                variant="admin"
+                :actual-qty="Number(row.actualQty ?? 0)"
+                @update:actual-qty="(val) => onArchiveBrandActualQty(row, val)"
+              />
             </div>
           </section>
 
@@ -163,6 +147,7 @@ import {
   type PlIoOrder,
   type Transport
 } from '@/api/pl'
+import BrandQtyChipsField from '@/components/business/brand-qty-chips-field/index.vue'
 import PlPlanFormProductTable from '@/components/business/pl-plan-form-dialog/PlPlanFormProductTable.vue'
 import PlPlanViewTabs from '@/components/business/pl-plan-view-tabs/index.vue'
 import type { PlanFormProductRow } from '@/composables/usePlContractToPlan'
@@ -235,6 +220,11 @@ const brandedProductRows = computed(() =>
   productRows.value.filter(row => row.brandMode === 'required' || row.brandMode === 'any')
 )
 
+function archiveBrandMode(row: PlanFormProductRow): 'any' | 'required' | null {
+  if (row.brandMode === 'any' || row.brandMode === 'required') return row.brandMode
+  return null
+}
+
 function brandQtySum(row: PlanFormProductRow): number {
   return (row.brandQtyRows || []).reduce((sum, item) => sum + Number(item.qty || 0), 0)
 }
@@ -244,9 +234,10 @@ function isBrandQtySumMatch(row: PlanFormProductRow): boolean {
   return Math.abs(brandQtySum(row) - Number(row.actualQty ?? 0)) < 0.0005
 }
 
-function onBrandQtyChange(row: PlanFormProductRow) {
+function onArchiveBrandActualQty(row: PlanFormProductRow, val: number) {
+  // any:合计回写实际数量;required:实际数量可在产品表改,芯片数量跟合计联动
   if (row.brandMode === 'any') {
-    row.actualQty = Number(brandQtySum(row).toFixed(3))
+    row.actualQty = Number(val)
   }
 }
 
@@ -274,6 +265,7 @@ function mapPlanProducts(products: PlanProduct[]): PlanFormProductRow[] {
     brandMode: item.brandMode,
     brandId: item.brandId,
     brandName: item.brandName,
+    brandOptions: [],
     brandQtyRows: [],
     model: item.model,
     categoryName: item.categoryName,
@@ -315,26 +307,36 @@ async function initBrandQtyRows(rows: PlanFormProductRow[]) {
   rows.forEach(row => {
     const actual = Number(row.actualQty ?? 0)
     if (row.brandMode === 'required') {
-      row.brandQtyRows = [
-        {
-          brandId: row.brandId || '',
-          brandName: row.brandName || '指定品牌',
-          qty: actual
-        }
-      ]
+      const option = {
+        brandId: row.brandId || '',
+        brandName: row.brandName || '指定品牌'
+      }
+      row.brandOptions = option.brandId ? [option] : []
+      row.brandQtyRows = option.brandId
+        ? [{ brandId: option.brandId, brandName: option.brandName, qty: actual }]
+        : []
       return
     }
     if (row.brandMode === 'any') {
       const brands = (detailMap.get(row.productId)?.brands || []).filter(
         b => (b.status || 'on') === 'on'
       )
-      row.brandQtyRows = brands.map(b => ({
+      row.brandOptions = brands.map(b => ({
         brandId: b.id || '',
-        brandName: b.brandName,
-        qty: 0
+        brandName: b.brandName || ''
       }))
-      if (row.brandQtyRows.length === 1) {
-        row.brandQtyRows[0].qty = actual
+      // 芯片交互:默认不预选;仅单品牌时预填实际数量
+      if (row.brandOptions.length === 1) {
+        row.brandQtyRows = [
+          {
+            brandId: row.brandOptions[0].brandId,
+            brandName: row.brandOptions[0].brandName,
+            qty: actual
+          }
+        ]
+        row.actualQty = actual
+      } else {
+        row.brandQtyRows = []
       }
     }
   })
@@ -594,11 +596,6 @@ async function handleSubmit() {
   color: #334155;
 }
 
-.pl-archive-brand-qty-warn {
-  font-size: 12px;
-  color: var(--el-color-danger, #ef4444);
-}
-
 .pl-plan-freight-section {
   .pl-plan-subtitle {
     margin-top: 4px;