From f9608b880eb17ed232b1dc9c216ae57a7fb5b4a9 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 21 九月 2026 18:06:58 +0800
Subject: [PATCH] 新疆-融邦环保 1.「油品出库就生成出库台账,不用区分出库类别了」+「代储出库也需要出库扣库存,选批号,生成发货台账」 2.车辆管理与出库残油联动

---
 src/components/AttachmentUpload/image/index.vue                     |   10 ++
 src/views/basicData/product/index.vue                               |    3 
 src/views/basicData/vehicle/index.vue                               |   38 ++++++++++++
 src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue |  125 +++++++++++++++++++++--------------------
 4 files changed, 109 insertions(+), 67 deletions(-)

diff --git a/src/components/AttachmentUpload/image/index.vue b/src/components/AttachmentUpload/image/index.vue
index 8243f9c..00b1582 100644
--- a/src/components/AttachmentUpload/image/index.vue
+++ b/src/components/AttachmentUpload/image/index.vue
@@ -1,6 +1,6 @@
 <script setup>
 import {Plus} from '@element-plus/icons-vue'
-import {uploadFile} from '@/api/basicData/common'
+import {uploadFile, uploadPublicFile} from '@/api/basicData/common'
 
 const props = defineProps({
   fileList: {
@@ -38,6 +38,12 @@
   uploadFieldName: {
     type: String,
     default: 'files',
+  },
+  // 涓绘暟鎹暱鏈熷彲鐪嬬殑鍥剧墖璧� /common/public/upload锛坧ublicKey 姘镐箙鍦板潃锛夛紝
+  // 榛樿浠嶈蛋 /common/upload锛堝甫 2 灏忔椂澶辨晥鐨� token锛変互淇濇寔鏃㈡湁琛屼负
+  permanent: {
+    type: Boolean,
+    default: false,
   },
 })
 
@@ -204,7 +210,7 @@
   proxy.$modal.loading('鍥剧墖涓婁紶涓紝璇风◢鍊�...')
 
   try {
-    const response = await uploadFile(formData)
+    const response = await (props.permanent ? uploadPublicFile(formData) : uploadFile(formData))
     const responseList = extractResponseArray(response).map((item, index) => normalizeResponseItem(item, index))
 
     if (!responseList.length) {
diff --git a/src/views/basicData/product/index.vue b/src/views/basicData/product/index.vue
index b05b215..542c8a0 100644
--- a/src/views/basicData/product/index.vue
+++ b/src/views/basicData/product/index.vue
@@ -9,8 +9,7 @@
                   @clear="searchFilter"
                   clearable
                   prefix-icon="Search" />
-        <el-button v-if="false"
-                   type="primary"
+        <el-button type="primary"
                    @click="openProDia('addOne')"
                    style="margin-left: 10px">鏂板浜у搧澶х被</el-button>
       </div>
diff --git a/src/views/basicData/vehicle/index.vue b/src/views/basicData/vehicle/index.vue
index 26e2200..81c6904 100644
--- a/src/views/basicData/vehicle/index.vue
+++ b/src/views/basicData/vehicle/index.vue
@@ -83,6 +83,19 @@
             </el-tag>
           </template>
         </el-table-column>
+        <el-table-column label="杞﹁締鐓х墖"
+                         min-width="100"
+                         align="center">
+          <template #default="scope">
+            <el-image v-if="scope.row.vehiclePhotos && scope.row.vehiclePhotos.length"
+                      :src="scope.row.vehiclePhotos[0]"
+                      :preview-src-list="scope.row.vehiclePhotos"
+                      preview-teleported
+                      fit="cover"
+                      style="width: 60px; height: 44px; border-radius: 4px" />
+            <span v-else>--</span>
+          </template>
+        </el-table-column>
         <el-table-column label="澶囨敞"
                          prop="remark"
                          min-width="160"
@@ -196,6 +209,16 @@
             </el-form-item>
           </el-col>
         </el-row>
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="杞﹁締鐓х墖锛�"
+                          prop="vehiclePhotos">
+              <ImageUpload v-model:file-list="form.vehiclePhotos"
+                           :limit="10"
+                           permanent />
+            </el-form-item>
+          </el-col>
+        </el-row>
       </el-form>
     </FormDialog>
   </div>
@@ -203,6 +226,7 @@
 
 <script setup>
   import FormDialog from "@/components/Dialog/FormDialog.vue";
+  import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
   import pagination from "@/components/PIMTable/Pagination.vue";
   import { ref, reactive, toRefs, getCurrentInstance } from "vue";
   import { ElMessageBox } from "element-plus";
@@ -239,6 +263,7 @@
       loadCapacity: null,
       status: "鍚敤",
       remark: "",
+      vehiclePhotos: [],
     },
     rules: {
       plateNumber: [{ required: true, message: "璇疯緭鍏ヨ溅鐗屽彿", trigger: "blur" }],
@@ -299,6 +324,8 @@
         loadCapacity,
         status: status || "鍚敤",
         remark,
+        // 鎺ュ彛杩斿洖鐨勫氨鏄� URL 瀛楃涓叉暟缁勶紝鐩存帴鍠傜粰 el-upload 鍥炴樉
+        vehiclePhotos: Array.isArray(row.vehiclePhotos) ? [...row.vehiclePhotos] : [],
       };
     } else {
       form.value = {
@@ -309,6 +336,7 @@
         loadCapacity: null,
         status: "鍚敤",
         remark: "",
+        vehiclePhotos: [],
       };
     }
   };
