From 0dadd48fa23109f8bb4087654d8e2dad0cbd8f20 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期三, 15 四月 2026 13:28:00 +0800
Subject: [PATCH] 新增入库功能至销售台账页面,支持选择入库状态并执行入库操作,提升用户交互体验

---
 src/views/salesManagement/salesLedger/index.vue |  397 ++++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 322 insertions(+), 75 deletions(-)

diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index 1222b2b..0965cf1 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -41,6 +41,12 @@
             <el-option label="宸插彂璐�" :value="4" />
           </el-select>
         </el-form-item>
+        <el-form-item label="鍏ュ簱鐘舵�侊細">
+          <el-select v-model="searchForm.stockStatus" placeholder="璇烽�夋嫨" clearable style="width: 140px">
+            <el-option label="鏈叆搴�" :value="0" />
+            <el-option label="宸插叆搴�" :value="1" />
+          </el-select>
+        </el-form-item>
         <el-form-item>
           <el-button type="primary" @click="handleQuery"> 鎼滅储 </el-button>
         </el-form-item>
@@ -59,6 +65,7 @@
           @confirm="handleProcessFlowSelectConfirm"
         />
 			<el-space wrap>
+					<el-button type="primary" @click="handleSalesStock">鍏ュ簱</el-button>
 					<el-button type="primary" @click="openForm('add')">鏂板鍙拌处</el-button>
 					<el-button type="primary" @click="handleBulkDelivery">鍙戣揣</el-button>
 					<el-button type="primary" plain @click="handleImport">瀵煎叆</el-button>
@@ -204,6 +211,13 @@
 						<el-tag v-else type="info">-</el-tag>
 					</template>
 		  </el-table-column>
+		  <el-table-column label="鍏ュ簱鐘舵��" width="120" align="center">
+				<template #default="scope">
+						<el-tag v-if="Number(scope.row.stockStatus) === 0" type="info">鏈叆搴�</el-tag>
+						<el-tag v-else-if="Number(scope.row.stockStatus) === 1" type="success">宸插叆搴�</el-tag>
+						<el-tag v-else type="info">-</el-tag>
+					</template>
+		  </el-table-column>
         <el-table-column label="褰曞叆浜�" prop="entryPersonName" width="100" show-overflow-tooltip />
         <el-table-column label="褰曞叆鏃ユ湡" prop="entryDate" width="120" show-overflow-tooltip />
         <el-table-column label="绛捐鏃ユ湡" prop="executionDate" width="120" show-overflow-tooltip />
@@ -298,7 +312,14 @@
 				</el-row>
 				<el-row>
 					<el-form-item label="浜у搧淇℃伅锛�" prop="entryDate">
-						<el-button v-if="operationType !== 'view'" type="primary" @click="addProductInline">娣诲姞</el-button>
+						<el-button
+							v-if="operationType !== 'view'"
+							type="primary"
+							:disabled="hasEditingProductRow()"
+							@click="addProductInline"
+						>
+							娣诲姞
+						</el-button>
 						<el-button v-if="operationType !== 'view'" plain type="danger" @click="deleteProduct" >鍒犻櫎</el-button>
 					</el-form-item>
 				</el-row>
@@ -325,7 +346,7 @@
 							<span v-else>{{ scope.row.productCategory ?? "" }}</span>
 						</template>
 					</el-table-column>
-					<el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" min-width="140">
+					<el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" min-width="160">
 						<template #default="scope">
 							<el-select
 								v-if="scope.row.__editing"
@@ -333,7 +354,7 @@
 								placeholder="璇烽�夋嫨"
 								clearable
 								filterable
-								style="width: 140px"
+								style="width: 100%"
 								@change="(val) => handleInlineProductModelChange(scope.row, val)"
 							>
 								<el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
@@ -341,7 +362,7 @@
 							<span v-else>{{ scope.row.specificationModel ?? "" }}</span>
 						</template>
 					</el-table-column>
-					<el-table-column label="鍘氬害" prop="thickness" min-width="90">
+					<el-table-column label="鍘氬害(mm)" prop="thickness" min-width="160">
 						<template #default="scope">
 							<el-input-number
 								v-if="scope.row.__editing"
@@ -349,14 +370,14 @@
 								:min="0"
 								:step="0.000000000000001"
 								:precision="15"
