gaoluyang
6 天以前 07c0b081ea49da450d397e441608c0add375174d
新疆-融邦
1.油品出库要绑定销售订单
已修改4个文件
258 ■■■■■ 文件已修改
src/api/inventoryManagement/stockOut.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/dispatchLog/Record.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue 238 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/deliveryLedger/index.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockOut.js
@@ -45,6 +45,15 @@
    });
}
// 油品出库表单-可绑定的销售台账下拉(分页参数与出库台账列表一致)
export const bindableSalesLedger = (params) => {
    return request({
        url: "/stockOutRecord/bindableSalesLedger",
        method: "get",
        params,
    });
}
// 油品出库可用量查询(上次剩余油量 + 当前库存)
export const getOilOutAvailableQty = (params) => {
    return request({
src/views/inventoryManagement/dispatchLog/Record.vue
@@ -165,6 +165,11 @@
                           prop="shippingNo"
                           min-width="140"
                           show-overflow-tooltip />
          <!-- 绑定了销售台账的出库记录才有值,未绑定为空 -->
          <el-table-column label="销售合同号"
                           prop="salesContractNo"
                           min-width="160"
                           show-overflow-tooltip />
          <el-table-column label="来源"
                           prop="recordType"
                           min-width="90"
src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue
@@ -10,6 +10,31 @@
               ref="formRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="绑定销售台账">
              <el-select v-model="formState.salesLedgerId"
                         placeholder="不绑定则按原有出库流程"
                         clearable
                         filterable
                         remote
                         :remote-method="searchSalesLedger"
                         :loading="salesLedgerLoading"
                         style="width: 100%"
                         @change="handleSalesLedgerChange">
                <el-option v-for="item in salesLedgerOptions"
                           :key="item.id"
                           :label="salesLedgerLabel(item)"
                           :value="item.id" />
              </el-select>
              <div v-if="boundLedgerRow"
                   class="bound-ledger-tip">
                已绑定:{{ salesLedgerLabel(boundLedgerRow) }};出库类别、客户{{ isStorageBound ? '、油库/储罐' : '' }}以台账为准
              </div>
            </el-form-item>
          </el-col>
        </el-row>
        <template v-if="!isStorageBound">
          <el-row :gutter="30">
            <el-col :span="24">
            <el-form-item label="产品名称"
                          prop="productModelId"
                          :rules="[
@@ -19,7 +44,21 @@
                trigger: 'change',
              }
            ]">
              <el-button type="primary"
                <!-- 绑定卖油台账后,产品只能从该台账明细里选(代储台账没有明细) -->
                <el-select v-if="hasBoundLedger"
                           v-model="formState.productModelId"
                           placeholder="请选择台账产品"
                           filterable
                           clearable
                           style="width: 100%"
                           @change="handleLedgerProductChange">
                  <el-option v-for="(item, index) in ledgerProductOptions"
                             :key="`${item.productModelId}-${index}`"
                             :label="ledgerProductLabel(item)"
                             :value="item.productModelId" />
                </el-select>
                <el-button v-else
                           type="primary"
                         @click="showProductSelectDialog = true">
                {{ formState.productName ? formState.productName : '选择产品' }}
              </el-button>
@@ -42,6 +81,7 @@
            </el-form-item>
          </el-col>
        </el-row>
        </template>
        <div class="form-section-title">出库信息</div>
        <el-row :gutter="30">
          <el-col :span="8">
@@ -51,6 +91,7 @@
              <el-select v-model="formState.outCategory"
                         placeholder="请选择"
                         clearable
                         :disabled="hasBoundLedger"
                         style="width: 100%">
                <el-option label="客存"
                           value="客存" />
@@ -86,6 +127,7 @@
              <el-select v-model="formState.oilDepotId"
                         placeholder="请选择"
                         clearable
                         :disabled="isStorageBound"
                         popper-class="oil-depot-select-popper"
                         style="width: 100%"
                         @change="depotChange">
@@ -112,6 +154,7 @@
              <el-select v-model="formState.tankId"
                         placeholder="请选择"
                         clearable
                         :disabled="isStorageBound"
                         popper-class="tank-select-popper"
                         style="width: 100%"
                         @change="tankChange">
@@ -148,7 +191,7 @@
          <el-col :span="12">
            <el-form-item label="油品"
                          prop="oilProduct"
                          :rules="[{ required: true, message: '请选择油品', trigger: 'change' }]">
                          :rules="isStorageBound ? [] : [{ required: true, message: '请选择油品', trigger: 'change' }]">
              <el-input :model-value="formState.oilProduct"
                        readonly
                        placeholder="--" />
@@ -170,6 +213,7 @@
                         placeholder="请选择"
                         clearable
                         filterable
                         :disabled="hasBoundLedger"
                         popper-class="customer-select-popper"
                         style="width: 100%"
                         @change="customerChange">
@@ -447,7 +491,12 @@
    addOilOutRecordOnly,
    getStockInventoryBatchNoQty,
  } from "@/api/inventoryManagement/stockInventory.js";
  import { getOilOutAvailableQty } from "@/api/inventoryManagement/stockOut.js";
  import {
    getOilOutAvailableQty,
    bindableSalesLedger,
  } from "@/api/inventoryManagement/stockOut.js";
  import { productList } from "@/api/salesManagement/salesLedger.js";
  import { productModelList } from "@/api/basicData/productModel";
  import { allOilDepot } from "@/api/basicData/oilDepot.js";
  import { allTankInfo } from "@/api/basicData/tankInfo.js";
  import { allVehicle } from "@/api/basicData/vehicle.js";
