From b8de520fa696d8a95393aa3ff544c5a6f5c36001 Mon Sep 17 00:00:00 2001
From: 吴羡 <16976746+Xian-TT3@user.noreply.gitee.com>
Date: 星期三, 30 九月 2026 15:33:41 +0800
Subject: [PATCH] 宁夏万通需求:出库单行支持主/辅单位录入;物料型号维护;考勤节假日配置时间段;设备批量删除;质检导出按筛选条件

---
 src/api/hrm/attendance/holiday/index.ts                  |    5 
 src/views/mes/qc/iqc/index.vue                           |   21 
 src/views/mes/dv/machinery/index.vue                     |   26 
 src/views/wls/itemreceipt/modules/line-form.vue          |   34 
 src/api/mes/dv/machinery/index.ts                        |    5 
 src/views/erp/purchase/order/data.ts                     |    2 
 src/views/wls/miscissue/modules/line-form.vue            |   33 
 src/views/wls/productsales/modules/line-form.vue         |   34 
 src/views/hrm/attendance/holiday/data.ts                 |   16 
 src/views/erp/purchase/request/data.ts                   |    2 
 src/views/mes/qc/rqc/index.vue                           |   21 
 src/views/basicData/mdm/components/item-model-config.vue |  211 ++++++
 src/api/mes/wm/productissue/line/index.ts                |   10 
 src/views/wls/utils/item-unit-convert.ts                 |  435 ++++++++++++++
 src/api/mdm/item-model/index.ts                          |   46 +
 src/api/mes/wm/miscissue/line/index.ts                   |   10 
 src/api/mes/wm/itemreceipt/line/index.ts                 |   10 
 src/views/hrm/attendance/holiday/index.vue               |    5 
 src/api/mes/wm/returnvendor/line/index.ts                |   10 
 src/views/hrm/attendance/holiday/modules/form.vue        |   19 
 src/views/basicData/mdm/modules/form.vue                 |   18 
 src/views/wls/itemreceipt/data.ts                        |  394 ++++++++++++
 src/views/wls/returnvendor/modules/line-form.vue         |   33 
 src/views/mes/qc/ipqc/index.vue                          |   21 
 src/views/wls/outsourceissue/data.ts                     |   47 +
 src/views/wls/productissue/data.ts                       |   45 
 /dev/null                                                |   89 --
 src/views/mes/qc/oqc/index.vue                           |   21 
 src/views/wls/outsourceissue/modules/line-form.vue       |   36 
 src/api/mes/wm/outsourceissue/line/index.ts              |   10 
 src/api/mes/wm/productsales/line/index.ts                |   10 
 src/views/wls/miscissue/data.ts                          |   51 
 src/views/wls/productissue/modules/line-form.vue         |   36 
 src/views/wls/returnvendor/data.ts                       |   43 
 src/views/wls/productsales/data.ts                       |   45 +
 35 files changed, 1,536 insertions(+), 318 deletions(-)

diff --git a/src/api/hrm/attendance/holiday/index.ts b/src/api/hrm/attendance/holiday/index.ts
index 89da4ec..b030fac 100644
--- a/src/api/hrm/attendance/holiday/index.ts
+++ b/src/api/hrm/attendance/holiday/index.ts
@@ -7,7 +7,10 @@
   export interface Holiday {
     id?: number;
     name?: string;
-    date?: string;
+    /** 寮�濮嬫棩鏈� */
+    startDate?: string;
+    /** 缁撴潫鏃ユ湡 */
+    endDate?: string;
     type?: number;
     typeName?: string;
     isLegal?: number;
diff --git a/src/api/mdm/item-model/index.ts b/src/api/mdm/item-model/index.ts
new file mode 100644
index 0000000..c738626
--- /dev/null
+++ b/src/api/mdm/item-model/index.ts
@@ -0,0 +1,46 @@
+import { requestClient } from '#/api/request';
+
+export namespace MdmItemModelApi {
+  /** 鐗╂枡鍨嬪彿锛堝瀷鍙峰悕绉� + 鍨嬪彿璁¢噺鍗曚綅 + 鎹㈢畻姣旂巼锛� */
+  export interface ItemModel {
+    id?: number; // 鍨嬪彿缂栧彿
+    itemId?: number; // 鐗╂枡缂栧彿
+    itemCode?: string; // 鐗╂枡缂栫爜
+    itemName?: string; // 鐗╂枡鍚嶇О
+    model?: string; // 鍨嬪彿鍚嶇О
+    unitMeasureId?: number; // 鍨嬪彿璁¢噺鍗曚綅缂栧彿锛堣緟鍗曚綅锛�
+    unitMeasureName?: string; // 鍨嬪彿璁¢噺鍗曚綅鍚嶇О
+    unitMeasureRate?: number; // 鎹㈢畻姣旂巼锛�1 鍨嬪彿璁¢噺鍗曚綅 = N 鐗╂枡涓昏閲忓崟浣�
+    status?: number; // 鐘舵�侊紙0姝e父 1鍋滅敤锛�
+    remark?: string; // 澶囨敞
+    createTime?: string; // 鍒涘缓鏃堕棿
+  }
+}
+
+/** 鏌ヨ鏌愮墿鏂欑殑鍨嬪彿鍒楄〃 */
+export function getItemModelListByItem(itemId: number) {
+  return requestClient.get<MdmItemModelApi.ItemModel[]>(
+    '/mdm/item-model/list-by-item',
+    { params: { itemId } },
+  );
+}
+
+/** 鎵归噺鏌ヨ澶氫釜鐗╂枡鐨勫瀷鍙峰垪琛� */
+export function getItemModelListByItems(itemIds: number[]) {
+  return requestClient.get<MdmItemModelApi.ItemModel[]>(
+    '/mdm/item-model/list-by-items',
+    { params: { itemIds: itemIds.join(',') } },
+  );
+}
+
+/** 鍏ㄩ噺瑕嗙洊淇濆瓨鏌愮墿鏂欑殑鍨嬪彿鍒楄〃锛堝厛鍒犲悗鎻掞級 */
+export function saveItemModelList(
+  itemId: number,
+  list: MdmItemModelApi.ItemModel[],
+) {
+  return requestClient.post<boolean>(
+    '/mdm/item-model/save-list',
+    list,
+    { params: { itemId } },
+  );
+}
diff --git a/src/api/mes/dv/machinery/index.ts b/src/api/mes/dv/machinery/index.ts
index c46b39f..93c5b94 100644
--- a/src/api/mes/dv/machinery/index.ts
+++ b/src/api/mes/dv/machinery/index.ts
@@ -70,6 +70,11 @@
   return requestClient.delete(`/mes/dv/machinery/delete?id=${id}`);
 }
 
+/** 鎵归噺鍒犻櫎璁惧 */
+export function deleteMachineryList(ids: number[]) {
+  return requestClient.delete(`/mes/dv/machinery/delete-list?ids=${ids.join(',')}`);
+}
+
 /** 瀵煎嚭璁惧 */
 export function exportMachinery(params: any) {
   return requestClient.download('/mes/dv/machinery/export-excel', { params });
diff --git a/src/api/mes/wm/itemreceipt/line/index.ts b/src/api/mes/wm/itemreceipt/line/index.ts
index 8ce7236..e7327b1 100644
--- a/src/api/mes/wm/itemreceipt/line/index.ts
+++ b/src/api/mes/wm/itemreceipt/line/index.ts
@@ -14,8 +14,14 @@
     itemCode?: string; // 鐗╂枡缂栫爜
     itemName?: string; // 鐗╂枡鍚嶇О
     specification?: string; // 瑙勬牸鍨嬪彿
-    unitMeasureName?: string; // 鍗曚綅
-    receivedQuantity?: number; // 鍏ュ簱鏁伴噺
+    unitMeasureId?: number; // 涓昏閲忓崟浣嶇紪鍙凤紙鐗╂枡涓诲崟浣嶏級
+    unitMeasureName?: string; // 涓昏閲忓崟浣嶅悕绉帮紙鐗╂枡涓诲崟浣嶏級
+    itemModelId?: number; // 鐗╂枡鍨嬪彿缂栧彿
+    itemModel?: string; // 鐗╂枡鍨嬪彿鍚嶇О
+    enterUnitMeasureId?: number; // 褰曞叆鍗曚綅缂栧彿
+    enterUnitMeasureName?: string; // 褰曞叆鍗曚綅鍚嶇О
+    enterQuantity?: number; // 褰曞叆鏁伴噺锛堟寜褰曞叆鍗曚綅璁★級
+    receivedQuantity?: number; // 鍏ュ簱鏁伴噺锛堢墿鏂欎富鍗曚綅鍙e緞锛岄�夎緟鍗曚綅褰曞叆鏃剁敱鍚庣鎹㈢畻锛�
     batchId?: number; // 鎵规缂栧彿
     batchCode?: string; // 鎵规鍙�
     productionDate?: number; // 鐢熶骇鏃ユ湡
diff --git a/src/api/mes/wm/miscissue/line/index.ts b/src/api/mes/wm/miscissue/line/index.ts
index 56f4d89..c54d5e7 100644
--- a/src/api/mes/wm/miscissue/line/index.ts
+++ b/src/api/mes/wm/miscissue/line/index.ts
@@ -13,9 +13,15 @@
     itemCode?: string; // 鐗╂枡缂栫爜
     itemName?: string; // 鐗╂枡鍚嶇О
     specification?: string; // 瑙勬牸鍨嬪彿
+    unitMeasureId?: number; // 涓昏閲忓崟浣嶇紪鍙凤紙鐗╂枡涓诲崟浣嶏級
     unitMeasure?: string; // 璁¢噺鍗曚綅
-    unitMeasureName?: string; // 璁¢噺鍗曚綅鍚嶇О
-    quantity?: number; // 鍑哄簱鏁伴噺
+    unitMeasureName?: string; // 涓昏閲忓崟浣嶅悕绉帮紙鐗╂枡涓诲崟浣嶏級
+    itemModelId?: number; // 鐗╂枡鍨嬪彿缂栧彿
+    itemModel?: string; // 鐗╂枡鍨嬪彿鍚嶇О
+    enterUnitMeasureId?: number; // 褰曞叆鍗曚綅缂栧彿
+    enterUnitMeasureName?: string; // 褰曞叆鍗曚綅鍚嶇О
+    enterQuantity?: number; // 褰曞叆鏁伴噺锛堟寜褰曞叆鍗曚綅璁★級
+    quantity?: number; // 鍑哄簱鏁伴噺锛堢墿鏂欎富鍗曚綅鍙e緞锛岄�夎緟鍗曚綅褰曞叆鏃剁敱鍚庣鎹㈢畻锛�
     batchId?: number; // 鎵规缂栧彿
     batchCode?: string; // 鎵规鍙�
     warehouseId?: number; // 浠撳簱缂栧彿
diff --git a/src/api/mes/wm/outsourceissue/line/index.ts b/src/api/mes/wm/outsourceissue/line/index.ts
index 68fae8f..f3b099f 100644
--- a/src/api/mes/wm/outsourceissue/line/index.ts
+++ b/src/api/mes/wm/outsourceissue/line/index.ts
@@ -11,8 +11,14 @@
     itemCode?: string; // 鐗╂枡缂栫爜
     itemName?: string; // 鐗╂枡鍚嶇О
     specification?: string; // 瑙勬牸鍨嬪彿
-    unitMeasureName?: string; // 璁¢噺鍗曚綅鍚嶇О
-    quantity?: number; // 鍙戞枡鏁伴噺
+    unitMeasureId?: number; // 涓昏閲忓崟浣嶇紪鍙凤紙鐗╂枡涓诲崟浣嶏級
+    unitMeasureName?: string; // 涓昏閲忓崟浣嶅悕绉�
+    itemModelId?: number; // 鐗╂枡鍨嬪彿缂栧彿
+    itemModel?: string; // 鐗╂枡鍨嬪彿鍚嶇О
+    enterUnitMeasureId?: number; // 褰曞叆鍗曚綅缂栧彿
+    enterUnitMeasureName?: string; // 褰曞叆鍗曚綅鍚嶇О
+    enterQuantity?: number; // 褰曞叆鏁伴噺锛堟寜褰曞叆鍗曚綅璁★級
+    quantity?: number; // 鍙戞枡鏁伴噺锛堢墿鏂欎富鍗曚綅鍙e緞锛屾寜褰曞叆鍗曚綅褰曞叆鏃剁敱鍚庣鎹㈢畻锛�
     materialStockId?: number; // 搴撳瓨缂栧彿
     batchId?: number; // 鎵规缂栧彿
     batchCode?: string; // 鎵规缂栫爜
diff --git a/src/api/mes/wm/productissue/line/index.ts b/src/api/mes/wm/productissue/line/index.ts
index 9b79b2f..4fdb12b 100644
--- a/src/api/mes/wm/productissue/line/index.ts
+++ b/src/api/mes/wm/productissue/line/index.ts
@@ -12,8 +12,14 @@
     itemCode?: string; // 鐗╂枡缂栫爜
     itemName?: string; // 鐗╂枡鍚嶇О
     specification?: string; // 瑙勬牸鍨嬪彿
-    unitMeasureName?: string; // 鍗曚綅
-    quantity?: number; // 棰嗘枡鏁伴噺
+    unitMeasureId?: number; // 涓昏閲忓崟浣嶇紪鍙凤紙鐗╂枡涓诲崟浣嶏級
+    unitMeasureName?: string; // 涓昏閲忓崟浣嶅悕绉�
+    itemModelId?: number; // 鐗╂枡鍨嬪彿缂栧彿
+    itemModel?: string; // 鐗╂枡鍨嬪彿鍚嶇О
+    enterUnitMeasureId?: number; // 褰曞叆鍗曚綅缂栧彿
+    enterUnitMeasureName?: string; // 褰曞叆鍗曚綅鍚嶇О
+    enterQuantity?: number; // 褰曞叆鏁伴噺锛堟寜褰曞叆鍗曚綅璁★級
+    quantity?: number; // 棰嗘枡鏁伴噺锛堢墿鏂欎富鍗曚綅鍙e緞锛屾寜褰曞叆鍗曚綅褰曞叆鏃剁敱鍚庣鎹㈢畻锛�
     batchId?: number; // 鎵规缂栧彿
     remark?: string; // 澶囨敞
   }
diff --git a/src/api/mes/wm/productsales/line/index.ts b/src/api/mes/wm/productsales/line/index.ts
index 1f8e46a..a27dd41 100644
--- a/src/api/mes/wm/productsales/line/index.ts
+++ b/src/api/mes/wm/productsales/line/index.ts
@@ -13,8 +13,14 @@
     itemCode?: string; // 鐗╂枡缂栫爜
     itemName?: string; // 鐗╂枡鍚嶇О
     specification?: string; // 瑙勬牸鍨嬪彿
-    unitMeasureName?: string; // 鍗曚綅
-    quantity?: number; // 鍑哄簱鏁伴噺
+    unitMeasureId?: number; // 涓昏閲忓崟浣嶇紪鍙凤紙鐗╂枡涓诲崟浣嶏級
+    unitMeasureName?: string; // 涓昏閲忓崟浣嶅悕绉�
+    itemModelId?: number; // 鐗╂枡鍨嬪彿缂栧彿
+    itemModel?: string; // 鐗╂枡鍨嬪彿鍚嶇О
+    enterUnitMeasureId?: number; // 褰曞叆鍗曚綅缂栧彿
+    enterUnitMeasureName?: string; // 褰曞叆鍗曚綅鍚嶇О
+    enterQuantity?: number; // 褰曞叆鏁伴噺锛堟寜褰曞叆鍗曚綅璁★級
+    quantity?: number; // 鍑哄簱鏁伴噺锛堢墿鏂欎富鍗曚綅鍙e緞锛屾寜褰曞叆鍗曚綅褰曞叆鏃剁敱鍚庣鎹㈢畻锛�
     pickedQuantity?: number; // 宸叉嫞璐ф暟閲�
     batchId?: number; // 鎵规缂栧彿
     batchCode?: string; // 鎵规鍙�
