Browse Source

feat: 制造资源的物料选择支持多选模式

xieyong 9 hours ago
parent
commit
199efee2ff

+ 128 - 14
src/components/select/bom/ProductModal.vue

@@ -32,8 +32,9 @@
         <template v-slot:content>
           <ele-pro-table
             ref="table"
-            :columns="columns"
+            :columns="isMultiple ? columnsMultiple : columns"
             :datasource="datasource"
+            :selection.sync="selection"
             row-key="id"
             height="calc(100vh - 350px)"
             class="dict-table"
@@ -41,7 +42,11 @@
           >
             <!-- 表头工具栏 -->
             <template v-slot:action="{ row }">
-              <el-radio class="radio" v-model="radio" :label="row.id"
+              <el-radio
+                v-if="!isMultiple"
+                class="radio"
+                v-model="radio"
+                :label="row.id"
                 ><i></i
               ></el-radio>
             </template>
@@ -66,7 +71,14 @@
       return {
         visible: false,
 
-        // 表格列配置
+        // 多选模式标记
+        isMultiple: false,
+        // 多选模式选中的行
+        selection: [],
+        // 多选模式禁用勾选的物料 id 列表(已在父表里存在)
+        existingIds: [],
+
+        // 表格列配置(单选)
         columns: [
           {
             columnKey: 'index',
@@ -149,6 +161,88 @@
             label: '选择'
           }
         ],
+        // 表格列配置(多选):在单选列配置前插入 checkbox 列
+        columnsMultiple: [
+          {
+            columnKey: 'selection',
+            type: 'selection',
+            width: 45,
+            align: 'center',
+            selectable: (row) => {
+              // 已在父表里存在的物料,禁止勾选
+              return !this.existingIds.includes(row.id);
+            },
+            reserveSelection: true
+          },
+          {
+            columnKey: 'index',
+            type: 'index',
+            width: 45,
+            align: 'center',
+            reserveSelection: true
+          },
+          {
+            prop: 'code',
+            label: '编码'
+          },
+          {
+            prop: 'name',
+            label: '名称',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'brandNum',
+            label: '牌号'
+          },
+          {
+            prop: 'modelType',
+            label: '型号',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'specification',
+            label: '规格',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'measuringUnit',
+            label: '计量单位',
+            showOverflowTooltip: true,
+            minWidth: 90
+          },
+          {
+            prop: 'weightUnit',
+            label: '重量单位',
+            showOverflowTooltip: true,
+            minWidth: 90
+          },
+          {
+            prop: 'roughWeight',
+            label: '毛重',
+            showOverflowTooltip: true,
+            minWidth: 90
+          },
+          {
+            prop: 'netWeight',
+            label: '净重',
+            showOverflowTooltip: true,
+            minWidth: 90
+          },
+          {
+            prop: 'packingUnit',
+            align: 'center',
+            label: '包装单位',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'categoryLevelPath',
+            label: '分类',
+            align: 'center',
+            showOverflowTooltip: true
+          }
+        ],
         title: null,
         categoryLevelId: null,
         radio: null,
@@ -190,21 +284,27 @@
         this.isCategory = false;
         this.$refs.table.reload({ pageNum: 1, where: where });
       },
-      open(item, title, type, idx) {
+      open(item, title, type, idx, isMultiple = false, existingIds = []) {
+        // 每次打开都清空上次的多选残留
+        this.isMultiple = !!isMultiple;
+        this.selection = [];
+        this.existingIds = Array.isArray(existingIds) ? existingIds : [];
+        // title/type/idx 始终赋值,避免 item=null(空表批量选择)时丢失
+        this.title = title;
+        this.type = type;
+        this.idx = idx;
         if (item) {
-          this.title = title;
-          this.type = type;
-          this.idx = idx;
-
           this.current = {
             id: item.categoryId,
             name: item.categoryName,
             code: item.categoryCode
           };
           this.radio = item.categoryId;
-
-          this.getTreeData();
+        } else {
+          this.current = {};
+          this.radio = '';
         }
+        this.getTreeData();
         this.visible = true;
       },
 
@@ -228,7 +328,9 @@
             });
             return this.treeList;
           }
-        } catch (error) {}
+        } catch (error) {
+          console.log(error);
+        }
         this.treeLoading = false;
       },
 
@@ -241,12 +343,24 @@
         this.visible = false;
         this.current = null;
         this.radio = '';
+        this.selection = [];
+        this.isMultiple = false;
+        this.existingIds = [];
       },
       selected() {
-        if (!this.current) {
-          return this.$message.warning('请选择工作中心');
+        if (this.isMultiple) {
+          if (this.selection.length <= 0) {
+            return this.$message.warning('请至少选择一条数据');
+          }
+          // 多选:传数组(事件名复用 changeProduct,由父组件按 Array.isArray 区分)
+          this.$emit('changeProduct', this.title, this.selection, this.idx);
+        } else {
+          if (!this.current) {
+            return this.$message.warning('请选择工作中心');
+          }
+          // 单选:传单条
+          this.$emit('changeProduct', this.title, this.current, this.idx);
         }
-        this.$emit('changeProduct', this.title, this.current, this.idx);
         this.handleClose();
       }
     }

+ 62 - 57
src/views/material/BOMmanage/components/workingProcedure.vue

@@ -766,7 +766,7 @@
                 ><el-input
                   :value="row.categoryName"
                   placeholder="请选择"
-                  @click.native="categorySelect(row, $index)"
+                  @click.native="categorySelect(row, $index, true)"
                 ></el-input
               ></div>
             </template>
