gaoluyang
昨天 79fb20a2102f2d6a050b83f20477aa13b221f096
src/views/productionManagement/productStructure/Detail/index.vue
@@ -2,16 +2,21 @@
  <div class="app-container">
    <PageHeader content="产品结构详情">
      <template #right-button>
        <el-button v-if="dataValue.isEdit && !isOrderPage"
                   @click="cancelEdit">取消
        </el-button>
        <el-button v-if="!dataValue.isEdit && !isOrderPage"
                   type="primary"
                   @click="dataValue.isEdit = true">编辑
        </el-button>
        <el-button v-if="dataValue.isEdit && !isOrderPage"
                   type="primary"
                   @click="cancelEdit">取消
                   @click="dataValue.isEdit = true">点击进行修改
        </el-button>
        <el-button v-if="!isOrderPage"
                   type="primary"
                   @click="openBomAddDialog"
                   :disabled="!dataValue.isEdit">
          <el-icon><Document /></el-icon> 按BOM添加
        </el-button>
        <el-button v-if="!isOrderPage"
                   type="success"
                   :loading="dataValue.loading"
                   @click="submit"
                   :disabled="!dataValue.isEdit">确认
@@ -38,8 +43,8 @@
                                              label="图纸编号">
                        <template #default="{ row, $index }">
                           <el-form-item v-if="dataValue.isEdit"
                                                :rules="[{ required: true, message: '请选择规格', trigger: ['blur','change'] }]"
                                                style="margin: 0">
                                             :rules="[{ required: true, message: '请选择规格', trigger: ['blur','change'] }]"
                                             style="margin: 0">
                              <el-select v-model="row.model"
                                              placeholder="请选择规格"
                                              clearable
@@ -53,13 +58,11 @@
                           </el-form-item>
                        </template>
                     </el-table-column>
              <el-table-column prop="drawingNumber"
                               label="规格型号" />
              <el-table-column prop="processName"
                               label="消耗工序">
                               label="工序">
                <template #default="{ row, $index }">
                  <el-form-item v-if="dataValue.isEdit"
                                :rules="dataValue.dataList.some(item => (item as any).tempId === row.tempId) ? [] : [{ required: true, message: '请选择消耗工序', trigger: 'change' }]"
                                :rules="dataValue.dataList.some(item => (item as any).tempId === row.tempId) ? [] : [{ required: true, message: '请选工序', trigger: 'change' }]"
                                style="margin: 0">
                    <el-select v-model="row.processId"
                               placeholder="请选择"
@@ -76,10 +79,10 @@
                </template>
              </el-table-column>
              <el-table-column prop="unitQuantity"
                               label="单位产出所需数量">
                               label="单位用量">
                <template #default="{ row, $index }">
                  <el-form-item v-if="dataValue.isEdit"
                                :rules="[{ required: true, message: '请输入单位产出所需数量', trigger: ['blur','change'] }]"
                                :rules="[{ required: true, message: '请输入单位用量', trigger: ['blur','change'] }]"
                                style="margin: 0">
                    <el-input-number v-model="row.unitQuantity"
                                     :min="0"
