From d4e039545c9e97347223eb415fbba85ee01bc263 Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期六, 11 四月 2026 10:10:14 +0800
Subject: [PATCH] #页面优化

---
 rsf-design/src/views/orders/transfer/modules/transfer-orders-panel.vue    |   76 +
 rsf-design/src/views/manager/task-log/taskLogPage.helpers.js              |   41 
 rsf-design/src/views/manager/task-log/index.vue                           |  131 +++
 rsf-design/src/views/orders/transfer/modules/transfer-material-dialog.vue |  252 ++++++
 rsf-design/build/manualChunks.js                                          |   22 
 rsf-design/src/views/orders/transfer/index.vue                            |  252 +++++-
 rsf-design/src/locales/langs/en.json                                      |   36 
 rsf-design/src/views/orders/transfer/transferPage.helpers.js              |  336 +++++++
 rsf-design/src/api/transfer.js                                            |   91 ++
 rsf-design/src/locales/langs/zh.json                                      |   36 
 rsf-design/src/views/orders/transfer/modules/transfer-dialog.vue          |  342 +++++++-
 rsf-design/src/views/manager/task-item-log/taskItemLogTable.columns.js    |   24 
 rsf-design/src/views/manager/task-log/modules/task-log-detail-drawer.vue  |   92 +
 rsf-design/src/api/task-item-log.js                                       |    4 
 rsf-design/src/views/manager/task-log/modules/task-item-log-panel.vue     |  339 ++++++++
 rsf-design/src/views/orders/transfer/modules/transfer-detail-drawer.vue   |   57 +
 rsf-design/src/api/task-log.js                                            |    4 
 rsf-design/src/views/manager/task-log/taskLogTable.columns.js             |   57 +
 rsf-design/src/views/manager/task-item-log/taskItemLogPage.helpers.js     |   47 
 rsf-design/src/views/orders/transfer/transferTable.columns.js             |   72 +
 20 files changed, 2,091 insertions(+), 220 deletions(-)

diff --git a/rsf-design/build/manualChunks.js b/rsf-design/build/manualChunks.js
index f803252..2f80179 100644
--- a/rsf-design/build/manualChunks.js
+++ b/rsf-design/build/manualChunks.js
@@ -16,12 +16,22 @@
     packages: ['xgplayer/']
   },
   {
-    name: 'vendor-element-plus',
-    packages: ['element-plus/', '@element-plus/']
-  },
-  {
-    name: 'vendor-vue',
-    packages: ['vue-router/', 'pinia/', '@vueuse/']
+    // Keep Vue runtime, router/store, vueuse, and Element Plus together.
+    // Splitting these into separate chunks can create circular chunk imports
+    // after Rollup optimization, which then crashes at runtime with
+    // "Cannot access 'x' before initialization".
+    name: 'vendor-framework',
+    packages: [
+      'vue/',
+      '@vue/',
+      'vue-router/',
+      'pinia/',
+      'vue-demi/',
+      '@vueuse/',
+      'element-plus/',
+      '@element-plus/',
+      '@floating-ui/'
+    ]
   },
   {
     name: 'vendor-utils',
diff --git a/rsf-design/src/api/task-item-log.js b/rsf-design/src/api/task-item-log.js
index aee4273..aa4e502 100644
--- a/rsf-design/src/api/task-item-log.js
+++ b/rsf-design/src/api/task-item-log.js
@@ -34,6 +34,10 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
+    orderBy:
+      typeof params.orderBy === 'string' && params.orderBy.trim()
+        ? params.orderBy.trim()
+        : 'create_time desc',
     ...filterParams(params, ['current', 'pageSize', 'size'])
   }
 }
diff --git a/rsf-design/src/api/task-log.js b/rsf-design/src/api/task-log.js
index 342f9ac..356f432 100644
--- a/rsf-design/src/api/task-log.js
+++ b/rsf-design/src/api/task-log.js
@@ -34,6 +34,10 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
+    orderBy:
+      typeof params.orderBy === 'string' && params.orderBy.trim()
+        ? params.orderBy.trim()
+        : 'create_time desc',
     ...filterParams(params, ['current', 'pageSize', 'size'])
   }
 }
