feat: 销售台账品名/材质/规格型号手工录入与材质列,回退采购台账相关改动
已修改2个文件
340 ■■■■■ 文件已修改
src/views/procurementManagement/procurementLedger/index.vue 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue
@@ -105,8 +105,6 @@
                               width="60" />
              <el-table-column label="产品大类"
                               prop="productCategory" />
              <el-table-column label="材质"
                               prop="material" />
              <el-table-column label="规格型号"
                               prop="specificationModel" />
              <el-table-column label="单位"
@@ -414,8 +412,6 @@
                           width="60" />
          <el-table-column label="产品大类"
                           prop="productCategory" />
          <el-table-column label="材质"
                           prop="material" />
          <el-table-column label="规格型号"
                           prop="specificationModel" />
          <el-table-column label="单位"
@@ -544,31 +540,34 @@
               ref="productFormRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="品名:"
                          prop="productCategory">
              <el-input v-model="productForm.productCategory"
                        placeholder="请输入"
                        clearable />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="材质:"
                          prop="material">
              <el-input v-model="productForm.material"
                        placeholder="请输入"
                        clearable />
            <el-form-item label="产品大类:"
                          prop="productId">
              <el-tree-select v-model="productForm.productId"
                              placeholder="请选择"
                              clearable
                              filterable
                              check-strictly
                              @change="getModels"
                              :data="productOptions"
                              :render-after-expand="false"
                              style="width: 100%" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="规格型号:"
                          prop="specificationModel">
              <el-input v-model="productForm.specificationModel"
                        placeholder="请输入"
                        clearable />
                          prop="productModelId">
              <el-select v-model="productForm.productModelId"
                         placeholder="请选择"
                         filterable
                         clearable
                         @change="getProductModel">
                <el-option v-for="item in modelOptions"
                           :key="item.id"
                           :label="item.model"
                           :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
@@ -745,7 +744,9 @@
  const productData = ref([]);
  const selectedRows = ref([]);
  const productSelectedRows = ref([]);
  const modelOptions = ref([]);
  const userList = ref([]);
  const productOptions = ref([]);
  const salesContractList = ref([]);
  const supplierList = ref([]);
  const tableLoading = ref(false);
@@ -758,6 +759,7 @@
  const total = ref(0);
  const fileList = ref([]);
  import useUserStore from "@/store/modules/user";
  import { modelList, productTreeList } from "@/api/basicData/product.js";
  import dayjs from "dayjs";
  import FileUpload from "@/components/AttachmentUpload/file/index.vue";
