From 7a0790d8224db45a039bf33d0ef4e24ae879a243 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期二, 30 六月 2026 10:38:05 +0800
Subject: [PATCH] 采购退货重构,添加新增、删除功能;供应商管理新增修改删除功能

---
 src/pages/procurementManagement/purchaseReturnOrder/add.vue |  856 +++++++++++++++++++++++++++++++--------------------------
 1 files changed, 466 insertions(+), 390 deletions(-)

diff --git a/src/pages/procurementManagement/purchaseReturnOrder/add.vue b/src/pages/procurementManagement/purchaseReturnOrder/add.vue
index fd5591e..4d411d9 100644
--- a/src/pages/procurementManagement/purchaseReturnOrder/add.vue
+++ b/src/pages/procurementManagement/purchaseReturnOrder/add.vue
@@ -1,263 +1,269 @@
 <template>
   <view class="account-detail">
-    <PageHeader title="鏂板閲囪喘閫�璐�" @back="goBack" />
-    <up-form ref="formRef" :model="form" :rules="rules" label-width="120">
-      <u-cell-group title="鍩烘湰淇℃伅" class="form-section">
-        <up-form-item label="閫�鏂欏崟鍙�" prop="no">
-          <up-input
-            v-model="form.no"
-            :disabled="form.isDefaultNo"
-            :placeholder="form.isDefaultNo ? '浣跨敤绯荤粺缂栧彿' : '璇疯緭鍏ラ��鏂欏崟鍙�'"
-            clearable
-          />
-          <template #right>
-            <up-switch v-model="form.isDefaultNo" @change="onDefaultNoChange" />
-          </template>
-        </up-form-item>
-        <up-form-item label="閫�璐ф柟寮�" prop="returnType" required>
-          <up-input
-            v-model="returnTypeText"
-            placeholder="璇烽�夋嫨"
-            readonly
-            @click="showReturnTypeSheet = true"
-          />
-          <template #right>
-            <up-icon name="arrow-right" @click="showReturnTypeSheet = true"></up-icon>
-          </template>
-        </up-form-item>
-        <up-form-item label="渚涘簲鍟�" prop="supplierId" required>
-          <up-input
-            v-model="supplierText"
-            placeholder="璇烽�夋嫨"
-            readonly
-            @click="showSupplierSheet = true"
-          />
-          <template #right>
-            <up-icon name="arrow-right" @click="showSupplierSheet = true"></up-icon>
-          </template>
-        </up-form-item>
-        <up-form-item label="椤圭洰闃舵" prop="projectPhase">
-          <up-input
-            v-model="projectPhaseText"
-            placeholder="璇烽�夋嫨"
-            readonly
-            @click="showProjectPhaseSheet = true"
-          />
-          <template #right>
-            <up-icon name="arrow-right" @click="showProjectPhaseSheet = true"></up-icon>
-          </template>
-        </up-form-item>
-        <up-form-item label="鍒朵綔鏃ユ湡" prop="preparedAt" required>
-          <up-input
-            v-model="form.preparedAt"
-            placeholder="璇烽�夋嫨"
-            readonly
-            @click="showPreparedAtPicker = true"
-          />
-          <template #right>
-            <up-icon name="arrow-right" @click="showPreparedAtPicker = true"></up-icon>
-          </template>
-        </up-form-item>
-        <up-form-item label="鍒跺崟浜�" prop="preparedUserId" required>
-          <up-input
-            v-model="preparedUserText"
-            placeholder="璇烽�夋嫨"
-            readonly
-            @click="showPreparedUserSheet = true"
-          />
-          <template #right>
-            <up-icon name="arrow-right" @click="showPreparedUserSheet = true"></up-icon>
-          </template>
-        </up-form-item>
-        <up-form-item label="閫�鏂欎汉" prop="returnUserId" required>
-          <up-input
-            v-model="returnUserText"
-            placeholder="璇烽�夋嫨"
-            readonly
-            @click="showReturnUserSheet = true"
-          />
-          <template #right>
-            <up-icon name="arrow-right" @click="showReturnUserSheet = true"></up-icon>
-          </template>
-        </up-form-item>
-        <up-form-item label="閲囪喘鍚堝悓鍙�" prop="purchaseLedgerId" required>
-          <up-input
-            v-model="purchaseContractText"
-            placeholder="璇烽�夋嫨"
-            readonly
-            @click="showPurchaseLedgerSheet = true"
-          />
-          <template #right>
-            <up-icon name="arrow-right" @click="showPurchaseLedgerSheet = true"></up-icon>
-          </template>
-        </up-form-item>
-        <up-form-item label="澶囨敞" prop="remark">
-          <up-textarea v-model="form.remark" placeholder="璇疯緭鍏�" auto-height />
-        </up-form-item>
-      </u-cell-group>
+    <PageHeader title="鏂板閲囪喘閫�璐�"
+                @back="goBack" />
+    <up-form ref="formRef"
+             :model="form"
+             :rules="rules"
+             label-width="110">
+      <up-form-item label="閫�鏂欏崟鍙�"
+                    prop="no">
+        <up-input v-model="form.no"
+                  :disabled="form.isDefaultNo"
+                  :placeholder="form.isDefaultNo ? '浣跨敤绯荤粺缂栧彿' : '璇疯緭鍏ラ��鏂欏崟鍙�'"
+                  clearable />
+        <template #right>
+          <up-switch v-model="form.isDefaultNo"
+                     @change="onDefaultNoChange" />
+        </template>
+      </up-form-item>
+      <up-form-item label="閫�璐ф柟寮�"
+                    prop="returnType"
+                    required>
+        <up-input v-model="returnTypeText"
+                  placeholder="璇烽�夋嫨"
+                  readonly
+                  @click="showReturnTypeSheet = true" />
+        <template #right>
+          <up-icon name="arrow-right"
+                   @click="showReturnTypeSheet = true"></up-icon>
+        </template>
+      </up-form-item>
+      <up-form-item label="渚涘簲鍟�"
+                    prop="supplierName"
+                    required>
+        <up-input v-model="form.supplierName"
+                  placeholder="璇烽�夋嫨"
+                  readonly
+                  @click="showSupplierSheet = true" />
+        <template #right>
+          <up-icon name="arrow-right"
+                   @click="showSupplierSheet = true"></up-icon>
+        </template>
+      </up-form-item>
+      <up-form-item label="椤圭洰闃舵"
+                    prop="projectPhase">
+        <up-input v-model="projectPhaseText"
+                  placeholder="璇烽�夋嫨"
+                  readonly
+                  @click="showProjectPhaseSheet = true" />
+        <template #right>
+          <up-icon name="arrow-right"
+                   @click="showProjectPhaseSheet = true"></up-icon>
+        </template>
+      </up-form-item>
+      <up-form-item label="鍒朵綔鏃ユ湡"
+                    prop="preparedAt"
+                    required>
+        <up-input v-model="form.preparedAt"
+                  placeholder="璇烽�夋嫨"
+                  readonly
+                  @click="showPreparedAtPicker = true" />
+        <template #right>
+          <up-icon name="arrow-right"
+                   @click="showPreparedAtPicker = true"></up-icon>
+        </template>
+      </up-form-item>
+      <up-form-item label="鍒跺崟浜�"
+                    prop="preparedUserName"
+                    required>
+        <up-input v-model="form.preparedUserName"
+                  placeholder="璇烽�夋嫨"
+                  readonly
+                  @click="openPreparedUserSheet" />
+        <template #right>
+          <up-icon name="arrow-right"
+                   @click="openPreparedUserSheet"></up-icon>
+        </template>
+      </up-form-item>
+      <up-form-item label="閫�鏂欎汉"
+                    prop="returnUserName"
+                    required>
+        <up-input v-model="form.returnUserName"
+                  placeholder="璇烽�夋嫨"
+                  readonly
+                  @click="openReturnUserSheet" />
+        <template #right>
+          <up-icon name="arrow-right"
+                   @click="openReturnUserSheet"></up-icon>
+        </template>
+      </up-form-item>
+      <up-form-item label="閲囪喘鍚堝悓鍙�"
+                    prop="purchaseContractNumber"
+                    required>
+        <up-input v-model="form.purchaseContractNumber"
+                  placeholder="璇烽�夋嫨"
+                  readonly
+                  @click="showPurchaseLedgerSheet = true" />
+        <template #right>
+          <up-icon name="arrow-right"
+                   @click="showPurchaseLedgerSheet = true"></up-icon>
+        </template>
+      </up-form-item>
+      <up-form-item label="澶囨敞"
+                    prop="remark">
+        <up-textarea v-model="form.remark"
+                     placeholder="璇疯緭鍏ュ娉�"
+                     auto-height />
+      </up-form-item>
 
