From a660db06773007b1be690e0674829c00a57aeb7b Mon Sep 17 00:00:00 2001
From: 廖井涛 <2265517004@qq.com>
Date: 星期三, 24 十二月 2025 16:21:23 +0800
Subject: [PATCH] 订单首页流程卡新增楼层编号显示

---
 north-glass-erp/northglass-erp/src/views/sd/bom/ProductBomAdd.vue |  372 +++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 333 insertions(+), 39 deletions(-)

diff --git a/north-glass-erp/northglass-erp/src/views/sd/bom/ProductBomAdd.vue b/north-glass-erp/northglass-erp/src/views/sd/bom/ProductBomAdd.vue
index 6d5191f..a364e41 100644
--- a/north-glass-erp/northglass-erp/src/views/sd/bom/ProductBomAdd.vue
+++ b/north-glass-erp/northglass-erp/src/views/sd/bom/ProductBomAdd.vue
@@ -1,10 +1,44 @@
 <script setup>
-import {reactive, ref} from "vue";
+import { reactive, ref, computed, watch,onMounted } from "vue";
 import {useI18n} from "vue-i18n";
+import request from "@/utils/request"
+import {changeFilterEvent, filterChanged} from "@/hook";
+import deepClone from "@/utils/deepClone"
+import { ElMessage, ElMessageBox} from "element-plus"
 const { t } = useI18n()
+const emit = defineEmits(['closeDialog'])
+let produceList = ref([])
+let props = defineProps({
+  productName:null,
+  produceId:null,
+  baseGridOptions: { type: Object, required: true },
+
+  gridDataByProduct: { type: Object, default: () => ({}) }
+})
+
+
+
 const xGrid = ref()
 const xGridByProduct = ref()
+const activeProduct = ref('')                 // 褰撳墠閫変腑鐨勪骇鍝佸悕
 let materialType = ref(1)