@@ -463,6 +512,8 @@
  const emit = defineEmits(["update:visible", "completed"]);
  const buildEmptyForm = () => ({
    // 绑定的销售台账 id;为空表示不绑定,走原有出库流程
    salesLedgerId: undefined,
    productId: undefined,
    productModelId: undefined,
    productName: "",
@@ -507,6 +558,17 @@
  const batchNoOptions = ref([]);
  // 销售类出库的可用量提示(上次剩余 + 当前库存)
  const availableQty = ref(null);
  // 绑定销售台账:下拉候选、当前绑定行、台账明细限定出的产品
  const salesLedgerOptions = ref([]);
  const salesLedgerLoading = ref(false);
  const boundLedgerRow = ref(null);
  const ledgerProductOptions = ref([]);
  // 台账明细行只有 productModelId,产品名称/型号/单位要回产品规格表取
  const productModelCache = ref(null);
  const hasBoundLedger = computed(() => !!formState.value.salesLedgerId);
  // 绑代储台账:不用选产品,油库/储罐由台账带入
  const isStorageBound = computed(() => boundLedgerRow.value?.ledgerType === "代储");
  const fetchBatchNoOptions = () => {
    batchNoOptions.value = [];
@@ -607,6 +669,8 @@
  const closeModal = () => {
    proxy.resetForm("formRef");
    formState.value = buildEmptyForm();
    boundLedgerRow.value = null;
    ledgerProductOptions.value = [];
    isShow.value = false;
  };
@@ -679,10 +743,154 @@
    }
  };
  const salesLedgerLabel = item =>
    [item.salesContractNo, item.customerName, item.projectName]
      .filter(Boolean)
      .join(" / ");
  // 搜索框只有一个,而后端三个参数是 AND 关系,所以依次按 合同号 → 客户名 → 项目名 试,命中即止
  const LEDGER_SEARCH_FIELDS = ["salesContractNo", "customerName", "projectName"];
  const queryLedger = params =>
    bindableSalesLedger({ current: 1, size: 50, ...params }).then(
      res => res.data?.records || []
    );
  // 绑定下拉:卖油和代储都会返回,不做类型过滤,由用户自己选
  const fetchSalesLedgerOptions = async keyword => {
    salesLedgerLoading.value = true;
    try {
      let records = [];
      if (keyword) {
        for (const field of LEDGER_SEARCH_FIELDS) {
          records = await queryLedger({ [field]: keyword });
          if (records.length) {
            break;
          }
        }
      } else {
        records = await queryLedger({});
      }
      // 搜索后已选中的台账可能不在结果里,补上避免下拉框只显示一个 id
      if (
        boundLedgerRow.value &&
        !records.some(item => item.id === boundLedgerRow.value.id)
      ) {
        records.unshift(boundLedgerRow.value);
      }
      salesLedgerOptions.value = records;
    } catch (e) {
      salesLedgerOptions.value = [];
    } finally {
      salesLedgerLoading.value = false;
    }
  };
  const searchSalesLedger = keyword =>
    fetchSalesLedgerOptions(String(keyword || "").trim());
  // 产品规格表全量取一次并缓存:台账明细只有 productModelId,名称/型号/单位得从这里补
  const getProductModels = () => {
    if (productModelCache.value) {
      return Promise.resolve(productModelCache.value);
    }
    return productModelList({ current: 1, size: 1000 })
      .then(res => {
        const list = res?.records || res?.data?.records || res.data || [];
        productModelCache.value = Array.isArray(list) ? list : [];
        return productModelCache.value;
      })
      .catch(() => []);
  };
  // 台账明细 → 产品下拉候选;代储台账没有明细,不会调到这里
  const fetchLedgerProducts = ledgerId => {
    ledgerProductOptions.value = [];
    return Promise.all([
      productList({ salesLedgerId: ledgerId, type: 1 }),
      getProductModels(),
    ]).then(([ledgerRes, models]) => {
      const rows = Array.isArray(ledgerRes?.data) ? ledgerRes.data : [];
      const modelMap = new Map((models || []).map(item => [String(item.id), item]));
      ledgerProductOptions.value = rows.map(row => {
        const model = modelMap.get(String(row.productModelId)) || {};
        return {
          productId: row.productId ?? model.productId,
          productModelId: row.productModelId ?? model.id,
          productName:
            model.productName || row.productCategory || row.specificationModel || "",
          model: model.model || row.specificationModel || "",
          unit: model.unit || row.unit || "",
        };
      });
    });
  };
  const ledgerProductLabel = item =>
    [item.productName, item.model].filter(Boolean).join(" / ");
  const clearProduct = () => {
    formState.value.productId = undefined;
    formState.value.productModelId = undefined;
    formState.value.productName = "";
    formState.value.model = "";
    formState.value.unit = "";
    formState.value.oilProduct = undefined;
    formState.value.batchNo = undefined;
    batchNoOptions.value = [];
  };
  const handleLedgerProductChange = productModelId => {
    const item = ledgerProductOptions.value.find(
      row => row.productModelId === productModelId
    );
    if (!item) {
      clearProduct();
      return;
    }
    formState.value.productId = item.productId;
    formState.value.productName = item.productName;
    formState.value.model = item.model;
    formState.value.unit = item.unit;
    formState.value.oilProduct = item.productName || item.model || undefined;
    formState.value.batchNo = undefined;
    fetchBatchNoOptions();
    proxy.$refs["formRef"]?.validateField("productModelId");
  };
  const handleSalesLedgerChange = id => {
    const row = salesLedgerOptions.value.find(item => item.id === id);
    // 清空选择时 el-select 会给空串,归一化成 undefined,避免把空串当台账 id 传后端
    if (!row) {
      formState.value.salesLedgerId = undefined;
    }
    boundLedgerRow.value = row || null;
    ledgerProductOptions.value = [];
    clearProduct();
    if (!row) {
      // 取消绑定:出库类别、客户恢复为手选
      return;
    }
    // 出库类别与客户以台账为准(后端也会强制覆盖),前端同步展示并置灰
    formState.value.outCategory = row.ledgerType === "代储" ? "代储" : "销售";
    customerChange(row.customerId);
    formState.value.customerName =
      row.customerName || formState.value.customerName || "";
    if (row.ledgerType === "代储") {
      formState.value.oilDepotId = row.oilDepotId;
      formState.value.oilDepotName = row.oilDepotName || "";
      formState.value.tankId = row.tankId;
      formState.value.tankNo = row.tankNo || "";
      return;
    }
    fetchLedgerProducts(row.id);
  };
  const handleSubmit = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        if (!formState.value.productModelId) {
        // 绑代储台账不用选产品(代储台账没有产品明细)
        if (!formState.value.productModelId && !isStorageBound.value) {
          proxy.$modal.msgError("请选择产品");
          return;
        }
@@ -704,7 +912,18 @@
    });
  };
  // 每次打开重新拉台账候选,避免在别处新建台账后本页看不到
  watch(
    () => props.visible,
    visible => {
      if (visible) {
        fetchSalesLedgerOptions("");
      }
    }
  );
  onMounted(() => {
    fetchSalesLedgerOptions("");
    allOilDepot().then(res => {
      oilDepotList.value = res?.data || [];
    });
@@ -733,6 +952,17 @@
    border-radius: 3px;
  }
  // 绑定销售台账后的提示条
  .bound-ledger-tip {
    margin-top: 6px;
    padding: 6px 14px;
    font-size: 13px;
    color: #303133;
    background: rgba(64, 158, 255, 0.08);
    border-left: 3px solid #409eff;
    border-radius: 3px;
  }
  // 储库名称下拉选项:小卡片样式
  .oil-depot-option {
    padding: 8px 10px;
src/views/salesManagement/deliveryLedger/index.vue
@@ -76,6 +76,12 @@
          prop="shippingNo"
          show-overflow-tooltip
        />
        <!-- 油品出库行取绑定的销售台账合同号,未绑定为空 -->
        <el-table-column
          label="销售合同号"
          prop="salesContractNo"
          show-overflow-tooltip
        />
        <el-table-column
          label="客户名称"
          prop="customerName"