-      <u-cell-group title="浜у搧鍒楄〃" class="form-section">
-        <view class="product-actions">
-          <up-button
-            type="primary"
-            size="small"
-            text="閫夋嫨浜у搧"
-            :disabled="!form.purchaseLedgerId"
-            @click="goSelectProducts"
-          />
-          <view class="amount-summary">
+      <view class="product-section">
+        <view class="section-header">
+          <view>
+            <text class="section-title">浜у搧淇℃伅</text>
+          </view>
+          <view class="section-actions">
             <text class="amount-text">鍚堣锛歿{ formatAmount(baseAmount) }}</text>
+            <up-button type="primary"
+                       size="small"
+                       class="add-btn"
+                       :disabled="!form.purchaseLedgerId"
+                       @click="goSelectProducts">
+              閫夋嫨浜у搧
+            </up-button>
           </view>
         </view>
-        <view v-if="form.purchaseReturnOrderProductsDtos.length === 0" class="empty-products">
+        <view v-if="form.purchaseReturnOrderProductsDtos.length === 0"
+              class="empty-products">
           <text>鏆傛棤浜у搧锛岃鍏堥�夋嫨浜у搧</text>
         </view>
-        <view v-else class="product-list">
-          <view
-            v-for="(item, index) in form.purchaseReturnOrderProductsDtos"
-            :key="item.salesLedgerProductId || item.id || index"
-            class="product-card"
-          >
+        <view v-else>
+          <view class="product-card"
+                v-for="(item, index) in form.purchaseReturnOrderProductsDtos"
+                :key="item.id || index">
             <view class="product-header">
               <view class="product-title">