diff --git a/src/api/mes/wm/returnvendor/line/index.ts b/src/api/mes/wm/returnvendor/line/index.ts
index 568c6ae..d59d7f0 100644
--- a/src/api/mes/wm/returnvendor/line/index.ts
+++ b/src/api/mes/wm/returnvendor/line/index.ts
@@ -11,8 +11,14 @@
     itemCode?: string; // 鐗╂枡缂栫爜
     itemName?: string; // 鐗╂枡鍚嶇О
     specification?: string; // 瑙勬牸鍨嬪彿
-    unitMeasureName?: string; // 鍗曚綅
-    quantity?: number; // 閫�璐ф暟閲�
+    unitMeasureId?: number; // 涓昏閲忓崟浣嶇紪鍙凤紙鐗╂枡涓诲崟浣嶏級
+    unitMeasureName?: string; // 涓昏閲忓崟浣嶅悕绉�
+    itemModelId?: number; // 鐗╂枡鍨嬪彿缂栧彿
+    itemModel?: string; // 鐗╂枡鍨嬪彿鍚嶇О
+    enterUnitMeasureId?: number; // 褰曞叆鍗曚綅缂栧彿
+    enterUnitMeasureName?: string; // 褰曞叆鍗曚綅鍚嶇О
+    enterQuantity?: number; // 褰曞叆鏁伴噺锛堟寜褰曞叆鍗曚綅璁★級
+    quantity?: number; // 閫�璐ф暟閲忥紙鐗╂枡涓诲崟浣嶅彛寰勶紝鎸夊綍鍏ュ崟浣嶅綍鍏ユ椂鐢卞悗绔崲绠楋級
     batchId?: number; // 鎵规缂栧彿
     batchCode?: string; // 鎵规鍙�
     remark?: string; // 澶囨敞
diff --git a/src/views/basicData/mdm/components/item-model-config.vue b/src/views/basicData/mdm/components/item-model-config.vue
new file mode 100644
index 0000000..e97b11f
--- /dev/null
+++ b/src/views/basicData/mdm/components/item-model-config.vue
@@ -0,0 +1,211 @@
+<script lang="ts" setup>
+import type { MdmItemModelApi } from '#/api/mdm/item-model';
+
+import { ref } from 'vue';
+
+import {
+  Button,
+  Divider,
+  Empty,
+  Input,
+  InputNumber,
+  Popconfirm,
+  Select,
+  Spin,
+  Table,
+} from 'ant-design-vue';
+
+import {
+  getItemModelListByItem,
+  saveItemModelList,
+} from '#/api/mdm/item-model';
+import { getUnitPage } from '#/api/mdm/unit';
+
+/** 琛屽唴缂栬緫闇�瑕佺ǔ瀹氱殑琛屾爣璇嗭紝鏁呭湪鎺ュ彛鏁版嵁涓婅ˉ涓�涓湰鍦� _key */
+type ModelRow = MdmItemModelApi.ItemModel & { _key: number };
+
+const rows = ref<ModelRow[]>([]);
+const units = ref<{ label: string; value: number }[]>([]);
+const loading = ref(false);
+let rowKey = 0;
+
+const columns = [
+  { title: '鍨嬪彿鍚嶇О', dataIndex: 'model', width: 200 },
+  { title: '鍨嬪彿璁¢噺鍗曚綅', dataIndex: 'unitMeasureId', width: 170 },
+  { title: '鎹㈢畻姣旂巼', dataIndex: 'unitMeasureRate', width: 200 },
+  { title: '澶囨敞', dataIndex: 'remark' },
+  { title: '鎿嶄綔', dataIndex: 'action', width: 70, align: 'center' as const },
+];
+
+/** 鍔犺浇璁¢噺鍗曚綅涓嬫媺閫夐」 */
+async function loadUnits() {
+  if (units.value.length > 0) {
+    return;
+  }
+  const res = await getUnitPage({ pageNo: 1, pageSize: 100, status: 0 });
+  units.value = (res.list || []).map((unit) => ({
+    label: unit.name,
+    value: unit.id!,
+  }));
+}
+
+/** 鍔犺浇鎸囧畾鐗╂枡宸查厤缃殑鍨嬪彿 */
+async function load(itemId?: number) {
+  if (!itemId) {
+    reset();
+    return;
+  }
+  loading.value = true;
+  try {
+    await loadUnits();
+    const list = await getItemModelListByItem(itemId);
+    rows.value = (list || []).map((model) => ({
+      ...model,
+      _key: ++rowKey,
+    }));
+  } finally {
+    loading.value = false;
+  }
+}
+
+/** 鏂板涓�琛屽瀷鍙� */
+async function handleAdd() {
+  await loadUnits();
+  rows.value.push({
+    _key: ++rowKey,
+    model: '',
+    unitMeasureId: undefined,
+    unitMeasureRate: undefined,
+    status: 0,
+    remark: '',
+  });
+}
+
+/** 鍒犻櫎鏌愯鍨嬪彿 */
+function handleRemove(key: number) {
+  rows.value = rows.value.filter((row) => row._key !== key);
+}
+
+/** 娓呯┖ */
+function reset() {
+  rows.value = [];
+}
+
+/** 鍨嬪彿璁¢噺鍗曚綅鍙樺寲鏃讹紝鍚屾鍗曚綅鍚嶇О */
+function handleUnitChange(row: ModelRow, value: unknown) {
+  row.unitMeasureName = units.value.find((u) => u.value === value)?.label;
+}
+
+/** 鍗曚綅涓嬫媺鏀寔鎸夊悕绉版悳绱� */
+function filterUnitOption(input: string, option: { label?: string }) {
+  return String(option?.label ?? '')
+    .toLowerCase()
+    .includes(input.toLowerCase());
+}
+
+/** 鏍¢獙鍨嬪彿鍒楄〃锛岃繑鍥炵涓�澶勯棶棰橈紱鏃犻棶棰樿繑鍥� undefined */
+function validate(): string | undefined {
+  const names = new Set<string>();
+  for (const [index, row] of rows.value.entries()) {
+    const label = `绗� ${index + 1} 琛宍;
+    const name = row.model?.trim();
+    if (!name) {
+      return `${label}鐨勫瀷鍙峰悕绉颁笉鑳戒负绌猴紝璇峰~鍐欏瀷鍙峰悕绉癭;
+    }
+    if (names.has(name)) {
+      return `${label}鐨勫瀷鍙峰悕绉般��${name}銆戜笌鍓嶉潰濉啓閲嶅锛屽悓涓�鐗╂枡涓嬪瀷鍙峰悕绉颁笉鑳介噸鍚嶏紝璇蜂慨鏀筦;
+    }
+    names.add(name);
+    if (row.unitMeasureId == null) {
+      return `${label}鍨嬪彿銆�${name}銆戣繕鏈�夋嫨鍨嬪彿璁¢噺鍗曚綅锛岃閫夋嫨璇ュ瀷鍙峰疄闄呮敹鍙戣揣浣跨敤鐨勫崟浣峘;
+    }
+    if (row.unitMeasureRate == null || row.unitMeasureRate <= 0) {
+      return `${label}鍨嬪彿銆�${name}銆戠殑鎹㈢畻姣旂巼鏈~鍐欐垨涓嶅ぇ浜� 0锛岃濉啓 1 涓瀷鍙疯閲忓崟浣嶅搴斿灏戜釜鐗╂枡涓诲崟浣峘;
+    }
+  }
+  return undefined;
+}
+
+/** 鍏ㄩ噺瑕嗙洊淇濆瓨鍨嬪彿鍒楄〃 */
+async function save(itemId: number) {
+  const payload = rows.value.map((row) => ({
+    id: row.id,
+    itemId,
+    model: row.model!.trim(),
+    unitMeasureId: row.unitMeasureId,
+    unitMeasureRate: row.unitMeasureRate,
+    status: row.status ?? 0,
+    remark: row.remark,
+  }));
+  await saveItemModelList(itemId, payload);
+}
+
+defineExpose({ load, reset, validate, save });
+</script>
+
+<template>
+  <div class="mx-4 mt-2">
+    <Divider orientation="left">鍨嬪彿閰嶇疆锛堜富杈呭崟浣嶆崲绠楋級</Divider>
+    <p class="mb-3 text-xs text-gray-400">
+      鍚屼竴鐗╂枡涓嶅悓鍨嬪彿鍙娇鐢ㄤ笉鍚岀殑璁¢噺鍗曚綅涓庢崲绠楁瘮鐜囥�傛崲绠楁瘮鐜囧惈涔夛細1 涓瀷鍙疯閲忓崟浣� = N 涓墿鏂欎富鍗曚綅锛堜緥锛氬瀷鍙�
+      銆�4绫�/鏍广�嶇殑璁¢噺鍗曚綅濉�屾牴銆嶃�佹崲绠楁瘮鐜囧~ 4锛岃〃绀� 1 鏍� = 4 绫筹級銆傚嚭鍏ュ簱鏃跺彲鎸夊瀷鍙烽�夋嫨杩欎簺鍗曚綅褰曞叆锛岀郴缁熻嚜鍔ㄦ崲绠椾负鐗╂枡涓诲崟浣嶆暟閲忚璐︺��
+    </p>
+    <Button class="mb-3" type="primary" @click="handleAdd">娣诲姞鍨嬪彿</Button>
+    <Spin :spinning="loading">
+      <div>
+      <Table
+        v-if="rows.length > 0"
+        :columns="columns"
+        :data-source="rows"
+        :pagination="false"
+        row-key="_key"
+        size="small"
+        bordered
+      >
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.dataIndex === 'model'">
+            <Input
+              v-model:value="record.model"
+              :maxlength="128"
+              placeholder="濡傦細4绫�/鏍�"
+            />
+          </template>
+          <template v-else-if="column.dataIndex === 'unitMeasureId'">
+            <Select
+              v-model:value="record.unitMeasureId"
+              :filter-option="filterUnitOption"
+              :options="units"
+              allow-clear
+              class="w-full"
+              placeholder="濡傦細鏍�"
+              show-search
+              @change="(value) => handleUnitChange(record, value)"
+            />
+          </template>
+          <template v-else-if="column.dataIndex === 'unitMeasureRate'">
+            <InputNumber
+              v-model:value="record.unitMeasureRate"
+              :min="0"
+              :precision="6"
+              class="!w-full"
+              placeholder="1 鍨嬪彿鍗曚綅 = N 涓诲崟浣�"
+            />
+          </template>
+          <template v-else-if="column.dataIndex === 'remark'">
+            <Input v-model:value="record.remark" :maxlength="500" />
+          </template>
+          <template v-else-if="column.dataIndex === 'action'">
+            <Popconfirm
+              title="纭鍒犻櫎璇ュ瀷鍙峰悧锛�"
+              @confirm="handleRemove(record._key)"
+            >
+              <Button danger size="small" type="link">鍒犻櫎</Button>
+            </Popconfirm>
+          </template>
+        </template>
+      </Table>
+      <Empty v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" description="鏆傛棤鍨嬪彿锛屽闇�鎸夎緟鍗曚綅锛堝鏍广�佽銆佺锛夋敹鍙戣揣锛岃娣诲姞鍨嬪彿" />
+      </div>
+    </Spin>
+  </div>
+</template>
diff --git a/src/views/basicData/mdm/modules/form.vue b/src/views/basicData/mdm/modules/form.vue
index 5d82e00..7aa7c27 100644
--- a/src/views/basicData/mdm/modules/form.vue
+++ b/src/views/basicData/mdm/modules/form.vue
@@ -13,11 +13,13 @@
 
 import { useFormSchema, clearUnitListCache, buildCategoryTree } from '../data';
 import { getItemTypeSimpleList } from '#/api/mes/md/item/type';
+import ItemModelConfig from '../components/item-model-config.vue';
 
 defineOptions({ name: "ItemForm" });
 
 const emit = defineEmits(['success']);
 const formData = ref<MdmItemApi.Item>();
+const itemModelRef = ref<InstanceType<typeof ItemModelConfig>>();
 
 const [Form, formApi] = useVbenForm({
   commonConfig: {
@@ -38,10 +40,20 @@
     if (!valid) {
       return;
     }
+    // 鍨嬪彿鍏堟牎楠岋紝閬垮厤鐗╂枡宸蹭繚瀛樿�屽瀷鍙锋湭淇濆瓨
+    const modelError = itemModelRef.value?.validate();
+    if (modelError) {
+      message.error(modelError);
+      return;
+    }
     modalApi.lock();
     const data = (await formApi.getValues()) as MdmItemApi.Item;
     try {
-      await (formData.value?.id ? updateItem(data) : createItem(data));
+      const itemId = formData.value?.id ?? (await createItem(data));
+      if (formData.value?.id) {
+        await updateItem(data);
+      }
+      await itemModelRef.value?.save(itemId);
       await modalApi.close();
       emit('success');
       message.success($t('ui.actionMessage.operationSuccess'));
@@ -52,6 +64,7 @@
   async onOpenChange(isOpen: boolean) {
     if (!isOpen) {
       formData.value = undefined;
+      itemModelRef.value?.reset();
       return;
     }
     const data = modalApi.getData<MdmItemApi.Item>();
@@ -70,6 +83,7 @@
         // 鍚屾鐘舵�佹槧灏勶細璇︽儏鎺ュ彛杩斿洖 syncedMes锛岃〃鍗曞瓧娈典负 syncMes
         formData.value.syncMes = formData.value.syncedMes ?? true;
         await formApi.setValues(formData.value);
+        await itemModelRef.value?.load(data.id);
       } finally {
         modalApi.unlock();
       }
@@ -77,6 +91,7 @@
       // 鏂板妯″紡
       formData.value = undefined;
       await formApi.resetForm();
+      itemModelRef.value?.reset();
     }
   },
 });
@@ -85,5 +100,6 @@
 <template>
   <Modal class="w-[1000px]" :title="formData?.id ? $t('ui.actionTitle.edit', ['鐗╂枡']) : $t('ui.actionTitle.create', ['鐗╂枡'])">
     <Form class="mx-4" />
+    <ItemModelConfig ref="itemModelRef" />
   </Modal>
 </template>
diff --git a/src/views/erp/purchase/order/data.ts b/src/views/erp/purchase/order/data.ts
index 0676202..426aa21 100644
--- a/src/views/erp/purchase/order/data.ts
+++ b/src/views/erp/purchase/order/data.ts
@@ -33,7 +33,7 @@
       label: '璁㈠崟鍗曞彿',
       component: 'Input',
       componentProps: {
-        placeholder: '绯荤粺鑷姩鐢熸垚',
+        placeholder: '璁㈠崟鍙疯嚜瀹氫箟',
         disabled: true,
       },
     },
diff --git a/src/views/erp/purchase/request/data.ts b/src/views/erp/purchase/request/data.ts
index ac87d16..81d0d93 100644
--- a/src/views/erp/purchase/request/data.ts
+++ b/src/views/erp/purchase/request/data.ts
@@ -36,7 +36,7 @@
       label: '鐢宠缂栧彿',
       component: 'Input',
       componentProps: {
-        placeholder: '绯荤粺鑷姩鐢熸垚',
+        placeholder: '绯荤粺鑷畾涔夌紪鍙�',
         disabled: true,
       },
     },
diff --git a/src/views/hrm/attendance/holiday/data.ts b/src/views/hrm/attendance/holiday/data.ts
index 822451b..9ae34ac 100644
--- a/src/views/hrm/attendance/holiday/data.ts
+++ b/src/views/hrm/attendance/holiday/data.ts
@@ -29,11 +29,10 @@
       rules: 'required',
     },
     {
-      fieldName: 'date',
-      label: '鏃ユ湡',
-      component: 'DatePicker',
+      fieldName: 'dateRange',
+      label: '鏃ユ湡鍖洪棿',
+      component: 'RangePicker',
       componentProps: {
-        placeholder: '閫夋嫨鏃ユ湡',
         valueFormat: 'YYYY-MM-DD',
         style: { width: '100%' },
       },
@@ -126,7 +125,14 @@
 export function useGridColumns(): VxeTableGridOptions<HrmAttendanceHolidayApi.Holiday>['columns'] {
   return [
     { field: 'name', title: '鑺傚亣鏃ュ悕绉�', minWidth: 150 },
-    { field: 'date', title: '鏃ユ湡', width: 120 },
+    {
+      field: 'startDate',
+      title: '鏃ユ湡鍖洪棿',
+      minWidth: 200,
+      slots: {
+        default: 'dateRange',
+      },
+    },
     {
       field: 'type',
       title: '绫诲瀷',
diff --git a/src/views/hrm/attendance/holiday/index.vue b/src/views/hrm/attendance/holiday/index.vue
index 8a33871..e5e0b51 100644
--- a/src/views/hrm/attendance/holiday/index.vue
+++ b/src/views/hrm/attendance/holiday/index.vue
@@ -114,6 +114,11 @@
           ]"
         />
       </template>
+      <template #dateRange="{ row }">
+        <span v-if="!row.startDate && !row.endDate">-</span>
+        <span v-else-if="row.startDate === row.endDate">{{ row.startDate }}</span>
+        <span v-else>{{ row.startDate }} ~ {{ row.endDate }}</span>
+      </template>
       <template #isLegal="{ row }">
         <Tag v-if="row.isLegal === 1" color="red">娉曞畾</Tag>
         <span v-else>-</span>
diff --git a/src/views/hrm/attendance/holiday/modules/form.vue b/src/views/hrm/attendance/holiday/modules/form.vue
index 2ae9c5d..614daa0 100644
--- a/src/views/hrm/attendance/holiday/modules/form.vue
+++ b/src/views/hrm/attendance/holiday/modules/form.vue
@@ -53,7 +53,15 @@
       return;
     }
     modalApi.lock();
-    const data = (await formApi.getValues()) as HrmAttendanceHolidayApi.Holiday;
+    const values = (await formApi.getValues()) as HrmAttendanceHolidayApi.Holiday & {
+      dateRange?: string[];
+    };
+    const { dateRange, ...rest } = values;
+    const data: HrmAttendanceHolidayApi.Holiday = {
+      ...rest,
+      startDate: dateRange?.[0],
+      endDate: dateRange?.[1],
+    };
     try {
       if (formType.value === 'update') {
         await updateAttendanceHoliday(data);
@@ -81,7 +89,14 @@
     }
     modalApi.lock();
     try {
-      await formApi.setValues(await getAttendanceHoliday(data.id));
+      const detail = await getAttendanceHoliday(data.id);
+      await formApi.setValues({
+        ...detail,
+        dateRange:
+          detail.startDate && detail.endDate
+            ? [detail.startDate, detail.endDate]
+            : undefined,
+      });
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/mes/dv/machinery/index.vue b/src/views/mes/dv/machinery/index.vue
index 8546a19..06aa887 100644
--- a/src/views/mes/dv/machinery/index.vue
+++ b/src/views/mes/dv/machinery/index.vue
@@ -13,6 +13,7 @@
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
 import {
   deleteMachinery,
+  deleteMachineryList,
   exportMachinery,
   getMachineryPage,
 } from '#/api/mes/dv/machinery';
@@ -31,6 +32,15 @@
   records: MesDvMachineryApi.Machinery[];
 }) {
   checkedIds.value = records.map((item) => item.id!);
+}
+
+/**
+ * 琛ㄦ牸鏁版嵁閲嶈浇鍚庯紙鏌ヨ/缈婚〉/鍒锋柊/鍒囨崲璁惧绫诲瀷锛夋竻绌哄嬀閫夛紝
+ * 閬垮厤鍕鹃�変簡涓婁竴椤电殑璁惧鍚庯紝椤甸潰宸插埛鏂颁絾浠嶆寜鏃у嬀閫夊垹闄�
+ */
+function handleDataChange() {
+  checkedIds.value = [];
+  gridApi.grid.clearCheckboxRow();
 }
 
 const [FormModal, formModalApi] = useVbenModal({
@@ -79,17 +89,21 @@
 
 /** 鎵归噺鍒犻櫎璁惧 */
 async function handleDeleteBatch() {
+  if (checkedIds.value.length === 0) {
+    return;
+  }
+  const count = checkedIds.value.length;
   const hideLoading = message.loading({
-    content: $t('ui.actionMessage.deleting', [`${checkedIds.value.length} 鍙癭]),
+    content: $t('ui.actionMessage.deleting', [`${count} 鍙癭]),
     duration: 0,
   });
   try {
-    await Promise.all(checkedIds.value.map((id) => deleteMachinery(id)));
-    message.success($t('ui.actionMessage.deleteSuccess', ['']));
-    checkedIds.value = [];
-    handleRefresh();
+    await deleteMachineryList(checkedIds.value);
+    message.success($t('ui.actionMessage.deleteSuccess', [`${count} 鍙癭]));
   } finally {
     hideLoading();
+    handleDataChange();
+    handleRefresh();
   }
 }
 
