gaoluyang
2026-04-27 b52c08544fd4ab1e8ff0f09efaac160608fd90ce
天津宝东
1.修改生产流程
已修改6个文件
458 ■■■■ 文件已修改
src/views/procurementManagement/invoiceEntry/components/Modal.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/New.vue 392 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/index.vue 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/invoiceRegistration/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/invoiceEntry/components/Modal.vue
@@ -233,8 +233,7 @@
const rules = ref({
    invoiceNumber: [
        { required: true, message: "请输入发票号", trigger: "blur" },
        { type: "string" },
        { required: false, message: "请输入发票号", trigger: "blur" },
    ],
    invoiceAmount: [
        { required: true, message: "请输入发票金额", trigger: "blur" },
src/views/productionManagement/productionOrder/New.vue
@@ -3,10 +3,15 @@
    <el-dialog
        v-model="isShow"
        title="新增生产订单"
        width="800"
      width="1280px"
        @close="closeModal"
    >
      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
      <el-form
        ref="formRef"
        :model="formState"
        label-width="140px"
        label-position="top"
      >
        <el-form-item
            label="产品名称"
            prop="productModelId"
@@ -15,17 +20,24 @@
                required: true,
                message: '请选择产品',
                trigger: 'change',
              }
            },
            ]"
        >
          <el-button type="primary" @click="showProductSelectDialog = true">
            {{ formState.productName ? formState.productName : '选择产品' }}
            {{ formState.productName ? formState.productName : "选择产品" }}
          </el-button>
        </el-form-item>
        <el-form-item
            label="规格"
            prop="productModelName"
          :rules="[
            {
              required: true,
              message: '请选择规格',
              trigger: 'change',
            },
          ]"
        >
          <el-input v-model="formState.productModelName"  disabled />
        </el-form-item>
@@ -33,31 +45,120 @@
        <el-form-item
            label="单位"
            prop="unit"
          :rules="[
            {
              required: true,
              message: '请选择单位',
              trigger: 'change',
            },
          ]"
        >
          <el-input v-model="formState.unit"  disabled />
        </el-form-item>
        <el-form-item label="工艺路线">
          <el-select v-model="formState.routeId"
        <el-form-item
          label="工艺路线"
          prop="routeId"
          :rules="[
            {
              required: true,
              message: '请选择工艺路线',
              trigger: 'change',
            },
          ]"
        >
          <el-select
            v-model="formState.routeId"
                     placeholder="请选择工艺路线"
                     style="width: 100%;"
                     :loading="bindRouteLoading">
            <el-option v-for="item in routeOptions"
            :loading="bindRouteLoading"
          >
            <el-option
              v-for="item in routeOptions"
                       :key="item.id"
                       :label="`${item.processRouteCode || ''}`"
                       :value="item.id" />
              :value="item.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="用料信息">
          <el-table :data="[materialForm]" border size="small" class="material-table">
            <el-table-column label="原纸规格" min-width="180" align="center">
              <template #default>
                <el-tree-select
                  v-model="materialForm.productCategoryId"
                  placeholder="请选择原纸规格"
                  filterable
                  clearable
                  check-strictly
                  :render-after-expand="false"
                  style="width: 100%;"
                  :loading="bindProductLoading"
                  :data="bindProductOptions"
                  @change="handleMaterialCategoryChange"
                />
              </template>
            </el-table-column>
            <el-table-column label="原料型号" min-width="180" align="center">
              <template #default>
                <el-select
                  v-model="materialForm.materialModel"
                  placeholder="请选择原料型号"
                  filterable
                  clearable
                  style="width: 100%;"
                  :loading="bindMaterialModelLoading"
                >
                  <el-option
                    v-for="item in bindMaterialModelOptions"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  />
                </el-select>
              </template>
            </el-table-column>
            <el-table-column label="原纸需要量" min-width="120" align="center">
              <template #default>
                <el-input-number
                  v-model="materialForm.basePaperQty"
                  :min="0"
                  :precision="2"
                  controls-position="right"
                  style="width: 100%"
                />
              </template>
            </el-table-column>
            <el-table-column label="纸箱需要量" min-width="120" align="center">
              <template #default>
                <el-input-number
                  v-model="materialForm.cartonQty"
                  :min="0"
                  :precision="2"
                  controls-position="right"
                  style="width: 100%"
                />
              </template>
            </el-table-column>
          </el-table>
        </el-form-item>
        <el-form-item
            label="需求数量"
            prop="quantity"
          :rules="[
            {
              required: true,
              message: '请填写需求数量',
              trigger: 'change',
            },
          ]"
        >
          <el-input-number v-model="formState.quantity" :step="1" :min="1" style="width: 100%" />
        </el-form-item>
      </el-form>
      <!-- 产品选择弹窗 -->
      <ProductSelectDialog
          v-model="showProductSelectDialog"
          @confirm="handleProductSelect"