-                <view class="document-icon">
-                  <up-icon name="file-text" size="16" color="#ffffff"></up-icon>
-                </view>
-                <text class="product-name">浜у搧 {{ index + 1 }}</text>
+                <up-icon name="file-text"
+                         size="16"
+                         color="#2979ff"></up-icon>
+                <text class="product-productCategory">浜у搧 {{ index + 1 }}</text>
               </view>
-              <up-icon name="trash" size="18" color="#ee0a24" @click="removeProduct(index)" />
+              <view>
+                <up-button type="error"
+                           size="mini"
+                           class="del-btn"
+                           @click="removeProduct(index)">
+                  鍒犻櫎
+                </up-button>
+              </view>
             </view>
-            <up-divider></up-divider>
-            <view class="product-body">
-              <view class="detail-row">
-                <text class="detail-label">浜у搧澶х被</text>
-                <text class="detail-value">{{ item.productCategory || "-" }}</text>
-              </view>
-              <view class="detail-row">
-                <text class="detail-label">瑙勬牸鍨嬪彿</text>
-                <text class="detail-value">{{ item.specificationModel || "-" }}</text>
-              </view>
-              <view class="detail-row">
-                <text class="detail-label">鏁伴噺</text>
-                <text class="detail-value">{{ item.quantity ?? "-" }}</text>
-              </view>
-              <view class="detail-row">
-                <text class="detail-label">鍚◣鍗曚环(鍏�)</text>
-                <text class="detail-value">{{ formatAmount(item.taxInclusiveUnitPrice) }}</text>
-              </view>
-              <view class="qty-row">
-                <text class="qty-label">閫�璐ф暟閲�</text>
-                <up-number-box
-                  v-model="item.returnQuantity"
-                  :min="0"
-                  :max="getReturnQtyMax(item)"
-                  :step="1"
-                  @change="syncRowTotal(item)"
-                />
-              </view>
-              <view class="detail-row">
-                <text class="detail-label">閫�璐ф�讳环(鍏�)</text>
-                <text class="detail-value highlight">{{ formatAmount(item.taxInclusiveTotalPrice) }}</text>
-              </view>
+            <view class="product-form">
+              <up-form-item label="浜у搧澶х被">
+                <up-input :modelValue="item.productCategory || '-'"
+                          disabled />
+              </up-form-item>
+              <up-form-item label="瑙勬牸鍨嬪彿">
+                <up-input :modelValue="item.specificationModel || '-'"
+                          disabled />
+              </up-form-item>
+              <up-form-item label="鍏ュ簱鏁伴噺">
+                <up-input :modelValue="String(item.stockInNum ?? item.quantity ?? '-')"
+                          disabled />
+              </up-form-item>
+              <up-form-item label="鍙��璐ф暟閲�">
+                <up-input :modelValue="String(item.unQuantity ?? '-')"
+                          disabled />
+              </up-form-item>
+              <up-form-item label="鍚◣鍗曚环(鍏�)">
+                <up-input :modelValue="formatAmount(item.taxInclusiveUnitPrice)"
+                          disabled />
+              </up-form-item>
+              <up-form-item label="閫�璐ф暟閲�">
+                <up-number-box v-model="item.returnQuantity"
+                               :min="0"
+                               :max="getReturnQtyMax(item)"
+                               :step="1"
+                               @change="syncRowTotal(item)" />
+              </up-form-item>
+              <up-form-item label="閫�璐ф�讳环(鍏�)">
+                <up-input :modelValue="formatAmount(item.taxInclusiveTotalPrice)"
+                          disabled />
+              </up-form-item>
             </view>
           </view>
         </view>
-      </u-cell-group>
+      </view>
 