@@ -952,8 +954,9 @@
  const currentId = ref("");
  const productFormData = reactive({
    productForm: {
      productId: "",
      productCategory: "",
      material: "",
      productModelId: "",
      specificationModel: "",
      unit: "",
      quantity: "",
@@ -966,11 +969,8 @@
      isChecked: false,
    },
    productRules: {
      productCategory: [{ required: true, message: "请输入", trigger: "blur" }],
      material: [{ required: true, message: "请输入", trigger: "blur" }],
      specificationModel: [
        { required: true, message: "请输入", trigger: "blur" },
      ],
      productId: [{ required: true, message: "请选择", trigger: "change" }],
      productModelId: [{ required: true, message: "请选择", trigger: "change" }],
      unit: [{ required: true, message: "请输入", trigger: "blur" }],
      quantity: [{ required: true, message: "请输入", trigger: "blur" }],
      taxInclusiveUnitPrice: [
@@ -1457,7 +1457,7 @@
    dialogFormVisible.value = false;
  };
  // 打开产品弹框
  const openProductForm = (type, row, index) => {
  const openProductForm = async (type, row, index) => {
    // 编辑时检查产品入库审核状态,完全入库时不能编辑
    if (type === "edit" && row && row.stockInApprovalStatus === "完全入库") {
      proxy.$modal.msgWarning("完全入库状态的产品不能编辑");
@@ -1470,6 +1470,12 @@
    proxy.resetForm("productFormRef");
    productFormVisible.value = true;
    // 先获取产品选项,确保数据加载完成
    await getProductOptions();
    // 等待 DOM 更新
    await nextTick();
    if (type === "add") {
      productForm.value.isChecked = false;
    }
@@ -1477,8 +1483,130 @@
    if (type === "edit") {
      // 复制行数据
      productForm.value = { ...row };
      // 如果是从模板加载的数据,可能没有 productId 和 productModelId
      // 需要根据 productCategory 和 specificationModel 来查找对应的 ID
      if (!productForm.value.productId && productForm.value.productCategory) {
        // 根据 productCategory 查找 productId
        const findProductIdByCategory = (nodes, categoryName) => {
          for (let i = 0; i < nodes.length; i++) {
            if (nodes[i].label === categoryName) {
              return nodes[i].value;
            }
            if (nodes[i].children && nodes[i].children.length > 0) {
              const found = findProductIdByCategory(
                nodes[i].children,
                categoryName
              );
              if (found) return found;
            }
          }
          return null;
        };
        const productId = findProductIdByCategory(
          productOptions.value,
          productForm.value.productCategory
        );
        if (productId) {
          productForm.value.productId = productId;
          // 获取型号列表并等待完成
          const modelRes = await modelList({ id: productId });
          modelOptions.value = modelRes;
          // 等待 DOM 更新
          await nextTick();
          // 根据 specificationModel 查找 productModelId
          if (
            productForm.value.specificationModel &&
            modelOptions.value.length > 0
          ) {
            const modelItem = modelOptions.value.find(
              item => item.model === productForm.value.specificationModel
            );
            if (modelItem) {
              productForm.value.productModelId = modelItem.id;
              // 设置规格型号和单位
              getProductModel(modelItem.id);
            }
          }
        }
      } else if (productForm.value.productId) {
        // 如果有 productId,正常加载型号列表
        await getModels(productForm.value.productId);
        // 等待 DOM 更新
        await nextTick();
        if (productForm.value.productModelId) {
          getProductModel(productForm.value.productModelId);
        }
      }
      // 最后再等待一次 DOM 更新,确保所有数据都已设置
      await nextTick();
    }
  };
  const getProductOptions = () => {
    return productTreeList().then(res => {
      productOptions.value = convertIdToValue(res);
      return res;
    });
  };
  const getModels = value => {
    if (value) {
      productForm.value.productCategory =
        findNodeById(productOptions.value, value) || "";
      return modelList({ id: value }).then(res => {
        modelOptions.value = res;
        return res;
      });
    } else {
      productForm.value.productCategory = "";
      modelOptions.value = [];
      return Promise.resolve([]);
    }
  };
  const getProductModel = value => {
    const index = modelOptions.value.findIndex(item => item.id === value);
    if (index !== -1) {
      productForm.value.specificationModel = modelOptions.value[index].model;
      productForm.value.unit = modelOptions.value[index].unit;
    } else {
      productForm.value.specificationModel = null;
      productForm.value.unit = null;
    }
  };
  const findNodeById = (nodes, productId) => {
    for (let i = 0; i < nodes.length; i++) {
      if (nodes[i].value === productId) {
        return nodes[i].label; // 找到节点,返回该节点的label
      }
      if (nodes[i].children && nodes[i].children.length > 0) {
        const foundNode = findNodeById(nodes[i].children, productId);
        if (foundNode) {
          return foundNode; // 在子节点中找到,直接返回(已经是label字符串)
        }
      }
    }
    return null; // 没有找到节点,返回null
  };
  function convertIdToValue(data) {
    return data.map(item => {
      const { id, children, ...rest } = item;
      const newItem = {
        ...rest,
        value: id, // 将 id 改为 value
      };
      if (children && children.length > 0) {
        newItem.children = convertIdToValue(children);
      }
      return newItem;
    });
  }
  // 提交产品表单
  const submitProduct = () => {
src/views/salesManagement/salesLedger/index.vue
@@ -92,6 +92,7 @@
            >
              <el-table-column align="center" label="序号" type="index" />
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="材质" prop="material" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="单位" prop="unit" />
              <el-table-column label="产品状态" width="100px" align="center">
@@ -521,6 +522,7 @@
            width="60"
          />
          <el-table-column label="产品大类" prop="productCategory" />
          <el-table-column label="材质" prop="material" />
          <el-table-column label="规格型号" prop="specificationModel" />
          <el-table-column label="单位" prop="unit" />
          <el-table-column label="数量" prop="quantity" />
@@ -705,38 +707,34 @@
      >
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品大类:" prop="productCategory">
              <el-tree-select
            <el-form-item label="品名:" prop="productCategory">
              <el-input
                v-model="productForm.productCategory"
                placeholder="请选择"
                placeholder="请输入"
                clearable
                filterable
                check-strictly
                @change="getModels"
                :data="productOptions"
                :render-after-expand="false"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select
                v-model="productForm.productModelId"
                placeholder="请选择"
            <el-form-item label="材质:" prop="material">
              <el-input
                v-model="productForm.material"
                placeholder="请输入"
                clearable
                @change="getProductModel"
                filterable
              >
                <el-option
                  v-for="item in modelOptions"
                  :key="item.id"
                  :label="item.model"
                  :value="item.id"
                />
              </el-select>
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="规格型号:" prop="specificationModel">
              <el-input
                v-model="productForm.specificationModel"
                placeholder="请输入"
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
@@ -1192,7 +1190,6 @@
  getProductInventory,
} from "@/api/salesManagement/salesLedger.js";
import { getStockInventoryByModelId } from "@/api/inventoryManagement/stockInventory.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
import FileUpload from "@/components/AttachmentUpload/file/index.vue";
@@ -1215,8 +1212,6 @@
const productSelectedRows = ref([]);
const userList = ref([]);
const customerOption = ref([]);
const productOptions = ref([]);
const modelOptions = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
@@ -1269,6 +1264,7 @@
const productFormData = reactive({
  productForm: {
    productCategory: "",
    material: "",
    specificationModel: "",
    unit: "",
    quantity: "",
@@ -1280,10 +1276,10 @@
    isProduction: false,
  },
  productRules: {
    productCategory: [{ required: true, message: "请选择", trigger: "change" }],
    productModelId: [{ required: true, message: "请选择", trigger: "change" }],
    productCategory: [{ required: true, message: "请输入", trigger: "blur" }],
    material: [{ required: true, message: "请输入", trigger: "blur" }],
    specificationModel: [
      { required: true, message: "请选择", trigger: "change" },
      { required: true, message: "请输入", trigger: "blur" },
    ],
    unit: [{ required: true, message: "请输入", trigger: "blur" }],
    quantity: [{ required: true, message: "请输入", trigger: "blur" }],
@@ -1532,14 +1528,6 @@
      tableLoading.value = false;
    });
};
// 获取产品大类tree数据
const getProductOptions = () => {
  // 返回 Promise,便于在编辑产品时等待加载完成
  return productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
    return productOptions.value;
  });
};
const formattedNumber = (row, column, cellValue) => {
  if (cellValue === undefined || cellValue === null || cellValue === "") {
    return "0.00";
@@ -1599,67 +1587,6 @@
  }
  return formattedNumber(row, column, cellValue);
};
// 获取tree子数据
const getModels = (value) => {
  productForm.value.productCategory = findNodeById(productOptions.value, value);
  modelList({ id: value }).then((res) => {
    modelOptions.value = res;
  });
};
const getProductModel = (value) => {
  const index = modelOptions.value.findIndex((item) => item.id === value);
  if (index !== -1) {
    productForm.value.specificationModel = modelOptions.value[index].model;
    productForm.value.unit = modelOptions.value[index].unit;
  } else {
    productForm.value.specificationModel = null;
    productForm.value.unit = null;
  }
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label; // 找到节点,返回该节点
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode; // 在子节点中找到,返回该节点
      }
    }
  }
  return null; // 没有找到节点,返回null
};
function convertIdToValue(data) {
  return data.map((item) => {
    const { id, children, ...rest } = item;
    const newItem = {
      ...rest,
      value: id, // 将 id 改为 value
    };
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children);
    }
    return newItem;
  });
}
// 根据名称反查产品大类 id,便于仅存名称时的反显
function findNodeIdByLabel(nodes, label) {
  if (!label) return null;
  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i];
    if (node.label === label) return node.value;
    if (node.children && node.children.length > 0) {
      const found = findNodeIdByLabel(node.children, label);
      if (found !== null && found !== undefined) return found;
    }
  }
  return null;
}
// 表格选择数据
const handleSelectionChange = (selection) => {
  // 过滤掉子数据
@@ -1930,7 +1857,7 @@
const productIndex = ref(0);
// 打开产品弹框
const openProductForm = async (type, row, index) => {
const openProductForm = (type, row, index) => {
  // 编辑时检查产品是否已发货或审核通过
  if (type === "edit" && isProductShipped(row)) {
    proxy.$modal.msgWarning("已发货或审核通过的产品不能编辑");
@@ -1946,33 +1873,6 @@
  if (type === "edit") {
    productForm.value = { ...row };
    productIndex.value = index;
    // 编辑时根据产品大类名称反查 tree 节点 id,并加载规格型号列表
    try {
      const options =
        productOptions.value && productOptions.value.length > 0
          ? productOptions.value
          : await getProductOptions();
      const categoryId = findNodeIdByLabel(
        options,
        productForm.value.productCategory
      );
      if (categoryId) {
        const models = await modelList({ id: categoryId });
        modelOptions.value = models || [];
        // 根据当前规格型号名称反查并设置 productModelId,便于下拉框显示已选值
        const currentModel = (modelOptions.value || []).find(
          (m) => m.model === productForm.value.specificationModel
        );
        if (currentModel) {
          productForm.value.productModelId = currentModel.id;
        }
      }
    } catch (e) {
      // 加载失败时保持可编辑,不中断弹窗
      console.error("加载产品规格型号失败", e);
    }
  } else {
    getProductOptions();
  }
  productFormVisible.value = true;
};