-								style="width: 110px"
+								style="width: 100%"
 								placeholder="璇疯緭鍏�"
 								clearable
 							/>
 							<span v-else>{{ scope.row.thickness ?? "" }}</span>
 						</template>
 					</el-table-column>
-					<el-table-column label="瀹�(mm)" prop="width" min-width="80">
+					<el-table-column label="瀹�(mm)" prop="width" min-width="160">
 						<template #default="scope">
 							<el-input-number
 								v-if="scope.row.__editing"
@@ -364,15 +385,16 @@
 								:min="0"
 								:step="1"
 								:precision="2"
-								style="width: 110px"
+								style="width:100%"
 								placeholder="璇疯緭鍏�"
 								clearable
 								@change="() => handleInlineSizeChange(scope.row)"
+								@input="() => handleInlineSizeChange(scope.row)"
 							/>
 							<span v-else>{{ scope.row.width ?? "" }}</span>
 						</template>
 					</el-table-column>
-					<el-table-column label="楂�(mm)" prop="height" min-width="80">
+					<el-table-column label="楂�(mm)" prop="height" min-width="160">
 						<template #default="scope">
 							<el-input-number
 								v-if="scope.row.__editing"
@@ -380,30 +402,32 @@
 								:min="0"
 								:step="1"
 								:precision="2"
-								style="width: 110px"
+								style="width: 100%"
 								placeholder="璇疯緭鍏�"
 								clearable
 								@change="() => handleInlineSizeChange(scope.row)"
+								@input="() => handleInlineSizeChange(scope.row)"
 							/>
 							<span v-else>{{ scope.row.height ?? "" }}</span>
 						</template>
 					</el-table-column>
-					<el-table-column label="闈㈢Н(m虏)" prop="actualTotalArea" min-width="100">
+					<el-table-column label="缁撶畻鍗曠墖闈㈢Н(銕�)" prop="settlePieceArea" min-width="160">
 						<template #default="scope">
 							<el-input-number
 								v-if="scope.row.__editing"
-								v-model="scope.row.actualTotalArea"
+								v-model="scope.row.settlePieceArea"
 								:min="0"
 								:step="0.00001"
 								:precision="5"
-								style="width: 120px"
-								placeholder="鑷姩璁$畻"
-								:disabled="true"
+								style="width: 100%"
+								placeholder="璇疯緭鍏�"
+								clearable
+								@change="() => handleInlineSettleAreaChange(scope.row)"
 							/>
-							<span v-else>{{ scope.row.actualTotalArea ?? "" }}</span>
+							<span v-else>{{ scope.row.settlePieceArea ?? "" }}</span>
 						</template>
 					</el-table-column>
-					<el-table-column label="鏁伴噺" prop="quantity" min-width="90">
+					<el-table-column label="鏁伴噺" prop="quantity" min-width="150">
 						<template #default="scope">
 							<el-input-number
 								v-if="scope.row.__editing"
@@ -411,12 +435,27 @@
 								:step="0.1"
 								:min="0"
 								:precision="2"
-								style="width: 110px"
+								style="width: 100%"
 								placeholder="璇疯緭鍏�"
 								clearable
 								@change="() => handleInlineQuantityChange(scope.row)"
+								@input="() => handleInlineQuantityChange(scope.row)"
 							/>
 							<span v-else>{{ scope.row.quantity ?? "" }}</span>
+						</template>
+					</el-table-column>
+					<el-table-column label="闈㈢Н(m虏)" prop="actualTotalArea" min-width="160">
+						<template #default="scope">
+							<el-input-number
+								v-if="scope.row.__editing"
+								v-model="scope.row.actualTotalArea"
+								:min="0"
+								:step="0.00001"
+								:precision="5"
+								style="width: 100%"
+								placeholder="鑷姩璁$畻"
+							/>
+							<span v-else>{{ scope.row.actualTotalArea ?? "" }}</span>
 						</template>
 					</el-table-column>
 					<el-table-column label="鍚◣鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" min-width="140">
@@ -426,23 +465,24 @@
 								:step="0.01"
 								:min="0"
 								:precision="2"
-								style="width: 120px"
+								style="width: 100%"
 								v-model="scope.row.taxInclusiveUnitPrice"
 								placeholder="璇疯緭鍏�"
 								clearable
 								@change="() => handleInlineUnitPriceChange(scope.row)"