-      <u-cell-group title="璐圭敤淇℃伅" class="form-section">
-        <up-form-item label="鏁村崟鎶樻墸棰�" prop="totalDiscountAmount">
-          <up-input
-            v-model="form.totalDiscountAmount"
-            type="number"
-            placeholder="璇疯緭鍏�"
-            @blur="onDiscountAmountBlur"
-            clearable
-          />
-        </up-form-item>
-        <up-form-item label="鏁村崟鎶樻墸鐜�(%)" prop="totalDiscountRate">
-          <up-input
-            v-model="form.totalDiscountRate"
-            type="number"
-            placeholder="璇疯緭鍏�"
-            @blur="onDiscountRateBlur"
-            clearable
-          />
-        </up-form-item>
-        <up-form-item label="鎴愪氦閲戦" prop="totalAmount" required>
-          <up-input v-model="form.totalAmount" disabled placeholder="鑷姩璁$畻" />
-        </up-form-item>
-        <up-form-item label="鏀舵鏂瑰紡" prop="incomeType" required>
-          <up-input
-            v-model="incomeTypeText"
-            placeholder="璇烽�夋嫨"
-            readonly
-            @click="showIncomeTypeSheet = true"
-          />
-          <template #right>
-            <up-icon name="arrow-right" @click="showIncomeTypeSheet = true"></up-icon>
-          </template>
-        </up-form-item>
-      </u-cell-group>
+      <up-form-item label="鏁村崟鎶樻墸棰�"
+                    prop="totalDiscountAmount">
+        <up-input v-model="form.totalDiscountAmount"
+                  type="number"
+                  placeholder="璇疯緭鍏�"
+                  @blur="onDiscountAmountBlur"
+                  clearable />
+      </up-form-item>
+      <up-form-item label="鏁村崟鎶樻墸鐜�(%)"
+                    prop="totalDiscountRate">
+        <up-input v-model="form.totalDiscountRate"
+                  type="number"
+                  placeholder="璇疯緭鍏�"
+                  @blur="onDiscountRateBlur"
+                  clearable />
+      </up-form-item>
+      <up-form-item label="鎴愪氦閲戦"
+                    prop="totalAmount"
+                    required>
+        <up-input v-model="form.totalAmount"
+                  disabled
+                  placeholder="鑷姩璁$畻" />
+      </up-form-item>
+      <up-form-item label="鏀舵鏂瑰紡"
+                    prop="incomeType"
+                    required>
+        <up-input v-model="incomeTypeText"
+                  placeholder="璇烽�夋嫨"
+                  readonly
+                  @click="showIncomeTypeSheet = true" />
+        <template #right>
+          <up-icon name="arrow-right"
+                   @click="showIncomeTypeSheet = true"></up-icon>
+        </template>
+      </up-form-item>
     </up-form>
-
-    <FooterButtons :loading="loading" confirmText="鎻愪氦" @cancel="goBack" @confirm="handleSubmit" />
-
-    <up-action-sheet
-      :show="showReturnTypeSheet"
-      title="閫夋嫨閫�璐ф柟寮�"
-      :actions="returnTypeActions"
-      @select="onSelectReturnType"
-      @close="showReturnTypeSheet = false"
-    />
-    <up-action-sheet
-      :show="showProjectPhaseSheet"
-      title="閫夋嫨椤圭洰闃舵"
-      :actions="projectPhaseActions"
-      @select="onSelectProjectPhase"
-      @close="showProjectPhaseSheet = false"
-    />
-    <up-action-sheet
-      :show="showSupplierSheet"
-      title="閫夋嫨渚涘簲鍟�"
-      :actions="supplierActions"
-      @select="onSelectSupplier"
-      @close="showSupplierSheet = false"
-    />
-    <up-action-sheet
-      :show="showPreparedUserSheet"
-      title="閫夋嫨鍒跺崟浜�"
-      :actions="userActions"
-      @select="onSelectPreparedUser"
-      @close="showPreparedUserSheet = false"
-    />
-    <up-action-sheet
-      :show="showReturnUserSheet"
-      title="閫夋嫨閫�鏂欎汉"
-      :actions="userActions"
-      @select="onSelectReturnUser"
-      @close="showReturnUserSheet = false"
-    />
-    <up-action-sheet
-      :show="showPurchaseLedgerSheet"
-      title="閫夋嫨閲囪喘鍚堝悓鍙�"
-      :actions="purchaseLedgerActions"
-      @select="onSelectPurchaseLedger"
-      @close="showPurchaseLedgerSheet = false"
-    />
-    <up-action-sheet
-      :show="showIncomeTypeSheet"
-      title="閫夋嫨鏀舵鏂瑰紡"
-      :actions="incomeTypeActions"
-      @select="onSelectIncomeType"
-      @close="showIncomeTypeSheet = false"
-    />
-    <up-datetime-picker
-      :show="showPreparedAtPicker"
-      v-model="preparedAtPickerValue"
-      mode="date"
-      @confirm="onPreparedAtConfirm"
-      @cancel="showPreparedAtPicker = false"
-    />
+    <FooterButtons :loading="loading"
+                   confirmText="鎻愪氦"
+                   @cancel="goBack"
+                   @confirm="handleSubmit" />
+    <up-action-sheet :show="showReturnTypeSheet"
+                     title="閫夋嫨閫�璐ф柟寮�"
+                     :actions="returnTypeActions"
+                     @select="onSelectReturnType"
+                     @close="showReturnTypeSheet = false" />
+    <up-action-sheet :show="showProjectPhaseSheet"
+                     title="閫夋嫨椤圭洰闃舵"
+                     :actions="projectPhaseActions"
+                     @select="onSelectProjectPhase"
+                     @close="showProjectPhaseSheet = false" />
+    <up-action-sheet :show="showSupplierSheet"
+                     title="閫夋嫨渚涘簲鍟�"
+                     :actions="supplierActions"
+                     @select="onSelectSupplier"
+                     @close="showSupplierSheet = false" />
+    <up-action-sheet :show="showPreparedUserSheet"
+                     title="閫夋嫨鍒跺崟浜�"
+                     :actions="userActions"
+                     @select="onSelectPreparedUser"
+                     @close="showPreparedUserSheet = false" />
+    <up-action-sheet :show="showReturnUserSheet"
+                     title="閫夋嫨閫�鏂欎汉"
+                     :actions="userActions"
+                     @select="onSelectReturnUser"
+                     @close="showReturnUserSheet = false" />
+    <up-action-sheet :show="showPurchaseLedgerSheet"
+                     title="閫夋嫨閲囪喘鍚堝悓鍙�"
+                     :actions="purchaseLedgerActions"
+                     @select="onSelectPurchaseLedger"
+                     @close="showPurchaseLedgerSheet = false" />
+    <up-action-sheet :show="showIncomeTypeSheet"
+                     title="閫夋嫨鏀舵鏂瑰紡"
+                     :actions="incomeTypeActions"
+                     @select="onSelectIncomeType"
+                     @close="showIncomeTypeSheet = false" />
+    <up-datetime-picker :show="showPreparedAtPicker"
+                        v-model="preparedAtPickerValue"
+                        mode="date"
+                        @confirm="onPreparedAtConfirm"
+                        @cancel="showPreparedAtPicker = false" />
   </view>
 </template>
 