@@ -146,13 +149,56 @@
      <el-table-column label="产品名称"
                       prop="productName" />
         <el-table-column label="图纸编号"
                                  prop="model" />
      <el-table-column label="规格型号"
                       prop="drawingNumber" />
                                           prop="model" />
    </el-table>
    <product-select-dialog v-if="dataValue.showProductDialog"
                           v-model:model-value="dataValue.showProductDialog"
                           @confirm="handleProduct" />
    <!-- 按BOM添加弹窗 -->
    <el-dialog v-model="bomAddDialogVisible" title="按BOM添加" width="500px" @close="closeBomAddDialog">
      <el-form ref="bomAddFormRef" :model="bomAddForm" :rules="bomAddRules" label-width="100px">
        <el-form-item label="父项产品" prop="parentProductId">
          <el-select v-model="bomAddForm.parentProductId" placeholder="请选择" clearable filterable
            style="width: 100%" @change="handleBomParentProductChange">
            <el-option v-for="item in parentProductOptions" :key="item.id"
              :label="`${item.model || item.productCode || item.id} | ${item.productName}`"
              :value="item.id" />
          </el-select>
        </el-form-item>
        <!-- 选中产品后展示产品信息 -->
        <div v-if="selectedBomProduct" class="selected-product-info">
          <div class="product-info-row">
            <span class="info-label">产品编号</span>
            <span class="info-value">{{ selectedBomProduct.model || selectedBomProduct.productCode || '-' }}</span>
          </div>
          <div class="product-info-row">
            <span class="info-label">产品名称</span>
            <span class="info-value">{{ selectedBomProduct.productName || '-' }}</span>
          </div>
          <div class="product-info-row">
            <span class="info-label">产品规格</span>
            <span class="info-value">{{ selectedBomProduct.spec || selectedBomProduct.drawingNumber || '-' }}</span>
          </div>
          <div class="stock-info-box">
            <div class="stock-number">{{ selectedBomProduct.stockQuantity || 0 }}</div>
            <div class="stock-label">库存数量(台)</div>
          </div>
        </div>
        <el-form-item label="用量系数" prop="coefficient" style="margin-top: 20px;">
          <el-input-number v-model="bomAddForm.coefficient" :min="0.01" :precision="2" :step="1"
            controls-position="right" style="width: 100%" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button type="primary" @click="submitBomAdd">确定</el-button>
        <el-button @click="closeBomAddDialog">取消</el-button>
      </template>
    </el-dialog>
    <!-- 高级选择产品弹窗 -->
    <product-select-dialog v-if="showAdvancedProductDialog" v-model:model-value="showAdvancedProductDialog"
      @confirm="handleAdvancedProductSelect" />
  </div>