+								@input="() => handleInlineUnitPriceChange(scope.row)"
 							/>
 							<span v-else>{{ formattedNumber(null, null, scope.row.taxInclusiveUnitPrice ?? 0) }}</span>
 						</template>
 					</el-table-column>
-					<el-table-column label="绋庣巼(%)" prop="taxRate" min-width="90">
+					<el-table-column label="绋庣巼(%)" prop="taxRate" min-width="120">
 						<template #default="scope">
 							<el-select
 								v-if="scope.row.__editing"
 								v-model="scope.row.taxRate"
 								placeholder="璇烽�夋嫨"
 								clearable
-								style="width: 90px"
+								style="width: 100%"
 								@change="() => handleInlineTaxRateChange(scope.row)"
 							>
 								<el-option label="1" value="1" />
@@ -454,8 +494,8 @@
 							<span v-else>{{ scope.row.taxRate ?? "" }}</span>
 						</template>
 					</el-table-column>
-					<el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
-					<el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
+					<el-table-column label="鍚◣鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber"  min-width="120"/>
+					<el-table-column label="涓嶅惈绋庢�讳环(鍏�)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber"  min-width="120"/>
 					<el-table-column label="鍙戠エ绫诲瀷" prop="invoiceType" min-width="120">
 						<template #default="scope">
 							<el-select
@@ -463,7 +503,7 @@
 								v-model="scope.row.invoiceType"
 								placeholder="璇烽�夋嫨"
 								clearable
-								style="width: 120px"
+								style="width: 100%"
 							>
 								<el-option label="澧炴櫘绁�" value="澧炴櫘绁�" />
 								<el-option label="澧炰笓绁�" value="澧炰笓绁�" />
@@ -471,22 +511,7 @@
 							<span v-else>{{ scope.row.invoiceType ?? "" }}</span>
 						</template>
 					</el-table-column>
-					<el-table-column label="缁撶畻鍗曠墖闈㈢Н(銕�)" prop="settlePieceArea" min-width="140">
-						<template #default="scope">
-							<el-input-number
-								v-if="scope.row.__editing"
-								v-model="scope.row.settlePieceArea"
-								:min="0"
-								:step="0.00001"
-								:precision="5"
-								style="width: 140px"
-								placeholder="璇疯緭鍏�"
-								clearable
-								@change="() => handleInlineSettleAreaChange(scope.row)"
-							/>
-							<span v-else>{{ scope.row.settlePieceArea ?? "" }}</span>
-						</template>
-					</el-table-column>
+
 					<el-table-column label="鍔犲伐瑕佹眰" prop="processRequirement" min-width="160" show-overflow-tooltip>
 						<template #default="scope">
 							<el-input
@@ -494,7 +519,7 @@
 								v-model="scope.row.processRequirement"
 								placeholder="璇疯緭鍏�"
 								clearable
-								style="width: 160px"
+								style="width: 100%"
 							/>
 							<span v-else>{{ scope.row.processRequirement ?? "" }}</span>
 						</template>
@@ -506,7 +531,7 @@
 								v-model="scope.row.remark"
 								placeholder="璇疯緭鍏�"
 								clearable
-								style="width: 140px"
+								style="width: 100%"
 							/>
 							<span v-else>{{ scope.row.remark ?? "" }}</span>
 						</template>
@@ -518,7 +543,7 @@
 								v-model="scope.row.floorCode"
 								placeholder="璇疯緭鍏�"
 								clearable
-								style="width: 140px"
+								style="width: 100%"
 							/>
 							<span v-else>{{ scope.row.floorCode ?? "" }}</span>
 						</template>
@@ -530,7 +555,7 @@
 								v-model="scope.row.heavyBox"
 								placeholder="璇疯緭鍏�"
 								clearable
-								style="width: 110px"
+								style="width: 100%"
 							/>
 							<span v-else>{{ scope.row.heavyBox ?? "" }}</span>
 						</template>
@@ -541,10 +566,11 @@
 								<el-button link type="primary" size="small" @click="saveProductInline(scope.row, scope.$index)">淇濆瓨</el-button>
 								<el-button link type="danger" size="small" @click="cancelProductInline(scope.row, scope.$index)">鍙栨秷</el-button>
 								<el-popover
-									:width="420"
+									:width="560"
 									trigger="click"
 									:hide-after="0"