@@ -267,8 +273,11 @@
   import FooterButtons from "@/components/FooterButtons.vue";
   import { formatDateToYMD } from "@/utils/ruoyi";
   import { createPurchaseReturnOrder } from "@/api/procurementManagement/purchaseReturnOrder";
-  import { getOptions, purchaseListPage, productList } from "@/api/procurementManagement/procurementLedger";
-  import { userListNoPageByTenantId } from "@/api/system/user";
+  import {
+    getOptions,
+    purchaseList,
+  } from "@/api/procurementManagement/procurementLedger";
+  import { userListNoPage, userListNoPageByTenantId } from "@/api/system/user";
 
   const formRef = ref();
   const loading = ref(false);
@@ -296,14 +305,110 @@
   });
 
   const rules = {
-    returnType: [{ required: true, message: "璇烽�夋嫨閫�璐ф柟寮�", trigger: "change" }],
-    supplierId: [{ required: true, message: "璇烽�夋嫨渚涘簲鍟�", trigger: "change" }],
-    preparedAt: [{ required: true, message: "璇烽�夋嫨鍒朵綔鏃ユ湡", trigger: "change" }],
-    preparedUserId: [{ required: true, message: "璇烽�夋嫨鍒跺崟浜�", trigger: "change" }],
-    returnUserId: [{ required: true, message: "璇烽�夋嫨閫�鏂欎汉", trigger: "change" }],
-    purchaseLedgerId: [{ required: true, message: "璇烽�夋嫨閲囪喘鍚堝悓鍙�", trigger: "change" }],
-    totalAmount: [{ required: true, message: "鎴愪氦閲戦涓嶈兘涓虹┖", trigger: "change" }],
-    incomeType: [{ required: true, message: "璇烽�夋嫨鏀舵鏂瑰紡", trigger: "change" }],
+    returnType: [
+      {
+        validator: (_rule, value, callback) => {
+          if (
+            value === 0 ||
+            value === 1 ||
+            String(value) === "0" ||
+            String(value) === "1"
+          ) {
+            callback();
+            return;
+          }
+          callback(new Error("璇烽�夋嫨閫�璐ф柟寮�"));
+        },
+        trigger: ["change", "blur"],
+      },
+    ],
+    supplierName: [
+      {
+        validator: (_rule, value, callback) => {
+          if (value === null || value === undefined) {
+            callback(new Error("璇烽�夋嫨渚涘簲鍟�"));
+            return;
+          }
+          if (String(value).trim() === "") {
+            callback(new Error("璇烽�夋嫨渚涘簲鍟�"));
+            return;
+          }
+          callback();
+        },
+        trigger: ["change", "blur"],
+      },
+    ],
+    preparedAt: [
+      { required: true, message: "璇烽�夋嫨鍒朵綔鏃ユ湡", trigger: "change" },
+    ],
+    preparedUserName: [
+      {
+        validator: (_rule, value, callback) => {
+          if (value === null || value === undefined) {
+            callback(new Error("璇烽�夋嫨鍒跺崟浜�"));
+            return;
+          }
+          if (String(value).trim() === "") {
+            callback(new Error("璇烽�夋嫨鍒跺崟浜�"));
+            return;
+          }
+          callback();
+        },
+        trigger: ["change", "blur"],
+      },
+    ],
+    returnUserName: [
+      {
+        validator: (_rule, value, callback) => {
+          if (value === null || value === undefined) {
+            callback(new Error("璇烽�夋嫨閫�鏂欎汉"));
+            return;
+          }
+          if (String(value).trim() === "") {
+            callback(new Error("璇烽�夋嫨閫�鏂欎汉"));
+            return;
+          }
+          callback();
+        },
+        trigger: ["change", "blur"],
+      },
+    ],
+    purchaseContractNumber: [
+      {
+        validator: (_rule, value, callback) => {
+          if (value === null || value === undefined) {
+            callback(new Error("璇烽�夋嫨閲囪喘鍚堝悓鍙�"));
+            return;
+          }
+          if (String(value).trim() === "") {
+            callback(new Error("璇烽�夋嫨閲囪喘鍚堝悓鍙�"));
+            return;
+          }
+          callback();
+        },
+        trigger: ["change", "blur"],
+      },
+    ],
+    totalAmount: [
+      {
+        validator: (_rule, value, callback) => {
+          if (value === null || value === undefined || value === "") {
+            callback(new Error("鎴愪氦閲戦涓嶈兘涓虹┖"));
+            return;
+          }
+          const num = Number(value);
+          if (Number.isNaN(num)) {
+            callback(new Error("鎴愪氦閲戦鏍煎紡涓嶆纭�"));
+            return;
+          }
+          callback();
+        },
+        trigger: ["change", "blur"],
+      },
+    ],
+    incomeType: [
+      { required: true, message: "璇烽�夋嫨鏀舵鏂瑰紡", trigger: "change" },
+    ],
   };
 
   const showReturnTypeSheet = ref(false);