@@ -74,26 +175,21 @@
</template>
<script setup>
import {ref, computed, getCurrentInstance} from "vue";
import { computed, getCurrentInstance, reactive, ref, watch } from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import {addProductOrder, listProcessRoute} from "@/api/productionManagement/productionOrder.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
  type: {
    type: String,
    required: true,
    default: 'qualified',
  },
});
const emit = defineEmits(['update:visible', 'completed']);
const emit = defineEmits(["update:visible", "completed"]);
// 响应式数据(替代选项式的 data)
const formRef = ref();
const formState = ref({
  productId: undefined,
  productModelId: undefined,
@@ -104,85 +200,255 @@
  quantity: 0,
});
const materialForm = reactive({
  productCategoryId: null,
  productCategory: "",
  materialModel: "",
  basePaperQty: null,
  cartonQty: null,
});
const bindProductOptions = ref([]);
const bindProductLoading = ref(false);
const bindMaterialModelOptions = ref([]);
const bindMaterialModelLoading = ref(false);
const routeOptions = ref([]);
const bindRouteLoading = ref(false);
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
    emit("update:visible", val);
  },
});
const showProductSelectDialog = ref(false);
const { proxy } = getCurrentInstance();
let { proxy } = getCurrentInstance()
const resetMaterialForm = () => {
  materialForm.productCategoryId = null;
  materialForm.productCategory = "";
  materialForm.materialModel = "";
  materialForm.basePaperQty = null;
  materialForm.cartonQty = null;
  bindMaterialModelOptions.value = [];
};
const closeModal = () => {
  // 重置表单数据
  formState.value = {
    productId: undefined,
    productModelId: undefined,
    routeId: undefined,
    productName: "",
    productModelName: "",
    quantity: '',
    unit: "",
    quantity: 0,
  };
  routeOptions.value = [];
  resetMaterialForm();
  isShow.value = false;
};
// 产品选择处理
const handleProductSelect = async (products) => {
  if (products && products.length > 0) {
  if (!products || products.length === 0) return;
    const product = products[0];
    formState.value.productId = product.productId;
  const selectedProductModelId = product.productModelId ?? product.id;
  const selectedProductId = product.productId ?? product.parentId ?? product.topProductParentId;
  formState.value.productId = selectedProductId;
    formState.value.productName = product.productName;
    formState.value.productModelName = product.model;
    formState.value.productModelId = product.id;
  formState.value.productModelId = selectedProductModelId;
    formState.value.unit = product.unit;
    showProductSelectDialog.value = false;
    fetchRouteOptions( product.id);
    // 触发表单验证更新
    proxy.$refs["formRef"]?.validateField('productModelId');
  }
  fetchRouteOptions(selectedProductModelId);
  formRef.value?.validateField("productModelId");
};
const routeOptions = ref([]);
const bindRouteLoading = ref(false);
const fetchRouteOptions = (productModelId) => {
  if (!productModelId) {
    routeOptions.value = [];
  formState.value.routeId = undefined;
  routeOptions.value = []
    return;
  }
  formState.value.routeId = undefined;
  routeOptions.value = [];
  bindRouteLoading.value = true;
  listProcessRoute({ productModelId: productModelId }).then(res => {
  listProcessRoute({ productModelId }).then((res) => {
    routeOptions.value = res.data || [];
  }).finally(() => {
    bindRouteLoading.value = false;
  })
}
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      // 验证是否选择了产品和规格
      if (!formState.value.productModelId) {
        proxy.$modal.msgError("请选择产品");
        return;
      }
      if (!formState.value.productModelId) {
        proxy.$modal.msgError("请选择规格");
        return;
      }
      addProductOrder(formState.value).then(res => {
        // 关闭模态框
        isShow.value = false;
        // 告知父组件已完成
        emit('completed');
        proxy.$modal.msgSuccess("提交成功");
      })
    }
  })
  });
};
function convertIdToValue(data) {
  return (data || []).map((item) => {
    const { id, children, ...rest } = item;
    const node = {
      ...rest,
      value: id,
    };
    if (children && children.length > 0) {
      node.children = convertIdToValue(children);
    }
    return node;
  });
}
const findNodeById = (nodes, productId) => {
  const tree = nodes || [];
  for (let i = 0; i < tree.length; i++) {
    if (String(tree[i].value) === String(productId)) {
      return tree[i].label;
    }
    if (tree[i].children && tree[i].children.length > 0) {
      const found = findNodeById(tree[i].children, productId);
      if (found) return found;
    }
  }
  return "";
};
const loadBindProductOptions = async () => {
  bindProductLoading.value = true;
  try {
    const res = await productTreeList();
    bindProductOptions.value = convertIdToValue(res || []);
  } catch (e) {
    console.error("获取产品选项失败:", e);
  } finally {
    bindProductLoading.value = false;
  }
};
const loadBindMaterialModelOptions = async (productCategoryId) => {
  if (!productCategoryId) {
    bindMaterialModelOptions.value = [];
    materialForm.materialModel = "";
    return;
  }
  bindMaterialModelLoading.value = true;
  try {
    const res = await modelList({ id: productCategoryId });
    const list = Array.isArray(res) ? res : (Array.isArray(res?.data) ? res.data : []);
    const mapped = list
      .map((item) => ({
        label: item.model || "",
        value: item.model || "",
      }))
      .filter((item) => item.value);
    const unique = [];
    const seen = new Set();
    mapped.forEach((item) => {
      if (!seen.has(item.value)) {
        seen.add(item.value);
        unique.push(item);
      }
    });
    bindMaterialModelOptions.value = unique;
    if (!unique.some((item) => item.value === materialForm.materialModel)) {
      materialForm.materialModel = "";
    }
  } catch (e) {
    console.error("获取原料型号失败:", e);
    bindMaterialModelOptions.value = [];
    materialForm.materialModel = "";
  } finally {
    bindMaterialModelLoading.value = false;
  }
};
const handleMaterialCategoryChange = async (value) => {
  materialForm.productCategoryId = value;
  materialForm.productCategory = findNodeById(bindProductOptions.value, value);
  materialForm.materialModel = "";
  await loadBindMaterialModelOptions(value);
};
const isEmptyField = (value) => value === null || value === undefined || value === "";
const validateMaterialForm = () => {
  if (!materialForm.productCategoryId && !materialForm.productCategory) {
    return { valid: false, message: "请选择原纸规格" };
  }
  if (!materialForm.materialModel) {
    return { valid: false, message: "请选择原料型号" };
  }
  if (isEmptyField(materialForm.basePaperQty)) {
    return { valid: false, message: "请填写原纸需要量" };
  }
  if (isEmptyField(materialForm.cartonQty)) {
    return { valid: false, message: "请填写纸箱需要量" };
  }
  return { valid: true, message: "" };
};
const validateOrderForm = () => {
  if (!formState.value.productModelId) {
    return { valid: false, message: "请选择产品" };
  }
  if (!formState.value.productModelName) {
    return { valid: false, message: "请选择规格" };
  }
  if (!formState.value.unit) {
    return { valid: false, message: "请选择单位" };
  }
  if (!formState.value.routeId) {
    return { valid: false, message: "请选择工艺路线" };
  }
  if (!(Number(formState.value.quantity || 0) > 0)) {
    return { valid: false, message: "请填写需求数量" };
  }
  return { valid: true, message: "" };
};
watch(
  () => isShow.value,
  (visible) => {
    if (visible) {
      loadBindProductOptions();
    }
  },
  { immediate: true }
);
const handleSubmit = () => {
  formRef.value?.validate((valid) => {
    if (!valid) return;
    const orderValidateResult = validateOrderForm();
    if (!orderValidateResult.valid) {
      proxy.$modal.msgWarning(orderValidateResult.message);
      return;
    }
    const materialValidateResult = validateMaterialForm();
    if (!materialValidateResult.valid) {
      proxy.$modal.msgWarning(materialValidateResult.message);
      return;
    }
    const submitData = {
      ...formState.value,
      productId: formState.value.productId,
      productModelId: formState.value.productModelId,
      materialList: [
        {
          productModelId: formState.value.productModelId,
          productCategoryId: materialForm.productCategoryId,
          productCategory: materialForm.productCategory,
          materialName: materialForm.productCategory,
          materialModel: materialForm.materialModel,
          basePaperQty: materialForm.basePaperQty,
          cartonQty: materialForm.cartonQty,
        },
      ],
    };
    addProductOrder(submitData).then(() => {
      isShow.value = false;
      emit("completed");
      proxy.$modal.msgSuccess("提交成功");
    });
  });
};
defineExpose({
  closeModal,
@@ -190,3 +456,9 @@
  isShow,
});
</script>
<style scoped lang="scss">
.material-table {
  width: 100%;
}
</style>
src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
@@ -6,13 +6,9 @@
        <el-table-column label="原料型号" prop="materialModel" min-width="180" />
        <el-table-column label="原纸需要量" prop="basePaperQty" min-width="120" />
        <el-table-column label="纸箱需要量" prop="cartonQty" min-width="120" />
        <el-table-column label="塑料袋数量" prop="plasticBagQty" min-width="120" />
        <el-table-column label="计量单位" prop="unit" width="100" />
        <el-table-column label="原纸领用数量" prop="basePaperPickQty" min-width="120" />
        <el-table-column label="纸箱领用数量" prop="cartonPickQty" min-width="120" />
        <el-table-column label="塑料袋领用数量" prop="plasticBagPickQty" min-width="120" />
        <el-table-column label="退料数量" prop="returnQty" min-width="110" />
        <el-table-column label="实际数量" prop="actualQty" min-width="110" />
      </el-table>
      <template #footer>
        <span class="dialog-footer">
