spring
15 小时以前 fa4ea8dd99638f58da60ac283186113619cc7e6e
src/views/productionManagement/productStructure/Detail/index.vue
@@ -1,146 +1,86 @@
<template>
  <div class="app-container">
    <el-button v-if="dataValue.isEdit"
               type="primary"
               @click="addItem"
               style="margin-bottom: 10px">添加
    </el-button>
    <el-button v-if="!dataValue.isEdit"
               type="primary"
               @click="dataValue.isEdit = true"
               style="margin-bottom: 10px">编辑
    </el-button>
    <el-button v-if="dataValue.isEdit"
               type="primary"
               @click="cancelEdit"
               style="margin-bottom: 10px">取消
    </el-button>
    <el-button type="primary"
               :loading="dataValue.loading"
               @click="submit"
               :disabled="!dataValue.isEdit"
               style="margin-bottom: 10px">确认
    </el-button>
    <el-table
        :data="tableData"
        border
        :preserve-expanded-content="false"
        style="width: 100%"
    >
    <PageHeader content="产品结构详情">
      <template #right-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">取消
        </el-button>
        <el-button v-if="!isOrderPage" type="primary" :loading="dataValue.loading" @click="submit"
          :disabled="!dataValue.isEdit">确认
        </el-button>
      </template>
    </PageHeader>
    <el-table :data="tableData" border :preserve-expanded-content="false" :default-expand-all="true"
      style="width: 100%">
      <el-table-column type="expand">
        <template #default="props">
          <el-form ref="form"
                   :model="dataValue">
            <el-table :data="dataValue.dataList"
                      style="width: 100%">
              <el-table-column prop="productName"
                               label="产品"/>
              <el-table-column prop="model"
                               label="规格">
          <el-form ref="form" :model="dataValue">
            <el-table :data="dataValue.dataList" row-key="tempId" default-expand-all
              :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" style="width: 100%">
              <el-table-column prop="productName" label="产品" />
              <el-table-column prop="model" label="规格">
                <template #default="{ row, $index }">
                  <el-form-item v-if="dataValue.isEdit"
                                :prop="`dataList.${$index}.model`"
                                :rules="[{ required: true, message: '请选择规格', trigger: ['blur','change'] }]"
                                style="margin: 0">
                    <el-select v-model="row.model"
                               placeholder="请选择规格"
                               clearable
                               :disabled="!dataValue.isEdit"
                               style="width: 100%"
                               @visible-change="(v) => { if (v) openDialog($index) }">
                      <el-option v-if="row.model"
                                 :label="row.model"
                                 :value="row.model" />
                    :rules="[{ required: true, message: '请选择规格', trigger: ['blur', 'change'] }]" style="margin: 0">
                    <el-select v-model="row.model" placeholder="请选择规格" clearable
                      :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)"
                      style="width: 100%" @visible-change="(v) => { if (v) openDialog(row.tempId) }">
                      <el-option v-if="row.model" :label="row.model" :value="row.model" />
                    </el-select>
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column prop="processId"
                               label="消耗工序">
              <el-table-column prop="processName" label="消耗工序">
                <template #default="{ row, $index }">
                  <el-form-item :prop="`dataList.${$index}.processId`"
                                :rules="[{ required: true, message: '请选择消耗工序', trigger: 'change' }]"
                                style="margin: 0">
                    <el-select v-model="row.processId"
                               placeholder="请选择"
                               filterable
                               clearable
                               style="width: 100%"
                               :disabled="!dataValue.isEdit">
                      <el-option v-for="item in dataValue.processOptions"
                                 :key="item.id"
                                 :label="item.name"
                                 :value="item.id" />
                  <el-form-item v-if="dataValue.isEdit"
                    :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="请选择" filterable clearable style="width: 100%"
                      :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)">
                      <el-option v-for="item in dataValue.processOptions" :key="item.id" :label="item.name"
                        :value="item.id" />
                    </el-select>
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column prop="unitQuantity"
                               label="单位产出所需数量">
              <el-table-column prop="unitQuantity" label="单位产出所需数量">
                <template #default="{ row, $index }">
                  <el-form-item :prop="`dataList.${$index}.unitQuantity`"
                                :rules="[{ required: true, message: '请输入单位产出所需数量', trigger: ['blur','change'] }]"
                                style="margin: 0">
                    <el-input-number v-model="row.unitQuantity"
                                     :min="0"
                                     :precision="2"
                                     :step="1"
                                     controls-position="right"
                                     style="width: 100%"
                                     :disabled="!dataValue.isEdit" />
                  <el-form-item v-if="dataValue.isEdit"
                    :rules="[{ required: true, message: '请输入单位产出所需数量', trigger: ['blur', 'change'] }]"
                    style="margin: 0">
                    <el-input-number v-model="row.unitQuantity" :min="0" :precision="2" :step="1"
                      controls-position="right" style="width: 100%"
                      :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column prop="demandedQuantity"
                               label="需求总量">
              <el-table-column v-if="isOrderPage" prop="demandedQuantity" label="需求总量">
                <template #default="{ row, $index }">
                  <el-form-item :prop="`dataList.${$index}.demandedQuantity`"
                                :rules="[{ required: true, message: '请输入需求总量', trigger: ['blur','change'] }]"
                                style="margin: 0">
                    <el-input-number v-model="row.demandedQuantity"
                                     :min="0"
                                     :precision="2"
                                     :step="1"
                                     controls-position="right"
                                     style="width: 100%"
                                     :disabled="!dataValue.isEdit" />
                  <el-form-item v-if="dataValue.isEdit"
                    :rules="[{ required: true, message: '请输入需求总量', trigger: ['blur', 'change'] }]" style="margin: 0">
                    <el-input-number v-model="row.demandedQuantity" :min="0" :precision="2" :step="1"
                      controls-position="right" style="width: 100%"
                      :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column prop="unit"
                               label="单位">
              <el-table-column prop="unit" label="单位">
                <template #default="{ row, $index }">
                  <el-form-item :prop="`dataList.${$index}.unit`"
                                :rules="[{ required: true, message: '请输入单位', trigger: ['blur','change'] }]"
                                style="margin: 0">
                    <el-input v-model="row.unit"
                              placeholder="请输入单位"
                              clearable
                              :disabled="!dataValue.isEdit" />
                  <el-form-item v-if="dataValue.isEdit"
                    :rules="[{ required: true, message: '请输入单位', trigger: ['blur', 'change'] }]" style="margin: 0">
                    <el-input v-model="row.unit" placeholder="请输入单位" clearable
                      :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column prop="diskQuantity"
                               label="盘数(盘)">
              <el-table-column label="操作" fixed="right" width="200">
                <template #default="{ row, $index }">
                  <el-form-item :prop="`dataList.${$index}.diskQuantity`"
                                :rules="[{ required: true, message: '请输入盘数', trigger: ['blur','change'] }]"
                                style="margin: 0">
                    <el-input-number v-model="row.diskQuantity"
                                     :min="0"
                                     :precision="0"
                                     :step="1"
                                     controls-position="right"
                                     style="width: 100%"
                                     :disabled="!dataValue.isEdit" />
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column label="操作" fixed="right" width="100">
                <template #default="{ row, $index }">
                  <el-button type="danger"
                             text
                             @click="dataValue.dataList.splice($index, 1)">删除
                  <el-button
                    v-if="dataValue.isEdit && !dataValue.dataList.some(item => (item as any).tempId === row.tempId)"
                    type="danger" text @click="removeItem(row.tempId)">删除
                  </el-button>
                  <el-button v-if="dataValue.isEdit" type="primary" text @click="addItem2(row.tempId)">添加
                  </el-button>
                </template>
              </el-table-column>