@@ -339,35 +444,69 @@
   ];
 
   const returnTypeText = computed(() => {
-    return returnTypeActions.find(i => String(i.value) === String(form.value.returnType))?.name || "";
+    return (
+      returnTypeActions.find(
+        i => String(i.value) === String(form.value.returnType)
+      )?.name || ""
+    );
   });
   const projectPhaseText = computed(() => {
-    return projectPhaseActions.find(i => String(i.value) === String(form.value.projectPhase))?.name || "";
+    return (
+      projectPhaseActions.find(
+        i => String(i.value) === String(form.value.projectPhase)
+      )?.name || ""
+    );
   });
   const supplierText = computed(() => {
-    return supplierOptions.value.find(i => String(i.id) === String(form.value.supplierId))?.supplierName || "";
+    return (
+      supplierOptions.value.find(
+        i => String(i.id) === String(form.value.supplierId)
+      )?.supplierName || ""
+    );
   });
   const preparedUserText = computed(() => {
-    return userOptions.value.find(i => String(i.userId) === String(form.value.preparedUserId))?.nickName || "";
+    return (
+      userOptions.value.find(
+        i => String(i.userId) === String(form.value.preparedUserId)
+      )?.nickName || ""
+    );
   });
   const returnUserText = computed(() => {
-    return userOptions.value.find(i => String(i.userId) === String(form.value.returnUserId))?.nickName || "";
+    return (
+      userOptions.value.find(
+        i => String(i.userId) === String(form.value.returnUserId)
+      )?.nickName || ""
+    );
   });
   const purchaseContractText = computed(() => {
-    return purchaseLedgerOptions.value.find(i => String(i.id) === String(form.value.purchaseLedgerId))?.purchaseContractNumber || "";
+    return (
+      purchaseLedgerOptions.value.find(
+        i => String(i.id) === String(form.value.purchaseLedgerId)
+      )?.purchaseContractNumber || ""
+    );
   });
   const incomeTypeText = computed(() => {
-    return incomeTypeActions.find(i => String(i.value) === String(form.value.incomeType))?.name || "";
+    return (
+      incomeTypeActions.find(
+        i => String(i.value) === String(form.value.incomeType)
+      )?.name || ""
+    );
   });
 
   const supplierActions = computed(() => {
-    return supplierOptions.value.map(i => ({ name: i.supplierName, value: i.id }));
+    return supplierOptions.value.map(i => ({
+      name: i.supplierName,
+      value: i.id,
+    }));
   });
   const userActions = computed(() => {
     return userOptions.value.map(i => ({ name: i.nickName, value: i.userId }));
   });
   const purchaseLedgerActions = computed(() => {
-    return purchaseLedgerOptions.value.map(i => ({ name: i.purchaseContractNumber, value: i.id }));
+    return purchaseLedgerOptions.value.map(i => ({
+      name: i.purchaseContractNumber,
+      value: i.id,
+    }));
   });
 
   const toNumber = val => {
@@ -384,7 +523,10 @@
 
   const baseAmount = computed(() => {
     const rows = form.value.purchaseReturnOrderProductsDtos || [];
-    return rows.reduce((sum, item) => sum + toNumber(item.taxInclusiveTotalPrice), 0);
+    return rows.reduce(
+      (sum, item) => sum + toNumber(item.taxInclusiveTotalPrice),
+      0
+    );
   });
 
   const syncTotalAmount = () => {
@@ -393,7 +535,7 @@
   };
 
   const getReturnQtyMax = row => {
-    const qty = Number(row?.quantity);
+    const qty = Number(row?.unQuantity ?? row?.quantity);
     if (Number.isNaN(qty) || qty < 0) return 0;
     return qty;
   };
@@ -432,7 +574,9 @@
   };
   const onSelectSupplier = action => {
     form.value.supplierId = action.value;
-    form.value.supplierName = supplierOptions.value.find(i => String(i.id) === String(action.value))?.supplierName || "";
+    form.value.supplierName =
+      supplierOptions.value.find(i => String(i.id) === String(action.value))
+        ?.supplierName || "";
     form.value.purchaseLedgerId = undefined;
     form.value.purchaseContractNumber = "";
     form.value.purchaseReturnOrderProductsDtos = [];
@@ -442,18 +586,23 @@
   };
   const onSelectPreparedUser = action => {
     form.value.preparedUserId = action.value;
-    form.value.preparedUserName = userOptions.value.find(i => String(i.userId) === String(action.value))?.nickName || "";
+    form.value.preparedUserName =
+      userOptions.value.find(i => String(i.userId) === String(action.value))
+        ?.nickName || "";
     showPreparedUserSheet.value = false;
   };
   const onSelectReturnUser = action => {
     form.value.returnUserId = action.value;
-    form.value.returnUserName = userOptions.value.find(i => String(i.userId) === String(action.value))?.nickName || "";
+    form.value.returnUserName =
+      userOptions.value.find(i => String(i.userId) === String(action.value))
+        ?.nickName || "";
     showReturnUserSheet.value = false;
   };
   const onSelectPurchaseLedger = action => {
     form.value.purchaseLedgerId = action.value;
     form.value.purchaseContractNumber =
-      purchaseLedgerOptions.value.find(i => String(i.id) === String(action.value))?.purchaseContractNumber || "";
+      purchaseLedgerOptions.value.find(i => String(i.id) === String(action.value))
+        ?.purchaseContractNumber || "";
     form.value.purchaseReturnOrderProductsDtos = [];
     resetFeeInfo();
     showPurchaseLedgerSheet.value = false;
@@ -474,7 +623,9 @@
       uni.showToast({ title: "鎶樻墸鐜囬渶鍦�0-100", icon: "none" });
       return;
     }