diff --git a/rsf-design/src/api/transfer.js b/rsf-design/src/api/transfer.js
index bfb389b..081f3f2 100644
--- a/rsf-design/src/api/transfer.js
+++ b/rsf-design/src/api/transfer.js
@@ -23,11 +23,31 @@
 
 export function buildTransferSearchParams(params = {}) {
   const result = {}
-  ;['condition', 'code', 'orgWareName', 'tarWareName', 'orgAreaName', 'tarAreaName', 'memo', 'timeStart', 'timeEnd'].forEach((key) => {
+  ;[
+    'condition',
+    'code',
+    'orgWareName',
+    'tarWareName',
+    'orgAreaName',
+    'tarAreaName',
+    'memo',
+    'timeStart',
+    'timeEnd',
+    'orderBy'
+  ].forEach((key) => {
     const value = normalizeText(params[key])
     if (value) result[key] = value
   })
-  ;['type', 'source', 'exceStatus', 'status', 'orgWareId', 'tarWareId', 'orgAreaId', 'tarAreaId'].forEach((key) => {
+  ;[
+    'type',
+    'source',
+    'exceStatus',
+    'status',
+    'orgWareId',
+    'tarWareId',
+    'orgAreaId',
+    'tarAreaId'
+  ].forEach((key) => {
     if (params[key] !== '' && params[key] !== undefined && params[key] !== null) {
       result[key] = normalizeNumber(params[key])
     }
@@ -39,16 +59,31 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
+    orderBy: normalizeText(params.orderBy) || 'create_time desc',
     ...buildTransferSearchParams(params)
+  }
+}
+
+export function buildTransferLocsItemPageParams(params = {}) {
+  return {
+    orgAreaId: normalizeNumber(params.orgAreaId, void 0),
+    matnrCode: normalizeText(params.matnrCode || params.code || ''),
+    maktx: normalizeText(params.maktx || params.name || ''),
+    current: params.current || 1,
+    pageSize: params.pageSize || params.size || 20
   }
 }
 
 export function buildTransferOrderPageParams(params = {}) {
   return {
+    ...(normalizeNumber(params.id, void 0) !== void 0
+      ? { id: normalizeNumber(params.id, void 0) }
+      : {}),
     condition: normalizeText(params.code || params.condition),
     code: normalizeText(params.code || params.condition),
     current: params.current || 1,
-    pageSize: params.pageSize || params.size || 20
+    pageSize: params.pageSize || params.size || 20,
+    orderBy: normalizeText(params.orderBy) || 'create_time desc'
   }
 }
 
@@ -67,8 +102,14 @@
   const tarAreaId = normalizeNumber(formData.tarAreaId, void 0)
   const orgArea = optionMap.get(orgAreaId) || {}
   const tarArea = optionMap.get(tarAreaId) || {}
-  const orgWareId = normalizeNumber(orgArea.warehouseId ?? orgArea.warehouse_id ?? orgArea.warehouseIdValue, void 0)
-  const tarWareId = normalizeNumber(tarArea.warehouseId ?? tarArea.warehouse_id ?? tarArea.warehouseIdValue, void 0)
+  const orgWareId = normalizeNumber(
+    orgArea.warehouseId ?? orgArea.warehouse_id ?? orgArea.warehouseIdValue,
+    void 0
+  )
+  const tarWareId = normalizeNumber(
+    tarArea.warehouseId ?? tarArea.warehouse_id ?? tarArea.warehouseIdValue,
+    void 0
+  )
 
   return {
     ...(formData.id !== undefined && formData.id !== null && formData.id !== ''
@@ -82,10 +123,18 @@
     ...(tarAreaId !== void 0 ? { tarAreaId } : {}),
     ...(orgWareId !== void 0 ? { orgWareId } : {}),
     ...(tarWareId !== void 0 ? { tarWareId } : {}),
-    ...(normalizeText(orgArea.name || orgArea.areaName) ? { orgAreaName: normalizeText(orgArea.name || orgArea.areaName) } : {}),
-    ...(normalizeText(tarArea.name || tarArea.areaName) ? { tarAreaName: normalizeText(tarArea.name || tarArea.areaName) } : {}),
-    ...(normalizeText(orgArea.warehouseId$ || orgArea.warehouseName) ? { orgWareName: normalizeText(orgArea.warehouseId$ || orgArea.warehouseName) } : {}),
-    ...(normalizeText(tarArea.warehouseId$ || tarArea.warehouseName) ? { tarWareName: normalizeText(tarArea.warehouseId$ || tarArea.warehouseName) } : {}),
+    ...(normalizeText(orgArea.name || orgArea.areaName)
+      ? { orgAreaName: normalizeText(orgArea.name || orgArea.areaName) }
+      : {}),
+    ...(normalizeText(tarArea.name || tarArea.areaName)
+      ? { tarAreaName: normalizeText(tarArea.name || tarArea.areaName) }
+      : {}),
+    ...(normalizeText(orgArea.warehouseId$ || orgArea.warehouseName)
+      ? { orgWareName: normalizeText(orgArea.warehouseId$ || orgArea.warehouseName) }
+      : {}),
+    ...(normalizeText(tarArea.warehouseId$ || tarArea.warehouseName)
+      ? { tarWareName: normalizeText(tarArea.warehouseId$ || tarArea.warehouseName) }
+      : {}),
     ...(formData.status !== undefined && formData.status !== null && formData.status !== ''
       ? { status: normalizeNumber(formData.status) }
       : { status: 1 }),
@@ -117,14 +166,36 @@
   return request.post({ url: '/transfer/update', params: payload })
 }
 
+export function fetchSaveTransferItems(payload = {}) {
+  return request.post({ url: '/transfer/items/save', params: payload })
+}
+
+export function fetchUpdateTransferItems(payload = {}) {
+  return request.post({ url: '/transfer/items/update', params: payload })
+}
+
 export function fetchTransferOrdersPage(params = {}) {
-  return request.post({ url: '/transfer/orders/page', params: buildTransferOrderPageParams(params) })
+  return request.post({
+    url: '/transfer/orders/page',
+    params: buildTransferOrderPageParams(params)
+  })
+}
+
+export function fetchTransferLocsItemsPage(params = {}) {
+  return request.post({
+    url: '/transfer/locs/items',
+    params: buildTransferLocsItemPageParams(params)
+  })
 }
 
 export function fetchTransferPubOutStock(payload = {}) {
   return request.post({ url: '/transfer/pub/outStock', params: payload })
 }
 
+export function fetchEnabledTransferFields() {
+  return request.get({ url: '/fields/enable/list' })
+}
+
 export async function fetchExportTransferReport(payload = {}, options = {}) {
   return fetch(`${import.meta.env.VITE_API_URL}/transfer/export`, {
     method: 'POST',
diff --git a/rsf-design/src/locales/langs/en.json b/rsf-design/src/locales/langs/en.json
index e391e9f..48b53ed 100644
--- a/rsf-design/src/locales/langs/en.json
+++ b/rsf-design/src/locales/langs/en.json
@@ -1881,17 +1881,23 @@
         "search": {
           "condition": "Keyword",
           "conditionPlaceholder": "Enter No./remark/warehouse/area",
+          "timeStart": "Start Time",
+          "timeEnd": "End Time",
           "code": "Transfer No.",
           "codePlaceholder": "Enter transfer No.",
           "type": "Transfer Type",
           "source": "Source",
           "exceStatus": "Execution Status",
+          "orgWareId": "Source Warehouse ID",
           "orgWareName": "Source Warehouse",
           "orgWareNamePlaceholder": "Enter source warehouse",
+          "tarWareId": "Target Warehouse ID",
           "tarWareName": "Target Warehouse",
           "tarWareNamePlaceholder": "Enter target warehouse",
+          "orgAreaId": "Source Area ID",
           "orgAreaName": "Source Area",
           "orgAreaNamePlaceholder": "Enter source area",
+          "tarAreaId": "Target Area ID",
           "tarAreaName": "Target Area",
           "tarAreaNamePlaceholder": "Enter target area",
           "status": "Status",
@@ -1920,9 +1926,13 @@
         "placeholder": {
           "condition": "Enter No./remark/warehouse/area",
           "code": "Enter transfer No.",
+          "orgWareId": "Enter source warehouse ID",
           "orgWareName": "Enter source warehouse",
+          "tarWareId": "Enter target warehouse ID",
           "tarWareName": "Enter target warehouse",
+          "orgAreaId": "Enter source area ID",
           "orgAreaName": "Enter source area",
+          "tarAreaId": "Enter target area ID",
           "tarAreaName": "Enter target area",
           "memo": "Enter remark"
         },
@@ -1968,15 +1978,36 @@
           "placeholderTarAreaId": "Please select a target area",
           "placeholderStatus": "Please select a status",
           "placeholderMemo": "Please enter a remark",
+          "addMaterial": "Add Material",
+          "deleteSelected": "Delete Selected",
+          "itemCount": "Items: {count}",
+          "supplierCode": "Supplier Code",
+          "supplierName": "Supplier Name",
+          "materialDuplicate": "The selected material already exists",
           "validation": {
             "type": "Please select a transfer type",
             "orgAreaId": "Please select a source area",
-            "tarAreaId": "Please select a target area"
+            "tarAreaId": "Please select a target area",
+            "items": "Please add at least one transfer item",
+            "anfme": "Transfer quantity must be greater than 0"
+          }
+        },
+        "materialDialog": {
+          "title": "Select Transfer Materials",
+          "selected": "Added",
+          "unselected": "Not Added",
+          "table": {
+            "status": "Status"
+          },
+          "placeholder": {
+            "maktx": "Enter material name",
+            "matnrCode": "Enter material code"
           }
         },
         "messages": {
           "detailTimeout": "Transfer detail timed out and waiting has stopped",
           "ordersTimeout": "Transfer items timed out and waiting has stopped",
+          "itemsTimeout": "Transfer edit items timed out and waiting has stopped",
           "ordersLoadFailed": "Failed to load transfer items",
           "detailLoadFailed": "Failed to load transfer detail",
           "publishConfirm": "Are you sure you want to dispatch transfer order \"{code}\"?",
@@ -1984,7 +2015,8 @@
           "publishSuccess": "Dispatched successfully",
           "publishFailed": "Dispatch failed",
           "typeOptionsTimeout": "Transfer type options timed out and waiting has stopped",
-          "areaOptionsTimeout": "Area options timed out and waiting has stopped"
+          "areaOptionsTimeout": "Area options timed out and waiting has stopped",
+          "fieldTimeout": "Field definitions timed out and waiting has stopped"
         }
       },
       "transferItem": {
diff --git a/rsf-design/src/locales/langs/zh.json b/rsf-design/src/locales/langs/zh.json
index b256485..5af6421 100644
--- a/rsf-design/src/locales/langs/zh.json
+++ b/rsf-design/src/locales/langs/zh.json
@@ -1889,17 +1889,23 @@
         "search": {
           "condition": "鍏抽敭瀛�",
           "conditionPlaceholder": "璇疯緭鍏ュ崟鍙�/澶囨敞/浠撳簱/搴撳尯",
+          "timeStart": "寮�濮嬫椂闂�",
+          "timeEnd": "缁撴潫鏃堕棿",
           "code": "璋冩嫧鍗曞彿",
           "codePlaceholder": "璇疯緭鍏ヨ皟鎷ㄥ崟鍙�",
           "type": "璋冩嫧绫诲瀷",
           "source": "鏉ユ簮",
           "exceStatus": "鎵ц鐘舵��",
+          "orgWareId": "婧愪粨搴揑D",
           "orgWareName": "婧愪粨搴�",
           "orgWareNamePlaceholder": "璇疯緭鍏ユ簮浠撳簱",
+          "tarWareId": "鐩爣浠撳簱ID",
           "tarWareName": "鐩爣浠撳簱",
           "tarWareNamePlaceholder": "璇疯緭鍏ョ洰鏍囦粨搴�",
+          "orgAreaId": "婧愬簱鍖篒D",
           "orgAreaName": "婧愬簱鍖�",
           "orgAreaNamePlaceholder": "璇疯緭鍏ユ簮搴撳尯",
+          "tarAreaId": "鐩爣搴撳尯ID",
           "tarAreaName": "鐩爣搴撳尯",
           "tarAreaNamePlaceholder": "璇疯緭鍏ョ洰鏍囧簱鍖�",
           "status": "鐘舵��",
@@ -1928,9 +1934,13 @@
         "placeholder": {
           "condition": "璇疯緭鍏ュ崟鍙�/澶囨敞/浠撳簱/搴撳尯",
           "code": "璇疯緭鍏ヨ皟鎷ㄥ崟鍙�",
+          "orgWareId": "璇疯緭鍏ユ簮浠撳簱ID",
           "orgWareName": "璇疯緭鍏ユ簮浠撳簱",
+          "tarWareId": "璇疯緭鍏ョ洰鏍囦粨搴揑D",
           "tarWareName": "璇疯緭鍏ョ洰鏍囦粨搴�",
+          "orgAreaId": "璇疯緭鍏ユ簮搴撳尯ID",
           "orgAreaName": "璇疯緭鍏ユ簮搴撳尯",
+          "tarAreaId": "璇疯緭鍏ョ洰鏍囧簱鍖篒D",
           "tarAreaName": "璇疯緭鍏ョ洰鏍囧簱鍖�",
           "memo": "璇疯緭鍏ュ娉�"
         },
@@ -1976,15 +1986,36 @@
           "placeholderTarAreaId": "璇烽�夋嫨鐩爣搴撳尯",
           "placeholderStatus": "璇烽�夋嫨鐘舵��",
           "placeholderMemo": "璇疯緭鍏ュ娉�",
+          "addMaterial": "鏂板鐗╂枡",
+          "deleteSelected": "鍒犻櫎閫変腑",
+          "itemCount": "鏄庣粏鏁帮細{count}",
+          "supplierCode": "渚涘簲鍟嗙紪鐮�",
+          "supplierName": "渚涘簲鍟嗗悕绉�",
+          "materialDuplicate": "鎵�閫夌墿鏂欏凡瀛樺湪锛屾棤闇�閲嶅娣诲姞",
           "validation": {
             "type": "璇烽�夋嫨璋冩嫧绫诲瀷",
             "orgAreaId": "璇烽�夋嫨婧愬簱鍖�",
-            "tarAreaId": "璇烽�夋嫨鐩爣搴撳尯"
+            "tarAreaId": "璇烽�夋嫨鐩爣搴撳尯",
+            "items": "璇疯嚦灏戞坊鍔犱竴鏉¤皟鎷ㄦ槑缁�",
+            "anfme": "璋冩嫧鏁伴噺蹇呴』澶т簬 0"
+          }
+        },
+        "materialDialog": {
+          "title": "閫夋嫨璋冩嫧鐗╂枡",
+          "selected": "宸叉坊鍔�",
+          "unselected": "鏈坊鍔�",
+          "table": {
+            "status": "鐘舵��"
+          },
+          "placeholder": {
+            "maktx": "璇疯緭鍏ョ墿鏂欏悕绉�",
+            "matnrCode": "璇疯緭鍏ョ墿鏂欑紪鐮�"
           }
         },
         "messages": {
           "detailTimeout": "璋冩嫧鍗曡鎯呭姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�",
           "ordersTimeout": "璋冩嫧鍗曟槑缁嗗姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�",
+          "itemsTimeout": "璋冩嫧鍗曠紪杈戞槑缁嗗姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�",
           "ordersLoadFailed": "璋冩嫧鍗曟槑缁嗗姞杞藉け璐�",
           "detailLoadFailed": "璋冩嫧鍗曡鎯呭姞杞藉け璐�",
           "publishConfirm": "纭畾瑕佷笅鍙戣皟鎷ㄥ崟銆寋code}銆嶅悧锛�",
@@ -1992,7 +2023,8 @@
           "publishSuccess": "涓嬪彂鎵ц鎴愬姛",
           "publishFailed": "涓嬪彂鎵ц澶辫触",
           "typeOptionsTimeout": "璋冩嫧绫诲瀷閫夐」鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟",
-          "areaOptionsTimeout": "搴撳尯閫夐」鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟"
+          "areaOptionsTimeout": "搴撳尯閫夐」鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟",
+          "fieldTimeout": "鎵╁睍瀛楁鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟"
         }
       },
       "transferItem": {
diff --git a/rsf-design/src/views/manager/task-item-log/taskItemLogPage.helpers.js b/rsf-design/src/views/manager/task-item-log/taskItemLogPage.helpers.js
index 6438952..3ac8de3 100644
--- a/rsf-design/src/views/manager/task-item-log/taskItemLogPage.helpers.js
+++ b/rsf-design/src/views/manager/task-item-log/taskItemLogPage.helpers.js
@@ -29,6 +29,9 @@
     logId: '',
     taskId: '',
     taskItemId: '',
+    orderId: '',
+    orderType: '',
+    orderItemId: '',
     matnrId: '',
     maktx: '',
     platItemId: '',
@@ -39,6 +42,7 @@
     sourceCode: '',
     sourceId: '',
     matnrCode: '',
+    trackCode: '',
     unit: '',
     anfme: '',
     batch: '',
@@ -60,7 +64,8 @@
     keeperId: '',
     keeperName: '',
     targetWarehouseId: '',
-    sourceWarehouseId: ''
+    sourceWarehouseId: '',
+    orderBy: 'create_time desc'
   }
 }
 
@@ -76,6 +81,7 @@
 
   ;[
     'condition',
+    'orderType',
     'maktx',
     'platItemId',
     'platOrderCode',
@@ -83,6 +89,7 @@
     'projectCode',
     'sourceCode',
     'matnrCode',
+    'trackCode',
     'unit',
     'batch',
     'spec',
@@ -109,21 +116,28 @@
       result[key] = value
     }
   })
-
   ;['timeStart', 'timeEnd'].forEach((key) => {
     if (params[key]) {
       result[key] = params[key]
     }
   })
-
-  ;['logId', 'taskId', 'taskItemId', 'matnrId', 'source', 'sourceId', 'anfme', 'status'].forEach(
-    (key) => {
-      const value = normalizeNumber(params[key])
-      if (value !== null) {
-        result[key] = value
-      }
+  ;[
+    'logId',
+    'taskId',
+    'taskItemId',
+    'orderId',
+    'orderItemId',
+    'matnrId',
+    'source',
+    'sourceId',
+    'anfme',
+    'status'
+  ].forEach((key) => {
+    const value = normalizeNumber(params[key])
+    if (value !== null) {
+      result[key] = value
     }
-  )
+  })
 
   return {
     condition: '',
@@ -135,6 +149,7 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
+    orderBy: normalizeText(params.orderBy) || 'create_time desc',
     ...buildTaskItemLogSearchParams(params)
   }
 }
@@ -150,6 +165,9 @@
     logId: record.logId ?? '--',
     taskId: record.taskId ?? '--',
     taskItemId: record.taskItemId ?? '--',
+    orderId: record.orderId ?? '--',
+    orderType: normalizeReportText(record.orderType || record['orderType$']),
+    orderItemId: record.orderItemId ?? '--',
     matnrId: record.matnrId ?? '--',
     maktx: normalizeReportText(record.maktx),
     platItemId: normalizeReportText(record.platItemId),
@@ -160,6 +178,7 @@
     sourceCode: normalizeReportText(record.sourceCode),
     sourceId: record.sourceId ?? '--',
     matnrCode: normalizeReportText(record.matnrCode),
+    trackCode: normalizeReportText(record.trackCode),
     unit: normalizeReportText(record.unit),
     anfme: record.anfme ?? '--',
     batch: normalizeReportText(record.batch),
@@ -194,6 +213,9 @@
   return [
     { source: 'taskId', label: '浠诲姟ID' },
     { source: 'taskItemId', label: '浠诲姟鏄庣粏ID' },
+    { source: 'orderId', label: '鍗曟嵁ID' },
+    { source: 'orderType', label: '鍗曟嵁绫诲瀷' },
+    { source: 'orderItemId', label: '鍗曟嵁鏄庣粏ID' },
     { source: 'matnrId', label: '鐗╂枡ID' },
     { source: 'maktx', label: '鐗╂枡鍚嶇О' },
     { source: 'platWorkCode', label: '宸ュ崟鍙�' },
@@ -202,6 +224,7 @@
     { source: 'source', label: '婧愮紪鐮�' },
     { source: 'sourceCode', label: '婧愬崟鍙�' },
     { source: 'matnrCode', label: '鐗╂枡缂栫爜' },
+    { source: 'trackCode', label: '杩借釜鐮�' },
     { source: 'unit', label: '搴撳瓨鍗曚綅' },
     { source: 'anfme', label: '鏁伴噺' },
     { source: 'batch', label: '搴撳瓨鎵规' },
@@ -223,6 +246,9 @@
     return {
       taskId: row.taskId,
       taskItemId: row.taskItemId,
+      orderId: row.orderId,
+      orderType: row.orderType,
+      orderItemId: row.orderItemId,
       matnrId: row.matnrId,
       maktx: row.maktx,
       platWorkCode: row.platWorkCode,
@@ -231,6 +257,7 @@
       source: row.source,
       sourceCode: row.sourceCode,
       matnrCode: row.matnrCode,
+      trackCode: row.trackCode,
       unit: row.unit,
       anfme: row.anfme,
       batch: row.batch,
diff --git a/rsf-design/src/views/manager/task-item-log/taskItemLogTable.columns.js b/rsf-design/src/views/manager/task-item-log/taskItemLogTable.columns.js
index c358b4e..c6f4fa4 100644
--- a/rsf-design/src/views/manager/task-item-log/taskItemLogTable.columns.js
+++ b/rsf-design/src/views/manager/task-item-log/taskItemLogTable.columns.js
@@ -19,6 +19,24 @@
       showOverflowTooltip: true
     },
     {
+      prop: 'orderId',
+      label: '鍗曟嵁ID',
+      width: 110,
+      showOverflowTooltip: true
+    },
+    {
+      prop: 'orderType',
+      label: '鍗曟嵁绫诲瀷',
+      minWidth: 120,
+      showOverflowTooltip: true
+    },
+    {
+      prop: 'orderItemId',
+      label: '鍗曟嵁鏄庣粏ID',
+      width: 120,
+      showOverflowTooltip: true
+    },
+    {
       prop: 'matnrId',
       label: '鐗╂枡ID',
       width: 110,
@@ -67,6 +85,12 @@
       showOverflowTooltip: true
     },
     {
+      prop: 'trackCode',
+      label: '杩借釜鐮�',
+      minWidth: 150,
+      showOverflowTooltip: true
+    },
+    {
       prop: 'unit',
       label: '搴撳瓨鍗曚綅',
       width: 100,
diff --git a/rsf-design/src/views/manager/task-log/index.vue b/rsf-design/src/views/manager/task-log/index.vue
index 6ccb90b..ec9746a 100644
--- a/rsf-design/src/views/manager/task-log/index.vue
+++ b/rsf-design/src/views/manager/task-log/index.vue
@@ -94,12 +94,42 @@
       }
     },
     {
+      label: t('pages.manager.taskLog.detail.taskId'),
+      key: 'taskId',
+      type: 'inputNumber',
+      props: {
+        clearable: true,
+        controlsPosition: 'right',
+        placeholder: '璇疯緭鍏ヤ换鍔D'
+      }
+    },
+    {
       label: t('pages.manager.taskLog.table.taskCode'),
       key: 'taskCode',
       type: 'input',
       props: {
         clearable: true,
         placeholder: t('pages.manager.taskLog.search.taskCodePlaceholder')
+      }
+    },
+    {
+      label: t('pages.manager.taskLog.table.taskStatus'),
+      key: 'taskStatus',
+      type: 'inputNumber',
+      props: {
+        clearable: true,
+        controlsPosition: 'right',
+        placeholder: '璇疯緭鍏ヤ换鍔$姸鎬�'
+      }
+    },
+    {
+      label: t('pages.manager.taskLog.table.taskType'),
+      key: 'taskType',
+      type: 'inputNumber',
+      props: {
+        clearable: true,
+        controlsPosition: 'right',
+        placeholder: '璇疯緭鍏ヤ换鍔$被鍨�'
       }
     },
     {
@@ -112,12 +142,30 @@
       }
     },
     {
+      label: t('pages.manager.taskLog.table.orgSite'),
+      key: 'orgSite',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: '璇疯緭鍏ユ簮绔欑偣'
+      }
+    },
+    {
       label: t('pages.manager.taskLog.table.targLoc'),
       key: 'targLoc',
       type: 'input',
       props: {
         clearable: true,
         placeholder: t('pages.manager.taskLog.search.targLocPlaceholder')
+      }
+    },
+    {
+      label: t('pages.manager.taskLog.table.targSite'),
+      key: 'targSite',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: '璇疯緭鍏ョ洰鏍囩珯鐐�'
       }
     },
     {
@@ -139,6 +187,44 @@
       }
     },
     {
+      label: t('pages.manager.taskLog.detail.exceStatus'),
+      key: 'exceStatus',
+      type: 'inputNumber',
+      props: {
+        clearable: true,
+        controlsPosition: 'right',
+        placeholder: '璇疯緭鍏ユ墽琛岀姸鎬�'
+      }
+    },
+    {
+      label: t('pages.manager.taskLog.detail.expDesc'),
+      key: 'expDesc',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: '璇疯緭鍏ュ紓甯告弿杩�'
+      }
+    },
+    {
+      label: t('pages.manager.taskLog.detail.sort'),
+      key: 'sort',
+      type: 'inputNumber',
+      props: {
+        clearable: true,
+        controlsPosition: 'right',
+        placeholder: '璇疯緭鍏ヤ紭鍏堢骇'
+      }
+    },
+    {
+      label: t('pages.manager.taskLog.detail.expCode'),
+      key: 'expCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: '璇疯緭鍏ュ紓甯哥紪鐮�'
+      }
+    },
+    {
       label: t('pages.manager.taskLog.search.timeStart'),
       key: 'timeStart',
       type: 'date',
@@ -156,6 +242,47 @@
         clearable: true,
         valueFormat: 'YYYY-MM-DD',
         type: 'date'
+      }
+    },
+    {
+      label: t('pages.manager.taskLog.table.startTime'),
+      key: 'startTime',
+      type: 'date',
+      props: {
+        clearable: true,
+        valueFormat: 'YYYY-MM-DD',
+        type: 'date'
+      }
+    },
+    {
+      label: t('pages.manager.taskLog.table.endTime'),
+      key: 'endTime',
+      type: 'date',
+      props: {
+        clearable: true,
+        valueFormat: 'YYYY-MM-DD',
+        type: 'date'
+      }
+    },
+    {
+      label: t('table.memo'),
+      key: 'memo',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: '璇疯緭鍏ュ娉�'
+      }
+    },
+    {
+      label: t('table.status'),
+      key: 'status',
+      type: 'select',
+      props: {
+        clearable: true,
+        options: [
+          { label: t('common.status.enabled'), value: 1 },
+          { label: t('common.status.disabled'), value: 0 }
+        ]
       }
     }
   ])
