From 07c0b081ea49da450d397e441608c0add375174d Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期日, 20 九月 2026 20:22:52 +0800
Subject: [PATCH] 新疆-融邦 1.油品出库要绑定销售订单
---
src/views/inventoryManagement/dispatchLog/Record.vue | 5
src/views/salesManagement/deliveryLedger/index.vue | 6 +
src/api/inventoryManagement/stockOut.js | 9 +
src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue | 288 +++++++++++++++++++++++++++++++++++++++++++----
4 files changed, 279 insertions(+), 29 deletions(-)
diff --git a/src/api/inventoryManagement/stockOut.js b/src/api/inventoryManagement/stockOut.js
index b70b7bc..7320ec3 100644
--- a/src/api/inventoryManagement/stockOut.js
+++ b/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({
diff --git a/src/views/inventoryManagement/dispatchLog/Record.vue b/src/views/inventoryManagement/dispatchLog/Record.vue
index 5658104..feb7bf5 100644
--- a/src/views/inventoryManagement/dispatchLog/Record.vue
+++ b/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"
diff --git a/src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue b/src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue
index 0b6244a..a3c01df 100644
--- a/src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue
+++ b/src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue
@@ -10,38 +10,78 @@
ref="formRef">
<el-row :gutter="30">
<el-col :span="24">
- <el-form-item label="浜у搧鍚嶇О"
- prop="productModelId"
- :rules="[
+ <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="[
{
required: true,
message: '璇烽�夋嫨浜у搧',
trigger: 'change',
}
]">
- <el-button type="primary"
- @click="showProductSelectDialog = true">
- {{ formState.productName ? formState.productName : '閫夋嫨浜у搧' }}
- </el-button>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="30">
- <el-col :span="12">
- <el-form-item label="瑙勬牸"
- prop="model">
- <el-input v-model="formState.model"
- disabled />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="鍗曚綅"
- prop="unit">
- <el-input v-model="formState.unit"
- disabled />
- </el-form-item>
- </el-col>
- </el-row>
+ <!-- 缁戝畾鍗栨补鍙拌处鍚庯紝浜у搧鍙兘浠庤鍙拌处鏄庣粏閲岄�夛紙浠e偍鍙拌处娌℃湁鏄庣粏锛� -->
+ <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>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="30">
+ <el-col :span="12">
+ <el-form-item label="瑙勬牸"
+ prop="model">
+ <el-input v-model="formState.model"
+ disabled />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍗曚綅"
+ prop="unit">
+ <el-input v-model="formState.unit"
+ disabled />
+ </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 === "浠e偍");
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 || []
+ );
+
+ // 缁戝畾涓嬫媺锛氬崠娌瑰拰浠e偍閮戒細杩斿洖锛屼笉鍋氱被鍨嬭繃婊わ紝鐢辩敤鎴疯嚜宸遍��
+ 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(() => []);
+ };
+
+ // 鍙拌处鏄庣粏 鈫� 浜у搧涓嬫媺鍊欓�夛紱浠e偍鍙拌处娌℃湁鏄庣粏锛屼笉浼氳皟鍒拌繖閲�
+ 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 === "浠e偍" ? "浠e偍" : "閿�鍞�";
+ customerChange(row.customerId);
+ formState.value.customerName =
+ row.customerName || formState.value.customerName || "";
+ if (row.ledgerType === "浠e偍") {
+ 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) {
+ // 缁戜唬鍌ㄥ彴璐︿笉鐢ㄩ�変骇鍝侊紙浠e偍鍙拌处娌℃湁浜у搧鏄庣粏锛�
+ 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;
diff --git a/src/views/salesManagement/deliveryLedger/index.vue b/src/views/salesManagement/deliveryLedger/index.vue
index e8eaeae..4a480ed 100644
--- a/src/views/salesManagement/deliveryLedger/index.vue
+++ b/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"
--
Gitblit v1.9.3