@@ -33,7 +29,6 @@
      <el-table :data="returnSummaryList" border row-key="summaryKey">
        <el-table-column label="原料名称" prop="materialName" min-width="180" />
        <el-table-column label="原料型号" prop="materialModel" min-width="180" />
        <el-table-column label="计量单位" prop="unit" min-width="100" />
        <el-table-column label="退料汇总数量" prop="returnQtyTotal" min-width="140" />
      </el-table>
@@ -71,11 +66,7 @@
const getPickQty = item => {
  const directPick = Number(item.pickQty ?? NaN);
  if (Number.isFinite(directPick)) return directPick;
  return (
    Number(item.basePaperPickQty || 0) +
    Number(item.cartonPickQty || 0) +
    Number(item.plasticBagPickQty || 0)
  );
  return Number(item.basePaperPickQty || 0) + Number(item.cartonPickQty || 0);
};
const calcReturnQty = item =>
  getPickQty(item) - Number(item.actualQty || 0);
@@ -120,12 +111,11 @@
  materialDetailTableData.value.forEach(item => {
    const returnQty = calcReturnQty(item);
    if (returnQty <= 0) return;
    const key = `${item.materialModelId || ""}_${item.materialName || ""}_${item.materialModel || ""}_${item.unit || ""}`;
    const key = `${item.materialModelId || ""}_${item.materialName || ""}_${item.materialModel || ""}`;
    const old = map.get(key) || {
      summaryKey: key,
      materialName: item.materialName || "",
      materialModel: item.materialModel || "",
      unit: item.unit || "",
      returnQtyTotal: 0,
    };
    old.returnQtyTotal += returnQty;
src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
@@ -22,11 +22,6 @@
            {{ row.cartonQty ?? "-" }}
          </template>
        </el-table-column>
        <el-table-column label="塑料袋数量" min-width="120">
          <template #default="{ row }">
            {{ row.plasticBagQty ?? "-" }}
          </template>
        </el-table-column>
<!--        <el-table-column label="计量单位" width="120">-->
<!--          <template #default="{ row }">-->
<!--            {{ row.unit || "-" }}-->
@@ -48,18 +43,6 @@
          <template #default="{ row }">
            <el-input-number
              v-model="row.cartonPickQty"
              :min="0"
              :precision="3"
              :step="1"
              controls-position="right"
              style="width: 100%;"
            />
          </template>
        </el-table-column>
        <el-table-column label="塑料袋领用数量" min-width="120">
          <template #default="{ row }">
            <el-input-number
              v-model="row.plasticBagPickQty"
              :min="0"
              :precision="3"
              :step="1"
@@ -130,10 +113,8 @@
  materialModel: row.materialModel || "",
  basePaperQty: Number(row.basePaperQty ?? row.requiredQty ?? 0),
  cartonQty: Number(row.cartonQty ?? 0),
  plasticBagQty: Number(row.plasticBagQty ?? 0),
  basePaperPickQty: Number(row.basePaperPickQty ?? row.pickQty ?? 0),
  cartonPickQty: Number(row.cartonPickQty ?? 0),
  plasticBagPickQty: Number(row.plasticBagPickQty ?? 0),
  unit: row.unit || "",
});
@@ -217,9 +198,7 @@
      item.basePaperPickQty === null ||
      item.basePaperPickQty === undefined ||
      item.cartonPickQty === null ||
      item.cartonPickQty === undefined ||
      item.plasticBagPickQty === null ||
      item.plasticBagPickQty === undefined
      item.cartonPickQty === undefined
  );
  if (invalidRow) {
    return { valid: false, message: "请完善领用数量后再保存" };
@@ -236,7 +215,8 @@
  }
  materialSaving.value = true;
  try {
    await saveMaterialPickingLedger(materialTableData.value[0]);
    const firstRow = { ...materialTableData.value[0] };
    await saveMaterialPickingLedger(firstRow);
    emit("saved");
    dialogVisible.value = false;
  } finally {
src/views/productionManagement/productionOrder/index.vue
@@ -175,17 +175,6 @@
                                 style="width: 100%" />
              </template>
            </el-table-column>
            <el-table-column label="塑料袋数量"
                             min-width="120"
                             align="center">
              <template #default>
                <el-input-number v-model="bindForm.plasticBagQty"
                                 :min="0"
                                 :precision="2"
                                 controls-position="right"
                                 style="width: 100%" />
              </template>
            </el-table-column>
          </el-table>
        </el-form-item>
      </el-form>
@@ -417,13 +406,13 @@
  const bindRouteTableData = computed(() => currentBindOrderRow.value ? [currentBindOrderRow.value] : []);
  const bindForm = reactive({
    orderId: null,
    productModelId: null,
    routeId: null,
    productCategoryId: null,
    productCategory: "",
    materialModel: "",
    basePaperQty: null,
    cartonQty: null,
    plasticBagQty: null,
  });
  const materialDialogVisible = ref(false);
  const currentMaterialOrder = ref(null);
@@ -523,13 +512,13 @@
  const openBindRouteDialog = async row => {
    currentBindOrderRow.value = row;
    bindForm.orderId = row.id;
    bindForm.productModelId = row.productModelId || null;
    bindForm.routeId = null;
    bindForm.productCategoryId = row.productCategoryId || null;
    bindForm.productCategory = row.productCategory || "";
    bindForm.materialModel = row.materialModel || "";
    bindForm.basePaperQty = null;
    bindForm.cartonQty = null;
    bindForm.plasticBagQty = null;
    bindProductOptions.value = [];
    bindMaterialModelOptions.value = [];
    bindRouteDialogVisible.value = true;
@@ -581,10 +570,6 @@
      proxy.$modal.msgWarning("请填写纸箱需要量");
      return;
    }
    if (isEmptyField(bindForm.plasticBagQty)) {
      proxy.$modal.msgWarning("请填写塑料袋数量");
      return;
    }
    bindRouteSaving.value = true;
    try {
      await bindingRoute({
@@ -592,13 +577,13 @@
        routeId: bindForm.routeId,
        materialList: [
          {
            productModelId: bindForm.productModelId,
            productCategoryId: bindForm.productCategoryId,
            productCategory: bindForm.productCategory,
            materialName: bindForm.productCategory,
            materialModel: bindForm.materialModel,
            basePaperQty: bindForm.basePaperQty,
            cartonQty: bindForm.cartonQty,
            plasticBagQty: bindForm.plasticBagQty,
          },
        ],
      });
src/views/salesManagement/invoiceRegistration/index.vue
@@ -443,7 +443,7 @@
    rules: {
        createUer: [{ required: true, message: "请选择", trigger: "blur" }],
        issueDate: [{ required: true, message: "请选择", trigger: "change" }],
        invoiceNo: [{ required: true, message: "请输入", trigger: "change" }],
        invoiceNo: [{ required: false, message: "请输入", trigger: "change" }],
        createTime: [{ required: true, message: "请选择", trigger: "change" }],
    },
});