+
+//鍏抽棴寮圭獥
+const closeDialog = () => {
+  emit('closeDialog')
+}
+
+const value = ref('')
+const options = [
+  {
+    value: t('ingredients.originalFilm'),
+    label: t('ingredients.originalFilm')
+  },
+  {
+    value: t('ingredients.accessories'),
+    label: t('ingredients.accessories'),
+  }
+]
 const gridOptions = reactive({
   border:  "full",//琛ㄦ牸鍔犺竟妗�
   keepSource: true,//淇濇寔婧愭暟鎹�
@@ -26,25 +60,9 @@
   customConfig: {
     storage: true
   },
-  editConfig: {
-    trigger: 'click',
-    mode: 'row',
-    showStatus: true
-  },
   columns:[
-    {field:'id',title: '鐗╂枡缂栧彿', },
-    {field: 'name', title: '鐗╂枡鍚嶇О'},
-    {field: 'thickness', title: '鍘氬害'},
-    {field: 'size', title: '灏哄'},
-    {field:'consumption',title: '娑堣�楅噺' },
-    {field:'unit',title: '鍗曚綅' },
-    {field:'price',title: '浠锋牸' }
   ],//琛ㄥご鍙傛暟
   data:[
-    {id:'1',name:'6mm瓒呯櫧',thickness:6,size:3660*2440,consumption:'1.2',unit:'/銕�',price:'60'},
-    {id:'2',name:'6mmPDE60A03',thickness:6,size:3660*3300,consumption:'1.17',unit:'/銕�',price:'70'},
-    {id:'3',name:'7mmPDE60A03',thickness:7,size:4280*3050,consumption:'1.21',unit:'/銕�',price:'75'},
-    {id:'4',name:'8mm瓒呯櫧US1.16',thickness:6,size:5800*3300,consumption:'1.3',unit:'/銕�',price:'90'}
   ],//琛ㄦ牸鏁版嵁
   toolbarConfig: {
     slots:{
@@ -80,16 +98,18 @@
     showStatus: true
   },
   columns:[
-    {field:'id',title: '鐗╂枡缂栧彿', },
-    {field: 'name', title: '鐗╂枡鍚嶇О'},
-    {field: 'thickness', title: '鍘氬害'},
-    {field: 'size', title: '灏哄'},
-    {field:'consumption',title: '娑堣�楅噺' },
-    {field:'unit',title: '鍗曚綅' },
-    {field:'price',title: '浠锋牸' }
+    {title: '', width: '110', slots: { default: 'button_slot' },fixed:'left'},
+    {field:'id',title: t('ingredients.materialCode') },
+    {field: 'name', title: t('ingredientsStock.materialName')},
+    {field: 'thickness', title: t('product.msg.allThickness')},
+    {field: 'width', title: t('order.width')},
+    {field: 'height', title: t('order.height')},
+    {field:'consume',title: t('bom.consume') },
+    {field:'unit',title: t('ingredients.unit') },
+    {field:'price',title: t('bom.price1') }
   ],//琛ㄥご鍙傛暟
   data:[
-    {id:'1',name:'6mm瓒呯櫧',thickness:6,size:3660*2440,consumption:'1.2',unit:'/銕�',price:'60'}
+
   ],//琛ㄦ牸鏁版嵁
   toolbarConfig: {
     slots: {
@@ -98,20 +118,252 @@
   }
 
 })
+let arr = [
+  {title: t('basicData.add'), width: '110', slots: { default: 'button_slot' },fixed:'left'},
+  {field: 'tabId', width: '150',title: 'BOMId', sortable: true,showOverflow:'ellipsis' ,filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged},
+  {field: 'id', width: '150',title: t('ingredients.materialCode'), sortable: true,showOverflow:'ellipsis' ,filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged},
+  {field: 'consume',title: t('bom.consume'),showOverflow:'ellipsis' },
+  {field: 'price',title: t('bom.price'),showOverflow:'ellipsis' }
+]
+const bomTitle = [
+
+]
+let pageNum=ref(1)
+let total = reactive({
+  pageTotal : 0,
+  dataTotal : 0,
+  pageSize : 1000
+})
+let filterData = ref({
+  type:''
+
+})
+let BasicData = ref([])
+
+let materialStore= ref([])
+onMounted(async () => {
+  //绗竴娆″姞杞介粯璁�
+  value.value=t('ingredients.originalFilm')
+  filterData.value.type=t('ingredients.originalFilm')
+  request.get(`/BasicWarehouse/BasicWarehouseType/${value.value}`).then((res) => {
+    if(res.code==200){
+      gridOptions.columns.splice(0,gridOptions.columns.length)
+      BasicData.value = res.data
+      //娣诲姞鍒�
+      gridOptions.columns=arr.slice()
+      for (let i=0;i<BasicData.value.length;i++){
+        let aa={field: BasicData.value[i].OperateType, width: '150',title: BasicData.value[i].OperateTypeName, sortable: true,showOverflow:'ellipsis' ,filters:[{ data: '' }],slots: { filter: 'num1_filter' },filterMethod:filterChanged}
+        gridOptions.columns.push(aa)
+      }
+      bomTitle.forEach((item) => {
+        gridOptions.columns.push(item)
+      })
+
+      getWorks()
+      getDetails();
+      editProductBOM();
+    }else{
+      ElMessage.warning(res.msg)
+    }
+  })
+})
 
 
+
+//鍒楁煡璇�
+const getWork = () => {
+  filterData.value.type=value.value
+  request.get(`/BasicWarehouse/BasicWarehouseType/${value.value}`).then((res) => {
+    if(res.code==200){
+      gridOptions.columns=[]
+      BasicData.value = res.data
+      //娣诲姞鍒�
+      gridOptions.columns=arr.slice()
+
+      for (let i=0;i<BasicData.value.length;i++){
+        let column={field: BasicData.value[i].OperateType,
+          width: '150',title: BasicData.value[i].OperateTypeName,
+          sortable: true,showOverflow:'ellipsis' ,
+          filters:[{ data: '' }],
+          slots: { filter: 'num1_filter' },
+          filterMethod:filterChanged}
+
+        gridOptions.columns.push(column)
+
+      }
+      bomTitle.forEach((item) => {
+        gridOptions.columns.push(item)
+      })
+      getWorks()
+
+    }else{
+      ElMessage.warning(res.msg)
+    }
+  })
+}
+
+
+//鏁版嵁缁戝畾
+const getWorks = () => {
+  request.post(`/materialStore/getSelectProductBOM/1/${total.pageSize}`,filterData.value).then((res) => {
+
+    if(res.code==200){
+      materialStore.value=[]
+      for (let i=0;i<res.data.data.length;i++){
+        materialStore.value[i]= JSON.parse(res.data.data[i].json)
+        materialStore.value[i].id= res.data.data[i].id
+        materialStore.value[i].consume= res.data.data[i].consume
+        materialStore.value[i].tabId= res.data.data[i].tabId
+        switch (res.data.data[i].bomType) {
+          case '1':
+            materialStore.value[i].bomType = t('order.area');
+            break;
+          case '2':
+            materialStore.value[i].bomType = t('order.perimeter');
+            break;
+          case "3":
+            materialStore.value[i].bomType = t('order.quantity');
+            break;
+          default:
+            materialStore.value[i].bomType = res.data.data[i].bomType; // 淇濈暀鍘熷��
+        }
+        materialStore.value[i].price= res.data.data[i].price
+      }
+
+      total.dataTotal = res.data.total.total*1
+      total.pageTotal= res.data.total.pageTotal
+      pageNum.value=1
+
+      produceList = deepClone(materialStore.value)
+      xGrid.value.loadData(produceList)
+      gridOptions.loading=false
+    }else{
+      ElMessage.warning(res.msg)
+      router.push("/login")
+    }
+  })
+}
+
+
+const getDetails = () => {
+}
+
+//鎷嗗垎姣忎釜浜у搧
+const productList = computed(() => {
+  const raw = (props.productName ?? '').toString().trim()
+  if (!raw) return [t('bom.other')]
+
+  const parts = raw
+      .split(/[+*]/)
+      .map(s => s.trim())
+      .filter(Boolean)
+
+  parts.push(t('bom.other'))
+
+  return parts
+})
+
+// 褰撳墠閫変腑 tab 鐨勪笅鏍�
+const activeProductIndex = ref(null)
+
+const gridDataMapByProduct = reactive({})
+
+const handleTabClick = (name, index) => {
+  activeProductIndex.value = index
+  if (!gridDataMapByProduct[index]) {
+    gridDataMapByProduct[index] = [] // 鍒濆鍖�
+  }
+  xGridByProduct.value?.loadData(gridDataMapByProduct[index])
+}
+const getTableRow = (row, type) => {
+  if (type !== 'add') return
+  const key = activeProductIndex.value
+  if (key=="" && key==null) {
+    ElMessage.warning(t('bom.msg.msg1'))
+    return
+  }
+  const plainRow = JSON.parse(JSON.stringify(row))
+  const oldData = (gridDataMapByProduct[key] || []).map((item) =>
+      JSON.parse(JSON.stringify(item))
+  )
+  gridDataMapByProduct[key] = [...oldData, plainRow]
+  xGridByProduct.value.loadData(gridDataMapByProduct[key])
+}
+const getTableRowDils = (row,type) =>{
+  switch (type) {
+    case 'delete' :{
+      const key = activeProductIndex.value
+      gridDataMapByProduct[key] = (gridDataMapByProduct[key] || []).filter(
+          item => item.id !== row.id
+      )
+
+      xGridByProduct.value.loadData(gridDataMapByProduct[key])
+      return
+    }
+  }
+}
+
+const saveProductBOM = () => {
+  gridOptionsByProduct.loading=true
+  productList.value.forEach((name, index) => {
+    const rows = gridDataMapByProduct[index] || []
+    const seq = index + 1
+
+   //缁欐瘡鏉℃暟鎹姞涓婂搴斿眰鍙枫�佷骇鍝佺紪鍙�
+    rows.forEach(row => {
+      row.layer = seq
+      row.produceId = props.produceId
+    })
+  })
+  request.post(`/BomData/saveProductBOM`,gridDataMapByProduct).then((res) => {
+    if(res.code==200){
+      ElMessage.success(t('basicData.msg.saveSuccess'))
+      gridOptionsByProduct.loading=false
+      emit('closeDialog')
+    }})
+}
+
+const editProductBOM = () => {
+  request.post(`/BomData/editProductBOM/${props.produceId}`).then((res) => {
+    if (res.code == 200 ) {
+      // 鍒濆鍖栨竻绌�
+      productList.value.forEach((_, index) => {
+        gridDataMapByProduct[index + 1] = []
+      })
+      // 閬嶅巻鍚庣杩斿洖鐨勬暟鎹�
+      res.data.data.forEach(item => {
+        const layer = item.product_layer - 1  // 渚嬪 1, 2, 3
+        if (!gridDataMapByProduct[layer]) {
+          gridDataMapByProduct[layer] = []
+        }
+
+        const plainItem = JSON.parse(JSON.stringify(item))
+        gridDataMapByProduct[layer].push(plainItem)
+      })
+
+      // 榛樿閫夋嫨绗竴灞�
+      activeProductIndex.value = 0
+      xGridByProduct.value.loadData(gridDataMapByProduct[0])
+    }
+  })
+}
 </script>
 
 <template>
   <div id="main_add">
     <div class="product">
-      <el-card style="max-width: 480px">
-        <p  class="text item">6mm瓒呯櫧閽㈠寲</p>
-        <p  class="text item">12AR</p>
-        <p style="color: blue" class="text item">6mm瓒呯櫧閽㈠寲</p>
-        <p  class="text item">0.76PVB</p>
-        <p  class="text item">6mm瓒呯櫧閽㈠寲</p>
-        <p  class="text item">鍏朵粬</p>
+      <el-card class="product-card" shadow="never">
+        <div class="product-tabs">
+          <p
+              v-for="(name, i) in productList"
+              :key="i"
+              class="text item tab"
+              :class="{ active: activeProductIndex === i }"
+              @click="handleTabClick(name, i)"
+          >
+            {{ name }}
+          </p>
+        </div>
       </el-card>
     </div>
     <div class="productLayer">
@@ -121,10 +373,16 @@
           height="100%"
           size="small"
           v-bind="gridOptionsByProduct"
+
       >
         <template #toolbar_buttons>
-          <el-button  >鍒犻櫎</el-button>
-          <el-button type="primary" >淇濆瓨</el-button>
+          <el-button type="primary" @click="saveProductBOM">淇濆瓨</el-button>
+        </template>
+        <template #button_slot="{ row }">
+          <el-button @click="getTableRowDils(row,'delete')"
+
+                     link type="primary" size="small">{{ $t('basicData.delete') }}</el-button>
+
         </template>
       </vxe-grid>
 
@@ -138,10 +396,27 @@
           size="small"
       >
         <template #toolbar_buttons>
-          <vxe-select  v-model="materialType">
-            <vxe-option :value="1" :label="'鍘熺墖'"></vxe-option>
-            <vxe-option :value="2" :label="'杈呮枡'"></vxe-option>
-          </vxe-select>
+          <el-select v-model="value" style="width: 150px;" :placeholder="$t('ingredients.pleaseSelectACategory')" @change="getWork">
+            <el-option
+                v-for="item in options"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+            />
+          </el-select>
+        </template>
+        <template #num1_filter="{ column, $panel }">
+          <div>
+            <div v-for="(option, index) in column.filters" :key="index">
+              <input type="type" v-model="option.data" @keyup.enter.native="$panel.confirmFilter()" @input="changeFilterEvent($event, option, $panel)"/>
+            </div>
+          </div>
+        </template>
+        <template #button_slot="{ row }">
+          <el-button @click="getTableRow(row,'add')"
+
+                     link type="primary" size="small">{{ $t('basicData.add') }}</el-button>
+
         </template>
       </vxe-grid>
     </div>
@@ -157,7 +432,7 @@
   height: 100%;
 }
 .product{
-  width: 35%;
+  width: 30%;
   height:50%;
   float: left;
 }
@@ -171,4 +446,23 @@
   height:50%;
   float: left;
 }
+.product-card { max-width: 960px; padding: 2px 2px 4px; }
+.product-tabs { display: flex; flex-direction: column; gap: 2px 12px; }
+.tab {
+  margin: 0; padding: 6px 10px; line-height: 1.6;
+  border-radius: 6px; cursor: pointer; user-select: none;
+  transition: all .15s ease; border: 1px solid var(--el-border-color);
+}
+.tab:hover { transform: translateY(-1px); }
+.tab.active {
+  background: var(--el-color-primary-light-9);
+  border-color: var(--el-color-primary);
+  color: var(--el-color-primary); font-weight: 600;
+}
+
+.tab.active {
+  font-weight: bold;
+  color: #409EFF; /* Element Plus 涓昏壊 */
+  border-bottom: 2px solid #409EFF;
+}
 </style>
\ No newline at end of file

--
Gitblit v1.8.0