@@ -148,15 +88,12 @@
          </el-form>
        </template>
      </el-table-column>
      <el-table-column label="产品编码" prop="productCode" />
      <el-table-column label="BOM编号" prop="bomNo" />
      <el-table-column label="产品名称" prop="productName" />
      <el-table-column label="规格型号" prop="model" />
      <el-table-column label="单位" prop="unit" />
    </el-table>
    <product-select-dialog v-if="dataValue.showProductDialog"
                           v-model:model-value="dataValue.showProductDialog"
                           @confirm="handleProduct" />
    <product-select-dialog v-if="dataValue.showProductDialog" v-model:model-value="dataValue.showProductDialog"
      @confirm="handleProduct" />
  </div>
</template>
@@ -170,31 +107,44 @@
  ref,
} from "vue";
import { queryList, add } from "@/api/productionManagement/productStructure.js";
import { listProcessBom } from "@/api/productionManagement/productionOrder.js";
import { list } from "@/api/productionManagement/productionProcess";
import { ElMessage } from "element-plus";
import {useRoute, useRouter} from "vue-router";
import { useRoute, useRouter } from "vue-router";
defineComponent({
  name: "StructureEdit",
});
const ProductSelectDialog = defineAsyncComponent(
    () => import("@/views/basicData/product/ProductSelectDialog.vue")
  () => import("@/views/basicData/product/ProductSelectDialog.vue")
);
const emit = defineEmits(["update:router"]);
const form = ref();
const route = useRoute()
const router = useRouter()
const route = useRoute();
const router = useRouter();
const routeId = computed({
  get() {
    return route.query.id;
  },
  set(val) {
    emit('update:router', val)
  }
    emit("update:router", val);
  },
});
// 从路由参数获取产品信息
const routeBomNo = computed(() => route.query.bomNo || "");
const routeProductName = computed(() => route.query.productName || "");
const routeProductModelName = computed(
  () => route.query.productModelName || ""
);
const routeOrderId = computed(() => route.query.orderId);
const pageType = computed(() => route.query.type);
const isOrderPage = computed(
  () => pageType.value === "order" && routeOrderId.value
);
const dataValue = reactive({
  dataList: [],
@@ -202,6 +152,7 @@
  processOptions: [],
  showProductDialog: false,
  currentRowIndex: null,
  currentRowName: null,
  loading: false,
  isEdit: false,
});
@@ -210,84 +161,289 @@
  {
    productName: "",
    model: "",
    unit: "",
    productCode: "",
  }
])
    bomNo: "",
  },
]);
const openDialog = index => {
  dataValue.currentRowIndex = index;
const openDialog = (tempId: any) => {
  console.log(tempId, "tempId");
  dataValue.currentRowName = tempId;
  dataValue.showProductDialog = true;
};
const fetchData = async () => {
  const { data } = await queryList(routeId.value);
  tableData[0].productName = data.productName;
  tableData[0].model = data.model;
  tableData[0].unit = data.unit;
  tableData[0].productCode = data.productCode;
  dataValue.dataList = data.productStructureList;
  if (isOrderPage.value) {
    // 订单情况:使用订单的产品结构接口
    const { data } = await listProcessBom({ orderId: routeOrderId.value });
    dataValue.dataList = (data as any) || [];
  } else {
    // 非订单情况:使用原来的接口
    const { data } = await queryList(routeId.value);
    dataValue.dataList = (data as any) || [];
    // 为所有项及其子项设置name属性
    const setNameRecursively = (items: any[]) => {
      items.forEach((item: any) => {
        item.tempId = item.id;
        item.processName =
          dataValue.processOptions.find(option => option.id === item.processId)
            ?.name || "";
        if (item.children && item.children.length > 0) {
          setNameRecursively(item.children);
        }
      });
    };
    setNameRecursively(dataValue.dataList);
    console.log(dataValue.dataList, "dataValue.dataList");
  }
};
const fetchProcessOptions = async () => {
  const { data } = await list(routeId.value);
  dataValue.processOptions = data;
  const { data } = await list();
  dataValue.processOptions = data as any;
};
const handleProduct = row => {
const handleProduct = (row: any) => {
  if (row?.length > 1) {
    ElMessage.error("只能选择一个产品");
  }
  dataValue.dataList[dataValue.currentRowIndex].productName =
      row[0].productName;
  dataValue.dataList[dataValue.currentRowIndex].model = row[0].model;
  dataValue.dataList[dataValue.currentRowIndex].productModelId = row[0].id;
  const productData = row[0];
  //  最外层组件中,与当前产品相同的产品只能有一个
  const isTopLevel = dataValue.dataList.some(item => (item as any).tempId === dataValue.currentRowName);
  if (isTopLevel) {
    if (productData.productName === tableData[0].productName &&
      productData.model === tableData[0].model) {
      //  查找是否已经有其他顶层行已经是这个产品
      const hasOther = dataValue.dataList.some(item =>
        (item as any).tempId !== dataValue.currentRowName &&
        (item as any).productName === tableData[0].productName &&
        (item as any).model === tableData[0].model
      );
      if (hasOther) {
        ElMessage.warning("最外层和当前产品一样的一级只能有一个");
        return;
      }
    }
  }
  // dataValue.dataList[dataValue.currentRowIndex].productName =
  //   row[0].productName;
  // dataValue.dataList[dataValue.currentRowIndex].model = row[0].model;
  // dataValue.dataList[dataValue.currentRowIndex].productModelId = row[0].id;
  // dataValue.dataList[dataValue.currentRowIndex].unit = row[0].unit || "";
  dataValue.dataList.map(item => {
    if (item.tempId === dataValue.currentRowName) {
      item.productName = productData.productName;
      item.model = productData.model;
      item.productModelId = productData.id;
      item.unit = productData.unit || "";
      return;
    }
    childItem(item, dataValue.currentRowName, productData);
  });
  dataValue.showProductDialog = false;
};
const childItem = (item: any, tempId: any, productData: any) => {
  if (item.tempId === tempId) {
    item.productName = productData.productName;
    item.model = productData.model;
    item.productModelId = productData.id;
    item.unit = productData.unit || "";
    return true;
  }
  if (item.children && item.children.length > 0) {
    for (let child of item.children) {
      if (childItem(child, tempId, productData)) {
        return true;
      }
    }
  }
  return false;
};
// 递归校验所有层级的表单数据
const validateAll = () => {
  let isValid = true;
  // 校验函数
  const validateItem = (item: any, isTopLevel = false) => {
    // 校验当前项的必填字段
    if (!item.model) {
      ElMessage.error("请选择规格");
      isValid = false;
      return;
    }
    if (!isTopLevel && !item.processId) {
      ElMessage.error("请选择消耗工序");
      isValid = false;
      return;
    }
    if (!item.unitQuantity) {
      ElMessage.error("请输入单位产出所需数量");
      isValid = false;
      return;
    }
    if (isOrderPage.value && !item.demandedQuantity) {
      ElMessage.error("请输入需求总量");
      isValid = false;
      return;
    }
    if (!item.unit) {
      ElMessage.error("请输入单位");
      isValid = false;
      return;
    }
    // 递归校验子项
    if (item.children && item.children.length > 0) {
      item.children.forEach(child => {
        validateItem(child, false);
      });
    }
  };
  // 遍历所有顶层项
  dataValue.dataList.forEach(item => {
    validateItem(item, true);
  });
  return isValid;
};
const submit = () => {
  form.value
      .validate(valid => {
        dataValue.loading = true;
        if (valid) {
          add({
            parentId: routeId.value,
            productStructureList: dataValue.dataList || [],
          }).then(res => {
            router.push({
              path: '/productionManagement/productionManagement/productStructure/index',
            })
            ElMessage.success("保存成功");
            dataValue.loading = false;
          });
        }
  dataValue.loading = true;
  // 先进行表单校验
  const valid = validateAll();
  console.log(dataValue.dataList, "dataValue.dataList");
  if (valid) {
    add({
      bomId: routeId.value,
      children: dataValue.dataList || [],
    })
      .then(res => {
        router.push({
          path: "/productionManagement/productionManagement/productStructure/index",
        });
        ElMessage.success("保存成功");
        dataValue.loading = false;
      })
      .finally(() => {
      .catch(() => {
        dataValue.loading = false;
      });
  } else {
    dataValue.loading = false;
  }
};
const addItem = () => {
  dataValue.dataList.push({
    productName: "",
    productId: "",
    model: undefined,
    productModelId: undefined,
    processId: "",
    unitQuantity: 0,
    demandedQuantity: 0,
    unit: "",
    diskQuantity: 0,
const removeItem = (tempId: string) => {
  // 先尝试从顶层删除
  const topIndex = dataValue.dataList.findIndex(item => item.tempId === tempId);
  if (topIndex !== -1) {
    dataValue.dataList.splice(topIndex, 1);
    return;
  }
  // 递归删除子项
  const delchildItem = (items: any[], tempId: any) => {
    for (let i = 0; i < items.length; i++) {
      const item = items[i];
      if (item.tempId === tempId) {
        items.splice(i, 1);
        return true;
      }
      if (item.children && item.children.length > 0) {
        if (delchildItem(item.children, tempId)) {
          return true;
        }
      }
    }
    return false;
  };
  dataValue.dataList.forEach(item => {
    if (item.children && item.children.length > 0) {
      delchildItem(item.children, tempId);
    }
  });
};
const addItem2 = tempId => {
  dataValue.dataList.map(item => {
    if (item.tempId === tempId) {
      if (!item.children) {
        item.children = [];
      }
      item.children.push({
        parentId: item.id || "",
        parentTempId: item.tempId || "",
        productName: "",
        productId: "",
        model: undefined,
        productModelId: undefined,
        processId: "",
        processName: "",
        unitQuantity: 0,
        demandedQuantity: 0,
        unit: "",
        children: [],
        tempId: new Date().getTime(),
      });
      return;
    }
    addchildItem(item, tempId);
  });
};
const addchildItem = (item: any, tempId: any) => {
  if (item.tempId === tempId) {
    console.log(item, "item");
    if (!item.children) {
      item.children = [];
    }
    item.children.push({
      parentId: item.id || "",
      parentTempId: item.tempId || "",
      productName: "",
      productId: "",
      model: undefined,
      productModelId: undefined,
      processId: "",
      unitQuantity: 0,
      demandedQuantity: 0,
      children: [],
      unit: "",
      tempId: new Date().getTime(),
    });
    return true;
  }
  if (item.children && item.children.length > 0) {
    for (let child of item.children) {
      if (addchildItem(child, tempId)) {
        return true;
      }
    }
  }
  return false;
};
const cancelEdit = () => {
  dataValue.isEdit = false;
  dataValue.dataList = dataValue.dataList.filter(item => item.id !== undefined);
  // dataValue.dataList = dataValue.dataList.filter(item => item.id !== undefined);
  fetchData();
};
onMounted(() => {
  fetchData();
  fetchProcessOptions();
onMounted(async () => {
  // 从路由参数回显数据
  tableData[0].productName = routeProductName.value as string;
  tableData[0].model = routeProductModelName.value as string;
  tableData[0].bomNo = routeBomNo.value as string;
  // 订单情况下禁用编辑
  if (isOrderPage.value) {
    dataValue.isEdit = false;
  }
  // 先加载工序选项,再加载数据,确保el-select能够正确回显
  await fetchProcessOptions();
  await fetchData();
});
</script>