@@ -143,6 +157,7 @@
   gridEvents: {
     checkboxAll: handleRowCheckboxChange,
     checkboxChange: handleRowCheckboxChange,
+    dataChange: handleDataChange,
   },
 });
 </script>
@@ -174,6 +189,7 @@
                   auth: ['mes:dv-machinery:delete'],
                   disabled: checkedIds.length === 0,
                   popConfirm: {
+                    disabled: checkedIds.length === 0,
                     title: $t('ui.actionMessage.deleteConfirm', [
                       `${checkedIds.length} 鍙癭,
                     ]),
diff --git a/src/views/mes/qc/components/export-range-modal.vue b/src/views/mes/qc/components/export-range-modal.vue
deleted file mode 100644
index 19fedfe..0000000
--- a/src/views/mes/qc/components/export-range-modal.vue
+++ /dev/null
@@ -1,89 +0,0 @@
-<script lang="ts" setup>
-import { ref } from 'vue';
-
-import { useVbenModal } from '@vben/common-ui';
-import { downloadFileFromBlobPart } from '@vben/utils';
-
-import { DatePicker, message } from 'ant-design-vue';
-
-import { getRangePickerDefaultProps } from '#/utils';
-
-/** 瀵煎嚭鍙傛暟锛堝悗绔� PageReqVO 浠ユ暟缁勮В鏋� inspectDate 璧锋锛� */
-interface ExportParams {
-  inspectDate?: [string, string];
-}
-
-interface ExportModalData {
-  /** 寮圭獥鏍囬 */
-  title?: string;
-  /** 妯″潡瀵煎嚭鍑芥暟锛歳equestClient.download(...) 杩斿洖 Blob */
-  exportApi: (params: ExportParams) => Promise<Blob>;
-  /** 瀵煎嚭鏂囦欢鍚� */
-  fileName: string;
-}
-
-defineOptions({ name: 'QcExportRangeModal' });
-
-const rangePickerProps = getRangePickerDefaultProps();
-
-const modalData = ref<ExportModalData>();
-const range = ref<[string, string] | undefined>(undefined);
-const exporting = ref(false);
-const title = ref('瀵煎嚭');
-
-const [Modal, modalApi] = useVbenModal({
-  async onConfirm() {
-    if (exporting.value) {
-      return;
-    }
-    const data = modalData.value;
-    if (!data) {
-      return;
-    }
-    exporting.value = true;
-    modalApi.lock();
-    try {
-      const params: ExportParams =
-        range.value && range.value.length === 2
-          ? { inspectDate: range.value }
-          : {};
-      const blob = await data.exportApi(params);
-      downloadFileFromBlobPart({ fileName: data.fileName, source: blob });
-      message.success('瀵煎嚭鎴愬姛');
-      await modalApi.close();
-    } finally {
-      exporting.value = false;
-      modalApi.unlock();
-    }
-  },
-  onOpenChange(isOpen: boolean) {
-    if (!isOpen) {
-      modalData.value = undefined;
-      range.value = undefined;
-      return;
-    }
-    const data = modalApi.getData<ExportModalData>();
-    modalData.value = data;
-    range.value = undefined;
-    title.value = data?.title ?? '瀵煎嚭';
-    modalApi.setState({ confirmText: '瀵煎嚭' });
-  },
-});
-</script>
-
-<template>
-  <Modal :title="title" class="w-2/5" :mask-closable="false">
-    <div class="px-1 py-3">
-      <div class="mb-2 text-sm font-medium text-gray-700">妫�楠屾椂闂�</div>
-      <DatePicker.RangePicker
-        v-model:value="range"
-        v-bind="rangePickerProps"
-        allow-clear
-        class="w-full"
-      />
-      <div class="mt-2 text-xs text-gray-400">
-        涓嶉�夋椂闂村垯瀵煎嚭鍏ㄩ儴鏁版嵁锛涘鍑烘寜銆屾娴嬫棩鏈熴�嶈繃婊�
-      </div>
-    </div>
-  </Modal>
-</template>
diff --git a/src/views/mes/qc/ipqc/index.vue b/src/views/mes/qc/ipqc/index.vue
index b879b96..5906711 100644
--- a/src/views/mes/qc/ipqc/index.vue
+++ b/src/views/mes/qc/ipqc/index.vue
@@ -4,6 +4,7 @@
 
 import { Page, useVbenModal } from '@vben/common-ui';
 import { MesQcStatusEnum } from '@vben/constants';
+import { downloadFileFromBlobPart } from '@vben/utils';
 
 import { Button, message } from 'ant-design-vue';
 
@@ -16,7 +17,6 @@
   importIpqcTemplate,
 } from '#/api/mes/qc/ipqc';
 import { $t } from '#/locales';
-import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue';
 import QcImportFormModal from '#/views/mes/qc/components/import-form-modal.vue';
 import QcReportGenerateModal from '#/views/mes/qc/components/report-generate-modal.vue';
 
@@ -28,11 +28,6 @@
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
-  destroyOnClose: true,
-});
-
-const [ExportRangeModal, exportRangeModalApi] = useVbenModal({
-  connectedComponent: QcExportRangeModal,
   destroyOnClose: true,
 });
 
@@ -101,15 +96,10 @@
   }
 }
 
-/** 瀵煎嚭琛ㄦ牸锛堝脊绐楅�夋楠屾椂闂磋寖鍥达級 */
-function handleExport() {
-  exportRangeModalApi
-    .setData({
-      title: '鎸夋楠屾椂闂村鍑鸿繃绋嬫楠屽崟',
-      exportApi: exportIpqc,
-      fileName: '杩囩▼妫�楠屽崟.xls',
-    })
-    .open();
+/** 瀵煎嚭琛ㄦ牸锛堟寜鍒楄〃绛涢�夋潯浠跺鍑猴級 */
+async function handleExport() {
+  const data = await exportIpqc(await gridApi.formApi.getValues());
+  downloadFileFromBlobPart({ fileName: '杩囩▼妫�楠屽崟.xls', source: data });
 }
 
 /** 鍑烘姤鍛婏細鏁版嵁鐢卞悗绔粠璇ヨ川妫�鍗曞彇锛屽墠绔彧閫夋ā鏉� */
@@ -153,7 +143,6 @@
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
-    <ExportRangeModal />
     <ReportModal />
     <ImportModal @success="handleRefresh" />
     <Grid table-title="杩囩▼妫�楠屽崟鍒楄〃">
diff --git a/src/views/mes/qc/iqc/index.vue b/src/views/mes/qc/iqc/index.vue
index 4be3aed..4b1f1c5 100644
--- a/src/views/mes/qc/iqc/index.vue
+++ b/src/views/mes/qc/iqc/index.vue
@@ -7,6 +7,7 @@
 
 import { Page, useVbenModal } from '@vben/common-ui';
 import { MesQcStatusEnum } from '@vben/constants';
+import { downloadFileFromBlobPart } from '@vben/utils';
 
 import { Button, message } from 'ant-design-vue';
 
@@ -19,7 +20,6 @@
   importIqcTemplate,
 } from '#/api/mes/qc/iqc';
 import { $t } from '#/locales';
-import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue';
 import QcImportFormModal from '#/views/mes/qc/components/import-form-modal.vue';
 import QcReportGenerateModal from '#/views/mes/qc/components/report-generate-modal.vue';
 
@@ -35,11 +35,6 @@
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
-  destroyOnClose: true,
-});
-
-const [ExportRangeModal, exportRangeModalApi] = useVbenModal({
-  connectedComponent: QcExportRangeModal,
   destroyOnClose: true,
 });
 
@@ -107,15 +102,10 @@
   }
 }
 
-/** 瀵煎嚭琛ㄦ牸锛堝脊绐楅�夋楠屾椂闂磋寖鍥达級 */
-function handleExport() {
-  exportRangeModalApi
-    .setData({
-      title: '鎸夋楠屾椂闂村鍑烘潵鏂欐楠屽崟',
-      exportApi: exportIqc,
-      fileName: '鏉ユ枡妫�楠屽崟.xls',
-    })
-    .open();
+/** 瀵煎嚭琛ㄦ牸锛堟寜鍒楄〃绛涢�夋潯浠跺鍑猴級 */
+async function handleExport() {
+  const data = await exportIqc(await gridApi.formApi.getValues());
+  downloadFileFromBlobPart({ fileName: '鏉ユ枡妫�楠屽崟.xls', source: data });
 }
 
 /** 鍑烘姤鍛婏細鏁版嵁鐢卞悗绔粠璇ヨ川妫�鍗曞彇锛屽墠绔彧閫夋ā鏉� */
@@ -166,7 +156,6 @@
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
-    <ExportRangeModal />
     <ReportModal />
     <ImportModal @success="handleRefresh" />
     <Grid table-title="鏉ユ枡妫�楠屽崟鍒楄〃">
diff --git a/src/views/mes/qc/oqc/index.vue b/src/views/mes/qc/oqc/index.vue
index 7491ac7..3782c48 100644
--- a/src/views/mes/qc/oqc/index.vue
+++ b/src/views/mes/qc/oqc/index.vue
@@ -4,6 +4,7 @@
 
 import { Page, useVbenModal } from '@vben/common-ui';
 import { MesQcStatusEnum } from '@vben/constants';
+import { downloadFileFromBlobPart } from '@vben/utils';
 
 import { Button, message } from 'ant-design-vue';
 
@@ -16,7 +17,6 @@
   importOqcTemplate,
 } from '#/api/mes/qc/oqc';
 import { $t } from '#/locales';
-import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue';
 import QcImportFormModal from '#/views/mes/qc/components/import-form-modal.vue';
 import QcReportGenerateModal from '#/views/mes/qc/components/report-generate-modal.vue';
 
@@ -28,11 +28,6 @@
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
-  destroyOnClose: true,
-});
-
-const [ExportRangeModal, exportRangeModalApi] = useVbenModal({
-  connectedComponent: QcExportRangeModal,
   destroyOnClose: true,
 });
 
@@ -100,15 +95,10 @@
   }
 }
 
-/** 瀵煎嚭琛ㄦ牸锛堝脊绐楅�夋楠屾椂闂磋寖鍥达級 */
-function handleExport() {
-  exportRangeModalApi
-    .setData({
-      title: '鎸夋楠屾椂闂村鍑鸿揣妫�楠屽崟',
-      exportApi: exportOqc,
-      fileName: '鍑鸿揣妫�楠屽崟.xls',
-    })
-    .open();
+/** 瀵煎嚭琛ㄦ牸锛堟寜鍒楄〃绛涢�夋潯浠跺鍑猴級 */
+async function handleExport() {
+  const data = await exportOqc(await gridApi.formApi.getValues());
+  downloadFileFromBlobPart({ fileName: '鍑鸿揣妫�楠屽崟.xls', source: data });
 }
 
 /** 鍑烘姤鍛婏細鏁版嵁鐢卞悗绔粠璇ヨ川妫�鍗曞彇锛屽墠绔彧閫夋ā鏉� */
@@ -152,7 +142,6 @@
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
-    <ExportRangeModal />
     <ReportModal />
     <ImportModal @success="handleRefresh" />
     <Grid table-title="鍑鸿揣妫�楠屽崟鍒楄〃">
diff --git a/src/views/mes/qc/rqc/index.vue b/src/views/mes/qc/rqc/index.vue
index 64564a9..a5d1e36 100644
--- a/src/views/mes/qc/rqc/index.vue
+++ b/src/views/mes/qc/rqc/index.vue
@@ -4,13 +4,13 @@
 
 import { Page, useVbenModal } from '@vben/common-ui';
 import { MesQcStatusEnum } from '@vben/constants';
+import { downloadFileFromBlobPart } from '@vben/utils';
 
 import { Button, message } from 'ant-design-vue';
 
 import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
 import { deleteRqc, exportRqc, getRqcPage } from '#/api/mes/qc/rqc';
 import { $t } from '#/locales';
-import QcExportRangeModal from '#/views/mes/qc/components/export-range-modal.vue';
 import QcReportGenerateModal from '#/views/mes/qc/components/report-generate-modal.vue';
 
 import { useGridColumns, useGridFormSchema } from './data';
@@ -21,11 +21,6 @@
 
 const [FormModal, formModalApi] = useVbenModal({
   connectedComponent: Form,
-  destroyOnClose: true,
-});
-
-const [ExportRangeModal, exportRangeModalApi] = useVbenModal({
-  connectedComponent: QcExportRangeModal,
   destroyOnClose: true,
 });
 
@@ -69,15 +64,10 @@
   }
 }
 
-/** 瀵煎嚭琛ㄦ牸锛堝脊绐楅�夋楠屾椂闂磋寖鍥达級 */
-function handleExport() {
-  exportRangeModalApi
-    .setData({
-      title: '鎸夋楠屾椂闂村閫�璐ф楠屽崟',
-      exportApi: exportRqc,
-      fileName: '閫�璐ф楠屽崟.xls',
-    })
-    .open();
+/** 瀵煎嚭琛ㄦ牸锛堟寜鍒楄〃绛涢�夋潯浠跺鍑猴級 */
+async function handleExport() {
+  const data = await exportRqc(await gridApi.formApi.getValues());
+  downloadFileFromBlobPart({ fileName: '閫�璐ф楠屽崟.xls', source: data });
 }
 
 /** 鍑烘姤鍛婏細鏁版嵁鐢卞悗绔粠璇ヨ川妫�鍗曞彇锛屽墠绔彧閫夋ā鏉� */
@@ -121,7 +111,6 @@
 <template>
   <Page auto-content-height>
     <FormModal @success="handleRefresh" />
-    <ExportRangeModal />
     <ReportModal />
     <Grid table-title="閫�璐ф楠屽崟鍒楄〃">
       <template #toolbar-tools>
diff --git a/src/views/wls/itemreceipt/data.ts b/src/views/wls/itemreceipt/data.ts
index 291714f..c2e3f70 100644
--- a/src/views/wls/itemreceipt/data.ts
+++ b/src/views/wls/itemreceipt/data.ts
@@ -1,10 +1,13 @@
 import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MdmItemModelApi } from '#/api/mdm/item-model';
+import type { MesMdItemApi } from '#/api/mes/md/item';
 import type { MesWmArrivalNoticeApi } from '#/api/mes/wm/arrivalnotice';
 import type { MesWmArrivalNoticeLineApi } from '#/api/mes/wm/arrivalnotice/line';
 import type { MesWmItemReceiptApi } from '#/api/mes/wm/itemreceipt';
 import type { MesWmItemReceiptDetailApi } from '#/api/mes/wm/itemreceipt/detail';
 import type { MesWmItemReceiptLineApi } from '#/api/mes/wm/itemreceipt/line';
+import type { SrmSupplierApi } from '#/api/srm/supplier';
 
 import { h, markRaw } from 'vue';
 
@@ -16,10 +19,12 @@
 
 import { Button } from 'ant-design-vue';
 
+import { getItem as getMdmItem } from '#/api/mdm/item';
+import { getItemModelListByItem } from '#/api/mdm/item-model';
 import { generateAutoCode } from '#/api/mes/md/autocode/record';
 import { getRangePickerDefaultProps } from '#/utils';
 import { MdItemSelect } from '#/views/mes/md/item/components';
-import { ErpSupplierSelect } from '#/views/erp/purchase/supplier/components';
+import { SrmSupplierSelect } from '#/views/srm/supplier/components';
 import {
   WmArrivalNoticeLineSelect,
   WmArrivalNoticeSelect,
@@ -132,10 +137,10 @@
     {
       fieldName: 'vendorId',
       label: '渚涘簲鍟�',
-      component: markRaw(ErpSupplierSelect),
+      component: markRaw(SrmSupplierSelect),
       componentProps: {
         placeholder: '璇烽�夋嫨渚涘簲鍟�',
-        onChange: async (supplier?: any) => {
+        onChange: async (supplier?: SrmSupplierApi.SupplierVO) => {
           await formApi?.setFieldValue('vendorName', supplier?.name);
         },
       },
@@ -193,7 +198,7 @@
     {
       fieldName: 'vendorId',
       label: '渚涘簲鍟�',
-      component: markRaw(ErpSupplierSelect),
+      component: markRaw(SrmSupplierSelect),
       componentProps: {
         placeholder: '璇烽�夋嫨渚涘簲鍟�',
       },
@@ -285,9 +290,24 @@
       minWidth: 120,
     },
     {
+      field: 'itemModel',
+      title: '鐗╂枡鍨嬪彿',
+      minWidth: 120,
+    },
+    {
       field: 'unitMeasureName',
-      title: '鍗曚綅',
-      width: 80,
+      title: '涓昏閲忓崟浣�',
+      width: 100,
+    },
+    {
+      field: 'enterUnitMeasureName',
+      title: '褰曞叆鍗曚綅',
+      width: 90,
+    },
+    {
+      field: 'enterQuantity',
+      title: '褰曞叆鏁伴噺',
+      width: 100,
     },
     {
       field: 'receivedQuantity',
@@ -312,11 +332,221 @@
   ];
 }
 
+/** 褰曞叆鍗曚綅閫夐」锛堝惈鍒扮墿鏂欎富鍗曚綅鐨勬崲绠楁瘮鐜囷級 */
+export interface ItemEnterUnitOption {
+  id: number;
+  name: string;
+  rate: number;
+}
+
+/** 鍨嬪彿涓嬫媺閫夐」锛欰piSelect 浼氭妸 id/model 鏄犲皠鎴� value/label锛屽叾浣欏瓧娈靛師鏍蜂繚鐣� */
+type ItemModelSelectOption = Pick<
+  MdmItemModelApi.ItemModel,
+  'unitMeasureId' | 'unitMeasureName' | 'unitMeasureRate'
+>;
+
+/** 鍏ュ簱鍗曡琛ㄥ崟涓笌鍙屽崟浣嶆崲绠楃浉鍏崇殑瀛楁 */
+interface LineUnitFormValues {
+  enterQuantity?: number;
+  enterUnitMeasureId?: number;
+  enterUnitMeasureName?: string;
+  enterUnitRate?: number;
+  itemId?: number;
+  itemModelId?: number;
+  itemPrimaryUnitId?: number;
+  itemPrimaryUnitName?: string;
+}
+
+/** 鍙屽崟浣嶆崲绠楃敤鐨勪复鏃惰〃鍗曞瓧娈碉紙鍙緵鍓嶇鎹㈢畻涓庡睍绀猴紝鎻愪氦鍓嶅繀椤诲墧闄わ級 */
+const LINE_TEMP_FIELDS = [
+  'itemPrimaryUnitId',
+  'itemPrimaryUnitName',
+  'enterUnitMeasureName',
+  'enterUnitRate',
+] as const;
+
+/** 鍓旈櫎鍙屽崟浣嶆崲绠楃敤鐨勪复鏃跺瓧娈碉紝寰楀埌鍙洿鎺ユ彁浜ょ殑琛屾暟鎹� */
+export function omitLineTempFields(
+  values: Record<string, unknown>,
+): MesWmItemReceiptLineApi.ItemReceiptLine {
+  const data = { ...values };
+  for (const field of LINE_TEMP_FIELDS) {
+    delete data[field];
+  }
+  return data as MesWmItemReceiptLineApi.ItemReceiptLine;
+}
+
+/** 鎹㈢畻姣旂巼鏄惁鏈夋晥锛堝繀椤诲ぇ浜� 0锛� */
+function isValidRate(rate?: number): rate is number {
+  return typeof rate === 'number' && rate > 0;
+}
+
+/** 鎹㈢畻缁撴灉鎸� 6 浣嶅皬鏁板睍绀猴紙鏈�缁堜互鍚庣鎹㈢畻缁撴灉涓哄噯锛� */
+function roundQuantity(quantity: number): number {
+  return Math.round(quantity * 1e6) / 1e6;
+}
+
+/**
+ * 鏌ヨ鐗╂枡鍙�夌殑褰曞叆鍗曚綅锛氫富鍗曚綅 + 杈呭崟浣�
+ *
+ * 閫夊畾鍨嬪彿鏃跺彧淇濈暀銆岀墿鏂欎富鍗曚綅銆嶄笌銆屽瀷鍙疯閲忓崟浣嶃�嶄袱绉嶅彛寰勶紝涓庡悗绔崲绠楄鍒欎繚鎸佷竴鑷�
+ */
+export async function getItemEnterUnits(params: {
+  itemId?: number;
+  itemModelId?: number;
+}): Promise<ItemEnterUnitOption[]> {
+  const itemId = params.itemId;
+  if (!itemId) {
+    return [];
+  }
+  const item = await getMdmItem(itemId);
+  if (!item?.unitMeasureId) {
+    return [];
+  }
+  // 涓诲崟浣嶅缁堝彲閫夛紝鎸変富鍗曚綅褰曞叆鏃朵笉鍋氭崲绠�
+  const primaryUnit: ItemEnterUnitOption = {
+    id: item.unitMeasureId,
+    name: item.unitMeasureName || '鐗╂枡涓诲崟浣�',
+    rate: 1,
+  };
+  const unitOptions: ItemEnterUnitOption[] = [primaryUnit];
+  const pushUnit = (id?: number, name?: string, rate?: number) => {
+    if (
+      !id ||
+      id === primaryUnit.id ||
+      !isValidRate(rate) ||
+      unitOptions.some((unit) => unit.id === id)
+    ) {
+      return;
+    }
+    unitOptions.push({ id, name: name || '杈呭崟浣�', rate });
+  };
+  if (params.itemModelId) {
+    const models = await getItemModelListByItem(itemId);
+    const modelOption = models?.find((option) => option.id === params.itemModelId);
+    pushUnit(
+      modelOption?.unitMeasureId,
+      modelOption?.unitMeasureName,
+      modelOption?.unitMeasureRate,
+    );
+    return unitOptions;
+  }
+  // 鏈�夊瀷鍙锋椂锛岀墿鏂欎富鏁版嵁涓婇厤缃簡鎹㈢畻姣旂巼鐨勮緟鍗曚綅鍙��
+  pushUnit(item.unitMeasureId2, item.unitMeasureName2, item.unitMeasureRate1);
+  pushUnit(item.unitMeasureId3, item.unitMeasureName3, item.unitMeasureRate2);
+  return unitOptions;
+}
+
+/** 鏌ヨ鏌愪釜褰曞叆鍗曚綅鍒扮墿鏂欎富鍗曚綅鐨勬崲绠楁瘮鐜囷紙缂栬緫鍥炴樉鏃惰ˉ榻愬墠绔崲绠椾笂涓嬫枃锛� */
+export async function getEnterUnitRate(
+  itemId?: number,
+  itemModelId?: number,
+  enterUnitMeasureId?: number,
+): Promise<number | undefined> {
+  if (!itemId || !enterUnitMeasureId) {
+    return undefined;
+  }
+  const unitOptions = await getItemEnterUnits({ itemId, itemModelId });
+  return unitOptions.find((unit) => unit.id === enterUnitMeasureId)?.rate;
+}
+
 /** 鍏ュ簱鍗曡鏂板/淇敼鐨勮〃鍗� */
 export function useLineFormSchema(
   hasNotice: boolean,
   formApi?: VbenFormApi,
 ): VbenFormSchema[] {
+  /** 鏄惁鎸夌墿鏂欎富鍗曚綅褰曞叆锛堟湭閫夊綍鍏ュ崟浣嶃�佹垨褰曞叆鍗曚綅灏辨槸鐗╂枡涓诲崟浣嶏級 */
+  function isPrimaryUnitEnter(values: LineUnitFormValues) {
+    return (
+      !values.enterUnitMeasureId ||
+      values.enterUnitMeasureId === values.itemPrimaryUnitId
+    );
+  }
+
+  /** 鍒囨崲鐗╂枡锛氶噸鏂拌褰曠墿鏂欎富鍗曚綅锛屽苟娓呯┖鍨嬪彿銆佸綍鍏ュ崟浣嶄笌鏁伴噺锛堝彛寰勯兘闅忕墿鏂欏彉鍖栵級 */
+  async function handleItemChange(itemId?: number) {
+    // 鍏堟竻绌猴紝閬垮厤涓婁竴涓墿鏂欑殑鍨嬪彿涓庡崟浣嶅彛寰勬畫鐣�
+    await formApi?.setValues({
+      itemId,
+      itemModelId: undefined,
+      itemPrimaryUnitId: undefined,
+      itemPrimaryUnitName: undefined,
+      enterUnitMeasureId: undefined,
+      enterUnitMeasureName: undefined,
+      enterUnitRate: undefined,
+      enterQuantity: undefined,
+      receivedQuantity: undefined,
+    });
+    if (!itemId) {
+      return;
+    }
+    const item = await getMdmItem(itemId);
+    await formApi?.setValues({
+      itemPrimaryUnitId: item?.unitMeasureId,
+      itemPrimaryUnitName: item?.unitMeasureName,
+    });
+  }
+
+  /** 閫夋嫨鍨嬪彿锛氶粯璁ゆ寜鍨嬪彿璁¢噺鍗曚綅褰曞叆锛屽苟璁板綍鎹㈢畻姣旂巼 */
+  async function handleModelChange(
+    option: ItemModelSelectOption | undefined,
+    values: LineUnitFormValues,
+  ) {
+    const enterUnitMeasureId =
+      option?.unitMeasureId && option.unitMeasureId !== values.itemPrimaryUnitId
+        ? option.unitMeasureId
+        : undefined;
+    await formApi?.setValues({
+      enterUnitMeasureId,
+      enterUnitMeasureName: enterUnitMeasureId
+        ? option?.unitMeasureName
+        : undefined,
+      enterUnitRate: enterUnitMeasureId ? option?.unitMeasureRate : undefined,
+      enterQuantity: undefined,
+      // 鍒囧埌鍨嬪彿鍗曚綅褰曞叆鍚庡叆搴撴暟閲忕敱绯荤粺鎹㈢畻锛屽厛娓呮帀涓婁竴娆″彛寰勭殑鏁板��
+      ...(enterUnitMeasureId ? { receivedQuantity: undefined } : {}),
+    });
+  }
+
+  /** 鍒囨崲褰曞叆鍗曚綅锛氳褰曟崲绠楁瘮鐜囧苟绔嬪嵆閲嶇畻鍏ュ簱鏁伴噺锛涙敼鍥炰富鍗曚綅鏃舵竻鎺夊綍鍏ユ暟閲� */
+  async function handleEnterUnitChange(
+    value: number | undefined,
+    option: { label?: string; rate?: number } | undefined,
+    values: LineUnitFormValues,
+  ) {
+    if (!value || value === values.itemPrimaryUnitId) {
+      await formApi?.setValues({
+        enterUnitMeasureName: undefined,
+        enterUnitRate: undefined,
+        enterQuantity: undefined,
+      });
+      return;
+    }
+    await formApi?.setValues({
+      enterUnitMeasureName: option?.label,
+      enterUnitRate: option?.rate,
+      ...(values.enterQuantity == null || !isValidRate(option?.rate)
+        ? { receivedQuantity: undefined }
+        : {
+            receivedQuantity: roundQuantity(values.enterQuantity * option.rate),
+          }),
+    });
+  }
+
+  /** 褰曞叆鏁伴噺鍙樺寲锛氭寜褰曞叆鍗曚綅涓庢崲绠楁瘮鐜囪瘯绠楀叆搴撴暟閲忥紝渚涚敤鎴锋牳瀵规崲绠楃粨鏋� */
+  async function handleEnterQuantityChange(
+    value: number | undefined,
+    values: LineUnitFormValues,
+  ) {
+    if (isPrimaryUnitEnter(values) || !isValidRate(values.enterUnitRate)) {
+      return;
+    }
+    await formApi?.setFieldValue(
+      'receivedQuantity',
+      value == null ? undefined : roundQuantity(value * values.enterUnitRate),
+    );
+  }
+
   return [
     {
       fieldName: 'noticeId',
@@ -331,14 +561,17 @@
       label: '鍒拌揣閫氱煡鍗曡',
       component: markRaw(WmArrivalNoticeLineSelect),
       componentProps: {
-        // 閫夋嫨鍒拌揣閫氱煡鍗曡鍚庯紝鑷姩鍥炲~鐗╂枡鍜屽叆搴撴暟閲�
+        // 閫夋嫨鍒拌揣閫氱煡鍗曡鍚庯紝鑷姩鍥炲~鐗╂枡涓庡叆搴撴暟閲�
         onChange: async (
           line?: MesWmArrivalNoticeLineApi.ArrivalNoticeLine,
         ) => {
-          await formApi?.setValues({
-            itemId: line?.itemId,
-            receivedQuantity: line?.arrivalQuantity,
-          });
+          await handleItemChange(line?.itemId);
+          if (line?.arrivalQuantity != null) {
+            await formApi?.setFieldValue(
+              'receivedQuantity',
+              line.arrivalQuantity,
+            );
+          }
         },
       },
       dependencies: {
@@ -355,6 +588,8 @@
       component: markRaw(MdItemSelect),
       componentProps: {
         placeholder: '璇烽�夋嫨鐗╂枡',
+        // 鍒囨崲鐗╂枡鍚庯紝鍨嬪彿銆佸綍鍏ュ崟浣嶄笌鏁伴噺閮借閲嶆柊閫�
+        onChange: (item?: MesMdItemApi.Item) => handleItemChange(item?.id),
       },
       rules: 'selectRequired',
       dependencies: {
@@ -365,6 +600,121 @@
         }),
       },
     },
+    // 鐗╂枡涓诲崟浣嶉殢鎵�閫夌墿鏂欏彉鍖栵紝浣嗚〃鍗� props 鍙兘闈欐�佸0鏄庯紝鏁呯敤闅愯棌瀛楁鎵胯浇
+    {
+      fieldName: 'itemPrimaryUnitId',
+      component: 'Input',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'itemPrimaryUnitName',
+      component: 'Input',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'itemModelId',
+      label: '鐗╂枡鍨嬪彿',
+      component: 'ApiSelect',
+      componentProps: {
+        labelField: 'model',
+        valueField: 'id',
+        allowClear: true,
+        api: (params: { itemId?: number }) =>
+          getItemModelListByItem(params.itemId!),
+        shouldFetch: (params: { itemId?: number }) => !!params?.itemId,
+        placeholder: '璇峰厛閫夋嫨鐗╂枡',
+      },
+      dependencies: {
+        triggerFields: ['itemId'],
+        componentProps: (values) => ({
+          disabled: !values.itemId,
+          params: { itemId: values.itemId },
+          placeholder: values.itemId ? '璇烽�夋嫨鐗╂枡鍨嬪彿' : '璇峰厛閫夋嫨鐗╂枡',
+          // 閫夊畾鍨嬪彿鍚庨粯璁ゆ寜鍏惰閲忓崟浣嶅綍鍏�
+          onChange: (option?: ItemModelSelectOption) =>
+            handleModelChange(option, values),
+        }),
+      },
+    },
+    {
+      fieldName: 'enterUnitMeasureId',
+      label: '褰曞叆鍗曚綅',
+      component: 'ApiSelect',
+      componentProps: {
+        allowClear: true,
+        labelField: 'name',
+        valueField: 'id',
+        api: (params: { itemId?: number; itemModelId?: number }) =>
+          getItemEnterUnits(params),
+        shouldFetch: (params: { itemId?: number }) => !!params?.itemId,
+        placeholder: '榛樿涓虹墿鏂欎富璁¢噺鍗曚綅',
+      },
+      dependencies: {
+        triggerFields: ['itemId', 'itemModelId', 'itemPrimaryUnitName'],
+        componentProps: (values) => ({
+          disabled: !values.itemId,
+          params: { itemId: values.itemId, itemModelId: values.itemModelId },
+          placeholder: values.itemId
+            ? `榛樿鎸夌墿鏂欎富鍗曚綅${values.itemPrimaryUnitName ? `銆�${values.itemPrimaryUnitName}銆慲 : ''}褰曞叆`
+            : '璇峰厛閫夋嫨鐗╂枡',
+          onChange: (
+            value: number | undefined,
+            option?: { label?: string; rate?: number },
+          ) => handleEnterUnitChange(value, option, values),
+        }),
+      },
+    },
+    // 褰曞叆鍗曚綅鍚嶇О涓庢崲绠楁瘮鐜囬殢鎵�閫夊崟浣嶅彉鍖栵紝鍚屾牱鐢ㄩ殣钘忓瓧娈垫壙杞�
+    {
+      fieldName: 'enterUnitMeasureName',
+      component: 'Input',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'enterUnitRate',
+      component: 'Input',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'enterQuantity',
+      label: '褰曞叆鏁伴噺',
+      component: 'InputNumber',
+      componentProps: {
+        class: '!w-full',
+        min: 0,
+        precision: 6,
+      },
+      dependencies: {
+        triggerFields: [
+          'enterUnitMeasureId',
+          'enterUnitMeasureName',
+          'itemPrimaryUnitId',
+        ],
+        show: (values) => !isPrimaryUnitEnter(values),
+        // 杩斿洖 null 琛ㄧず涓嶈拷鍔犺鍒欙紙鍥為��鍩虹 rules锛�
+        rules: (values) => (isPrimaryUnitEnter(values) ? null : 'required'),
+        componentProps: (values) => ({
+          placeholder: values.enterUnitMeasureName
+            ? `璇峰~鍐欐寜銆�${values.enterUnitMeasureName}銆戣鐨勬暟閲廯
+            : '璇锋寜褰曞叆鍗曚綅濉啓鏁伴噺',
+          // 褰曞叆鏁伴噺鍙樺寲鏃剁珛鍗宠瘯绠楀叆搴撴暟閲�
+          onChange: (value: number | undefined) =>
+            handleEnterQuantityChange(value, values),
+        }),
+      },
+    },
     {
       fieldName: 'receivedQuantity',
       label: '鍏ュ簱鏁伴噺',
@@ -372,10 +722,26 @@
       componentProps: {
         class: '!w-full',
         min: 0,
-        placeholder: '璇疯緭鍏ュ叆搴撴暟閲�',
-        precision: 2,
+        precision: 6,
       },
-      rules: 'required',
+      dependencies: {
+        triggerFields: [
+          'enterUnitMeasureId',
+          'itemPrimaryUnitId',
+          'itemPrimaryUnitName',
+        ],
+        // 鎸変富鍗曚綅褰曞叆鏃舵墜宸ュ~鍐欍�佸繀濉紱鎸夎緟鍗曚綅褰曞叆鏃朵负绯荤粺鎹㈢畻缁撴灉锛屾棤闇�鏍¢獙
+        rules: (values) => (isPrimaryUnitEnter(values) ? 'required' : null),
+        componentProps: (values) => {
+          const primary = isPrimaryUnitEnter(values);
+          return {
+            disabled: !primary,
+            placeholder: primary
+              ? `璇锋寜鐗╂枡涓诲崟浣�${values.itemPrimaryUnitName ? `銆�${values.itemPrimaryUnitName}銆慲 : ''}濉啓鏁伴噺`
+              : '鎸夊綍鍏ュ崟浣嶄笌鎹㈢畻姣旂巼鑷姩鎹㈢畻',
+          };
+        },
+      },
     },
     {
       fieldName: 'productionDate',
diff --git a/src/views/wls/itemreceipt/modules/line-form.vue b/src/views/wls/itemreceipt/modules/line-form.vue
index debfc76..47ee948 100644
--- a/src/views/wls/itemreceipt/modules/line-form.vue
+++ b/src/views/wls/itemreceipt/modules/line-form.vue
@@ -15,7 +15,11 @@
 } from '#/api/mes/wm/itemreceipt/line';
 import { $t } from '#/locales';
 
-import { useLineFormSchema } from '../data';
+import {
+  getEnterUnitRate,
+  omitLineTempFields,
+  useLineFormSchema,
+} from '../data';
 
 const emit = defineEmits(['success']);
 const formData = ref<MesWmItemReceiptLineApi.ItemReceiptLine>();
@@ -49,9 +53,8 @@
       return;
     }
     modalApi.lock();