-    form.value.totalDiscountAmount = Number((baseAmount.value * (rate / 100)).toFixed(2));
+    form.value.totalDiscountAmount = Number(
+      (baseAmount.value * (rate / 100)).toFixed(2)
+    );
     syncTotalAmount();
   };
 
@@ -520,26 +671,45 @@
   };
 
   const fetchUserOptions = () => {
-    userListNoPageByTenantId()
+    return userListNoPageByTenantId()
       .then(res => {
-        userOptions.value = res.data || [];
+        const rows = res?.data || res?.rows || [];
+        if (Array.isArray(rows) && rows.length > 0) {
+          userOptions.value = rows;
+          return;
+        }
+        return userListNoPage().then(res2 => {
+          const rows2 = res2?.data || res2?.rows || [];
+          userOptions.value = Array.isArray(rows2) ? rows2 : [];
+        });
       })
       .catch(() => {
         userOptions.value = [];
       });
   };
 
+  const ensureUserOptions = async () => {
+    if (userOptions.value?.length) return;
+    await fetchUserOptions();
+  };
+
+  const openPreparedUserSheet = async () => {
+    await ensureUserOptions();
+    showPreparedUserSheet.value = true;
+  };
+
+  const openReturnUserSheet = async () => {
+    await ensureUserOptions();
+    showReturnUserSheet.value = true;
+  };
+
   const fetchPurchaseLedgerOptions = () => {
     purchaseLedgerOptions.value = [];
     if (!form.value.supplierId) return;
-    purchaseListPage({
-      current: -1,
-      size: -1,
-      supplierId: form.value.supplierId,
-      approvalStatus: 3,
-    })
+    purchaseList({ supplierId: form.value.supplierId, approvalStatus: 3 })
       .then(res => {
-        purchaseLedgerOptions.value = res?.data?.records || [];
+        const rows = res?.rows || res?.data?.records || res?.data || [];
+        purchaseLedgerOptions.value = Array.isArray(rows) ? rows : [];
       })
       .catch(() => {
         purchaseLedgerOptions.value = [];
@@ -547,33 +717,18 @@
   };
 
   const mergeSelectedProducts = selectedRows => {
-    const existing = new Set((form.value.purchaseReturnOrderProductsDtos || []).map(i => String(i.salesLedgerProductId || i.id)));
+    const existing = new Set(
+      (form.value.purchaseReturnOrderProductsDtos || []).map(i => String(i.id))
+    );
     const toAdd = (selectedRows || [])
       .filter(i => !existing.has(String(i.id)))
       .map(i => ({
         ...i,
-        salesLedgerProductId: i.id,
         returnQuantity: 0,
         taxInclusiveTotalPrice: 0,
       }));
     form.value.purchaseReturnOrderProductsDtos.push(...toAdd);
     syncTotalAmount();
-  };
-
-  const loadProductsFromPurchaseLedger = () => {
-    if (!form.value.purchaseLedgerId) return;
-    uni.showLoading({ title: "鍔犺浇浜у搧...", mask: true });
-    productList({ salesLedgerId: form.value.purchaseLedgerId, type: 2 })
-      .then(res => {
-        const rows = res.data || [];
-        mergeSelectedProducts(rows);
-      })
-      .catch(() => {
-        uni.showToast({ title: "鍔犺浇浜у搧澶辫触", icon: "error" });
-      })
-      .finally(() => {
-        uni.hideLoading();
-      });
   };
 
   const validateProducts = () => {
@@ -607,7 +762,12 @@
     });
     const payload = {
       ...form.value,
-      purchaseReturnOrderProductsDtos: rows.filter(i => toNumber(i.returnQuantity) > 0),
+      purchaseReturnOrderProductsDtos: rows
+        .filter(i => toNumber(i.returnQuantity) > 0)
+        .map(row => ({
+          ...row,
+          stockInRecordId: row.id,
+        })),
     };
     createPurchaseReturnOrder(payload)
       .then(() => {
@@ -649,110 +809,26 @@
       }
     }
   });