@@ -185,7 +312,8 @@
       columnsFactory: () => createTaskLogTableColumns({ handleView: openDetail })
     },
     transform: {
-      dataTransformer: (records) => (Array.isArray(records) ? records.map((item) => normalizeTaskLogRow(item)) : [])
+      dataTransformer: (records) =>
+        Array.isArray(records) ? records.map((item) => normalizeTaskLogRow(item)) : []
     }
   })
 
@@ -241,6 +369,7 @@
   }
 
   function handleSearch(params) {
+    searchForm.value = { ...searchForm.value, ...params }
     replaceSearchParams(buildTaskLogSearchParams(params))
     getData()
   }
diff --git a/rsf-design/src/views/manager/task-log/modules/task-item-log-panel.vue b/rsf-design/src/views/manager/task-log/modules/task-item-log-panel.vue
new file mode 100644
index 0000000..2e75f15
--- /dev/null
+++ b/rsf-design/src/views/manager/task-log/modules/task-item-log-panel.vue
@@ -0,0 +1,339 @@
+<template>
+  <div class="task-item-log-panel space-y-4">
+    <ArtSearchBar
+      v-model="searchForm"
+      :items="searchItems"
+      :showExpand="true"
+      @search="handleSearch"
+      @reset="handleReset"
+    />
+
+    <div class="flex items-center justify-between">
+      <div class="text-sm font-medium text-[var(--art-gray-900)]">浠诲姟鏄庣粏鍘嗗彶妗�</div>
+      <ListExportPrint
+        class="inline-flex"
+        :preview-visible="previewVisible"
+        @update:previewVisible="handlePreviewVisibleChange"
+        :report-title="reportTitle"
+        :selected-rows="selectedRows"
+        :query-params="reportQueryParams"
+        :columns="reportColumns"
+        :preview-rows="previewRows"
+        :preview-meta="previewMeta"
+        :total="pagination.total"
+        :disabled="loading || !effectiveLogId"
+        @export="handleExport"
+        @print="handlePrint"
+      />
+    </div>
+
+    <ArtTable
+      :loading="loading"
+      :data="data"
+      :columns="columns"
+      :pagination="pagination"
+      @selection-change="handleSelectionChange"
+      @pagination:size-change="handleSizeChange"
+      @pagination:current-change="handleCurrentChange"
+    />
+
+    <TaskItemLogDetailDrawer v-model:visible="detailDrawerVisible" :detail="detailData" />
+  </div>
+</template>
+
+<script setup>
+  import { computed, ref, watch } from 'vue'
+  import { useUserStore } from '@/store/modules/user'
+  import { useTable } from '@/hooks/core/useTable'
+  import { usePrintExportPage } from '@/views/system/common/usePrintExportPage'
+  import ListExportPrint from '@/components/biz/list-export-print/index.vue'
+  import { defaultResponseAdapter } from '@/utils/table/tableUtils'
+  import {
+    fetchExportTaskItemLogReport,
+    fetchGetTaskItemLogDetail,
+    fetchGetTaskItemLogMany,
+    fetchTaskItemLogPage
+  } from '@/api/task-item-log'
+  import {
+    buildTaskItemLogPageQueryParams,
+    buildTaskItemLogPrintRows,
+    buildTaskItemLogSearchParams,
+    createTaskItemLogSearchState,
+    getTaskItemLogPaginationKey,
+    getTaskItemLogReportColumns,
+    normalizeTaskItemLogRow,
+    TASK_ITEM_LOG_REPORT_TITLE
+  } from '@/views/manager/task-item-log/taskItemLogPage.helpers.js'
+  import { createTaskItemLogTableColumns } from '@/views/manager/task-item-log/taskItemLogTable.columns.js'
+  import TaskItemLogDetailDrawer from '@/views/manager/task-item-log/modules/task-item-log-detail-drawer.vue'
+
+  defineOptions({ name: 'TaskItemLogPanel' })
+
+  const props = defineProps({
+    logId: { type: [Number, String], default: undefined }
+  })
+
+  const userStore = useUserStore()
+  const searchForm = ref(createTaskItemLogSearchState())
+  const selectedRows = ref([])
+  const detailDrawerVisible = ref(false)
+  const detailData = ref({})
+  const reportTitle = TASK_ITEM_LOG_REPORT_TITLE
+  const reportColumns = getTaskItemLogReportColumns()
+
+  const effectiveLogId = computed(() => {
+    if (props.logId === '' || props.logId === null || props.logId === undefined) return undefined
+    const numericValue = Number(props.logId)
+    return Number.isFinite(numericValue) ? numericValue : undefined
+  })
+
+  const reportQueryParams = computed(() =>
+    buildTaskItemLogSearchParams({
+      ...searchForm.value,
+      logId: effectiveLogId.value
+    })
+  )
+
+  const searchItems = computed(() => [
+    {
+      label: '鍏抽敭瀛�',
+      key: 'condition',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ョ墿鏂�/浠诲姟/鍗曞彿鍏抽敭璇�' }
+    },
+    {
+      label: '浠诲姟ID',
+      key: 'taskId',
+      type: 'inputNumber',
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏ヤ换鍔D' }
+    },
+    {
+      label: '浠诲姟鏄庣粏ID',
+      key: 'taskItemId',
+      type: 'inputNumber',
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏ヤ换鍔℃槑缁咺D' }
+    },
+    {
+      label: '鍗曟嵁ID',
+      key: 'orderId',
+      type: 'inputNumber',
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏ュ崟鎹甀D' }
+    },
+    {
+      label: '鍗曟嵁绫诲瀷',
+      key: 'orderType',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ崟鎹被鍨�' }
+    },
+    {
+      label: '鐗╂枡ID',
+      key: 'matnrId',
+      type: 'inputNumber',
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏ョ墿鏂橧D' }
+    },
+    {
+      label: '鐗╂枡鍚嶇О',
+      key: 'maktx',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ョ墿鏂欏悕绉�' }
+    },
+    {
+      label: '鐗╂枡缂栫爜',
+      key: 'matnrCode',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ョ墿鏂欑紪鐮�' }
+    },
+    {
+      label: '杩借釜鐮�',
+      key: 'trackCode',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ヨ拷韪爜' }
+    },
+    {
+      label: '搴撳瓨鍗曚綅',
+      key: 'unit',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ簱瀛樺崟浣�' }
+    },
+    {
+      label: '鏁伴噺',
+      key: 'anfme',
+      type: 'inputNumber',
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏ユ暟閲�' }
+    },
+    {
+      label: '搴撳瓨鎵规',
+      key: 'batch',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ簱瀛樻壒娆�' }
+    },
+    {
+      label: '瑙勬牸',
+      key: 'spec',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ヨ鏍�' }
+    },
+    {
+      label: '鍨嬪彿',
+      key: 'model',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ瀷鍙�' }
+    },
+    {
+      label: '瀛楁绱㈠紩',
+      key: 'fieldsIndex',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ瓧娈电储寮�' }
+    },
+    {
+      label: '澶囨敞',
+      key: 'memo',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ娉�' }
+    },
+    {
+      label: '鐘舵��',
+      key: 'status',
+      type: 'select',
+      props: {
+        clearable: true,
+        placeholder: '璇烽�夋嫨鐘舵��',
+        options: [
+          { label: '姝e父', value: 1 },
+          { label: '鍐荤粨', value: 0 }
+        ]
+      }
+    },
+    {
+      label: '寮�濮嬫棩鏈�',
+      key: 'timeStart',
+      type: 'date',
+      props: { clearable: true, valueFormat: 'YYYY-MM-DD', type: 'date' }
+    },
+    {
+      label: '缁撴潫鏃ユ湡',
+      key: 'timeEnd',
+      type: 'date',
+      props: { clearable: true, valueFormat: 'YYYY-MM-DD', type: 'date' }
+    }
+  ])
+
+  function buildPanelParams(extra = {}) {
+    return buildTaskItemLogPageQueryParams({
+      ...searchForm.value,
+      ...extra,
+      logId: effectiveLogId.value
+    })
+  }
+
+  function openDetail(row) {
+    detailDrawerVisible.value = true
+    loadDetail(row.id, row)
+  }
+
+  const {
+    columns,
+    data,
+    loading,
+    pagination,
+    getData,
+    replaceSearchParams,
+    handleSizeChange,
+    handleCurrentChange
+  } = useTable({
+    core: {
+      apiFn: fetchTaskItemLogPage,
+      apiParams: buildPanelParams(),
+      paginationKey: getTaskItemLogPaginationKey(),
+      columnsFactory: () => createTaskItemLogTableColumns({ handleView: openDetail })
+    },
+    transform: {
+      dataTransformer: (records) =>
+        Array.isArray(records) ? records.map((item) => normalizeTaskItemLogRow(item)) : []
+    }
+  })
+
+  const resolvePrintRecords = async (payload) => {
+    if (Array.isArray(payload?.ids) && payload.ids.length > 0) {
+      return defaultResponseAdapter(await fetchGetTaskItemLogMany(payload.ids)).records
+    }
+    return defaultResponseAdapter(
+      await fetchTaskItemLogPage({
+        ...reportQueryParams.value,
+        logId: effectiveLogId.value,
+        current: 1,
+        pageSize: Number(pagination.total) > 0 ? Number(pagination.total) : 20
+      })
+    ).records
+  }
+
+  const {
+    previewVisible,
+    previewRows,
+    previewMeta,
+    handlePreviewVisibleChange,
+    handleExport,
+    handlePrint
+  } = usePrintExportPage({
+    downloadFileName: 'task-item-log.xlsx',
+    requestExport: (payload) =>
+      fetchExportTaskItemLogReport(
+        {
+          ...payload,
+          ...reportQueryParams.value,
+          logId: effectiveLogId.value
+        },
+        {
+          headers: {
+            Authorization: userStore.accessToken || ''
+          }
+        }
+      ),
+    resolvePrintRecords,
+    buildPreviewRows: (records) => buildTaskItemLogPrintRows(records),
+    buildPreviewMeta: (rows) => ({
+      reportTitle,
+      reportDate: new Date().toLocaleDateString('zh-CN'),
+      printedAt: new Date().toLocaleString('zh-CN', { hour12: false }),
+      operator: userStore.getUserInfo?.name || userStore.getUserInfo?.username || '',
+      count: rows.length
+    })
+  })
+
+  async function loadDetail(id, fallback) {
+    const detail = await fetchGetTaskItemLogDetail(id)
+    detailData.value = normalizeTaskItemLogRow({
+      ...fallback,
+      ...detail
+    })
+  }
+
+  function handleSelectionChange(rows) {
+    selectedRows.value = Array.isArray(rows) ? rows : []
+  }
+
+  function handleSearch(params) {
+    searchForm.value = { ...searchForm.value, ...params }
+    replaceSearchParams(buildPanelParams(params))
+    getData()
+  }
+
+  function handleReset() {
+    searchForm.value = createTaskItemLogSearchState()
+    selectedRows.value = []
+    replaceSearchParams(buildPanelParams())
+    getData()
+  }
+
+  watch(
+    () => effectiveLogId.value,
+    (value) => {
+      if (value === undefined) return
+      selectedRows.value = []
+      searchForm.value = createTaskItemLogSearchState()
+      replaceSearchParams(buildPanelParams())
+      getData()
+    },
+    { immediate: true }
+  )
+</script>
diff --git a/rsf-design/src/views/manager/task-log/modules/task-log-detail-drawer.vue b/rsf-design/src/views/manager/task-log/modules/task-log-detail-drawer.vue
index d92c299..9e8fc9c 100644
--- a/rsf-design/src/views/manager/task-log/modules/task-log-detail-drawer.vue
+++ b/rsf-design/src/views/manager/task-log/modules/task-log-detail-drawer.vue
@@ -8,31 +8,77 @@
     <ElScrollbar class="h-[calc(100vh-120px)]">
       <div class="flex min-h-full flex-col gap-4 pr-2">
         <ElDescriptions :column="4" border>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.detail.taskId')">{{ detail.taskId ?? '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.taskCode')">{{ detail.taskCode || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.taskStatus')">{{ detail.taskStatusText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.taskType')">{{ detail.taskTypeText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.orgLoc')">{{ detail.orgLoc || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.orgSite')">{{ detail.orgSite || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.targLoc')">{{ detail.targLoc || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.targSite')">{{ detail.targSite || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.barcode')">{{ detail.barcode || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.robotCode')">{{ detail.robotCode || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.detail.exceStatus')">{{ detail.exceStatusText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.detail.sort')">{{ detail.sort ?? '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.detail.expDesc')">{{ detail.expDesc || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.detail.expCode')">{{ detail.expCode || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.startTime')">{{ detail.startTimeText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.endTime')">{{ detail.endTimeText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('table.createBy')">{{ detail.createByText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('table.createTime')">{{ detail.createTimeText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('table.updateBy')">{{ detail.updateByText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('table.updateTime')">{{ detail.updateTimeText || '--' }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.detail.taskId')">{{
+            detail.taskId ?? '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.taskCode')">{{
+            detail.taskCode || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.taskStatus')">{{
+            detail.taskStatusText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.taskType')">{{
+            detail.taskTypeText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.orgLoc')">{{
+            detail.orgLoc || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.orgSite')">{{
+            detail.orgSite || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.targLoc')">{{
+            detail.targLoc || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.targSite')">{{
+            detail.targSite || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.barcode')">{{
+            detail.barcode || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.robotCode')">{{
+            detail.robotCode || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.detail.exceStatus')">{{
+            detail.exceStatusText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.detail.sort')">{{
+            detail.sort ?? '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.detail.expDesc')">{{
+            detail.expDesc || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.detail.expCode')">{{
+            detail.expCode || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.startTime')">{{
+            detail.startTimeText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.manager.taskLog.table.endTime')">{{
+            detail.endTimeText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('table.createBy')">{{
+            detail.createByText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('table.createTime')">{{
+            detail.createTimeText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('table.updateBy')">{{
+            detail.updateByText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('table.updateTime')">{{
+            detail.updateTimeText || '--'
+          }}</ElDescriptionsItem>
           <ElDescriptionsItem :label="t('table.status')">
-            <ElTag :type="detail.statusType || 'info'" effect="light">{{ detail.statusText || '--' }}</ElTag>
+            <ElTag :type="detail.statusType || 'info'" effect="light">{{
+              detail.statusText || '--'
+            }}</ElTag>
           </ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('table.memo')" :span="3">{{ detail.memo || '--' }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('table.memo')" :span="3">{{
+            detail.memo || '--'
+          }}</ElDescriptionsItem>
         </ElDescriptions>
+
+        <TaskItemLogPanel :log-id="detail.id" />
       </div>
     </ElScrollbar>
   </ElDrawer>
@@ -40,6 +86,8 @@
 
 <script setup>
   import { useI18n } from 'vue-i18n'
+  import TaskItemLogPanel from './task-item-log-panel.vue'
+
   defineOptions({ name: 'TaskLogDetailDrawer' })
 
   defineProps({
diff --git a/rsf-design/src/views/manager/task-log/taskLogPage.helpers.js b/rsf-design/src/views/manager/task-log/taskLogPage.helpers.js
index de00109..d4bf5dd 100644
--- a/rsf-design/src/views/manager/task-log/taskLogPage.helpers.js
+++ b/rsf-design/src/views/manager/task-log/taskLogPage.helpers.js
@@ -39,7 +39,10 @@
     sort: '',
     expCode: '',
     startTime: '',
-    endTime: ''
+    endTime: '',
+    memo: '',
+    status: '',
+    orderBy: 'create_time desc'
   }
 }
 
@@ -63,21 +66,20 @@
     'barcode',
     'robotCode',
     'expDesc',
-    'expCode'
+    'expCode',
+    'memo'
   ].forEach((key) => {
     const value = normalizeText(params[key])
     if (value) {
       result[key] = value
     }
   })
-
   ;['timeStart', 'timeEnd', 'startTime', 'endTime'].forEach((key) => {
     if (params[key]) {
       result[key] = params[key]
     }
   })
-
-  ;['taskId', 'taskStatus', 'taskType', 'exceStatus', 'sort'].forEach((key) => {
+  ;['taskId', 'taskStatus', 'taskType', 'exceStatus', 'sort', 'status'].forEach((key) => {
     const value = normalizeNumber(params[key])
     if (value !== null) {
       result[key] = value
@@ -94,6 +96,7 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
+    orderBy: normalizeText(params.orderBy) || 'create_time desc',
     ...buildTaskLogSearchParams(params)
   }
 }
@@ -134,15 +137,27 @@
 
 export function getTaskLogReportColumns() {
   return [
+    { prop: 'id', label: $t('table.id') },
+    { prop: 'taskId', label: $t('pages.manager.taskLog.detail.taskId') },
     { prop: 'taskCode', label: $t('pages.manager.taskLog.table.taskCode') },
     { prop: 'taskStatusText', label: $t('pages.manager.taskLog.table.taskStatus') },
     { prop: 'taskTypeText', label: $t('pages.manager.taskLog.table.taskType') },
     { prop: 'orgLoc', label: $t('pages.manager.taskLog.table.orgLoc') },
+    { prop: 'orgSite', label: $t('pages.manager.taskLog.table.orgSite') },
     { prop: 'targLoc', label: $t('pages.manager.taskLog.table.targLoc') },
+    { prop: 'targSite', label: $t('pages.manager.taskLog.table.targSite') },
     { prop: 'barcode', label: $t('pages.manager.taskLog.table.barcode') },
     { prop: 'robotCode', label: $t('pages.manager.taskLog.table.robotCode') },
+    { prop: 'exceStatusText', label: $t('pages.manager.taskLog.detail.exceStatus') },
+    { prop: 'sort', label: $t('pages.manager.taskLog.detail.sort') },
+    { prop: 'expCode', label: $t('pages.manager.taskLog.detail.expCode') },
     { prop: 'startTimeText', label: $t('pages.manager.taskLog.table.startTime') },
-    { prop: 'endTimeText', label: $t('pages.manager.taskLog.table.endTime') }
+    { prop: 'endTimeText', label: $t('pages.manager.taskLog.table.endTime') },
+    { prop: 'createByText', label: $t('table.createBy') },
+    { prop: 'createTimeText', label: $t('table.createTime') },
+    { prop: 'updateByText', label: $t('table.updateBy') },
+    { prop: 'updateTimeText', label: $t('table.updateTime') },
+    { prop: 'memo', label: $t('table.memo') }
   ]
 }
 
@@ -150,15 +165,27 @@
   return records.map((record) => {
     const row = normalizeTaskLogRow(record)
     return {
+      id: row.id ?? '--',
+      taskId: row.taskId,
       taskCode: row.taskCode,
       taskStatusText: row.taskStatusText,
       taskTypeText: row.taskTypeText,
       orgLoc: row.orgLoc,
+      orgSite: row.orgSite,
       targLoc: row.targLoc,
+      targSite: row.targSite,
       barcode: row.barcode,
       robotCode: row.robotCode,
+      exceStatusText: row.exceStatusText,
+      sort: row.sort,
+      expCode: row.expCode,
       startTimeText: row.startTimeText,
-      endTimeText: row.endTimeText
+      endTimeText: row.endTimeText,
+      createByText: row.createByText,
+      createTimeText: row.createTimeText,
+      updateByText: row.updateByText,
+      updateTimeText: row.updateTimeText,
+      memo: row.memo
     }
   })
 }
diff --git a/rsf-design/src/views/manager/task-log/taskLogTable.columns.js b/rsf-design/src/views/manager/task-log/taskLogTable.columns.js
index cb54e81..278b4f0 100644
--- a/rsf-design/src/views/manager/task-log/taskLogTable.columns.js
+++ b/rsf-design/src/views/manager/task-log/taskLogTable.columns.js
@@ -8,6 +8,20 @@
     { type: 'selection', width: 48, align: 'center' },
     { type: 'globalIndex', label: $t('table.index'), width: 72, align: 'center' },
     {
+      prop: 'id',
+      label: $t('table.id'),
+      width: 90,
+      align: 'center',
+      formatter: (row) => row.id ?? '--'
+    },
+    {
+      prop: 'taskId',
+      label: $t('pages.manager.taskLog.detail.taskId'),
+      width: 110,
+      align: 'center',
+      formatter: (row) => row.taskId ?? '--'
+    },
+    {
       prop: 'taskCode',
       label: $t('pages.manager.taskLog.table.taskCode'),
       minWidth: 170,
@@ -62,6 +76,25 @@
       showOverflowTooltip: true
     },
     {
+      prop: 'exceStatusText',
+      label: $t('pages.manager.taskLog.detail.exceStatus'),
+      minWidth: 120,
+      showOverflowTooltip: true
+    },
+    {
+      prop: 'sort',
+      label: $t('pages.manager.taskLog.detail.sort'),
+      width: 100,
+      align: 'right',
+      formatter: (row) => row.sort ?? '--'
+    },
+    {
+      prop: 'expCode',
+      label: $t('pages.manager.taskLog.detail.expCode'),
+      minWidth: 140,
+      showOverflowTooltip: true
+    },
+    {
       prop: 'startTimeText',
       label: $t('pages.manager.taskLog.table.startTime'),
       minWidth: 170,
@@ -89,12 +122,36 @@
         )
     },
     {
+      prop: 'createByText',
+      label: $t('table.createBy'),
+      minWidth: 120,
+      showOverflowTooltip: true
+    },
+    {
+      prop: 'createTimeText',
+      label: $t('table.createTime'),
+      minWidth: 170,
+      showOverflowTooltip: true
+    },
+    {
+      prop: 'updateByText',
+      label: $t('table.updateBy'),
+      minWidth: 120,
+      showOverflowTooltip: true
+    },
+    {
       prop: 'updateTimeText',
       label: $t('table.updateTime'),
       minWidth: 170,
       showOverflowTooltip: true
     },
     {
+      prop: 'memo',
+      label: $t('table.memo'),
+      minWidth: 180,
+      showOverflowTooltip: true
+    },
+    {
       prop: 'operation',
       label: $t('table.operation'),
       width: 92,
diff --git a/rsf-design/src/views/orders/transfer/index.vue b/rsf-design/src/views/orders/transfer/index.vue
index 3de4a1d..f57b73c 100644
--- a/rsf-design/src/views/orders/transfer/index.vue
+++ b/rsf-design/src/views/orders/transfer/index.vue
@@ -12,7 +12,9 @@
       <ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
         <template #left>
           <ElSpace wrap>
-            <ElButton v-if="canCreate" type="primary" @click="showDialog('add')" v-ripple>{{ t('pages.orders.transfer.actions.add') }}</ElButton>
+            <ElButton v-if="canCreate" type="primary" @click="showDialog('add')" v-ripple>{{
+              t('pages.orders.transfer.actions.add')
+            }}</ElButton>
             <ElButton
               v-if="canDelete"
               type="danger"
@@ -57,6 +59,7 @@
         :transfer-data="currentTransferData"
         :type-options="typeOptions"
         :area-options="areaOptions"
+        :field-definitions="fieldDefinitions"
         :submit-loading="dialogSubmitting"
         @submit="handleDialogSubmit"
       />
@@ -68,6 +71,7 @@
         :detail="detailData"
         :order-rows="detailOrderRows"
         :order-pagination="detailOrderPagination"
+        :on-order-view="handleViewRelatedOrder"
         @size-change="handleDetailSizeChange"
         @current-change="handleDetailCurrentChange"
       />
@@ -90,16 +94,18 @@
   import { guardRequestWithMessage } from '@/utils/sys/requestGuard'
   import { fetchDictDataPage } from '@/api/system-manage'
   import { fetchWarehouseAreasList } from '@/api/warehouse-areas'
+  import { fetchTransferItemList } from '@/api/transfer-item'
   import {
     fetchDeleteTransfer,
+    fetchEnabledTransferFields,
     fetchExportTransferReport,
     fetchTransferDetail,
     fetchTransferMany,
+    fetchSaveTransferItems,
     fetchTransferOrdersPage,
     fetchTransferPage,
     fetchTransferPubOutStock,
-    fetchSaveTransfer,
-    fetchUpdateTransfer
+    fetchUpdateTransferItems
   } from '@/api/transfer'
   import TransferDialog from './modules/transfer-dialog.vue'
   import TransferDetailDrawer from './modules/transfer-detail-drawer.vue'
@@ -107,11 +113,11 @@
   import {
     TRANSFER_REPORT_STYLE,
     buildTransferDetailOrderQueryParams,
-    buildTransferDialogModel,
+    buildTransferItemsSavePayload,
+    buildTransferManageDialogModel,
     buildTransferPageQueryParams,
     buildTransferPrintRows,
     buildTransferReportMeta,
-    buildTransferSavePayload,
     buildTransferSearchParams,
     createTransferFormState,
     createTransferSearchState,
@@ -138,6 +144,7 @@
   const searchForm = ref(createTransferSearchState())
   const typeOptions = ref([])
   const areaOptions = ref([])
+  const fieldDefinitions = ref([])
   const selectedRows = ref([])
   const detailDrawerVisible = ref(false)
   const detailLoading = ref(false)
@@ -159,8 +166,18 @@
 
   const reportQueryParams = computed(() => buildTransferSearchParams(searchForm.value))
   const searchItems = computed(() => [
-    { label: t('pages.orders.transfer.search.condition'), key: 'condition', type: 'input', props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.condition') } },
-    { label: t('pages.orders.transfer.search.code'), key: 'code', type: 'input', props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.code') } },
+    {
+      label: t('pages.orders.transfer.search.condition'),
+      key: 'condition',
+      type: 'input',
+      props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.condition') }
+    },
+    {
+      label: t('pages.orders.transfer.search.code'),
+      key: 'code',
+      type: 'input',
+      props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.code') }
+    },
     {
       label: t('pages.orders.transfer.search.type'),
       key: 'type',
@@ -179,17 +196,94 @@
       type: 'select',
       props: { clearable: true, options: getTransferExceStatusOptions(t) }
     },
-    { label: t('pages.orders.transfer.search.orgWareName'), key: 'orgWareName', type: 'input', props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.orgWareName') } },
-    { label: t('pages.orders.transfer.search.tarWareName'), key: 'tarWareName', type: 'input', props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.tarWareName') } },
-    { label: t('pages.orders.transfer.search.orgAreaName'), key: 'orgAreaName', type: 'input', props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.orgAreaName') } },
-    { label: t('pages.orders.transfer.search.tarAreaName'), key: 'tarAreaName', type: 'input', props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.tarAreaName') } },
+    {
+      label: t('pages.orders.transfer.search.timeStart'),
+      key: 'timeStart',
+      type: 'date',
+      props: { clearable: true, type: 'date', valueFormat: 'YYYY-MM-DD' }
+    },
+    {
+      label: t('pages.orders.transfer.search.timeEnd'),
+      key: 'timeEnd',
+      type: 'date',
+      props: { clearable: true, type: 'date', valueFormat: 'YYYY-MM-DD' }
+    },
+    {
+      label: t('pages.orders.transfer.search.orgWareId'),
+      key: 'orgWareId',
+      type: 'inputNumber',
+      props: {
+        clearable: true,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.transfer.placeholder.orgWareId')
+      }
+    },
+    {
+      label: t('pages.orders.transfer.search.orgWareName'),
+      key: 'orgWareName',
+      type: 'input',
+      props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.orgWareName') }
+    },
+    {
+      label: t('pages.orders.transfer.search.tarWareId'),
+      key: 'tarWareId',
+      type: 'inputNumber',
+      props: {
+        clearable: true,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.transfer.placeholder.tarWareId')
+      }
+    },
+    {
+      label: t('pages.orders.transfer.search.tarWareName'),
+      key: 'tarWareName',
+      type: 'input',
+      props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.tarWareName') }
+    },
+    {
+      label: t('pages.orders.transfer.search.orgAreaId'),
+      key: 'orgAreaId',
+      type: 'inputNumber',
+      props: {
+        clearable: true,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.transfer.placeholder.orgAreaId')
+      }
+    },
+    {
+      label: t('pages.orders.transfer.search.orgAreaName'),
+      key: 'orgAreaName',
+      type: 'input',
+      props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.orgAreaName') }
+    },
+    {
+      label: t('pages.orders.transfer.search.tarAreaId'),
+      key: 'tarAreaId',
+      type: 'inputNumber',
+      props: {
+        clearable: true,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.transfer.placeholder.tarAreaId')
+      }
+    },
+    {
+      label: t('pages.orders.transfer.search.tarAreaName'),
+      key: 'tarAreaName',
+      type: 'input',
+      props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.tarAreaName') }
+    },
     {
       label: t('pages.orders.transfer.search.status'),
       key: 'status',
       type: 'select',
       props: { clearable: true, options: getTransferStatusOptions(t) }
     },
-    { label: t('pages.orders.transfer.search.memo'), key: 'memo', type: 'input', props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.memo') } }
+    {
+      label: t('pages.orders.transfer.search.memo'),
+      key: 'memo',
+      type: 'input',
+      props: { clearable: true, placeholder: t('pages.orders.transfer.placeholder.memo') }
+    }
   ])
 
   function handleSelectionChange(rows) {
@@ -211,24 +305,32 @@
     }
   }
 
-  async function loadTransferOrders(code) {
+  async function loadTransferOrders(transferId, code) {
     detailOrdersLoading.value = true
     try {
       const response = await guardRequestWithMessage(
         fetchTransferOrdersPage(
           buildTransferDetailOrderQueryParams({
+            id: transferId,
             code,
             current: detailOrderPagination.current,
             pageSize: detailOrderPagination.size
           })
         ),
-        { records: [], total: 0, current: detailOrderPagination.current, size: detailOrderPagination.size },
+        {
+          records: [],
+          total: 0,
+          current: detailOrderPagination.current,
+          size: detailOrderPagination.size
+        },
         { timeoutMessage: t('pages.orders.transfer.messages.ordersTimeout') }
       )
       const normalized = defaultResponseAdapter(response)
       detailOrderRows.value = normalized.records.map((item) => normalizeTransferOrderRow(item, t))
       detailOrderPagination.total = Number(normalized.total || 0)
-      detailOrderPagination.current = Number(normalized.current || detailOrderPagination.current || 1)
+      detailOrderPagination.current = Number(
+        normalized.current || detailOrderPagination.current || 1
+      )
       detailOrderPagination.size = Number(normalized.size || detailOrderPagination.size || 20)
     } catch (error) {
       detailOrderRows.value = []
@@ -249,7 +351,7 @@
     try {
       await loadTransferDetail(row.id)
       activeTransferCode.value = detailData.value.code || row.code || activeTransferCode.value
-      await loadTransferOrders(activeTransferCode.value)
+      await loadTransferOrders(row.id, activeTransferCode.value)
     } catch (error) {
       detailDrawerVisible.value = false
       detailData.value = {}
@@ -260,12 +362,23 @@
 
   async function openEditDialog(row) {
     try {
-      const detail = await guardRequestWithMessage(
-        fetchTransferDetail(row.id),
-        {},
-        { timeoutMessage: t('pages.orders.transfer.messages.detailTimeout') }
-      )
-      showDialog('edit', detail)
+      const [detail, itemResponse] = await Promise.all([
+        guardRequestWithMessage(
+          fetchTransferDetail(row.id),
+          {},
+          { timeoutMessage: t('pages.orders.transfer.messages.detailTimeout') }
+        ),
+        guardRequestWithMessage(fetchTransferItemList({ transferId: row.id }), [], {
+          timeoutMessage: t('pages.orders.transfer.messages.itemsTimeout')
+        })
+      ])
+      const itemRecords = Array.isArray(itemResponse)
+        ? itemResponse
+        : defaultResponseAdapter(itemResponse).records
+      showDialog('edit', {
+        transfer: detail,
+        items: itemRecords
+      })
     } catch (error) {
       ElMessage.error(error?.message || t('pages.orders.transfer.messages.detailLoadFailed'))
     }
@@ -273,21 +386,23 @@
 
   async function handlePublish(row) {
     try {
-      await ElMessageBox.confirm(t('pages.orders.transfer.messages.publishConfirm', { code: row.code || row.id }), t('pages.orders.transfer.messages.publishTitle'), {
-        confirmButtonText: t('common.confirm'),
-        cancelButtonText: t('common.cancel'),
-        type: 'warning'
-      })
-      const response = await fetchTransferPubOutStock({ id: row.id })
-      if (response?.code !== 200 && response?.success !== true) {
-        throw new Error(response?.message || t('pages.orders.transfer.messages.publishFailed'))
-      }
-      ElMessage.success(response?.message || t('pages.orders.transfer.messages.publishSuccess'))
+      await ElMessageBox.confirm(
+        t('pages.orders.transfer.messages.publishConfirm', { code: row.code || row.id }),
+        t('pages.orders.transfer.messages.publishTitle'),
+        {
+          confirmButtonText: t('common.confirm'),
+          cancelButtonText: t('common.cancel'),
+          type: 'warning'
+        }
+      )
+      await fetchTransferPubOutStock({ id: row.id })
+      ElMessage.success(t('pages.orders.transfer.messages.publishSuccess'))
       await refreshData()
       if (detailDrawerVisible.value && activeTransferId.value === row.id) {
         await loadTransferDetail(row.id)
-        await loadTransferOrders(row.code || activeTransferCode.value)
+        await loadTransferOrders(row.id, row.code || activeTransferCode.value)
       }
+      router.push('/orders/out-stock')
     } catch (error) {
       if (error === 'cancel' || error?.message === 'cancel') return
       ElMessage.error(error?.message || t('pages.orders.transfer.messages.publishFailed'))
@@ -346,10 +461,15 @@
       apiFn: fetchTransferPage,
       apiParams: buildTransferPageQueryParams(searchForm.value),
       paginationKey: getTransferPaginationKey(),
-      columnsFactory: () => createTransferTableColumns({ handleActionClick })
+      columnsFactory: () =>
+        createTransferTableColumns({
+          handleActionClick,
+          handleViewOrder: handleViewRelatedOrder
+        })
     },
     transform: {
-      dataTransformer: (records) => (Array.isArray(records) ? records.map((item) => normalizeTransferRow(item, t)) : [])
+      dataTransformer: (records) =>
+        Array.isArray(records) ? records.map((item) => normalizeTransferRow(item, t)) : []
     }
   })
 
@@ -357,18 +477,21 @@
     dialogVisible,
     dialogType,
     currentRecord: currentTransferData,
-    selectedRows: crudSelectedRows,
     handleSelectionChange: handleCrudSelectionChange,
     showDialog,
     handleDialogSubmit: handleCrudDialogSubmit,
     handleDelete,
     handleBatchDelete
   } = useCrudPage({
-    createEmptyModel: () => createTransferFormState(),
-    buildEditModel: (record) => buildTransferDialogModel(record),
-    buildSavePayload: (formData) => buildTransferSavePayload(formData, areaOptions.value),
-    saveRequest: fetchSaveTransfer,
-    updateRequest: fetchUpdateTransfer,
+    createEmptyModel: () => ({
+      transfer: createTransferFormState(),
+      items: []
+    }),
+    buildEditModel: (record) => buildTransferManageDialogModel(record, fieldDefinitions.value),
+    buildSavePayload: (formData) =>
+      buildTransferItemsSavePayload(formData, areaOptions.value, fieldDefinitions.value),
+    saveRequest: fetchSaveTransferItems,
+    updateRequest: fetchUpdateTransferItems,
     deleteRequest: fetchDeleteTransfer,
     entityName: t('pages.orders.transfer.entity'),
     resolveRecordLabel: (record) => record?.code || record?.id,
@@ -387,7 +510,12 @@
 
   async function loadTypeOptions() {
     const response = await guardRequestWithMessage(
-      fetchDictDataPage({ current: 1, pageSize: 200, dictTypeCode: 'sys_transfer_type', status: 1 }),
+      fetchDictDataPage({
+        current: 1,
+        pageSize: 200,
+        dictTypeCode: 'sys_transfer_type',
+        status: 1
+      }),
       { records: [] },
       { timeoutMessage: t('pages.orders.transfer.messages.typeOptionsTimeout') }
     )
@@ -403,6 +531,23 @@
     areaOptions.value = resolveTransferAreaOptions(defaultResponseAdapter(response).records)
   }
 
+  async function loadFieldDefinitions() {
+    const records = await guardRequestWithMessage(fetchEnabledTransferFields(), [], {
+      timeoutMessage: t('pages.orders.transfer.messages.fieldTimeout')
+    })
+    fieldDefinitions.value = Array.isArray(records) ? records : []
+  }
+
+  function handleViewRelatedOrder(row) {
+    if (row?.type === 'out') {
+      router.push('/orders/out-stock')
+      return
+    }
+    if (row?.type === 'in') {
+      router.push('/orders/asn-order')
+    }
+  }
+
   function handleSearch(params) {
     searchForm.value = { ...searchForm.value, ...params }
     replaceSearchParams(buildTransferSearchParams(searchForm.value))
@@ -416,13 +561,13 @@
 
   async function handleDetailCurrentChange(current) {
     detailOrderPagination.current = current
-    await loadTransferOrders(activeTransferCode.value)
+    await loadTransferOrders(activeTransferId.value, activeTransferCode.value)
   }
 
   async function handleDetailSizeChange(size) {
     detailOrderPagination.size = size
     detailOrderPagination.current = 1
-    await loadTransferOrders(activeTransferCode.value)
+    await loadTransferOrders(activeTransferId.value, activeTransferCode.value)
   }
 
   const resolvePrintRecords = async (payload) => {
@@ -433,7 +578,8 @@
       await fetchTransferPage({
         ...reportQueryParams.value,
         current: 1,
-        pageSize: Number(pagination.total) > 0 ? Number(pagination.total) : Number(payload?.pageSize) || 20
+        pageSize:
+          Number(pagination.total) > 0 ? Number(pagination.total) : Number(payload?.pageSize) || 20
       })
     ).records
   }
@@ -448,11 +594,14 @@
   } = usePrintExportPage({
     downloadFileName: 'transfer.xlsx',
     requestExport: (payload) =>
-      fetchExportTransferReport(Array.isArray(payload?.ids) && payload.ids.length > 0 ? reportQueryParams.value : payload, {
-        headers: {
-          Authorization: userStore.accessToken || ''
+      fetchExportTransferReport(
+        Array.isArray(payload?.ids) && payload.ids.length > 0 ? reportQueryParams.value : payload,
+        {
+          headers: {
+            Authorization: userStore.accessToken || ''
+          }
         }
-      }),
+      ),
     resolvePrintRecords,
     buildPreviewRows: (records) => buildTransferPrintRows(records, t),
     buildPreviewMeta: (rows) => {
@@ -478,12 +627,13 @@
     buildTransferReportMeta({
       previewMeta: rawPreviewMeta.value,
       count: previewRows.value.length,
-      orientation: rawPreviewMeta.value?.reportStyle?.orientation || TRANSFER_REPORT_STYLE.orientation,
+      orientation:
+        rawPreviewMeta.value?.reportStyle?.orientation || TRANSFER_REPORT_STYLE.orientation,
       t
     })
   )
 
   onMounted(async () => {
-    await Promise.allSettled([loadTypeOptions(), loadAreaOptions()])
+    await Promise.allSettled([loadTypeOptions(), loadAreaOptions(), loadFieldDefinitions()])
   })
 </script>
diff --git a/rsf-design/src/views/orders/transfer/modules/transfer-detail-drawer.vue b/rsf-design/src/views/orders/transfer/modules/transfer-detail-drawer.vue
index 0cff854..60967a9 100644
--- a/rsf-design/src/views/orders/transfer/modules/transfer-detail-drawer.vue
+++ b/rsf-design/src/views/orders/transfer/modules/transfer-detail-drawer.vue
@@ -12,8 +12,12 @@
       </div>
       <div v-else class="space-y-4">
         <ElDescriptions :title="t('pages.orders.transfer.detail.baseInfo')" :column="2" border>
-          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.code')">{{ detail.code || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.type')">{{ detail.typeLabel || '--' }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.code')">{{
+            detail.code || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.type')">{{
+            detail.typeLabel || '--'
+          }}</ElDescriptionsItem>
           <ElDescriptionsItem :label="t('pages.orders.transfer.detail.source')">
             <ElTag :type="detail.sourceTagType || 'info'" effect="light">
               {{ detail.sourceText || '--' }}
@@ -24,28 +28,48 @@
               {{ detail.exceStatusText || '--' }}
             </ElTag>
           </ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.orgWareName')">{{ detail.orgWareName || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.tarWareName')">{{ detail.tarWareName || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.orgAreaName')">{{ detail.orgAreaName || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.tarAreaName')">{{ detail.tarAreaName || '--' }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.orgWareName')">{{
+            detail.orgWareName || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.tarWareName')">{{
+            detail.tarWareName || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.orgAreaName')">{{
+            detail.orgAreaName || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.tarAreaName')">{{
+            detail.tarAreaName || '--'
+          }}</ElDescriptionsItem>
           <ElDescriptionsItem :label="t('pages.orders.transfer.detail.status')">
             <ElTag :type="detail.statusType || 'info'" effect="light">
               {{ detail.statusText || '--' }}
             </ElTag>
           </ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.memo')" :span="2">{{ detail.memo || '--' }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.memo')" :span="2">{{
+            detail.memo || '--'
+          }}</ElDescriptionsItem>
         </ElDescriptions>
 
         <ElDescriptions :title="t('pages.orders.transfer.detail.auditInfo')" :column="2" border>
-          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.createBy')">{{ detail.createByText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.createTime')">{{ detail.createTimeText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.updateBy')">{{ detail.updateByText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.updateTime')">{{ detail.updateTimeText || '--' }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.createBy')">{{
+            detail.createByText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.createTime')">{{
+            detail.createTimeText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.updateBy')">{{
+            detail.updateByText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.transfer.detail.updateTime')">{{
+            detail.updateTimeText || '--'
+          }}</ElDescriptionsItem>
         </ElDescriptions>
 
         <div class="space-y-3">
           <div class="flex items-center justify-between">
-            <div class="text-sm font-medium text-[var(--art-gray-900)]">{{ t('pages.orders.transfer.detail.relatedOrders') }}</div>
+            <div class="text-sm font-medium text-[var(--art-gray-900)]">{{
+              t('pages.orders.transfer.detail.relatedOrders')
+            }}</div>
             <ElTag effect="plain">{{ t('common.count', { count: orderRows.length }) }}</ElTag>
           </div>
           <ArtTable
@@ -76,13 +100,18 @@
     ordersLoading: { type: Boolean, default: false },
     detail: { type: Object, default: () => ({}) },
     orderRows: { type: Array, default: () => [] },
-    orderPagination: { type: Object, default: () => ({ current: 1, size: 20, total: 0 }) }
+    orderPagination: { type: Object, default: () => ({ current: 1, size: 20, total: 0 }) },
+    onOrderView: { type: Function, default: null }
   })
 
   const emit = defineEmits(['update:visible', 'size-change', 'current-change'])
   const { t } = useI18n()
 
-  const orderColumns = createTransferOrderTableColumns()
+  const orderColumns = computed(() =>
+    createTransferOrderTableColumns({
+      handleViewOrder: props.onOrderView
+    })
+  )
 
   const visible = computed({
     get: () => props.visible,
diff --git a/rsf-design/src/views/orders/transfer/modules/transfer-dialog.vue b/rsf-design/src/views/orders/transfer/modules/transfer-dialog.vue
index 43e6a52..a799a82 100644
--- a/rsf-design/src/views/orders/transfer/modules/transfer-dialog.vue
+++ b/rsf-design/src/views/orders/transfer/modules/transfer-dialog.vue
@@ -1,36 +1,226 @@
+<template>
+  <ElDialog
+    :model-value="visible"
+    :title="dialogTitle"
+    width="92%"
+    top="4vh"
+    destroy-on-close
+    @update:model-value="handleVisibleChange"
+    @closed="handleClosed"
+  >
+    <div class="flex flex-col gap-4">
+      <ArtForm
+        ref="formRef"
+        v-model="form"
+        :items="formItems"
+        :rules="rules"
+        :span="8"
+        :gutter="20"
+        label-width="110px"
+        :show-reset="false"
+        :show-submit="false"
+      />
+
+      <div class="flex flex-wrap items-center justify-between gap-3">
+        <ElSpace wrap>
+          <ElButton type="primary" @click="handleOpenMaterialDialog">
+            {{ t('pages.orders.transfer.dialog.addMaterial') }}
+          </ElButton>
+          <ElButton
+            type="danger"
+            plain
+            :disabled="isEdit || selectedItemKeys.length === 0"
+            @click="handleBatchRemove"
+          >
+            {{ t('pages.orders.transfer.dialog.deleteSelected') }}
+          </ElButton>
+        </ElSpace>
+        <div class="text-xs text-[var(--art-gray-600)]">
+          {{ t('pages.orders.transfer.dialog.itemCount', { count: itemRows.length }) }}
+        </div>
+      </div>
+
+      <ElTable
+        :data="itemRows"
+        row-key="__rowKey"
+        border
+        size="small"
+        max-height="420"
+        @selection-change="handleItemSelectionChange"
+      >
+        <ElTableColumn type="selection" width="48" align="center" />
+        <ElTableColumn type="index" :label="t('table.index')" width="72" align="center" />
+        <ElTableColumn
+          prop="matnrCode"
+          :label="t('table.materialCode')"
+          min-width="140"
+          show-overflow-tooltip
+        />
+        <ElTableColumn
+          prop="maktx"
+          :label="t('table.materialName')"
+          min-width="220"
+          show-overflow-tooltip
+        />
+        <ElTableColumn
+          prop="spec"
+          :label="t('pages.orders.transferItem.table.spec')"
+          min-width="140"
+          show-overflow-tooltip
+        />
+        <ElTableColumn
+          prop="model"
+          :label="t('pages.orders.transferItem.table.model')"
+          min-width="140"
+          show-overflow-tooltip
+        />
+        <ElTableColumn :label="t('table.quantity')" width="150" align="right">
+          <template #default="{ row }">
+            <ElInputNumber
+              v-model="row.anfme"
+              :min="0"
+              :precision="2"
+              controls-position="right"
+              class="w-full"
+            />
+          </template>
+        </ElTableColumn>
+        <ElTableColumn
+          :label="t('pages.orders.transfer.dialog.supplierCode')"
+          min-width="150"
+          show-overflow-tooltip
+        >
+          <template #default="{ row }">
+            <ElInput v-model="row.splrCode" clearable />
+          </template>
+        </ElTableColumn>
+        <ElTableColumn
+          :label="t('pages.orders.transfer.dialog.supplierName')"
+          min-width="180"
+          show-overflow-tooltip
+        >
+          <template #default="{ row }">
+            <ElInput v-model="row.splrName" clearable />
+          </template>
+        </ElTableColumn>
+        <ElTableColumn :label="t('table.batch')" min-width="140" show-overflow-tooltip>
+          <template #default="{ row }">
+            <ElInput v-model="row.batch" clearable />
+          </template>
+        </ElTableColumn>
+        <ElTableColumn :label="t('table.unit')" width="120" align="center">
+          <template #default="{ row }">
+            <ElInput v-model="row.unit" clearable />
+          </template>
+        </ElTableColumn>
+        <ElTableColumn
+          v-for="field in fieldDefinitions"
+          :key="field.fields"
+          :label="field.fieldsAlise || field.fields"
+          min-width="140"
+        >
+          <template #default="{ row }">
+            <ElInput v-model="row[field.fields]" clearable />
+          </template>
+        </ElTableColumn>
+        <ElTableColumn :label="t('table.operation')" fixed="right" width="88" align="center">
+          <template #default="{ row }">
+            <ElButton link type="danger" :disabled="isEdit" @click="handleRemoveRow(row)">
+              {{ t('pages.orders.transfer.actions.delete') }}
+            </ElButton>
+          </template>
+        </ElTableColumn>
+      </ElTable>
+    </div>
+
+    <template #footer>
+      <ElSpace>
+        <ElButton @click="handleVisibleChange(false)">{{ t('common.cancel') }}</ElButton>
+        <ElButton type="primary" :loading="submitLoading" @click="handleSubmit">
+          {{ t('common.confirm') }}
+        </ElButton>
+      </ElSpace>
+    </template>
+
+    <TransferMaterialDialog
+      v-model:visible="materialDialogVisible"
+      :org-area-id="form.orgAreaId"
+      :field-definitions="fieldDefinitions"
+      :selected-matnr-ids="selectedMatnrIds"
+      @confirm="handleMaterialConfirm"
+    />
+  </ElDialog>
+</template>
+
 <script setup>
   import { computed, nextTick, reactive, ref, watch } from 'vue'
+  import { ElMessage } from 'element-plus'
   import { useI18n } from 'vue-i18n'
   import ArtForm from '@/components/core/forms/art-form/index.vue'
   import {
-    buildTransferDialogModel,
-    createTransferFormState,
-    getTransferStatusOptions
+    buildTransferManageDialogModel,
+    createTransferEditableItemFromMaterial,
+    createTransferFormState
   } from '../transferPage.helpers.js'
+  import TransferMaterialDialog from './transfer-material-dialog.vue'
 
   const props = defineProps({
     visible: { type: Boolean, default: false },
     dialogType: { type: String, default: 'add' },
-    transferData: { type: Object, default: () => ({}) },
+    transferData: {
+      type: Object,
+      default: () => ({
+        transfer: {},
+        items: []
+      })
+    },
     typeOptions: { type: Array, default: () => [] },
     areaOptions: { type: Array, default: () => [] },
+    fieldDefinitions: { type: Array, default: () => [] },
     submitLoading: { type: Boolean, default: false }
   })
 
   const emit = defineEmits(['update:visible', 'submit'])
+  const { t } = useI18n()
+
   const formRef = ref()
   const form = reactive(createTransferFormState())
-  const { t } = useI18n()
+  const itemRows = ref([])
+  const selectedItemKeys = ref([])
+  const materialDialogVisible = ref(false)
 
   const isEdit = computed(() => props.dialogType === 'edit')
   const dialogTitle = computed(() =>
-    isEdit.value ? t('pages.orders.transfer.dialog.titleEdit') : t('pages.orders.transfer.dialog.titleAdd')
+    isEdit.value
+      ? t('pages.orders.transfer.dialog.titleEdit')
+      : t('pages.orders.transfer.dialog.titleAdd')
+  )
+  const selectedMatnrIds = computed(() =>
+    itemRows.value.map((item) => item.matnrId).filter((item) => item !== undefined && item !== null)
   )
 
   const rules = computed(() => ({
-    type: [{ required: true, message: t('pages.orders.transfer.dialog.validation.type'), trigger: 'change' }],
-    orgAreaId: [{ required: true, message: t('pages.orders.transfer.dialog.validation.orgAreaId'), trigger: 'change' }],
-    tarAreaId: [{ required: true, message: t('pages.orders.transfer.dialog.validation.tarAreaId'), trigger: 'change' }]
+    type: [
+      {
+        required: true,
+        message: t('pages.orders.transfer.dialog.validation.type'),
+        trigger: 'change'
+      }
+    ],
+    orgAreaId: [
+      {
+        required: true,
+        message: t('pages.orders.transfer.dialog.validation.orgAreaId'),
+        trigger: 'change'
+      }
+    ],
+    tarAreaId: [
+      {
+        required: true,
+        message: t('pages.orders.transfer.dialog.validation.tarAreaId'),
+        trigger: 'change'
+      }
+    ]
   }))
 
   const formItems = computed(() => [
@@ -84,7 +274,10 @@
       props: {
         placeholder: t('pages.orders.transfer.dialog.placeholderStatus'),
         clearable: true,
-        options: getTransferStatusOptions()
+        options: [
+          { label: t('pages.orders.transfer.status.normal'), value: 1 },
+          { label: t('pages.orders.transfer.status.frozen'), value: 0 }
+        ]
       }
     },
     {
@@ -101,38 +294,110 @@
     }
   ])
 
-  const loadFormData = () => {
-    Object.assign(form, buildTransferDialogModel(props.transferData))
+  function loadDialogData() {
+    const dialogData = buildTransferManageDialogModel(props.transferData, props.fieldDefinitions)
+    Object.assign(form, dialogData.transfer)
+    itemRows.value = dialogData.items
+    selectedItemKeys.value = []
   }
 
-  const resetForm = () => {
+  function resetDialogData() {
     Object.assign(form, createTransferFormState())
+    itemRows.value = []
+    selectedItemKeys.value = []
+    materialDialogVisible.value = false
     formRef.value?.clearValidate?.()
   }
 
-  const handleSubmit = async () => {
+  function handleItemSelectionChange(rows) {
+    selectedItemKeys.value = Array.isArray(rows) ? rows.map((item) => item.__rowKey) : []
+  }
+
+  function handleRemoveRow(row) {
+    itemRows.value = itemRows.value.filter((item) => item.__rowKey !== row.__rowKey)
+    selectedItemKeys.value = selectedItemKeys.value.filter((item) => item !== row.__rowKey)
+  }
+
+  function handleBatchRemove() {
+    if (!selectedItemKeys.value.length) return
+    const selectedKeySet = new Set(selectedItemKeys.value)
+    itemRows.value = itemRows.value.filter((item) => !selectedKeySet.has(item.__rowKey))
+    selectedItemKeys.value = []
+  }
+
+  function handleOpenMaterialDialog() {
+    if (!form.orgAreaId) {
+      ElMessage.warning(t('pages.orders.transfer.dialog.validation.orgAreaId'))
+      return
+    }
+    if (!form.tarAreaId) {
+      ElMessage.warning(t('pages.orders.transfer.dialog.validation.tarAreaId'))
+      return
+    }
+    materialDialogVisible.value = true
+  }
+
+  function handleMaterialConfirm(rows) {
+    const existingMatnrIds = new Set(selectedMatnrIds.value)
+    const nextRows = rows
+      .map((item) => createTransferEditableItemFromMaterial(item, props.fieldDefinitions))
+      .filter((item) => !existingMatnrIds.has(item.matnrId))
+
+    if (!nextRows.length) {
+      ElMessage.warning(t('pages.orders.transfer.dialog.materialDuplicate'))
+      return
+    }
+
+    itemRows.value = [...itemRows.value, ...nextRows]
+  }
+
+  function validateItems() {
+    if (!itemRows.value.length) {
+      ElMessage.warning(t('pages.orders.transfer.dialog.validation.items'))
+      return false
+    }
+
+    const invalidRow = itemRows.value.find((item) => Number(item.anfme) <= 0)
+    if (invalidRow) {
+      ElMessage.warning(t('pages.orders.transfer.dialog.validation.anfme'))
+      return false
+    }
+
+    return true
+  }
+
+  async function handleSubmit() {
     if (!formRef.value) return
+
     try {
       await formRef.value.validate()
-      emit('submit', { ...form })
     } catch {
       return
     }
+
+    if (!validateItems()) {
+      return
+    }
+
+    emit('submit', {
+      transfer: { ...form },
+      items: itemRows.value.map((item) => ({ ...item }))
+    })
   }
 
-  const handleCancel = () => {
-    emit('update:visible', false)
+  function handleVisibleChange(visible) {
+    emit('update:visible', visible)
   }
 
-  const handleClosed = () => {
-    resetForm()
+  function handleClosed() {
+    resetDialogData()
   }
 
   watch(
     () => props.visible,
     (visible) => {
       if (visible) {
-        loadFormData()
+        loadDialogData()
         nextTick(() => {
           formRef.value?.clearValidate?.()
         })
@@ -145,44 +410,9 @@
     () => props.transferData,
     () => {
       if (props.visible) {
-        loadFormData()
+        loadDialogData()
       }
     },
     { deep: true }
   )
 </script>
-
-<template>
-  <ElDialog
-    :title="dialogTitle"
-    :model-value="visible"
-    width="760px"
-    align-center
-    destroy-on-close
-    @update:model-value="handleCancel"
-    @closed="handleClosed"
-  >
-    <div class="mb-3 rounded-lg border border-[var(--art-border-color)] bg-[var(--art-bg-color)] px-3 py-2 text-xs text-[var(--art-text-gray-600)]">
-      {{ t('pages.orders.transfer.dialog.tip') }}
-    </div>
-
-    <ArtForm
-      ref="formRef"
-      v-model="form"
-      :items="formItems"
-      :rules="rules"
-      :span="12"
-      :gutter="20"
-      label-width="110px"
-      :show-reset="false"
-      :show-submit="false"
-    />
-
-    <template #footer>
-      <span class="dialog-footer">
-        <ElButton @click="handleCancel">{{ t('common.cancel') }}</ElButton>
-        <ElButton type="primary" :loading="submitLoading" @click="handleSubmit">{{ t('common.confirm') }}</ElButton>
-      </span>
-    </template>
-  </ElDialog>
-</template>
diff --git a/rsf-design/src/views/orders/transfer/modules/transfer-material-dialog.vue b/rsf-design/src/views/orders/transfer/modules/transfer-material-dialog.vue
new file mode 100644
index 0000000..7b9c46c
--- /dev/null
+++ b/rsf-design/src/views/orders/transfer/modules/transfer-material-dialog.vue
@@ -0,0 +1,252 @@
+<template>
+  <ElDialog
+    :model-value="visible"
+    :title="t('pages.orders.transfer.materialDialog.title')"
+    width="88%"
+    top="5vh"
+    destroy-on-close
+    @update:model-value="handleVisibleChange"
+  >
+    <div class="flex flex-col gap-4">
+      <ArtSearchBar
+        v-model="searchForm"
+        :items="searchItems"
+        :showExpand="false"
+        @search="handleSearch"
+        @reset="handleReset"
+      />
+
+      <ArtTable
+        :loading="loading"
+        :data="data"
+        :columns="columns"
+        :pagination="pagination"
+        @selection-change="handleSelectionChange"
+        @pagination:size-change="handleSizeChange"
+        @pagination:current-change="handleCurrentChange"
+      />
+    </div>
+
+    <template #footer>
+      <ElSpace>
+        <ElButton @click="handleVisibleChange(false)">{{ t('common.cancel') }}</ElButton>
+        <ElButton type="primary" @click="handleConfirm">{{ t('common.confirm') }}</ElButton>
+      </ElSpace>
+    </template>
+  </ElDialog>
+</template>
+
+<script setup>
+  import { computed, h, ref, watch } from 'vue'
+  import { ElTag } from 'element-plus'
+  import { useI18n } from 'vue-i18n'
+  import { useTable } from '@/hooks/core/useTable'
+  import { fetchTransferLocsItemsPage } from '@/api/transfer'
+  import {
+    createTransferEditableItemFromMaterial,
+    createTransferMaterialSearchState
+  } from '../transferPage.helpers'
+
+  const props = defineProps({
+    visible: { type: Boolean, default: false },
+    orgAreaId: { type: [Number, String], default: undefined },
+    fieldDefinitions: { type: Array, default: () => [] },
+    selectedMatnrIds: { type: Array, default: () => [] }
+  })
+
+  const emit = defineEmits(['update:visible', 'confirm'])
+  const { t } = useI18n()
+
+  const searchForm = ref(createTransferMaterialSearchState())
+  const selectedRows = ref([])
+
+  const searchItems = computed(() => [
+    {
+      label: t('table.materialName'),
+      key: 'maktx',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.transfer.materialDialog.placeholder.maktx')
+      }
+    },
+    {
+      label: t('table.materialCode'),
+      key: 'matnrCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.transfer.materialDialog.placeholder.matnrCode')
+      }
+    }
+  ])
+
+  const createColumns = () => {
+    const baseColumns = [
+      { type: 'selection', width: 48, align: 'center' },
+      { type: 'globalIndex', label: t('table.index'), width: 72, align: 'center' },
+      {
+        prop: 'matnrCode',
+        label: t('table.materialCode'),
+        minWidth: 150,
+        showOverflowTooltip: true
+      },
+      {
+        prop: 'maktx',
+        label: t('table.materialName'),
+        minWidth: 220,
+        showOverflowTooltip: true
+      },
+      {
+        prop: 'spec',
+        label: t('pages.orders.transferItem.table.spec'),
+        minWidth: 140,
+        showOverflowTooltip: true
+      },
+      {
+        prop: 'model',
+        label: t('pages.orders.transferItem.table.model'),
+        minWidth: 140,
+        showOverflowTooltip: true
+      },
+      {
+        prop: 'batch',
+        label: t('table.batch'),
+        minWidth: 140,
+        showOverflowTooltip: true
+      },
+      {
+        prop: 'unit',
+        label: t('table.unit'),
+        width: 100,
+        align: 'center'
+      },
+      {
+        prop: 'anfme',
+        label: t('table.quantity'),
+        width: 110,
+        align: 'right'
+      },
+      {
+        prop: 'selected',
+        label: t('pages.orders.transfer.materialDialog.table.status'),
+        width: 110,
+        formatter: (row) =>
+          h(
+            ElTag,
+            {
+              type: props.selectedMatnrIds.includes(row.matnrId) ? 'warning' : 'info',
+              effect: 'light'
+            },
+            () =>
+              props.selectedMatnrIds.includes(row.matnrId)
+                ? t('pages.orders.transfer.materialDialog.selected')
+                : t('pages.orders.transfer.materialDialog.unselected')
+          )
+      }
+    ]
+
+    return [
+      ...baseColumns,
+      ...props.fieldDefinitions.map((item) => ({
+        prop: item.fields,
+        label: item.fieldsAlise || item.fields,
+        minWidth: 140,
+        showOverflowTooltip: true,
+        visible: false
+      }))
+    ]
+  }
+
+  const {
+    data,
+    loading,
+    pagination,
+    columns,
+    replaceSearchParams,
+    handleSizeChange,
+    handleCurrentChange,
+    getData,
+    resetColumns
+  } = useTable({
+    core: {
+      apiFn: fetchTransferLocsItemsPage,
+      apiParams: {
+        ...createTransferMaterialSearchState(),
+        orgAreaId: props.orgAreaId
+      },
+      immediate: false,
+      columnsFactory: createColumns
+    },
+    transform: {
+      dataTransformer: (records) =>
+        Array.isArray(records)
+          ? records.map((item) => {
+              const editable = createTransferEditableItemFromMaterial(item, props.fieldDefinitions)
+              return {
+                ...item,
+                ...editable
+              }
+            })
+          : []
+    }
+  })
+
+  function buildParams() {
+    return {
+      ...searchForm.value,
+      orgAreaId: props.orgAreaId
+    }
+  }
+
+  function handleSelectionChange(rows) {
+    selectedRows.value = Array.isArray(rows) ? rows : []
+  }
+
+  function handleSearch(params) {
+    searchForm.value = { ...searchForm.value, ...params }
+    replaceSearchParams(buildParams())
+    getData()
+  }
+
+  function handleReset() {
+    searchForm.value = createTransferMaterialSearchState()
+    replaceSearchParams(buildParams())
+    getData()
+  }
+
+  function handleConfirm() {
+    emit(
+      'confirm',
+      selectedRows.value.map((item) => ({ ...item }))
+    )
+    handleVisibleChange(false)
+  }
+
+  function handleVisibleChange(visible) {
+    emit('update:visible', visible)
+  }
+
+  watch(
+    () => props.visible,
+    (visible) => {
+      if (!visible) {
+        searchForm.value = createTransferMaterialSearchState()
+        selectedRows.value = []
+        return
+      }
+
+      searchForm.value = createTransferMaterialSearchState()
+      replaceSearchParams(buildParams())
+      getData()
+    }
+  )
+
+  watch(
+    () => props.fieldDefinitions,
+    () => {
+      resetColumns?.()
+    },
+    { deep: true }
+  )
+</script>
diff --git a/rsf-design/src/views/orders/transfer/modules/transfer-orders-panel.vue b/rsf-design/src/views/orders/transfer/modules/transfer-orders-panel.vue
new file mode 100644
index 0000000..86525d1
--- /dev/null
+++ b/rsf-design/src/views/orders/transfer/modules/transfer-orders-panel.vue
@@ -0,0 +1,76 @@
+<template>
+  <div class="transfer-orders-panel">
+    <ArtTable :loading="loading" :data="rows" :columns="columns" />
+  </div>
+</template>
+
+<script setup>
+  import { computed, ref, watch } from 'vue'
+  import { useI18n } from 'vue-i18n'
+  import { defaultResponseAdapter } from '@/utils/table/tableUtils'
+  import { fetchTransferOrdersPage } from '@/api/transfer'
+  import {
+    buildTransferDetailOrderQueryParams,
+    normalizeTransferOrderRow
+  } from '../transferPage.helpers.js'
+  import { createTransferOrderTableColumns } from '../transferTable.columns.js'
+
+  defineOptions({ name: 'TransferOrdersPanel' })
+
+  const props = defineProps({
+    transferId: { type: [Number, String], default: undefined },
+    transferCode: { type: String, default: '' },
+    onOrderView: { type: Function, default: null }
+  })
+
+  const loading = ref(false)
+  const rows = ref([])
+  const { t } = useI18n()
+
+  const columns = computed(() =>
+    createTransferOrderTableColumns({
+      handleViewOrder: props.onOrderView
+    })
+  )
+
+  async function loadRows() {
+    if (props.transferId === undefined || props.transferId === null || props.transferId === '') {
+      rows.value = []
+      return
+    }
+
+    loading.value = true
+    try {
+      const response = await fetchTransferOrdersPage(
+        buildTransferDetailOrderQueryParams({
+          id: props.transferId,
+          code: props.transferCode,
+          current: 1,
+          pageSize: 200
+        })
+      )
+      const records = defaultResponseAdapter(response).records
+      rows.value = Array.isArray(records)
+        ? records.map((item) => normalizeTransferOrderRow(item, t))
+        : []
+    } catch {
+      rows.value = []
+    } finally {
+      loading.value = false
+    }
+  }
+
+  watch(
+    () => [props.transferId, props.transferCode],
+    () => {
+      loadRows()
+    },
+    { immediate: true }
+  )
+</script>
+
+<style scoped>
+  .transfer-orders-panel {
+    padding: 12px 0;
+  }
+</style>
diff --git a/rsf-design/src/views/orders/transfer/transferPage.helpers.js b/rsf-design/src/views/orders/transfer/transferPage.helpers.js
index 2b3200a..8b5a1cf 100644
--- a/rsf-design/src/views/orders/transfer/transferPage.helpers.js
+++ b/rsf-design/src/views/orders/transfer/transferPage.helpers.js
@@ -53,18 +53,23 @@
 export function createTransferSearchState() {
   return {
     condition: '',
+    timeStart: '',
+    timeEnd: '',
     code: '',
     type: '',
     source: '',
     exceStatus: '',
+    orgWareId: '',
     orgWareName: '',
+    tarWareId: '',
     tarWareName: '',
+    orgAreaId: '',
     orgAreaName: '',
+    tarAreaId: '',
     tarAreaName: '',
     memo: '',
     status: '',
-    timeStart: '',
-    timeEnd: ''
+    orderBy: 'create_time desc'
   }
 }
 
@@ -85,10 +90,18 @@
 export function buildTransferDialogModel(record = {}) {
   return {
     ...createTransferFormState(),
-    ...(record.id !== undefined && record.id !== null && record.id !== '' ? { id: Number(record.id) } : {}),
+    ...(record.id !== undefined && record.id !== null && record.id !== ''
+      ? { id: Number(record.id) }
+      : {}),
     code: normalizeText(record.code || ''),
-    type: record.type !== undefined && record.type !== null && record.type !== '' ? Number(record.type) : '',
-    source: record.source !== undefined && record.source !== null && record.source !== '' ? Number(record.source) : 2,
+    type:
+      record.type !== undefined && record.type !== null && record.type !== ''
+        ? Number(record.type)
+        : '',
+    source:
+      record.source !== undefined && record.source !== null && record.source !== ''
+        ? Number(record.source)
+        : 2,
     exceStatus:
       record.exceStatus !== undefined && record.exceStatus !== null && record.exceStatus !== ''
         ? Number(record.exceStatus)
@@ -103,6 +116,35 @@
         : void 0,
     status: record.status !== undefined && record.status !== null ? Number(record.status) : 1,
     memo: normalizeText(record.memo || '')
+  }
+}
+
+function createTransferItemRowKey(record = {}) {
+  const keySeed = [
+    record.id ?? 'new',
+    record.transferId ?? 'transfer',
+    record.matnrId ?? 'matnr',
+    record.batch ?? '',
+    record.fieldsIndex ?? ''
+  ]
+  return keySeed.join('-')
+}
+
+function resolveDynamicFieldValues(record = {}, fieldDefinitions = []) {
+  return Object.fromEntries(
+    (Array.isArray(fieldDefinitions) ? fieldDefinitions : []).map((item) => [
+      item.fields,
+      record[item.fields] ?? record.extendFields?.[item.fields] ?? ''
+    ])
+  )
+}
+
+export function buildTransferManageDialogModel(record = {}, fieldDefinitions = []) {
+  const transferRecord = record?.transfer || record || {}
+  const itemRecords = Array.isArray(record?.items) ? record.items : []
+  return {
+    transfer: buildTransferDialogModel(transferRecord),
+    items: itemRecords.map((item) => buildTransferEditableItem(item, fieldDefinitions))
   }
 }
 
@@ -136,11 +178,28 @@
 
 export function buildTransferSearchParams(params = {}) {
   const result = {}
-  ;['condition', 'code', 'orgWareName', 'tarWareName', 'orgAreaName', 'tarAreaName', 'memo'].forEach((key) => {
+  ;[
+    'condition',
+    'code',
+    'orgWareName',
+    'tarWareName',
+    'orgAreaName',
+    'tarAreaName',
+    'memo'
+  ].forEach((key) => {
     const value = normalizeText(params[key])
     if (value) result[key] = value
   })
-  ;['type', 'source', 'exceStatus', 'status', 'orgWareId', 'tarWareId', 'orgAreaId', 'tarAreaId'].forEach((key) => {
+  ;[
+    'type',
+    'source',
+    'exceStatus',
+    'status',
+    'orgWareId',
+    'tarWareId',
+    'orgAreaId',
+    'tarAreaId'
+  ].forEach((key) => {
     if (params[key] !== '' && params[key] !== undefined && params[key] !== null) {
       result[key] = normalizeNumber(params[key])
     }
@@ -158,16 +217,21 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
+    orderBy: normalizeText(params.orderBy) || 'create_time desc',
     ...buildTransferSearchParams(params)
   }
 }
 
 export function buildTransferDetailOrderQueryParams(params = {}) {
   return {
+    ...(normalizeNumber(params.id, void 0) !== void 0
+      ? { id: normalizeNumber(params.id, void 0) }
+      : {}),
     condition: normalizeText(params.code || params.condition),
     code: normalizeText(params.code || params.condition),
     current: params.current || 1,
-    pageSize: params.pageSize || params.size || 20
+    pageSize: params.pageSize || params.size || 20,
+    orderBy: normalizeText(params.orderBy) || 'create_time desc'
   }
 }
 
@@ -186,8 +250,14 @@
   const tarAreaId = normalizeNumber(formData.tarAreaId, void 0)
   const orgArea = optionMap.get(orgAreaId) || {}
   const tarArea = optionMap.get(tarAreaId) || {}
-  const orgWareId = normalizeNumber(orgArea.warehouseId ?? orgArea.warehouse_id ?? orgArea.warehouseIdValue, void 0)
-  const tarWareId = normalizeNumber(tarArea.warehouseId ?? tarArea.warehouse_id ?? tarArea.warehouseIdValue, void 0)
+  const orgWareId = normalizeNumber(
+    orgArea.warehouseId ?? orgArea.warehouse_id ?? orgArea.warehouseIdValue,
+    void 0
+  )
+  const tarWareId = normalizeNumber(
+    tarArea.warehouseId ?? tarArea.warehouse_id ?? tarArea.warehouseIdValue,
+    void 0
+  )
 
   return {
     ...(formData.id !== undefined && formData.id !== null && formData.id !== ''
@@ -200,17 +270,27 @@
     ...(formData.source !== undefined && formData.source !== null && formData.source !== ''
       ? { source: normalizeNumber(formData.source) }
       : {}),
-    ...(formData.exceStatus !== undefined && formData.exceStatus !== null && formData.exceStatus !== ''
+    ...(formData.exceStatus !== undefined &&
+    formData.exceStatus !== null &&
+    formData.exceStatus !== ''
       ? { exceStatus: normalizeNumber(formData.exceStatus) }
       : {}),
     ...(orgAreaId !== void 0 ? { orgAreaId } : {}),
     ...(tarAreaId !== void 0 ? { tarAreaId } : {}),
     ...(orgWareId !== void 0 ? { orgWareId } : {}),
     ...(tarWareId !== void 0 ? { tarWareId } : {}),
-    ...(normalizeText(orgArea.name || orgArea.areaName) ? { orgAreaName: normalizeText(orgArea.name || orgArea.areaName) } : {}),
-    ...(normalizeText(tarArea.name || tarArea.areaName) ? { tarAreaName: normalizeText(tarArea.name || tarArea.areaName) } : {}),
-    ...(normalizeText(orgArea.warehouseId$ || orgArea.warehouseName) ? { orgWareName: normalizeText(orgArea.warehouseId$ || orgArea.warehouseName) } : {}),
-    ...(normalizeText(tarArea.warehouseId$ || tarArea.warehouseName) ? { tarWareName: normalizeText(tarArea.warehouseId$ || tarArea.warehouseName) } : {}),
+    ...(normalizeText(orgArea.name || orgArea.areaName)
+      ? { orgAreaName: normalizeText(orgArea.name || orgArea.areaName) }
+      : {}),
+    ...(normalizeText(tarArea.name || tarArea.areaName)
+      ? { tarAreaName: normalizeText(tarArea.name || tarArea.areaName) }
+      : {}),
+    ...(normalizeText(orgArea.warehouseId$ || orgArea.warehouseName)
+      ? { orgWareName: normalizeText(orgArea.warehouseId$ || orgArea.warehouseName) }
+      : {}),
+    ...(normalizeText(tarArea.warehouseId$ || tarArea.warehouseName)
+      ? { tarWareName: normalizeText(tarArea.warehouseId$ || tarArea.warehouseName) }
+      : {}),
     ...(formData.status !== undefined && formData.status !== null && formData.status !== ''
       ? { status: normalizeNumber(formData.status) }
       : { status: 1 }),
@@ -219,16 +299,34 @@
 }
 
 function resolveAreaText(record = {}, key) {
-  return normalizeText(record[`${key}AreaName$`] || record[`${key}AreaName`] || record[`${key}AreaId$`] || record[`${key}AreaId`])
+  return normalizeText(
+    record[`${key}AreaName$`] ||
+      record[`${key}AreaName`] ||
+      record[`${key}AreaId$`] ||
+      record[`${key}AreaId`]
+  )
 }
 
 function resolveWarehouseText(record = {}, key) {
-  return normalizeText(record[`${key}WareName$`] || record[`${key}WareName`] || record[`${key}WareId$`] || record[`${key}WareId`])
+  return normalizeText(
+    record[`${key}WareName$`] ||
+      record[`${key}WareName`] ||
+      record[`${key}WareId$`] ||
+      record[`${key}WareId`]
+  )
 }
 
 export function normalizeTransferRow(record = {}, t = $t) {
-  const statusMeta = metaByValue(record.statusBool ?? record.status, getTransferStatusMetaMap(t), t('common.status.unknown'))
-  const exceStatusMeta = metaByValue(record.exceStatus, getTransferExceStatusMetaMap(t), record.exceStatusText)
+  const statusMeta = metaByValue(
+    record.statusBool ?? record.status,
+    getTransferStatusMetaMap(t),
+    t('common.status.unknown')
+  )
+  const exceStatusMeta = metaByValue(
+    record.exceStatus,
+    getTransferExceStatusMetaMap(t),
+    record.exceStatusText
+  )
   const sourceMeta = metaByValue(record.source, getTransferSourceMetaMap(t), record.sourceText)
   return {
     ...record,
@@ -237,7 +335,8 @@
     typeLabel: normalizeText(record['type$'] || record.type) || '--',
     sourceText: sourceMeta.text,
     sourceTagType: sourceMeta.type,
-    exceStatusText: normalizeText(record['exceStatus$'] || record.exceStatusText) || exceStatusMeta.text,
+    exceStatusText:
+      normalizeText(record['exceStatus$'] || record.exceStatusText) || exceStatusMeta.text,
     exceStatusTagType: exceStatusMeta.type,
     orgWareName: resolveWarehouseText(record, 'org') || '--',
     tarWareName: resolveWarehouseText(record, 'tar') || '--',
@@ -247,9 +346,11 @@
     statusType: statusMeta.type,
     statusBool: record.statusBool !== void 0 ? Boolean(record.statusBool) : statusMeta.bool,
     createByText: normalizeText(record['createBy$'] || record.createByText) || '--',
-    createTimeText: normalizeText(record['createTime$'] || record.createTimeText || record.createTime) || '--',
+    createTimeText:
+      normalizeText(record['createTime$'] || record.createTimeText || record.createTime) || '--',
     updateByText: normalizeText(record['updateBy$'] || record.updateByText) || '--',
-    updateTimeText: normalizeText(record['updateTime$'] || record.updateTimeText || record.updateTime) || '--',
+    updateTimeText:
+      normalizeText(record['updateTime$'] || record.updateTimeText || record.updateTime) || '--',
     memo: normalizeText(record.memo) || '--'
   }
 }
@@ -259,8 +360,16 @@
 }
 
 export function normalizeTransferOrderRow(record = {}, t = $t) {
-  const statusMeta = metaByValue(record.statusBool ?? record.status, getTransferStatusMetaMap(t), t('common.status.unknown'))
-  const exceStatusMeta = metaByValue(record.exceStatus, getTransferExceStatusMetaMap(t), record.exceStatusText)
+  const statusMeta = metaByValue(
+    record.statusBool ?? record.status,
+    getTransferStatusMetaMap(t),
+    t('common.status.unknown')
+  )
+  const exceStatusMeta = metaByValue(
+    record.exceStatus,
+    getTransferExceStatusMetaMap(t),
+    record.exceStatusText
+  )
   return {
     ...record,
     id: record.id ?? null,
@@ -268,7 +377,8 @@
     poCode: normalizeText(record.poCode) || '--',
     typeLabel: normalizeText(record['type$'] || record.type) || '--',
     wkTypeLabel: normalizeText(record['wkType$'] || record.wkType) || '--',
-    exceStatusText: normalizeText(record['exceStatus$'] || record.exceStatusText) || exceStatusMeta.text,
+    exceStatusText:
+      normalizeText(record['exceStatus$'] || record.exceStatusText) || exceStatusMeta.text,
     exceStatusTagType: exceStatusMeta.type,
     statusText: statusMeta.text,
     statusType: statusMeta.type,
@@ -277,12 +387,153 @@
     workQty: record.workQty ?? '--',
     qty: record.qty ?? '--',
     stationId: normalizeText(record.stationId) || '--',
-    businessTimeText: normalizeText(record['businessTime$'] || record.businessTimeText || record.businessTime) || '--',
+    businessTimeText:
+      normalizeText(record['businessTime$'] || record.businessTimeText || record.businessTime) ||
+      '--',
     createByText: normalizeText(record['createBy$'] || record.createByText) || '--',
-    createTimeText: normalizeText(record['createTime$'] || record.createTimeText || record.createTime) || '--',
+    createTimeText:
+      normalizeText(record['createTime$'] || record.createTimeText || record.createTime) || '--',
     updateByText: normalizeText(record['updateBy$'] || record.updateByText) || '--',
-    updateTimeText: normalizeText(record['updateTime$'] || record.updateTimeText || record.updateTime) || '--',
+    updateTimeText:
+      normalizeText(record['updateTime$'] || record.updateTimeText || record.updateTime) || '--',
     memo: normalizeText(record.memo) || '--'
+  }
+}
+
+export function buildTransferEditableItem(record = {}, fieldDefinitions = []) {
+  return {
+    __rowKey: createTransferItemRowKey(record),
+    id: normalizeNumber(record.id, void 0),
+    transferId: normalizeNumber(record.transferId, void 0),
+    transferCode: normalizeText(record.transferCode || ''),
+    matnrId: normalizeNumber(record.matnrId, void 0),
+    maktx: normalizeText(record.maktx || ''),
+    matnrCode: normalizeText(record.matnrCode || ''),
+    anfme: normalizeNumber(record.anfme, 0) ?? 0,
+    splrCode: normalizeText(record.splrCode || ''),
+    splrName: normalizeText(record.splrName || ''),
+    batch: normalizeText(record.batch || ''),
+    unit: normalizeText(record.unit || record.stockUnit || ''),
+    stockUnit: normalizeText(record.stockUnit || record.unit || ''),
+    spec: normalizeText(record.spec || ''),
+    model: normalizeText(record.model || ''),
+    fieldsIndex: normalizeText(record.fieldsIndex || ''),
+    platItemId: normalizeText(record.platItemId || ''),
+    platOrderCode: normalizeText(record.platOrderCode || ''),
+    platWorkCode: normalizeText(record.platWorkCode || ''),
+    projectCode: normalizeText(record.projectCode || ''),
+    status: normalizeNumber(record.status, 1) ?? 1,
+    memo: normalizeText(record.memo || ''),
+    ...resolveDynamicFieldValues(record, fieldDefinitions)
+  }
+}
+
+export function createTransferEditableItemFromMaterial(record = {}, fieldDefinitions = []) {
+  return {
+    __rowKey: createTransferItemRowKey({
+      ...record,
+      id: void 0,
+      matnrId: record.matnrId ?? record.id
+    }),
+    id: void 0,
+    transferId: void 0,
+    transferCode: '',
+    matnrId: normalizeNumber(record.matnrId ?? record.id, void 0),
+    maktx: normalizeText(record.maktx || ''),
+    matnrCode: normalizeText(record.matnrCode || ''),
+    anfme: normalizeNumber(record.anfme, 0) ?? 0,
+    splrCode: '',
+    splrName: '',
+    batch: normalizeText(record.batch || ''),
+    unit: normalizeText(record.unit || record.stockUnit || ''),
+    stockUnit: normalizeText(record.stockUnit || record.unit || ''),
+    spec: normalizeText(record.spec || ''),
+    model: normalizeText(record.model || ''),
+    fieldsIndex: normalizeText(record.fieldsIndex || ''),
+    platItemId: '',
+    platOrderCode: '',
+    platWorkCode: '',
+    projectCode: '',
+    status: 1,
+    memo: '',
+    ...resolveDynamicFieldValues(record, fieldDefinitions)
+  }
+}
+
+export function buildTransferItemsSavePayload(
+  formData = {},
+  areaOptions = [],
+  fieldDefinitions = []
+) {
+  const transfer = buildTransferSavePayload(formData.transfer || formData, areaOptions)
+  const rows = Array.isArray(formData.items) ? formData.items : []
+  return {
+    transfer,
+    items: rows.map((item) => {
+      const payload = {
+        ...(item.id !== undefined && item.id !== null && item.id !== ''
+          ? { id: normalizeNumber(item.id, void 0) }
+          : {}),
+        ...(item.transferId !== undefined && item.transferId !== null && item.transferId !== ''
+          ? { transferId: normalizeNumber(item.transferId, void 0) }
+          : {}),
+        ...(normalizeText(item.transferCode)
+          ? { transferCode: normalizeText(item.transferCode) }
+          : {}),
+        ...(item.matnrId !== undefined && item.matnrId !== null && item.matnrId !== ''
+          ? { matnrId: normalizeNumber(item.matnrId, void 0) }
+          : {}),
+        ...(normalizeText(item.maktx) ? { maktx: normalizeText(item.maktx) } : {}),
+        ...(normalizeText(item.matnrCode) ? { matnrCode: normalizeText(item.matnrCode) } : {}),
+        ...(item.anfme !== undefined && item.anfme !== null && item.anfme !== ''
+          ? { anfme: normalizeNumber(item.anfme, 0) }
+          : {}),
+        ...(normalizeText(item.splrCode) ? { splrCode: normalizeText(item.splrCode) } : {}),
+        ...(normalizeText(item.splrName) ? { splrName: normalizeText(item.splrName) } : {}),
+        ...(normalizeText(item.batch) ? { batch: normalizeText(item.batch) } : {}),
+        ...(normalizeText(item.unit || item.stockUnit)
+          ? {
+              unit: normalizeText(item.unit || item.stockUnit),
+              stockUnit: normalizeText(item.stockUnit || item.unit)
+            }
+          : {}),
+        ...(normalizeText(item.spec) ? { spec: normalizeText(item.spec) } : {}),
+        ...(normalizeText(item.model) ? { model: normalizeText(item.model) } : {}),
+        ...(normalizeText(item.fieldsIndex)
+          ? { fieldsIndex: normalizeText(item.fieldsIndex) }
+          : {}),
+        ...(normalizeText(item.platItemId) ? { platItemId: normalizeText(item.platItemId) } : {}),
+        ...(normalizeText(item.platOrderCode)
+          ? { platOrderCode: normalizeText(item.platOrderCode) }
+          : {}),
+        ...(normalizeText(item.platWorkCode)
+          ? { platWorkCode: normalizeText(item.platWorkCode) }
+          : {}),
+        ...(normalizeText(item.projectCode)
+          ? { projectCode: normalizeText(item.projectCode) }
+          : {}),
+        ...(item.status !== undefined && item.status !== null && item.status !== ''
+          ? { status: normalizeNumber(item.status, 1) }
+          : { status: 1 }),
+        memo: normalizeText(item.memo || '')
+      }
+
+      fieldDefinitions.forEach((field) => {
+        const value = item[field.fields]
+        if (value !== undefined && value !== null && String(value).trim() !== '') {
+          payload[field.fields] = value
+        }
+      })
+
+      return payload
+    })
+  }
+}
+
+export function createTransferMaterialSearchState() {
+  return {
+    maktx: '',
+    matnrCode: ''
   }
 }
 
@@ -321,8 +572,17 @@
     { key: 'edit', label: $t('pages.orders.transfer.actions.edit'), icon: 'ri:pencil-line' }
   ]
   if (Number(normalizedRow.exceStatus) === 0) {
-    actions.push({ key: 'publish', label: $t('pages.orders.transfer.actions.publish'), icon: 'ri:send-plane-line' })
-    actions.push({ key: 'delete', label: $t('pages.orders.transfer.actions.delete'), icon: 'ri:delete-bin-5-line', color: 'var(--art-error)' })
+    actions.push({
+      key: 'publish',
+      label: $t('pages.orders.transfer.actions.publish'),
+      icon: 'ri:send-plane-line'
+    })
+    actions.push({
+      key: 'delete',
+      label: $t('pages.orders.transfer.actions.delete'),
+      icon: 'ri:delete-bin-5-line',
+      color: 'var(--art-error)'
+    })
   }
   return actions
 }
@@ -336,7 +596,14 @@
       if (value === void 0) return null
       return {
         value,
-        label: normalizeText(item.name || item.areaName || item.code || item.warehouseId$ || item.warehouseName || `${$t('menu.warehouseAreas')} ${value}`),
+        label: normalizeText(
+          item.name ||
+            item.areaName ||
+            item.code ||
+            item.warehouseId$ ||
+            item.warehouseName ||
+            `${$t('menu.warehouseAreas')} ${value}`
+        ),
         raw: item
       }
     })
@@ -352,7 +619,12 @@
       if (value === void 0) return null
       return {
         value,
-        label: normalizeText(item.label || item.name || item.dictLabel || `${$t('pages.orders.transfer.dialog.type')} ${value}`)
+        label: normalizeText(
+          item.label ||
+            item.name ||
+            item.dictLabel ||
+            `${$t('pages.orders.transfer.dialog.type')} ${value}`
+        )
       }
     })
     .filter(Boolean)
diff --git a/rsf-design/src/views/orders/transfer/transferTable.columns.js b/rsf-design/src/views/orders/transfer/transferTable.columns.js
index 189e1c8..07228ae 100644
--- a/rsf-design/src/views/orders/transfer/transferTable.columns.js
+++ b/rsf-design/src/views/orders/transfer/transferTable.columns.js
@@ -1,13 +1,34 @@
 import { h } from 'vue'
-import { ElTag } from 'element-plus'
+import { ElLink, ElTag } from 'element-plus'
 import { $t } from '@/locales'
 import ArtButtonMore from '@/components/core/forms/art-button-more/index.vue'
 import { getTransferActionList } from './transferPage.helpers.js'
+import TransferOrdersPanel from './modules/transfer-orders-panel.vue'
 
-export function createTransferTableColumns({ handleActionClick } = {}) {
+export function createTransferTableColumns({ handleActionClick, handleViewOrder } = {}) {
   return [
+    {
+      type: 'expand',
+      width: 56,
+      formatter: (row) => ({
+        render() {
+          return h(TransferOrdersPanel, {
+            transferId: row.id,
+            transferCode: row.code,
+            onOrderView: handleViewOrder
+          })
+        }
+      })
+    },
     { type: 'selection', width: 48, align: 'center' },
     { type: 'globalIndex', label: $t('table.index'), width: 72, align: 'center' },
+    {
+      prop: 'id',
+      label: $t('table.id'),
+      width: 100,
+      align: 'center',
+      formatter: (row) => row.id ?? '--'
+    },
     {
       prop: 'code',
       label: $t('pages.orders.transfer.search.code'),
@@ -28,7 +49,11 @@
       minWidth: 120,
       showOverflowTooltip: true,
       formatter: (row) =>
-        h(ElTag, { type: row.sourceTagType || 'info', effect: 'light' }, () => row.sourceText || '--')
+        h(
+          ElTag,
+          { type: row.sourceTagType || 'info', effect: 'light' },
+          () => row.sourceText || '--'
+        )
     },
     {
       prop: 'orgWareName',
@@ -63,7 +88,11 @@
       label: $t('pages.orders.transfer.search.exceStatus'),
       minWidth: 120,
       formatter: (row) =>
-        h(ElTag, { type: row.exceStatusTagType || 'info', effect: 'light' }, () => row.exceStatusText || '--')
+        h(
+          ElTag,
+          { type: row.exceStatusTagType || 'info', effect: 'light' },
+          () => row.exceStatusText || '--'
+        )
     },
     {
       prop: 'statusText',
@@ -72,6 +101,20 @@
       align: 'center',
       formatter: (row) =>
         h(ElTag, { type: row.statusType || 'info', effect: 'light' }, () => row.statusText || '--')
+    },
+    {
+      prop: 'updateByText',
+      label: $t('table.updateBy'),
+      minWidth: 120,
+      showOverflowTooltip: true,
+      formatter: (row) => row.updateByText || '--'
+    },
+    {
+      prop: 'createByText',
+      label: $t('table.createBy'),
+      minWidth: 120,
+      showOverflowTooltip: true,
+      formatter: (row) => row.createByText || '--'
     },
     {
       prop: 'updateTimeText',
@@ -109,7 +152,7 @@
   ]
 }
 
-export function createTransferOrderTableColumns() {
+export function createTransferOrderTableColumns({ handleViewOrder } = {}) {
   return [
     { type: 'globalIndex', label: $t('table.index'), width: 72, align: 'center' },
     {
@@ -117,7 +160,18 @@
       label: $t('pages.orders.transfer.detail.relatedCode'),
       minWidth: 170,
       showOverflowTooltip: true,
-      formatter: (row) => row.code || '--'
+      formatter: (row) =>
+        row.code && typeof handleViewOrder === 'function'
+          ? h(
+              ElLink,
+              {
+                type: 'primary',
+                underline: 'hover',
+                onClick: () => handleViewOrder(row)
+              },
+              () => row.code
+            )
+          : row.code || '--'
     },
     {
       prop: 'poCode',
@@ -145,7 +199,11 @@
       label: $t('pages.orders.transfer.detail.exceStatus'),
       minWidth: 120,
       formatter: (row) =>
-        h(ElTag, { type: row.exceStatusTagType || 'info', effect: 'light' }, () => row.exceStatusText || '--')
+        h(
+          ElTag,
+          { type: row.exceStatusTagType || 'info', effect: 'light' },
+          () => row.exceStatusText || '--'
+        )
     },
     {
       prop: 'statusText',

--
Gitblit v1.9.1