-									v-model:visible="scope.row.__otherAmountPopoverVisible"
+									:visible="scope.row.__otherAmountPopoverVisible"
+									@update:visible="(val) => handleOtherAmountPopoverVisibleChange(scope.row, val)"
 								>
 									<template #reference>
 										<el-button
@@ -565,14 +591,50 @@
 											鏂板
 										</el-button>
 									</div>
+									<div
+										v-if="scope.row.__inlineOtherAmountAdding"
+										style="display:flex; flex-direction:column; gap: 8px; margin-bottom: 10px;"
+										@click.stop
+									>
+										<el-select
+											v-model="scope.row.__inlineOtherAmountAddId"
+											filterable
+											clearable
+											placeholder="璇烽�夋嫨鍏朵粬閲戦椤圭洰"
+											style="width: 100%;"
+										>
+											<el-option
+												v-for="item in otherAmountSelectOptions"
+												:key="item.id"
+												:label="item.processName"
+												:value="item.id"
+											/>
+										</el-select>
+										<div style="display:flex; justify-content:flex-end; gap: 8px;">
+											<el-button
+												size="small"
+												@click="scope.row.__inlineOtherAmountAdding = false; scope.row.__inlineOtherAmountAddId = null"
+											>
+												鍙栨秷
+											</el-button>
+											<el-button
+												type="primary"
+												size="small"
+												:disabled="scope.row.__inlineOtherAmountAddId === null || scope.row.__inlineOtherAmountAddId === undefined || scope.row.__inlineOtherAmountAddId === ''"
+												@click="confirmAddOtherAmountForRow(scope.row)"
+											>
+												纭娣诲姞
+											</el-button>
+										</div>
+									</div>
 
 									<div v-if="Array.isArray(scope.row.salesProductProcessList) && scope.row.salesProductProcessList.length > 0"
-										style="display:flex; flex-direction:column; gap: 8px;"
+										style="display:flex; flex-wrap:wrap; gap: 8px;"
 									>
 										<div
 											v-for="(item, idx) in scope.row.salesProductProcessList"
 											:key="String(item.id) + '_' + idx"
-											style="display:flex; align-items:center; gap: 8px;"
+											style="display:flex; align-items:center; gap: 8px; flex: 0 0 calc(50% - 4px); max-width: calc(50% - 4px);"
 										>
 											<el-tag type="info" style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
 												{{ item.processName }}
@@ -608,10 +670,11 @@
 									缂栬緫
 								</el-button>
 								<el-popover
-									:width="420"
+									:width="560"
 									trigger="click"
 									:hide-after="0"
-									v-model:visible="scope.row.__otherAmountPopoverVisible"
+									:visible="scope.row.__otherAmountPopoverVisible"
+									@update:visible="(val) => handleOtherAmountPopoverVisibleChange(scope.row, val)"
 								>
 									<template #reference>
 										<el-button
@@ -639,14 +702,52 @@
 											鏂板
 										</el-button>
 									</div>
+									<div
+										v-if="scope.row.__inlineOtherAmountAdding"
+										style="display:flex; flex-direction:column; gap: 8px; margin-bottom: 10px;"
+										@click.stop
+									>
+										<el-select
+											v-model="scope.row.__inlineOtherAmountAddId"
+											filterable
+											clearable
+											placeholder="璇烽�夋嫨鍏朵粬閲戦椤圭洰"
+											style="width: 100%;"
+											:disabled="isProductShipped(scope.row)"
+										>
+											<el-option
+												v-for="item in otherAmountSelectOptions"
+												:key="item.id"
+												:label="item.processName"
+												:value="item.id"
+											/>
+										</el-select>
+										<div style="display:flex; justify-content:flex-end; gap: 8px;">
+											<el-button
+												size="small"
+												:disabled="isProductShipped(scope.row)"
+												@click="scope.row.__inlineOtherAmountAdding = false; scope.row.__inlineOtherAmountAddId = null"
+											>
+												鍙栨秷
+											</el-button>
+											<el-button
+												type="primary"
+												size="small"
+												:disabled="isProductShipped(scope.row) || scope.row.__inlineOtherAmountAddId === null || scope.row.__inlineOtherAmountAddId === undefined || scope.row.__inlineOtherAmountAddId === ''"
+												@click="confirmAddOtherAmountForRow(scope.row)"
+											>
+												纭娣诲姞
+											</el-button>
+										</div>
+									</div>
 
 									<div v-if="Array.isArray(scope.row.salesProductProcessList) && scope.row.salesProductProcessList.length > 0"