</template>
@@ -165,11 +211,14 @@
    reactive,
    ref,
  } from "vue";
  import { queryList, add } from "@/api/productionManagement/productStructure.js";
  import { queryList, add, listByBomIdIsParent } from "@/api/productionManagement/productStructure.js";
  import { listProcessBom } from "@/api/productionManagement/productionOrder.js";
  import { list } from "@/api/productionManagement/productionProcess";
  import { productListPage } from "@/api/basicData/product.js";
  import { listPage as listBomPage } from "@/api/productionManagement/productBom.js";
  import { ElMessage } from "element-plus";
  import { useRoute, useRouter } from "vue-router";
  import { Search, Document } from '@element-plus/icons-vue'
  defineComponent({
    name: "StructureEdit",
@@ -216,6 +265,21 @@
    loading: false,
    isEdit: false,
  });
  // 按BOM添加相关
  const bomAddDialogVisible = ref(false);
  const bomAddFormRef = ref();
  const parentProductOptions = ref([]);
  const selectedBomProduct = ref(null);
  const selectedBomTreeData = ref([]); // 保存选中产品时获取的BOM树数据
  const bomAddForm = reactive({
    parentProductId: undefined,
    coefficient:1
  });
  const bomAddRules = {
    parentProductId: [{ required: true, message: "请选择父项产品", trigger: "change" }],
    coefficient: [{ required: true, message: "请输入用量系数", trigger: "blur" }]
  };
  const tableData = reactive([
    {
@@ -340,13 +404,8 @@
        isValid = false;
        return;
      }
      if (!isTopLevel && !item.processId) {
        ElMessage.error("请选择消耗工序");
        isValid = false;
        return;
      }
      if (!item.unitQuantity) {
        ElMessage.error("请输入单位产出所需数量");
        ElMessage.error("请输入单位用量");
        isValid = false;
        return;
      }
@@ -511,6 +570,151 @@
    fetchData();
  };
  // 获取父项产品列表
  const getParentProductList = async () => {
    try {
      const { data } = await listBomPage({ current: 1, size: 1000 });
      parentProductOptions.value = data?.records || [];
    } catch (error) {
      console.error("获取父项产品列表失败:", error);
      parentProductOptions.value = [];
    }
  };
  // 打开按BOM添加弹窗
  const openBomAddDialog = () => {
    bomAddDialogVisible.value = true;
    bomAddForm.parentProductId = undefined;
    bomAddForm.coefficient = 1;
    selectedBomProduct.value = null;
    getParentProductList();
  };
  // 关闭按BOM添加弹窗
  const closeBomAddDialog = () => {
    bomAddDialogVisible.value = false;
    bomAddFormRef.value?.resetFields();
    selectedBomProduct.value = null;
  };
  // 打开高级选择
  const openAdvancedSelect = () => {
    showAdvancedProductDialog.value = true;
  };
  // 父项产品变更
  const handleBomParentProductChange = async (val) => {
    if (val) {
      const product = parentProductOptions.value.find(item => item.id === val);
      selectedBomProduct.value = product || null;
      // 直接用选中的 BOM ID 调用 listByBomIdIsParent
      const { data: treeData } = await listByBomIdIsParent(val);
      selectedBomTreeData.value = treeData || [];
      // 将二级树中的产品添加到父项产品选项(从第二级开始)
      const addTreeToOptions = (items: any[]) => {
        items.forEach((item: any) => {
          // 跳过第一级,只添加第二级及以下的产品
          if (item.children && item.children.length > 0) {
            item.children.forEach((child: any) => {
              const exists = parentProductOptions.value.some(opt => opt.id === child.id);
              if (!exists) {
                parentProductOptions.value.push({
                  id: child.id,
                  productName: child.productName,
                  model: child.model,
                  productCode: child.model,
                  spec: child.drawingNumber,
                  drawingNumber: child.drawingNumber,
                  stockQuantity: child.stockQuantity || 0
                });
              }
              // 递归添加子项
              if (child.children && child.children.length > 0) {
                addTreeToOptions([child]);
              }
            });
          }
        });
      };
      if (selectedBomTreeData.value.length > 0) {
        addTreeToOptions(selectedBomTreeData.value);
      }
    } else {
      selectedBomProduct.value = null;
      selectedBomTreeData.value = [];
    }
  };
  // 提交按BOM添加
  const submitBomAdd = () => {
    bomAddFormRef.value.validate(async (valid) => {
      if (!valid) return;
      const product = parentProductOptions.value.find(item => item.id === bomAddForm.parentProductId);
      if (!product) {
        ElMessage.error("未找到选中的产品");
        return;
      }
      try {
        // 使用选择产品时保存的BOM树数据
        const bomItems = selectedBomTreeData.value || [];
        if (bomItems.length === 0) {
          ElMessage.warning("该产品没有BOM信息");
          return;
        }
        // 列表的第一级已经存在,把BOM数据作为第一级的子项(第二级)添加
        if (dataValue.dataList.length > 0) {
          const firstLevelItem = dataValue.dataList[0];
          // 把BOM数据添加到第一级的children中
          const addBomItemsRecursively = (items: any[], parentItem: any) => {
            items.forEach((item: any) => {
              const newItem: any = {
                parentId: item.parentId || "",
                parentTempId: parentItem.tempId || "",
                productName: item.productName || "",
                productId: item.productId || item.productModelId || "",
                model: item.model || "",
                productModelId: item.productModelId || "",
                drawingNumber: item.drawingNumber || "",
                processId: item.processId || "",
                processName: item.processName || "",
                unitQuantity: (item.unitQuantity || 0) * bomAddForm.coefficient,
                demandedQuantity: (item.demandedQuantity || 0) * bomAddForm.coefficient,
                unit: item.unit || "",
                children: [],
                tempId: new Date().getTime() + Math.random(),
              };
              // 添加到父项的children
              if (!parentItem.children) {
                parentItem.children = [];
              }
              parentItem.children.push(newItem);
              // 递归处理子项
              if (item.children && item.children.length > 0) {
                addBomItemsRecursively(item.children, newItem);
              }
            });
          };
          addBomItemsRecursively(bomItems, firstLevelItem);
        }
        ElMessage.success("添加成功");
        closeBomAddDialog();
      } catch (error) {
        console.error("按BOM添加失败:", error);
        ElMessage.error("添加失败");
      }
    });
  };
  onMounted(async () => {
    // 从路由参数回显数据
    tableData[0].productName = routeProductName.value as string;
@@ -527,4 +731,56 @@
    await fetchProcessOptions();
    await fetchData();
  });
</script>
</script>
<style scoped>
.selected-product-info {
  background-color: #f5f7fa;
  border-radius: 4px;
  padding: 16px;
  margin: 10px 0;
  position: relative;
}
.product-info-row {
  display: flex;
  margin-bottom: 8px;
  font-size: 14px;
}
.product-info-row:last-child {
  margin-bottom: 0;
}
.info-label {
  color: #909399;
  width: 70px;
  flex-shrink: 0;
}
.info-value {
  color: #303133;
  flex: 1;
}
.stock-info-box {
  position: absolute;
  right: 16px;
  top: 50%;
  transform: translateY(-50%);
  text-align: center;
}
.stock-number {
  font-size: 28px;
  font-weight: bold;
  color: #303133;
  line-height: 1;
  margin-bottom: 4px;
}
.stock-label {
  font-size: 12px;
  color: #909399;
}
</style>