@@ -317,7 +345,15 @@
     proxy.$refs["formRef"].validate(valid => {
       if (valid) {
         const api = form.value.id ? updateVehicle : addVehicle;
-        api(form.value).then(() => {
+        // 鍚庣 vehiclePhotos 鏄� List<String>锛屽彧瀛� URL锛涗笂浼犵粍浠剁粰鐨勬槸 {name, url} 瀵硅薄鏁扮粍锛�
+        // 鐩存帴浼犲璞′細 JSON 瑙f瀽澶辫触鎶� 400銆傚彟澶栦紶 null 涓嶆洿鏂帮紝鎯冲垹鍏夊繀椤绘樉寮忎紶 []
+        const payload = {
+          ...form.value,
+          vehiclePhotos: (form.value.vehiclePhotos || [])
+            .map(item => (typeof item === "string" ? item : item?.url || item?.previewURL || ""))
+            .filter(Boolean),
+        };
+        api(payload).then(() => {
           proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
           closeDia();
           getList();
diff --git a/src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue b/src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue
index 7b361a9..4272035 100644
--- a/src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue
+++ b/src/views/inventoryManagement/stockManagement/OilStockOutDialog.vue
@@ -32,56 +32,54 @@
             </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',
-              }
-            ]">
-                <!-- 缁戝畾鍗栨补鍙拌处鍚庯紝浜у搧鍙兘浠庤鍙拌处鏄庣粏閲岄�夛紙浠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>
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="浜у搧鍚嶇О"
+                          prop="productModelId"
+                          :rules="[
+              {
+              required: true,
+              message: '璇烽�夋嫨浜у搧瑙勬牸',
+              trigger: 'change',
+            }
+          ]">
+              <!-- 缁戝畾鍗栨补鍙拌处鍚庝骇鍝佸彧鑳戒粠璇ュ彴璐︽槑缁嗛噷閫夛紱浠e偍鍙拌处娌℃湁鏄庣粏锛屼粛璧颁骇鍝侀�夋嫨寮圭獥 -->
+              <el-select v-if="useLedgerProducts"
+                         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>
         <div class="form-section-title">鍑哄簱淇℃伅</div>
         <el-row :gutter="30">
           <el-col :span="8">
@@ -105,9 +103,7 @@
           <el-col :span="8">
             <el-form-item label="鍑哄簱鎵规"
                           prop="batchNo"
-                          :rules="isSalesOut
-                ? [{ required: true, message: '閿�鍞被鍑哄簱蹇呴』閫夋嫨鍑哄簱鎵规', trigger: 'change' }]
-                : []">
+                          :rules="[{ required: true, message: '娌瑰搧鍑哄簱蹇呴』閫夋嫨鍑哄簱鎵规', trigger: 'change' }]">
               <el-select v-model="formState.batchNo"
                          placeholder="璇烽�夋嫨"
                          clearable
@@ -396,7 +392,9 @@
           <el-col :span="12">
             <el-form-item label="杞︽寕鐗屽彿"
                           prop="trailerPlateNo"
-                          :rules="[{ required: true, message: '璇疯緭鍏ヨ溅鎸傜墝鍙�', trigger: 'blur' }]">
+                          :rules="isSalesOut
+                ? [{ required: true, message: '璇疯緭鍏ヨ溅鎸傜墝鍙�', trigger: 'blur' }]
+                : []">
               <el-input v-model="formState.trailerPlateNo"
                         :readonly="!!formState.vehicleId"
                         placeholder="璇疯緭鍏�"
@@ -455,7 +453,7 @@
           </el-col>
         </el-row>
         <el-row :gutter="30"
-                v-if="isSalesOut">
+                v-if="formState.productModelId">
           <el-col :span="24">
             <el-form-item label="鍙敤閲�">
               <div class="available-qty-tip">
@@ -567,8 +565,10 @@
   const productModelCache = ref(null);
 
   const hasBoundLedger = computed(() => !!formState.value.salesLedgerId);
-  // 缁戜唬鍌ㄥ彴璐︼細涓嶇敤閫変骇鍝侊紝娌瑰簱/鍌ㄧ綈鐢卞彴璐﹀甫鍏�
+  // 缁戜唬鍌ㄥ彴璐︼細娌瑰簱/鍌ㄧ綈鐢卞彴璐﹀甫鍏ワ紝浣嗕骇鍝佷粛瑕侀�夛紙浠e偍鍑哄簱涔熻鎵e簱瀛橈級
   const isStorageBound = computed(() => boundLedgerRow.value?.ledgerType === "浠e偍");
+  // 鍙湁鍗栨补鍙拌处鏈変骇鍝佹槑缁嗗彲鎸戯紱浠e偍鍙拌处娌℃槑缁嗭紝璧颁骇鍝侀�夋嫨寮圭獥
+  const useLedgerProducts = computed(() => hasBoundLedger.value && !isStorageBound.value);
 
   const fetchBatchNoOptions = () => {
     batchNoOptions.value = [];
@@ -588,14 +588,15 @@
     });
   };
 
-  // 閿�鍞被鍑哄簱蹇呴』缁欏嚭搴撴壒娆★紙鍚庣鏂板鏃跺嵆鏍¢獙锛屼笖瀹℃壒閫氳繃浼氭寜鎵规鎵e簱瀛橈級
+  // 杞﹁締鍙湁閿�鍞被鍑哄簱蹇呭~锛堥攢鍞被鎵嶆寜杞︾畻銆屼笂娆″墿浣欐补閲忋�嶏紝浠e偍/瀹㈠瓨娈嬫补鎸� 0锛�
   const isSalesOut = computed(() => formState.value.outCategory === "閿�鍞�");
 
-  // 鍙敤閲忔彁绀猴細杞﹁締 + 瑙勬牸锛�+鎵规锛夌‘瀹氬悗鍚戝悗绔彇銆屼笂娆″墿浣� / 褰撳墠搴撳瓨 / 鍙敤閲忋��
+  // 鍙敤閲忔彁绀猴細瑙勬牸锛�+鎵规+杞﹁締锛夌‘瀹氬悗鍚戝悗绔彇銆屼笂娆″墿浣� / 褰撳墠搴撳瓨 / 鍙敤閲忋�嶃��
+  // 浠e偍/瀹㈠瓨娌℃湁杞﹁締锛屼笉浼� vehicleId锛屽悗绔寜涓婃鍓╀綑 0 澶勭悊锛屽彲鐢ㄩ噺 = 褰撳墠搴撳瓨
   const fetchAvailableQty = () => {
     availableQty.value = null;
     const { vehicleId, productModelId, batchNo } = formState.value;
-    if (!isSalesOut.value || !vehicleId || !productModelId) {
+    if (!productModelId) {
       return;
     }
     getOilOutAvailableQty({ vehicleId, productModelId, batchNo })
@@ -911,9 +912,9 @@
   const handleSubmit = () => {
     proxy.$refs["formRef"].validate(valid => {
       if (valid) {
-        // 缁戜唬鍌ㄥ彴璐︿笉鐢ㄩ�変骇鍝侊紙浠e偍鍙拌处娌℃湁浜у搧鏄庣粏锛�
-        if (!formState.value.productModelId && !isStorageBound.value) {
-          proxy.$modal.msgError("璇烽�夋嫨浜у搧");
+        // 閿�鍞�/浠e偍/瀹㈠瓨閮借鎵e簱瀛橈紝涓�寰嬪繀椤婚�夎鏍硷紙浠e偍鍙拌处娌℃槑缁嗭紝浠庝骇鍝佸脊绐楅�夛級
+        if (!formState.value.productModelId) {
+          proxy.$modal.msgError("璇烽�夋嫨浜у搧瑙勬牸");
           return;
         }
         // 璁$畻缁撴灉闅忚〃鍗曟彁浜�

--
Gitblit v1.9.3