-										style="display:flex; flex-direction:column; gap: 8px;"
+										style="display:flex; flex-wrap:wrap; gap: 8px;"
 									>
 										<div
 											v-for="(item, idx) in scope.row.salesProductProcessList"
 											:key="String(item.id) + '_' + idx"
-											style="display:flex; align-items:center; gap: 8px;"
+											style="display:flex; align-items:center; gap: 8px; flex: 0 0 calc(50% - 4px); max-width: calc(50% - 4px);"
 										>
 											<el-tag type="info" style="max-width: 170px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
 												{{ item.processName }}
@@ -1278,7 +1379,7 @@
 <script setup>
 import { getToken } from "@/utils/auth";
 import pagination from "@/components/PIMTable/Pagination.vue";
-import {onMounted, ref, getCurrentInstance, watch} from "vue";
+import {onMounted, ref, getCurrentInstance, watch, nextTick} from "vue";
 import { addShippingInfo } from "@/api/salesManagement/deliveryLedger.js";
 import { ElMessageBox, ElMessage } from "element-plus";
 import { ArrowDown } from "@element-plus/icons-vue";
@@ -1308,6 +1409,7 @@
 	getSalesOrder,
 	getSalesInvoices,
 	getSalesLabel,
+	salesStock,
 } from "@/api/salesManagement/salesLedger.js";
 import { modelList, productTreeList } from "@/api/basicData/product.js";
 import useFormData from "@/hooks/useFormData.js";
@@ -1356,6 +1458,7 @@
 		entryDateStart: undefined,
 		entryDateEnd: undefined,
 		deliveryStatus: undefined, // 鍙戣揣鐘舵�侊細1鏈彂璐� 2瀹℃壒涓� 3瀹℃壒澶辫触 4宸插彂璐�
+		stockStatus: undefined, // 鍏ュ簱鐘舵�侊細0鏈叆搴� 1宸插叆搴�
 	},
 	form: {
 		salesContractNo: "",
@@ -1436,6 +1539,8 @@
 	if (!row || typeof row !== "object") return;
 	if (!Array.isArray(row.salesProductProcessList)) row.salesProductProcessList = [];
 	if (row.__otherAmountPopoverVisible === undefined || row.__otherAmountPopoverVisible === null) row.__otherAmountPopoverVisible = false;
+	if (row.__inlineOtherAmountAdding === undefined || row.__inlineOtherAmountAdding === null) row.__inlineOtherAmountAdding = false;
+	if (row.__inlineOtherAmountAddId === undefined) row.__inlineOtherAmountAddId = null;
 	if (row.width === undefined || row.width === null) row.width = 0;
 	if (row.height === undefined || row.height === null) row.height = 0;
 	if (row.perimeter === undefined || row.perimeter === null) row.perimeter = 0;
@@ -1461,10 +1566,16 @@
 	editingProductRow.value = null;
 };
 