-    // 鎻愪氦琛ㄥ崟
-    const data =
-      (await formApi.getValues()) as MesWmItemReceiptLineApi.ItemReceiptLine;
+    // 鎻愪氦琛ㄥ崟锛堝墧闄や粎鐢ㄤ簬鍓嶇鎹㈢畻/灞曠ず鐨勪复鏃跺瓧娈碉級
+    const data = omitLineTempFields(await formApi.getValues());
     data.receiptId = receiptId.value;
     try {
       await (formData.value?.id
@@ -87,9 +90,26 @@
     }
     modalApi.lock();
     try {
-      formData.value = await getItemReceiptLine(data.id);
-      // 璁剧疆鍒� values
-      await formApi.setValues({ ...formData.value, noticeId: noticeId.value });
+      const line = await getItemReceiptLine(data.id);
+      formData.value = line;
+      // 璁剧疆鍒� values锛涜鎯呴噷鐨� unitMeasureId/unitMeasureName 鍗崇墿鏂欎富鍗曚綅锛岃ˉ鍒板墠绔崲绠楃敤鐨勯殣钘忓瓧娈典笂
+      await formApi.setValues({
+        ...line,
+        noticeId: noticeId.value,
+        itemPrimaryUnitId: line.unitMeasureId,
+        itemPrimaryUnitName: line.unitMeasureName,
+      });
+      // 鍥炴樉褰曞叆鍗曚綅鏃惰ˉ榻愭崲绠楁瘮鐜囷紙鍓嶇棰勮鎹㈢畻闇�瑕侊級
+      if (line.enterUnitMeasureId) {
+        await formApi.setFieldValue(
+          'enterUnitRate',
+          await getEnterUnitRate(
+            line.itemId,
+            line.itemModelId,
+            line.enterUnitMeasureId,
+          ),
+        );
+      }
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/wls/miscissue/data.ts b/src/views/wls/miscissue/data.ts
index 5018e10..bb784d5 100644
--- a/src/views/wls/miscissue/data.ts
+++ b/src/views/wls/miscissue/data.ts
@@ -13,6 +13,10 @@
 
 import { generateAutoCode } from '#/api/mes/md/autocode/record';
 import { getRangePickerDefaultProps } from '#/utils';
+import {
+  useLineUnitHandlers,
+  buildItemUnitSchema,
+} from '#/views/wls/utils/item-unit-convert';
 import { WmMaterialStockSelect } from '#/views/wls/materialstock/components';
 import {
   WmWarehouseAreaSelect,
@@ -283,9 +287,24 @@
       minWidth: 120,
     },
     {
+      field: 'itemModel',
+      title: '鐗╂枡鍨嬪彿',
+      minWidth: 120,
+    },
+    {
       field: 'unitMeasureName',
-      title: '鍗曚綅',
-      width: 80,
+      title: '涓昏閲忓崟浣�',
+      width: 100,
+    },
+    {
+      field: 'enterUnitMeasureName',
+      title: '褰曞叆鍗曚綅',
+      width: 90,
+    },
+    {
+      field: 'enterQuantity',
+      title: '褰曞叆鏁伴噺',
+      width: 100,
     },
     {
       field: 'quantity',
@@ -327,6 +346,7 @@
 
 /** 鍑哄簱鍗曡鏂板/淇敼鐨勮〃鍗� */
 export function useLineFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
+  const unitHandlers = useLineUnitHandlers(formApi);
   return [
     {
       fieldName: 'itemId',
@@ -351,10 +371,11 @@
       componentProps: {
         // 閫夋嫨搴撳瓨鐗╄祫鍚庯紝鑷姩鍥炲~鐗╂枡銆佹壒娆″彿銆佷粨搴撲綅缃拰鏈�澶у彲鍑哄簱鏁伴噺
         onChange: async (stock?: MesWmMaterialStockApi.MaterialStock) => {
+          // 鍒囨崲搴撳瓨鐗╄祫绛変簬鍒囨崲鐗╂枡锛屽厛娓呮帀涓婁竴浠藉瀷鍙蜂笌褰曞叆鍗曚綅鍙e緞
+          await unitHandlers.handleItemChange(stock?.itemId);
           await formApi?.setValues({
             areaId: stock?.areaId,
             batchCode: stock?.batchCode,
-            itemId: stock?.itemId,
             locationId: stock?.locationId,
             quantityMax: stock?.quantity,
             warehouseId: stock?.warehouseId,
@@ -369,28 +390,8 @@
         }),
       },
     },
-    {
-      fieldName: 'quantity',
-      label: '鍑哄簱鏁伴噺',
-      component: 'InputNumber',
-      componentProps: {
-        class: '!w-full',
-        min: 0.01,
-        placeholder: '璇疯緭鍏ュ嚭搴撴暟閲�',
-        precision: 2,
-      },
-      rules: 'required',
-      dependencies: {
-        triggerFields: ['quantityMax'],
-        componentProps: (values) => ({
-          class: '!w-full',
-          max: values.quantityMax,
-          min: 0.01,
-          placeholder: '璇疯緭鍏ュ嚭搴撴暟閲�',
-          precision: 2,
-        }),
-      },
-    },
+    // 鐗╂枡鍨嬪彿 / 褰曞叆鍗曚綅 / 褰曞叆鏁伴噺 / 鍑哄簱鏁伴噺锛堜富鍗曚綅鍙e緞锛�
+    ...buildItemUnitSchema(formApi),
     {
       fieldName: 'batchCode',
       label: '鎵规鍙�',
diff --git a/src/views/wls/miscissue/modules/line-form.vue b/src/views/wls/miscissue/modules/line-form.vue
index bd689a6..e1cb531 100644
--- a/src/views/wls/miscissue/modules/line-form.vue
+++ b/src/views/wls/miscissue/modules/line-form.vue
@@ -14,6 +14,10 @@
   updateMiscIssueLine,
 } from '#/api/mes/wm/miscissue/line';
 import { $t } from '#/locales';
+import {
+  getEnterUnitRate,
+  omitLineTempFields,
+} from '#/views/wls/utils/item-unit-convert';
 
 import { useLineFormSchema } from '../data';
 
@@ -48,9 +52,10 @@
       return;
     }
     modalApi.lock();
-    // 鎻愪氦琛ㄥ崟
-    const data =
-      (await formApi.getValues()) as MesWmMiscIssueLineApi.MiscIssueLine;
+    // 鎻愪氦琛ㄥ崟锛堝墧闄や粎鐢ㄤ簬鍓嶇鎹㈢畻/灞曠ず鐨勪复鏃跺瓧娈碉級
+    const data = omitLineTempFields(
+      (await formApi.getValues()) as MesWmMiscIssueLineApi.MiscIssueLine,
+    );
     data.issueId = issueId.value;
     try {
       await (formData.value?.id
@@ -81,9 +86,25 @@
     }
     modalApi.lock();
     try {
-      formData.value = await getMiscIssueLine(data.id);
-      // 璁剧疆鍒� values
-      await formApi.setValues(formData.value);
+      const line = await getMiscIssueLine(data.id);
+      formData.value = line;
+      // 璁剧疆鍒� values锛涜鎯呴噷鐨� unitMeasureId/unitMeasureName 鍗崇墿鏂欎富鍗曚綅锛岃ˉ鍒板墠绔崲绠楃敤鐨勯殣钘忓瓧娈典笂
+      await formApi.setValues({
+        ...line,
+        itemPrimaryUnitId: line.unitMeasureId,
+        itemPrimaryUnitName: line.unitMeasureName,
+      });
+      // 鍥炴樉褰曞叆鍗曚綅鏃惰ˉ榻愭崲绠楁瘮鐜囷紙鍓嶇棰勮鎹㈢畻闇�瑕侊級
+      if (line.enterUnitMeasureId) {
+        await formApi.setFieldValue(
+          'enterUnitRate',
+          await getEnterUnitRate(
+            line.itemId,
+            line.itemModelId,
+            line.enterUnitMeasureId,
+          ),
+        );
+      }
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/wls/outsourceissue/data.ts b/src/views/wls/outsourceissue/data.ts
index dd70166..915327c 100644
--- a/src/views/wls/outsourceissue/data.ts
+++ b/src/views/wls/outsourceissue/data.ts
@@ -1,5 +1,6 @@
 锘縤mport type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesMdItemApi } from '#/api/mes/md/item';
 import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder';
 import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock';
 import type { MesWmOutsourceIssueApi } from '#/api/mes/wm/outsourceissue';
@@ -22,6 +23,10 @@
 import { MdItemSelect } from '#/views/mes/md/item/components';
 import { MdVendorSelect } from '#/views/mes/md/vendor/components';
 import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components';
+import {
+  buildItemUnitSchema,
+  useLineUnitHandlers,
+} from '#/views/wls/utils/item-unit-convert';
 import { WmMaterialStockSelect } from '#/views/wls/materialstock/components';
 import {
   WmWarehouseAreaSelect,
@@ -254,13 +259,28 @@
       minWidth: 120,
     },
     {
+      field: 'itemModel',
+      title: '鐗╂枡鍨嬪彿',
+      minWidth: 120,
+    },
+    {
       field: 'unitMeasureName',
-      title: '鍗曚綅',
-      width: 80,
+      title: '涓昏閲忓崟浣�',
+      width: 100,
+    },
+    {
+      field: 'enterUnitMeasureName',
+      title: '褰曞叆鍗曚綅',
+      width: 90,
+    },
+    {
+      field: 'enterQuantity',
+      title: '褰曞叆鏁伴噺',
+      width: 100,
     },
     {
       field: 'quantity',
-      title: '棰嗘枡鏁伴噺',
+      title: '鍙戞枡鏁伴噺',
       width: 100,
     },
     {
@@ -287,7 +307,8 @@
 }
 
 /** 鍙戞枡鍗曡鏂板/淇敼鐨勮〃鍗� */
-export function useLineFormSchema(): VbenFormSchema[] {
+export function useLineFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
+  const unitHandlers = useLineUnitHandlers(formApi);
   return [
     {
       fieldName: 'itemId',
@@ -295,21 +316,15 @@
       component: markRaw(MdItemSelect),
       componentProps: {
         placeholder: '璇烽�夋嫨鐗╂枡',
+        // 鍒囨崲鐗╂枡鍚庯紝閲嶆柊璁板綍鐗╂枡涓诲崟浣嶅苟娓呯┖鍨嬪彿銆佸綍鍏ュ崟浣嶅彛寰�
+        onChange: async (item?: MesMdItemApi.Item) => {
+          await unitHandlers.handleItemChange(item?.id);
+        },
       },
       rules: 'selectRequired',
     },
-    {
-      fieldName: 'quantity',
-      label: '鍙戞枡鏁伴噺',
-      component: 'InputNumber',
-      componentProps: {
-        class: '!w-full',
-        min: 0,
-        placeholder: '璇疯緭鍏ュ彂鏂欐暟閲�',
-        precision: 2,
-      },
-      rules: 'required',
-    },
+    // 鐗╂枡鍨嬪彿 / 褰曞叆鍗曚綅 / 褰曞叆鏁伴噺 / 鍙戞枡鏁伴噺锛堢墿鏂欎富鍗曚綅鍙e緞锛�
+    ...buildItemUnitSchema(formApi, { quantityLabel: '鍙戞枡鏁伴噺' }),
     {
       fieldName: 'batchCode',
       label: '鎵规鍙�',
diff --git a/src/views/wls/outsourceissue/modules/line-form.vue b/src/views/wls/outsourceissue/modules/line-form.vue
index b0f8f6d..6430b9f 100644
--- a/src/views/wls/outsourceissue/modules/line-form.vue
+++ b/src/views/wls/outsourceissue/modules/line-form.vue
@@ -14,6 +14,10 @@
   updateOutsourceIssueLine,
 } from '#/api/mes/wm/outsourceissue/line';
 import { $t } from '#/locales';
+import {
+  getEnterUnitRate,
+  omitLineTempFields,
+} from '#/views/wls/utils/item-unit-convert';
 
 import { useLineFormSchema } from '../data';
 
@@ -36,7 +40,7 @@
     labelWidth: 90,
   },
   layout: 'horizontal',
-  schema: useLineFormSchema(),
+  schema: [],
   showDefaultActions: false,
   wrapperClass: 'grid-cols-3',
 });
@@ -48,9 +52,10 @@
       return;
     }
     modalApi.lock();
-    // 鎻愪氦琛ㄥ崟
-    const data =
-      (await formApi.getValues()) as MesWmOutsourceIssueLineApi.OutsourceIssueLine;
+    // 鎻愪氦琛ㄥ崟锛堝墧闄や粎鐢ㄤ簬鍓嶇鎹㈢畻/灞曠ず鐨勪复鏃跺瓧娈碉級
+    const data = omitLineTempFields(
+      (await formApi.getValues()) as MesWmOutsourceIssueLineApi.OutsourceIssueLine,
+    );
     data.issueId = issueId.value;
     try {
       await (formData.value?.id
@@ -69,6 +74,7 @@
       formData.value = undefined;
       return;
     }
+    formApi.setState({ schema: useLineFormSchema(formApi) });
     // 鍔犺浇鏁版嵁
     const data = modalApi.getData<{
       id?: number;
@@ -80,9 +86,25 @@
     }
     modalApi.lock();
     try {
-      formData.value = await getOutsourceIssueLine(data.id);
-      // 璁剧疆鍒� values
-      await formApi.setValues(formData.value);
+      const line = await getOutsourceIssueLine(data.id);
+      formData.value = line;
+      // 璁剧疆鍒� values锛涜鎯呴噷鐨� unitMeasureId/unitMeasureName 鍗崇墿鏂欎富鍗曚綅锛岃ˉ鍒板墠绔崲绠楃敤鐨勯殣钘忓瓧娈典笂
+      await formApi.setValues({
+        ...line,
+        itemPrimaryUnitId: line.unitMeasureId,
+        itemPrimaryUnitName: line.unitMeasureName,
+      });
+      // 鍥炴樉褰曞叆鍗曚綅鏃惰ˉ榻愭崲绠楁瘮鐜囷紙鍓嶇棰勮鎹㈢畻闇�瑕侊級
+      if (line.enterUnitMeasureId) {
+        await formApi.setFieldValue(
+          'enterUnitRate',
+          await getEnterUnitRate(
+            line.itemId,
+            line.itemModelId,
+            line.enterUnitMeasureId,
+          ),
+        );
+      }
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/wls/productissue/data.ts b/src/views/wls/productissue/data.ts
index 7fd1ed3..781bd29 100644
--- a/src/views/wls/productissue/data.ts
+++ b/src/views/wls/productissue/data.ts
@@ -1,5 +1,6 @@
 锘縤mport type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesMdItemApi } from '#/api/mes/md/item';
 import type { MesProTaskApi } from '#/api/mes/pro/task';
 import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock';
 import type { MesWmProductIssueApi } from '#/api/mes/wm/productissue';
@@ -24,6 +25,10 @@
 import { ProTaskSelect } from '#/views/mes/pro/task/components';
 import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components';
 import { WmMaterialStockSelect } from '#/views/wls/materialstock/components';
+import {
+  buildItemUnitSchema,
+  useLineUnitHandlers,
+} from '#/views/wls/utils/item-unit-convert';
 import {
   WmWarehouseAreaSelect,
   WmWarehouseLocationSelect,
@@ -287,9 +292,24 @@
       minWidth: 120,
     },
     {
+      field: 'itemModel',
+      title: '鐗╂枡鍨嬪彿',
+      minWidth: 120,
+    },
+    {
       field: 'unitMeasureName',
-      title: '鍗曚綅',
-      width: 80,
+      title: '涓昏閲忓崟浣�',
+      width: 100,
+    },
+    {
+      field: 'enterUnitMeasureName',
+      title: '褰曞叆鍗曚綅',
+      width: 90,
+    },
+    {
+      field: 'enterQuantity',
+      title: '褰曞叆鏁伴噺',
+      width: 100,
     },
     {
       field: 'quantity',
@@ -310,7 +330,8 @@
 }
 
 /** 棰嗘枡鍗曡鏂板/淇敼鐨勮〃鍗� */
-export function useLineFormSchema(): VbenFormSchema[] {
+export function useLineFormSchema(formApi?: VbenFormApi): VbenFormSchema[] {
+  const unitHandlers = useLineUnitHandlers(formApi);
   return [
     {
       fieldName: 'itemId',
@@ -318,21 +339,15 @@
       component: markRaw(MdItemSelect),
       componentProps: {
         placeholder: '璇烽�夋嫨浜у搧鐗╂枡',
+        // 鍒囨崲鐗╂枡鍚庯紝閲嶆柊璁板綍鐗╂枡涓诲崟浣嶅苟娓呯┖鍨嬪彿銆佸綍鍏ュ崟浣嶅彛寰�
+        onChange: async (item?: MesMdItemApi.Item) => {
+          await unitHandlers.handleItemChange(item?.id);
+        },
       },
       rules: 'selectRequired',
     },
-    {
-      fieldName: 'quantity',
-      label: '棰嗘枡鏁伴噺',
-      component: 'InputNumber',
-      componentProps: {
-        class: '!w-full',
-        min: 0,
-        placeholder: '璇疯緭鍏ラ鏂欐暟閲�',
-        precision: 2,
-      },
-      rules: 'required',
-    },
+    // 鐗╂枡鍨嬪彿 / 褰曞叆鍗曚綅 / 褰曞叆鏁伴噺 / 棰嗘枡鏁伴噺锛堢墿鏂欎富鍗曚綅鍙e緞锛�
+    ...buildItemUnitSchema(formApi, { quantityLabel: '棰嗘枡鏁伴噺' }),
     {
       fieldName: 'remark',
       label: '澶囨敞',
diff --git a/src/views/wls/productissue/modules/line-form.vue b/src/views/wls/productissue/modules/line-form.vue
index 8bf9782..5c627e2 100644
--- a/src/views/wls/productissue/modules/line-form.vue
+++ b/src/views/wls/productissue/modules/line-form.vue
@@ -14,6 +14,10 @@
   updateProductIssueLine,
 } from '#/api/mes/wm/productissue/line';
 import { $t } from '#/locales';
+import {
+  getEnterUnitRate,
+  omitLineTempFields,
+} from '#/views/wls/utils/item-unit-convert';
 
 import { useLineFormSchema } from '../data';
 
@@ -36,7 +40,7 @@
     labelWidth: 90,
   },
   layout: 'horizontal',
-  schema: useLineFormSchema(),
+  schema: [],
   showDefaultActions: false,
   wrapperClass: 'grid-cols-3',
 });
@@ -48,9 +52,10 @@
       return;
     }
     modalApi.lock();
-    // 鎻愪氦琛ㄥ崟
-    const data =
-      (await formApi.getValues()) as MesWmProductIssueLineApi.ProductIssueLine;
+    // 鎻愪氦琛ㄥ崟锛堝墧闄や粎鐢ㄤ簬鍓嶇鎹㈢畻/灞曠ず鐨勪复鏃跺瓧娈碉級
+    const data = omitLineTempFields(
+      (await formApi.getValues()) as MesWmProductIssueLineApi.ProductIssueLine,
+    );
     data.issueId = issueId.value;
     try {
       await (formData.value?.id
@@ -69,6 +74,7 @@
       formData.value = undefined;
       return;
     }
+    formApi.setState({ schema: useLineFormSchema(formApi) });
     // 鍔犺浇鏁版嵁
     const data = modalApi.getData<{ id?: number; issueId: number }>();
     issueId.value = data.issueId;
@@ -77,9 +83,25 @@
     }
     modalApi.lock();
     try {
-      formData.value = await getProductIssueLine(data.id);
-      // 璁剧疆鍒� values
-      await formApi.setValues(formData.value);
+      const line = await getProductIssueLine(data.id);
+      formData.value = line;
+      // 璁剧疆鍒� values锛涜鎯呴噷鐨� unitMeasureId/unitMeasureName 鍗崇墿鏂欎富鍗曚綅锛岃ˉ鍒板墠绔崲绠楃敤鐨勯殣钘忓瓧娈典笂
+      await formApi.setValues({
+        ...line,
+        itemPrimaryUnitId: line.unitMeasureId,
+        itemPrimaryUnitName: line.unitMeasureName,
+      });
+      // 鍥炴樉褰曞叆鍗曚綅鏃惰ˉ榻愭崲绠楁瘮鐜囷紙鍓嶇棰勮鎹㈢畻闇�瑕侊級
+      if (line.enterUnitMeasureId) {
+        await formApi.setFieldValue(
+          'enterUnitRate',
+          await getEnterUnitRate(
+            line.itemId,
+            line.itemModelId,
+            line.enterUnitMeasureId,
+          ),
+        );
+      }
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/wls/productsales/data.ts b/src/views/wls/productsales/data.ts
index 9522d75..6099512 100644
--- a/src/views/wls/productsales/data.ts
+++ b/src/views/wls/productsales/data.ts
@@ -1,5 +1,6 @@
 import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesMdItemApi } from '#/api/mes/md/item';
 import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock';
 import type { MesWmProductSalesApi } from '#/api/mes/wm/productsales';
 import type { MesWmProductSalesDetailApi } from '#/api/mes/wm/productsales/detail';
@@ -25,6 +26,10 @@
 import { getRangePickerDefaultProps } from '#/utils';
 import { MdItemSelect } from '#/views/mes/md/item/components';
 import { WmMaterialStockSelect } from '#/views/wls/materialstock/components';
+import {
+  buildItemUnitSchema,
+  useLineUnitHandlers,
+} from '#/views/wls/utils/item-unit-convert';
 import {
   WmSalesNoticeLineSelect,
   WmSalesNoticeSelect,
@@ -426,9 +431,24 @@
       minWidth: 120,
     },
     {
+      field: 'itemModel',
+      title: '鐗╂枡鍨嬪彿',
+      minWidth: 120,
+    },
+    {
       field: 'unitMeasureName',
-      title: '鍗曚綅',
-      width: 80,
+      title: '涓昏閲忓崟浣�',
+      width: 100,
+    },
+    {
+      field: 'enterUnitMeasureName',
+      title: '褰曞叆鍗曚綅',
+      width: 90,
+    },
+    {
+      field: 'enterQuantity',
+      title: '褰曞叆鏁伴噺',
+      width: 100,
     },
     {
       field: 'quantity',
@@ -472,6 +492,7 @@
   hasNotice: boolean,
   formApi?: VbenFormApi,
 ): VbenFormSchema[] {
+  const unitHandlers = useLineUnitHandlers(formApi);
   return [
     {
       fieldName: 'noticeId',
@@ -488,6 +509,8 @@
       componentProps: {
         // 閫夋嫨鍙戣揣閫氱煡鍗曡鍚庯紝鑷姩鍥炲~鐗╂枡銆佹暟閲忋�佹壒娆°�佹槸鍚︽楠�
         onChange: async (line?: MesWmSalesNoticeLineApi.SalesNoticeLine) => {
+          // 鍒囨崲閫氱煡琛岀瓑浜庡垏鎹骇鍝侊紝鍏堥噸缃瀷鍙蜂笌鍗曚綅鍙e緞锛堜細娓呯┖鏁伴噺锛夛紝鍐嶆寜閫氱煡琛屽洖濉�
+          await unitHandlers.handleItemChange(line?.itemId);
           await formApi?.setValues({
             batchCode: line?.batchCode,
             itemId: line?.itemId,
@@ -510,6 +533,10 @@
       component: markRaw(MdItemSelect),
       componentProps: {
         placeholder: '璇烽�夋嫨浜у搧',
+        // 鍒囨崲浜у搧鍚庯紝閲嶆柊璁板綍鐗╂枡涓诲崟浣嶅苟娓呯┖鍨嬪彿銆佸綍鍏ュ崟浣嶅彛寰�
+        onChange: async (item?: MesMdItemApi.Item) => {
+          await unitHandlers.handleItemChange(item?.id);
+        },
       },
       rules: 'selectRequired',
       dependencies: {
@@ -520,6 +547,8 @@
         }),
       },
     },
+    // 鐗╂枡鍨嬪彿 / 褰曞叆鍗曚綅 / 褰曞叆鏁伴噺 / 鍑哄簱鏁伴噺锛堢墿鏂欎富鍗曚綅鍙e緞锛�
+    ...buildItemUnitSchema(formApi),
     {
       fieldName: 'batchCode',
       label: '鎵规鍙�',
@@ -527,18 +556,6 @@
       componentProps: {
         placeholder: '璇疯緭鍏ユ壒娆″彿',
       },
-    },
-    {
-      fieldName: 'quantity',
-      label: '鍑哄簱鏁伴噺',
-      component: 'InputNumber',
-      componentProps: {
-        class: '!w-full',
-        min: 0,
-        placeholder: '璇疯緭鍏ュ嚭搴撴暟閲�',
-        precision: 2,
-      },
-      rules: 'required',
     },
     {
       fieldName: 'oqcCheckFlag',
diff --git a/src/views/wls/productsales/modules/line-form.vue b/src/views/wls/productsales/modules/line-form.vue
index d8470da..8f8589f 100644
--- a/src/views/wls/productsales/modules/line-form.vue
+++ b/src/views/wls/productsales/modules/line-form.vue
@@ -14,6 +14,10 @@
   updateProductSalesLine,
 } from '#/api/mes/wm/productsales/line';
 import { $t } from '#/locales';
+import {
+  getEnterUnitRate,
+  omitLineTempFields,
+} from '#/views/wls/utils/item-unit-convert';
 
 import { useLineFormSchema } from '../data';
 
@@ -49,9 +53,10 @@
       return;
     }
     modalApi.lock();
-    // 鎻愪氦琛ㄥ崟
-    const data =
-      (await formApi.getValues()) as MesWmProductSalesLineApi.ProductSalesLine;
+    // 鎻愪氦琛ㄥ崟锛堝墧闄や粎鐢ㄤ簬鍓嶇鎹㈢畻/灞曠ず鐨勪复鏃跺瓧娈碉級
+    const data = omitLineTempFields(
+      (await formApi.getValues()) as MesWmProductSalesLineApi.ProductSalesLine,
+    );
     data.salesId = salesId.value;
     try {
       await (formData.value?.id
@@ -87,9 +92,26 @@
     }
     modalApi.lock();
     try {
-      formData.value = await getProductSalesLine(data.id);
-      // 璁剧疆鍒� values
-      await formApi.setValues({ ...formData.value, noticeId: noticeId.value });
+      const line = await getProductSalesLine(data.id);
+      formData.value = line;
+      // 璁剧疆鍒� values锛涜鎯呴噷鐨� unitMeasureId/unitMeasureName 鍗崇墿鏂欎富鍗曚綅锛岃ˉ鍒板墠绔崲绠楃敤鐨勯殣钘忓瓧娈典笂
+      await formApi.setValues({
+        ...line,
+        noticeId: noticeId.value,
+        itemPrimaryUnitId: line.unitMeasureId,
+        itemPrimaryUnitName: line.unitMeasureName,
+      });
+      // 鍥炴樉褰曞叆鍗曚綅鏃惰ˉ榻愭崲绠楁瘮鐜囷紙鍓嶇棰勮鎹㈢畻闇�瑕侊級
+      if (line.enterUnitMeasureId) {
+        await formApi.setFieldValue(
+          'enterUnitRate',
+          await getEnterUnitRate(
+            line.itemId,
+            line.itemModelId,
+            line.enterUnitMeasureId,
+          ),
+        );
+      }
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/wls/returnvendor/data.ts b/src/views/wls/returnvendor/data.ts
index 7a4442d..cc98016 100644
--- a/src/views/wls/returnvendor/data.ts
+++ b/src/views/wls/returnvendor/data.ts
@@ -1,5 +1,6 @@
 import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
 import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesMdItemApi } from '#/api/mes/md/item';
 import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock';
 import type { MesWmReturnVendorApi } from '#/api/mes/wm/returnvendor';
 import type { MesWmReturnVendorDetailApi } from '#/api/mes/wm/returnvendor/detail';
@@ -17,6 +18,10 @@
 import { ErpPurchaseOrderSelect } from '#/views/erp/purchase/order/components';
 import { WmBatchSelect } from '#/views/wls/batch/components';
 import { WmMaterialStockSelect } from '#/views/wls/materialstock/components';
+import {
+  buildItemUnitSchema,
+  useLineUnitHandlers,
+} from '#/views/wls/utils/item-unit-convert';
 import {
   WmWarehouseAreaSelect,
   WmWarehouseLocationSelect,
@@ -301,9 +306,24 @@
       minWidth: 120,
     },
     {
+      field: 'itemModel',
+      title: '鐗╂枡鍨嬪彿',
+      minWidth: 120,
+    },
+    {
       field: 'unitMeasureName',
-      title: '鍗曚綅',
-      width: 80,
+      title: '涓昏閲忓崟浣�',
+      width: 100,
+    },
+    {
+      field: 'enterUnitMeasureName',
+      title: '褰曞叆鍗曚綅',
+      width: 90,
+    },
+    {
+      field: 'enterQuantity',
+      title: '褰曞叆鏁伴噺',
+      width: 100,
     },
     {
       field: 'quantity',
@@ -333,6 +353,7 @@
   vendorId?: number,
   formApi?: VbenFormApi,
 ): VbenFormSchema[] {
+  const unitHandlers = useLineUnitHandlers(formApi);
   return [
     {
       fieldName: 'itemId',
@@ -340,21 +361,15 @@
       component: markRaw(MdItemSelect),
       componentProps: {
         placeholder: '璇烽�夋嫨浜у搧鐗╂枡',
+        // 鍒囨崲浜у搧鍚庯紝閲嶆柊璁板綍鐗╂枡涓诲崟浣嶅苟娓呯┖鍨嬪彿銆佸綍鍏ュ崟浣嶅彛寰�
+        onChange: async (item?: MesMdItemApi.Item) => {
+          await unitHandlers.handleItemChange(item?.id);
+        },
       },
       rules: 'selectRequired',
     },
-    {
-      fieldName: 'quantity',
-      label: '閫�璐ф暟閲�',
-      component: 'InputNumber',
-      componentProps: {
-        class: '!w-full',
-        min: 0,
-        placeholder: '璇疯緭鍏ラ��璐ф暟閲�',
-        precision: 2,
-      },
-      rules: 'required',
-    },
+    // 鐗╂枡鍨嬪彿 / 褰曞叆鍗曚綅 / 褰曞叆鏁伴噺 / 閫�璐ф暟閲忥紙鐗╂枡涓诲崟浣嶅彛寰勶級
+    ...buildItemUnitSchema(formApi, { quantityLabel: '閫�璐ф暟閲�' }),
     {
       fieldName: 'batchId',
       label: '鎵规鍙�',
diff --git a/src/views/wls/returnvendor/modules/line-form.vue b/src/views/wls/returnvendor/modules/line-form.vue
index a7bf7a6..74ed75e 100644
--- a/src/views/wls/returnvendor/modules/line-form.vue
+++ b/src/views/wls/returnvendor/modules/line-form.vue
@@ -14,6 +14,10 @@
   updateReturnVendorLine,
 } from '#/api/mes/wm/returnvendor/line';
 import { $t } from '#/locales';
+import {
+  getEnterUnitRate,
+  omitLineTempFields,
+} from '#/views/wls/utils/item-unit-convert';
 
 import { useLineFormSchema } from '../data';
 
@@ -48,9 +52,10 @@
       return;
     }
     modalApi.lock();
-    // 鎻愪氦琛ㄥ崟
-    const data =
-      (await formApi.getValues()) as MesWmReturnVendorLineApi.ReturnVendorLine;
+    // 鎻愪氦琛ㄥ崟锛堝墧闄や粎鐢ㄤ簬鍓嶇鎹㈢畻/灞曠ず鐨勪复鏃跺瓧娈碉級
+    const data = omitLineTempFields(
+      (await formApi.getValues()) as MesWmReturnVendorLineApi.ReturnVendorLine,
+    );
     data.returnId = returnId.value;
     try {
       await (formData.value?.id
@@ -82,9 +87,25 @@
     }
     modalApi.lock();
     try {
-      formData.value = await getReturnVendorLine(data.id);
-      // 璁剧疆鍒� values
-      await formApi.setValues(formData.value);
+      const line = await getReturnVendorLine(data.id);
+      formData.value = line;
+      // 璁剧疆鍒� values锛涜鎯呴噷鐨� unitMeasureId/unitMeasureName 鍗崇墿鏂欎富鍗曚綅锛岃ˉ鍒板墠绔崲绠楃敤鐨勯殣钘忓瓧娈典笂
+      await formApi.setValues({
+        ...line,
+        itemPrimaryUnitId: line.unitMeasureId,
+        itemPrimaryUnitName: line.unitMeasureName,
+      });
+      // 鍥炴樉褰曞叆鍗曚綅鏃惰ˉ榻愭崲绠楁瘮鐜囷紙鍓嶇棰勮鎹㈢畻闇�瑕侊級
+      if (line.enterUnitMeasureId) {
+        await formApi.setFieldValue(
+          'enterUnitRate',
+          await getEnterUnitRate(
+            line.itemId,
+            line.itemModelId,
+            line.enterUnitMeasureId,
+          ),
+        );
+      }
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/wls/utils/item-unit-convert.ts b/src/views/wls/utils/item-unit-convert.ts
new file mode 100644
index 0000000..cfa4198
--- /dev/null
+++ b/src/views/wls/utils/item-unit-convert.ts
@@ -0,0 +1,435 @@
+import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
+import type { MdmItemModelApi } from '#/api/mdm/item-model';
+
+import { getItem as getMdmItem } from '#/api/mdm/item';
+import { getItemModelListByItem } from '#/api/mdm/item-model';
+
+/** 褰曞叆鍗曚綅閫夐」锛堝惈鍒扮墿鏂欎富鍗曚綅鐨勬崲绠楁瘮鐜囷級 */
+export interface ItemEnterUnitOption {
+  id: number;
+  name: string;
+  rate: number;
+}
+
+/** 鍨嬪彿涓嬫媺閫夐」锛欰piSelect 浼氭妸 id/model 鏄犲皠鎴� value/label锛屽叾浣欏瓧娈靛師鏍蜂繚鐣� */
+export type ItemModelSelectOption = Pick<
+  MdmItemModelApi.ItemModel,
+  'unitMeasureId' | 'unitMeasureName' | 'unitMeasureRate'
+>;
+
+/** 鍑哄簱/鍏ュ簱鍗曡琛ㄥ崟涓笌鍙屽崟浣嶆崲绠楃浉鍏崇殑瀛楁 */
+export interface LineUnitFormValues {
+  enterQuantity?: number;
+  enterUnitMeasureId?: number;
+  enterUnitMeasureName?: string;
+  enterUnitRate?: number;
+  itemId?: number;
+  itemModelId?: number;
+  itemPrimaryUnitId?: number;
+  itemPrimaryUnitName?: string;
+  quantityMax?: number;
+}
+
+/** 鍙屽崟浣嶆崲绠楃敤鐨勪复鏃惰〃鍗曞瓧娈碉紙鍙緵鍓嶇鎹㈢畻涓庡睍绀猴紝鎻愪氦鍓嶅繀椤诲墧闄わ級 */
+const LINE_TEMP_FIELDS = [
+  'itemPrimaryUnitId',
+  'itemPrimaryUnitName',
+  'enterUnitMeasureName',
+  'enterUnitRate',
+] as const;
+
+/** 鍓旈櫎鍙屽崟浣嶆崲绠楃敤鐨勪复鏃跺瓧娈碉紝寰楀埌鍙洿鎺ユ彁浜ょ殑琛屾暟鎹� */
+export function omitLineTempFields<T extends object>(values: T): T {
+  const data = { ...values } as Record<string, unknown>;
+  for (const field of LINE_TEMP_FIELDS) {
+    delete data[field];
+  }
+  return data as T;
+}
+
+/** 鎹㈢畻姣旂巼鏄惁鏈夋晥锛堝繀椤诲ぇ浜� 0锛� */
+function isValidRate(rate?: number): rate is number {
+  return typeof rate === 'number' && rate > 0;
+}
+
+/** 鎹㈢畻缁撴灉鎸� 6 浣嶅皬鏁板睍绀猴紙鏈�缁堜互鍚庣鎹㈢畻缁撴灉涓哄噯锛� */
+function roundQuantity(quantity: number): number {
+  return Math.round(quantity * 1e6) / 1e6;
+}
+
+/**
+ * 鏌ヨ鐗╂枡鍙�夌殑褰曞叆鍗曚綅锛氫富鍗曚綅 + 杈呭崟浣�
+ *
+ * 閫夊畾鍨嬪彿鏃跺彧淇濈暀銆岀墿鏂欎富鍗曚綅銆嶄笌銆屽瀷鍙疯閲忓崟浣嶃�嶄袱绉嶅彛寰勶紝涓庡悗绔崲绠楄鍒欎繚鎸佷竴鑷�
+ */
+export async function getItemEnterUnits(params: {
+  itemId?: number;
+  itemModelId?: number;
+}): Promise<ItemEnterUnitOption[]> {
+  const itemId = params.itemId;
+  if (!itemId) {
+    return [];
+  }
+  const item = await getMdmItem(itemId);
+  if (!item?.unitMeasureId) {
+    return [];
+  }
+  // 涓诲崟浣嶅缁堝彲閫夛紝鎸変富鍗曚綅褰曞叆鏃朵笉鍋氭崲绠�
+  const primaryUnit: ItemEnterUnitOption = {
+    id: item.unitMeasureId,
+    name: item.unitMeasureName || '鐗╂枡涓诲崟浣�',
+    rate: 1,
+  };
+  const unitOptions: ItemEnterUnitOption[] = [primaryUnit];
+  const pushUnit = (id?: number, name?: string, rate?: number) => {
+    if (
+      !id ||
+      id === primaryUnit.id ||
+      !isValidRate(rate) ||
+      unitOptions.some((unit) => unit.id === id)
+    ) {
+      return;
+    }
+    unitOptions.push({ id, name: name || '杈呭崟浣�', rate });
+  };
+  if (params.itemModelId) {
+    const models = await getItemModelListByItem(itemId);
+    const modelOption = models?.find(
+      (option) => option.id === params.itemModelId,
+    );
+    pushUnit(
+      modelOption?.unitMeasureId,
+      modelOption?.unitMeasureName,
+      modelOption?.unitMeasureRate,
+    );
+    return unitOptions;
+  }
+  // 鏈�夊瀷鍙锋椂锛岀墿鏂欎富鏁版嵁涓婇厤缃簡鎹㈢畻姣旂巼鐨勮緟鍗曚綅鍙��
+  pushUnit(item.unitMeasureId2, item.unitMeasureName2, item.unitMeasureRate1);
+  pushUnit(item.unitMeasureId3, item.unitMeasureName3, item.unitMeasureRate2);
+  return unitOptions;
+}
+
+/** 鏌ヨ鏌愪釜褰曞叆鍗曚綅鍒扮墿鏂欎富鍗曚綅鐨勬崲绠楁瘮鐜囷紙缂栬緫鍥炴樉鏃惰ˉ榻愬墠绔崲绠椾笂涓嬫枃锛� */
+export async function getEnterUnitRate(
+  itemId?: number,
+  itemModelId?: number,
+  enterUnitMeasureId?: number,
+): Promise<number | undefined> {
+  if (!itemId || !enterUnitMeasureId) {
+    return undefined;
+  }
+  const unitOptions = await getItemEnterUnits({ itemId, itemModelId });
+  return unitOptions.find((unit) => unit.id === enterUnitMeasureId)?.rate;
+}
+
+/** 琛岃〃鍗曞弻鍗曚綅褰曞叆鐨勮仈鍔ㄦ柟娉� */
+export interface LineUnitHandlers {
+  /** 鏄惁鎸夌墿鏂欎富鍗曚綅褰曞叆锛堟湭閫夊綍鍏ュ崟浣嶃�佹垨褰曞叆鍗曚綅灏辨槸鐗╂枡涓诲崟浣嶏級 */
+  isPrimaryUnitEnter: (values: LineUnitFormValues) => boolean;
+  /** 鍒囨崲鐗╂枡锛氶噸鏂拌褰曠墿鏂欎富鍗曚綅锛屽苟娓呯┖鍨嬪彿銆佸綍鍏ュ崟浣嶄笌鏁伴噺锛堝彛寰勯兘闅忕墿鏂欏彉鍖栵級 */
+  handleItemChange: (itemId?: number) => Promise<void>;
+  /** 閫夋嫨鍨嬪彿锛氶粯璁ゆ寜鍨嬪彿璁¢噺鍗曚綅褰曞叆锛屽苟璁板綍鎹㈢畻姣旂巼 */
+  handleModelChange: (
+    option: ItemModelSelectOption | undefined,
+    values: LineUnitFormValues,
+  ) => Promise<void>;
+  /** 鍒囨崲褰曞叆鍗曚綅锛氳褰曟崲绠楁瘮鐜囧苟绔嬪嵆閲嶇畻涓诲崟浣嶆暟閲忥紱鏀瑰洖涓诲崟浣嶆椂娓呮帀褰曞叆鏁伴噺 */
+  handleEnterUnitChange: (
+    value: number | undefined,
+    option: { label?: string; rate?: number } | undefined,
+    values: LineUnitFormValues,
+  ) => Promise<void>;
+  /** 褰曞叆鏁伴噺鍙樺寲锛氭寜褰曞叆鍗曚綅涓庢崲绠楁瘮鐜囪瘯绠椾富鍗曚綅鏁伴噺锛屼緵鐢ㄦ埛鏍稿鎹㈢畻缁撴灉 */
+  handleEnterQuantityChange: (
+    value: number | undefined,
+    values: LineUnitFormValues,
+  ) => Promise<void>;
+}
+
+/**
+ * 鐢熸垚琛岃〃鍗曠殑鍙屽崟浣嶅綍鍏ヨ仈鍔ㄦ柟娉�
+ *
+ * @param quantityField 鐗╂枡涓诲崟浣嶅彛寰勭殑鏁伴噺瀛楁鍚嶏紙鍑哄簱涓� quantity锛屽叆搴撲负 receivedQuantity锛�
+ */
+export function useLineUnitHandlers(
+  formApi?: VbenFormApi,
+  quantityField = 'quantity',
+): LineUnitHandlers {
+  function isPrimaryUnitEnter(values: LineUnitFormValues) {
+    return (
+      !values.enterUnitMeasureId ||
+      values.enterUnitMeasureId === values.itemPrimaryUnitId
+    );
+  }
+
+  async function handleItemChange(itemId?: number) {
+    // 鍏堟竻绌猴紝閬垮厤涓婁竴涓墿鏂欑殑鍨嬪彿涓庡崟浣嶅彛寰勬畫鐣�
+    await formApi?.setValues({
+      itemId,
+      itemModelId: undefined,
+      itemPrimaryUnitId: undefined,
+      itemPrimaryUnitName: undefined,
+      enterUnitMeasureId: undefined,
+      enterUnitMeasureName: undefined,
+      enterUnitRate: undefined,
+      enterQuantity: undefined,
+      [quantityField]: undefined,
+    });
+    if (!itemId) {
+      return;
+    }
+    const item = await getMdmItem(itemId);
+    await formApi?.setValues({
+      itemPrimaryUnitId: item?.unitMeasureId,
+      itemPrimaryUnitName: item?.unitMeasureName,
+    });
+  }
+
+  async function handleModelChange(
+    option: ItemModelSelectOption | undefined,
+    values: LineUnitFormValues,
+  ) {
+    const enterUnitMeasureId =
+      option?.unitMeasureId && option.unitMeasureId !== values.itemPrimaryUnitId
+        ? option.unitMeasureId
+        : undefined;
+    await formApi?.setValues({
+      enterUnitMeasureId,
+      enterUnitMeasureName: enterUnitMeasureId
+        ? option?.unitMeasureName
+        : undefined,
+      enterUnitRate: enterUnitMeasureId ? option?.unitMeasureRate : undefined,
+      enterQuantity: undefined,
+      // 鍒囧埌鍨嬪彿鍗曚綅褰曞叆鍚庝富鍗曚綅鏁伴噺鐢辩郴缁熸崲绠楋紝鍏堟竻鎺変笂涓�娆″彛寰勭殑鏁板��
+      ...(enterUnitMeasureId ? { [quantityField]: undefined } : {}),
+    });
+  }
+
+  async function handleEnterUnitChange(
+    value: number | undefined,
+    option: { label?: string; rate?: number } | undefined,
+    values: LineUnitFormValues,
+  ) {
+    if (!value || value === values.itemPrimaryUnitId) {
+      await formApi?.setValues({
+        enterUnitMeasureName: undefined,
+        enterUnitRate: undefined,
+        enterQuantity: undefined,
+      });
+      return;
+    }
+    await formApi?.setValues({
+      enterUnitMeasureName: option?.label,
+      enterUnitRate: option?.rate,
+      ...(values.enterQuantity == null || !isValidRate(option?.rate)
+        ? { [quantityField]: undefined }
+        : {
+            [quantityField]: roundQuantity(
+              values.enterQuantity * option.rate,
+            ),
+          }),
+    });
+  }
+
+  async function handleEnterQuantityChange(
+    value: number | undefined,
+    values: LineUnitFormValues,
+  ) {
+    if (isPrimaryUnitEnter(values) || !isValidRate(values.enterUnitRate)) {
+      return;
+    }
+    await formApi?.setFieldValue(
+      quantityField,
+      value == null ? undefined : roundQuantity(value * values.enterUnitRate),
+    );
+  }
+
+  return {
+    handleEnterQuantityChange,
+    handleEnterUnitChange,
+    handleItemChange,
+    handleModelChange,
+    isPrimaryUnitEnter,
+  };
+}
+
+/** 琛岃〃鍗曘�岀墿鏂欏瀷鍙� / 褰曞叆鍗曚綅 / 褰曞叆鏁伴噺 / 涓诲崟浣嶆暟閲忋�嶇浉鍏冲瓧娈电殑閫氱敤閰嶇疆 */
+export interface ItemUnitSchemaOptions {
+  /** 鐗╂枡涓诲崟浣嶅彛寰勭殑鏁伴噺瀛楁鍚嶏紙榛樿 quantity锛� */
+  quantityField?: string;
+  /** 鐗╂枡涓诲崟浣嶅彛寰勭殑鏁伴噺瀛楁鏍囩锛堥粯璁ゃ�屽嚭搴撴暟閲忋�嶏級 */
+  quantityLabel?: string;
+}
+
+/**
+ * 鐢熸垚琛岃〃鍗曠殑鍙屽崟浣嶅瓧娈电粍锛堝瀷鍙枫�佸綍鍏ュ崟浣嶃�佸綍鍏ユ暟閲忋�佷富鍗曚綅鏁伴噺鍙婃崲绠楃敤闅愯棌瀛楁锛�
+ *
+ * 涓诲崟浣嶆暟閲忓瓧娈靛湪鎸夎緟鍗曚綅褰曞叆鏃剁敱绯荤粺鎹㈢畻銆佷笉鍙墜宸ヤ慨鏀癸紱鎸変富鍗曚綅褰曞叆鏃舵墜宸ュ~鍐欏苟鍙楀簱瀛樹笂闄愮害鏉�
+ */
+export function buildItemUnitSchema(
+  formApi?: VbenFormApi,
+  options: ItemUnitSchemaOptions = {},
+): VbenFormSchema[] {
+  const quantityField = options.quantityField ?? 'quantity';
+  const quantityLabel = options.quantityLabel ?? '鍑哄簱鏁伴噺';
+  const handlers = useLineUnitHandlers(formApi, quantityField);
+
+  return [
+    // 鐗╂枡涓诲崟浣嶉殢鎵�閫夌墿鏂欏彉鍖栵紝浣嗚〃鍗� props 鍙兘闈欐�佸0鏄庯紝鏁呯敤闅愯棌瀛楁鎵胯浇
+    {
+      fieldName: 'itemPrimaryUnitId',
+      component: 'Input',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'itemPrimaryUnitName',
+      component: 'Input',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'itemModelId',
+      label: '鐗╂枡鍨嬪彿',
+      component: 'ApiSelect',
+      componentProps: {
+        labelField: 'model',
+        valueField: 'id',
+        allowClear: true,
+        api: (params: { itemId?: number }) =>
+          getItemModelListByItem(params.itemId!),
+        shouldFetch: (params: { itemId?: number }) => !!params?.itemId,
+        placeholder: '璇峰厛閫夋嫨鐗╂枡',
+      },
+      dependencies: {
+        triggerFields: ['itemId'],
+        componentProps: (values) => ({
+          disabled: !values.itemId,
+          params: { itemId: values.itemId },
+          placeholder: values.itemId ? '璇烽�夋嫨鐗╂枡鍨嬪彿' : '璇峰厛閫夋嫨鐗╂枡',
+          // 閫夊畾鍨嬪彿鍚庨粯璁ゆ寜鍏惰閲忓崟浣嶅綍鍏�
+          onChange: (option?: ItemModelSelectOption) =>
+            handlers.handleModelChange(option, values),
+        }),
+      },
+    },
+    {
+      fieldName: 'enterUnitMeasureId',
+      label: '褰曞叆鍗曚綅',
+      component: 'ApiSelect',
+      componentProps: {
+        allowClear: true,
+        labelField: 'name',
+        valueField: 'id',
+        api: (params: { itemId?: number; itemModelId?: number }) =>
+          getItemEnterUnits(params),
+        shouldFetch: (params: { itemId?: number }) => !!params?.itemId,
+        placeholder: '榛樿涓虹墿鏂欎富璁¢噺鍗曚綅',
+      },
+      dependencies: {
+        triggerFields: ['itemId', 'itemModelId', 'itemPrimaryUnitName'],
+        componentProps: (values) => ({
+          disabled: !values.itemId,
+          params: { itemId: values.itemId, itemModelId: values.itemModelId },
+          placeholder: values.itemId
+            ? `榛樿鎸夌墿鏂欎富鍗曚綅${values.itemPrimaryUnitName ? `銆�${values.itemPrimaryUnitName}銆慲 : ''}褰曞叆`
+            : '璇峰厛閫夋嫨鐗╂枡',
+          onChange: (
+            value: number | undefined,
+            option?: { label?: string; rate?: number },
+          ) => handlers.handleEnterUnitChange(value, option, values),
+        }),
+      },
+    },
+    // 褰曞叆鍗曚綅鍚嶇О涓庢崲绠楁瘮鐜囬殢鎵�閫夊崟浣嶅彉鍖栵紝鍚屾牱鐢ㄩ殣钘忓瓧娈垫壙杞�
+    {
+      fieldName: 'enterUnitMeasureName',
+      component: 'Input',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'enterUnitRate',
+      component: 'Input',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
+      fieldName: 'enterQuantity',
+      label: '褰曞叆鏁伴噺',
+      component: 'InputNumber',
+      componentProps: {
+        class: '!w-full',
+        min: 0,
+        precision: 6,
+      },
+      dependencies: {
+        triggerFields: [
+          'enterUnitMeasureId',
+          'enterUnitMeasureName',
+          'itemPrimaryUnitId',
+          'quantityMax',
+          'enterUnitRate',
+        ],
+        show: (values) => !handlers.isPrimaryUnitEnter(values),
+        // 杩斿洖 null 琛ㄧず涓嶈拷鍔犺鍒欙紙鍥為��鍩虹 rules锛�
+        rules: (values) =>
+          handlers.isPrimaryUnitEnter(values) ? null : 'required',
+        componentProps: (values) => ({
+          placeholder: values.enterUnitMeasureName
+            ? `璇峰~鍐欐寜銆�${values.enterUnitMeasureName}銆戣鐨勬暟閲廯
+            : '璇锋寜褰曞叆鍗曚綅濉啓鏁伴噺',
+          // 鎸夎緟鍗曚綅褰曞叆鏃讹紝鍙綍鍏ヤ笂闄愪负銆屽簱瀛樹笂闄� 梅 鎹㈢畻姣旂巼銆�
+          max:
+            values.quantityMax != null && isValidRate(values.enterUnitRate)
+              ? Math.floor((values.quantityMax / values.enterUnitRate) * 1e6) /
+                1e6
+              : undefined,
+          // 褰曞叆鏁伴噺鍙樺寲鏃剁珛鍗宠瘯绠椾富鍗曚綅鏁伴噺
+          onChange: (value: number | undefined) =>
+            handlers.handleEnterQuantityChange(value, values),
+        }),
+      },
+    },
+    {
+      fieldName: quantityField,
+      label: quantityLabel,
+      component: 'InputNumber',
+      componentProps: {
+        class: '!w-full',
+        min: 0.01,
+        precision: 6,
+      },
+      dependencies: {
+        triggerFields: [
+          'enterUnitMeasureId',
+          'itemPrimaryUnitId',
+          'itemPrimaryUnitName',
+          'quantityMax',
+        ],
+        // 鎸変富鍗曚綅褰曞叆鏃舵墜宸ュ~鍐欍�佸繀濉紱鎸夎緟鍗曚綅褰曞叆鏃朵负绯荤粺鎹㈢畻缁撴灉锛屾棤闇�鏍¢獙
+        rules: (values) =>
+          handlers.isPrimaryUnitEnter(values) ? 'required' : null,
+        componentProps: (values) => {
+          const primary = handlers.isPrimaryUnitEnter(values);
+          return {
+            disabled: !primary,
+            max: values.quantityMax,
+            placeholder: primary
+              ? `璇锋寜鐗╂枡涓诲崟浣�${values.itemPrimaryUnitName ? `銆�${values.itemPrimaryUnitName}銆慲 : ''}濉啓鏁伴噺`
+              : '鎸夊綍鍏ュ崟浣嶄笌鎹㈢畻姣旂巼鑷姩鎹㈢畻',
+          };
+        },
+      },
+    },
+  ];
+}

--
Gitblit v1.9.3