-
-  const showAutoLoadModalOnceKey = "purchaseReturnOrderAutoLoadShown";
-  watch(
-    () => form.value.purchaseLedgerId,
-    (val, oldVal) => {
-      if (!val || String(val) === String(oldVal)) return;
-      if (uni.getStorageSync(showAutoLoadModalOnceKey)) return;
-      uni.setStorageSync(showAutoLoadModalOnceKey, "1");
-      uni.showModal({
-        title: "鎻愮ず",
-        content: "鏄惁鑷姩鍔犺浇璇ラ噰璐悎鍚屼笅鍏ㄩ儴浜у搧锛�",
-        success: res => {
-          if (res.confirm) loadProductsFromPurchaseLedger();
-        },
-      });
-    }
-  );
 </script>
 
 <style scoped lang="scss">
-  @import "@/styles/procurement-common.scss";
+  @import "@/static/scss/form-common.scss";
 
-  .account-detail {
-    min-height: 100vh;
-    background: #f8f9fa;
-    padding-bottom: 90px;
-  }
-
-  .form-section {
-    margin: 12px;
-    border-radius: 12px;
-    overflow: hidden;
-  }
-
-  .product-actions {
+  .section-actions {
     display: flex;
     align-items: center;
-    justify-content: space-between;
-    padding: 12px 12px 0 12px;
-  }
-
-  .amount-summary {
-    display: flex;
-    align-items: center;
+    gap: 10px;
   }
 
   .amount-text {
-    font-size: 14px;
-    color: #333;
-    font-weight: 600;
-  }
-
-  .empty-products {
-    padding: 16px 12px;
-    color: #999;
-    font-size: 14px;
-  }
-
-  .product-list {
-    padding: 12px;
-    display: flex;
-    flex-direction: column;
-    gap: 12px;
-  }
-
-  .product-card {
-    background: #fff;
-    border-radius: 12px;
-    padding: 0 12px;
-    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
-  }
-
-  .product-header {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    padding: 12px 0;
-  }
-
-  .product-title {
-    display: flex;
-    align-items: center;
-    gap: 8px;
-  }
-
-  .product-name {
-    font-size: 14px;
-    color: #333;
+    font-size: 13px;
+    color: #2979ff;
     font-weight: 500;
   }
 
-  .product-body {
-    padding: 12px 0;
-  }
-
-  .qty-row {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    padding: 8px 0;
-  }
-
-  .qty-label {
-    font-size: 12px;
-    color: #777;
+  .empty-products {
+    padding: 8px 0 0 0;
+    color: #999;
+    font-size: 14px;
   }
 </style>

--
Gitblit v1.9.3