+const hasEditingProductRow = () => {
+	return (productData.value || []).some((r) => r && r.__editing);
+};
+
 const addProductInline = async () => {
 	if (operationType.value === "view") return;
-	// 宸叉湁琛屽湪缂栬緫鏃讹紝鍏堝彇娑堝叾缂栬緫鐘舵�侊紝閬垮厤娣蜂贡
-	stopOtherEditingRows();
+	if (hasEditingProductRow()) {
+		proxy.$modal.msgWarning("璇峰厛淇濆瓨鎴栧彇娑堝綋鍓嶇紪杈戣");
+		return;
+	}
 	await getProductOptions();
 	await fetchOtherAmountSelectOptions(true);
 	const row = {
@@ -1591,6 +1702,9 @@
 
 	// 鎻愪氦鍓嶅厹搴曡绠椾竴娆★紙娌跨敤鍘熼�昏緫锛�
 	recalcAreaTotals();
+	// 鎻愪氦鍏滃簳锛氱◣鐜�/鏁伴噺鏈~鏃舵寜鏁板瓧 0 浼犻��
+	row.taxRate = Number(row.taxRate ?? 0) || 0;
+	row.quantity = Number(row.quantity ?? 0) || 0;
 
 	// 瑙勮寖鍖栧叾浠栭噾棰濇彁浜ょ粨鏋�
 	row.salesProductProcessList = (Array.isArray(row.salesProductProcessList) ? row.salesProductProcessList : [])
@@ -1655,11 +1769,43 @@
 	}
 	ensureProductRowDefaults(row);
 	productForm.value = row;
+	otherAmountAddTargetRow.value = row;
 	await fetchOtherAmountSelectOptions(true);
 	mergeOtherAmountOptionsBySelection(row.salesProductProcessList);
 	row.salesProductProcessList = fillOtherAmountProcessName(row.salesProductProcessList);
 	// 鍙仛鏁版嵁鍑嗗涓庢墦寮�娴眰锛堟柊澧炵敱娴眰鍐呮寜閽Е鍙戯級
 	row.__otherAmountPopoverVisible = true;
+};
+
+const keepOtherAmountPopoverOpenKey = ref(null);
+const keepOtherAmountPopoverOpenUntil = ref(0);
+
+const getOtherAmountRowKey = (row) => String(row?.__tempKey ?? row?.id ?? "");
+
+const lockOtherAmountPopoverOpen = (row, durationMs = 1200) => {
+	const key = getOtherAmountRowKey(row);
+	if (!key) return;
+	keepOtherAmountPopoverOpenKey.value = key;
+	keepOtherAmountPopoverOpenUntil.value = Date.now() + durationMs;
+};
+
+const handleOtherAmountPopoverVisibleChange = (row, visible) => {
+	if (!row) return;
+	if (visible) {
+		row.__otherAmountPopoverVisible = true;
+		return;
+	}
+	if (row.__inlineOtherAmountAdding) {
+		row.__otherAmountPopoverVisible = true;
+		return;
+	}
+	const key = getOtherAmountRowKey(row);
+	const shouldKeepOpen = Boolean(
+		key &&
+		keepOtherAmountPopoverOpenKey.value === key &&
+		Date.now() < keepOtherAmountPopoverOpenUntil.value
+	);
+	row.__otherAmountPopoverVisible = shouldKeepOpen;
 };
 
 const startAddOtherAmountForRow = async (row) => {
@@ -1674,7 +1820,36 @@
 	await fetchOtherAmountSelectOptions(true);
 	mergeOtherAmountOptionsBySelection(row.salesProductProcessList);
 	row.salesProductProcessList = fillOtherAmountProcessName(row.salesProductProcessList);
-	startAddOtherAmount();
+	row.__inlineOtherAmountAddId = null;
+	row.__inlineOtherAmountAdding = true;
+	row.__otherAmountPopoverVisible = true;
+};
+
+const confirmAddOtherAmountForRow = (row) => {
+	if (!row) return;
+	ensureProductRowDefaults(row);
+	productForm.value = row;
+	const selectedId = row.__inlineOtherAmountAddId;
+	if (selectedId === null || selectedId === undefined || selectedId === "") return;
+	const opt = otherAmountSelectOptions.value.find((o) => String(o.id) === String(selectedId));
+	if (!opt) return;
+	const exists = (row.salesProductProcessList ?? []).some(
+		(it) => String(it?.id) === String(opt.id)
+	);
+	if (exists) {
+		proxy.$modal.msgWarning("璇ュ叾浠栭噾棰濋」鐩凡娣诲姞");
+		return;
+	}
+	row.salesProductProcessList.push({
+		id: opt.id,
+		processName: opt.processName,
+		unitPrice: opt.unitPrice ?? 0,
+		quantity: 0,
+	});
+	row.__inlineOtherAmountAddId = null;
+	row.__inlineOtherAmountAdding = false;
+	row.__otherAmountPopoverVisible = true;
+	calculateFromUnitPrice(true);
 };
 
 const removeOtherAmountAtForRow = (row, index) => {
@@ -1892,6 +2067,8 @@
 // 鍏朵粬閲戦锛氱偣鍑烩�滄柊澧炩�濆悗鍦ㄥ脊绐楅噷閫夋嫨涓�涓」鐩�
 const otherAmountAddDialogVisible = ref(false);
 const otherAmountAddId = ref(null);
+const otherAmountAddTargetRow = ref(null);
+const otherAmountAddTargetRowKey = ref(null);
 
 const startAddOtherAmount = () => {
 	if (operationType.value === "view") return;
@@ -1906,6 +2083,10 @@
 const cancelAddOtherAmount = () => {
 	otherAmountAddDialogVisible.value = false;
 	otherAmountAddId.value = null;
+	otherAmountAddTargetRow.value = null;
+	otherAmountAddTargetRowKey.value = null;
+	keepOtherAmountPopoverOpenKey.value = null;
+	keepOtherAmountPopoverOpenUntil.value = 0;
 };
 
 const handleOtherAmountSelected = (id) => {
@@ -1930,9 +2111,30 @@
 	});
 	calculateFromUnitPrice(true);
 
-	// 閫夋嫨瀹屾垚鍚庡叧闂脊绐楋紝涓嬩竴娆″彲鍐嶆鐐瑰嚮鈥滄柊澧炩�濈户缁坊鍔�
+	// 閫夋嫨瀹屾垚鍚庡叧闂�滄柊澧炲叾浠栭噾棰濃�濆脊绐楋紝骞朵繚鎸佽鍐呪�滃叾浠栭噾棰濃�濆脊灞傚紑鍚紝渚夸簬鐩存帴濉啓鏁伴噺
 	otherAmountAddDialogVisible.value = false;
 	otherAmountAddId.value = null;
+	const reopenOtherAmountPopover = () => {
+		let targetRow = otherAmountAddTargetRow.value;
+		const rowKey = otherAmountAddTargetRowKey.value;
+		if (rowKey) {
+			const matchedRow = (productData.value || []).find(
+				(it) => String(it?.__tempKey ?? it?.id ?? "") === rowKey
+			);
+			if (matchedRow) targetRow = matchedRow;
+		}
+		if (targetRow && typeof targetRow === "object") {
+			lockOtherAmountPopoverOpen(targetRow, 1500);
+			targetRow.__otherAmountPopoverVisible = true;
+		}
+	};
+	nextTick(() => {
+		reopenOtherAmountPopover();
+		setTimeout(reopenOtherAmountPopover, 0);
+		setTimeout(reopenOtherAmountPopover, 80);
+	});
+	otherAmountAddTargetRow.value = null;
+	otherAmountAddTargetRowKey.value = null;
 };
 
 const confirmAddOtherAmount = () => {
@@ -2066,6 +2268,43 @@
 		.catch(() => {
 			tableLoading.value = false;
 		});
+};
+
+// 鍏ュ簱锛堥攢鍞彴璐� -> 鍏ュ簱鐘舵�侊級
+const handleSalesStock = async () => {
+	if (selectedRows.value.length !== 1) {
+		ElMessage.warning("璇峰嬀閫変竴鏉″彴璐︽暟鎹繘琛屽叆搴�");
+		return;
+	}
+	const row = selectedRows.value[0] || {};
+	const id = row?.id;
+	if (!id) {
+		ElMessage.warning("鎵�閫夋暟鎹己灏慽d锛屾棤娉曞叆搴�");
+		return;
+	}
+	if (Number(row.stockStatus) === 1) {
+		ElMessage.info("璇ュ彴璐﹀凡鍏ュ簱锛屾棤闇�閲嶅鎿嶄綔");
+		return;
+	}
+	try {
+		await ElMessageBox.confirm("纭瀵规墍閫夊彴璐︽墽琛屽叆搴擄紵", "鎻愮ず", {
+			confirmButtonText: "纭畾",
+			cancelButtonText: "鍙栨秷",
+			type: "warning",
+		});
+	} catch {
+		return;
+	}
+	proxy?.$modal?.loading?.("姝e湪鍏ュ簱锛岃绋嶅��...");
+	try {
+		await salesStock({ id });
+		proxy?.$modal?.msgSuccess?.("鍏ュ簱鎴愬姛");
+		await getList();
+	} catch (e) {
+		proxy?.$modal?.msgError?.("鍏ュ簱澶辫触锛岃绋嶅悗閲嶈瘯");
+	} finally {
+		proxy?.$modal?.closeLoading?.();
+	}
 };
 
 // 鎵撳紑鈥滃伐鑹鸿矾绾块厤缃�濋�夋嫨寮圭獥锛堝繀椤绘樉寮忛�夋嫨锛�
@@ -2213,15 +2452,18 @@
 	}
 	return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
 };
-function convertIdToValue(data) {
+function convertIdToValue(data, level = 0) {
 	return data.map((item) => {
 		const { id, children, ...rest } = item;
+		const hasChildren = Array.isArray(children) && children.length > 0;
 		const newItem = {
 			...rest,
 			value: id, // 灏� id 鏀逛负 value
+			// 浠呭厑璁稿彾瀛愯妭鐐硅閫夋嫨锛堟湁瀛愯妭鐐圭殑鍒嗙被鑺傜偣缁熶竴绂佺敤锛�
+			disabled: Boolean(rest?.disabled) || hasChildren,
 		};
-		if (children && children.length > 0) {
-			newItem.children = convertIdToValue(children);
+		if (hasChildren) {
+			newItem.children = convertIdToValue(children, level + 1);
 		}
 		
 		return newItem;
@@ -2505,6 +2747,8 @@
 				const cleanedProducts = (productData.value || []).map((p) => {
 					if (!p || typeof p !== "object") return p;
 					const { __editing, __isNew, __backup, __productCategoryId, __tempKey, __otherAmountPopoverVisible, ...rest } = p;
+					rest.taxRate = Number(rest.taxRate ?? 0) || 0;
+					rest.quantity = Number(rest.quantity ?? 0) || 0;
 					return rest;
 				});
 				form.value.productData = proxy.HaveJson(cleanedProducts);
@@ -2625,6 +2869,9 @@
 
 			// 闈㈢Н/鎬昏瀛楁鍦ㄦ彁浜ゅ墠鍏滃簳璁$畻涓�娆�
 			recalcAreaTotals();
+			// 鎻愪氦鍏滃簳锛氱◣鐜�/鏁伴噺鏈~鏃舵寜鏁板瓧 0 浼犻��
+			productForm.value.taxRate = Number(productForm.value.taxRate ?? 0) || 0;
+			productForm.value.quantity = Number(productForm.value.quantity ?? 0) || 0;
 			// 鍏朵粬閲戦鍙彁浜� {id, processName, quantity}锛堝悗绔瓧娈碉細salesProductProcessList锛�
 			productForm.value.salesProductProcessList = (Array.isArray(productForm.value.salesProductProcessList)
 			? productForm.value.salesProductProcessList
@@ -3060,10 +3307,10 @@
 
 // 鏍规嵁涓嶅惈绋庢�讳环璁$畻鍚◣鍗曚环鍜屾暟閲�
 const calculateFromExclusiveTotalPrice = () => {
-	if (!productForm.value.taxRate) {
-		proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
-		return;
-	}
+	// if (!productForm.value.taxRate) {
+	// 	proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+	// 	return;
+	// }
 	if (isCalculating.value) return;
 	
 	const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice);
@@ -3093,10 +3340,10 @@
 
 // 鏍规嵁鏁伴噺鍙樺寲璁$畻鎬讳环
 const calculateFromQuantity = () => {
-	if (!productForm.value.taxRate) {
-		proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
-		return;
-	}
+	// if (!productForm.value.taxRate) {
+	// 	proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+	// 	return;
+	// }
 	if (isCalculating.value) return;
 
 	const quantity = parseFloat(productForm.value.quantity);
@@ -3130,10 +3377,10 @@
 
 // 鏍规嵁鍚◣鍗曚环鍙樺寲璁$畻鎬讳环
 const calculateFromUnitPrice = (silent = false) => {
-	if (!productForm.value.taxRate) {
-		if (!silent) proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
-		return;
-	}
+	// if (!productForm.value.taxRate) {
+	// 	if (!silent) proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+	// 	return;
+	// }
 	if (isCalculating.value) return;
 
 	const quantity = parseFloat(productForm.value.quantity);
@@ -3167,10 +3414,10 @@
 
 // 鏍规嵁绋庣巼鍙樺寲璁$畻涓嶅惈绋庢�讳环
 const calculateFromTaxRate = () => {
-	if (!productForm.value.taxRate) {
-		proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
-		return;
-	}
+	// if (!productForm.value.taxRate) {
+	// 	proxy.$modal.msgWarning("璇峰厛閫夋嫨绋庣巼");
+	// 	return;
+	// }
 	if (isCalculating.value) return;
 	
 	const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);

--
Gitblit v1.9.3