@@ -1002,7 +1002,7 @@
   import dictMixins from '@/mixins/dictMixins';
   import * as dayjs from 'dayjs';
   import { getFile } from '@/api/system/file';
-  // import fileUpload from '@/components/addDoc/index.vue';
+  import fileUpload from '@/components/addDoc/index.vue';
   import { workingProcedureUpdate } from '@/api/material/BOM';
   import ParamModal from '@/views/technology/productParam/components/ParamModal.vue';
   import ProductModal from '@/components/select/bom/ProductModal.vue';
@@ -1025,7 +1025,7 @@
     components: {
       userSearch,
       ParamModal,
-      // fileUpload,
+      fileUpload,
       ProductModal,
       jobDialog,
       ProductModalMultiple,
@@ -1617,7 +1617,7 @@
           this.$refs.jobTable.getData()
         );
       },
-      determineChoose(title, row, idx) {
+      determineChoose(title, rows, idx) {
         let type = '';
         if (this.activeName === '材料定额') {
           type = 'materialQuota';
@@ -1626,60 +1626,51 @@
         }
 
         if (title == '选择物料') {
-          console.log(row, 'row');
-          this.$set(
-            this.tableData.taskParam[this.currentIndex][type][idx],
-            'categoryName',
-            row.name
-          );
-          this.$set(
-            this.tableData.taskParam[this.currentIndex][type][idx],
-            'categoryId',
-            row.id
-          );
-          this.$set(
-            this.tableData.taskParam[this.currentIndex][type][idx],
-            'categoryCode',
-            row.code
-          );
-          this.$set(
-            this.tableData.taskParam[this.currentIndex][type][idx],
-            'unit',
-            row.measuringUnit
-          );
-          this.$set(
-            this.tableData.taskParam[this.currentIndex][type][idx],
-            'weightUnit',
-            row.weightUnit
-          );
-          this.$set(
-            this.tableData.taskParam[this.currentIndex][type][idx],
-            'brandNum',
-            row.brandNum
-          );
-          this.$set(
-            this.tableData.taskParam[this.currentIndex][type][idx],
-            'modelType',
-            row.modelType
-          );
-          this.$set(
-            this.tableData.taskParam[this.currentIndex][type][idx],
-            'rootCategoryLevelId',
-            row.categoryLevelPathIdParent
-          );
-          this.$set(
-            this.tableData.taskParam[this.currentIndex][type][idx],
-            'specification',
-            row.specification
-          );
+          // ProductModal 多选时回传数组,单选时回传单条;统一成数组处理
+          const list = Array.isArray(rows) ? rows : [rows];
+          const startIdx = idx; // 多选且空表时为 -1,含义是"没有可写的现有行"
+
+          list.forEach((row, i) => {
+            let targetIdx;
+            if (i === 0 && startIdx >= 0) {
+              // 多选首条(写到最后一行)或单选:写到现有 idx
+              targetIdx = startIdx;
+            } else {
+              // 其余条目:调用 add() 新增空行,再回写
+              this.add();
+              targetIdx =
+                this.tableData.taskParam[this.currentIndex][type].length - 1;
+            }
+            this.writeProductFields(type, targetIdx, row);
+          });
 
-          this.$set(
-            this.tableData.taskParam[this.currentIndex][type][idx],
-            'id',
-            row.id
-          );
+          // 刷新对应 Tab 的表格 UI(仅多选追加了行才需要;单选不会触发分支)
+          if (type === 'resource' && this.$refs.resourceTable) {
+            this.$refs.resourceTable.setData(
+              this.tableData.taskParam[this.currentIndex][type]
+            );
+          } else if (type === 'materialQuota' && this.$refs.materialTable) {
+            this.$refs.materialTable.setData(
+              this.tableData.taskParam[this.currentIndex][type]
+            );
+          }
         }
       },
+      // 把 ProductModal 选中的物料字段写到指定行(资源表 / 材料定额表共用)
+      writeProductFields(type, idx, row) {
+        const target =
+          this.tableData.taskParam[this.currentIndex][type][idx];
+        this.$set(target, 'categoryName', row.name);
+        this.$set(target, 'categoryId', row.id);
+        this.$set(target, 'categoryCode', row.code);
+        this.$set(target, 'unit', row.measuringUnit);
+        this.$set(target, 'weightUnit', row.weightUnit);
+        this.$set(target, 'brandNum', row.brandNum);
+        this.$set(target, 'modelType', row.modelType);
+        this.$set(target, 'rootCategoryLevelId', row.categoryLevelPathIdParent);
+        this.$set(target, 'specification', row.specification);
+        this.$set(target, 'id', row.id);
+      },
       uploadedSuccess(data, row) {
         row.bomArtFiles = data[0].storePath;
         this.filedList = [];
@@ -1692,8 +1683,22 @@
       downloadFile(url) {
         getFile({ objectName: url }, '附件');
       },
-      categorySelect(row, idx) {
-        this.$refs.productRefs.open(row, '选择物料', '1', idx);
+      categorySelect(row, idx, isMultiple = false) {
+        // 多选时把"已在 resource 列表里的物料 id"传给 ProductModal,禁掉这些行的勾选
+        let existingIds = [];
+        if (isMultiple && this.activeName === '制造资源') {
+          const list =
+            this.tableData.taskParam[this.currentIndex]?.['resource'] || [];
+          existingIds = list.map((item) => item.id).filter(Boolean);
+        }
+        this.$refs.productRefs.open(
+          row,
+          '选择物料',
+          '1',
+          idx,
+          isMultiple,
+          existingIds
+        );
       },
       handleDel(row, index) {
         let type = '';