From 0d93ec4c10d146ffe287e7f4430ee66ad5832a17 Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期五, 10 四月 2026 16:08:20 +0800
Subject: [PATCH] #页面优化

---
 .gitignore                                                                           |    1 
 rsf-design/src/api/preparation-item.js                                               |   20 
 rsf-design/src/views/basic-info/bas-station/modules/bas-station-tag-cell.vue         |   71 
 rsf-design/src/views/basic-info/bas-station/index.vue                                |  262 +
 rsf-design/src/views/orders/asn-order-log/modules/asn-order-item-log-panel.vue       |  447 +++
 rsf-design/src/views/orders/asn-order/asnOrderPage.helpers.js                        |  258 +
 rsf-design/src/views/basic-info/bas-container/modules/bas-container-areas-editor.vue |  266 +
 rsf-design/src/views/orders/delivery/index.vue                                       |  352 ++
 rsf-design/src/views/orders/preparation-item/preparationItemTable.columns.js         |   66 
 rsf-design/src/views/orders/preparation/modules/preparation-task-dialog.vue          |  259 ++
 rsf-design/src/views/orders/asn-order-log/asnOrderLogTable.columns.js                |   49 
 rsf-design/src/views/orders/asn-order/modules/asn-order-dialog.vue                   |  370 ++
 rsf-design/src/views/orders/preparation/preparationPage.helpers.js                   |   70 
 rsf-design/src/api/asn-order.js                                                      |   57 
 rsf-design/src/api/delivery.js                                                       |   60 
 rsf-design/src/views/orders/asn-order-log/modules/asn-order-log-detail-drawer.vue    |   62 
 rsf-design/src/views/orders/delivery-item/deliveryItemTable.columns.js               |   86 
 rsf-design/src/views/orders/delivery/deliveryPage.helpers.js                         |  118 
 rsf-design/src/views/orders/asn-order/asnOrderTable.columns.js                       |   66 
 rsf-design/src/views/orders/asn-order/modules/asn-order-material-dialog.vue          |  269 ++
 rsf-design/src/views/orders/preparation/index.vue                                    |  135 +
 rsf-design/src/views/orders/preparation-item/index.vue                               |  371 ++
 rsf-design/src/api/bas-station.js                                                    |   41 
 rsf-design/src/views/orders/asn-order/modules/asn-order-item-dialog.vue              |  342 ++
 rsf-design/src/locales/langs/en.json                                                 |  186 +
 rsf-design/src/locales/langs/zh.json                                                 |  186 +
 rsf-design/src/views/orders/delivery-item/deliveryItemPage.helpers.js                |  124 
 rsf-design/src/views/orders/preparation-item/modules/preparation-item-dialog.vue     |  238 +
 rsf-design/src/views/orders/preparation/preparationTable.columns.js                  |   11 
 rsf-design/src/views/basic-info/bas-station/modules/bas-station-init-dialog.vue      |  239 +
 rsf-design/src/views/orders/asn-order-log/index.vue                                  |   95 
 rsf-design/src/views/orders/delivery-item/modules/delivery-item-dialog.vue           |  271 ++
 rsf-design/src/views/orders/delivery/deliveryTable.columns.js                        |   56 
 rsf-design/src/views/orders/preparation/modules/preparation-wave-dialog.vue          |   97 
 rsf-design/src/views/orders/delivery-item/modules/delivery-item-manage-panel.vue     |  441 +++
 rsf-design/src/views/orders/preparation/modules/preparation-generate-dialog.vue      |  301 ++
 rsf-design/src/views/orders/asn-order/modules/asn-order-create-by-po-dialog.vue      |   31 
 rsf-design/src/views/basic-info/bas-station/basStationTable.columns.js               |   46 
 rsf-design/src/views/orders/asn-order/modules/asn-order-detail-drawer.vue            |   64 
 rsf-design/src/views/orders/delivery-item/index.vue                                  |  200 -
 rsf-design/src/api/preparation.js                                                    |   27 
 rsf-design/src/views/orders/delivery/modules/delivery-manage-dialog.vue              |   86 
 rsf-design/src/views/orders/asn-order-log/asnOrderLogPage.helpers.js                 |   12 
 rsf-design/src/views/basic-info/bas-station/basStationPage.helpers.js                |  267 +
 rsf-design/src/views/orders/asn-order/index.vue                                      |  440 +++
 rsf-design/src/views/orders/preparation-item/preparationItemPage.helpers.js          |  112 
 46 files changed, 6,932 insertions(+), 696 deletions(-)

diff --git a/.gitignore b/.gitignore
index dbeb5f5..a0cadb6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,4 @@
 /log.path_IS_UNDEFINED/*.log
 /log.path_IS_UNDEFINED/*/*.log
 .worktrees/
+.playwright-cli/
diff --git a/rsf-design/src/api/asn-order.js b/rsf-design/src/api/asn-order.js
index dc822da..d5e6ef0 100644
--- a/rsf-design/src/api/asn-order.js
+++ b/rsf-design/src/api/asn-order.js
@@ -34,6 +34,7 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
+    orderBy: params.orderBy || 'create_time desc',
     ...filterParams(params, ['current', 'pageSize', 'size'])
   }
 }
@@ -96,6 +97,33 @@
   })
 }
 
+export function fetchSaveAsnOrderWithItems(payload = {}) {
+  return request.post({
+    url: '/asnOrder/items/save',
+    params: payload
+  })
+}
+
+export function fetchUpdateAsnOrderWithItems(payload = {}) {
+  return request.post({
+    url: '/asnOrder/items/update',
+    params: payload
+  })
+}
+
+export function fetchDeleteAsnOrder(ids) {
+  return request.post({
+    url: `/asnOrder/remove/${normalizeIds(ids)}`
+  })
+}
+
+export function fetchInspectAsnOrder(payload = []) {
+  return request.post({
+    url: '/asnOrder/inspect',
+    params: payload
+  })
+}
+
 export function fetchPurchaseFilterPage(params = {}) {
   return request.post({
     url: '/purchase/filters/page',
@@ -117,6 +145,12 @@
   })
 }
 
+export function fetchEnabledAsnOrderFields() {
+  return request.get({
+    url: '/fields/enable/list'
+  })
+}
+
 export async function fetchExportAsnOrderReport(payload = {}, options = {}) {
   return fetch(`${import.meta.env.VITE_API_URL}/asnOrder/export`, {
     method: 'POST',
@@ -127,3 +161,26 @@
     body: JSON.stringify(payload)
   })
 }
+
+export async function fetchDownloadAsnOrderTemplate(payload = {}, options = {}) {
+  return fetch(`${import.meta.env.VITE_API_URL}/asnOrderItem/template/download`, {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      ...(options.headers || {})
+    },
+    body: JSON.stringify(payload)
+  })
+}
+
+export function fetchImportAsnOrder(file) {
+  const formData = new FormData()
+  formData.append('file', file)
+  return request.post({
+    url: '/asnOrderItem/import',
+    data: formData,
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    }
+  })
+}
diff --git a/rsf-design/src/api/bas-station.js b/rsf-design/src/api/bas-station.js
index 25a40c2..8546ede 100644
--- a/rsf-design/src/api/bas-station.js
+++ b/rsf-design/src/api/bas-station.js
@@ -34,7 +34,8 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
-    ...filterParams(params, ['current', 'pageSize', 'size'])
+    orderBy: normalizeText(params.orderBy) || 'create_time desc',
+    ...filterParams(params, ['current', 'pageSize', 'size', 'orderBy'])
   }
 }
 
@@ -48,6 +49,14 @@
         ? Number(params.type)
         : void 0,
     useStatus: normalizeText(params.useStatus),
+    inAble:
+      params.inAble !== undefined && params.inAble !== null && params.inAble !== ''
+        ? Number(params.inAble)
+        : void 0,
+    outAble:
+      params.outAble !== undefined && params.outAble !== null && params.outAble !== ''
+        ? Number(params.outAble)
+        : void 0,
     area:
       params.area !== undefined && params.area !== null && params.area !== ''
         ? Number(params.area)
@@ -56,13 +65,23 @@
       params.isCrossZone !== undefined && params.isCrossZone !== null && params.isCrossZone !== ''
         ? Number(params.isCrossZone)
         : void 0,
+    crossZoneArea: normalizeText(params.crossZoneArea),
     isWcs:
       params.isWcs !== undefined && params.isWcs !== null && params.isWcs !== ''
         ? Number(params.isWcs)
         : void 0,
+    wcsData: normalizeText(params.wcsData),
+    containerType:
+      params.containerType !== undefined &&
+      params.containerType !== null &&
+      params.containerType !== ''
+        ? Number(params.containerType)
+        : void 0,
     barcode: normalizeText(params.barcode),
     autoTransfer:
-      params.autoTransfer !== undefined && params.autoTransfer !== null && params.autoTransfer !== ''
+      params.autoTransfer !== undefined &&
+      params.autoTransfer !== null &&
+      params.autoTransfer !== ''
         ? Number(params.autoTransfer)
         : void 0,
     status:
@@ -75,7 +94,9 @@
   }
 
   return Object.fromEntries(
-    Object.entries(searchParams).filter(([, value]) => value !== '' && value !== void 0 && value !== null)
+    Object.entries(searchParams).filter(
+      ([, value]) => value !== '' && value !== void 0 && value !== null
+    )
   )
 }
 
@@ -93,7 +114,9 @@
       ? { area: Number(formData.area) }
       : {}),
     useStatus: normalizeText(formData.useStatus) || '',
-    ...(formData.isCrossZone !== undefined && formData.isCrossZone !== null && formData.isCrossZone !== ''
+    ...(formData.isCrossZone !== undefined &&
+    formData.isCrossZone !== null &&
+    formData.isCrossZone !== ''
       ? { isCrossZone: Number(formData.isCrossZone) }
       : {}),
     ...(Array.isArray(formData.areaIds) && formData.areaIds.length
@@ -104,10 +127,16 @@
       : {}),
     wcsData: normalizeText(formData.wcsData) || '',
     ...(Array.isArray(formData.containerTypes) && formData.containerTypes.length
-      ? { containerTypes: formData.containerTypes.map((id) => Number(id)).filter((id) => !Number.isNaN(id)) }
+      ? {
+          containerTypes: formData.containerTypes
+            .map((id) => Number(id))
+            .filter((id) => !Number.isNaN(id))
+        }
       : {}),
     barcode: normalizeText(formData.barcode) || '',
-    ...(formData.autoTransfer !== undefined && formData.autoTransfer !== null && formData.autoTransfer !== ''
+    ...(formData.autoTransfer !== undefined &&
+    formData.autoTransfer !== null &&
+    formData.autoTransfer !== ''
       ? { autoTransfer: Number(formData.autoTransfer) }
       : {}),
     ...(formData.inAble !== undefined && formData.inAble !== null && formData.inAble !== ''
diff --git a/rsf-design/src/api/delivery.js b/rsf-design/src/api/delivery.js
index 0b33f8a..38e9ef4 100644
--- a/rsf-design/src/api/delivery.js
+++ b/rsf-design/src/api/delivery.js
@@ -32,9 +32,27 @@
 
 export function buildDeliverySearchParams(params = {}) {
   const result = {}
-  ;['condition', 'code', 'platId', 'type', 'wkType', 'source', 'timeStart', 'timeEnd', 'memo'].forEach((key) => {
+  ;[
+    'condition',
+    'code',
+    'platId',
+    'type',
+    'wkType',
+    'source',
+    'platCode',
+    'timeStart',
+    'timeEnd',
+    'startTime',
+    'endTime',
+    'memo'
+  ].forEach((key) => {
     const value = normalizeText(params[key])
     if (value) result[key] = value
+  })
+  ;['anfme', 'qty', 'workQty'].forEach((key) => {
+    if (params[key] !== '' && params[key] !== undefined && params[key] !== null) {
+      result[key] = Number(params[key])
+    }
   })
   if (params.status !== '' && params.status !== undefined && params.status !== null) {
     result.status = Number(params.status)
@@ -49,6 +67,7 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
+    orderBy: normalizeText(params.orderBy) || 'create_time desc',
     ...buildDeliverySearchParams(params)
   }
 }
@@ -74,7 +93,8 @@
     'splrBatch',
     'timeStart',
     'timeEnd',
-    'memo'
+    'memo',
+    'fieldsIndex'
   ].forEach((key) => {
     const value = normalizeText(params[key])
     if (value) result[key] = value
@@ -92,6 +112,7 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
+    orderBy: normalizeText(params.orderBy) || 'create_time desc',
     ...buildDeliveryItemSearchParams(params)
   }
 }
@@ -134,6 +155,12 @@
   })
 }
 
+export function fetchDeleteDeliveryMany(ids) {
+  return request.post({
+    url: `/delivery/remove/${normalizeIds(ids)}`
+  })
+}
+
 export function fetchSaveDelivery(payload = {}) {
   return request.post({
     url: '/delivery/save',
@@ -145,6 +172,18 @@
   return request.post({
     url: '/delivery/update',
     params: payload
+  })
+}
+
+export function fetchImportDelivery(file) {
+  const formData = new FormData()
+  formData.append('file', file)
+  return request.post({
+    url: '/delivery/import',
+    data: formData,
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    }
   })
 }
 
@@ -173,6 +212,12 @@
   })
 }
 
+export function fetchDeleteDeliveryItemMany(ids) {
+  return request.post({
+    url: `/deliveryItem/remove/${normalizeIds(ids)}`
+  })
+}
+
 export function fetchSaveDeliveryItem(payload = {}) {
   return request.post({
     url: '/deliveryItem/save',
@@ -198,3 +243,14 @@
     body: JSON.stringify(buildDeliveryExportParams(payload))
   })
 }
+
+export async function fetchDownloadDeliveryTemplate(payload = {}, options = {}) {
+  return fetch(`${import.meta.env.VITE_API_URL}/delivery/template/download`, {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      ...(options.headers || {})
+    },
+    body: JSON.stringify(payload)
+  })
+}
diff --git a/rsf-design/src/api/preparation-item.js b/rsf-design/src/api/preparation-item.js
index 4a0b7e2..57b5df6 100644
--- a/rsf-design/src/api/preparation-item.js
+++ b/rsf-design/src/api/preparation-item.js
@@ -66,6 +66,26 @@
   })
 }
 
+export function fetchSavePreparationItem(payload = {}) {
+  return request.post({
+    url: '/outStockItem/save',
+    params: payload
+  })
+}
+
+export function fetchUpdatePreparationItem(payload = {}) {
+  return request.post({
+    url: '/outStockItem/update',
+    params: payload
+  })
+}
+
+export function fetchDeletePreparationItem(ids) {
+  return request.post({
+    url: `/outStockItem/remove/${normalizeIds(ids)}`
+  })
+}
+
 export async function fetchExportPreparationItemReport(payload = {}, options = {}) {
   return fetch(`${import.meta.env.VITE_API_URL}/outStockItem/export`, {
     method: 'POST',
diff --git a/rsf-design/src/api/preparation.js b/rsf-design/src/api/preparation.js
index 0049199..5a7cdfc 100644
--- a/rsf-design/src/api/preparation.js
+++ b/rsf-design/src/api/preparation.js
@@ -51,6 +51,13 @@
   return request.post({ url: '/preparation/page', params: buildPreparationPageParams(params) })
 }
 
+export function fetchPreparationDialogPage(params = {}) {
+  return request.post({
+    url: '/deliveryItem/filters/page',
+    params: buildPreparationPageParams(params)
+  })
+}
+
 export function fetchPreparationItemPage(params = {}) {
   return request.post({
     url: '/outStockItem/page',
@@ -78,6 +85,26 @@
   return request.get({ url: `/preparation/cancel/${id}` })
 }
 
+export function fetchGeneratePreparationOrders(payload = {}) {
+  return request.post({ url: '/preparation/generate/orders', params: payload })
+}
+
+export function fetchGeneratePreparationWave(payload = {}) {
+  return request.post({ url: '/preparation/generate/wave', params: payload })
+}
+
+export function fetchPreparationTaskPreview(payload = {}) {
+  return request.post({ url: '/preparation/order/getOutTaskItems', params: payload })
+}
+
+export function fetchGeneratePreparationTasks(payload = {}) {
+  return request.post({ url: '/preparation/generate/tasks', params: payload })
+}
+
+export function fetchPreparationTaskSites() {
+  return request.get({ url: '/preparation/tasks/sites' })
+}
+
 export async function fetchExportPreparationReport(payload = {}, options = {}) {
   return fetch(`${import.meta.env.VITE_API_URL}/preparation/export`, {
     method: 'POST',
diff --git a/rsf-design/src/locales/langs/en.json b/rsf-design/src/locales/langs/en.json
index f4ef9d8..e5ed2d3 100644
--- a/rsf-design/src/locales/langs/en.json
+++ b/rsf-design/src/locales/langs/en.json
@@ -1192,6 +1192,10 @@
         "reportTitle": "ASN Report",
         "entity": "ASN",
         "buttons": {
+          "create": "New ASN",
+          "import": "Import",
+          "downloadTemplate": "Download Template",
+          "inspection": "Batch Inspection",
           "createByPo": "Create by PO"
         },
         "search": {
@@ -1201,8 +1205,14 @@
           "codePlaceholder": "Enter ASN No.",
           "poCode": "PO No.",
           "poCodePlaceholder": "Enter PO No.",
+          "poId": "PO ID",
+          "type": "Order Type",
           "wkType": "Business Type",
-          "wkTypePlaceholder": "Enter business type",
+          "anfme": "Delivery Qty",
+          "qty": "Received Qty",
+          "logisNo": "Logistics No.",
+          "arrTime": "Estimated Arrival",
+          "memo": "Remark",
           "exceStatus": "Document Status",
           "supplierName": "Supplier",
           "supplierPlaceholder": "Enter supplier",
@@ -1213,7 +1223,14 @@
           "condition": "Enter ASN No./PO No./Supplier",
           "code": "Enter ASN No.",
           "poCode": "Enter PO No.",
-          "wkType": "Enter business type",
+          "poId": "Enter PO ID",
+          "type": "Select order type",
+          "wkType": "Select business type",
+          "anfme": "Enter delivery qty",
+          "qty": "Enter received qty",
+          "logisNo": "Enter logistics No.",
+          "arrTime": "Select estimated arrival time",
+          "memo": "Enter remark",
           "supplierName": "Enter supplier",
           "purchaseUserName": "Enter purchaser"
         },
@@ -1229,6 +1246,8 @@
         "actions": {
           "view": "View Detail",
           "items": "Receiving Items",
+          "edit": "Edit",
+          "delete": "Delete",
           "print": "Print",
           "complete": "Complete"
         },
@@ -1256,6 +1275,77 @@
           "actionFailed": "ASN action failed",
           "detailTimeout": "ASN detail items timed out and waiting has stopped",
           "itemsTimeout": "ASN detail items timed out and waiting has stopped"
+        },
+        "dialog": {
+          "createTitle": "Create ASN",
+          "editTitle": "Edit ASN",
+          "addMaterial": "Add Material",
+          "deleteSelected": "Delete Selected",
+          "itemCount": "{count} item(s)",
+          "materialDuplicate": "The selected materials already exist in current items",
+          "placeholder": {
+            "type": "Select order type",
+            "wkType": "Select business type",
+            "poCode": "Enter PO No.",
+            "logisNo": "Enter logistics No.",
+            "arrTime": "Select estimated arrival time",
+            "memo": "Enter remark"
+          },
+          "validation": {
+            "type": "Please select order type",
+            "wkType": "Please select business type",
+            "items": "Please add at least one material item",
+            "anfme": "Expected quantity must be greater than 0"
+          }
+        },
+        "materialDialog": {
+          "title": "Select Material",
+          "selected": "Selected",
+          "unselected": "Available",
+          "search": {
+            "name": "Material Name",
+            "code": "Material Code",
+            "groupId": "Material Group"
+          },
+          "placeholder": {
+            "name": "Enter material name",
+            "code": "Enter material code",
+            "groupId": "Select material group"
+          },
+          "table": {
+            "code": "Material Code",
+            "name": "Material Name",
+            "group": "Material Group",
+            "spec": "Specification",
+            "model": "Model",
+            "status": "Selection Status"
+          },
+          "messages": {
+            "groupTimeout": "Material group loading timed out and waiting has stopped"
+          }
+        },
+        "messages": {
+          "createSuccess": "ASN created successfully",
+          "createFailed": "Failed to create ASN",
+          "updateSuccess": "ASN updated successfully",
+          "updateFailed": "Failed to update ASN",
+          "deleteTitle": "Delete Confirmation",
+          "deleteConfirm": "Are you sure you want to delete ASN {code}?",
+          "deleteSuccess": "ASN deleted successfully",
+          "detailTimeout": "ASN detail loading timed out and waiting has stopped",
+          "detailFailed": "Failed to load ASN detail",
+          "inspectionTitle": "Inspection Confirmation",
+          "inspectionConfirm": "Are you sure you want to inspect the selected {count} ASN(s)?",
+          "inspectionSuccess": "ASN inspection submitted successfully",
+          "inspectionFailed": "Failed to submit ASN inspection",
+          "inspectionSelectRequired": "Please select the ASN records to inspect first",
+          "importSuccess": "ASN import succeeded",
+          "importFailed": "ASN import failed",
+          "templateDownloadSuccess": "Template downloaded successfully",
+          "templateDownloadFailed": "Failed to download template",
+          "typeOptionsTimeout": "Order type options timed out and waiting has stopped",
+          "wkTypeOptionsTimeout": "Business type options timed out and waiting has stopped",
+          "fieldOptionsTimeout": "Extended field loading timed out and waiting has stopped"
         },
         "createByPoDialog": {
           "title": "Create by PO",
@@ -1287,6 +1377,9 @@
           }
         },
         "table": {
+          "purchaseOrgName": "Purchasing Org",
+          "businessTime": "Purchase Date",
+          "supplierId": "Supplier Code",
           "poItemId": "PO Line No.",
           "expectedQty": "Expected Qty",
           "receivedQty": "Received Qty",
@@ -1556,6 +1649,8 @@
         "search": {
           "condition": "Keyword",
           "conditionPlaceholder": "Enter No./ERP master order/platform order",
+          "timeStart": "Created From",
+          "timeEnd": "Created To",
           "code": "No.",
           "codePlaceholder": "Enter No.",
           "platId": "ERP Master Order ID",
@@ -1566,6 +1661,12 @@
           "wkTypePlaceholder": "Enter business type",
           "source": "Order Source",
           "sourcePlaceholder": "Enter order source",
+          "anfme": "Outbound Qty",
+          "qty": "Outbound Done Qty",
+          "workQty": "Working Qty",
+          "platCode": "Platform Order No.",
+          "startTime": "Planned Outbound Time",
+          "endTime": "Planned Outbound End Time",
           "exceStatus": "Execution Status",
           "exceStatusPlaceholder": "Enter execution status",
           "memo": "Remark",
@@ -1573,13 +1674,26 @@
         },
         "placeholder": {
           "condition": "Enter No./ERP master order/platform order",
+          "timeStart": "Select created from",
+          "timeEnd": "Select created to",
           "code": "Enter No.",
           "platId": "Enter ERP master order ID",
           "type": "Enter order type",
           "wkType": "Enter business type",
           "source": "Enter order source",
+          "anfme": "Enter outbound qty",
+          "qty": "Enter outbound done qty",
+          "workQty": "Enter working qty",
+          "platCode": "Enter platform order No.",
+          "startTime": "Select planned outbound time",
+          "endTime": "Select planned outbound end time",
+          "status": "Select status",
           "exceStatus": "Enter execution status",
           "memo": "Enter remark"
+        },
+        "buttons": {
+          "import": "Import",
+          "downloadTemplate": "Download Template"
         },
         "status": {
           "normal": "Normal",
@@ -1591,8 +1705,13 @@
         },
         "actions": {
           "view": "View Detail",
+          "edit": "Edit",
           "items": "Items",
           "delete": "Delete"
+        },
+        "manage": {
+          "title": "Edit DO",
+          "baseInfo": "Order Information"
         },
         "detail": {
           "title": "Handover Order Detail",
@@ -1640,7 +1759,11 @@
         "messages": {
           "itemsTimeout": "DO items timed out and waiting has stopped",
           "detailTimeout": "DO detail timed out and waiting has stopped",
-          "detailLoadFailed": "Failed to load DO detail"
+          "detailLoadFailed": "Failed to load DO detail",
+          "importSuccess": "DO imported successfully",
+          "importFailed": "Failed to import DO",
+          "templateDownloadSuccess": "Template downloaded successfully",
+          "templateDownloadFailed": "Failed to download template"
         }
       },
       "deliveryItem": {
@@ -1653,11 +1776,58 @@
           "deliveryCodePlaceholder": "Enter DO No.",
           "platItemId": "Platform Line No.",
           "platItemIdPlaceholder": "Enter platform line No.",
+          "fieldsIndexPlaceholder": "Enter field index",
           "matnrCodePlaceholder": "Enter material code",
           "maktxPlaceholder": "Enter material name",
           "supplierName": "Supplier Name",
           "supplierNamePlaceholder": "Enter supplier name",
-          "supplierBatchPlaceholder": "Enter supplier batch"
+          "supplierCodePlaceholder": "Enter supplier code",
+          "supplierBatchPlaceholder": "Enter supplier batch",
+          "statusPlaceholder": "Select status",
+          "timeStart": "Created From",
+          "timeStartPlaceholder": "Select created from",
+          "timeEnd": "Created To",
+          "timeEndPlaceholder": "Select created to",
+          "memoPlaceholder": "Enter remark"
+        },
+        "actions": {
+          "add": "Add Item"
+        },
+        "dialog": {
+          "titleAdd": "Add DO Item",
+          "titleEdit": "Edit DO Item",
+          "deliveryId": "DO ID",
+          "platItemId": "Platform Line No.",
+          "matnrCode": "Material Code",
+          "maktx": "Material Name",
+          "fieldsIndex": "Field Index",
+          "unit": "Unit",
+          "anfme": "Qty",
+          "qty": "Outbound Qty",
+          "printQty": "Print Qty",
+          "splrName": "Supplier Name",
+          "splrCode": "Supplier Code",
+          "splrBatch": "Supplier Batch",
+          "status": "Status",
+          "memo": "Remark",
+          "placeholder": {
+            "platItemId": "Enter platform line No.",
+            "matnrCode": "Enter material code",
+            "maktx": "Enter material name",
+            "fieldsIndex": "Enter field index",
+            "unit": "Enter unit",
+            "anfme": "Enter qty",
+            "qty": "Enter outbound qty",
+            "printQty": "Enter print qty",
+            "splrName": "Enter supplier name",
+            "splrCode": "Enter supplier code",
+            "splrBatch": "Enter supplier batch",
+            "status": "Select status",
+            "memo": "Enter remark"
+          },
+          "validation": {
+            "anfme": "Enter qty"
+          }
         },
         "table": {
           "deliveryId": "DO ID",
@@ -1690,7 +1860,13 @@
         },
         "messages": {
           "detailTimeout": "DO item detail timed out and waiting has stopped",
-          "detailFailed": "Failed to load DO item detail"
+          "detailFailed": "Failed to load DO item detail",
+          "createSuccess": "DO item created successfully",
+          "createFailed": "Failed to create DO item",
+          "updateSuccess": "DO item updated successfully",
+          "updateFailed": "Failed to update DO item",
+          "deleteConfirm": "Are you sure you want to delete DO item {code}?",
+          "actionFailed": "DO item action failed"
         }
       },
       "transfer": {
diff --git a/rsf-design/src/locales/langs/zh.json b/rsf-design/src/locales/langs/zh.json
index 170cebe..27b8c95 100644
--- a/rsf-design/src/locales/langs/zh.json
+++ b/rsf-design/src/locales/langs/zh.json
@@ -1194,6 +1194,10 @@
         "reportTitle": "鍏ュ簱閫氱煡鍗曟姤琛�",
         "entity": "鍏ュ簱閫氱煡鍗�",
         "buttons": {
+          "create": "鏂板缓鍏ュ簱閫氱煡鍗�",
+          "import": "瀵煎叆",
+          "downloadTemplate": "涓嬭浇妯℃澘",
+          "inspection": "鎵归噺鎶ユ",
           "createByPo": "鎸塒O寤哄崟"
         },
         "search": {
@@ -1203,8 +1207,14 @@
           "codePlaceholder": "璇疯緭鍏� ASN 鍗曞彿",
           "poCode": "PO鍗曞彿",
           "poCodePlaceholder": "璇疯緭鍏� PO 鍗曞彿",
+          "poId": "PO鍗旾D",
+          "type": "鍗曟嵁绫诲瀷",
           "wkType": "涓氬姟绫诲瀷",
-          "wkTypePlaceholder": "璇疯緭鍏ヤ笟鍔$被鍨�",
+          "anfme": "閫佽揣鏁伴噺",
+          "qty": "宸叉敹鏁伴噺",
+          "logisNo": "鐗╂祦鍗曞彿",
+          "arrTime": "棰勮鍒拌揪鏃堕棿",
+          "memo": "澶囨敞",
           "exceStatus": "鍗曟嵁鐘舵��",
           "supplierName": "渚涘簲鍟�",
           "supplierPlaceholder": "璇疯緭鍏ヤ緵搴斿晢",
@@ -1215,7 +1225,14 @@
           "condition": "璇疯緭鍏� ASN 鍗曞彿/PO 鍗曞彿/渚涘簲鍟�",
           "code": "璇疯緭鍏� ASN 鍗曞彿",
           "poCode": "璇疯緭鍏� PO 鍗曞彿",
-          "wkType": "璇疯緭鍏ヤ笟鍔$被鍨�",
+          "poId": "璇疯緭鍏� PO 鍗曞彿 ID",
+          "type": "璇烽�夋嫨鍗曟嵁绫诲瀷",
+          "wkType": "璇烽�夋嫨涓氬姟绫诲瀷",
+          "anfme": "璇疯緭鍏ラ�佽揣鏁伴噺",
+          "qty": "璇疯緭鍏ュ凡鏀舵暟閲�",
+          "logisNo": "璇疯緭鍏ョ墿娴佸崟鍙�",
+          "arrTime": "璇烽�夋嫨棰勮鍒拌揪鏃堕棿",
+          "memo": "璇疯緭鍏ュ娉�",
           "supplierName": "璇疯緭鍏ヤ緵搴斿晢",
           "purchaseUserName": "璇疯緭鍏ラ噰璐憳"
         },
@@ -1231,6 +1248,8 @@
         "actions": {
           "view": "鏌ョ湅璇︽儏",
           "items": "鏀惰揣鏄庣粏",
+          "edit": "缂栬緫",
+          "delete": "鍒犻櫎",
           "print": "鎵撳嵃",
           "complete": "瀹屾垚"
         },
@@ -1258,6 +1277,77 @@
           "actionFailed": "鍏ュ簱閫氱煡鍗曟搷浣滃け璐�",
           "detailTimeout": "鍏ュ簱閫氱煡鍗曟槑缁嗗姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�",
           "itemsTimeout": "鍏ュ簱閫氱煡鍗曟槑缁嗗姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�"
+        },
+        "dialog": {
+          "createTitle": "鏂板鍏ュ簱閫氱煡鍗�",
+          "editTitle": "缂栬緫鍏ュ簱閫氱煡鍗�",
+          "addMaterial": "鏂板鐗╂枡",
+          "deleteSelected": "鍒犻櫎閫変腑",
+          "itemCount": "褰撳墠鍏� {count} 鏉℃槑缁�",
+          "materialDuplicate": "鎵�閫夌墿鏂欏凡瀛樺湪浜庡綋鍓嶆槑缁嗕腑",
+          "placeholder": {
+            "type": "璇烽�夋嫨鍗曟嵁绫诲瀷",
+            "wkType": "璇烽�夋嫨涓氬姟绫诲瀷",
+            "poCode": "璇疯緭鍏� PO 鍗曞彿",
+            "logisNo": "璇疯緭鍏ョ墿娴佸崟鍙�",
+            "arrTime": "璇烽�夋嫨棰勮鍒拌揪鏃堕棿",
+            "memo": "璇疯緭鍏ュ娉�"
+          },
+          "validation": {
+            "type": "璇烽�夋嫨鍗曟嵁绫诲瀷",
+            "wkType": "璇烽�夋嫨涓氬姟绫诲瀷",
+            "items": "璇疯嚦灏戞坊鍔犱竴鏉$墿鏂欐槑缁�",
+            "anfme": "鐗╂枡鏄庣粏鐨勫簲鏀舵暟閲忓繀椤诲ぇ浜� 0"
+          }
+        },
+        "materialDialog": {
+          "title": "閫夋嫨鐗╂枡",
+          "selected": "宸查�夋嫨",
+          "unselected": "鍙�夋嫨",
+          "search": {
+            "name": "鐗╂枡鍚嶇О",
+            "code": "鐗╂枡缂栫爜",
+            "groupId": "鐗╂枡鍒嗙粍"
+          },
+          "placeholder": {
+            "name": "璇疯緭鍏ョ墿鏂欏悕绉�",
+            "code": "璇疯緭鍏ョ墿鏂欑紪鐮�",
+            "groupId": "璇烽�夋嫨鐗╂枡鍒嗙粍"
+          },
+          "table": {
+            "code": "鐗╂枡缂栫爜",
+            "name": "鐗╂枡鍚嶇О",
+            "group": "鐗╂枡鍒嗙粍",
+            "spec": "瑙勬牸",
+            "model": "鍨嬪彿",
+            "status": "閫夋嫨鐘舵��"
+          },
+          "messages": {
+            "groupTimeout": "鐗╂枡鍒嗙粍鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟"
+          }
+        },
+        "messages": {
+          "createSuccess": "鍏ュ簱閫氱煡鍗曟柊澧炴垚鍔�",
+          "createFailed": "鍏ュ簱閫氱煡鍗曟柊澧炲け璐�",
+          "updateSuccess": "鍏ュ簱閫氱煡鍗曟洿鏂版垚鍔�",
+          "updateFailed": "鍏ュ簱閫氱煡鍗曟洿鏂板け璐�",
+          "deleteTitle": "鍒犻櫎纭",
+          "deleteConfirm": "纭畾鍒犻櫎鍏ュ簱閫氱煡鍗� {code} 鍚楋紵",
+          "deleteSuccess": "鍏ュ簱閫氱煡鍗曞垹闄ゆ垚鍔�",
+          "detailTimeout": "鍏ュ簱閫氱煡鍗曡鎯呭姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�",
+          "detailFailed": "鑾峰彇鍏ュ簱閫氱煡鍗曡鎯呭け璐�",
+          "inspectionTitle": "鎶ユ纭",
+          "inspectionConfirm": "纭畾瀵归�変腑鐨� {count} 寮犲叆搴撻�氱煡鍗曟墽琛屾姤妫�鍚楋紵",
+          "inspectionSuccess": "鍏ュ簱閫氱煡鍗曟姤妫�鎴愬姛",
+          "inspectionFailed": "鍏ュ簱閫氱煡鍗曟姤妫�澶辫触",
+          "inspectionSelectRequired": "璇峰厛閫夋嫨闇�瑕佹姤妫�鐨勫叆搴撻�氱煡鍗�",
+          "importSuccess": "鍏ュ簱閫氱煡鍗曞鍏ユ垚鍔�",
+          "importFailed": "鍏ュ簱閫氱煡鍗曞鍏ュけ璐�",
+          "templateDownloadSuccess": "妯℃澘涓嬭浇鎴愬姛",
+          "templateDownloadFailed": "妯℃澘涓嬭浇澶辫触",
+          "typeOptionsTimeout": "鍗曟嵁绫诲瀷閫夐」鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟",
+          "wkTypeOptionsTimeout": "涓氬姟绫诲瀷閫夐」鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟",
+          "fieldOptionsTimeout": "鎵╁睍瀛楁鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟"
         },
         "createByPoDialog": {
           "title": "鎸塒O寤哄崟",
@@ -1289,6 +1379,9 @@
           }
         },
         "table": {
+          "purchaseOrgName": "閲囪喘缁勭粐",
+          "businessTime": "閲囪喘鏃ユ湡",
+          "supplierId": "渚涘簲鍟嗙紪鐮�",
           "poItemId": "PO琛屽彿",
           "expectedQty": "搴旀敹鏁伴噺",
           "receivedQty": "宸叉敹鏁伴噺",
@@ -1564,6 +1657,8 @@
         "search": {
           "condition": "鍏抽敭瀛�",
           "conditionPlaceholder": "璇疯緭鍏ュ崟鍙�/ERP涓诲崟鏍囪瘑/骞冲彴鍗曞彿",
+          "timeStart": "鍒涘缓寮�濮嬫椂闂�",
+          "timeEnd": "鍒涘缓缁撴潫鏃堕棿",
           "code": "鍗曞彿",
           "codePlaceholder": "璇疯緭鍏ュ崟鍙�",
           "platId": "ERP涓诲崟鏍囪瘑",
@@ -1574,6 +1669,12 @@
           "wkTypePlaceholder": "璇疯緭鍏ヤ笟鍔$被鍨�",
           "source": "鍗曟嵁鏉ユ簮",
           "sourcePlaceholder": "璇疯緭鍏ュ崟鎹潵婧�",
+          "anfme": "鍑哄簱鏁伴噺",
+          "qty": "宸插嚭搴撴暟閲�",
+          "workQty": "鎵ц涓暟閲�",
+          "platCode": "骞冲彴鍗曞彿",
+          "startTime": "璁″垝鍑哄簱鏃堕棿",
+          "endTime": "璁″垝鍑哄簱缁撴潫鏃堕棿",
           "exceStatus": "鎵ц鐘舵��",
           "exceStatusPlaceholder": "璇疯緭鍏ユ墽琛岀姸鎬�",
           "memo": "澶囨敞",
@@ -1581,13 +1682,26 @@
         },
         "placeholder": {
           "condition": "璇疯緭鍏ュ崟鍙�/ERP涓诲崟鏍囪瘑/骞冲彴鍗曞彿",
+          "timeStart": "璇烽�夋嫨鍒涘缓寮�濮嬫椂闂�",
+          "timeEnd": "璇烽�夋嫨鍒涘缓缁撴潫鏃堕棿",
           "code": "璇疯緭鍏ュ崟鍙�",
           "platId": "璇疯緭鍏RP涓诲崟鏍囪瘑",
           "type": "璇疯緭鍏ュ崟鎹被鍨�",
           "wkType": "璇疯緭鍏ヤ笟鍔$被鍨�",
           "source": "璇疯緭鍏ュ崟鎹潵婧�",
+          "anfme": "璇疯緭鍏ュ嚭搴撴暟閲�",
+          "qty": "璇疯緭鍏ュ凡鍑哄簱鏁伴噺",
+          "workQty": "璇疯緭鍏ユ墽琛屼腑鏁伴噺",
+          "platCode": "璇疯緭鍏ュ钩鍙板崟鍙�",
+          "startTime": "璇烽�夋嫨璁″垝鍑哄簱鏃堕棿",
+          "endTime": "璇烽�夋嫨璁″垝鍑哄簱缁撴潫鏃堕棿",
+          "status": "璇烽�夋嫨鐘舵��",
           "exceStatus": "璇疯緭鍏ユ墽琛岀姸鎬�",
           "memo": "璇疯緭鍏ュ娉�"
+        },
+        "buttons": {
+          "import": "瀵煎叆",
+          "downloadTemplate": "涓嬭浇妯℃澘"
         },
         "status": {
           "normal": "姝e父",
@@ -1599,8 +1713,13 @@
         },
         "actions": {
           "view": "鏌ョ湅璇︽儏",
+          "edit": "缂栬緫",
           "items": "鏄庣粏",
           "delete": "鍒犻櫎"
+        },
+        "manage": {
+          "title": "缂栬緫DO鍗�",
+          "baseInfo": "涓诲崟淇℃伅"
         },
         "detail": {
           "title": "浜ゆ帴鍗曡鎯�",
@@ -1648,7 +1767,11 @@
         "messages": {
           "itemsTimeout": "DO鍗曟槑缁嗗姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�",
           "detailTimeout": "DO鍗曡鎯呭姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�",
-          "detailLoadFailed": "DO鍗曡鎯呭姞杞藉け璐�"
+          "detailLoadFailed": "DO鍗曡鎯呭姞杞藉け璐�",
+          "importSuccess": "DO鍗曞鍏ユ垚鍔�",
+          "importFailed": "DO鍗曞鍏ュけ璐�",
+          "templateDownloadSuccess": "妯℃澘涓嬭浇鎴愬姛",
+          "templateDownloadFailed": "妯℃澘涓嬭浇澶辫触"
         }
       },
       "deliveryItem": {
@@ -1661,11 +1784,58 @@
           "deliveryCodePlaceholder": "璇疯緭鍏O鍗曞彿",
           "platItemId": "骞冲彴琛屽彿",
           "platItemIdPlaceholder": "璇疯緭鍏ュ钩鍙拌鍙�",
+          "fieldsIndexPlaceholder": "璇疯緭鍏ュ瓧娈电储寮�",
           "matnrCodePlaceholder": "璇疯緭鍏ョ墿鏂欑紪鐮�",
           "maktxPlaceholder": "璇疯緭鍏ョ墿鏂欏悕绉�",
           "supplierName": "渚涘簲鍟嗗悕绉�",
           "supplierNamePlaceholder": "璇疯緭鍏ヤ緵搴斿晢鍚嶇О",
-          "supplierBatchPlaceholder": "璇疯緭鍏ヤ緵搴斿晢鎵规"
+          "supplierCodePlaceholder": "璇疯緭鍏ヤ緵搴斿晢缂栫爜",
+          "supplierBatchPlaceholder": "璇疯緭鍏ヤ緵搴斿晢鎵规",
+          "statusPlaceholder": "璇烽�夋嫨鐘舵��",
+          "timeStart": "鍒涘缓寮�濮嬫椂闂�",
+          "timeStartPlaceholder": "璇烽�夋嫨鍒涘缓寮�濮嬫椂闂�",
+          "timeEnd": "鍒涘缓缁撴潫鏃堕棿",
+          "timeEndPlaceholder": "璇烽�夋嫨鍒涘缓缁撴潫鏃堕棿",
+          "memoPlaceholder": "璇疯緭鍏ュ娉�"
+        },
+        "actions": {
+          "add": "鏂板鏄庣粏"
+        },
+        "dialog": {
+          "titleAdd": "鏂板DO鍗曟槑缁�",
+          "titleEdit": "缂栬緫DO鍗曟槑缁�",
+          "deliveryId": "DO鍗旾D",
+          "platItemId": "骞冲彴琛屽彿",
+          "matnrCode": "鐗╂枡缂栫爜",
+          "maktx": "鐗╂枡鍚嶇О",
+          "fieldsIndex": "瀛楁绱㈠紩",
+          "unit": "鍗曚綅",
+          "anfme": "鏁伴噺",
+          "qty": "宸插嚭鏁伴噺",
+          "printQty": "鎵撳嵃鏁伴噺",
+          "splrName": "渚涘簲鍟嗗悕绉�",
+          "splrCode": "渚涘簲鍟嗙紪鐮�",
+          "splrBatch": "渚涘簲鍟嗘壒娆�",
+          "status": "鐘舵��",
+          "memo": "澶囨敞",
+          "placeholder": {
+            "platItemId": "璇疯緭鍏ュ钩鍙拌鍙�",
+            "matnrCode": "璇疯緭鍏ョ墿鏂欑紪鐮�",
+            "maktx": "璇疯緭鍏ョ墿鏂欏悕绉�",
+            "fieldsIndex": "璇疯緭鍏ュ瓧娈电储寮�",
+            "unit": "璇疯緭鍏ュ崟浣�",
+            "anfme": "璇疯緭鍏ユ暟閲�",
+            "qty": "璇疯緭鍏ュ凡鍑烘暟閲�",
+            "printQty": "璇疯緭鍏ユ墦鍗版暟閲�",
+            "splrName": "璇疯緭鍏ヤ緵搴斿晢鍚嶇О",
+            "splrCode": "璇疯緭鍏ヤ緵搴斿晢缂栫爜",
+            "splrBatch": "璇疯緭鍏ヤ緵搴斿晢鎵规",
+            "status": "璇烽�夋嫨鐘舵��",
+            "memo": "璇疯緭鍏ュ娉�"
+          },
+          "validation": {
+            "anfme": "璇疯緭鍏ユ暟閲�"
+          }
         },
         "table": {
           "deliveryId": "DO鍗旾D",
@@ -1698,7 +1868,13 @@
         },
         "messages": {
           "detailTimeout": "DO鍗曟槑缁嗚鎯呭姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�",
-          "detailFailed": "鑾峰彇DO鍗曟槑缁嗚鎯呭け璐�"
+          "detailFailed": "鑾峰彇DO鍗曟槑缁嗚鎯呭け璐�",
+          "createSuccess": "DO鍗曟槑缁嗘柊澧炴垚鍔�",
+          "createFailed": "DO鍗曟槑缁嗘柊澧炲け璐�",
+          "updateSuccess": "DO鍗曟槑缁嗕慨鏀规垚鍔�",
+          "updateFailed": "DO鍗曟槑缁嗕慨鏀瑰け璐�",
+          "deleteConfirm": "纭畾鍒犻櫎DO鍗曟槑缁� {code} 鍚楋紵",
+          "actionFailed": "DO鍗曟槑缁嗘搷浣滃け璐�"
         }
       },
       "transfer": {
diff --git a/rsf-design/src/views/basic-info/bas-container/modules/bas-container-areas-editor.vue b/rsf-design/src/views/basic-info/bas-container/modules/bas-container-areas-editor.vue
index 2ce6b2e..9d66a21 100644
--- a/rsf-design/src/views/basic-info/bas-container/modules/bas-container-areas-editor.vue
+++ b/rsf-design/src/views/basic-info/bas-container/modules/bas-container-areas-editor.vue
@@ -1,9 +1,9 @@
 <template>
-  <div class="space-y-3">
-    <div class="flex flex-wrap items-center gap-2">
+  <div class="areas-editor">
+    <div class="areas-editor__toolbar">
       <ElSelect
         v-model="selectedAreaId"
-        class="min-w-0 flex-1"
+        class="areas-editor__select"
         clearable
         filterable
         placeholder="璇烽�夋嫨鍙叆搴撳尯"
@@ -15,44 +15,73 @@
           :value="option.value"
         />
       </ElSelect>
-      <ElButton :disabled="selectedAreaId === '' || selectedAreaId === void 0" @click="handleAddArea">
+      <ElButton
+        :disabled="selectedAreaId === '' || selectedAreaId === void 0"
+        @click="handleAddArea"
+      >
         娣诲姞
       </ElButton>
+      <span class="areas-editor__toolbar-tip">
+        宸查�� {{ selectedAreas.length }} 涓簱鍖猴紝鍙皟鏁撮『搴�
+      </span>
     </div>
 
-    <ElEmpty v-if="!selectedAreas.length" description="鏆傛棤鍙叆搴撳尯" />
+    <div class="areas-editor__panel">
+      <template v-if="selectedAreas.length">
+        <div class="areas-editor__header">
+          <span>搴忓彿</span>
+          <span>搴撳尯</span>
+          <span>鎺掑簭</span>
+          <span>鎿嶄綔</span>
+        </div>
 
-    <div v-else class="space-y-2">
-      <div
-        v-for="(item, index) in selectedAreas"
-        :key="item.id"
-        class="flex flex-wrap items-center gap-2 rounded-lg border border-[var(--art-border-color)] px-3 py-2"
-      >
-        <div class="flex w-10 shrink-0 items-center justify-center text-sm text-[var(--art-text-secondary)]">
-          {{ index + 1 }}
-        </div>
-        <div class="min-w-0 flex-1">
-          <div class="truncate font-medium text-[var(--art-text-primary)]">
-            {{ resolveAreaLabel(item) }}
+        <ElScrollbar max-height="240px">
+          <div class="areas-editor__list">
+            <div v-for="(item, index) in selectedAreas" :key="item.id" class="areas-editor__row">
+              <div class="areas-editor__index">
+                {{ index + 1 }}
+              </div>
+              <div class="areas-editor__name">
+                <div class="areas-editor__name-text">
+                  {{ resolveAreaLabel(item) }}
+                </div>
+                <div class="areas-editor__meta"> ID: {{ item.id }} </div>
+              </div>
+              <div class="areas-editor__sort">
+                <ElInputNumber
+                  :model-value="item.sort"
+                  :min="1"
+                  :controls-position="'right'"
+                  class="areas-editor__sort-input"
+                  @update:model-value="handleSortChange(item.id, $event)"
+                />
+              </div>
+              <div class="areas-editor__actions">
+                <ElButton text size="small" :disabled="index === 0" @click="handleMoveUp(index)">
+                  涓婄Щ
+                </ElButton>
+                <ElButton
+                  text
+                  size="small"
+                  :disabled="index === selectedAreas.length - 1"
+                  @click="handleMoveDown(index)"
+                >
+                  涓嬬Щ
+                </ElButton>
+                <ElButton text size="small" type="danger" @click="handleRemove(item.id)">
+                  鍒犻櫎
+                </ElButton>
+              </div>
+            </div>
           </div>
-          <div class="text-xs text-[var(--art-text-secondary)]">
-            ID: {{ item.id }}
-          </div>
-        </div>
-        <div class="flex items-center gap-2">
-          <ElInputNumber
-            :model-value="item.sort"
-            :min="1"
-            :controls-position="'right'"
-            class="!w-24"
-            @update:model-value="handleSortChange(item.id, $event)"
-          />
-          <ElButton text :disabled="index === 0" @click="handleMoveUp(index)">涓婄Щ</ElButton>
-          <ElButton text :disabled="index === selectedAreas.length - 1" @click="handleMoveDown(index)">
-            涓嬬Щ
-          </ElButton>
-          <ElButton text type="danger" @click="handleRemove(item.id)">鍒犻櫎</ElButton>
-        </div>
+        </ElScrollbar>
+      </template>
+
+      <div v-else class="areas-editor__empty">
+        <div class="areas-editor__empty-title">鏆傛棤鍙叆搴撳尯</div>
+        <div class="areas-editor__empty-tip"
+          >浠庝笂鏂归�夋嫨搴撳尯鍚庣偣鍑烩�滄坊鍔犫�濓紝鍐嶆寜鎺掑簭鍊兼垨涓婁笅绉诲姩璋冩暣椤哄簭</div
+        >
       </div>
     </div>
   </div>
@@ -70,13 +99,14 @@
   const selectedAreaId = ref('')
 
   const selectedAreas = computed(() => normalizeSelectedAreas(modelValue.value))
-  const areaOptionMap = computed(() =>
-    new Map(
-      (Array.isArray(props.areaOptions) ? props.areaOptions : [])
-        .map((item) => normalizeAreaOption(item))
-        .filter(Boolean)
-        .map((item) => [String(item.value), item.label])
-    )
+  const areaOptionMap = computed(
+    () =>
+      new Map(
+        (Array.isArray(props.areaOptions) ? props.areaOptions : [])
+          .map((item) => normalizeAreaOption(item))
+          .filter(Boolean)
+          .map((item) => [String(item.value), item.label])
+      )
   )
   const availableAreaOptions = computed(() => {
     const selectedIds = new Set(selectedAreas.value.map((item) => String(item.id)))
@@ -95,7 +125,9 @@
     }
     return {
       value: Number(value),
-      label: String(option.label || option.name || option.areaName || option.code || option.areaCode || value)
+      label: String(
+        option.label || option.name || option.areaName || option.code || option.areaCode || value
+      )
     }
   }
 
@@ -175,3 +207,151 @@
     syncModel(selectedAreas.value.filter((item) => Number(item.id) !== Number(id)))
   }
 </script>
+
+<style scoped>
+  .areas-editor {
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+    width: 100%;
+  }
+
+  .areas-editor__toolbar {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    gap: 8px;
+  }
+
+  .areas-editor__select {
+    width: 320px;
+    max-width: 100%;
+  }
+
+  .areas-editor__toolbar-tip {
+    color: var(--art-text-secondary);
+    font-size: 12px;
+    line-height: 1.5;
+  }
+
+  .areas-editor__panel {
+    border: 1px solid var(--art-border-color);
+    border-radius: 12px;
+    background: var(--art-bg-color);
+    overflow: hidden;
+  }
+
+  .areas-editor__header {
+    display: grid;
+    grid-template-columns: 56px minmax(0, 1fr) 112px 190px;
+    gap: 12px;
+    align-items: center;
+    padding: 10px 16px;
+    background: var(--el-fill-color-light);
+    color: var(--art-text-secondary);
+    font-size: 12px;
+    line-height: 1;
+  }
+
+  .areas-editor__list {
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+    padding: 12px 16px;
+  }
+
+  .areas-editor__row {
+    display: grid;
+    grid-template-columns: 56px minmax(0, 1fr) 112px 190px;
+    gap: 12px;
+    align-items: center;
+    border: 1px solid var(--art-border-color);
+    border-radius: 10px;
+    padding: 10px 12px;
+    background: var(--el-bg-color-page);
+  }
+
+  .areas-editor__index {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: var(--art-text-secondary);
+    font-size: 13px;
+  }
+
+  .areas-editor__name {
+    min-width: 0;
+  }
+
+  .areas-editor__name-text {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    color: var(--art-text-primary);
+    font-weight: 500;
+    line-height: 1.4;
+  }
+
+  .areas-editor__meta {
+    margin-top: 2px;
+    color: var(--art-text-secondary);
+    font-size: 12px;
+    line-height: 1.4;
+  }
+
+  .areas-editor__sort {
+    display: flex;
+    justify-content: flex-start;
+  }
+
+  :deep(.areas-editor__sort-input) {
+    width: 100px;
+  }
+
+  .areas-editor__actions {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    gap: 4px;
+  }
+
+  .areas-editor__empty {
+    display: flex;
+    min-height: 136px;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    gap: 8px;
+    padding: 20px 16px;
+    text-align: center;
+  }
+
+  .areas-editor__empty-title {
+    color: var(--art-text-primary);
+    font-size: 14px;
+    font-weight: 500;
+    line-height: 1.4;
+  }
+
+  .areas-editor__empty-tip {
+    max-width: 420px;
+    color: var(--art-text-secondary);
+    font-size: 12px;
+    line-height: 1.6;
+  }
+
+  @media (max-width: 900px) {
+    .areas-editor__header {
+      display: none;
+    }
+
+    .areas-editor__row {
+      grid-template-columns: 48px minmax(0, 1fr);
+    }
+
+    .areas-editor__sort,
+    .areas-editor__actions {
+      grid-column: 2;
+    }
+  }
+</style>
diff --git a/rsf-design/src/views/basic-info/bas-station/basStationPage.helpers.js b/rsf-design/src/views/basic-info/bas-station/basStationPage.helpers.js
index 6750a27..b8a49d8 100644
--- a/rsf-design/src/views/basic-info/bas-station/basStationPage.helpers.js
+++ b/rsf-design/src/views/basic-info/bas-station/basStationPage.helpers.js
@@ -91,9 +91,14 @@
     stationId: '',
     type: '',
     useStatus: '',
+    inAble: '',
+    outAble: '',
     area: '',
     isCrossZone: '',
+    crossZoneArea: '',
     isWcs: '',
+    wcsData: '',
+    containerType: '',
     barcode: '',
     autoTransfer: '',
     status: '',
@@ -122,6 +127,25 @@
     outAble: 0,
     status: 1,
     memo: ''
+  }
+}
+
+export function createBasStationInitState() {
+  return {
+    type: 0,
+    useStatus: '',
+    areaIds: [],
+    containerTypes: [],
+    inAble: 0,
+    outAble: 0,
+    rows: [createBasStationInitRow()]
+  }
+}
+
+export function createBasStationInitRow() {
+  return {
+    stationName: '',
+    stationId: ''
   }
 }
 
@@ -203,6 +227,14 @@
         ? Number(params.type)
         : void 0,
     useStatus: normalizeText(params.useStatus),
+    inAble:
+      params.inAble !== undefined && params.inAble !== null && params.inAble !== ''
+        ? Number(params.inAble)
+        : void 0,
+    outAble:
+      params.outAble !== undefined && params.outAble !== null && params.outAble !== ''
+        ? Number(params.outAble)
+        : void 0,
     area:
       params.area !== undefined && params.area !== null && params.area !== ''
         ? Number(params.area)
@@ -211,13 +243,23 @@
       params.isCrossZone !== undefined && params.isCrossZone !== null && params.isCrossZone !== ''
         ? Number(params.isCrossZone)
         : void 0,
+    crossZoneArea: normalizeText(params.crossZoneArea),
     isWcs:
       params.isWcs !== undefined && params.isWcs !== null && params.isWcs !== ''
         ? Number(params.isWcs)
         : void 0,
+    wcsData: normalizeText(params.wcsData),
+    containerType:
+      params.containerType !== undefined &&
+      params.containerType !== null &&
+      params.containerType !== ''
+        ? Number(params.containerType)
+        : void 0,
     barcode: normalizeText(params.barcode),
     autoTransfer:
-      params.autoTransfer !== undefined && params.autoTransfer !== null && params.autoTransfer !== ''
+      params.autoTransfer !== undefined &&
+      params.autoTransfer !== null &&
+      params.autoTransfer !== ''
         ? Number(params.autoTransfer)
         : void 0,
     status:
@@ -230,7 +272,9 @@
   }
 
   return Object.fromEntries(
-    Object.entries(searchParams).filter(([, value]) => value !== '' && value !== void 0 && value !== null)
+    Object.entries(searchParams).filter(
+      ([, value]) => value !== '' && value !== void 0 && value !== null
+    )
   )
 }
 
@@ -238,6 +282,7 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
+    orderBy: normalizeText(params.orderBy) || 'create_time desc',
     ...buildBasStationSearchParams(params)
   }
 }
@@ -256,10 +301,14 @@
       ? { area: Number(formData.area) }
       : {}),
     useStatus: normalizeText(formData.useStatus) || '',
-    ...(formData.isCrossZone !== void 0 && formData.isCrossZone !== null && formData.isCrossZone !== ''
+    ...(formData.isCrossZone !== void 0 &&
+    formData.isCrossZone !== null &&
+    formData.isCrossZone !== ''
       ? { isCrossZone: Number(formData.isCrossZone) }
       : {}),
-    ...(normalizeIdArray(formData.areaIds).length ? { areaIds: normalizeIdArray(formData.areaIds).map((item) => item.id) } : {}),
+    ...(normalizeIdArray(formData.areaIds).length
+      ? { areaIds: normalizeIdArray(formData.areaIds).map((item) => item.id) }
+      : {}),
     ...(formData.isWcs !== void 0 && formData.isWcs !== null && formData.isWcs !== ''
       ? { isWcs: Number(formData.isWcs) }
       : {}),
@@ -268,7 +317,9 @@
       ? { containerTypes: normalizePlainIdArray(formData.containerTypes) }
       : {}),
     barcode: normalizeText(formData.barcode) || '',
-    ...(formData.autoTransfer !== void 0 && formData.autoTransfer !== null && formData.autoTransfer !== ''
+    ...(formData.autoTransfer !== void 0 &&
+    formData.autoTransfer !== null &&
+    formData.autoTransfer !== ''
       ? { autoTransfer: Number(formData.autoTransfer) }
       : {}),
     ...(formData.inAble !== void 0 && formData.inAble !== null && formData.inAble !== ''
@@ -288,7 +339,9 @@
 export function createBasStationDialogModel(record = {}) {
   return {
     ...createBasStationFormState(),
-    ...(record.id !== void 0 && record.id !== null && record.id !== '' ? { id: Number(record.id) } : {}),
+    ...(record.id !== void 0 && record.id !== null && record.id !== ''
+      ? { id: Number(record.id) }
+      : {}),
     stationName: normalizeText(record.stationName || ''),
     stationId: normalizeText(record.stationId || ''),
     type:
@@ -310,13 +363,18 @@
         ? Number(record.isWcs)
         : 0,
     wcsData: normalizeText(record.wcsData || ''),
-    containerTypes: normalizePlainIdArray(record.containerTypes ?? record.containerType ?? record.containerTypes$ ?? []),
+    containerTypes: normalizePlainIdArray(
+      record.containerTypes ?? record.containerType ?? record.containerTypes$ ?? []
+    ),
     barcode: normalizeText(record.barcode || ''),
     autoTransfer:
       record.autoTransfer !== void 0 && record.autoTransfer !== null && record.autoTransfer !== ''
         ? Number(record.autoTransfer)
         : 0,
-    inAble: record.inAble !== void 0 && record.inAble !== null && record.inAble !== '' ? Number(record.inAble) : 0,
+    inAble:
+      record.inAble !== void 0 && record.inAble !== null && record.inAble !== ''
+        ? Number(record.inAble)
+        : 0,
     outAble:
       record.outAble !== void 0 && record.outAble !== null && record.outAble !== ''
         ? Number(record.outAble)
@@ -327,6 +385,74 @@
 }
 
 export const buildBasStationDialogModel = createBasStationDialogModel
+
+export function buildBasStationInitModel(record = {}) {
+  const initialRows = Array.isArray(record.rows) ? record.rows : record.pairs
+  const rows =
+    Array.isArray(initialRows) && initialRows.length
+      ? initialRows
+          .map((item) => ({
+            stationName: normalizeText(item?.stationName || ''),
+            stationId: normalizeText(item?.stationId || '')
+          }))
+          .filter((item) => item.stationName || item.stationId)
+      : [createBasStationInitRow()]
+
+  return {
+    ...createBasStationInitState(),
+    type:
+      record.type !== void 0 && record.type !== null && record.type !== ''
+        ? Number(record.type)
+        : 0,
+    useStatus: normalizeText(record.useStatus || ''),
+    areaIds: normalizePlainIdArray(record.areaIds || []),
+    containerTypes: normalizePlainIdArray(record.containerTypes || []),
+    inAble:
+      record.inAble !== void 0 && record.inAble !== null && record.inAble !== ''
+        ? Number(record.inAble)
+        : 0,
+    outAble:
+      record.outAble !== void 0 && record.outAble !== null && record.outAble !== ''
+        ? Number(record.outAble)
+        : 0,
+    rows
+  }
+}
+
+export function buildBasStationInitPayloadList(formData = {}) {
+  const useStatus = normalizeText(formData.useStatus)
+  const basePayload = {
+    ...(formData.type !== void 0 && formData.type !== null && formData.type !== ''
+      ? { type: Number(formData.type) }
+      : {}),
+    ...(useStatus ? { useStatus } : {}),
+    ...(normalizePlainIdArray(formData.areaIds).length
+      ? { areaIds: normalizePlainIdArray(formData.areaIds) }
+      : {}),
+    ...(normalizePlainIdArray(formData.containerTypes).length
+      ? { containerTypes: normalizePlainIdArray(formData.containerTypes) }
+      : {}),
+    ...(formData.inAble !== void 0 && formData.inAble !== null && formData.inAble !== ''
+      ? { inAble: Number(formData.inAble) }
+      : {}),
+    ...(formData.outAble !== void 0 && formData.outAble !== null && formData.outAble !== ''
+      ? { outAble: Number(formData.outAble) }
+      : {})
+  }
+
+  const rows = Array.isArray(formData.rows) ? formData.rows : []
+  return rows
+    .map((item) => ({
+      stationName: normalizeText(item?.stationName || ''),
+      stationId: normalizeText(item?.stationId || '')
+    }))
+    .filter((item) => item.stationName && item.stationId)
+    .map((item) => ({
+      ...basePayload,
+      stationName: item.stationName,
+      stationId: item.stationId
+    }))
+}
 
 export function resolveBasStationAreaOptions(records = []) {
   if (!Array.isArray(records)) {
@@ -344,7 +470,9 @@
       }
       return {
         value: Number(value),
-        label: normalizeText(item.name || item.areaName || item.code || item.areaCode || `搴撳尯 ${value}`)
+        label: normalizeText(
+          item.name || item.areaName || item.code || item.areaCode || `搴撳尯 ${value}`
+        )
       }
     })
     .filter(Boolean)
@@ -403,7 +531,7 @@
       if (item === null || item === undefined || item === '') {
         return ''
       }
-      const id = typeof item === 'object' ? item.id ?? item.areaId ?? item.value : item
+      const id = typeof item === 'object' ? (item.id ?? item.areaId ?? item.value) : item
       if (typeof resolveLabel === 'function') {
         const resolvedLabel = normalizeText(resolveLabel(id))
         if (resolvedLabel) {
@@ -411,7 +539,9 @@
         }
       }
       if (typeof item === 'object') {
-        return normalizeText(item.name || item.areaName || item.label || item.code || item.areaCode || id)
+        return normalizeText(
+          item.name || item.areaName || item.label || item.code || item.areaCode || id
+        )
       }
       return normalizeText(`${fallbackPrefix} ${id}`)
     })
@@ -425,7 +555,7 @@
   }
   return records
     .map((item) => {
-      const id = typeof item === 'object' ? item.id ?? item.value : item
+      const id = typeof item === 'object' ? (item.id ?? item.value) : item
       if (typeof resolveLabel === 'function') {
         const resolved = normalizeText(resolveLabel(id))
         if (resolved) {
@@ -441,55 +571,108 @@
     .join('銆�')
 }
 
-export function normalizeBasStationDetailRecord(record = {}, resolveAreaLabel, resolveContainerTypeLabel) {
+export function normalizeBasStationDetailRecord(
+  record = {},
+  resolveAreaLabel,
+  resolveContainerTypeLabel
+) {
   const areaIds = normalizeIdArray(record.areaIds ?? record.crossZoneArea ?? [])
-  const containerTypes = normalizePlainIdArray(record.containerTypes ?? record.containerType ?? record.containerTypes$ ?? [])
+  const containerTypes = normalizePlainIdArray(
+    record.containerTypes ?? record.containerType ?? record.containerTypes$ ?? []
+  )
   const statusMeta = getBasStationStatusMeta(record.statusBool ?? record.status)
   const typeMeta = getBasStationTypeMeta(record.type)
   const useStatusMeta = getBasStationUseStatusMeta(record.useStatus$ || record.useStatus)
+  const crossZoneAreaItems = areaIds
+    .map((item, index) => {
+      const label = normalizeText(
+        resolveAreaLabel?.(item.id) ||
+          item.name ||
+          item.areaName ||
+          item.label ||
+          item.code ||
+          item.areaCode ||
+          ''
+      )
+      return {
+        id: item.id,
+        sort: item.sort ?? index + 1,
+        label: label || `搴撳尯 ${item.id}`
+      }
+    })
+    .filter((item) => item.id !== void 0 && item.id !== null && item.id !== '')
+  const containerTypeItems = containerTypes
+    .map((item) => {
+      const label = normalizeText(resolveContainerTypeLabel?.(item) || '')
+      return {
+        value: item,
+        label: label || `瀹瑰櫒绫诲瀷 ${item}`
+      }
+    })
+    .filter((item) => item.value !== void 0 && item.value !== null && item.value !== '')
 
   return {
     ...record,
     stationName: normalizeText(record.stationName || ''),
     stationId: normalizeText(record.stationId || ''),
-    type: record.type !== void 0 && record.type !== null && record.type !== '' ? Number(record.type) : void 0,
+    type:
+      record.type !== void 0 && record.type !== null && record.type !== ''
+        ? Number(record.type)
+        : void 0,
     typeText: typeMeta.text,
     useStatus: normalizeText(record.useStatus || record.useStatus$ || ''),
     useStatusText: useStatusMeta.text,
-    area: record.area !== void 0 && record.area !== null && record.area !== '' ? Number(record.area) : void 0,
-    areaText: normalizeText(resolveAreaLabel?.(record.area) || record.area$ || record.areaName || ''),
+    area:
+      record.area !== void 0 && record.area !== null && record.area !== ''
+        ? Number(record.area)
+        : void 0,
+    areaText: normalizeText(
+      resolveAreaLabel?.(record.area) || record.area$ || record.areaName || ''
+    ),
     areaIds,
+    crossZoneAreaItems,
     crossZoneAreaText: buildIdLabelText(areaIds, resolveAreaLabel, '搴撳尯'),
-    isCrossZone: record.isCrossZone !== void 0 && record.isCrossZone !== null && record.isCrossZone !== ''
-      ? Number(record.isCrossZone)
-      : void 0,
+    isCrossZone:
+      record.isCrossZone !== void 0 && record.isCrossZone !== null && record.isCrossZone !== ''
+        ? Number(record.isCrossZone)
+        : void 0,
     isCrossZoneText: getBasStationBooleanMeta(record.isCrossZone).text,
-    isWcs: record.isWcs !== void 0 && record.isWcs !== null && record.isWcs !== ''
-      ? Number(record.isWcs)
-      : void 0,
+    isWcs:
+      record.isWcs !== void 0 && record.isWcs !== null && record.isWcs !== ''
+        ? Number(record.isWcs)
+        : void 0,
     isWcsText: getBasStationBooleanMeta(record.isWcs).text,
     wcsData: normalizeText(record.wcsData || ''),
     containerTypes,
+    containerTypeItems,
     containerTypesText: buildArrayText(containerTypes, resolveContainerTypeLabel, '瀹瑰櫒绫诲瀷'),
     barcode: normalizeText(record.barcode || ''),
-    autoTransfer: record.autoTransfer !== void 0 && record.autoTransfer !== null && record.autoTransfer !== ''
-      ? Number(record.autoTransfer)
-      : void 0,
+    autoTransfer:
+      record.autoTransfer !== void 0 && record.autoTransfer !== null && record.autoTransfer !== ''
+        ? Number(record.autoTransfer)
+        : void 0,
     autoTransferText: getBasStationBooleanMeta(record.autoTransfer).text,
-    inAble: record.inAble !== void 0 && record.inAble !== null && record.inAble !== ''
-      ? Number(record.inAble)
-      : void 0,
+    inAble:
+      record.inAble !== void 0 && record.inAble !== null && record.inAble !== ''
+        ? Number(record.inAble)
+        : void 0,
     inAbleText: getBasStationBooleanMeta(record.inAble).text,
-    outAble: record.outAble !== void 0 && record.outAble !== null && record.outAble !== ''
-      ? Number(record.outAble)
-      : void 0,
+    outAble:
+      record.outAble !== void 0 && record.outAble !== null && record.outAble !== ''
+        ? Number(record.outAble)
+        : void 0,
     outAbleText: getBasStationBooleanMeta(record.outAble).text,
     statusText: statusMeta.text,
     statusType: statusMeta.type,
     statusBool: record.statusBool !== void 0 ? Boolean(record.statusBool) : statusMeta.bool,
-    stationAlias: Array.isArray(record.stationAlias) ? [...record.stationAlias] : record.stationAlias,
+    stationAlias: Array.isArray(record.stationAlias)
+      ? [...record.stationAlias]
+      : record.stationAlias,
     stationAliasText: Array.isArray(record.stationAlias)
-      ? record.stationAlias.map((item) => normalizeText(item)).filter(Boolean).join('銆�')
+      ? record.stationAlias
+          .map((item) => normalizeText(item))
+          .filter(Boolean)
+          .join('銆�')
       : normalizeText(record.stationAlias || record.stationAlias$ || ''),
     productionLineCode: normalizeText(record.productionLineCode || ''),
     productionLineName: normalizeText(record.productionLineName || ''),
@@ -501,15 +684,25 @@
   }
 }
 
-export function normalizeBasStationListRow(record = {}, resolveAreaLabel, resolveContainerTypeLabel) {
+export function normalizeBasStationListRow(
+  record = {},
+  resolveAreaLabel,
+  resolveContainerTypeLabel
+) {
   return normalizeBasStationDetailRecord(record, resolveAreaLabel, resolveContainerTypeLabel)
 }
 
-export function buildBasStationPrintRows(records = [], resolveAreaLabel, resolveContainerTypeLabel) {
+export function buildBasStationPrintRows(
+  records = [],
+  resolveAreaLabel,
+  resolveContainerTypeLabel
+) {
   if (!Array.isArray(records)) {
     return []
   }
-  return records.map((record) => normalizeBasStationListRow(record, resolveAreaLabel, resolveContainerTypeLabel))
+  return records.map((record) =>
+    normalizeBasStationListRow(record, resolveAreaLabel, resolveContainerTypeLabel)
+  )
 }
 
 export function buildBasStationReportMeta({
diff --git a/rsf-design/src/views/basic-info/bas-station/basStationTable.columns.js b/rsf-design/src/views/basic-info/bas-station/basStationTable.columns.js
index 6a6c46f..0697d6a 100644
--- a/rsf-design/src/views/basic-info/bas-station/basStationTable.columns.js
+++ b/rsf-design/src/views/basic-info/bas-station/basStationTable.columns.js
@@ -2,6 +2,7 @@
 import { ElTag } from 'element-plus'
 import ArtButtonMore from '@/components/core/forms/art-button-more/index.vue'
 import { $t } from '@/locales'
+import BasStationTagCell from './modules/bas-station-tag-cell.vue'
 import {
   getBasStationUseStatusMeta,
   getBasStationStatusMeta,
@@ -10,6 +11,7 @@
 
 export function createBasStationTableColumns({
   handleView,
+  handleCopy,
   handleEdit,
   handleDelete,
   canEdit = true,
@@ -17,12 +19,21 @@
 } = {}) {
   const operations = [{ key: 'view', label: $t('common.actions.detail'), icon: 'ri:eye-line' }]
 
+  if (handleCopy) {
+    operations.push({ key: 'copy', label: '澶嶅埗鍒濆鍖�', icon: 'ri:file-copy-line' })
+  }
+
   if (canEdit && handleEdit) {
     operations.push({ key: 'edit', label: $t('common.actions.edit'), icon: 'ri:pencil-line' })
   }
 
   if (canDelete && handleDelete) {
-    operations.push({ key: 'delete', label: $t('common.actions.delete'), icon: 'ri:delete-bin-5-line', color: 'var(--art-error)' })
+    operations.push({
+      key: 'delete',
+      label: $t('common.actions.delete'),
+      icon: 'ri:delete-bin-5-line',
+      color: 'var(--art-error)'
+    })
   }
 
   return [
@@ -70,18 +81,24 @@
       formatter: (row) => row.areaText || row.area$ || '--'
     },
     {
-      prop: 'crossZoneAreaText',
+      prop: 'crossZoneAreaItems',
       label: $t('pages.basicInfo.basStation.table.crossZoneArea'),
       minWidth: 220,
-      showOverflowTooltip: true,
-      formatter: (row) => row.crossZoneAreaText || '--'
+      formatter: (row) =>
+        h(BasStationTagCell, {
+          items: row.crossZoneAreaItems || [],
+          title: '鍙法鍖哄簱鍖�'
+        })
     },
     {
-      prop: 'containerTypesText',
+      prop: 'containerTypeItems',
       label: $t('pages.basicInfo.basStation.table.containerTypes'),
       minWidth: 200,
-      showOverflowTooltip: true,
-      formatter: (row) => row.containerTypesText || '--'
+      formatter: (row) =>
+        h(BasStationTagCell, {
+          items: row.containerTypeItems || [],
+          title: '鍙叆瀹瑰櫒绫诲瀷'
+        })
     },
     {
       prop: 'barcode',
@@ -150,6 +167,20 @@
       formatter: (row) => row.updateTimeText || row.updateTime$ || '--'
     },
     {
+      prop: 'createByText',
+      label: $t('table.createBy'),
+      minWidth: 120,
+      showOverflowTooltip: true,
+      formatter: (row) => row.createByText || row.createBy$ || '--'
+    },
+    {
+      prop: 'createTimeText',
+      label: $t('table.createTime'),
+      minWidth: 170,
+      showOverflowTooltip: true,
+      formatter: (row) => row.createTimeText || row.createTime$ || '--'
+    },
+    {
       prop: 'memo',
       label: $t('table.remark'),
       minWidth: 180,
@@ -167,6 +198,7 @@
           list: operations,
           onClick: (item) => {
             if (item.key === 'view') handleView?.(row)
+            if (item.key === 'copy') handleCopy?.(row)
             if (item.key === 'edit') handleEdit?.(row)
             if (item.key === 'delete') handleDelete?.(row)
           }
diff --git a/rsf-design/src/views/basic-info/bas-station/index.vue b/rsf-design/src/views/basic-info/bas-station/index.vue
index a5dd00a..4995c25 100644
--- a/rsf-design/src/views/basic-info/bas-station/index.vue
+++ b/rsf-design/src/views/basic-info/bas-station/index.vue
@@ -13,6 +13,7 @@
         <template #left>
           <ElSpace wrap>
             <ElButton v-auth="'add'" @click="showDialog('add')" v-ripple>鏂板绔欑偣</ElButton>
+            <ElButton v-auth="'add'" @click="openInitDialog()" v-ripple>绔欑偣鍒濆鍖�</ElButton>
             <ElButton
               v-auth="'delete'"
               type="danger"
@@ -68,6 +69,15 @@
         :resolve-area-label="resolveAreaLabel"
         :resolve-container-type-label="resolveContainerTypeLabel"
       />
+
+      <BasStationInitDialog
+        v-model:visible="initDialogVisible"
+        :initial-data="currentInitData"
+        :area-options="areaOptions"
+        :container-type-options="containerTypeOptions"
+        :use-status-options="useStatusOptions"
+        @submit="handleInitSubmit"
+      />
     </ElCard>
   </div>
 </template>
@@ -96,11 +106,14 @@
   } from '@/api/bas-station'
   import BasStationDialog from './modules/bas-station-dialog.vue'
   import BasStationDetailDrawer from './modules/bas-station-detail-drawer.vue'
+  import BasStationInitDialog from './modules/bas-station-init-dialog.vue'
   import { createBasStationTableColumns } from './basStationTable.columns'
   import {
     BAS_STATION_REPORT_STYLE,
     BAS_STATION_REPORT_TITLE,
     buildBasStationDialogModel,
+    buildBasStationInitModel,
+    buildBasStationInitPayloadList,
     buildBasStationPageQueryParams,
     buildBasStationPrintRows,
     buildBasStationReportMeta,
@@ -129,6 +142,8 @@
   const detailDrawerVisible = ref(false)
   const detailLoading = ref(false)
   const detailData = ref({})
+  const initDialogVisible = ref(false)
+  const currentInitData = ref(buildBasStationInitModel())
   let handleDeleteAction = null
 
   const reportTitle = BAS_STATION_REPORT_TITLE
@@ -207,6 +222,24 @@
       }
     },
     {
+      label: '鍙叆',
+      key: 'inAble',
+      type: 'select',
+      props: {
+        clearable: true,
+        options: getBasStationBooleanOptions()
+      }
+    },
+    {
+      label: '鍙嚭',
+      key: 'outAble',
+      type: 'select',
+      props: {
+        clearable: true,
+        options: getBasStationBooleanOptions()
+      }
+    },
+    {
       label: '鎵�灞炲簱鍖�',
       key: 'area',
       type: 'select',
@@ -226,12 +259,40 @@
       }
     },
     {
+      label: '鍙法鍖哄簱鍖�',
+      key: 'crossZoneArea',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: '璇疯緭鍏ュ彲璺ㄥ尯搴撳尯'
+      }
+    },
+    {
       label: '鏄惁WCS',
       key: 'isWcs',
       type: 'select',
       props: {
         clearable: true,
         options: getBasStationBooleanOptions()
+      }
+    },
+    {
+      label: 'WCS鏁版嵁',
+      key: 'wcsData',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: '璇疯緭鍏CS鏁版嵁'
+      }
+    },
+    {
+      label: '瀹瑰櫒绫诲瀷',
+      key: 'containerType',
+      type: 'select',
+      props: {
+        clearable: true,
+        filterable: true,
+        options: containerTypeOptions.value
       }
     },
     {
@@ -301,10 +362,18 @@
     detailDrawerVisible.value = true
     detailLoading.value = true
     try {
-      const detail = await guardRequestWithMessage(fetchGetBasStationDetail(row.id), {}, {
-        timeoutMessage: '绔欑偣璇︽儏鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟'
-      })
-      detailData.value = normalizeBasStationDetailRecord(detail, resolveAreaLabel, resolveContainerTypeLabel)
+      const detail = await guardRequestWithMessage(
+        fetchGetBasStationDetail(row.id),
+        {},
+        {
+          timeoutMessage: '绔欑偣璇︽儏鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟'
+        }
+      )
+      detailData.value = normalizeBasStationDetailRecord(
+        detail,
+        resolveAreaLabel,
+        resolveContainerTypeLabel
+      )
     } catch (error) {
       detailDrawerVisible.value = false
       detailData.value = {}
@@ -316,39 +385,86 @@
 
   async function openEditDialog(row) {
     try {
-      const detail = await guardRequestWithMessage(fetchGetBasStationDetail(row.id), {}, {
-        timeoutMessage: '绔欑偣璇︽儏鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟'
-      })
+      const detail = await guardRequestWithMessage(
+        fetchGetBasStationDetail(row.id),
+        {},
+        {
+          timeoutMessage: '绔欑偣璇︽儏鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟'
+        }
+      )
       showDialog('edit', detail)
     } catch (error) {
       ElMessage.error(error?.message || '鑾峰彇绔欑偣璇︽儏澶辫触')
     }
   }
 
-  const { columns, columnChecks, data, loading, pagination, getData, replaceSearchParams, resetSearchParams, handleSizeChange, handleCurrentChange, refreshData, refreshCreate, refreshUpdate, refreshRemove } =
-    useTable({
-      core: {
-        apiFn: fetchBasStationPage,
-        apiParams: buildBasStationPageQueryParams(searchForm.value),
-        paginationKey: getBasStationPaginationKey(),
-        columnsFactory: () =>
-          createBasStationTableColumns({
-            handleView: openDetail,
-            handleEdit: hasAuth('update') ? openEditDialog : null,
-            handleDelete: hasAuth('delete') ? (row) => handleDeleteAction?.(row) : null,
-            canEdit: hasAuth('update'),
-            canDelete: hasAuth('delete')
-          })
-      },
-      transform: {
-        dataTransformer: (records) => {
-          if (!Array.isArray(records)) {
-            return []
-          }
-          return records.map((item) => normalizeBasStationListRow(item, resolveAreaLabel, resolveContainerTypeLabel))
+  function createInitRecordFromRow(row) {
+    return buildBasStationInitModel({
+      type: row.type ?? 0,
+      useStatus: row.useStatus || row.useStatusText || '',
+      areaIds: Array.isArray(row.areaIds)
+        ? row.areaIds.map((item) =>
+            typeof item === 'object' ? (item.id ?? item.areaId ?? item.value) : item
+          )
+        : [],
+      containerTypes: Array.isArray(row.containerTypes) ? [...row.containerTypes] : [],
+      inAble: row.inAble ?? 0,
+      outAble: row.outAble ?? 0,
+      rows: [
+        {
+          stationName: row.stationName || '',
+          stationId: row.stationId || ''
         }
-      }
+      ]
     })
+  }
+
+  function openInitDialog(record = null) {
+    currentInitData.value = record ? createInitRecordFromRow(record) : buildBasStationInitModel()
+    initDialogVisible.value = true
+  }
+
+  const {
+    columns,
+    columnChecks,
+    data,
+    loading,
+    pagination,
+    getData,
+    replaceSearchParams,
+    resetSearchParams,
+    handleSizeChange,
+    handleCurrentChange,
+    refreshData,
+    refreshCreate,
+    refreshUpdate,
+    refreshRemove
+  } = useTable({
+    core: {
+      apiFn: fetchBasStationPage,
+      apiParams: buildBasStationPageQueryParams(searchForm.value),
+      paginationKey: getBasStationPaginationKey(),
+      columnsFactory: () =>
+        createBasStationTableColumns({
+          handleView: openDetail,
+          handleCopy: hasAuth('add') ? openInitDialog : null,
+          handleEdit: hasAuth('update') ? openEditDialog : null,
+          handleDelete: hasAuth('delete') ? (row) => handleDeleteAction?.(row) : null,
+          canEdit: hasAuth('update'),
+          canDelete: hasAuth('delete')
+        })
+    },
+    transform: {
+      dataTransformer: (records) => {
+        if (!Array.isArray(records)) {
+          return []
+        }
+        return records.map((item) =>
+          normalizeBasStationListRow(item, resolveAreaLabel, resolveContainerTypeLabel)
+        )
+      }
+    }
+  })
 
   const {
     dialogVisible,
@@ -394,30 +510,39 @@
       await fetchBasStationPage({
         ...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
   }
 
-  const { previewVisible, previewRows, previewMeta, handlePreviewVisibleChange, handleExport, handlePrint } =
-    usePrintExportPage({
-      downloadFileName: 'bas-station.xlsx',
-      requestExport: (payload) =>
-        fetchExportBasStationReport(payload, {
-          headers: {
-            Authorization: userStore.accessToken || ''
-          }
-        }),
-      resolvePrintRecords,
-      buildPreviewRows: (records) => buildBasStationPrintRows(records, resolveAreaLabel, resolveContainerTypeLabel),
-      buildPreviewMeta
-    })
+  const {
+    previewVisible,
+    previewRows,
+    previewMeta,
+    handlePreviewVisibleChange,
+    handleExport,
+    handlePrint
+  } = usePrintExportPage({
+    downloadFileName: 'bas-station.xlsx',
+    requestExport: (payload) =>
+      fetchExportBasStationReport(payload, {
+        headers: {
+          Authorization: userStore.accessToken || ''
+        }
+      }),
+    resolvePrintRecords,
+    buildPreviewRows: (records) =>
+      buildBasStationPrintRows(records, resolveAreaLabel, resolveContainerTypeLabel),
+    buildPreviewMeta
+  })
 
   const resolvedPreviewMeta = computed(() =>
     buildBasStationReportMeta({
       previewMeta: previewMeta.value,
       count: previewRows.value.length,
-      orientation: previewMeta.value?.reportStyle?.orientation || BAS_STATION_REPORT_STYLE.orientation
+      orientation:
+        previewMeta.value?.reportStyle?.orientation || BAS_STATION_REPORT_STYLE.orientation
     })
   )
 
@@ -429,6 +554,47 @@
   function handleReset() {
     Object.assign(searchForm.value, createBasStationSearchState())
     resetSearchParams()
+  }
+
+  async function handleInitSubmit(formData) {
+    const payloads = buildBasStationInitPayloadList(formData)
+    if (!payloads.length) {
+      ElMessage.error('璇疯嚦灏戝~鍐欎竴缁勭珯鐐圭紪鐮佸拰绔欑偣鍚嶇О')
+      return
+    }
+
+    let successCount = 0
+    let failCount = 0
+    let firstErrorMessage = ''
+
+    for (const payload of payloads) {
+      try {
+        await fetchSaveBasStation(payload)
+        successCount += 1
+      } catch (error) {
+        failCount += 1
+        if (!firstErrorMessage) {
+          firstErrorMessage = error?.message || `${payload.stationName} 鍒濆鍖栧け璐
+        }
+      }
+    }
+
+    if (successCount > 0) {
+      ElMessage.success(
+        failCount > 0
+          ? `鎴愬姛淇濆瓨 ${successCount} 鏉★紝澶辫触 ${failCount} 鏉
+          : `鎴愬姛淇濆瓨 ${successCount} 鏉
+      )
+      initDialogVisible.value = false
+      currentInitData.value = buildBasStationInitModel()
+      await refreshCreate()
+      if (failCount > 0 && firstErrorMessage) {
+        ElMessage.warning(firstErrorMessage)
+      }
+      return
+    }
+
+    ElMessage.error(firstErrorMessage || '绔欑偣鍒濆鍖栧け璐�')
   }
 
   async function loadAreaOptions() {
@@ -449,7 +615,9 @@
       { records: [] },
       { timeoutMessage: '瀹瑰櫒绫诲瀷鍔犺浇瓒呮椂锛屽凡鍋滄绛夊緟' }
     )
-    containerTypeOptions.value = buildBasStationContainerTypeOptions(defaultResponseAdapter(response).records)
+    containerTypeOptions.value = buildBasStationContainerTypeOptions(
+      defaultResponseAdapter(response).records
+    )
   }
 
   async function loadUseStatusOptions() {
@@ -463,7 +631,9 @@
       { records: [] },
       { timeoutMessage: '浣跨敤鐘舵�佸姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�' }
     )
-    useStatusOptions.value = buildBasStationUseStatusOptions(defaultResponseAdapter(response).records)
+    useStatusOptions.value = buildBasStationUseStatusOptions(
+      defaultResponseAdapter(response).records
+    )
   }
 
   onMounted(async () => {
diff --git a/rsf-design/src/views/basic-info/bas-station/modules/bas-station-init-dialog.vue b/rsf-design/src/views/basic-info/bas-station/modules/bas-station-init-dialog.vue
new file mode 100644
index 0000000..064fe0b
--- /dev/null
+++ b/rsf-design/src/views/basic-info/bas-station/modules/bas-station-init-dialog.vue
@@ -0,0 +1,239 @@
+<template>
+  <ElDialog
+    :title="dialogTitle"
+    :model-value="visible"
+    width="1080px"
+    align-center
+    destroy-on-close
+    @update:model-value="handleCancel"
+    @closed="handleClosed"
+  >
+    <ArtForm
+      ref="formRef"
+      v-model="form"
+      :items="formItems"
+      :rules="rules"
+      :span="12"
+      :gutter="20"
+      label-width="110px"
+      :show-reset="false"
+      :show-submit="false"
+    />
+
+    <div class="mt-4">
+      <div class="mb-3 flex items-center justify-between">
+        <div class="text-sm font-medium text-[var(--art-gray-900)]">绔欑偣琛屽垪琛�</div>
+        <ElButton size="small" @click="addRow">鏂板涓�琛�</ElButton>
+      </div>
+      <ElTable :data="form.rows" border>
+        <ElTableColumn label="绔欑偣缂栫爜" min-width="240">
+          <template #default="{ row }">
+            <ElInput v-model="row.stationName" clearable placeholder="璇疯緭鍏ョ珯鐐圭紪鐮�" />
+          </template>
+        </ElTableColumn>
+        <ElTableColumn label="绔欑偣鍚嶇О" min-width="240">
+          <template #default="{ row }">
+            <ElInput v-model="row.stationId" clearable placeholder="璇疯緭鍏ョ珯鐐瑰悕绉�" />
+          </template>
+        </ElTableColumn>
+        <ElTableColumn label="鎿嶄綔" width="100" align="center">
+          <template #default="{ $index }">
+            <ElButton
+              link
+              type="danger"
+              :disabled="form.rows.length <= 1"
+              @click="removeRow($index)"
+            >
+              鍒犻櫎
+            </ElButton>
+          </template>
+        </ElTableColumn>
+      </ElTable>
+      <div class="mt-2 text-xs text-[var(--art-gray-500)]">
+        姣忚琛ㄧず涓�缁勭珯鐐圭紪鐮佸拰绔欑偣鍚嶇О锛屾彁浜ゆ椂浼氭寜涓婃柟鍏叡閰嶇疆鎵归噺鍒濆鍖栥��
+      </div>
+    </div>
+
+    <template #footer>
+      <span class="dialog-footer">
+        <ElButton @click="handleCancel">鍙栨秷</ElButton>
+        <ElButton type="primary" @click="handleSubmit">纭畾</ElButton>
+      </span>
+    </template>
+  </ElDialog>
+</template>
+
+<script setup>
+  import { computed, nextTick, reactive, ref, watch } from 'vue'
+  import { ElMessage } from 'element-plus'
+  import ArtForm from '@/components/core/forms/art-form/index.vue'
+  import {
+    buildBasStationInitModel,
+    createBasStationInitRow,
+    createBasStationInitState,
+    getBasStationBooleanOptions,
+    getBasStationTypeOptions
+  } from '../basStationPage.helpers'
+
+  const props = defineProps({
+    visible: { type: Boolean, default: false },
+    initialData: { type: Object, default: () => ({}) },
+    areaOptions: { type: Array, default: () => [] },
+    containerTypeOptions: { type: Array, default: () => [] },
+    useStatusOptions: { type: Array, default: () => [] }
+  })
+
+  const emit = defineEmits(['update:visible', 'submit'])
+  const formRef = ref()
+  const form = reactive(createBasStationInitState())
+
+  const dialogTitle = computed(() => '绔欑偣鍒濆鍖�')
+
+  const rules = computed(() => ({
+    type: [{ required: true, message: '璇烽�夋嫨绔欑偣绫诲瀷', trigger: 'change' }],
+    areaIds: [{ type: 'array', required: true, message: '璇烽�夋嫨鍙法鍖哄簱鍖�', trigger: 'change' }],
+    containerTypes: [
+      { type: 'array', required: true, message: '璇烽�夋嫨鍙叆瀹瑰櫒绫诲瀷', trigger: 'change' }
+    ]
+  }))
+
+  const formItems = computed(() => [
+    {
+      label: '绔欑偣绫诲瀷',
+      key: 'type',
+      type: 'select',
+      props: {
+        placeholder: '璇烽�夋嫨绔欑偣绫诲瀷',
+        clearable: true,
+        options: getBasStationTypeOptions()
+      }
+    },
+    {
+      label: '浣跨敤鐘舵��',
+      key: 'useStatus',
+      type: 'select',
+      props: {
+        placeholder: '璇烽�夋嫨浣跨敤鐘舵��',
+        clearable: true,
+        filterable: true,
+        options: props.useStatusOptions || []
+      }
+    },
+    {
+      label: '鍙法鍖哄簱鍖�',
+      key: 'areaIds',
+      type: 'select',
+      span: 24,
+      props: {
+        placeholder: '璇烽�夋嫨鍙法鍖哄簱鍖�',
+        clearable: true,
+        multiple: true,
+        collapseTags: true,
+        filterable: true,
+        options: props.areaOptions || []
+      }
+    },
+    {
+      label: '鍙叆瀹瑰櫒绫诲瀷',
+      key: 'containerTypes',
+      type: 'select',
+      span: 24,
+      props: {
+        placeholder: '璇烽�夋嫨鍙叆瀹瑰櫒绫诲瀷',
+        clearable: true,
+        multiple: true,
+        collapseTags: true,
+        filterable: true,
+        options: props.containerTypeOptions || []
+      }
+    },
+    {
+      label: '鍙叆',
+      key: 'inAble',
+      type: 'select',
+      props: {
+        placeholder: '璇烽�夋嫨鍙叆',
+        clearable: true,
+        options: getBasStationBooleanOptions()
+      }
+    },
+    {
+      label: '鍙嚭',
+      key: 'outAble',
+      type: 'select',
+      props: {
+        placeholder: '璇烽�夋嫨鍙嚭',
+        clearable: true,
+        options: getBasStationBooleanOptions()
+      }
+    }
+  ])
+
+  function loadFormData() {
+    Object.assign(form, buildBasStationInitModel(props.initialData))
+  }
+
+  function resetForm() {
+    Object.assign(form, createBasStationInitState())
+    form.rows = [createBasStationInitRow()]
+    formRef.value?.clearValidate?.()
+  }
+
+  function addRow() {
+    form.rows.push(createBasStationInitRow())
+  }
+
+  function removeRow(index) {
+    if (form.rows.length <= 1) {
+      return
+    }
+    form.rows.splice(index, 1)
+  }
+
+  async function handleSubmit() {
+    if (!formRef.value) return
+    try {
+      await formRef.value.validate()
+      const validRows = (Array.isArray(form.rows) ? form.rows : []).filter(
+        (item) => String(item?.stationName || '').trim() && String(item?.stationId || '').trim()
+      )
+      if (!validRows.length) {
+        ElMessage.error('璇疯嚦灏戝~鍐欎竴缁勭珯鐐圭紪鐮佸拰绔欑偣鍚嶇О')
+        return
+      }
+      emit('submit', { ...form, rows: validRows })
+    } catch {
+      return
+    }
+  }
+
+  function handleCancel() {
+    emit('update:visible', false)
+  }
+
+  function handleClosed() {
+    resetForm()
+  }
+
+  watch(
+    () => props.visible,
+    async (visible) => {
+      if (visible) {
+        loadFormData()
+        await nextTick()
+        formRef.value?.clearValidate?.()
+      }
+    },
+    { immediate: true }
+  )
+
+  watch(
+    () => props.initialData,
+    () => {
+      if (props.visible) {
+        loadFormData()
+      }
+    },
+    { deep: true }
+  )
+</script>
diff --git a/rsf-design/src/views/basic-info/bas-station/modules/bas-station-tag-cell.vue b/rsf-design/src/views/basic-info/bas-station/modules/bas-station-tag-cell.vue
new file mode 100644
index 0000000..6efb4ac
--- /dev/null
+++ b/rsf-design/src/views/basic-info/bas-station/modules/bas-station-tag-cell.vue
@@ -0,0 +1,71 @@
+<template>
+  <div v-if="displayItems.length" class="tag-cell" @click="dialogVisible = true">
+    <ElTag v-for="item in previewItems" :key="item.key" effect="plain" size="small">
+      {{ item.label }}
+    </ElTag>
+    <ElTag v-if="hiddenCount > 0" effect="plain" size="small"> +{{ hiddenCount }} </ElTag>
+  </div>
+  <span v-else>{{ emptyText }}</span>
+
+  <ElDialog v-model="dialogVisible" :title="title" width="720px" append-to-body destroy-on-close>
+    <div v-if="displayItems.length" class="tag-cell__dialog-list">
+      <ElTag v-for="item in displayItems" :key="item.key" effect="plain">
+        {{ item.label }}
+      </ElTag>
+    </div>
+    <ElEmpty v-else :description="emptyText" :image-size="88" />
+  </ElDialog>
+</template>
+
+<script setup>
+  import { computed, ref } from 'vue'
+
+  const props = defineProps({
+    items: { type: Array, default: () => [] },
+    title: { type: String, default: '' },
+    emptyText: { type: String, default: '--' },
+    previewCount: { type: Number, default: 1 }
+  })
+
+  const dialogVisible = ref(false)
+
+  const displayItems = computed(() =>
+    (Array.isArray(props.items) ? props.items : [])
+      .map((item, index) => {
+        if (!item || typeof item !== 'object') {
+          return null
+        }
+        const rawLabel = item.label ?? item.name ?? item.text ?? item.value ?? item.id
+        const label = String(rawLabel ?? '').trim()
+        if (!label) {
+          return null
+        }
+        return {
+          key: item.key ?? item.id ?? item.value ?? `${label}-${index}`,
+          label
+        }
+      })
+      .filter(Boolean)
+  )
+
+  const previewItems = computed(() => displayItems.value.slice(0, props.previewCount))
+  const hiddenCount = computed(() =>
+    Math.max(displayItems.value.length - previewItems.value.length, 0)
+  )
+</script>
+
+<style scoped>
+  .tag-cell {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    gap: 6px;
+    cursor: pointer;
+  }
+
+  .tag-cell__dialog-list {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+  }
+</style>
diff --git a/rsf-design/src/views/orders/asn-order-log/asnOrderLogPage.helpers.js b/rsf-design/src/views/orders/asn-order-log/asnOrderLogPage.helpers.js
index 3c8a7d5..81eed70 100644
--- a/rsf-design/src/views/orders/asn-order-log/asnOrderLogPage.helpers.js
+++ b/rsf-design/src/views/orders/asn-order-log/asnOrderLogPage.helpers.js
@@ -165,6 +165,13 @@
     wkTypeText: normalizeTagText(record['wkType$'] || record.wkTypeText || record.wkType, {}),
     anfme: record.anfme ?? '--',
     qty: record.qty ?? '--',
+    purchaseOrgName: normalizeText(record.purchaseOrgName) || '--',
+    purchaseUserName: normalizeText(record.purchaseUserName) || '--',
+    purchaseDateText:
+      normalizeDateText(record.purchaseDate || record.businessTime || record['purchaseDate$']) ||
+      '--',
+    supplierId: normalizeText(record.supplierId) || '--',
+    supplierName: normalizeText(record.supplierName || record['supplierId$']) || '--',
     logisNo: normalizeText(record.logisNo) || '--',
     arrTime: record.arrTime ?? null,
     arrTimeText: normalizeDateText(record['arrTime$'] || record.arrTime) || '--',
@@ -175,7 +182,10 @@
     ntyStatusText: normalizeTagText(record['ntyStatus$'] || ntyStatusMeta.text, NTY_STATUS_META),
     ntyStatusTagType: ntyStatusMeta.type,
     exceStatus: record.exceStatus ?? '--',
-    exceStatusText: normalizeTagText(record['exceStatus$'] || record.exceStatusText || record.exceStatus, {}),
+    exceStatusText: normalizeTagText(
+      record['exceStatus$'] || record.exceStatusText || record.exceStatus,
+      {}
+    ),
     status: record.status ?? '--',
     statusText: normalizeTagText(record['status$'] || statusMeta.text, STATUS_META),
     statusType: statusMeta.type,
diff --git a/rsf-design/src/views/orders/asn-order-log/asnOrderLogTable.columns.js b/rsf-design/src/views/orders/asn-order-log/asnOrderLogTable.columns.js
index 9ecb695..c4666c7 100644
--- a/rsf-design/src/views/orders/asn-order-log/asnOrderLogTable.columns.js
+++ b/rsf-design/src/views/orders/asn-order-log/asnOrderLogTable.columns.js
@@ -69,6 +69,41 @@
       formatter: (row) => row.qty ?? '--'
     },
     {
+      prop: 'purchaseOrgName',
+      label: '閲囪喘缁勭粐',
+      minWidth: 140,
+      showOverflowTooltip: true,
+      formatter: (row) => row.purchaseOrgName || '--'
+    },
+    {
+      prop: 'purchaseUserName',
+      label: '閲囪喘鍛�',
+      minWidth: 120,
+      showOverflowTooltip: true,
+      formatter: (row) => row.purchaseUserName || '--'
+    },
+    {
+      prop: 'purchaseDateText',
+      label: '閲囪喘鏃ユ湡',
+      minWidth: 160,
+      showOverflowTooltip: true,
+      formatter: (row) => row.purchaseDateText || '--'
+    },
+    {
+      prop: 'supplierId',
+      label: '渚涘簲鍟嗙紪鐮�',
+      minWidth: 140,
+      showOverflowTooltip: true,
+      formatter: (row) => row.supplierId || '--'
+    },
+    {
+      prop: 'supplierName',
+      label: '渚涘簲鍟�',
+      minWidth: 160,
+      showOverflowTooltip: true,
+      formatter: (row) => row.supplierName || '--'
+    },
+    {
       prop: 'logisNo',
       label: $t('pages.orders.asnOrderLog.table.logisNo'),
       minWidth: 160,
@@ -125,6 +160,20 @@
       formatter: (row) => row.updateTimeText || '--'
     },
     {
+      prop: 'createByText',
+      label: $t('table.createBy'),
+      minWidth: 120,
+      showOverflowTooltip: true,
+      formatter: (row) => row.createByText || '--'
+    },
+    {
+      prop: 'createTimeText',
+      label: $t('table.createTime'),
+      minWidth: 170,
+      showOverflowTooltip: true,
+      formatter: (row) => row.createTimeText || '--'
+    },
+    {
       prop: 'memo',
       label: $t('table.memo'),
       minWidth: 180,
diff --git a/rsf-design/src/views/orders/asn-order-log/index.vue b/rsf-design/src/views/orders/asn-order-log/index.vue
index d1fc896..db2a6be 100644
--- a/rsf-design/src/views/orders/asn-order-log/index.vue
+++ b/rsf-design/src/views/orders/asn-order-log/index.vue
@@ -44,20 +44,14 @@
     <AsnOrderLogDetailDrawer
       v-model:visible="detailDrawerVisible"
       :loading="detailLoading"
-      :items-loading="detailItemsLoading"
       :detail="detailData"
-      :item-rows="detailItemRows"
-      :item-columns="detailItemColumns"
-      :pagination="detailPagination"
-      @refresh="loadDetailResources"
-      @size-change="handleDetailSizeChange"
-      @current-change="handleDetailCurrentChange"
+      :log-id="activeLogId"
     />
   </div>
 </template>
 
 <script setup>
-  import { computed, onMounted, reactive, ref } from 'vue'
+  import { computed, onMounted, ref } from 'vue'
   import { ElMessage } from 'element-plus'
   import { useUserStore } from '@/store/modules/user'
   import { useTableColumns } from '@/hooks/core/useTableColumns'
@@ -68,7 +62,6 @@
   import { fetchDictDataPage } from '@/api/system-manage'
   import {
     DEFAULT_ASN_ORDER_LOG_PAGE_SIZE,
-    buildAsnOrderLogDetailQueryParams,
     buildAsnOrderLogPageQueryParams,
     buildAsnOrderLogPrintRows,
     buildAsnOrderLogReportMeta,
@@ -77,24 +70,19 @@
     getAsnOrderLogNtyStatusOptions,
     getAsnOrderLogRleStatusOptions,
     getAsnOrderLogStatusOptions,
-    normalizeAsnOrderItemLogRow,
     normalizeAsnOrderLogRow,
     resolveDictOptions,
     ASN_ORDER_LOG_REPORT_STYLE,
     ASN_ORDER_LOG_REPORT_TITLE
   } from './asnOrderLogPage.helpers'
   import {
-    fetchAsnOrderItemLogPage,
     fetchAsnOrderLogPage,
     fetchExportAsnOrderLogReport,
     fetchGetAsnOrderLogDetail,
     fetchGetAsnOrderLogMany
   } from '@/api/asn-order-log'
   import AsnOrderLogDetailDrawer from './modules/asn-order-log-detail-drawer.vue'
-  import {
-    createAsnOrderItemLogColumns,
-    createAsnOrderLogTableColumns
-  } from './asnOrderLogTable.columns'
+  import { createAsnOrderLogTableColumns } from './asnOrderLogTable.columns'
 
   defineOptions({ name: 'AsnOrderLog' })
 
@@ -106,24 +94,15 @@
   const loading = ref(false)
   const detailDrawerVisible = ref(false)
   const detailLoading = ref(false)
-  const detailItemsLoading = ref(false)
   const detailData = ref({})
-  const detailItemRows = ref([])
   const activeLogId = ref(null)
   const typeOptions = ref([])
   const wkTypeOptions = ref([])
   const exceStatusOptions = ref([])
-  const detailItemColumns = createAsnOrderItemLogColumns()
   const pageSize = ref(DEFAULT_ASN_ORDER_LOG_PAGE_SIZE)
   const cursorHistory = ref([null])
   const nextCursor = ref(null)
   const hasNext = ref(false)
-
-  const detailPagination = reactive({
-    current: 1,
-    size: 20,
-    total: 0
-  })
 
   const reportQueryParams = computed(() => buildAsnOrderLogSearchParams(searchForm.value))
   const mainPaginationOptions = {
@@ -132,7 +111,8 @@
   }
   const pagination = computed(() => {
     const current = Math.max(1, cursorHistory.value.length || 1)
-    const size = Number(pageSize.value) > 0 ? Number(pageSize.value) : DEFAULT_ASN_ORDER_LOG_PAGE_SIZE
+    const size =
+      Number(pageSize.value) > 0 ? Number(pageSize.value) : DEFAULT_ASN_ORDER_LOG_PAGE_SIZE
     const recordCount = Math.max(0, Number(data.value.length) || 0)
     const total = hasNext.value ? current * size + 1 : (current - 1) * size + recordCount
 
@@ -266,8 +246,7 @@
   async function loadMainList({ history = cursorHistory.value } = {}) {
     loading.value = true
     try {
-      const normalizedHistory =
-        Array.isArray(history) && history.length > 0 ? [...history] : [null]
+      const normalizedHistory = Array.isArray(history) && history.length > 0 ? [...history] : [null]
       const response = await guardRequestWithMessage(
         fetchAsnOrderLogPage(
           buildAsnOrderLogPageQueryParams({
@@ -303,11 +282,11 @@
     }
   }
 
-  function openDetail(row) {
+  async function openDetail(row) {
     activeLogId.value = row.id
-    detailPagination.current = 1
+    detailData.value = normalizeAsnOrderLogRow(row || {})
     detailDrawerVisible.value = true
-    loadDetailResources()
+    await loadDetailResources()
   }
 
   async function loadDetailResources() {
@@ -316,47 +295,22 @@
     }
 
     detailLoading.value = true
-    detailItemsLoading.value = true
     try {
-      const [detailResponse, itemResponse] = await Promise.all([
-        guardRequestWithMessage(
-          fetchGetAsnOrderLogDetail(activeLogId.value),
-          {},
-          {
-            timeoutMessage: '鍘嗗彶閫氱煡鍗曡鎯呭姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�'
-          }
-        ),
-        guardRequestWithMessage(
-          fetchAsnOrderItemLogPage(
-            buildAsnOrderLogDetailQueryParams({
-              logId: activeLogId.value,
-              current: detailPagination.current,
-              pageSize: detailPagination.size
-            })
-          ),
-          { records: [], total: 0, current: detailPagination.current, size: detailPagination.size },
-          {
-            timeoutMessage: '鍘嗗彶閫氱煡鍗曟槑缁嗗姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�'
-          }
-        )
-      ])
+      const detailResponse = await guardRequestWithMessage(
+        fetchGetAsnOrderLogDetail(activeLogId.value),
+        {},
+        {
+          timeoutMessage: '鍘嗗彶閫氱煡鍗曡鎯呭姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�'
+        }
+      )
 
       detailData.value = normalizeAsnOrderLogRow(detailResponse || {})
-      const itemResult = defaultResponseAdapter(itemResponse)
-      detailItemRows.value = Array.isArray(itemResult.records)
-        ? itemResult.records.map((item) => normalizeAsnOrderItemLogRow(item))
-        : []
-      detailPagination.total = Number(itemResult?.total || 0)
-      detailPagination.current = Number(itemResult?.current || detailPagination.current || 1)
-      detailPagination.size = Number(itemResult?.size || detailPagination.size || 20)
     } catch (error) {
       detailDrawerVisible.value = false
       detailData.value = {}
-      detailItemRows.value = []
       ElMessage.error(error?.message || '鑾峰彇鍘嗗彶閫氱煡鍗曡鎯呭け璐�')
     } finally {
       detailLoading.value = false
-      detailItemsLoading.value = false
     }
   }
 
@@ -416,17 +370,6 @@
     await loadMainList()
   }
 
-  function handleDetailSizeChange(size) {
-    detailPagination.size = size
-    detailPagination.current = 1
-    loadDetailResources()
-  }
-
-  function handleDetailCurrentChange(current) {
-    detailPagination.current = current
-    loadDetailResources()
-  }
-
   function buildPreviewDialogMeta(rows) {
     const now = new Date()
     return {
@@ -461,7 +404,11 @@
 
       records.push(...pageRecords)
 
-      if (!response?.hasNext || response?.nextCursor === null || response?.nextCursor === undefined) {
+      if (
+        !response?.hasNext ||
+        response?.nextCursor === null ||
+        response?.nextCursor === undefined
+      ) {
         break
       }
       cursor = response.nextCursor
diff --git a/rsf-design/src/views/orders/asn-order-log/modules/asn-order-item-log-panel.vue b/rsf-design/src/views/orders/asn-order-log/modules/asn-order-item-log-panel.vue
new file mode 100644
index 0000000..b144931
--- /dev/null
+++ b/rsf-design/src/views/orders/asn-order-log/modules/asn-order-item-log-panel.vue
@@ -0,0 +1,447 @@
+<template>
+  <div class="flex min-h-0 flex-1 flex-col gap-3">
+    <ArtSearchBar
+      v-model="searchForm"
+      :items="searchItems"
+      :showExpand="true"
+      @search="handleSearch"
+      @reset="handleReset"
+    />
+
+    <ElCard class="art-table-card min-h-0 flex-1">
+      <ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
+        <template #left>
+          <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="resolvedPreviewMeta"
+            :total="pagination.total"
+            :disabled="loading"
+            @export="handleExport"
+            @print="handlePrint"
+          />
+        </template>
+      </ArtTableHeader>
+
+      <ArtTable
+        :loading="loading"
+        :data="data"
+        :columns="columns"
+        :pagination="pagination"
+        @pagination:size-change="handleSizeChange"
+        @pagination:current-change="handleCurrentChange"
+      />
+    </ElCard>
+  </div>
+</template>
+
+<script setup>
+  import { computed, ref, watch } from 'vue'
+  import { useI18n } from 'vue-i18n'
+  import { useUserStore } from '@/store/modules/user'
+  import { useTable } from '@/hooks/core/useTable'
+  import { defaultResponseAdapter } from '@/utils/table/tableUtils'
+  import { usePrintExportPage } from '@/views/system/common/usePrintExportPage'
+  import ListExportPrint from '@/components/biz/list-export-print/index.vue'
+  import {
+    fetchAsnOrderItemLogPage,
+    fetchExportAsnOrderItemLogReport,
+    fetchGetAsnOrderItemLogMany
+  } from '@/api/asn-order-log'
+  import { createAsnOrderItemLogColumns } from '../asnOrderLogTable.columns'
+  import { normalizeAsnOrderItemLogRow } from '../asnOrderLogPage.helpers'
+  import {
+    ASN_ORDER_ITEM_LOG_REPORT_STYLE,
+    ASN_ORDER_ITEM_LOG_REPORT_TITLE,
+    buildAsnOrderItemLogPageQueryParams,
+    buildAsnOrderItemLogPrintRows,
+    buildAsnOrderItemLogReportMeta,
+    buildAsnOrderItemLogSearchParams,
+    createAsnOrderItemLogSearchState,
+    getAsnOrderItemLogPaginationKey,
+    getAsnOrderItemLogReportColumns
+  } from '../../asn-order-item-log/asnOrderItemLogPage.helpers'
+
+  defineOptions({ name: 'AsnOrderItemLogPanel' })
+
+  const props = defineProps({
+    logId: {
+      type: [Number, String],
+      default: ''
+    }
+  })
+
+  const { t } = useI18n()
+  const userStore = useUserStore()
+  const selectedRows = ref([])
+  const searchForm = ref(createSeededSearchState())
+  const reportTitle = ASN_ORDER_ITEM_LOG_REPORT_TITLE
+  const reportColumns = getAsnOrderItemLogReportColumns()
+
+  const searchItems = computed(() => [
+    {
+      label: t('pages.orders.asnOrderItemLog.search.condition'),
+      key: 'condition',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.search.conditionPlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.asnCode'),
+      key: 'orderCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.search.asnCodePlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.platItemId'),
+      key: 'platItemId',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.table.platItemId')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.poDetlId'),
+      key: 'poDetlId',
+      type: 'inputNumber',
+      props: {
+        clearable: true,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.asnOrderItemLog.table.poDetlId')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.poCode'),
+      key: 'poCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.search.poCodePlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.fieldsIndex'),
+      key: 'fieldsIndex',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.table.fieldsIndex')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.matnrCode'),
+      key: 'matnrCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.search.matnrCodePlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.maktx'),
+      key: 'maktx',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.search.maktxPlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.anfme'),
+      key: 'anfme',
+      type: 'inputNumber',
+      props: {
+        clearable: true,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.asnOrderItemLog.table.anfme')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.stockUnit'),
+      key: 'stockUnit',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.table.stockUnit')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.purQty'),
+      key: 'purQty',
+      type: 'inputNumber',
+      props: {
+        clearable: true,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.asnOrderItemLog.table.purQty')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.purUnit'),
+      key: 'purUnit',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.table.purUnit')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.qty'),
+      key: 'qty',
+      type: 'inputNumber',
+      props: {
+        clearable: true,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.asnOrderItemLog.table.qty')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.splrCode'),
+      key: 'splrCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.table.splrCode')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.splrBatch'),
+      key: 'splrBatch',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.search.splrBatchPlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.splrName'),
+      key: 'splrName',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.table.splrName')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.qrcode'),
+      key: 'qrcode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.table.qrcode')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.trackCode'),
+      key: 'trackCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.table.trackCode')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.barcode'),
+      key: 'barcode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.table.barcode')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.packName'),
+      key: 'packName',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItemLog.table.packName')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItemLog.table.ntyStatus'),
+      key: 'ntyStatus',
+      type: 'select',
+      props: {
+        clearable: true,
+        options: [
+          { label: t('pages.orders.asnOrderItemLog.status.notReported'), value: 0 },
+          { label: t('pages.orders.asnOrderItemLog.status.reported'), value: 1 },
+          { label: t('pages.orders.asnOrderItemLog.status.partialReported'), value: 2 }
+        ]
+      }
+    },
+    {
+      label: t('table.memo'),
+      key: 'memo',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('table.memo')
+      }
+    },
+    {
+      label: t('table.status'),
+      key: 'status',
+      type: 'select',
+      props: {
+        clearable: true,
+        options: [
+          { label: t('common.status.normal'), value: 1 },
+          { label: t('common.status.frozen'), value: 0 }
+        ]
+      }
+    }
+  ])
+
+  const reportQueryParams = computed(() =>
+    buildAsnOrderItemLogSearchParams({
+      ...searchForm.value,
+      logId: normalizeLogId(props.logId)
+    })
+  )
+
+  const {
+    columns,
+    columnChecks,
+    data,
+    loading,
+    pagination,
+    getData,
+    replaceSearchParams,
+    handleSizeChange,
+    handleCurrentChange,
+    refreshData
+  } = useTable({
+    core: {
+      apiFn: fetchAsnOrderItemLogPage,
+      apiParams: buildAsnOrderItemLogPageQueryParams(searchForm.value),
+      paginationKey: getAsnOrderItemLogPaginationKey(),
+      immediate: false,
+      columnsFactory: () => createAsnOrderItemLogColumns()
+    },
+    transform: {
+      dataTransformer: (records) =>
+        Array.isArray(records) ? records.map((item) => normalizeAsnOrderItemLogRow(item)) : []
+    }
+  })
+
+  watch(
+    () => props.logId,
+    (value) => {
+      if (normalizeLogId(value) === '') {
+        return
+      }
+      searchForm.value = createSeededSearchState()
+      replaceSearchParams(buildAsnOrderItemLogPageQueryParams(searchForm.value))
+      getData()
+    },
+    { immediate: true }
+  )
+
+  function normalizeLogId(value) {
+    if (value === '' || value === null || value === undefined) {
+      return ''
+    }
+    const parsed = Number(value)
+    return Number.isFinite(parsed) ? parsed : ''
+  }
+
+  function createSeededSearchState() {
+    return createAsnOrderItemLogSearchState({
+      logId: normalizeLogId(props.logId)
+    })
+  }
+
+  function buildQueryParams(params = {}) {
+    return buildAsnOrderItemLogPageQueryParams({
+      ...params,
+      logId: normalizeLogId(props.logId)
+    })
+  }
+
+  function handleSearch(params) {
+    searchForm.value = {
+      ...searchForm.value,
+      ...params,
+      logId: normalizeLogId(props.logId)
+    }
+    replaceSearchParams(buildQueryParams(searchForm.value))
+    getData()
+  }
+
+  function handleReset() {
+    searchForm.value = createSeededSearchState()
+    replaceSearchParams(buildQueryParams(searchForm.value))
+    getData()
+  }
+
+  const resolvePrintRecords = async (payload) => {
+    if (Array.isArray(payload?.ids) && payload.ids.length > 0) {
+      return defaultResponseAdapter(await fetchGetAsnOrderItemLogMany(payload.ids)).records
+    }
+
+    return defaultResponseAdapter(
+      await fetchAsnOrderItemLogPage({
+        ...reportQueryParams.value,
+        logId: normalizeLogId(props.logId),
+        current: 1,
+        pageSize: Number(pagination.total) > 0 ? Number(pagination.total) : 20
+      })
+    ).records
+  }
+
+  const {
+    previewVisible,
+    previewRows,
+    previewMeta,
+    handlePreviewVisibleChange,
+    handleExport,
+    handlePrint
+  } = usePrintExportPage({
+    downloadFileName: 'asn-order-item-log.xlsx',
+    requestExport: () =>
+      fetchExportAsnOrderItemLogReport(
+        {
+          logId: normalizeLogId(props.logId)
+        },
+        {
+          headers: {
+            Authorization: userStore.accessToken || ''
+          }
+        }
+      ),
+    resolvePrintRecords,
+    buildPreviewRows: (records) => buildAsnOrderItemLogPrintRows(records),
+    buildPreviewMeta: (rows) => ({
+      reportTitle,
+      reportDate: new Date().toLocaleDateString(),
+      printedAt: new Date().toLocaleString([], { hour12: false }),
+      operator: userStore.getUserInfo?.name || userStore.getUserInfo?.username || '',
+      count: rows.length,
+      reportStyle: {
+        ...ASN_ORDER_ITEM_LOG_REPORT_STYLE
+      }
+    })
+  })
+
+  const resolvedPreviewMeta = computed(() =>
+    buildAsnOrderItemLogReportMeta({
+      previewMeta: previewMeta.value,
+      count: previewRows.value.length,
+      orientation:
+        previewMeta.value?.reportStyle?.orientation || ASN_ORDER_ITEM_LOG_REPORT_STYLE.orientation
+    })
+  )
+</script>
diff --git a/rsf-design/src/views/orders/asn-order-log/modules/asn-order-log-detail-drawer.vue b/rsf-design/src/views/orders/asn-order-log/modules/asn-order-log-detail-drawer.vue
index 529bd8b..03792cb 100644
--- a/rsf-design/src/views/orders/asn-order-log/modules/asn-order-log-detail-drawer.vue
+++ b/rsf-design/src/views/orders/asn-order-log/modules/asn-order-log-detail-drawer.vue
@@ -14,57 +14,73 @@
           <ElDescriptionsItem label="PO鍗旾D">{{ detail.poId ?? '--' }}</ElDescriptionsItem>
           <ElDescriptionsItem label="鍗曟嵁绫诲瀷">{{ detail.typeText || '--' }}</ElDescriptionsItem>
           <ElDescriptionsItem label="涓氬姟绫诲瀷">{{ detail.wkTypeText || '--' }}</ElDescriptionsItem>
+          <ElDescriptionsItem label="閲囪喘缁勭粐">{{
+            detail.purchaseOrgName || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem label="閲囪喘鍛�">{{
+            detail.purchaseUserName || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem label="閲囪喘鏃ユ湡">{{
+            detail.purchaseDateText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem label="渚涘簲鍟嗙紪鐮�">{{
+            detail.supplierId || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem label="渚涘簲鍟�">{{ detail.supplierName || '--' }}</ElDescriptionsItem>
           <ElDescriptionsItem label="閫佽揣鏁伴噺">{{ detail.anfme ?? '--' }}</ElDescriptionsItem>
           <ElDescriptionsItem label="宸叉敹鏁伴噺">{{ detail.qty ?? '--' }}</ElDescriptionsItem>
           <ElDescriptionsItem label="鐗╂祦鍗曞彿">{{ detail.logisNo || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem label="棰勮鍒拌揪鏃堕棿">{{ detail.arrTimeText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem label="閲婃斁鐘舵��">{{ detail.rleStatusText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem label="涓婃姤鐘舵��">{{ detail.ntyStatusText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem label="鎵ц鐘舵��">{{ detail.exceStatusText || '--' }}</ElDescriptionsItem>
+          <ElDescriptionsItem label="棰勮鍒拌揪鏃堕棿">{{
+            detail.arrTimeText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem label="閲婃斁鐘舵��">{{
+            detail.rleStatusText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem label="涓婃姤鐘舵��">{{
+            detail.ntyStatusText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem label="鎵ц鐘舵��">{{
+            detail.exceStatusText || '--'
+          }}</ElDescriptionsItem>
           <ElDescriptionsItem label="鐘舵��">
-            <ElTag :type="detail.statusType || 'info'" effect="light">{{ detail.statusText || '--' }}</ElTag>
+            <ElTag :type="detail.statusType || 'info'" effect="light">{{
+              detail.statusText || '--'
+            }}</ElTag>
           </ElDescriptionsItem>
           <ElDescriptionsItem label="鍒涘缓浜�">{{ detail.createByText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem label="鍒涘缓鏃堕棿">{{ detail.createTimeText || '--' }}</ElDescriptionsItem>
+          <ElDescriptionsItem label="鍒涘缓鏃堕棿">{{
+            detail.createTimeText || '--'
+          }}</ElDescriptionsItem>
           <ElDescriptionsItem label="鏇存柊浜�">{{ detail.updateByText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem label="鏇存柊鏃堕棿">{{ detail.updateTimeText || '--' }}</ElDescriptionsItem>
+          <ElDescriptionsItem label="鏇存柊鏃堕棿">{{
+            detail.updateTimeText || '--'
+          }}</ElDescriptionsItem>
           <ElDescriptionsItem label="澶囨敞" :span="4">{{ detail.memo || '--' }}</ElDescriptionsItem>
         </ElDescriptions>
 
         <div class="flex items-center justify-between">
           <div class="text-sm text-[var(--art-gray-600)]">鍘嗗彶鏄庣粏</div>
-          <ElButton :loading="loading || itemsLoading" @click="$emit('refresh')">鍒锋柊</ElButton>
         </div>
 
-        <ElCard shadow="never" class="border border-[var(--art-border-color)]">
-          <ArtTable
-            :loading="itemsLoading"
-            :data="itemRows"
-            :columns="itemColumns"
-            :pagination="pagination"
-            @pagination:size-change="$emit('size-change', $event)"
-            @pagination:current-change="$emit('current-change', $event)"
-          />
-        </ElCard>
+        <AsnOrderItemLogPanel :log-id="logId" />
       </div>
     </ElScrollbar>
   </ElDrawer>
 </template>
 
 <script setup>
+  import AsnOrderItemLogPanel from './asn-order-item-log-panel.vue'
+
   defineOptions({ name: 'AsnOrderLogDetailDrawer' })
 
   defineProps({
     visible: { type: Boolean, default: false },
     loading: { type: Boolean, default: false },
-    itemsLoading: { type: Boolean, default: false },
     detail: { type: Object, default: () => ({}) },
-    itemRows: { type: Array, default: () => [] },
-    itemColumns: { type: Array, default: () => [] },
-    pagination: { type: Object, default: () => ({ current: 1, size: 20, total: 0 }) }
+    logId: { type: [Number, String], default: '' }
   })
 
-  const emit = defineEmits(['update:visible', 'refresh', 'size-change', 'current-change'])
+  const emit = defineEmits(['update:visible'])
 
   function handleVisibleChange(visible) {
     emit('update:visible', visible)
diff --git a/rsf-design/src/views/orders/asn-order/asnOrderPage.helpers.js b/rsf-design/src/views/orders/asn-order/asnOrderPage.helpers.js
index 1e3063d..d878b78 100644
--- a/rsf-design/src/views/orders/asn-order/asnOrderPage.helpers.js
+++ b/rsf-design/src/views/orders/asn-order/asnOrderPage.helpers.js
@@ -15,6 +15,10 @@
   return String(value ?? '').trim()
 }
 
+function normalizeDateValue(value) {
+  return value ? String(value) : ''
+}
+
 function normalizeNumber(value) {
   if (value === '' || value === null || value === undefined) {
     return 0
@@ -60,7 +64,14 @@
     condition: '',
     code: '',
     poCode: '',
+    poId: '',
+    type: '',
     wkType: '',
+    anfme: '',
+    qty: '',
+    logisNo: '',
+    arrTime: '',
+    memo: '',
     exceStatus: '',
     supplierName: '',
     purchaseUserName: ''
@@ -78,10 +89,27 @@
 
 export function buildAsnOrderSearchParams(params = {}) {
   const result = {}
-  ;['condition', 'code', 'poCode', 'wkType', 'supplierName', 'purchaseUserName'].forEach((key) => {
+  ;[
+    'condition',
+    'code',
+    'poCode',
+    'type',
+    'wkType',
+    'logisNo',
+    'arrTime',
+    'memo',
+    'supplierName',
+    'purchaseUserName'
+  ].forEach((key) => {
     const value = normalizeText(params[key])
     if (value) {
       result[key] = value
+    }
+  })
+  ;['poId', 'anfme', 'qty'].forEach((key) => {
+    const value = normalizeText(params[key])
+    if (value) {
+      result[key] = Number(value)
     }
   })
 
@@ -96,6 +124,7 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
+    orderBy: params.orderBy || 'create_time desc',
     ...buildAsnOrderSearchParams(params)
   }
 }
@@ -129,11 +158,13 @@
 
 export function normalizeAsnOrderRow(record = {}, t = $t) {
   const statusConfig = getStatusConfig(record.exceStatus, record['exceStatus$'], t)
+  const exceStatus = Number(record.exceStatus)
   return {
     ...record,
     id: record.id ?? null,
     code: record.code || '-',
     poCode: record.poCode || '-',
+    poId: record.poId ?? '-',
     wkTypeLabel: record['wkType$'] || record.wkType || '-',
     orderTypeLabel: record['type$'] || record.type || '-',
     exceStatusText: statusConfig.label,
@@ -149,8 +180,12 @@
     updateTimeText: record['updateTime$'] || record.updateTime || '-',
     createByText: record['createBy$'] || '-',
     createTimeText: record['createTime$'] || record.createTime || '-',
+    logisNo: record.logisNo || '-',
+    arrTimeText: record['arrTime$'] || record.arrTime || '-',
     memo: record.memo || '-',
-    canComplete: Number(record.exceStatus) === 1
+    canEdit: exceStatus === 0 || exceStatus === 1,
+    canDelete: exceStatus === 0,
+    canComplete: exceStatus === 1
   }
 }
 
@@ -173,6 +208,200 @@
     memo: record.memo || '-',
     prodTimeText: record['prodTime$'] || record.prodTime || '-'
   }
+}
+
+let tempItemSeed = 0
+
+function createTempRowKey(prefix = 'asn-item') {
+  tempItemSeed += 1
+  return `${prefix}-${Date.now()}-${tempItemSeed}`
+}
+
+export function createAsnOrderFormState() {
+  return {
+    id: undefined,
+    version: undefined,
+    code: '',
+    type: '',
+    wkType: '',
+    poCode: '',
+    logisNo: '',
+    arrTime: '',
+    memo: ''
+  }
+}
+
+export function buildAsnOrderDialogModel(record = {}) {
+  return {
+    ...createAsnOrderFormState(),
+    ...record,
+    poCode: normalizeText(record.poCode),
+    logisNo: normalizeText(record.logisNo),
+    arrTime: normalizeDateValue(record.arrTime || record['arrTime$']),
+    memo: normalizeText(record.memo)
+  }
+}
+
+export function createAsnOrderMaterialSearchState() {
+  return {
+    name: '',
+    code: '',
+    groupId: undefined
+  }
+}
+
+export function buildAsnOrderMaterialSearchParams(params = {}) {
+  const result = {}
+  ;['name', 'code'].forEach((key) => {
+    const value = normalizeText(params[key])
+    if (value) {
+      result[key] = value
+    }
+  })
+
+  const groupId = params.groupId
+  if (groupId !== undefined && groupId !== null && groupId !== '') {
+    result.groupId = groupId
+  }
+
+  return result
+}
+
+export function buildAsnOrderEditableItem(record = {}, fieldDefinitions = []) {
+  const dynamicFields = Object.fromEntries(
+    fieldDefinitions.map((item) => [
+      item.fields,
+      record[item.fields] ?? record.extendFields?.[item.fields] ?? ''
+    ])
+  )
+
+  return {
+    ...record,
+    ...dynamicFields,
+    __rowKey: record.__rowKey || (record.id ? `existing-${record.id}` : createTempRowKey()),
+    matnrId: record.matnrId ?? null,
+    matnrCode: record.matnrCode || '-',
+    maktx: record.maktx || record.matnrName || '-',
+    stockUnit: record.stockUnit || record.unit || '-',
+    purUnit: record.purUnit || record.stockUnit || record.unit || '-',
+    platItemId: record.platItemId || '',
+    splrBatch: record.splrBatch || '',
+    splrCode: record.splrCode || '',
+    splrName: record.splrName || record.supplierName || '',
+    memo: record.memo || '',
+    anfme: normalizeNumber(record.anfme),
+    qty: normalizeNumber(record.qty)
+  }
+}
+
+export function createAsnOrderEditableItemFromMaterial(record = {}, fieldDefinitions = []) {
+  const dynamicFields = Object.fromEntries(
+    fieldDefinitions.map((item) => [
+      item.fields,
+      record[item.fields] ?? record.extendFields?.[item.fields] ?? ''
+    ])
+  )
+
+  return {
+    ...record,
+    ...dynamicFields,
+    __rowKey: createTempRowKey('asn-material'),
+    id: undefined,
+    matnrId: record.id ?? record.matnrId ?? null,
+    matnrCode: record.code || record.matnrCode || '',
+    maktx: record.name || record.maktx || '',
+    stockUnit: record.stockUnit || record.unit || '',
+    purUnit: record.purUnit || record.unit || '',
+    platItemId: '',
+    splrBatch: '',
+    splrCode: '',
+    splrName: '',
+    memo: '',
+    anfme: 0,
+    qty: 0
+  }
+}
+
+export function buildAsnOrderSavePayload({
+  formData = {},
+  itemRows = [],
+  fieldDefinitions = []
+} = {}) {
+  const orders = {
+    ...formData,
+    type: normalizeText(formData.type),
+    wkType: normalizeText(formData.wkType),
+    poCode: normalizeText(formData.poCode),
+    logisNo: normalizeText(formData.logisNo),
+    arrTime: normalizeDateValue(formData.arrTime),
+    memo: normalizeText(formData.memo)
+  }
+
+  const items = itemRows.map((row) => {
+    const payload = { ...row }
+    delete payload.__rowKey
+    fieldDefinitions.forEach((item) => {
+      const value = row[item.fields] ?? row.extendFields?.[item.fields]
+      if (value !== undefined && value !== null && value !== '') {
+        payload[item.fields] = value
+      }
+    })
+    return payload
+  })
+
+  return {
+    orders,
+    items
+  }
+}
+
+export function resolveDictOptions(records = [], options = {}) {
+  const { group } = options
+  if (!Array.isArray(records)) {
+    return []
+  }
+
+  return records
+    .filter((item) => {
+      if (!item || typeof item !== 'object') {
+        return false
+      }
+      if (group === undefined) {
+        return true
+      }
+      return normalizeText(item.group) === normalizeText(group)
+    })
+    .map((item) => {
+      const value = item.value ?? item.id ?? item.dictValue
+      if (value === undefined || value === null || value === '') {
+        return null
+      }
+      return {
+        value: normalizeText(value),
+        label: normalizeText(item.label || item.name || item.dictLabel || value)
+      }
+    })
+    .filter(Boolean)
+}
+
+export function normalizeTreeOptions(records = [], level = 0) {
+  if (!Array.isArray(records)) {
+    return []
+  }
+
+  return records
+    .map((item) => {
+      if (!item || typeof item !== 'object') {
+        return null
+      }
+      const children = normalizeTreeOptions(item.children || item.childrens || [], level + 1)
+      return {
+        value: item.id,
+        label: `${' '.repeat(level * 2)}${item.name || item.label || item.code || item.id}`,
+        children
+      }
+    })
+    .filter(Boolean)
 }
 
 export function normalizePurchaseRow(record = {}) {
@@ -243,9 +472,9 @@
   }))
 }
 
-export function getAsnOrderActionList(row = {}) {
+export function getAsnOrderActionList(row = {}, options = {}) {
   const normalizedRow = normalizeAsnOrderRow(row)
-  return [
+  const actionList = [
     {
       key: 'view',
       label: $t('pages.orders.asnOrder.actions.view'),
@@ -269,4 +498,25 @@
       disabled: !normalizedRow.canComplete
     }
   ]
+
+  if (options.canEdit) {
+    actionList.splice(2, 0, {
+      key: 'edit',
+      label: $t('pages.orders.asnOrder.actions.edit'),
+      icon: 'ri:edit-line',
+      disabled: !normalizedRow.canEdit
+    })
+  }
+
+  if (options.canDelete) {
+    actionList.push({
+      key: 'delete',
+      label: $t('pages.orders.asnOrder.actions.delete'),
+      icon: 'ri:delete-bin-line',
+      color: 'var(--el-color-danger)',
+      disabled: !normalizedRow.canDelete
+    })
+  }
+
+  return actionList
 }
diff --git a/rsf-design/src/views/orders/asn-order/asnOrderTable.columns.js b/rsf-design/src/views/orders/asn-order/asnOrderTable.columns.js
index d3fb242..2955a14 100644
--- a/rsf-design/src/views/orders/asn-order/asnOrderTable.columns.js
+++ b/rsf-design/src/views/orders/asn-order/asnOrderTable.columns.js
@@ -5,7 +5,11 @@
 import ArtButtonTable from '@/components/core/forms/art-button-table/index.vue'
 import { getAsnOrderActionList } from './asnOrderPage.helpers'
 
-export function createAsnOrderTableColumns({ handleActionClick }) {
+export function createAsnOrderTableColumns({
+  handleActionClick,
+  canEdit = false,
+  canDelete = false
+}) {
   return [
     {
       type: 'selection',
@@ -29,6 +33,13 @@
       label: $t('pages.orders.asnOrder.search.poCode'),
       minWidth: 170,
       showOverflowTooltip: true
+    },
+    {
+      prop: 'poId',
+      label: $t('pages.orders.asnOrder.search.poId'),
+      width: 110,
+      align: 'right',
+      visible: false
     },
     {
       prop: 'wkTypeLabel',
@@ -61,6 +72,27 @@
       showOverflowTooltip: true
     },
     {
+      prop: 'purchaseOrgName',
+      label: $t('pages.orders.asnOrder.table.purchaseOrgName'),
+      minWidth: 140,
+      showOverflowTooltip: true,
+      visible: false
+    },
+    {
+      prop: 'businessTimeText',
+      label: $t('pages.orders.asnOrder.table.businessTime'),
+      minWidth: 170,
+      showOverflowTooltip: true,
+      visible: false
+    },
+    {
+      prop: 'supplierId',
+      label: $t('pages.orders.asnOrder.table.supplierId'),
+      minWidth: 140,
+      showOverflowTooltip: true,
+      visible: false
+    },
+    {
       prop: 'exceStatusText',
       label: $t('pages.orders.asnOrder.search.exceStatus'),
       width: 120,
@@ -72,20 +104,48 @@
         )
     },
     {
+      prop: 'updateByText',
+      label: $t('table.updateBy'),
+      minWidth: 120,
+      showOverflowTooltip: true,
+      visible: false
+    },
+    {
       prop: 'updateTimeText',
       label: $t('pages.orders.asnOrder.detail.updateTime'),
       minWidth: 170,
       showOverflowTooltip: true
     },
     {
+      prop: 'createByText',
+      label: $t('table.createBy'),
+      minWidth: 120,
+      showOverflowTooltip: true,
+      visible: false
+    },
+    {
+      prop: 'createTimeText',
+      label: $t('pages.orders.asnOrder.detail.createTime'),
+      minWidth: 170,
+      showOverflowTooltip: true,
+      visible: false
+    },
+    {
+      prop: 'memo',
+      label: $t('table.remark'),
+      minWidth: 180,
+      showOverflowTooltip: true,
+      visible: false
+    },
+    {
       prop: 'operation',
       label: $t('table.operation'),
-      width: 120,
+      width: 130,
       align: 'center',
       fixed: 'right',
       formatter: (row) =>
         h(ArtButtonMore, {
-          list: getAsnOrderActionList(row),
+          list: getAsnOrderActionList(row, { canEdit, canDelete }),
           onClick: (item) => handleActionClick(item, row)
         })
     }
diff --git a/rsf-design/src/views/orders/asn-order/index.vue b/rsf-design/src/views/orders/asn-order/index.vue
index 1a23773..275ed97 100644
--- a/rsf-design/src/views/orders/asn-order/index.vue
+++ b/rsf-design/src/views/orders/asn-order/index.vue
@@ -12,8 +12,35 @@
       <ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
         <template #left>
           <ElSpace wrap>
-            <ElButton type="primary" @click="poDialogVisible = true">{{ t('pages.orders.asnOrder.buttons.createByPo') }}</ElButton>
+            <ElButton v-auth="'add'" type="primary" @click="showDialog('add')">
+              {{ t('pages.orders.asnOrder.buttons.create') }}
+            </ElButton>
+            <ElButton v-auth="'add'" @click="poDialogVisible = true">
+              {{ t('pages.orders.asnOrder.buttons.createByPo') }}
+            </ElButton>
+            <ElUpload
+              v-auth="'update'"
+              :auto-upload="false"
+              :show-file-list="false"
+              accept=".xlsx,.xls"
+              @change="handleImportFileChange"
+            >
+              <ElButton :loading="importing">
+                {{ t('pages.orders.asnOrder.buttons.import') }}
+              </ElButton>
+            </ElUpload>
+            <ElButton
+              v-auth="'update'"
+              :loading="templateDownloading"
+              @click="handleDownloadTemplate"
+            >
+              {{ t('pages.orders.asnOrder.buttons.downloadTemplate') }}
+            </ElButton>
+            <ElButton :disabled="selectedRows.length === 0" @click="handleInspectSelected">
+              {{ t('pages.orders.asnOrder.buttons.inspection') }}
+            </ElButton>
             <ListExportPrint
+              class="inline-flex"
               :preview-visible="previewVisible"
               @update:previewVisible="handlePreviewVisibleChange"
               :report-title="reportTitle"
@@ -42,6 +69,16 @@
       />
     </ElCard>
 
+    <AsnOrderDialog
+      v-model:visible="dialogVisible"
+      :dialog-type="dialogType"
+      :order-data="currentOrderData"
+      :type-options="typeOptions"
+      :wk-type-options="wkTypeOptions"
+      :field-definitions="fieldDefinitions"
+      @submit="handleDialogSubmit"
+    />
+
     <AsnOrderDetailDrawer
       v-model:visible="detailDrawerVisible"
       :loading="detailLoading"
@@ -55,17 +92,20 @@
     />
 
     <AsnOrderCreateByPoDialog v-model:visible="poDialogVisible" @success="handlePoCreateSuccess" />
+
+    <AsnOrderItemDialog v-model:visible="itemDialogVisible" :order="currentItemOrder" />
   </div>
 </template>
 
 <script setup>
-  import { computed, reactive, ref } from 'vue'
-  import { useRouter } from 'vue-router'
+  import { computed, onMounted, reactive, ref } from 'vue'
   import { useI18n } from 'vue-i18n'
   import { ElMessage, ElMessageBox } from 'element-plus'
   import { useUserStore } from '@/store/modules/user'
+  import { useAuth } from '@/hooks/core/useAuth'
   import { useTable } from '@/hooks/core/useTable'
   import { usePrintExportPage } from '@/views/system/common/usePrintExportPage'
+  import { fetchDictDataPage } from '@/api/system-manage'
   import ListExportPrint from '@/components/biz/list-export-print/index.vue'
   import { defaultResponseAdapter } from '@/utils/table/tableUtils'
   import { guardRequestWithMessage } from '@/utils/sys/requestGuard'
@@ -73,11 +113,21 @@
     fetchAsnOrderItemPage,
     fetchAsnOrderPage,
     fetchCompleteAsnOrder,
+    fetchDeleteAsnOrder,
+    fetchDownloadAsnOrderTemplate,
+    fetchEnabledAsnOrderFields,
     fetchExportAsnOrderReport,
-    fetchGetAsnOrderMany
+    fetchGetAsnOrderDetail,
+    fetchGetAsnOrderMany,
+    fetchImportAsnOrder,
+    fetchInspectAsnOrder,
+    fetchSaveAsnOrderWithItems,
+    fetchUpdateAsnOrderWithItems
   } from '@/api/asn-order'
+  import AsnOrderDialog from './modules/asn-order-dialog.vue'
   import AsnOrderDetailDrawer from './modules/asn-order-detail-drawer.vue'
   import AsnOrderCreateByPoDialog from './modules/asn-order-create-by-po-dialog.vue'
+  import AsnOrderItemDialog from './modules/asn-order-item-dialog.vue'
   import {
     createAsnOrderDetailItemColumns,
     createAsnOrderTableColumns
@@ -85,22 +135,27 @@
   import {
     ASN_ORDER_REPORT_STYLE,
     buildAsnOrderDetailQueryParams,
+    buildAsnOrderDialogModel,
     buildAsnOrderPageQueryParams,
     buildAsnOrderPrintRows,
     buildAsnOrderReportMeta,
+    buildAsnOrderSavePayload,
     buildAsnOrderSearchParams,
+    createAsnOrderFormState,
     createAsnOrderSearchState,
     getAsnOrderReportTitle,
     getAsnOrderStatusOptions,
     normalizeAsnOrderItemRow,
-    normalizeAsnOrderRow
+    normalizeAsnOrderRow,
+    resolveDictOptions
   } from './asnOrderPage.helpers'
 
   defineOptions({ name: 'AsnOrder' })
 
   const userStore = useUserStore()
-  const router = useRouter()
+  const { hasAuth } = useAuth()
   const { t } = useI18n()
+
   const reportTitle = computed(() => getAsnOrderReportTitle(t))
   const searchForm = ref(createAsnOrderSearchState())
   const selectedRows = ref([])
@@ -111,6 +166,19 @@
   const activeOrderId = ref(null)
   const activeOrderRow = ref(null)
   const poDialogVisible = ref(false)
+  const dialogVisible = ref(false)
+  const itemDialogVisible = ref(false)
+  const dialogType = ref('add')
+  const currentOrderData = ref({
+    order: createAsnOrderFormState(),
+    items: []
+  })
+  const currentItemOrder = ref({})
+  const typeOptions = ref([])
+  const wkTypeOptions = ref([])
+  const fieldDefinitions = ref([])
+  const importing = ref(false)
+  const templateDownloading = ref(false)
 
   const detailPagination = reactive({
     current: 1,
@@ -149,12 +217,81 @@
       }
     },
     {
-      label: t('pages.orders.asnOrder.search.wkType'),
-      key: 'wkType',
+      label: t('pages.orders.asnOrder.search.poId'),
+      key: 'poId',
       type: 'input',
       props: {
         clearable: true,
+        placeholder: t('pages.orders.asnOrder.placeholder.poId')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.search.type'),
+      key: 'type',
+      type: 'select',
+      props: {
+        clearable: true,
+        filterable: true,
+        options: typeOptions.value,
+        placeholder: t('pages.orders.asnOrder.placeholder.type')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.search.wkType'),
+      key: 'wkType',
+      type: 'select',
+      props: {
+        clearable: true,
+        filterable: true,
+        options: wkTypeOptions.value,
         placeholder: t('pages.orders.asnOrder.placeholder.wkType')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.search.anfme'),
+      key: 'anfme',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrder.placeholder.anfme')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.search.qty'),
+      key: 'qty',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrder.placeholder.qty')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.search.logisNo'),
+      key: 'logisNo',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrder.placeholder.logisNo')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.search.arrTime'),
+      key: 'arrTime',
+      type: 'date',
+      props: {
+        clearable: true,
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
+        format: 'YYYY-MM-DD HH:mm:ss',
+        placeholder: t('pages.orders.asnOrder.placeholder.arrTime')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.search.memo'),
+      key: 'memo',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrder.placeholder.memo')
       }
     },
     {
@@ -186,6 +323,63 @@
     }
   ])
 
+  function updatePaginationState(target, response, fallbackCurrent, fallbackSize) {
+    target.total = Number(response?.total || 0)
+    target.current = Number(response?.current || fallbackCurrent || 1)
+    target.size = Number(response?.size || fallbackSize || target.size || 20)
+  }
+
+  function createEmptyDialogData() {
+    return {
+      order: buildAsnOrderDialogModel(),
+      items: []
+    }
+  }
+
+  async function fetchAllOrderItems(orderId) {
+    const firstPage = await guardRequestWithMessage(
+      fetchAsnOrderItemPage({
+        orderId,
+        current: 1,
+        pageSize: 200
+      }),
+      {
+        records: [],
+        total: 0,
+        current: 1,
+        size: 200
+      },
+      {
+        timeoutMessage: t('pages.orders.asnOrder.messages.detailTimeout')
+      }
+    )
+
+    const firstRecords = Array.isArray(firstPage?.records) ? firstPage.records : []
+    const total = Number(firstPage?.total || firstRecords.length)
+
+    if (total > firstRecords.length) {
+      const fullPage = await guardRequestWithMessage(
+        fetchAsnOrderItemPage({
+          orderId,
+          current: 1,
+          pageSize: total
+        }),
+        {
+          records: firstRecords,
+          total,
+          current: 1,
+          size: total
+        },
+        {
+          timeoutMessage: t('pages.orders.asnOrder.messages.detailTimeout')
+        }
+      )
+      return Array.isArray(fullPage?.records) ? fullPage.records : firstRecords
+    }
+
+    return firstRecords
+  }
+
   async function openDetail(row) {
     activeOrderId.value = row.id
     activeOrderRow.value = row
@@ -193,6 +387,50 @@
     detailPagination.current = 1
     detailDrawerVisible.value = true
     await loadDetailResources()
+  }
+
+  function showDialog(type, payload = createEmptyDialogData()) {
+    dialogType.value = type
+    currentOrderData.value = payload
+    dialogVisible.value = true
+  }
+
+  async function openEditDialog(row) {
+    try {
+      const [detail, items] = await Promise.all([
+        guardRequestWithMessage(
+          fetchGetAsnOrderDetail(row.id),
+          {},
+          {
+            timeoutMessage: t('pages.orders.asnOrder.messages.detailTimeout')
+          }
+        ),
+        fetchAllOrderItems(row.id)
+      ])
+
+      showDialog('edit', {
+        order: buildAsnOrderDialogModel(detail),
+        items
+      })
+    } catch (error) {
+      ElMessage.error(error?.message || t('pages.orders.asnOrder.messages.detailFailed'))
+    }
+  }
+
+  async function handleDelete(row) {
+    await ElMessageBox.confirm(
+      t('pages.orders.asnOrder.messages.deleteConfirm', { code: row.code || '' }),
+      t('pages.orders.asnOrder.messages.deleteTitle'),
+      {
+        confirmButtonText: t('common.confirm'),
+        cancelButtonText: t('common.cancel'),
+        type: 'warning'
+      }
+    )
+
+    await fetchDeleteAsnOrder(row.id)
+    ElMessage.success(t('pages.orders.asnOrder.messages.deleteSuccess'))
+    await refreshData()
   }
 
   async function handleActionClick(action, row) {
@@ -206,18 +444,24 @@
         return
       }
 
+      if (action.key === 'edit') {
+        await openEditDialog(row)
+        return
+      }
+
       if (action.key === 'print') {
         await handlePrint({ ids: [row.id], pageSize: 1 })
         return
       }
 
       if (action.key === 'items') {
-        router.push({
-          path: '/orders/asn-order-item',
-          query: {
-            orderId: String(row.id)
-          }
-        })
+        currentItemOrder.value = row
+        itemDialogVisible.value = true
+        return
+      }
+
+      if (action.key === 'delete') {
+        await handleDelete(row)
         return
       }
 
@@ -265,7 +509,9 @@
       apiParams: buildAsnOrderPageQueryParams(searchForm.value),
       columnsFactory: () =>
         createAsnOrderTableColumns({
-          handleActionClick
+          handleActionClick,
+          canEdit: hasAuth('update'),
+          canDelete: hasAuth('delete')
         })
     },
     transform: {
@@ -273,12 +519,6 @@
         Array.isArray(records) ? records.map((item) => normalizeAsnOrderRow(item, t)) : []
     }
   })
-
-  function updatePaginationState(target, response, fallbackCurrent, fallbackSize) {
-    target.total = Number(response?.total || 0)
-    target.current = Number(response?.current || fallbackCurrent || 1)
-    target.size = Number(response?.size || fallbackSize || target.size || 20)
-  }
 
   async function loadDetailResources() {
     if (!activeOrderId.value) {
@@ -354,6 +594,157 @@
     await refreshSoft()
   }
 
+  async function handleDialogSubmit(payload) {
+    try {
+      const requestPayload = buildAsnOrderSavePayload({
+        formData: payload.order,
+        itemRows: payload.items,
+        fieldDefinitions: fieldDefinitions.value
+      })
+
+      if (dialogType.value === 'edit') {
+        await fetchUpdateAsnOrderWithItems(requestPayload)
+        ElMessage.success(t('pages.orders.asnOrder.messages.updateSuccess'))
+      } else {
+        await fetchSaveAsnOrderWithItems(requestPayload)
+        ElMessage.success(t('pages.orders.asnOrder.messages.createSuccess'))
+      }
+
+      dialogVisible.value = false
+      currentOrderData.value = createEmptyDialogData()
+      await refreshData()
+    } catch (error) {
+      ElMessage.error(
+        error?.message ||
+          (dialogType.value === 'edit'
+            ? t('pages.orders.asnOrder.messages.updateFailed')
+            : t('pages.orders.asnOrder.messages.createFailed'))
+      )
+    }
+  }
+
+  async function handleInspectSelected() {
+    if (!selectedRows.value.length) {
+      ElMessage.warning(t('pages.orders.asnOrder.messages.inspectionSelectRequired'))
+      return
+    }
+
+    try {
+      await ElMessageBox.confirm(
+        t('pages.orders.asnOrder.messages.inspectionConfirm', { count: selectedRows.value.length }),
+        t('pages.orders.asnOrder.messages.inspectionTitle'),
+        {
+          confirmButtonText: t('common.confirm'),
+          cancelButtonText: t('common.cancel'),
+          type: 'warning'
+        }
+      )
+      await fetchInspectAsnOrder(selectedRows.value.map((row) => ({ id: row.id })))
+      ElMessage.success(t('pages.orders.asnOrder.messages.inspectionSuccess'))
+      await refreshData()
+    } catch (error) {
+      if (error === 'cancel' || error?.message === 'cancel') {
+        return
+      }
+      ElMessage.error(error?.message || t('pages.orders.asnOrder.messages.inspectionFailed'))
+    }
+  }
+
+  async function handleImportFileChange(uploadFile) {
+    if (!uploadFile?.raw) {
+      return
+    }
+    importing.value = true
+    try {
+      await fetchImportAsnOrder(uploadFile.raw)
+      ElMessage.success(t('pages.orders.asnOrder.messages.importSuccess'))
+      await refreshData()
+    } catch (error) {
+      ElMessage.error(error?.message || t('pages.orders.asnOrder.messages.importFailed'))
+    } finally {
+      importing.value = false
+    }
+  }
+
+  async function downloadFile(response, fallbackName) {
+    const blob = await response.blob()
+    if (!blob || !blob.size) {
+      throw new Error(t('pages.orders.asnOrder.messages.templateDownloadFailed'))
+    }
+    const disposition = response.headers.get('Content-Disposition') || ''
+    const matchedName =
+      disposition.match(/filename\*=UTF-8''([^;]+)/i)?.[1] ||
+      disposition.match(/filename="?([^";]+)"?/i)?.[1]
+    const fileName = matchedName ? decodeURIComponent(matchedName) : fallbackName
+    const url = URL.createObjectURL(blob)
+    const anchor = document.createElement('a')
+    anchor.href = url
+    anchor.download = fileName
+    document.body.appendChild(anchor)
+    anchor.click()
+    anchor.remove()
+    URL.revokeObjectURL(url)
+  }
+
+  async function handleDownloadTemplate() {
+    templateDownloading.value = true
+    try {
+      const response = await fetchDownloadAsnOrderTemplate(
+        {},
+        {
+          headers: {
+            Authorization: userStore.accessToken || ''
+          }
+        }
+      )
+      await downloadFile(response, 'asn-order-template.xlsx')
+      ElMessage.success(t('pages.orders.asnOrder.messages.templateDownloadSuccess'))
+    } catch (error) {
+      ElMessage.error(error?.message || t('pages.orders.asnOrder.messages.templateDownloadFailed'))
+    } finally {
+      templateDownloading.value = false
+    }
+  }
+
+  async function loadTypeOptions() {
+    const response = await guardRequestWithMessage(
+      fetchDictDataPage({
+        current: 1,
+        pageSize: 200,
+        dictTypeCode: 'sys_order_type',
+        group: '1',
+        status: 1
+      }),
+      { records: [] },
+      { timeoutMessage: t('pages.orders.asnOrder.messages.typeOptionsTimeout') }
+    )
+    typeOptions.value = resolveDictOptions(defaultResponseAdapter(response).records, { group: '1' })
+  }
+
+  async function loadWkTypeOptions() {
+    const response = await guardRequestWithMessage(
+      fetchDictDataPage({
+        current: 1,
+        pageSize: 200,
+        dictTypeCode: 'sys_business_type',
+        group: '1',
+        status: 1
+      }),
+      { records: [] },
+      { timeoutMessage: t('pages.orders.asnOrder.messages.wkTypeOptionsTimeout') }
+    )
+    wkTypeOptions.value = resolveDictOptions(defaultResponseAdapter(response).records, {
+      group: '1'
+    })
+  }
+
+  async function loadFieldDefinitions() {
+    const records = await guardRequestWithMessage(fetchEnabledAsnOrderFields(), [], {
+      timeoutMessage: t('pages.orders.asnOrder.messages.fieldOptionsTimeout')
+    })
+    fieldDefinitions.value = Array.isArray(records) ? records : []
+  }
+
   const {
     previewVisible,
     previewRows,
@@ -404,8 +795,13 @@
     buildAsnOrderReportMeta({
       previewMeta: previewMeta.value,
       count: previewRows.value.length,
-      orientation: previewMeta.value?.reportStyle?.orientation || ASN_ORDER_REPORT_STYLE.orientation,
+      orientation:
+        previewMeta.value?.reportStyle?.orientation || ASN_ORDER_REPORT_STYLE.orientation,
       t
     })
   )
+
+  onMounted(async () => {
+    await Promise.allSettled([loadTypeOptions(), loadWkTypeOptions(), loadFieldDefinitions()])
+  })
 </script>
diff --git a/rsf-design/src/views/orders/asn-order/modules/asn-order-create-by-po-dialog.vue b/rsf-design/src/views/orders/asn-order/modules/asn-order-create-by-po-dialog.vue
index 578c02c..5eb3b79 100644
--- a/rsf-design/src/views/orders/asn-order/modules/asn-order-create-by-po-dialog.vue
+++ b/rsf-design/src/views/orders/asn-order/modules/asn-order-create-by-po-dialog.vue
@@ -20,8 +20,12 @@
         <ElCard class="art-table-card" shadow="never">
           <template #header>
             <div class="flex items-center justify-between gap-3">
-              <div class="text-sm font-medium">{{ t('pages.orders.asnOrder.createByPoDialog.purchaseList') }}</div>
-              <ElButton :loading="purchaseLoading" @click="refreshPurchaseData">{{ t('common.actions.refresh') }}</ElButton>
+              <div class="text-sm font-medium">{{
+                t('pages.orders.asnOrder.createByPoDialog.purchaseList')
+              }}</div>
+              <ElButton :loading="purchaseLoading" @click="refreshPurchaseData">{{
+                t('common.actions.refresh')
+              }}</ElButton>
             </div>
           </template>
 
@@ -39,11 +43,15 @@
           <template #header>
             <div class="flex items-center justify-between gap-3">
               <div class="flex flex-col">
-                <span class="text-sm font-medium">{{ t('pages.orders.asnOrder.createByPoDialog.purchasePreview') }}</span>
+                <span class="text-sm font-medium">{{
+                  t('pages.orders.asnOrder.createByPoDialog.purchasePreview')
+                }}</span>
                 <span class="text-xs text-[var(--art-gray-600)]">
                   {{
                     selectedPurchase.code
-                      ? t('pages.orders.asnOrder.createByPoDialog.purchaseSelected', { code: selectedPurchase.code })
+                      ? t('pages.orders.asnOrder.createByPoDialog.purchaseSelected', {
+                          code: selectedPurchase.code
+                        })
                       : t('pages.orders.asnOrder.createByPoDialog.purchaseEmpty')
                   }}
                 </span>
@@ -68,7 +76,10 @@
         <div class="text-xs text-[var(--art-gray-600)]">
           {{
             selectedPurchase.id
-              ? t('pages.orders.asnOrder.createByPoDialog.purchaseGenerateHint', { code: selectedPurchase.code, count: purchaseItems.length })
+              ? t('pages.orders.asnOrder.createByPoDialog.purchaseGenerateHint', {
+                  code: selectedPurchase.code,
+                  count: purchaseItems.length
+                })
               : t('pages.orders.asnOrder.createByPoDialog.purchaseGenerateEmpty')
           }}
         </div>
@@ -252,7 +263,7 @@
           current: 1,
           size: 200
         },
-          {
+        {
           timeoutMessage: t('pages.orders.asnOrder.createByPoDialog.messages.purchaseItemsTimeout')
         }
       )
@@ -274,7 +285,9 @@
             size: total
           },
           {
-            timeoutMessage: t('pages.orders.asnOrder.createByPoDialog.messages.purchaseItemsAllTimeout')
+            timeoutMessage: t(
+              'pages.orders.asnOrder.createByPoDialog.messages.purchaseItemsAllTimeout'
+            )
           }
         )
         purchaseItems.value = Array.isArray(fullPage?.records)
@@ -333,7 +346,9 @@
       emit('success')
       handleVisibleChange(false)
     } catch (error) {
-      ElMessage.error(error?.message || t('pages.orders.asnOrder.createByPoDialog.messages.createByPoFailed'))
+      ElMessage.error(
+        error?.message || t('pages.orders.asnOrder.createByPoDialog.messages.createByPoFailed')
+      )
     } finally {
       submitLoading.value = false
     }
diff --git a/rsf-design/src/views/orders/asn-order/modules/asn-order-detail-drawer.vue b/rsf-design/src/views/orders/asn-order/modules/asn-order-detail-drawer.vue
index 903e6d1..097d55b 100644
--- a/rsf-design/src/views/orders/asn-order/modules/asn-order-detail-drawer.vue
+++ b/rsf-design/src/views/orders/asn-order/modules/asn-order-detail-drawer.vue
@@ -9,27 +9,59 @@
     <ElScrollbar class="h-[calc(100vh-180px)] pr-1">
       <div class="space-y-4">
         <ElDescriptions :title="t('pages.orders.asnOrder.detail.baseInfo')" :column="4" border>
-          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.asnCode')">{{ detail.code || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.poCode')">{{ detail.poCode || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.wkType')">{{ detail.wkTypeLabel || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.orderType')">{{ detail.orderTypeLabel || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.status')">{{ detail.exceStatusText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.purchaseOrg')">{{ detail.purchaseOrgName || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.purchaseUser')">{{ detail.purchaseUserName || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.supplier')">{{ detail.supplierName || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.anfme')">{{ detail.anfme ?? 0 }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.qty')">{{ detail.qty ?? 0 }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.updateTime')">{{ detail.updateTimeText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.createTime')">{{ detail.createTimeText || '--' }}</ElDescriptionsItem>
-          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.memo')" :span="4">{{ detail.memo || '--' }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.asnCode')">{{
+            detail.code || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.poCode')">{{
+            detail.poCode || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.wkType')">{{
+            detail.wkTypeLabel || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.orderType')">{{
+            detail.orderTypeLabel || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.status')">{{
+            detail.exceStatusText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.purchaseOrg')">{{
+            detail.purchaseOrgName || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.purchaseUser')">{{
+            detail.purchaseUserName || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.supplier')">{{
+            detail.supplierName || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.anfme')">{{
+            detail.anfme ?? 0
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.qty')">{{
+            detail.qty ?? 0
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.updateTime')">{{
+            detail.updateTimeText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.createTime')">{{
+            detail.createTimeText || '--'
+          }}</ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.memo')" :span="4">{{
+            detail.memo || '--'
+          }}</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.asnOrder.detail.items') }}</div>
+            <div class="text-sm font-medium text-[var(--art-gray-900)]">{{
+              t('pages.orders.asnOrder.detail.items')
+            }}</div>
             <div class="flex items-center gap-3">
-              <ElTag effect="plain">{{ t('pages.orders.asnOrder.detail.count', { count: data.length }) }}</ElTag>
-              <ElButton :loading="loading" @click="$emit('refresh')">{{ t('common.actions.refresh') }}</ElButton>
+              <ElTag effect="plain">{{
+                t('pages.orders.asnOrder.detail.count', { count: data.length })
+              }}</ElTag>
+              <ElButton :loading="loading" @click="$emit('refresh')">{{
+                t('common.actions.refresh')
+              }}</ElButton>
             </div>
           </div>
 
diff --git a/rsf-design/src/views/orders/asn-order/modules/asn-order-dialog.vue b/rsf-design/src/views/orders/asn-order/modules/asn-order-dialog.vue
new file mode 100644
index 0000000..4c31363
--- /dev/null
+++ b/rsf-design/src/views/orders/asn-order/modules/asn-order-dialog.vue
@@ -0,0 +1,370 @@
+<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="materialDialogVisible = true">
+            {{ t('pages.orders.asnOrder.dialog.addMaterial') }}
+          </ElButton>
+          <ElButton
+            type="danger"
+            plain
+            :disabled="selectedItemKeys.length === 0"
+            @click="handleBatchRemove"
+          >
+            {{ t('pages.orders.asnOrder.dialog.deleteSelected') }}
+          </ElButton>
+        </ElSpace>
+        <div class="text-xs text-[var(--art-gray-600)]">
+          {{ t('pages.orders.asnOrder.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
+          :label="t('pages.orders.asnOrder.table.expectedQty')"
+          width="140"
+          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.asnOrder.table.poItemId')" min-width="120">
+          <template #default="{ row }">
+            <ElInput v-model="row.platItemId" clearable />
+          </template>
+        </ElTableColumn>
+        <ElTableColumn :label="t('table.supplierBatch')" min-width="140">
+          <template #default="{ row }">
+            <ElInput v-model="row.splrBatch" clearable />
+          </template>
+        </ElTableColumn>
+        <ElTableColumn prop="stockUnit" :label="t('table.unit')" width="100" align="center" />
+        <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" @click="handleRemoveRow(row)">
+              {{ t('pages.orders.asnOrder.actions.delete') }}
+            </ElButton>
+          </template>
+        </ElTableColumn>
+      </ElTable>
+    </div>
+
+    <template #footer>
+      <ElSpace>
+        <ElButton @click="handleVisibleChange(false)">{{ t('common.cancel') }}</ElButton>
+        <ElButton type="primary" @click="handleSubmit">{{ t('common.confirm') }}</ElButton>
+      </ElSpace>
+    </template>
+
+    <AsnOrderMaterialDialog
+      v-model:visible="materialDialogVisible"
+      :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 {
+    buildAsnOrderDialogModel,
+    buildAsnOrderEditableItem,
+    createAsnOrderEditableItemFromMaterial,
+    createAsnOrderFormState
+  } from '../asnOrderPage.helpers'
+  import AsnOrderMaterialDialog from './asn-order-material-dialog.vue'
+
+  const props = defineProps({
+    visible: { type: Boolean, default: false },
+    dialogType: { type: String, default: 'add' },
+    orderData: {
+      type: Object,
+      default: () => ({
+        order: {},
+        items: []
+      })
+    },
+    typeOptions: { type: Array, default: () => [] },
+    wkTypeOptions: { type: Array, default: () => [] },
+    fieldDefinitions: { type: Array, default: () => [] }
+  })
+
+  const emit = defineEmits(['update:visible', 'submit'])
+  const { t } = useI18n()
+
+  const formRef = ref()
+  const form = reactive(createAsnOrderFormState())
+  const itemRows = ref([])
+  const selectedItemKeys = ref([])
+  const materialDialogVisible = ref(false)
+
+  const isEdit = computed(() => props.dialogType === 'edit')
+  const dialogTitle = computed(() =>
+    isEdit.value
+      ? t('pages.orders.asnOrder.dialog.editTitle')
+      : t('pages.orders.asnOrder.dialog.createTitle')
+  )
+  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.asnOrder.dialog.validation.type'),
+        trigger: 'change'
+      }
+    ],
+    wkType: [
+      {
+        required: true,
+        message: t('pages.orders.asnOrder.dialog.validation.wkType'),
+        trigger: 'change'
+      }
+    ]
+  }))
+
+  const formItems = computed(() => [
+    {
+      label: t('pages.orders.asnOrder.detail.orderType'),
+      key: 'type',
+      type: 'select',
+      props: {
+        clearable: true,
+        filterable: true,
+        placeholder: t('pages.orders.asnOrder.dialog.placeholder.type'),
+        options: props.typeOptions
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.detail.wkType'),
+      key: 'wkType',
+      type: 'select',
+      props: {
+        clearable: true,
+        filterable: true,
+        placeholder: t('pages.orders.asnOrder.dialog.placeholder.wkType'),
+        options: props.wkTypeOptions
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.detail.poCode'),
+      key: 'poCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrder.dialog.placeholder.poCode')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.search.logisNo'),
+      key: 'logisNo',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrder.dialog.placeholder.logisNo')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.search.arrTime'),
+      key: 'arrTime',
+      type: 'datetime',
+      props: {
+        clearable: true,
+        valueFormat: 'YYYY-MM-DD HH:mm:ss',
+        format: 'YYYY-MM-DD HH:mm:ss',
+        placeholder: t('pages.orders.asnOrder.dialog.placeholder.arrTime')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.detail.memo'),
+      key: 'memo',
+      type: 'input',
+      span: 24,
+      props: {
+        type: 'textarea',
+        rows: 3,
+        clearable: true,
+        placeholder: t('pages.orders.asnOrder.dialog.placeholder.memo')
+      }
+    }
+  ])
+
+  function loadDialogData() {
+    Object.assign(form, buildAsnOrderDialogModel(props.orderData?.order || {}))
+    itemRows.value = Array.isArray(props.orderData?.items)
+      ? props.orderData.items.map((item) => buildAsnOrderEditableItem(item, props.fieldDefinitions))
+      : []
+    selectedItemKeys.value = []
+  }
+
+  function resetDialogData() {
+    Object.assign(form, createAsnOrderFormState())
+    itemRows.value = []
+    selectedItemKeys.value = []
+    formRef.value?.clearValidate?.()
+  }
+
+  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 handleMaterialConfirm(rows) {
+    const existingMatnrIds = new Set(selectedMatnrIds.value)
+    const nextRows = rows
+      .map((item) => createAsnOrderEditableItemFromMaterial(item, props.fieldDefinitions))
+      .filter((item) => !existingMatnrIds.has(item.matnrId))
+
+    if (!nextRows.length) {
+      ElMessage.warning(t('pages.orders.asnOrder.dialog.materialDuplicate'))
+      return
+    }
+
+    itemRows.value = [...itemRows.value, ...nextRows]
+  }
+
+  function validateItems() {
+    if (!itemRows.value.length) {
+      ElMessage.warning(t('pages.orders.asnOrder.dialog.validation.items'))
+      return false
+    }
+
+    const invalidRow = itemRows.value.find((item) => Number(item.anfme) <= 0)
+    if (invalidRow) {
+      ElMessage.warning(t('pages.orders.asnOrder.dialog.validation.anfme'))
+      return false
+    }
+
+    return true
+  }
+
+  async function handleSubmit() {
+    if (!formRef.value) {
+      return
+    }
+
+    try {
+      await formRef.value.validate()
+    } catch {
+      return
+    }
+
+    if (!validateItems()) {
+      return
+    }
+
+    emit('submit', {
+      order: { ...form },
+      items: itemRows.value.map((item) => ({ ...item }))
+    })
+  }
+
+  function handleVisibleChange(visible) {
+    emit('update:visible', visible)
+  }
+
+  function handleClosed() {
+    resetDialogData()
+  }
+
+  watch(
+    () => props.visible,
+    (visible) => {
+      if (visible) {
+        loadDialogData()
+        nextTick(() => {
+          formRef.value?.clearValidate?.()
+        })
+      }
+    },
+    { immediate: true }
+  )
+
+  watch(
+    () => props.orderData,
+    () => {
+      if (props.visible) {
+        loadDialogData()
+      }
+    },
+    { deep: true }
+  )
+</script>
diff --git a/rsf-design/src/views/orders/asn-order/modules/asn-order-item-dialog.vue b/rsf-design/src/views/orders/asn-order/modules/asn-order-item-dialog.vue
new file mode 100644
index 0000000..db63b16
--- /dev/null
+++ b/rsf-design/src/views/orders/asn-order/modules/asn-order-item-dialog.vue
@@ -0,0 +1,342 @@
+<template>
+  <ElDialog
+    :model-value="visible"
+    :title="t('menus.orders.asnOrderItem')"
+    width="92vw"
+    top="4vh"
+    destroy-on-close
+    append-to-body
+    @update:model-value="handleVisibleChange"
+  >
+    <div class="flex h-[78vh] min-h-0 flex-col gap-3">
+      <ElCard shadow="never" class="shrink-0">
+        <ElDescriptions :column="4" border>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.asnCode')">
+            {{ order.code || t('common.placeholder.empty') }}
+          </ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.poCode')">
+            {{ order.poCode || t('common.placeholder.empty') }}
+          </ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.wkType')">
+            {{ order.wkTypeLabel || t('common.placeholder.empty') }}
+          </ElDescriptionsItem>
+          <ElDescriptionsItem :label="t('pages.orders.asnOrder.detail.status')">
+            {{ order.exceStatusText || t('common.placeholder.empty') }}
+          </ElDescriptionsItem>
+        </ElDescriptions>
+      </ElCard>
+
+      <ArtSearchBar
+        v-model="searchForm"
+        class="shrink-0"
+        :items="searchItems"
+        :showExpand="true"
+        @search="handleSearch"
+        @reset="handleReset"
+      />
+
+      <ElCard class="art-table-card min-h-0 flex-1">
+        <ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData" />
+
+        <ArtTable
+          :loading="loading"
+          :data="data"
+          :columns="columns"
+          :pagination="pagination"
+          @pagination:size-change="handleSizeChange"
+          @pagination:current-change="handleCurrentChange"
+        />
+      </ElCard>
+    </div>
+
+    <template #footer>
+      <ElButton @click="handleVisibleChange(false)">{{ t('common.cancel') }}</ElButton>
+    </template>
+
+    <AsnOrderItemDetailDrawer
+      v-model:visible="detailDrawerVisible"
+      :loading="detailLoading"
+      :detail="detailData"
+    />
+  </ElDialog>
+</template>
+
+<script setup>
+  import { ref, watch } from 'vue'
+  import { useI18n } from 'vue-i18n'
+  import { ElMessage } from 'element-plus'
+  import { useTable } from '@/hooks/core/useTable'
+  import { guardRequestWithMessage } from '@/utils/sys/requestGuard'
+  import { fetchAsnOrderItemFullPage, fetchGetAsnOrderItemDetail } from '@/api/asn-order-item'
+  import AsnOrderItemDetailDrawer from '../../asn-order-item/modules/asn-order-item-detail-drawer.vue'
+  import { createAsnOrderItemTableColumns } from '../../asn-order-item/asnOrderItemTable.columns'
+  import {
+    buildAsnOrderItemPageQueryParams,
+    createAsnOrderItemSearchState,
+    normalizeAsnOrderItemDetail,
+    normalizeAsnOrderItemRow
+  } from '../../asn-order-item/asnOrderItemPage.helpers'
+
+  defineOptions({ name: 'AsnOrderItemDialog' })
+
+  const DEFAULT_PAGE_SIZE = 20
+
+  const props = defineProps({
+    visible: { type: Boolean, default: false },
+    order: { type: Object, default: () => ({}) }
+  })
+
+  const emit = defineEmits(['update:visible'])
+  const { t } = useI18n()
+
+  const searchForm = ref(createDialogSearchState())
+  const detailDrawerVisible = ref(false)
+  const detailLoading = ref(false)
+  const detailData = ref({})
+  const activeItemId = ref(null)
+
+  const searchItems = computed(() => [
+    {
+      label: t('table.keyword'),
+      key: 'condition',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItem.search.conditionPlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItem.search.poCode'),
+      key: 'poCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItem.search.poCodePlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItem.search.platWorkCode'),
+      key: 'platWorkCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItem.search.platWorkCodePlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItem.search.platItemId'),
+      key: 'platItemId',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItem.search.platItemIdPlaceholder')
+      }
+    },
+    {
+      label: t('table.materialCode'),
+      key: 'matnrCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItem.search.matnrCodePlaceholder')
+      }
+    },
+    {
+      label: t('table.materialName'),
+      key: 'maktx',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItem.search.maktxPlaceholder')
+      }
+    },
+    {
+      label: t('table.supplierBatch'),
+      key: 'splrBatch',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItem.search.splrBatchPlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItem.search.stockUnit'),
+      key: 'stockUnit',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrderItem.search.stockUnitPlaceholder')
+      }
+    },
+    {
+      label: t('table.status'),
+      key: 'status',
+      type: 'select',
+      props: {
+        clearable: true,
+        options: [
+          { label: t('common.status.normal'), value: 1 },
+          { label: t('common.status.frozen'), value: 0 }
+        ]
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItem.search.ntyStatus'),
+      key: 'ntyStatus',
+      type: 'select',
+      props: {
+        clearable: true,
+        options: [
+          { label: t('pages.orders.asnOrderItem.ntyStatus.notReported'), value: 0 },
+          { label: t('pages.orders.asnOrderItem.ntyStatus.reported'), value: 1 }
+        ]
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItem.search.createTimeRange'),
+      key: 'createTimeRange',
+      type: 'datetimerange',
+      props: {
+        clearable: true,
+        startPlaceholder: t('pages.orders.asnOrderItem.search.startTime'),
+        endPlaceholder: t('pages.orders.asnOrderItem.search.endTime'),
+        rangeSeparator: t('pages.orders.asnOrderItem.search.rangeSeparator')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrderItem.search.updateTimeRange'),
+      key: 'updateTimeRange',
+      type: 'datetimerange',
+      props: {
+        clearable: true,
+        startPlaceholder: t('pages.orders.asnOrderItem.search.startTime'),
+        endPlaceholder: t('pages.orders.asnOrderItem.search.endTime'),
+        rangeSeparator: t('pages.orders.asnOrderItem.search.rangeSeparator')
+      }
+    }
+  ])
+
+  const {
+    columns,
+    columnChecks,
+    data,
+    loading,
+    pagination,
+    replaceSearchParams,
+    handleSizeChange,
+    handleCurrentChange,
+    refreshData,
+    getData
+  } = useTable({
+    core: {
+      apiFn: fetchAsnOrderItemFullPage,
+      apiParams: buildQueryParams({
+        ...searchForm.value,
+        pageSize: DEFAULT_PAGE_SIZE
+      }),
+      immediate: false,
+      columnsFactory: () =>
+        createAsnOrderItemTableColumns({
+          handleView: openDetail,
+          t
+        })
+    },
+    transform: {
+      dataTransformer: (records) =>
+        Array.isArray(records) ? records.map((item) => normalizeAsnOrderItemRow(item, t)) : []
+    }
+  })
+
+  watch(
+    () => [props.visible, props.order?.id],
+    ([visible, orderId]) => {
+      if (!visible || !orderId) {
+        return
+      }
+      resetDialogState()
+      replaceSearchParams(buildQueryParams(searchForm.value))
+      getData()
+    },
+    { immediate: true }
+  )
+
+  function createDialogSearchState() {
+    return {
+      ...createAsnOrderItemSearchState(),
+      orderId: props.order?.id ? String(props.order.id) : ''
+    }
+  }
+
+  function buildQueryParams(params = {}) {
+    return buildAsnOrderItemPageQueryParams({
+      ...params,
+      orderId: props.order?.id ? String(props.order.id) : '',
+      orderBy: 'id desc'
+    })
+  }
+
+  function resetDialogState() {
+    searchForm.value = createDialogSearchState()
+    detailDrawerVisible.value = false
+    detailData.value = {}
+    activeItemId.value = null
+  }
+
+  function handleVisibleChange(value) {
+    emit('update:visible', value)
+  }
+
+  function handleSearch(params) {
+    searchForm.value = {
+      ...searchForm.value,
+      ...params,
+      orderId: props.order?.id ? String(props.order.id) : ''
+    }
+    replaceSearchParams(buildQueryParams(searchForm.value))
+    getData()
+  }
+
+  function handleReset() {
+    resetDialogState()
+    replaceSearchParams(buildQueryParams(searchForm.value))
+    getData()
+  }
+
+  async function openDetail(row) {
+    activeItemId.value = row.id
+    detailData.value = normalizeAsnOrderItemDetail(row, t)
+    detailDrawerVisible.value = true
+    await loadDetailResource()
+  }
+
+  async function loadDetailResource() {
+    if (!activeItemId.value) {
+      return
+    }
+
+    detailLoading.value = true
+    try {
+      const detailResponse = await guardRequestWithMessage(
+        fetchGetAsnOrderItemDetail(activeItemId.value),
+        {},
+        {
+          timeoutMessage: t('pages.orders.asnOrderItem.messages.detailTimeout')
+        }
+      )
+
+      detailData.value = normalizeAsnOrderItemDetail(
+        {
+          ...detailData.value,
+          ...detailResponse
+        },
+        t
+      )
+    } catch (error) {
+      detailDrawerVisible.value = false
+      detailData.value = {}
+      ElMessage.error(error?.message || t('pages.orders.asnOrderItem.messages.detailFailed'))
+    } finally {
+      detailLoading.value = false
+    }
+  }
+</script>
diff --git a/rsf-design/src/views/orders/asn-order/modules/asn-order-material-dialog.vue b/rsf-design/src/views/orders/asn-order/modules/asn-order-material-dialog.vue
new file mode 100644
index 0000000..babcf2e
--- /dev/null
+++ b/rsf-design/src/views/orders/asn-order/modules/asn-order-material-dialog.vue
@@ -0,0 +1,269 @@
+<template>
+  <ElDialog
+    :model-value="visible"
+    :title="t('pages.orders.asnOrder.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 { defaultResponseAdapter } from '@/utils/table/tableUtils'
+  import { guardRequestWithMessage } from '@/utils/sys/requestGuard'
+  import { fetchMatnrGroupTree, fetchMatnrPage } from '@/api/wh-mat'
+  import {
+    buildAsnOrderMaterialSearchParams,
+    createAsnOrderMaterialSearchState,
+    normalizeTreeOptions
+  } from '../asnOrderPage.helpers'
+
+  const props = defineProps({
+    visible: { type: Boolean, default: false },
+    fieldDefinitions: { type: Array, default: () => [] },
+    selectedMatnrIds: { type: Array, default: () => [] }
+  })
+
+  const emit = defineEmits(['update:visible', 'confirm'])
+  const { t } = useI18n()
+
+  const searchForm = ref(createAsnOrderMaterialSearchState())
+  const selectedRows = ref([])
+  const groupTreeOptions = ref([])
+
+  const searchItems = computed(() => [
+    {
+      label: t('pages.orders.asnOrder.materialDialog.search.name'),
+      key: 'name',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrder.materialDialog.placeholder.name')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.materialDialog.search.code'),
+      key: 'code',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.asnOrder.materialDialog.placeholder.code')
+      }
+    },
+    {
+      label: t('pages.orders.asnOrder.materialDialog.search.groupId'),
+      key: 'groupId',
+      type: 'treeselect',
+      props: {
+        clearable: true,
+        checkStrictly: true,
+        renderAfterExpand: false,
+        showCheckbox: false,
+        data: groupTreeOptions.value,
+        placeholder: t('pages.orders.asnOrder.materialDialog.placeholder.groupId')
+      }
+    }
+  ])
+
+  const createColumns = () => {
+    const baseColumns = [
+      { type: 'selection', width: 48, align: 'center' },
+      { type: 'globalIndex', label: t('table.index'), width: 72, align: 'center' },
+      {
+        prop: 'code',
+        label: t('pages.orders.asnOrder.materialDialog.table.code'),
+        minWidth: 170,
+        showOverflowTooltip: true
+      },
+      {
+        prop: 'name',
+        label: t('pages.orders.asnOrder.materialDialog.table.name'),
+        minWidth: 220,
+        showOverflowTooltip: true
+      },
+      {
+        prop: 'groupName',
+        label: t('pages.orders.asnOrder.materialDialog.table.group'),
+        minWidth: 140,
+        showOverflowTooltip: true
+      },
+      {
+        prop: 'spec',
+        label: t('pages.orders.asnOrder.materialDialog.table.spec'),
+        minWidth: 120,
+        showOverflowTooltip: true,
+        visible: false
+      },
+      {
+        prop: 'model',
+        label: t('pages.orders.asnOrder.materialDialog.table.model'),
+        minWidth: 120,
+        showOverflowTooltip: true,
+        visible: false
+      },
+      {
+        prop: 'unit',
+        label: t('table.unit'),
+        width: 100,
+        align: 'center'
+      },
+      {
+        prop: 'selected',
+        label: t('pages.orders.asnOrder.materialDialog.table.status'),
+        width: 110,
+        formatter: (row) =>
+          h(
+            ElTag,
+            {
+              type: props.selectedMatnrIds.includes(row.id) ? 'warning' : 'info',
+              effect: 'light'
+            },
+            () =>
+              props.selectedMatnrIds.includes(row.id)
+                ? t('pages.orders.asnOrder.materialDialog.selected')
+                : t('pages.orders.asnOrder.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: fetchMatnrPage,
+      apiParams: buildAsnOrderMaterialSearchParams(searchForm.value),
+      immediate: false,
+      columnsFactory: createColumns
+    },
+    transform: {
+      dataTransformer: (records) =>
+        Array.isArray(records)
+          ? records.map((item) => ({
+              ...item,
+              groupName: item['groupId$'] || item.groupName || '-',
+              unit: item.unit || item.stockUnit || '-',
+              ...Object.fromEntries(
+                props.fieldDefinitions.map((field) => [
+                  field.fields,
+                  item[field.fields] ?? item.extendFields?.[field.fields] ?? ''
+                ])
+              )
+            }))
+          : []
+    }
+  })
+
+  function handleSelectionChange(rows) {
+    selectedRows.value = Array.isArray(rows) ? rows : []
+  }
+
+  function handleSearch(params) {
+    searchForm.value = {
+      ...searchForm.value,
+      ...params
+    }
+    replaceSearchParams(buildAsnOrderMaterialSearchParams(searchForm.value))
+    getData()
+  }
+
+  function handleReset() {
+    searchForm.value = createAsnOrderMaterialSearchState()
+    replaceSearchParams(buildAsnOrderMaterialSearchParams(searchForm.value))
+    getData()
+  }
+
+  function handleConfirm() {
+    emit(
+      'confirm',
+      selectedRows.value.map((item) => ({ ...item }))
+    )
+    handleVisibleChange(false)
+  }
+
+  function handleVisibleChange(visible) {
+    emit('update:visible', visible)
+  }
+
+  async function loadGroupTree() {
+    const response = await guardRequestWithMessage(fetchMatnrGroupTree({}), [], {
+      timeoutMessage: t('pages.orders.asnOrder.materialDialog.messages.groupTimeout')
+    })
+    const records = Array.isArray(response) ? response : defaultResponseAdapter(response).records
+    groupTreeOptions.value = normalizeTreeOptions(records)
+  }
+
+  watch(
+    () => props.visible,
+    async (visible) => {
+      if (!visible) {
+        searchForm.value = createAsnOrderMaterialSearchState()
+        selectedRows.value = []
+        return
+      }
+
+      searchForm.value = createAsnOrderMaterialSearchState()
+      replaceSearchParams(buildAsnOrderMaterialSearchParams(searchForm.value))
+      await Promise.allSettled([loadGroupTree(), getData()])
+    }
+  )
+
+  watch(
+    () => props.fieldDefinitions,
+    () => {
+      resetColumns?.()
+    },
+    { deep: true }
+  )
+</script>
diff --git a/rsf-design/src/views/orders/delivery-item/deliveryItemPage.helpers.js b/rsf-design/src/views/orders/delivery-item/deliveryItemPage.helpers.js
index db0887c..fbb4491 100644
--- a/rsf-design/src/views/orders/delivery-item/deliveryItemPage.helpers.js
+++ b/rsf-design/src/views/orders/delivery-item/deliveryItemPage.helpers.js
@@ -50,21 +50,39 @@
     platItemId: '',
     matnrCode: '',
     maktx: '',
+    fieldsIndex: '',
     splrName: '',
+    splrCode: '',
+    status: '',
+    timeStart: '',
+    timeEnd: '',
+    memo: '',
+    orderBy: 'create_time desc',
     splrBatch: ''
   }
 }
 
 export function buildDeliveryItemSearchParams(params = {}) {
   const result = {}
-  ;['condition', 'deliveryCode', 'platItemId', 'matnrCode', 'maktx', 'splrName', 'splrCode', 'splrBatch', 'memo'].forEach(
-    (key) => {
-      const value = normalizeText(params[key])
-      if (value) {
-        result[key] = value
-      }
+  ;[
+    'condition',
+    'deliveryCode',
+    'platItemId',
+    'matnrCode',
+    'maktx',
+    'fieldsIndex',
+    'splrName',
+    'splrCode',
+    'splrBatch',
+    'timeStart',
+    'timeEnd',
+    'memo'
+  ].forEach((key) => {
+    const value = normalizeText(params[key])
+    if (value) {
+      result[key] = value
     }
-  )
+  })
 
   if (params.deliveryId !== '' && params.deliveryId !== undefined && params.deliveryId !== null) {
     result.deliveryId = normalizeNumber(params.deliveryId)
@@ -81,6 +99,7 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
+    orderBy: normalizeText(params.orderBy) || 'create_time desc',
     ...buildDeliveryItemSearchParams(params)
   }
 }
@@ -117,18 +136,103 @@
     batch: normalizeText(record.batch) || '--',
     trackCode: normalizeText(record.trackCode) || '--',
     packName: normalizeText(record.packName) || '--',
-    prodTimeText: normalizeText(record['prodTime$'] || record.prodTimeText || record.prodTime) || '--',
+    prodTimeText:
+      normalizeText(record['prodTime$'] || record.prodTimeText || record.prodTime) || '--',
     statusText: statusMeta.text,
     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) || '--'
   }
 }
 
+export function createDeliveryItemFormState() {
+  return {
+    id: undefined,
+    deliveryId: undefined,
+    platItemId: '',
+    matnrId: undefined,
+    matnrCode: '',
+    maktx: '',
+    fieldsIndex: '',
+    unit: '',
+    anfme: undefined,
+    qty: undefined,
+    printQty: undefined,
+    splrName: '',
+    splrCode: '',
+    splrBatch: '',
+    status: 1,
+    memo: ''
+  }
+}
+
+export function buildDeliveryItemDialogModel(record = {}) {
+  return {
+    ...createDeliveryItemFormState(),
+    ...record,
+    id: record.id ?? undefined,
+    deliveryId: record.deliveryId ?? undefined,
+    platItemId: normalizeText(record.platItemId),
+    matnrId: record.matnrId ?? undefined,
+    matnrCode: normalizeText(record.matnrCode),
+    maktx: normalizeText(record.maktx || record.matnrName),
+    fieldsIndex: normalizeText(record.fieldsIndex),
+    unit: normalizeText(record.unit),
+    anfme: normalizeNumber(record.anfme, undefined),
+    qty: normalizeNumber(record.qty, undefined),
+    printQty: normalizeNumber(record.printQty, undefined),
+    splrName: normalizeText(record.splrName),
+    splrCode: normalizeText(record.splrCode),
+    splrBatch: normalizeText(record.splrBatch),
+    status: normalizeNumber(record.statusBool ?? record.status, 1),
+    memo: normalizeText(record.memo)
+  }
+}
+
+export function buildDeliveryItemSavePayload(formData = {}) {
+  return {
+    ...(formData.id !== undefined && formData.id !== null ? { id: Number(formData.id) } : {}),
+    ...(formData.deliveryId !== undefined && formData.deliveryId !== null
+      ? { deliveryId: Number(formData.deliveryId) }
+      : {}),
+    ...(formData.matnrId !== undefined && formData.matnrId !== null
+      ? { matnrId: Number(formData.matnrId) }
+      : {}),
+    platItemId: normalizeText(formData.platItemId),
+    matnrCode: normalizeText(formData.matnrCode),
+    maktx: normalizeText(formData.maktx),
+    fieldsIndex: normalizeText(formData.fieldsIndex),
+    unit: normalizeText(formData.unit),
+    ...(formData.anfme !== undefined && formData.anfme !== null && formData.anfme !== ''
+      ? { anfme: Number(formData.anfme) }
+      : {}),
+    ...(formData.qty !== undefined && formData.qty !== null && formData.qty !== ''
+      ? { qty: Number(formData.qty) }
+      : {}),
+    ...(formData.printQty !== undefined && formData.printQty !== null && formData.printQty !== ''
+      ? { printQty: Number(formData.printQty) }
+      : {}),
+    splrName: normalizeText(formData.splrName),
+    splrCode: normalizeText(formData.splrCode),
+    splrBatch: normalizeText(formData.splrBatch),
+    status: normalizeNumber(formData.status, 1),
+    memo: normalizeText(formData.memo)
+  }
+}
+
+export function getDeliveryItemStatusOptions(t = $t) {
+  return [
+    { value: 1, label: t('common.status.normal') },
+    { value: 0, label: t('common.status.frozen') }
+  ]
+}
+
 export function buildDeliveryItemPrintRows(records = [], t = $t) {
   if (!Array.isArray(records)) {
     return []
diff --git a/rsf-design/src/views/orders/delivery-item/deliveryItemTable.columns.js b/rsf-design/src/views/orders/delivery-item/deliveryItemTable.columns.js
index 23275c6..3e0c3ce 100644
--- a/rsf-design/src/views/orders/delivery-item/deliveryItemTable.columns.js
+++ b/rsf-design/src/views/orders/delivery-item/deliveryItemTable.columns.js
@@ -1,12 +1,54 @@
 import { h } from 'vue'
 import { ElTag } from 'element-plus'
-import ArtButtonTable from '@/components/core/forms/art-button-table/index.vue'
+import ArtButtonMore from '@/components/core/forms/art-button-more/index.vue'
 import { $t } from '@/locales'
 
-export function createDeliveryItemTableColumns({ handleActionClick, t = $t } = {}) {
+function createActionList({ canEdit = true, canDelete = true, t = $t } = {}) {
+  const actions = [
+    {
+      key: 'view',
+      label: t('pages.orders.delivery.actions.view'),
+      icon: 'ri:eye-line'
+    }
+  ]
+
+  if (canEdit) {
+    actions.push({
+      key: 'edit',
+      label: t('common.actions.edit'),
+      icon: 'ri:edit-line'
+    })
+  }
+
+  if (canDelete) {
+    actions.push({
+      key: 'delete',
+      label: t('common.actions.delete'),
+      icon: 'ri:delete-bin-5-line',
+      color: 'var(--art-error)'
+    })
+  }
+
+  return actions
+}
+
+export function createDeliveryItemTableColumns({
+  handleActionClick,
+  canEdit = true,
+  canDelete = true,
+  t = $t
+} = {}) {
   return [
     { 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: 'right',
+      visible: false,
+      formatter: (row) => row.id ?? '--'
+    },
     {
       prop: 'deliveryCode',
       label: t('pages.orders.deliveryItem.table.deliveryCode'),
@@ -121,6 +163,38 @@
         h(ElTag, { type: row.statusType || 'info', effect: 'light' }, () => row.statusText || '--')
     },
     {
+      prop: 'updateByText',
+      label: t('table.updateBy'),
+      minWidth: 130,
+      visible: false,
+      showOverflowTooltip: true,
+      formatter: (row) => row.updateByText || '--'
+    },
+    {
+      prop: 'updateTimeText',
+      label: t('table.updateTime'),
+      minWidth: 170,
+      visible: false,
+      showOverflowTooltip: true,
+      formatter: (row) => row.updateTimeText || '--'
+    },
+    {
+      prop: 'createByText',
+      label: t('table.createBy'),
+      minWidth: 130,
+      visible: false,
+      showOverflowTooltip: true,
+      formatter: (row) => row.createByText || '--'
+    },
+    {
+      prop: 'createTimeText',
+      label: t('table.createTime'),
+      minWidth: 170,
+      visible: false,
+      showOverflowTooltip: true,
+      formatter: (row) => row.createTimeText || '--'
+    },
+    {
       prop: 'memo',
       label: t('table.memo'),
       minWidth: 180,
@@ -130,12 +204,12 @@
     {
       prop: 'operation',
       label: t('table.operation'),
-      width: 92,
+      width: 120,
       fixed: 'right',
       formatter: (row) =>
-        h(ArtButtonTable, {
-          type: 'view',
-          onClick: () => handleActionClick?.(row)
+        h(ArtButtonMore, {
+          list: createActionList({ canEdit, canDelete, t }),
+          onClick: (item) => handleActionClick?.(item, row)
         })
     }
   ]
diff --git a/rsf-design/src/views/orders/delivery-item/index.vue b/rsf-design/src/views/orders/delivery-item/index.vue
index 0c0f089..312980f 100644
--- a/rsf-design/src/views/orders/delivery-item/index.vue
+++ b/rsf-design/src/views/orders/delivery-item/index.vue
@@ -3,38 +3,24 @@
     <ElCard v-if="activeSourceSummary" class="mb-3">
       <div class="flex items-center justify-between gap-3">
         <div class="flex items-center gap-2 text-sm text-[var(--art-text-gray-600)]">
-          <span class="font-medium text-[var(--art-text-gray-900)]">{{ t('pages.orders.deliveryItem.sourceTitle') }}</span>
-          <span>{{ t('pages.orders.deliveryItem.sourceLabel', { id: activeSourceSummary.deliveryId }) }}</span>
+          <span class="font-medium text-[var(--art-text-gray-900)]">{{
+            t('pages.orders.deliveryItem.sourceTitle')
+          }}</span>
+          <span>{{
+            t('pages.orders.deliveryItem.sourceLabel', { id: activeSourceSummary.deliveryId })
+          }}</span>
         </div>
-        <ElButton link type="primary" @click="handleClearSourceFilter">{{ t('common.actions.viewAll') }}</ElButton>
+        <ElButton link type="primary" @click="handleClearSourceFilter">{{
+          t('common.actions.viewAll')
+        }}</ElButton>
       </div>
     </ElCard>
 
-    <ArtSearchBar
-      v-model="searchForm"
-      :items="searchItems"
-      :showExpand="true"
-      @search="handleSearch"
-      @reset="handleReset"
-    />
-
-    <ElCard class="art-table-card">
-      <ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData" />
-
-      <ArtTable
-        :loading="loading"
-        :data="data"
-        :columns="columns"
-        :pagination="pagination"
-        @pagination:size-change="handleSizeChange"
-        @pagination:current-change="handleCurrentChange"
-      />
-    </ElCard>
-
-    <DeliveryItemDetailDrawer
-      v-model:visible="detailDrawerVisible"
-      :loading="detailLoading"
-      :detail="detailData"
+    <DeliveryItemManagePanel
+      :delivery-id="searchForm.deliveryId"
+      :can-add="hasAuth('add')"
+      :can-edit="hasAuth('update')"
+      :can-delete="hasAuth('delete')"
     />
   </div>
 </template>
@@ -42,164 +28,32 @@
 <script setup>
   import { computed, onMounted, ref, watch } from 'vue'
   import { useI18n } from 'vue-i18n'
-  import { ElButton, ElMessage } from 'element-plus'
+  import { ElButton } from 'element-plus'
   import { useRoute, useRouter } from 'vue-router'
-  import { useTable } from '@/hooks/core/useTable'
-  import { guardRequestWithMessage } from '@/utils/sys/requestGuard'
-  import { fetchDeliveryItemPage, fetchGetDeliveryItemDetail } from '@/api/delivery'
-  import DeliveryItemDetailDrawer from './modules/delivery-item-detail-drawer.vue'
-  import { createDeliveryItemTableColumns } from './deliveryItemTable.columns.js'
-  import {
-    buildDeliveryItemPageQueryParams,
-    createDeliveryItemSearchState,
-    normalizeDeliveryItemRow
-  } from './deliveryItemPage.helpers.js'
+  import { useAuth } from '@/hooks/core/useAuth'
+  import DeliveryItemManagePanel from './modules/delivery-item-manage-panel.vue'
+  import { createDeliveryItemSearchState } from './deliveryItemPage.helpers.js'
 
   defineOptions({ name: 'DeliveryItem' })
 
   const { t } = useI18n()
+  const { hasAuth } = useAuth()
   const route = useRoute()
   const router = useRouter()
   const searchForm = ref(createDeliveryItemSearchState())
-  const detailDrawerVisible = ref(false)
-  const detailLoading = ref(false)
-  const detailData = ref({})
 
   const activeSourceSummary = computed(() => {
-    if (searchForm.value.deliveryId === '' || searchForm.value.deliveryId === undefined || searchForm.value.deliveryId === null) {
+    if (
+      searchForm.value.deliveryId === '' ||
+      searchForm.value.deliveryId === undefined ||
+      searchForm.value.deliveryId === null
+    ) {
       return null
     }
     return {
       deliveryId: searchForm.value.deliveryId
     }
   })
-
-  const searchItems = computed(() => [
-    {
-      label: t('table.keyword'),
-      key: 'condition',
-      type: 'input',
-      props: {
-        clearable: true,
-        placeholder: t('pages.orders.deliveryItem.search.conditionPlaceholder')
-      }
-    },
-    {
-      label: t('pages.orders.deliveryItem.search.deliveryCode'),
-      key: 'deliveryCode',
-      type: 'input',
-      props: {
-        clearable: true,
-        placeholder: t('pages.orders.deliveryItem.search.deliveryCodePlaceholder')
-      }
-    },
-    {
-      label: t('pages.orders.deliveryItem.search.platItemId'),
-      key: 'platItemId',
-      type: 'input',
-      props: {
-        clearable: true,
-        placeholder: t('pages.orders.deliveryItem.search.platItemIdPlaceholder')
-      }
-    },
-    {
-      label: t('table.materialCode'),
-      key: 'matnrCode',
-      type: 'input',
-      props: {
-        clearable: true,
-        placeholder: t('pages.orders.deliveryItem.search.matnrCodePlaceholder')
-      }
-    },
-    {
-      label: t('table.materialName'),
-      key: 'maktx',
-      type: 'input',
-      props: {
-        clearable: true,
-        placeholder: t('pages.orders.deliveryItem.search.maktxPlaceholder')
-      }
-    },
-    {
-      label: t('pages.orders.deliveryItem.search.supplierName'),
-      key: 'splrName',
-      type: 'input',
-      props: {
-        clearable: true,
-        placeholder: t('pages.orders.deliveryItem.search.supplierNamePlaceholder')
-      }
-    },
-    {
-      label: t('table.supplierBatch'),
-      key: 'splrBatch',
-      type: 'input',
-      props: {
-        clearable: true,
-        placeholder: t('pages.orders.deliveryItem.search.supplierBatchPlaceholder')
-      }
-    }
-  ])
-
-  function openDetail(row) {
-    detailDrawerVisible.value = true
-    detailLoading.value = true
-    guardRequestWithMessage(fetchGetDeliveryItemDetail(row.id), {}, {
-      timeoutMessage: t('pages.orders.deliveryItem.messages.detailTimeout')
-    })
-      .then((detail) => {
-        detailData.value = normalizeDeliveryItemRow(detail, t)
-      })
-      .catch((error) => {
-        detailDrawerVisible.value = false
-        detailData.value = {}
-        ElMessage.error(error?.message || t('pages.orders.deliveryItem.messages.detailFailed'))
-      })
-      .finally(() => {
-        detailLoading.value = false
-      })
-  }
-
-  const {
-    columns,
-    columnChecks,
-    data,
-    loading,
-    pagination,
-    replaceSearchParams,
-    resetSearchParams,
-    handleSizeChange,
-    handleCurrentChange,
-    refreshData,
-    getData
-  } = useTable({
-    core: {
-      apiFn: fetchDeliveryItemPage,
-      apiParams: buildDeliveryItemPageQueryParams({
-        ...searchForm.value,
-        pageSize: 20
-      }),
-      immediate: false,
-      columnsFactory: () => createDeliveryItemTableColumns({ handleActionClick: openDetail, t })
-    },
-    transform: {
-      dataTransformer: (records) =>
-        Array.isArray(records) ? records.map((item) => normalizeDeliveryItemRow(item, t)) : []
-    }
-  })
-
-  function handleSearch(params) {
-    searchForm.value = {
-      ...searchForm.value,
-      ...params
-    }
-    replaceSearchParams(buildDeliveryItemPageQueryParams(searchForm.value))
-    getData()
-  }
-
-  function handleReset() {
-    Object.assign(searchForm.value, createDeliveryItemSearchState())
-    resetSearchParams()
-  }
 
   function applyRouteSearch() {
     const deliveryId = route.query.deliveryId
@@ -220,8 +74,6 @@
         deliveryId: undefined
       }
     })
-    replaceSearchParams(buildDeliveryItemPageQueryParams(searchForm.value))
-    getData()
   }
 
   watch(
@@ -231,14 +83,10 @@
         return
       }
       applyRouteSearch()
-      replaceSearchParams(buildDeliveryItemPageQueryParams(searchForm.value))
-      getData()
     }
   )
 
   onMounted(() => {
     applyRouteSearch()
-    replaceSearchParams(buildDeliveryItemPageQueryParams(searchForm.value))
-    getData()
   })
 </script>
diff --git a/rsf-design/src/views/orders/delivery-item/modules/delivery-item-dialog.vue b/rsf-design/src/views/orders/delivery-item/modules/delivery-item-dialog.vue
new file mode 100644
index 0000000..8217c47
--- /dev/null
+++ b/rsf-design/src/views/orders/delivery-item/modules/delivery-item-dialog.vue
@@ -0,0 +1,271 @@
+<template>
+  <ElDialog
+    :model-value="visible"
+    :title="dialogTitle"
+    width="860px"
+    destroy-on-close
+    @update:model-value="handleVisibleChange"
+    @closed="handleClosed"
+  >
+    <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>
+      <ElSpace>
+        <ElButton @click="handleVisibleChange(false)">{{ t('common.cancel') }}</ElButton>
+        <ElButton type="primary" :loading="submitLoading" @click="handleSubmit">
+          {{ t('common.confirm') }}
+        </ElButton>
+      </ElSpace>
+    </template>
+  </ElDialog>
+</template>
+
+<script setup>
+  import { computed, nextTick, reactive, ref, watch } from 'vue'
+  import { useI18n } from 'vue-i18n'
+  import ArtForm from '@/components/core/forms/art-form/index.vue'
+  import {
+    buildDeliveryItemDialogModel,
+    createDeliveryItemFormState,
+    getDeliveryItemStatusOptions
+  } from '../deliveryItemPage.helpers.js'
+
+  const props = defineProps({
+    visible: { type: Boolean, default: false },
+    dialogType: { type: String, default: 'add' },
+    itemData: { type: Object, default: () => ({}) },
+    deliveryId: { type: [Number, String], default: undefined },
+    submitLoading: { type: Boolean, default: false }
+  })
+
+  const emit = defineEmits(['update:visible', 'submit'])
+  const { t } = useI18n()
+
+  const formRef = ref()
+  const form = reactive(createDeliveryItemFormState())
+
+  const isEdit = computed(() => props.dialogType === 'edit')
+  const dialogTitle = computed(() =>
+    isEdit.value
+      ? t('pages.orders.deliveryItem.dialog.titleEdit')
+      : t('pages.orders.deliveryItem.dialog.titleAdd')
+  )
+
+  const rules = computed(() => ({
+    anfme: [
+      {
+        required: true,
+        message: t('pages.orders.deliveryItem.dialog.validation.anfme'),
+        trigger: 'blur'
+      }
+    ]
+  }))
+
+  const formItems = computed(() => [
+    {
+      label: t('pages.orders.deliveryItem.dialog.deliveryId'),
+      key: 'deliveryId',
+      type: 'input',
+      hidden: true,
+      props: {
+        disabled: true
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.dialog.platItemId'),
+      key: 'platItemId',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.dialog.placeholder.platItemId')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.dialog.matnrCode'),
+      key: 'matnrCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.dialog.placeholder.matnrCode')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.dialog.maktx'),
+      key: 'maktx',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.dialog.placeholder.maktx')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.dialog.fieldsIndex'),
+      key: 'fieldsIndex',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.dialog.placeholder.fieldsIndex')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.dialog.unit'),
+      key: 'unit',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.dialog.placeholder.unit')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.dialog.anfme'),
+      key: 'anfme',
+      type: 'number',
+      props: {
+        min: 0,
+        precision: 2,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.deliveryItem.dialog.placeholder.anfme')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.dialog.qty'),
+      key: 'qty',
+      type: 'number',
+      props: {
+        min: 0,
+        precision: 2,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.deliveryItem.dialog.placeholder.qty')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.dialog.printQty'),
+      key: 'printQty',
+      type: 'number',
+      props: {
+        min: 0,
+        precision: 2,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.deliveryItem.dialog.placeholder.printQty')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.dialog.splrName'),
+      key: 'splrName',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.dialog.placeholder.splrName')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.dialog.splrCode'),
+      key: 'splrCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.dialog.placeholder.splrCode')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.dialog.splrBatch'),
+      key: 'splrBatch',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.dialog.placeholder.splrBatch')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.dialog.status'),
+      key: 'status',
+      type: 'select',
+      props: {
+        clearable: true,
+        options: getDeliveryItemStatusOptions(t),
+        placeholder: t('pages.orders.deliveryItem.dialog.placeholder.status')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.dialog.memo'),
+      key: 'memo',
+      type: 'input',
+      span: 24,
+      props: {
+        type: 'textarea',
+        rows: 3,
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.dialog.placeholder.memo')
+      }
+    }
+  ])
+
+  function loadFormData() {
+    const nextForm = buildDeliveryItemDialogModel({
+      ...props.itemData,
+      deliveryId: props.itemData?.deliveryId ?? props.deliveryId
+    })
+    Object.assign(form, nextForm)
+  }
+
+  function resetForm() {
+    Object.assign(form, createDeliveryItemFormState(), {
+      deliveryId: props.deliveryId
+    })
+    formRef.value?.clearValidate?.()
+  }
+
+  async function handleSubmit() {
+    if (!formRef.value) {
+      return
+    }
+    try {
+      await formRef.value.validate()
+      emit('submit', { ...form })
+    } catch {
+      return
+    }
+  }
+
+  function handleVisibleChange(value) {
+    emit('update:visible', value)
+  }
+
+  function handleClosed() {
+    resetForm()
+  }
+
+  watch(
+    () => props.visible,
+    (visible) => {
+      if (!visible) {
+        return
+      }
+      loadFormData()
+      nextTick(() => {
+        formRef.value?.clearValidate?.()
+      })
+    },
+    { immediate: true }
+  )
+
+  watch(
+    () => props.itemData,
+    () => {
+      if (props.visible) {
+        loadFormData()
+      }
+    },
+    { deep: true }
+  )
+</script>
diff --git a/rsf-design/src/views/orders/delivery-item/modules/delivery-item-manage-panel.vue b/rsf-design/src/views/orders/delivery-item/modules/delivery-item-manage-panel.vue
new file mode 100644
index 0000000..4129c92
--- /dev/null
+++ b/rsf-design/src/views/orders/delivery-item/modules/delivery-item-manage-panel.vue
@@ -0,0 +1,441 @@
+<template>
+  <div class="flex flex-col gap-4">
+    <ArtSearchBar
+      v-model="searchForm"
+      :items="searchItems"
+      :showExpand="true"
+      @search="handleSearch"
+      @reset="handleReset"
+    />
+
+    <ElCard class="art-table-card">
+      <ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
+        <template #left>
+          <ElSpace wrap>
+            <ElButton type="primary" :disabled="!canCreateItem" @click="openCreateDialog">
+              {{ t('pages.orders.deliveryItem.actions.add') }}
+            </ElButton>
+            <ElButton
+              type="danger"
+              plain
+              :disabled="!selectedRows.length || !canDelete"
+              @click="handleBatchDelete"
+            >
+              {{ t('common.actions.batchDelete') }}
+            </ElButton>
+          </ElSpace>
+        </template>
+      </ArtTableHeader>
+
+      <ArtTable
+        :loading="loading"
+        :data="data"
+        :columns="columns"
+        :pagination="pagination"
+        @selection-change="handleSelectionChange"
+        @pagination:size-change="handleSizeChange"
+        @pagination:current-change="handleCurrentChange"
+      />
+    </ElCard>
+
+    <DeliveryItemDetailDrawer
+      v-model:visible="detailDrawerVisible"
+      :loading="detailLoading"
+      :detail="detailData"
+    />
+
+    <DeliveryItemDialog
+      v-model:visible="dialogVisible"
+      :dialog-type="dialogType"
+      :item-data="currentItemData"
+      :delivery-id="resolvedDeliveryId"
+      :submit-loading="submitLoading"
+      @submit="handleDialogSubmit"
+    />
+  </div>
+</template>
+
+<script setup>
+  import { computed, ref, watch } from 'vue'
+  import { ElMessage, ElMessageBox } from 'element-plus'
+  import { useI18n } from 'vue-i18n'
+  import { useTable } from '@/hooks/core/useTable'
+  import { guardRequestWithMessage } from '@/utils/sys/requestGuard'
+  import {
+    fetchDeleteDeliveryItem,
+    fetchDeleteDeliveryItemMany,
+    fetchDeliveryItemPage,
+    fetchGetDeliveryItemDetail,
+    fetchSaveDeliveryItem,
+    fetchUpdateDeliveryItem
+  } from '@/api/delivery'
+  import DeliveryItemDetailDrawer from './delivery-item-detail-drawer.vue'
+  import DeliveryItemDialog from './delivery-item-dialog.vue'
+  import { createDeliveryItemTableColumns } from '../deliveryItemTable.columns.js'
+  import {
+    buildDeliveryItemDialogModel,
+    buildDeliveryItemPageQueryParams,
+    buildDeliveryItemSavePayload,
+    createDeliveryItemFormState,
+    createDeliveryItemSearchState,
+    getDeliveryItemStatusOptions,
+    normalizeDeliveryItemRow
+  } from '../deliveryItemPage.helpers.js'
+
+  const props = defineProps({
+    deliveryId: { type: [Number, String], default: undefined },
+    canEdit: { type: Boolean, default: true },
+    canDelete: { type: Boolean, default: true },
+    canAdd: { type: Boolean, default: true }
+  })
+
+  const emit = defineEmits(['changed'])
+  const { t } = useI18n()
+
+  const searchForm = ref(createDeliveryItemSearchState())
+  const selectedRows = ref([])
+  const detailDrawerVisible = ref(false)
+  const detailLoading = ref(false)
+  const detailData = ref({})
+  const dialogVisible = ref(false)
+  const dialogType = ref('add')
+  const currentItemData = ref(createDeliveryItemFormState())
+  const submitLoading = ref(false)
+
+  const resolvedDeliveryId = computed(() => {
+    if (props.deliveryId === '' || props.deliveryId === undefined || props.deliveryId === null) {
+      return undefined
+    }
+    const numericValue = Number(props.deliveryId)
+    return Number.isFinite(numericValue) ? numericValue : undefined
+  })
+
+  const canCreateItem = computed(() => props.canAdd && resolvedDeliveryId.value !== undefined)
+
+  const searchItems = computed(() => [
+    {
+      label: t('table.keyword'),
+      key: 'condition',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.search.conditionPlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.search.deliveryCode'),
+      key: 'deliveryCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.search.deliveryCodePlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.search.platItemId'),
+      key: 'platItemId',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.search.platItemIdPlaceholder')
+      }
+    },
+    {
+      label: t('table.materialCode'),
+      key: 'matnrCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.search.matnrCodePlaceholder')
+      }
+    },
+    {
+      label: t('table.materialName'),
+      key: 'maktx',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.search.maktxPlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.table.fieldsIndex'),
+      key: 'fieldsIndex',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.search.fieldsIndexPlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.search.supplierName'),
+      key: 'splrName',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.search.supplierNamePlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.table.supplierCode'),
+      key: 'splrCode',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.search.supplierCodePlaceholder')
+      }
+    },
+    {
+      label: t('table.supplierBatch'),
+      key: 'splrBatch',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.search.supplierBatchPlaceholder')
+      }
+    },
+    {
+      label: t('table.status'),
+      key: 'status',
+      type: 'select',
+      props: {
+        clearable: true,
+        options: getDeliveryItemStatusOptions(t),
+        placeholder: t('pages.orders.deliveryItem.search.statusPlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.search.timeStart'),
+      key: 'timeStart',
+      type: 'date',
+      props: {
+        clearable: true,
+        valueFormat: 'YYYY-MM-DD',
+        placeholder: t('pages.orders.deliveryItem.search.timeStartPlaceholder')
+      }
+    },
+    {
+      label: t('pages.orders.deliveryItem.search.timeEnd'),
+      key: 'timeEnd',
+      type: 'date',
+      props: {
+        clearable: true,
+        valueFormat: 'YYYY-MM-DD',
+        placeholder: t('pages.orders.deliveryItem.search.timeEndPlaceholder')
+      }
+    },
+    {
+      label: t('table.memo'),
+      key: 'memo',
+      type: 'input',
+      props: {
+        clearable: true,
+        placeholder: t('pages.orders.deliveryItem.search.memoPlaceholder')
+      }
+    }
+  ])
+
+  const {
+    columns,
+    columnChecks,
+    data,
+    loading,
+    pagination,
+    replaceSearchParams,
+    handleSizeChange,
+    handleCurrentChange,
+    refreshData,
+    getData
+  } = useTable({
+    core: {
+      apiFn: fetchDeliveryItemPage,
+      apiParams: buildDeliveryItemPageQueryParams(searchForm.value),
+      immediate: false,
+      columnsFactory: () =>
+        createDeliveryItemTableColumns({
+          handleActionClick: handleActionClick,
+          canEdit: props.canEdit,
+          canDelete: props.canDelete,
+          t
+        })
+    },
+    transform: {
+      dataTransformer: (records) =>
+        Array.isArray(records) ? records.map((item) => normalizeDeliveryItemRow(item, t)) : []
+    }
+  })
+
+  function syncDeliveryFilter() {
+    searchForm.value.deliveryId = resolvedDeliveryId.value ?? ''
+  }
+
+  function handleSelectionChange(rows) {
+    selectedRows.value = Array.isArray(rows) ? rows : []
+  }
+
+  function handleSearch(params) {
+    searchForm.value = {
+      ...searchForm.value,
+      ...params,
+      deliveryId: resolvedDeliveryId.value ?? searchForm.value.deliveryId
+    }
+    replaceSearchParams(buildDeliveryItemPageQueryParams(searchForm.value))
+    getData()
+  }
+
+  function handleReset() {
+    searchForm.value = createDeliveryItemSearchState()
+    syncDeliveryFilter()
+    replaceSearchParams(buildDeliveryItemPageQueryParams(searchForm.value))
+    getData()
+  }
+
+  async function openDetail(row) {
+    detailDrawerVisible.value = true
+    detailLoading.value = true
+    try {
+      const detail = await guardRequestWithMessage(
+        fetchGetDeliveryItemDetail(row.id),
+        {},
+        {
+          timeoutMessage: t('pages.orders.deliveryItem.messages.detailTimeout')
+        }
+      )
+      detailData.value = normalizeDeliveryItemRow(detail, t)
+    } catch (error) {
+      detailDrawerVisible.value = false
+      detailData.value = {}
+      ElMessage.error(error?.message || t('pages.orders.deliveryItem.messages.detailFailed'))
+    } finally {
+      detailLoading.value = false
+    }
+  }
+
+  async function openEditDialog(row) {
+    dialogType.value = 'edit'
+    currentItemData.value = buildDeliveryItemDialogModel(row)
+    dialogVisible.value = true
+  }
+
+  function openCreateDialog() {
+    if (!canCreateItem.value) {
+      return
+    }
+    dialogType.value = 'add'
+    currentItemData.value = buildDeliveryItemDialogModel({
+      deliveryId: resolvedDeliveryId.value
+    })
+    dialogVisible.value = true
+  }
+
+  async function handleDelete(row) {
+    await ElMessageBox.confirm(
+      t('pages.orders.deliveryItem.messages.deleteConfirm', {
+        code: row.platItemId || row.id || ''
+      }),
+      t('crud.confirm.deleteTitle'),
+      {
+        confirmButtonText: t('common.confirm'),
+        cancelButtonText: t('common.cancel'),
+        type: 'warning'
+      }
+    )
+    await fetchDeleteDeliveryItem(row.id)
+    ElMessage.success(t('crud.messages.deleteSuccess'))
+    await refreshData()
+    emit('changed')
+  }
+
+  async function handleBatchDelete() {
+    if (!selectedRows.value.length) {
+      return
+    }
+    await ElMessageBox.confirm(
+      t('crud.confirm.batchDeleteMessage', {
+        count: selectedRows.value.length,
+        entity: t('menu.deliveryItem')
+      }),
+      t('crud.confirm.batchDeleteTitle'),
+      {
+        confirmButtonText: t('common.confirm'),
+        cancelButtonText: t('common.cancel'),
+        type: 'warning'
+      }
+    )
+    await fetchDeleteDeliveryItemMany(selectedRows.value.map((item) => item.id))
+    ElMessage.success(t('crud.messages.batchDeleteSuccess'))
+    selectedRows.value = []
+    await refreshData()
+    emit('changed')
+  }
+
+  async function handleActionClick(action, row) {
+    try {
+      if (action.key === 'view') {
+        await openDetail(row)
+        return
+      }
+      if (action.key === 'edit') {
+        await openEditDialog(row)
+        return
+      }
+      if (action.key === 'delete') {
+        await handleDelete(row)
+      }
+    } catch (error) {
+      if (error === 'cancel' || error?.message === 'cancel') {
+        return
+      }
+      ElMessage.error(error?.message || t('pages.orders.deliveryItem.messages.actionFailed'))
+    }
+  }
+
+  async function handleDialogSubmit(payload) {
+    submitLoading.value = true
+    try {
+      const requestPayload = buildDeliveryItemSavePayload({
+        ...payload,
+        deliveryId: payload.deliveryId ?? resolvedDeliveryId.value
+      })
+      if (dialogType.value === 'edit') {
+        await fetchUpdateDeliveryItem(requestPayload)
+        ElMessage.success(t('pages.orders.deliveryItem.messages.updateSuccess'))
+      } else {
+        await fetchSaveDeliveryItem(requestPayload)
+        ElMessage.success(t('pages.orders.deliveryItem.messages.createSuccess'))
+      }
+      dialogVisible.value = false
+      await refreshData()
+      emit('changed')
+    } catch (error) {
+      ElMessage.error(
+        error?.message ||
+          (dialogType.value === 'edit'
+            ? t('pages.orders.deliveryItem.messages.updateFailed')
+            : t('pages.orders.deliveryItem.messages.createFailed'))
+      )
+    } finally {
+      submitLoading.value = false
+    }
+  }
+
+  async function reload() {
+    syncDeliveryFilter()
+    replaceSearchParams(buildDeliveryItemPageQueryParams(searchForm.value))
+    await getData()
+  }
+
+  watch(
+    () => props.deliveryId,
+    () => {
+      syncDeliveryFilter()
+      replaceSearchParams(buildDeliveryItemPageQueryParams(searchForm.value))
+      getData()
+    },
+    { immediate: true }
+  )
+
+  defineExpose({
+    reload
+  })
+</script>
diff --git a/rsf-design/src/views/orders/delivery/deliveryPage.helpers.js b/rsf-design/src/views/orders/delivery/deliveryPage.helpers.js
index 9b61115..2c99bcb 100644
--- a/rsf-design/src/views/orders/delivery/deliveryPage.helpers.js
+++ b/rsf-design/src/views/orders/delivery/deliveryPage.helpers.js
@@ -60,23 +60,51 @@
     }
     return fallback
   }
-  return deliveryExceStatusMeta[Number(exceStatus)] || {
-    text: normalizeText(exceStatus) || '--',
-    type: 'info'
-  }
+  return (
+    deliveryExceStatusMeta[Number(exceStatus)] || {
+      text: normalizeText(exceStatus) || '--',
+      type: 'info'
+    }
+  )
 }
 
 export function createDeliverySearchState() {
   return {
     condition: '',
+    timeStart: '',
+    timeEnd: '',
     code: '',
     platId: '',
     type: '',
     wkType: '',
     source: '',
+    anfme: '',
+    qty: '',
+    workQty: '',
+    platCode: '',
+    startTime: '',
+    endTime: '',
+    status: '',
     exceStatus: '',
-    memo: ''
+    memo: '',
+    orderBy: 'create_time desc'
   }
+}
+
+export function getDeliveryStatusOptions(t = $t) {
+  return [
+    { value: 1, label: t('pages.orders.delivery.status.normal') },
+    { value: 0, label: t('pages.orders.delivery.status.disabled') }
+  ]
+}
+
+export function getDeliveryExceStatusOptions(t = $t) {
+  return [
+    { value: 0, label: t('pages.orders.delivery.status.pending') },
+    { value: 1, label: t('pages.orders.delivery.status.running') },
+    { value: 2, label: t('pages.orders.delivery.status.partial') },
+    { value: 3, label: t('pages.orders.delivery.status.completed') }
+  ]
 }
 
 export function getDeliveryPaginationKey() {
@@ -88,10 +116,17 @@
 
 export function buildDeliverySearchParams(params = {}) {
   const result = {}
-  ;['condition', 'code', 'platId', 'type', 'wkType', 'source', 'memo'].forEach((key) => {
-    const value = normalizeText(params[key])
-    if (value) {
-      result[key] = value
+  ;['condition', 'code', 'platId', 'type', 'wkType', 'source', 'platCode', 'memo'].forEach(
+    (key) => {
+      const value = normalizeText(params[key])
+      if (value) {
+        result[key] = value
+      }
+    }
+  )
+  ;['anfme', 'qty', 'workQty'].forEach((key) => {
+    if (params[key] !== '' && params[key] !== undefined && params[key] !== null) {
+      result[key] = normalizeNumber(params[key])
     }
   })
 
@@ -111,6 +146,14 @@
     result.timeEnd = normalizeText(params.timeEnd)
   }
 
+  if (params.startTime !== '' && params.startTime !== undefined && params.startTime !== null) {
+    result.startTime = normalizeText(params.startTime)
+  }
+
+  if (params.endTime !== '' && params.endTime !== undefined && params.endTime !== null) {
+    result.endTime = normalizeText(params.endTime)
+  }
+
   return result
 }
 
@@ -118,6 +161,7 @@
   return {
     current: params.current || 1,
     pageSize: params.pageSize || params.size || 20,
+    orderBy: normalizeText(params.orderBy) || 'create_time desc',
     ...buildDeliverySearchParams(params)
   }
 }
@@ -132,7 +176,11 @@
 
 export function normalizeDeliveryRow(record = {}, t = $t) {
   const statusMeta = normalizeStatusMeta(record.statusBool ?? record.status, t)
-  const exceStatusMeta = normalizeExceStatusMeta(record.exceStatus, record['exceStatus$'] || record.exceStatusText, t)
+  const exceStatusMeta = normalizeExceStatusMeta(
+    record.exceStatus,
+    record['exceStatus$'] || record.exceStatusText,
+    t
+  )
   return {
     ...record,
     id: record.id ?? null,
@@ -145,17 +193,21 @@
     statusText: statusMeta.text,
     statusType: statusMeta.type,
     statusBool: record.statusBool !== void 0 ? Boolean(record.statusBool) : statusMeta.bool,
-    exceStatusText: normalizeText(record['exceStatus$'] || record.exceStatusText) || exceStatusMeta.text,
+    exceStatusText:
+      normalizeText(record['exceStatus$'] || record.exceStatusText) || exceStatusMeta.text,
     exceStatusTagType: exceStatusMeta.type,
     anfme: record.anfme ?? '--',
     qty: record.qty ?? '--',
     workQty: record.workQty ?? '--',
-    startTimeText: normalizeText(record['startTime$'] || record.startTimeText || record.startTime) || '--',
+    startTimeText:
+      normalizeText(record['startTime$'] || record.startTimeText || record.startTime) || '--',
     endTimeText: normalizeText(record['endTime$'] || record.endTimeText || record.endTime) || '--',
     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) || '--'
   }
 }
@@ -187,17 +239,17 @@
     batch: normalizeText(record.batch) || '--',
     trackCode: normalizeText(record.trackCode) || '--',
     packName: normalizeText(record.packName) || '--',
-    prodTimeText: normalizeText(record['prodTime$'] || record.prodTimeText || record.prodTime) || '--',
+    prodTimeText:
+      normalizeText(record['prodTime$'] || record.prodTimeText || record.prodTime) || '--',
     statusText: statusMeta.text,
     statusType: statusMeta.type,
-    statusBool:
-      record.statusBool !== void 0
-        ? Boolean(record.statusBool)
-        : statusMeta.bool,
+    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) || '--'
   }
 }
@@ -254,22 +306,32 @@
   }
 }
 
-export function getDeliveryActionList(row = {}) {
+export function getDeliveryActionList(row = {}, options = {}) {
   const normalizedRow = normalizeDeliveryRow(row)
+  const { canEdit = true, canDelete = true } = options
   const actions = [
     {
       key: 'view',
       label: $t('pages.orders.delivery.actions.view'),
       icon: 'ri:eye-line'
-    },
-    {
-      key: 'items',
-      label: $t('pages.orders.delivery.actions.items'),
-      icon: 'ri:list-check-3'
     }
   ]
 
-  if (Number(normalizedRow.exceStatus) === 0) {
+  if (canEdit) {
+    actions.push({
+      key: 'edit',
+      label: $t('pages.orders.delivery.actions.edit'),
+      icon: 'ri:edit-line'
+    })
+  }
+
+  actions.push({
+    key: 'items',
+    label: $t('pages.orders.delivery.actions.items'),
+    icon: 'ri:list-check-3'
+  })
+
+  if (canDelete && Number(normalizedRow.exceStatus) === 0) {
     actions.push({
       key: 'delete',
       label: $t('pages.orders.delivery.actions.delete'),
diff --git a/rsf-design/src/views/orders/delivery/deliveryTable.columns.js b/rsf-design/src/views/orders/delivery/deliveryTable.columns.js
index 7a07112..7cae065 100644
--- a/rsf-design/src/views/orders/delivery/deliveryTable.columns.js
+++ b/rsf-design/src/views/orders/delivery/deliveryTable.columns.js
@@ -4,10 +4,22 @@
 import ArtButtonMore from '@/components/core/forms/art-button-more/index.vue'
 import { getDeliveryActionList } from './deliveryPage.helpers.js'
 
-export function createDeliveryTableColumns({ handleActionClick } = {}) {
+export function createDeliveryTableColumns({
+  handleActionClick,
+  canEdit = true,
+  canDelete = true
+} = {}) {
   return [
     { 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: 'right',
+      visible: false,
+      formatter: (row) => row.id ?? '--'
+    },
     {
       prop: 'code',
       label: $t('pages.orders.delivery.search.code'),
@@ -73,7 +85,7 @@
     },
     {
       prop: 'status',
-      label: $t('pages.orders.transfer.search.status'),
+      label: $t('table.status'),
       width: 96,
       align: 'center',
       formatter: (row) =>
@@ -85,7 +97,11 @@
       minWidth: 120,
       showOverflowTooltip: true,
       formatter: (row) =>
-        h(ElTag, { type: row.exceStatusTagType || 'info', effect: 'light' }, () => row.exceStatusText || '--')
+        h(
+          ElTag,
+          { type: row.exceStatusTagType || 'info', effect: 'light' },
+          () => row.exceStatusText || '--'
+        )
     },
     {
       prop: 'startTimeText',
@@ -102,6 +118,38 @@
       formatter: (row) => row.endTimeText || '--'
     },
     {
+      prop: 'updateByText',
+      label: $t('table.updateBy'),
+      minWidth: 130,
+      visible: false,
+      showOverflowTooltip: true,
+      formatter: (row) => row.updateByText || '--'
+    },
+    {
+      prop: 'updateTimeText',
+      label: $t('table.updateTime'),
+      minWidth: 170,
+      visible: false,
+      showOverflowTooltip: true,
+      formatter: (row) => row.updateTimeText || '--'
+    },
+    {
+      prop: 'createByText',
+      label: $t('table.createBy'),
+      minWidth: 130,
+      visible: false,
+      showOverflowTooltip: true,
+      formatter: (row) => row.createByText || '--'
+    },
+    {
+      prop: 'createTimeText',
+      label: $t('table.createTime'),
+      minWidth: 170,
+      visible: false,
+      showOverflowTooltip: true,
+      formatter: (row) => row.createTimeText || '--'
+    },
+    {
       prop: 'memo',
       label: $t('pages.orders.delivery.search.memo'),
       minWidth: 180,
@@ -116,7 +164,7 @@
       fixed: 'right',
       formatter: (row) =>
         h(ArtButtonMore, {
-          list: getDeliveryActionList(row),
+          list: getDeliveryActionList(row, { canEdit, canDelete }),
           onClick: (item) => handleActionClick?.(item, row)
         })
     }
diff --git a/rsf-design/src/views/orders/delivery/index.vue b/rsf-design/src/views/orders/delivery/index.vue
index f679705..3086179 100644
--- a/rsf-design/src/views/orders/delivery/index.vue
+++ b/rsf-design/src/views/orders/delivery/index.vue
@@ -11,21 +11,50 @@
     <ElCard class="art-table-card">
       <ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
         <template #left>
-          <ListExportPrint
-            class="inline-flex"
-            :preview-visible="previewVisible"
-            @update:previewVisible="handlePreviewVisibleChange"
-            :report-title="reportTitle"
-            :selected-rows="selectedRows"
-            :query-params="reportQueryParams"
-            :columns="columns"
-            :preview-rows="previewRows"
-            :preview-meta="resolvedPreviewMeta"
-            :total="pagination.total"
-            :disabled="loading"
-            @export="handleExport"
-            @print="handlePrint"
-          />
+          <ElSpace wrap>
+            <ElUpload
+              v-auth="'update'"
+              :auto-upload="false"
+              :show-file-list="false"
+              accept=".xlsx,.xls"
+              @change="handleImportFileChange"
+            >
+              <ElButton :loading="importing">{{
+                t('pages.orders.delivery.buttons.import')
+              }}</ElButton>
+            </ElUpload>
+            <ElButton
+              v-auth="'update'"
+              :loading="templateDownloading"
+              @click="handleDownloadTemplate"
+            >
+              {{ t('pages.orders.delivery.buttons.downloadTemplate') }}
+            </ElButton>
+            <ElButton
+              v-auth="'delete'"
+              type="danger"
+              plain
+              :disabled="selectedRows.length === 0"
+              @click="handleBatchDelete"
+            >
+              {{ t('common.actions.batchDelete') }}
+            </ElButton>
+            <ListExportPrint
+              class="inline-flex"
+              :preview-visible="previewVisible"
+              @update:previewVisible="handlePreviewVisibleChange"
+              :report-title="reportTitle"
+              :selected-rows="selectedRows"
+              :query-params="reportQueryParams"
+              :columns="columns"
+              :preview-rows="previewRows"
+              :preview-meta="resolvedPreviewMeta"
+              :total="pagination.total"
+              :disabled="loading"
+              @export="handleExport"
+              @print="handlePrint"
+            />
+          </ElSpace>
         </template>
       </ArtTableHeader>
 
@@ -50,15 +79,24 @@
       @size-change="handleDetailSizeChange"
       @current-change="handleDetailCurrentChange"
     />
+
+    <DeliveryManageDialog
+      v-model:visible="manageDialogVisible"
+      :delivery="manageDeliveryData"
+      :can-add="hasAuth('update')"
+      :can-edit="hasAuth('update')"
+      :can-delete="hasAuth('delete')"
+      @changed="handleManageChanged"
+    />
   </div>
 </template>
 
 <script setup>
   import { computed, reactive, ref } from 'vue'
-  import { useRouter } from 'vue-router'
   import { useI18n } from 'vue-i18n'
   import { ElMessage, ElMessageBox } from 'element-plus'
   import { useUserStore } from '@/store/modules/user'
+  import { useAuth } from '@/hooks/core/useAuth'
   import { useTable } from '@/hooks/core/useTable'
   import { usePrintExportPage } from '@/views/system/common/usePrintExportPage'
   import ListExportPrint from '@/components/biz/list-export-print/index.vue'
@@ -73,24 +111,29 @@
     createDeliverySearchState,
     getDeliveryReportTitle,
     getDeliveryPaginationKey,
+    getDeliveryStatusOptions,
+    getDeliveryExceStatusOptions,
     normalizeDeliveryItemRow,
     normalizeDeliveryRow
   } from './deliveryPage.helpers.js'
   import {
-    fetchDeleteDelivery,
+    fetchDeleteDeliveryMany,
     fetchDeliveryItemPage,
     fetchDeliveryPage,
+    fetchDownloadDeliveryTemplate,
     fetchExportDeliveryReport,
     fetchGetDeliveryDetail,
     fetchGetDeliveryMany
   } from '@/api/delivery'
+  import { fetchImportDelivery } from '@/api/delivery'
   import DeliveryDetailDrawer from './modules/delivery-detail-drawer.vue'
+  import DeliveryManageDialog from './modules/delivery-manage-dialog.vue'
   import { createDeliveryTableColumns } from './deliveryTable.columns.js'
 
   defineOptions({ name: 'Delivery' })
 
   const userStore = useUserStore()
-  const router = useRouter()
+  const { hasAuth } = useAuth()
   const { t } = useI18n()
   const reportTitle = computed(() => getDeliveryReportTitle(t))
   const searchForm = ref(createDeliverySearchState())
@@ -101,6 +144,10 @@
   const detailData = ref({})
   const detailItemRows = ref([])
   const activeDeliveryId = ref(null)
+  const importing = ref(false)
+  const templateDownloading = ref(false)
+  const manageDialogVisible = ref(false)
+  const manageDeliveryData = ref({})
   const detailItemPagination = reactive({
     current: 1,
     size: 20,
@@ -116,6 +163,26 @@
       props: {
         clearable: true,
         placeholder: t('pages.orders.delivery.placeholder.condition')
+      }
+    },
+    {
+      label: t('pages.orders.delivery.search.timeStart'),
+      key: 'timeStart',
+      type: 'date',
+      props: {
+        clearable: true,
+        valueFormat: 'YYYY-MM-DD',
+        placeholder: t('pages.orders.delivery.placeholder.timeStart')
+      }
+    },
+    {
+      label: t('pages.orders.delivery.search.timeEnd'),
+      key: 'timeEnd',
+      type: 'date',
+      props: {
+        clearable: true,
+        valueFormat: 'YYYY-MM-DD',
+        placeholder: t('pages.orders.delivery.placeholder.timeEnd')
       }
     },
     {
@@ -164,11 +231,84 @@
       }
     },
     {
-      label: t('pages.orders.delivery.search.exceStatus'),
-      key: 'exceStatus',
+      label: t('pages.orders.delivery.search.anfme'),
+      key: 'anfme',
+      type: 'number',
+      props: {
+        min: 0,
+        precision: 2,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.delivery.placeholder.anfme')
+      }
+    },
+    {
+      label: t('pages.orders.delivery.search.qty'),
+      key: 'qty',
+      type: 'number',
+      props: {
+        min: 0,
+        precision: 2,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.delivery.placeholder.qty')
+      }
+    },
+    {
+      label: t('pages.orders.delivery.search.workQty'),
+      key: 'workQty',
+      type: 'number',
+      props: {
+        min: 0,
+        precision: 2,
+        controlsPosition: 'right',
+        placeholder: t('pages.orders.delivery.placeholder.workQty')
+      }
+    },
+    {
+      label: t('pages.orders.delivery.search.platCode'),
+      key: 'platCode',
       type: 'input',
       props: {
         clearable: true,
+        placeholder: t('pages.orders.delivery.placeholder.platCode')
+      }
+    },
+    {
+      label: t('pages.orders.delivery.search.startTime'),
+      key: 'startTime',
+      type: 'date',
+      props: {
+        clearable: true,
+        valueFormat: 'YYYY-MM-DD',
+        placeholder: t('pages.orders.delivery.placeholder.startTime')
+      }
+    },
+    {
+      label: t('pages.orders.delivery.search.endTime'),
+      key: 'endTime',
+      type: 'date',
+      props: {
+        clearable: true,
+        valueFormat: 'YYYY-MM-DD',
+        placeholder: t('pages.orders.delivery.placeholder.endTime')
+      }
+    },
+    {
+      label: t('table.status'),
+      key: 'status',
+      type: 'select',
+      props: {
+        clearable: true,
+        options: getDeliveryStatusOptions(t),
+        placeholder: t('pages.orders.delivery.placeholder.status')
+      }
+    },
+    {
+      label: t('pages.orders.delivery.search.exceStatus'),
+      key: 'exceStatus',
+      type: 'select',
+      props: {
+        clearable: true,
+        options: getDeliveryExceStatusOptions(t),
         placeholder: t('pages.orders.delivery.placeholder.exceStatus')
       }
     },
@@ -208,9 +348,13 @@
         { timeoutMessage: t('pages.orders.delivery.messages.itemsTimeout') }
       )
       const normalizedResponse = defaultResponseAdapter(response)
-      detailItemRows.value = normalizedResponse.records.map((item) => normalizeDeliveryItemRow(item, t))
+      detailItemRows.value = normalizedResponse.records.map((item) =>
+        normalizeDeliveryItemRow(item, t)
+      )
       detailItemPagination.total = Number(normalizedResponse.total || 0)
-      detailItemPagination.current = Number(normalizedResponse.current || detailItemPagination.current || 1)
+      detailItemPagination.current = Number(
+        normalizedResponse.current || detailItemPagination.current || 1
+      )
       detailItemPagination.size = Number(normalizedResponse.size || detailItemPagination.size || 20)
     } finally {
       detailItemsLoading.value = false
@@ -248,51 +392,75 @@
   }
 
   async function handleDelete(row) {
-    try {
-      await ElMessageBox.confirm(
-        t('crud.confirm.deleteMessage', {
-          entity: t('pages.orders.delivery.entity'),
-          label: row.code || row.id
-        }),
-        t('crud.confirm.deleteTitle'),
-        {
-          confirmButtonText: t('common.confirm'),
-          cancelButtonText: t('common.cancel'),
-          type: 'warning'
-        }
-      )
-      await fetchDeleteDelivery(row.id)
-      ElMessage.success(t('crud.messages.deleteSuccess'))
-      await refreshRemove()
-    } catch (error) {
-      if (error !== 'cancel') {
-        ElMessage.error(error?.message || t('crud.messages.deleteFailed'))
+    await ElMessageBox.confirm(
+      t('crud.confirm.deleteMessage', {
+        entity: t('pages.orders.delivery.entity'),
+        label: row.code || row.id
+      }),
+      t('crud.confirm.deleteTitle'),
+      {
+        confirmButtonText: t('common.confirm'),
+        cancelButtonText: t('common.cancel'),
+        type: 'warning'
       }
-    }
+    )
+    await fetchDeleteDeliveryMany([row.id])
+    ElMessage.success(t('crud.messages.deleteSuccess'))
+    await refreshRemove()
   }
 
-  function handleTableActionClick(action, row) {
+  function openManageDialog(row) {
+    manageDeliveryData.value = normalizeDeliveryRow(row, t)
+    manageDialogVisible.value = true
+  }
+
+  async function handleBatchDelete() {
+    if (!selectedRows.value.length) {
+      return
+    }
+    await ElMessageBox.confirm(
+      t('crud.confirm.batchDeleteMessage', {
+        count: selectedRows.value.length,
+        entity: t('pages.orders.delivery.entity')
+      }),
+      t('crud.confirm.batchDeleteTitle'),
+      {
+        confirmButtonText: t('common.confirm'),
+        cancelButtonText: t('common.cancel'),
+        type: 'warning'
+      }
+    )
+    await fetchDeleteDeliveryMany(selectedRows.value.map((item) => item.id))
+    ElMessage.success(t('crud.messages.batchDeleteSuccess'))
+    selectedRows.value = []
+    await refreshData()
+  }
+
+  async function handleTableActionClick(action, row) {
     if (!action) {
       return
     }
     if (action.key === 'view') {
-      openDetail(row)
+      await openDetail(row)
+      return
+    }
+    if (action.key === 'edit') {
+      openManageDialog(row)
       return
     }
     if (action.key === 'items') {
-      if (!row?.id) {
-        return
-      }
-      router.push({
-        path: '/orders/delivery-item',
-        query: {
-          deliveryId: String(row.id)
-        }
-      })
+      openManageDialog(row)
       return
     }
     if (action.key === 'delete') {
-      handleDelete(row)
+      try {
+        await handleDelete(row)
+      } catch (error) {
+        if (error === 'cancel' || error?.message === 'cancel') {
+          return
+        }
+        ElMessage.error(error?.message || t('crud.messages.deleteFailed'))
+      }
     }
   }
 
@@ -317,10 +485,16 @@
         pageSize: 20
       }),
       paginationKey: getDeliveryPaginationKey(),
-      columnsFactory: () => createDeliveryTableColumns({ handleActionClick: handleTableActionClick })
+      columnsFactory: () =>
+        createDeliveryTableColumns({
+          handleActionClick: handleTableActionClick,
+          canEdit: hasAuth('update'),
+          canDelete: hasAuth('delete')
+        })
     },
     transform: {
-      dataTransformer: (records) => (Array.isArray(records) ? records.map((item) => normalizeDeliveryRow(item, t)) : [])
+      dataTransformer: (records) =>
+        Array.isArray(records) ? records.map((item) => normalizeDeliveryRow(item, t)) : []
     }
   })
 
@@ -338,6 +512,69 @@
     resetSearchParams()
   }
 
+  async function handleManageChanged() {
+    await refreshData()
+    if (detailDrawerVisible.value && activeDeliveryId.value) {
+      await loadDetailItems(activeDeliveryId.value)
+    }
+  }
+
+  async function handleImportFileChange(uploadFile) {
+    if (!uploadFile?.raw) {
+      return
+    }
+    importing.value = true
+    try {
+      await fetchImportDelivery(uploadFile.raw)
+      ElMessage.success(t('pages.orders.delivery.messages.importSuccess'))
+      await refreshData()
+    } catch (error) {
+      ElMessage.error(error?.message || t('pages.orders.delivery.messages.importFailed'))
+    } finally {
+      importing.value = false
+    }
+  }
+
+  async function downloadFile(response, fallbackName) {
+    const blob = await response.blob()
+    if (!blob || !blob.size) {
+      throw new Error(t('pages.orders.delivery.messages.templateDownloadFailed'))
+    }
+    const disposition = response.headers.get('Content-Disposition') || ''
+    const matchedName =
+      disposition.match(/filename\*=UTF-8''([^;]+)/i)?.[1] ||
+      disposition.match(/filename="?([^";]+)"?/i)?.[1]
+    const fileName = matchedName ? decodeURIComponent(matchedName) : fallbackName
+    const url = URL.createObjectURL(blob)
+    const anchor = document.createElement('a')
+    anchor.href = url
+    anchor.download = fileName
+    document.body.appendChild(anchor)
+    anchor.click()
+    anchor.remove()
+    URL.revokeObjectURL(url)
+  }
+
+  async function handleDownloadTemplate() {
+    templateDownloading.value = true
+    try {
+      const response = await fetchDownloadDeliveryTemplate(
+        {},
+        {
+          headers: {
+            Authorization: userStore.accessToken || ''
+          }
+        }
+      )
+      await downloadFile(response, 'delivery-template.xlsx')
+      ElMessage.success(t('pages.orders.delivery.messages.templateDownloadSuccess'))
+    } catch (error) {
+      ElMessage.error(error?.message || t('pages.orders.delivery.messages.templateDownloadFailed'))
+    } finally {
+      templateDownloading.value = false
+    }
+  }
+
   const resolvePrintRecords = async (payload) => {
     if (Array.isArray(payload?.ids) && payload.ids.length > 0) {
       return defaultResponseAdapter(await fetchGetDeliveryMany(payload.ids)).records
@@ -346,7 +583,8 @@
       await fetchDeliveryPage({
         ...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
   }
diff --git a/rsf-design/src/views/orders/delivery/modules/delivery-manage-dialog.vue b/rsf-design/src/views/orders/delivery/modules/delivery-manage-dialog.vue
new file mode 100644
index 0000000..e76c6a0
--- /dev/null
+++ b/rsf-design/src/views/orders/delivery/modules/delivery-manage-dialog.vue
@@ -0,0 +1,86 @@
+<template>
+  <ElDialog
+    :model-value="visible"
+    :title="t('pages.orders.delivery.manage.title')"
+    width="94%"
+    top="4vh"
+    destroy-on-close
+    @update:model-value="handleVisibleChange"
+  >
+    <div class="flex flex-col gap-4">
+      <ElDescriptions
+        :title="t('pages.orders.delivery.manage.baseInfo')"
+        :column="3"
+        border
+        size="small"
+      >
+        <ElDescriptionsItem :label="t('pages.orders.delivery.detail.code')">{{
+          delivery.code || '--'
+        }}</ElDescriptionsItem>
+        <ElDescriptionsItem :label="t('pages.orders.delivery.detail.platId')">{{
+          delivery.platId || '--'
+        }}</ElDescriptionsItem>
+        <ElDescriptionsItem :label="t('pages.orders.delivery.detail.platCode')">{{
+          delivery.platCode || '--'
+        }}</ElDescriptionsItem>
+        <ElDescriptionsItem :label="t('pages.orders.delivery.detail.type')">{{
+          delivery.typeLabel || '--'
+        }}</ElDescriptionsItem>
+        <ElDescriptionsItem :label="t('pages.orders.delivery.detail.wkType')">{{
+          delivery.wkTypeLabel || '--'
+        }}</ElDescriptionsItem>
+        <ElDescriptionsItem :label="t('pages.orders.delivery.detail.source')">{{
+          delivery.source || '--'
+        }}</ElDescriptionsItem>
+        <ElDescriptionsItem :label="t('pages.orders.delivery.detail.anfme')">{{
+          delivery.anfme ?? '--'
+        }}</ElDescriptionsItem>
+        <ElDescriptionsItem :label="t('pages.orders.delivery.detail.qty')">{{
+          delivery.qty ?? '--'
+        }}</ElDescriptionsItem>
+        <ElDescriptionsItem :label="t('pages.orders.delivery.detail.workQty')">{{
+          delivery.workQty ?? '--'
+        }}</ElDescriptionsItem>
+        <ElDescriptionsItem :label="t('pages.orders.delivery.detail.startTime')">{{
+          delivery.startTimeText || '--'
+        }}</ElDescriptionsItem>
+        <ElDescriptionsItem :label="t('pages.orders.delivery.detail.endTime')">{{
+          delivery.endTimeText || '--'
+        }}</ElDescriptionsItem>
+        <ElDescriptionsItem :label="t('pages.orders.delivery.detail.memo')">{{
+          delivery.memo || '--'
+        }}</ElDescriptionsItem>
+      </ElDescriptions>
+
+      <DeliveryItemManagePanel
+        :delivery-id="delivery.id"
+        :can-add="canAdd"
+        :can-edit="canEdit"
+        :can-delete="canDelete"
+        @changed="emit('changed')"
+      />
+    </div>
+  </ElDialog>
+</template>
+
+<script setup>
+  import { useI18n } from 'vue-i18n'
+  import DeliveryItemManagePanel from '@/views/orders/delivery-item/modules/delivery-item-manage-panel.vue'
+
+  defineOptions({ name: 'DeliveryManageDialog' })
+
+  defineProps({
+    visible: { type: Boolean, default: false },
+    delivery: { type: Object, default: () => ({}) },
+    canAdd: { type: Boolean, default: true },
+    canEdit: { type: Boolean, default: true },
+    canDelete: { type: Boolean, default: true }
+  })
+
+  const emit = defineEmits(['update:visible', 'changed'])
+  const { t } = useI18n()
+
+  function handleVisibleChange(value) {
+    emit('update:visible', value)
+  }
+</script>
diff --git a/rsf-design/src/views/orders/preparation-item/index.vue b/rsf-design/src/views/orders/preparation-item/index.vue
index 2631793..750f3f2 100644
--- a/rsf-design/src/views/orders/preparation-item/index.vue
+++ b/rsf-design/src/views/orders/preparation-item/index.vue
@@ -21,21 +21,34 @@
     <ElCard class="art-table-card">
       <ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
         <template #left>
-          <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="resolvedPreviewMeta"
-            :total="pagination.total"
-            :disabled="loading"
-            @export="handleExport"
-            @print="handlePrint"
-          />
+          <ElSpace wrap>
+            <ElButton type="primary" :disabled="!canCreateItem" @click="openCreateDialog">
+              鏂板鏄庣粏
+            </ElButton>
+            <ElButton
+              type="danger"
+              plain
+              :disabled="!selectedRows.length || !canDelete"
+              @click="handleBatchDelete"
+            >
+              鎵归噺鍒犻櫎
+            </ElButton>
+            <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="resolvedPreviewMeta"
+              :total="pagination.total"
+              :disabled="loading"
+              @export="handleExport"
+              @print="handlePrint"
+            />
+          </ElSpace>
         </template>
       </ArtTableHeader>
 
@@ -55,13 +68,23 @@
       :loading="detailLoading"
       :detail="detailData"
     />
+
+    <PreparationItemDialog
+      v-model:visible="dialogVisible"
+      :dialog-type="dialogType"
+      :item-data="currentItemData"
+      :order-id="resolvedOrderId"
+      :submit-loading="submitLoading"
+      @submit="handleDialogSubmit"
+    />
   </div>
 </template>
 
 <script setup>
   import { computed, onMounted, ref, watch } from 'vue'
   import { useRoute, useRouter } from 'vue-router'
-  import { ElButton, ElMessage } from 'element-plus'
+  import { ElButton, ElMessage, ElMessageBox, ElSpace } from 'element-plus'
+  import { useAuth } from '@/hooks/core/useAuth'
   import { useUserStore } from '@/store/modules/user'
   import { useTable } from '@/hooks/core/useTable'
   import { defaultResponseAdapter } from '@/utils/table/tableUtils'
@@ -69,20 +92,27 @@
   import { usePrintExportPage } from '@/views/system/common/usePrintExportPage'
   import ListExportPrint from '@/components/biz/list-export-print/index.vue'
   import {
+    fetchDeletePreparationItem,
     fetchExportPreparationItemReport,
     fetchGetPreparationItemDetail,
     fetchGetPreparationItemMany,
-    fetchPreparationItemPage
+    fetchPreparationItemPage,
+    fetchSavePreparationItem,
+    fetchUpdatePreparationItem
   } from '@/api/preparation-item'
-  import { createOutStockItemTableColumns } from '../out-stock-item/outStockItemTable.columns.js'
   import OutStockItemDetailDrawer from '../out-stock-item/modules/out-stock-item-detail-drawer.vue'
+  import PreparationItemDialog from './modules/preparation-item-dialog.vue'
+  import { createPreparationItemTableColumns } from './preparationItemTable.columns.js'
   import {
     PREPARATION_ITEM_REPORT_STYLE,
     PREPARATION_ITEM_REPORT_TITLE,
+    buildPreparationItemDialogModel,
     buildPreparationItemPageQueryParams,
     buildPreparationItemPrintRows,
     buildPreparationItemReportMeta,
+    buildPreparationItemSavePayload,
     buildPreparationItemSearchParams,
+    createPreparationItemFormState,
     createPreparationItemSearchState,
     getPreparationItemPaginationKey,
     getPreparationItemReportColumns,
@@ -93,6 +123,7 @@
 
   const route = useRoute()
   const router = useRouter()
+  const { hasAuth } = useAuth()
   const userStore = useUserStore()
   const initialOrderId = route.query.orderId || route.query.id
   const searchForm = ref(
@@ -104,126 +135,163 @@
   const detailLoading = ref(false)
   const detailData = ref({})
   const selectedRows = ref([])
+  const dialogVisible = ref(false)
+  const dialogType = ref('add')
+  const currentItemData = ref(createPreparationItemFormState())
+  const submitLoading = ref(false)
   const reportTitle = PREPARATION_ITEM_REPORT_TITLE
   const reportColumns = getPreparationItemReportColumns()
   const reportQueryParams = computed(() => buildPreparationItemSearchParams(searchForm.value))
-  const activeSourceSummary = computed(() => {
+  const resolvedOrderId = computed(() => {
     if (
       searchForm.value.orderId === '' ||
       searchForm.value.orderId === undefined ||
       searchForm.value.orderId === null
     ) {
-      return null
+      return undefined
     }
-    return {
-      orderId: searchForm.value.orderId
-    }
+    const numericValue = Number(searchForm.value.orderId)
+    return Number.isFinite(numericValue) ? numericValue : undefined
   })
+  const canUpdate = computed(() => hasAuth('update'))
+  const canDelete = computed(() => hasAuth('delete'))
+  const canCreateItem = computed(() => hasAuth('add') && resolvedOrderId.value !== undefined)
+  const activeSourceSummary = computed(() =>
+    resolvedOrderId.value === undefined
+      ? null
+      : {
+          orderId: resolvedOrderId.value
+        }
+  )
 
   const searchItems = computed(() => [
     {
       label: '鍏抽敭瀛�',
       key: 'condition',
       type: 'input',
-      props: {
-        clearable: true,
-        placeholder: '璇疯緭鍏ュ鏂欏崟鍙�/鐗╂枡缂栫爜/鐗╂枡鍚嶇О'
-      }
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ鏂欏崟鍙�/鐗╂枡缂栫爜/鐗╂枡鍚嶇О' }
     },
     {
       label: '澶囨枡鍗旾D',
       key: 'orderId',
       type: 'inputNumber',
-      props: {
-        clearable: true,
-        controlsPosition: 'right',
-        placeholder: '璇疯緭鍏ュ鏂欏崟ID'
-      }
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏ュ鏂欏崟ID' }
     },
     {
       label: '澶囨枡鍗曞彿',
       key: 'orderCode',
       type: 'input',
-      props: {
-        clearable: true,
-        placeholder: '璇疯緭鍏ュ鏂欏崟鍙�'
-      }
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ鏂欏崟鍙�' }
     },
     {
-      label: 'PO鍗曞彿',
-      key: 'poCode',
+      label: 'PO鏄庣粏ID',
+      key: 'poDetlId',
       type: 'input',
-      props: {
-        clearable: true,
-        placeholder: '璇疯緭鍏O鍗曞彿'
-      }
+      props: { clearable: true, placeholder: '璇疯緭鍏� PO 鏄庣粏ID' }
     },
     {
-      label: '鐗╂枡缂栫爜',
-      key: 'matnrCode',
+      label: '鐗╂枡ID',
+      key: 'matnrId',
       type: 'input',
-      props: {
-        clearable: true,
-        placeholder: '璇疯緭鍏ョ墿鏂欑紪鐮�'
-      }
+      props: { clearable: true, placeholder: '璇疯緭鍏ョ墿鏂橧D' }
     },
     {
       label: '鐗╂枡鍚嶇О',
       key: 'maktx',
       type: 'input',
-      props: {
-        clearable: true,
-        placeholder: '璇疯緭鍏ョ墿鏂欏悕绉�'
-      }
+      props: { clearable: true, placeholder: '璇疯緭鍏ョ墿鏂欏悕绉�' }
+    },
+    {
+      label: '鐗╂枡缂栫爜',
+      key: 'matnrCode',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ョ墿鏂欑紪鐮�' }
+    },
+    {
+      label: '璁″垝鏁伴噺',
+      key: 'anfme',
+      type: 'inputNumber',
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏ヨ鍒掓暟閲�' }
+    },
+    {
+      label: '搴撳瓨鍗曚綅',
+      key: 'stockUnit',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ簱瀛樺崟浣�' }
+    },
+    {
+      label: '閲囪喘鏁伴噺',
+      key: 'purQty',
+      type: 'inputNumber',
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏ラ噰璐暟閲�' }
+    },
+    {
+      label: '閲囪喘鍗曚綅',
+      key: 'purUnit',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ラ噰璐崟浣�' }
+    },
+    {
+      label: '宸插嚭鏁伴噺',
+      key: 'qty',
+      type: 'inputNumber',
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏ュ凡鍑烘暟閲�' }
+    },
+    {
+      label: '渚涘簲鍟嗙紪鐮�',
+      key: 'splrCode',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ヤ緵搴斿晢缂栫爜' }
+    },
+    {
+      label: '渚涘簲鍟�',
+      key: 'splrName',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ヤ緵搴斿晢' }
+    },
+    {
+      label: '浜岀淮鐮�',
+      key: 'qrcode',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ヤ簩缁寸爜' }
+    },
+    {
+      label: '鏉$爜',
+      key: 'trackCode',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ユ潯鐮�' }
+    },
+    {
+      label: '鍖呰',
+      key: 'packName',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ寘瑁�' }
     },
     {
       label: '鎵规',
       key: 'batch',
       type: 'input',
-      props: {
-        clearable: true,
-        placeholder: '璇疯緭鍏ユ壒娆�'
-      }
+      props: { clearable: true, placeholder: '璇疯緭鍏ユ壒娆�' }
     },
     {
       label: '渚涘簲鍟嗘壒娆�',
       key: 'splrBatch',
       type: 'input',
-      props: {
-        clearable: true,
-        placeholder: '璇疯緭鍏ヤ緵搴斿晢鎵规'
-      }
+      props: { clearable: true, placeholder: '璇疯緭鍏ヤ緵搴斿晢鎵规' }
     },
     {
       label: '瀛楁绱㈠紩',
       key: 'fieldsIndex',
       type: 'input',
-      props: {
-        clearable: true,
-        placeholder: '璇疯緭鍏ュ瓧娈电储寮�'
-      }
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ瓧娈电储寮�' }
+    },
+    {
+      label: '澶囨敞',
+      key: 'memo',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ娉�' }
     }
   ])
-
-  async function openDetail(row) {
-    detailDrawerVisible.value = true
-    detailLoading.value = true
-    try {
-      const detail = await guardRequestWithMessage(fetchGetPreparationItemDetail(row.id), {}, {
-        timeoutMessage: '澶囨枡鍗曟槑缁嗚鎯呭姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�'
-      })
-      detailData.value = normalizePreparationItemRow({
-        ...row,
-        ...(detail || {})
-      })
-    } catch (error) {
-      detailDrawerVisible.value = false
-      detailData.value = {}
-      ElMessage.error(error?.message || '鑾峰彇澶囨枡鍗曟槑缁嗚鎯呭け璐�')
-    } finally {
-      detailLoading.value = false
-    }
-  }
 
   const {
     columns,
@@ -243,7 +311,12 @@
       apiParams: buildPreparationItemPageQueryParams(searchForm.value),
       immediate: false,
       paginationKey: getPreparationItemPaginationKey(),
-      columnsFactory: () => createOutStockItemTableColumns({ handleActionClick: openDetail })
+      columnsFactory: () =>
+        createPreparationItemTableColumns({
+          handleActionClick,
+          canEdit: canUpdate.value,
+          canDelete: canDelete.value
+        })
     },
     transform: {
       dataTransformer: (records) =>
@@ -265,10 +338,132 @@
   }
 
   function handleReset() {
-    const resetSeed =
-      initialOrderId !== undefined ? { orderId: Number(initialOrderId) || '' } : {}
+    const resetSeed = initialOrderId !== undefined ? { orderId: Number(initialOrderId) || '' } : {}
     Object.assign(searchForm.value, createPreparationItemSearchState(resetSeed))
-    resetSearchParams(buildPreparationItemPageQueryParams(createPreparationItemSearchState(resetSeed)))
+    resetSearchParams(
+      buildPreparationItemPageQueryParams(createPreparationItemSearchState(resetSeed))
+    )
+  }
+
+  async function openDetail(row) {
+    detailDrawerVisible.value = true
+    detailLoading.value = true
+    try {
+      const detail = await guardRequestWithMessage(
+        fetchGetPreparationItemDetail(row.id),
+        {},
+        {
+          timeoutMessage: '澶囨枡鍗曟槑缁嗚鎯呭姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�'
+        }
+      )
+      detailData.value = normalizePreparationItemRow({
+        ...row,
+        ...(detail || {})
+      })
+    } catch (error) {
+      detailDrawerVisible.value = false
+      detailData.value = {}
+      ElMessage.error(error?.message || '鑾峰彇澶囨枡鍗曟槑缁嗚鎯呭け璐�')
+    } finally {
+      detailLoading.value = false
+    }
+  }
+
+  function openCreateDialog() {
+    if (!canCreateItem.value) {
+      return
+    }
+    dialogType.value = 'add'
+    currentItemData.value = buildPreparationItemDialogModel({
+      orderId: resolvedOrderId.value
+    })
+    dialogVisible.value = true
+  }
+
+  function openEditDialog(row) {
+    dialogType.value = 'edit'
+    currentItemData.value = buildPreparationItemDialogModel(row)
+    dialogVisible.value = true
+  }
+
+  async function handleDelete(row) {
+    await ElMessageBox.confirm(
+      `纭畾鍒犻櫎澶囨枡鏄庣粏 ${row.matnrCode || row.id || ''} 鍚楋紵`,
+      '鍒犻櫎纭',
+      {
+        confirmButtonText: '纭畾',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }
+    )
+    await fetchDeletePreparationItem(row.id)
+    ElMessage.success('澶囨枡鏄庣粏宸插垹闄�')
+    await refreshData()
+  }
+
+  async function handleBatchDelete() {
+    if (!selectedRows.value.length) {
+      return
+    }
+    await ElMessageBox.confirm(
+      `纭畾鍒犻櫎閫変腑鐨� ${selectedRows.value.length} 鏉″鏂欐槑缁嗗悧锛焋,
+      '鎵归噺鍒犻櫎纭',
+      {
+        confirmButtonText: '纭畾',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }
+    )
+    await fetchDeletePreparationItem(selectedRows.value.map((item) => item.id))
+    ElMessage.success('澶囨枡鏄庣粏宸叉壒閲忓垹闄�')
+    selectedRows.value = []
+    await refreshData()
+  }
+
+  async function handleActionClick(action, row) {
+    try {
+      if (action.key === 'view') {
+        await openDetail(row)
+        return
+      }
+      if (action.key === 'edit') {
+        openEditDialog(row)
+        return
+      }
+      if (action.key === 'delete') {
+        await handleDelete(row)
+      }
+    } catch (error) {
+      if (error === 'cancel' || error?.message === 'cancel') {
+        return
+      }
+      ElMessage.error(error?.message || '澶囨枡鏄庣粏鎿嶄綔澶辫触')
+    }
+  }
+
+  async function handleDialogSubmit(payload) {
+    submitLoading.value = true
+    try {
+      const requestPayload = buildPreparationItemSavePayload({
+        ...payload,
+        orderId: payload.orderId ?? resolvedOrderId.value
+      })
+      if (dialogType.value === 'edit') {
+        await fetchUpdatePreparationItem(requestPayload)
+        ElMessage.success('澶囨枡鏄庣粏宸叉洿鏂�')
+      } else {
+        await fetchSavePreparationItem(requestPayload)
+        ElMessage.success('澶囨枡鏄庣粏宸叉柊澧�')
+      }
+      dialogVisible.value = false
+      await refreshData()
+    } catch (error) {
+      ElMessage.error(
+        error?.message || (dialogType.value === 'edit' ? '澶囨枡鏄庣粏鏇存柊澶辫触' : '澶囨枡鏄庣粏鏂板澶辫触')
+      )
+    } finally {
+      submitLoading.value = false
+    }
   }
 
   function applyRouteSearch() {
diff --git a/rsf-design/src/views/orders/preparation-item/modules/preparation-item-dialog.vue b/rsf-design/src/views/orders/preparation-item/modules/preparation-item-dialog.vue
new file mode 100644
index 0000000..c475541
--- /dev/null
+++ b/rsf-design/src/views/orders/preparation-item/modules/preparation-item-dialog.vue
@@ -0,0 +1,238 @@
+<template>
+  <ElDialog
+    :model-value="visible"
+    :title="dialogTitle"
+    width="900px"
+    destroy-on-close
+    @update:model-value="handleVisibleChange"
+    @closed="handleClosed"
+  >
+    <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>
+      <ElSpace>
+        <ElButton @click="handleVisibleChange(false)">鍙栨秷</ElButton>
+        <ElButton type="primary" :loading="submitLoading" @click="handleSubmit">纭畾</ElButton>
+      </ElSpace>
+    </template>
+  </ElDialog>
+</template>
+
+<script setup>
+  import { computed, nextTick, reactive, ref, watch } from 'vue'
+  import ArtForm from '@/components/core/forms/art-form/index.vue'
+  import {
+    buildPreparationItemDialogModel,
+    createPreparationItemFormState,
+    getPreparationItemStatusOptions
+  } from '../preparationItemPage.helpers.js'
+
+  const props = defineProps({
+    visible: { type: Boolean, default: false },
+    dialogType: { type: String, default: 'add' },
+    itemData: { type: Object, default: () => ({}) },
+    orderId: { type: [Number, String], default: undefined },
+    submitLoading: { type: Boolean, default: false }
+  })
+
+  const emit = defineEmits(['update:visible', 'submit'])
+
+  const formRef = ref()
+  const form = reactive(createPreparationItemFormState())
+
+  const isEdit = computed(() => props.dialogType === 'edit')
+  const dialogTitle = computed(() => (isEdit.value ? '缂栬緫澶囨枡鏄庣粏' : '鏂板澶囨枡鏄庣粏'))
+
+  const rules = computed(() => ({
+    anfme: [{ required: true, message: '璇疯緭鍏ヨ鍒掓暟閲�', trigger: 'blur' }]
+  }))
+
+  const formItems = computed(() => [
+    {
+      label: '澶囨枡鍗旾D',
+      key: 'orderId',
+      type: 'input',
+      hidden: true,
+      props: { disabled: true }
+    },
+    {
+      label: '澶囨枡鍗曞彿',
+      key: 'orderCode',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ鏂欏崟鍙�' }
+    },
+    {
+      label: 'PO鏄庣粏ID',
+      key: 'poDetlId',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏� PO 鏄庣粏ID' }
+    },
+    {
+      label: '鐗╂枡ID',
+      key: 'matnrId',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ョ墿鏂橧D' }
+    },
+    {
+      label: '鐗╂枡缂栫爜',
+      key: 'matnrCode',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ョ墿鏂欑紪鐮�' }
+    },
+    {
+      label: '鐗╂枡鍚嶇О',
+      key: 'maktx',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ョ墿鏂欏悕绉�' }
+    },
+    {
+      label: '璁″垝鏁伴噺',
+      key: 'anfme',
+      type: 'number',
+      props: { min: 0, precision: 2, controlsPosition: 'right', placeholder: '璇疯緭鍏ヨ鍒掓暟閲�' }
+    },
+    {
+      label: '搴撳瓨鍗曚綅',
+      key: 'stockUnit',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ簱瀛樺崟浣�' }
+    },
+    {
+      label: '閲囪喘鏁伴噺',
+      key: 'purQty',
+      type: 'number',
+      props: { min: 0, precision: 2, controlsPosition: 'right', placeholder: '璇疯緭鍏ラ噰璐暟閲�' }
+    },
+    {
+      label: '閲囪喘鍗曚綅',
+      key: 'purUnit',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ラ噰璐崟浣�' }
+    },
+    {
+      label: '宸插嚭鏁伴噺',
+      key: 'qty',
+      type: 'number',
+      props: { min: 0, precision: 2, controlsPosition: 'right', placeholder: '璇疯緭鍏ュ凡鍑烘暟閲�' }
+    },
+    {
+      label: '渚涘簲鍟嗙紪鐮�',
+      key: 'splrCode',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ヤ緵搴斿晢缂栫爜' }
+    },
+    {
+      label: '渚涘簲鍟�',
+      key: 'splrName',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ヤ緵搴斿晢' }
+    },
+    {
+      label: '渚涘簲鍟嗘壒娆�',
+      key: 'splrBatch',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ヤ緵搴斿晢鎵规' }
+    },
+    {
+      label: '浜岀淮鐮�',
+      key: 'qrcode',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ヤ簩缁寸爜' }
+    },
+    {
+      label: '鏉$爜',
+      key: 'trackCode',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ユ潯鐮�' }
+    },
+    {
+      label: '鍖呰',
+      key: 'packName',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ寘瑁�' }
+    },
+    {
+      label: '鐘舵��',
+      key: 'status',
+      type: 'select',
+      props: {
+        clearable: true,
+        options: getPreparationItemStatusOptions(),
+        placeholder: '璇烽�夋嫨鐘舵��'
+      }
+    },
+    {
+      label: '澶囨敞',
+      key: 'memo',
+      type: 'input',
+      span: 24,
+      props: { type: 'textarea', rows: 3, clearable: true, placeholder: '璇疯緭鍏ュ娉�' }
+    }
+  ])
+
+  function loadFormData() {
+    Object.assign(
+      form,
+      buildPreparationItemDialogModel({
+        ...props.itemData,
+        orderId: props.itemData?.orderId ?? props.orderId
+      })
+    )
+  }
+
+  function resetForm() {
+    Object.assign(form, createPreparationItemFormState(), {
+      orderId: props.orderId
+    })
+    formRef.value?.clearValidate?.()
+  }
+
+  async function handleSubmit() {
+    try {
+      await formRef.value?.validate?.()
+      emit('submit', { ...form })
+    } catch {
+      return
+    }
+  }
+
+  function handleVisibleChange(value) {
+    emit('update:visible', value)
+  }
+
+  function handleClosed() {
+    resetForm()
+  }
+
+  watch(
+    () => props.visible,
+    (visible) => {
+      if (!visible) {
+        return
+      }
+      loadFormData()
+      nextTick(() => formRef.value?.clearValidate?.())
+    },
+    { immediate: true }
+  )
+
+  watch(
+    () => props.itemData,
+    () => {
+      if (props.visible) {
+        loadFormData()
+      }
+    },
+    { deep: true }
+  )
+</script>
diff --git a/rsf-design/src/views/orders/preparation-item/preparationItemPage.helpers.js b/rsf-design/src/views/orders/preparation-item/preparationItemPage.helpers.js
index 93afb51..b026138 100644
--- a/rsf-design/src/views/orders/preparation-item/preparationItemPage.helpers.js
+++ b/rsf-design/src/views/orders/preparation-item/preparationItemPage.helpers.js
@@ -18,14 +18,26 @@
     orderId: '',
     orderCode: '',
     poCode: '',
+    poDetlId: '',
+    matnrId: '',
     platItemId: '',
     matnrCode: '',
     maktx: '',
+    anfme: '',
+    stockUnit: '',
+    purQty: '',
+    purUnit: '',
+    qty: '',
+    splrCode: '',
+    splrName: '',
     batch: '',
     splrBatch: '',
     trackCode: '',
+    qrcode: '',
+    packName: '',
     barcode: '',
     fieldsIndex: '',
+    memo: '',
     status: '',
     ...seed
   }
@@ -42,6 +54,31 @@
   const result = {
     ...buildOutStockItemSearchParams(params)
   }
+
+  ;[
+    'poDetlId',
+    'matnrId',
+    'stockUnit',
+    'purUnit',
+    'splrCode',
+    'splrName',
+    'qrcode',
+    'packName',
+    'memo'
+  ].forEach((key) => {
+    const value = params[key]
+    if (value !== '' && value !== undefined && value !== null) {
+      result[key] = value
+    }
+  })
+  ;['anfme', 'purQty', 'qty'].forEach((key) => {
+    if (params[key] !== '' && params[key] !== undefined && params[key] !== null) {
+      const numericValue = Number(params[key])
+      if (!Number.isNaN(numericValue)) {
+        result[key] = numericValue
+      }
+    }
+  })
 
   if (params.orderId !== '' && params.orderId !== undefined && params.orderId !== null) {
     const numericValue = Number(params.orderId)
@@ -65,6 +102,81 @@
   return normalizeOutStockItemRow(record)
 }
 
+export function getPreparationItemStatusOptions() {
+  return [
+    { label: '姝e父', value: 1 },
+    { label: '鍐荤粨', value: 0 }
+  ]
+}
+
+export function createPreparationItemFormState() {
+  return {
+    id: undefined,
+    orderId: undefined,
+    orderCode: '',
+    poDetlId: '',
+    matnrId: '',
+    matnrCode: '',
+    maktx: '',
+    anfme: undefined,
+    stockUnit: '',
+    purQty: undefined,
+    purUnit: '',
+    qty: undefined,
+    splrCode: '',
+    splrName: '',
+    splrBatch: '',
+    qrcode: '',
+    trackCode: '',
+    packName: '',
+    memo: '',
+    status: 1
+  }
+}
+
+export function buildPreparationItemDialogModel(record = {}) {
+  return {
+    ...createPreparationItemFormState(),
+    ...record
+  }
+}
+
+export function buildPreparationItemSavePayload(formData = {}) {
+  const payload = {}
+  ;[
+    'id',
+    'orderId',
+    'orderCode',
+    'poDetlId',
+    'matnrId',
+    'matnrCode',
+    'maktx',
+    'stockUnit',
+    'purUnit',
+    'splrCode',
+    'splrName',
+    'splrBatch',
+    'qrcode',
+    'trackCode',
+    'packName',
+    'memo'
+  ].forEach((key) => {
+    if (formData[key] !== '' && formData[key] !== undefined && formData[key] !== null) {
+      payload[key] = formData[key]
+    }
+  })
+  ;['anfme', 'purQty', 'qty', 'status'].forEach((key) => {
+    if (formData[key] !== '' && formData[key] !== undefined && formData[key] !== null) {
+      const numericValue = Number(formData[key])
+      if (!Number.isNaN(numericValue)) {
+        payload[key] = numericValue
+      }
+    }
+  })
+
+  return payload
+}
+
 export function getPreparationItemReportColumns() {
   return [
     { key: 'orderCode', label: '澶囨枡鍗曞彿' },
diff --git a/rsf-design/src/views/orders/preparation-item/preparationItemTable.columns.js b/rsf-design/src/views/orders/preparation-item/preparationItemTable.columns.js
new file mode 100644
index 0000000..ac37f8b
--- /dev/null
+++ b/rsf-design/src/views/orders/preparation-item/preparationItemTable.columns.js
@@ -0,0 +1,66 @@
+import { h } from 'vue'
+import { ElTag } from 'element-plus'
+import ArtButtonMore from '@/components/core/forms/art-button-more/index.vue'
+
+function buildStatusTag(row) {
+  return h(
+    ElTag,
+    { type: row.statusTagType || 'info', effect: 'light' },
+    () => row.statusText || '--'
+  )
+}
+
+export function createPreparationItemTableColumns({
+  handleActionClick,
+  canEdit = true,
+  canDelete = true
+} = {}) {
+  return [
+    { type: 'selection', width: 48, align: 'center' },
+    { type: 'globalIndex', label: '搴忓彿', width: 72, align: 'center' },
+    { prop: 'orderCode', label: '澶囨枡鍗曞彿', minWidth: 150, showOverflowTooltip: true },
+    { prop: 'poDetlId', label: 'PO鏄庣粏ID', minWidth: 120, showOverflowTooltip: true },
+    { prop: 'matnrId', label: '鐗╂枡ID', minWidth: 120, showOverflowTooltip: true },
+    { prop: 'matnrCode', label: '鐗╂枡缂栫爜', minWidth: 150, showOverflowTooltip: true },
+    { prop: 'maktx', label: '鐗╂枡鍚嶇О', minWidth: 220, showOverflowTooltip: true },
+    { prop: 'anfme', label: '璁″垝鏁伴噺', width: 100, align: 'right' },
+    { prop: 'stockUnit', label: '搴撳瓨鍗曚綅', width: 100, align: 'center' },
+    { prop: 'purQty', label: '閲囪喘鏁伴噺', width: 100, align: 'right' },
+    { prop: 'purUnit', label: '閲囪喘鍗曚綅', width: 100, align: 'center' },
+    { prop: 'qty', label: '宸插嚭鏁伴噺', width: 100, align: 'right' },
+    { prop: 'splrCode', label: '渚涘簲鍟嗙紪鐮�', minWidth: 120, showOverflowTooltip: true },
+    { prop: 'splrName', label: '渚涘簲鍟�', minWidth: 150, showOverflowTooltip: true },
+    { prop: 'qrcode', label: '浜岀淮鐮�', minWidth: 140, showOverflowTooltip: true },
+    { prop: 'trackCode', label: '鏉$爜', minWidth: 140, showOverflowTooltip: true },
+    { prop: 'packName', label: '鍖呰', minWidth: 120, showOverflowTooltip: true },
+    {
+      prop: 'statusText',
+      label: '鐘舵��',
+      width: 96,
+      align: 'center',
+      formatter: (row) => buildStatusTag(row)
+    },
+    { prop: 'memo', label: '澶囨敞', minWidth: 150, showOverflowTooltip: true },
+    {
+      prop: 'operation',
+      label: '鎿嶄綔',
+      width: 120,
+      fixed: 'right',
+      formatter: (row) =>
+        h(ArtButtonMore, {
+          list: [
+            { key: 'view', label: '鏌ョ湅', icon: 'ri:eye-line' },
+            { key: 'edit', label: '缂栬緫', icon: 'ri:edit-line', disabled: !canEdit },
+            {
+              key: 'delete',
+              label: '鍒犻櫎',
+              icon: 'ri:delete-bin-6-line',
+              color: 'var(--el-color-danger)',
+              disabled: !canDelete
+            }
+          ],
+          onClick: (item) => handleActionClick?.(item, row)
+        })
+    }
+  ]
+}
diff --git a/rsf-design/src/views/orders/preparation/index.vue b/rsf-design/src/views/orders/preparation/index.vue
index 1e162a0..525d8a4 100644
--- a/rsf-design/src/views/orders/preparation/index.vue
+++ b/rsf-design/src/views/orders/preparation/index.vue
@@ -12,6 +12,16 @@
       <ArtTableHeader v-model:columns="columnChecks" :loading="loading" @refresh="refreshData">
         <template #left>
           <ElSpace wrap>
+            <ElButton v-if="canCreate" type="primary" @click="generateDialogVisible = true">
+              鎵嬪姩寤哄崟
+            </ElButton>
+            <ElButton
+              v-if="canUpdate"
+              :disabled="loading || selectedRows.length === 0"
+              @click="waveDialogVisible = true"
+            >
+              鐢熸垚娉㈡
+            </ElButton>
             <ListExportPrint
               :preview-visible="previewVisible"
               @update:previewVisible="handlePreviewVisibleChange"
@@ -52,13 +62,31 @@
       @size-change="handleDetailSizeChange"
       @current-change="handleDetailCurrentChange"
     />
+
+    <PreparationGenerateDialog
+      v-model:visible="generateDialogVisible"
+      @created="handlePreparationCreated"
+    />
+
+    <PreparationWaveDialog
+      v-model:visible="waveDialogVisible"
+      :submitting="waveSubmitting"
+      @submit="handleGenerateWave"
+    />
+
+    <PreparationTaskDialog
+      v-model:visible="taskDialogVisible"
+      :row="activeTaskRow"
+      @submitted="handleTaskSubmitted"
+    />
   </div>
 </template>
 
 <script setup>
   import { computed, reactive, ref } from 'vue'
-  import { ElMessage, ElMessageBox } from 'element-plus'
+  import { ElButton, ElMessage, ElMessageBox, ElSpace } from 'element-plus'
   import { useRouter } from 'vue-router'
+  import { useAuth } from '@/hooks/core/useAuth'
   import { useUserStore } from '@/store/modules/user'
   import { useTable } from '@/hooks/core/useTable'
   import { usePrintExportPage } from '@/views/system/common/usePrintExportPage'
@@ -70,15 +98,20 @@
     fetchCompletePreparation,
     fetchDeletePreparation,
     fetchExportPreparationReport,
+    fetchGeneratePreparationWave,
     fetchGetPreparationDetail,
     fetchGetPreparationMany,
     fetchPreparationItemPage,
     fetchPreparationPage
   } from '@/api/preparation'
   import PreparationDetailDrawer from './modules/preparation-detail-drawer.vue'
+  import PreparationGenerateDialog from './modules/preparation-generate-dialog.vue'
+  import PreparationTaskDialog from './modules/preparation-task-dialog.vue'
+  import PreparationWaveDialog from './modules/preparation-wave-dialog.vue'
   import { createPreparationTableColumns } from './preparationTable.columns'
   import {
     buildPreparationDetailQueryParams,
+    buildPreparationGenerateWavePayload,
     buildPreparationPageQueryParams,
     buildPreparationPrintRows,
     buildPreparationReportMeta,
@@ -93,6 +126,7 @@
 
   defineOptions({ name: 'Preparation' })
 
+  const { hasAuth } = useAuth()
   const userStore = useUserStore()
   const router = useRouter()
   const reportTitle = PREPARATION_REPORT_TITLE
@@ -103,6 +137,11 @@
   const detailData = ref({})
   const detailTableData = ref([])
   const activePreparationId = ref(null)
+  const generateDialogVisible = ref(false)
+  const waveDialogVisible = ref(false)
+  const waveSubmitting = ref(false)
+  const taskDialogVisible = ref(false)
+  const activeTaskRow = ref({})
 
   const detailPagination = reactive({
     current: 1,
@@ -110,6 +149,8 @@
     total: 0
   })
 
+  const canCreate = computed(() => hasAuth('add'))
+  const canUpdate = computed(() => hasAuth('update'))
   const reportQueryParams = computed(() => buildPreparationSearchParams(searchForm.value))
   const detailColumns = computed(() => createPreparationDetailItemColumns())
   const searchItems = computed(() => [
@@ -132,6 +173,12 @@
       props: { clearable: true, placeholder: '璇疯緭鍏� PO 鍗曞彿' }
     },
     {
+      label: 'PO ID',
+      key: 'poId',
+      type: 'inputNumber',
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏� PO ID' }
+    },
+    {
       label: '涓氬姟绫诲瀷',
       key: 'wkType',
       type: 'input',
@@ -140,20 +187,56 @@
     {
       label: '鍗曟嵁鐘舵��',
       key: 'exceStatus',
-      type: 'input',
-      props: { clearable: true, placeholder: '璇疯緭鍏ョ姸鎬�' }
+      type: 'inputNumber',
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏ョ姸鎬�' }
     },
     {
       label: '閲婃斁鐘舵��',
       key: 'rleStatus',
+      type: 'inputNumber',
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏ラ噴鏀剧姸鎬�' }
+    },
+    {
+      label: '璁″垝鏁伴噺',
+      key: 'anfme',
+      type: 'inputNumber',
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏ヨ鍒掓暟閲�' }
+    },
+    {
+      label: '宸插嚭鏁伴噺',
+      key: 'qty',
+      type: 'inputNumber',
+      props: { clearable: true, controlsPosition: 'right', placeholder: '璇疯緭鍏ュ凡鍑烘暟閲�' }
+    },
+    {
+      label: '鐗╂祦鍗曞彿',
+      key: 'logisNo',
       type: 'input',
-      props: { clearable: true, placeholder: '璇疯緭鍏ラ噴鏀剧姸鎬�' }
+      props: { clearable: true, placeholder: '璇疯緭鍏ョ墿娴佸崟鍙�' }
+    },
+    {
+      label: '鍒拌揣鏃ユ湡',
+      key: 'arrTime',
+      type: 'date',
+      props: { clearable: true, valueFormat: 'YYYY-MM-DD', placeholder: '璇烽�夋嫨鍒拌揣鏃ユ湡' }
     },
     {
       label: '瀹㈡埛',
       key: 'customerName',
       type: 'input',
       props: { clearable: true, placeholder: '璇疯緭鍏ュ鎴�' }
+    },
+    {
+      label: '閿�鍞粍缁�',
+      key: 'saleOrgName',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ラ攢鍞粍缁�' }
+    },
+    {
+      label: '澶囨敞',
+      key: 'memo',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ娉�' }
     }
   ])
 
@@ -178,9 +261,13 @@
     detailLoading.value = true
     try {
       const [detailResponse, itemResponse] = await Promise.all([
-        guardRequestWithMessage(fetchGetPreparationDetail(activePreparationId.value), {}, {
-          timeoutMessage: '澶囨枡鍗曡鎯呭姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�'
-        }),
+        guardRequestWithMessage(
+          fetchGetPreparationDetail(activePreparationId.value),
+          {},
+          {
+            timeoutMessage: '澶囨枡鍗曡鎯呭姞杞借秴鏃讹紝宸插仠姝㈢瓑寰�'
+          }
+        ),
         guardRequestWithMessage(
           fetchPreparationItemPage(
             buildPreparationDetailQueryParams({
@@ -239,6 +326,12 @@
             orderId: String(row.id)
           }
         })
+        return
+      }
+
+      if (action.key === 'public') {
+        activeTaskRow.value = row
+        taskDialogVisible.value = true
         return
       }
 
@@ -329,6 +422,34 @@
     resetSearchParams()
   }
 
+  async function handleGenerateWave(waveRuleId) {
+    waveSubmitting.value = true
+    try {
+      await fetchGeneratePreparationWave(
+        buildPreparationGenerateWavePayload(selectedRows.value, waveRuleId)
+      )
+      ElMessage.success('娉㈡鐢熸垚鎴愬姛')
+      waveDialogVisible.value = false
+      selectedRows.value = []
+      await refreshData()
+    } catch (error) {
+      ElMessage.error(error?.message || '娉㈡鐢熸垚澶辫触')
+    } finally {
+      waveSubmitting.value = false
+    }
+  }
+
+  async function handlePreparationCreated() {
+    await refreshData()
+  }
+
+  async function handleTaskSubmitted() {
+    await refreshData()
+    if (detailDrawerVisible.value && activePreparationId.value === activeTaskRow.value?.id) {
+      await loadDetailResources()
+    }
+  }
+
   function handleDetailSizeChange(size) {
     detailPagination.size = size
     detailPagination.current = 1
diff --git a/rsf-design/src/views/orders/preparation/modules/preparation-generate-dialog.vue b/rsf-design/src/views/orders/preparation/modules/preparation-generate-dialog.vue
new file mode 100644
index 0000000..0320f04
--- /dev/null
+++ b/rsf-design/src/views/orders/preparation/modules/preparation-generate-dialog.vue
@@ -0,0 +1,301 @@
+<template>
+  <ElDialog
+    :model-value="visible"
+    title="鐢熸垚澶囨枡鍗�"
+    width="92%"
+    top="4vh"
+    destroy-on-close
+    @update:model-value="handleVisibleChange"
+  >
+    <div class="flex flex-col gap-4">
+      <ArtSearchBar
+        v-model="searchForm"
+        :items="searchItems"
+        :show-expand="true"
+        @search="handleSearch"
+        @reset="handleReset"
+      />
+
+      <ElCard class="art-table-card">
+        <template #header>
+          <div class="flex items-center justify-between gap-3">
+            <span class="font-medium">鍙�夊彂璐ф槑缁�</span>
+            <ElSpace>
+              <ElButton @click="reloadData">鍒锋柊</ElButton>
+              <ElButton type="primary" :disabled="!selectedRows.length" @click="openPreview">
+                棰勮鐢熸垚
+              </ElButton>
+            </ElSpace>
+          </div>
+        </template>
+
+        <ArtTable
+          :loading="loading"
+          :data="tableData"
+          :columns="tableColumns"
+          :pagination="pagination"
+          @selection-change="handleSelectionChange"
+          @pagination:size-change="handleSizeChange"
+          @pagination:current-change="handleCurrentChange"
+        />
+      </ElCard>
+    </div>
+
+    <ElDialog v-model="previewVisible" title="鐢熸垚棰勮" width="88%" append-to-body destroy-on-close>
+      <div class="flex flex-col gap-4">
+        <div class="text-sm text-[var(--art-text-gray-600)]">
+          宸查�夋嫨 {{ previewRows.length }} 鏉″彂璐ф槑缁嗭紝鍙湪涓嬫柟璋冩暣鏈澶囨枡鏁伴噺銆�
+        </div>
+
+        <ElTable :data="previewRows" border height="56vh">
+          <ElTableColumn type="index" label="搴忓彿" width="72" align="center" />
+          <ElTableColumn
+            prop="deliveryCode"
+            label="鍙戣揣鍗曞彿"
+            min-width="160"
+            show-overflow-tooltip
+          />
+          <ElTableColumn prop="matnrCode" label="鐗╂枡缂栫爜" min-width="150" show-overflow-tooltip />
+          <ElTableColumn prop="maktx" label="鐗╂枡鍚嶇О" min-width="220" show-overflow-tooltip />
+          <ElTableColumn prop="splrName" label="渚涘簲鍟�" min-width="150" show-overflow-tooltip />
+          <ElTableColumn prop="unit" label="鍗曚綅" width="90" align="center" />
+          <ElTableColumn label="鍓╀綑鏁伴噺" width="110" align="right">
+            <template #default="{ row }">
+              {{ row.remainingQty }}
+            </template>
+          </ElTableColumn>
+          <ElTableColumn label="鏈澶囨枡鏁伴噺" width="160" align="center">
+            <template #default="{ row }">
+              <ElInputNumber
+                v-model="row.anfme"
+                :min="0"
+                :max="row.remainingQty"
+                :precision="2"
+                controls-position="right"
+              />
+            </template>
+          </ElTableColumn>
+          <ElTableColumn
+            prop="splrBatch"
+            label="渚涘簲鍟嗘壒娆�"
+            min-width="140"
+            show-overflow-tooltip
+          />
+          <ElTableColumn prop="updateTime" label="鏇存柊鏃堕棿" min-width="170" show-overflow-tooltip />
+        </ElTable>
+      </div>
+
+      <template #footer>
+        <ElSpace>
+          <ElButton @click="previewVisible = false">鍙栨秷</ElButton>
+          <ElButton type="primary" :loading="submitting" @click="handleConfirmGenerate">
+            鐢熸垚澶囨枡鍗�
+          </ElButton>
+        </ElSpace>
+      </template>
+    </ElDialog>
+
+    <template #footer>
+      <ElButton @click="handleVisibleChange(false)">鍏抽棴</ElButton>
+    </template>
+  </ElDialog>
+</template>
+
+<script setup>
+  import { computed, reactive, ref, watch } from 'vue'
+  import { ElMessage } from 'element-plus'
+  import { defaultResponseAdapter } from '@/utils/table/tableUtils'
+  import { fetchPreparationDialogPage, fetchGeneratePreparationOrders } from '@/api/preparation'
+
+  const props = defineProps({
+    visible: { type: Boolean, default: false }
+  })
+
+  const emit = defineEmits(['update:visible', 'created'])
+
+  const loading = ref(false)
+  const submitting = ref(false)
+  const tableData = ref([])
+  const selectedRows = ref([])
+  const previewVisible = ref(false)
+  const previewRows = ref([])
+  const searchForm = ref(createSearchState())
+  const pagination = reactive({
+    current: 1,
+    size: 20,
+    total: 0
+  })
+
+  const searchItems = computed(() => [
+    {
+      label: '鍏抽敭瀛�',
+      key: 'condition',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ彂璐у崟鍙�/鐗╂枡缂栫爜/鐗╂枡鍚嶇О' }
+    },
+    {
+      label: '鍙戣揣鍗曞彿',
+      key: 'deliveryCode',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ュ彂璐у崟鍙�' }
+    },
+    {
+      label: '鐗╂枡鍚嶇О',
+      key: 'maktx',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ョ墿鏂欏悕绉�' }
+    },
+    {
+      label: '鐗╂枡缂栫爜',
+      key: 'matnrCode',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ョ墿鏂欑紪鐮�' }
+    },
+    {
+      label: '渚涘簲鍟�',
+      key: 'splrName',
+      type: 'input',
+      props: { clearable: true, placeholder: '璇疯緭鍏ヤ緵搴斿晢' }
+    }
+  ])
+
+  const tableColumns = [
+    { type: 'selection', width: 48, align: 'center' },
+    { type: 'globalIndex', label: '搴忓彿', width: 72, align: 'center' },
+    { prop: 'deliveryCode', label: '鍙戣揣鍗曞彿', minWidth: 160, showOverflowTooltip: true },
+    { prop: 'matnrCode', label: '鐗╂枡缂栫爜', minWidth: 150, showOverflowTooltip: true },
+    { prop: 'maktx', label: '鐗╂枡鍚嶇О', minWidth: 220, showOverflowTooltip: true },
+    { prop: 'unit', label: '鍗曚綅', width: 90, align: 'center' },
+    { prop: 'anfme', label: '璁″垝鏁伴噺', width: 100, align: 'right' },
+    { prop: 'workQty', label: '鎵ц鏁伴噺', width: 100, align: 'right' },
+    { prop: 'qty', label: '宸插嚭鏁伴噺', width: 100, align: 'right' },
+    { prop: 'splrName', label: '渚涘簲鍟�', minWidth: 150, showOverflowTooltip: true },
+    { prop: 'splrBatch', label: '渚涘簲鍟嗘壒娆�', minWidth: 140, showOverflowTooltip: true },
+    { prop: 'updateTime', label: '鏇存柊鏃堕棿', minWidth: 170, showOverflowTooltip: true }
+  ]
+
+  function createSearchState() {
+    return {
+      condition: '',
+      deliveryCode: '',
+      maktx: '',
+      matnrCode: '',
+      splrName: ''
+    }
+  }
+
+  function buildQueryParams() {
+    return {
+      current: pagination.current,
+      pageSize: pagination.size,
+      ...Object.fromEntries(
+        Object.entries(searchForm.value).filter(([, value]) => value !== '' && value !== undefined)
+      )
+    }
+  }
+
+  function normalizeRow(row = {}) {
+    const anfme = Number(row.anfme || 0)
+    const workQty = Number(row.workQty || 0)
+    const qty = Number(row.qty || 0)
+    const remainingQty = Math.max(anfme - workQty - qty, 0)
+
+    return {
+      ...row,
+      remainingQty,
+      anfme: remainingQty
+    }
+  }
+
+  async function loadData() {
+    loading.value = true
+    try {
+      const response = await fetchPreparationDialogPage(buildQueryParams())
+      const normalized = defaultResponseAdapter(response)
+      tableData.value = Array.isArray(normalized.records) ? normalized.records : []
+      pagination.total = Number(normalized.total || 0)
+      pagination.current = Number(normalized.current || pagination.current || 1)
+      pagination.size = Number(normalized.size || pagination.size || 20)
+    } catch (error) {
+      tableData.value = []
+      pagination.total = 0
+      ElMessage.error(error?.message || '鍙戣揣鏄庣粏鍔犺浇澶辫触')
+    } finally {
+      loading.value = false
+    }
+  }
+
+  function reloadData() {
+    loadData()
+  }
+
+  function handleSelectionChange(rows) {
+    selectedRows.value = Array.isArray(rows) ? rows : []
+  }
+
+  function handleSearch(params) {
+    searchForm.value = { ...searchForm.value, ...params }
+    pagination.current = 1
+    loadData()
+  }
+
+  function handleReset() {
+    searchForm.value = createSearchState()
+    pagination.current = 1
+    loadData()
+  }
+
+  function handleSizeChange(size) {
+    pagination.size = size
+    pagination.current = 1
+    loadData()
+  }
+
+  function handleCurrentChange(current) {
+    pagination.current = current
+    loadData()
+  }
+
+  function openPreview() {
+    previewRows.value = selectedRows.value.map((row) => normalizeRow({ ...row }))
+    previewVisible.value = true
+  }
+
+  async function handleConfirmGenerate() {
+    const rows = previewRows.value.filter((row) => Number(row.anfme) > 0)
+    if (!rows.length) {
+      ElMessage.warning('鑷冲皯淇濈暀涓�鏉℃暟閲忓ぇ浜� 0 鐨勫彂璐ф槑缁�')
+      return
+    }
+
+    submitting.value = true
+    try {
+      await fetchGeneratePreparationOrders({ ids: rows })
+      ElMessage.success('澶囨枡鍗曠敓鎴愭垚鍔�')
+      previewVisible.value = false
+      emit('created')
+      handleVisibleChange(false)
+    } catch (error) {
+      ElMessage.error(error?.message || '澶囨枡鍗曠敓鎴愬け璐�')
+    } finally {
+      submitting.value = false
+    }
+  }
+
+  function handleVisibleChange(value) {
+    emit('update:visible', value)
+  }
+
+  watch(
+    () => props.visible,
+    (visible) => {
+      if (!visible) {
+        selectedRows.value = []
+        previewRows.value = []
+        previewVisible.value = false
+        return
+      }
+      loadData()
+    }
+  )
+</script>
diff --git a/rsf-design/src/views/orders/preparation/modules/preparation-task-dialog.vue b/rsf-design/src/views/orders/preparation/modules/preparation-task-dialog.vue
new file mode 100644
index 0000000..0398528
--- /dev/null
+++ b/rsf-design/src/views/orders/preparation/modules/preparation-task-dialog.vue
@@ -0,0 +1,259 @@
+<template>
+  <ElDialog
+    :model-value="visible"
+    title="涓嬪彂鎵ц"
+    width="94%"
+    top="4vh"
+    destroy-on-close
+    @update:model-value="handleVisibleChange"
+  >
+    <div class="flex flex-col gap-4">
+      <ElCard shadow="never">
+        <div class="grid gap-4 md:grid-cols-[240px_240px_auto]">
+          <div>
+            <div class="mb-2 text-sm text-[var(--art-text-gray-600)]">娉㈡绛栫暐</div>
+            <ElSelect
+              v-model="waveRuleId"
+              class="w-full"
+              filterable
+              clearable
+              placeholder="璇烽�夋嫨娉㈡绛栫暐"
+              :loading="metaLoading"
+              @change="handleWaveRuleChange"
+            >
+              <ElOption
+                v-for="item in waveRuleOptions"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              />
+            </ElSelect>
+          </div>
+
+          <div>
+            <div class="mb-2 text-sm text-[var(--art-text-gray-600)]">鎵归噺璁剧疆绔欑偣</div>
+            <ElSelect
+              v-model="batchSiteNo"
+              class="w-full"
+              filterable
+              clearable
+              placeholder="璇烽�夋嫨绔欑偣"
+            >
+              <ElOption
+                v-for="site in siteOptions"
+                :key="site.value"
+                :label="site.label"
+                :value="site.value"
+              />
+            </ElSelect>
+          </div>
+
+          <div class="flex items-end gap-2">
+            <ElButton :loading="previewLoading" @click="loadTaskPreview">鍒锋柊棰勮</ElButton>
+            <ElButton :disabled="!selectedRowIds.length || !batchSiteNo" @click="applyBatchSite">
+              搴旂敤鍒伴�変腑琛�
+            </ElButton>
+          </div>
+        </div>
+      </ElCard>
+
+      <ElTable :data="taskRows" border height="58vh" @selection-change="handleSelectionChange">
+        <ElTableColumn type="selection" width="48" align="center" />
+        <ElTableColumn type="index" label="搴忓彿" width="72" align="center" />
+        <ElTableColumn prop="locCode" label="搴撲綅" min-width="120" show-overflow-tooltip />
+        <ElTableColumn prop="barcode" label="瀹瑰櫒" min-width="120" show-overflow-tooltip />
+        <ElTableColumn prop="matnrCode" label="鐗╂枡缂栫爜" min-width="150" show-overflow-tooltip />
+        <ElTableColumn prop="batch" label="鎵规" min-width="120" show-overflow-tooltip />
+        <ElTableColumn prop="unit" label="鍗曚綅" width="90" align="center" />
+        <ElTableColumn prop="outQty" label="鍑哄簱鏁伴噺" width="110" align="right" />
+        <ElTableColumn prop="anfme" label="搴撳瓨鏁伴噺" width="110" align="right" />
+        <ElTableColumn label="鍑哄簱鍙�" width="180">
+          <template #default="{ row }">
+            <ElSelect
+              v-model="row.siteNo"
+              class="w-full"
+              filterable
+              clearable
+              placeholder="璇烽�夋嫨绔欑偣"
+            >
+              <ElOption
+                v-for="site in resolveSiteOptions(row)"
+                :key="site.value"
+                :label="site.label"
+                :value="site.value"
+              />
+            </ElSelect>
+          </template>
+        </ElTableColumn>
+      </ElTable>
+    </div>
+
+    <template #footer>
+      <ElSpace>
+        <ElButton @click="handleVisibleChange(false)">鍏抽棴</ElButton>
+        <ElButton type="primary" :loading="submitting" @click="handleSubmit"> 鐢熸垚浠诲姟 </ElButton>
+      </ElSpace>
+    </template>
+  </ElDialog>
+</template>
+
+<script setup>
+  import { ref, watch } from 'vue'
+  import { ElMessage } from 'element-plus'
+  import { fetchWaveRulePage } from '@/api/system-manage'
+  import { defaultResponseAdapter } from '@/utils/table/tableUtils'
+  import {
+    fetchGeneratePreparationTasks,
+    fetchPreparationTaskPreview,
+    fetchPreparationTaskSites
+  } from '@/api/preparation'
+
+  const props = defineProps({
+    visible: { type: Boolean, default: false },
+    row: { type: Object, default: () => ({}) }
+  })
+
+  const emit = defineEmits(['update:visible', 'submitted'])
+
+  const metaLoading = ref(false)
+  const previewLoading = ref(false)
+  const submitting = ref(false)
+  const waveRuleId = ref()
+  const waveRuleOptions = ref([])
+  const siteOptions = ref([])
+  const batchSiteNo = ref('')
+  const selectedRowIds = ref([])
+  const taskRows = ref([])
+
+  function handleVisibleChange(value) {
+    emit('update:visible', value)
+  }
+
+  function normalizeSiteOptions(records = []) {
+    return (Array.isArray(records) ? records : []).map((item) => ({
+      label: item.site || item.staNo || item.name || '--',
+      value: item.site || item.staNo || item.name || '',
+      raw: item
+    }))
+  }
+
+  async function loadMeta() {
+    metaLoading.value = true
+    try {
+      const [waveResponse, siteResponse] = await Promise.all([
+        fetchWaveRulePage({ current: 1, pageSize: 200, status: 1 }),
+        fetchPreparationTaskSites()
+      ])
+      const waveRecords = defaultResponseAdapter(waveResponse).records
+      waveRuleOptions.value = waveRecords.map((item) => ({
+        label: item.name || item.code || `娉㈡绛栫暐${item.id}`,
+        value: Number(item.id)
+      }))
+      if (!waveRuleId.value && waveRuleOptions.value.length) {
+        waveRuleId.value = waveRuleOptions.value[0].value
+      }
+      siteOptions.value = normalizeSiteOptions(siteResponse)
+    } catch (error) {
+      waveRuleOptions.value = []
+      siteOptions.value = []
+      ElMessage.error(error?.message || '涓嬪彂鎵ц鍒濆鍖栧け璐�')
+    } finally {
+      metaLoading.value = false
+    }
+  }
+
+  function normalizeTaskRow(row = {}) {
+    return {
+      ...row,
+      siteNo: row.siteNo || row.site || ''
+    }
+  }
+
+  async function loadTaskPreview() {
+    if (!props.row?.id) {
+      return
+    }
+    if (!waveRuleId.value) {
+      ElMessage.warning('璇烽�夋嫨娉㈡绛栫暐')
+      return
+    }
+
+    previewLoading.value = true
+    try {
+      const response = await fetchPreparationTaskPreview({
+        orderId: Number(props.row.id),
+        waveId: Number(waveRuleId.value)
+      })
+      taskRows.value = Array.isArray(response) ? response.map((item) => normalizeTaskRow(item)) : []
+      selectedRowIds.value = []
+    } catch (error) {
+      taskRows.value = []
+      ElMessage.error(error?.message || '浠诲姟棰勮鍔犺浇澶辫触')
+    } finally {
+      previewLoading.value = false
+    }
+  }
+
+  function handleWaveRuleChange() {
+    if (props.visible) {
+      loadTaskPreview()
+    }
+  }
+
+  function handleSelectionChange(rows) {
+    selectedRowIds.value = Array.isArray(rows) ? rows.map((item) => item.id) : []
+  }
+
+  function applyBatchSite() {
+    taskRows.value = taskRows.value.map((item) =>
+      selectedRowIds.value.includes(item.id) ? { ...item, siteNo: batchSiteNo.value } : item
+    )
+  }
+
+  function resolveSiteOptions(row) {
+    if (Array.isArray(row?.staNos) && row.staNos.length) {
+      return row.staNos.map((item) => ({
+        label: item.staNo || item.site || '--',
+        value: item.staNo || item.site || ''
+      }))
+    }
+    return siteOptions.value
+  }
+
+  async function handleSubmit() {
+    const items = taskRows.value.filter((item) => item.locCode && item.siteNo)
+    if (!items.length) {
+      ElMessage.warning('璇疯嚦灏戜负涓�鏉℃湁搴撲綅鐨勮褰曟寚瀹氱珯鐐�')
+      return
+    }
+
+    submitting.value = true
+    try {
+      await fetchGeneratePreparationTasks({
+        outId: Number(props.row.id),
+        items
+      })
+      ElMessage.success('浠诲姟鐢熸垚鎴愬姛')
+      emit('submitted')
+      handleVisibleChange(false)
+    } catch (error) {
+      ElMessage.error(error?.message || '浠诲姟鐢熸垚澶辫触')
+    } finally {
+      submitting.value = false
+    }
+  }
+
+  watch(
+    () => props.visible,
+    async (visible) => {
+      if (!visible) {
+        taskRows.value = []
+        selectedRowIds.value = []
+        batchSiteNo.value = ''
+        return
+      }
+      await loadMeta()
+      await loadTaskPreview()
+    }
+  )
+</script>
diff --git a/rsf-design/src/views/orders/preparation/modules/preparation-wave-dialog.vue b/rsf-design/src/views/orders/preparation/modules/preparation-wave-dialog.vue
new file mode 100644
index 0000000..8deaa9a
--- /dev/null
+++ b/rsf-design/src/views/orders/preparation/modules/preparation-wave-dialog.vue
@@ -0,0 +1,97 @@
+<template>
+  <ElDialog
+    :model-value="visible"
+    title="鐢熸垚娉㈡"
+    width="520px"
+    destroy-on-close
+    @update:model-value="handleVisibleChange"
+  >
+    <ElForm label-width="110px">
+      <ElFormItem label="娉㈡绛栫暐" required>
+        <ElSelect
+          v-model="waveRuleId"
+          class="w-full"
+          filterable
+          clearable
+          placeholder="璇烽�夋嫨娉㈡绛栫暐"
+          :loading="loading"
+        >
+          <ElOption
+            v-for="item in waveRuleOptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </ElSelect>
+      </ElFormItem>
+    </ElForm>
+
+    <template #footer>
+      <ElSpace>
+        <ElButton @click="handleVisibleChange(false)">鍙栨秷</ElButton>
+        <ElButton type="primary" :loading="submitting" @click="handleSubmit">纭畾</ElButton>
+      </ElSpace>
+    </template>
+  </ElDialog>
+</template>
+
+<script setup>
+  import { ref, watch } from 'vue'
+  import { ElMessage } from 'element-plus'
+  import { fetchWaveRulePage } from '@/api/system-manage'
+  import { defaultResponseAdapter } from '@/utils/table/tableUtils'
+
+  const props = defineProps({
+    visible: { type: Boolean, default: false },
+    submitting: { type: Boolean, default: false }
+  })
+
+  const emit = defineEmits(['update:visible', 'submit'])
+
+  const loading = ref(false)
+  const waveRuleId = ref()
+  const waveRuleOptions = ref([])
+
+  async function loadWaveRuleOptions() {
+    loading.value = true
+    try {
+      const response = await fetchWaveRulePage({ current: 1, pageSize: 200, status: 1 })
+      const normalized = defaultResponseAdapter(response)
+      const records = Array.isArray(normalized.records) ? normalized.records : []
+      waveRuleOptions.value = records.map((item) => ({
+        label: item.name || item.code || `娉㈡绛栫暐${item.id}`,
+        value: Number(item.id)
+      }))
+      if (!waveRuleId.value && waveRuleOptions.value.length) {
+        waveRuleId.value = waveRuleOptions.value[0].value
+      }
+    } catch (error) {
+      waveRuleOptions.value = []
+      ElMessage.error(error?.message || '娉㈡绛栫暐鍔犺浇澶辫触')
+    } finally {
+      loading.value = false
+    }
+  }
+
+  function handleVisibleChange(value) {
+    emit('update:visible', value)
+  }
+
+  function handleSubmit() {
+    if (!waveRuleId.value) {
+      ElMessage.warning('璇烽�夋嫨娉㈡绛栫暐')
+      return
+    }
+    emit('submit', waveRuleId.value)
+  }
+
+  watch(
+    () => props.visible,
+    (visible) => {
+      if (!visible) {
+        return
+      }
+      loadWaveRuleOptions()
+    }
+  )
+</script>
diff --git a/rsf-design/src/views/orders/preparation/preparationPage.helpers.js b/rsf-design/src/views/orders/preparation/preparationPage.helpers.js
index 53bdd34..d04f094 100644
--- a/rsf-design/src/views/orders/preparation/preparationPage.helpers.js
+++ b/rsf-design/src/views/orders/preparation/preparationPage.helpers.js
@@ -37,30 +37,38 @@
 
 function normalizeStatusMeta(exceStatus, exceStatusText) {
   if (exceStatusText) {
-    return PREPARATION_STATUS_META[Number(exceStatus)] || {
-      text: exceStatusText,
-      type: 'info'
-    }
+    return (
+      PREPARATION_STATUS_META[Number(exceStatus)] || {
+        text: exceStatusText,
+        type: 'info'
+      }
+    )
   }
 
-  return PREPARATION_STATUS_META[Number(exceStatus)] || {
-    text: normalizeText(exceStatus) || '--',
-    type: 'info'
-  }
+  return (
+    PREPARATION_STATUS_META[Number(exceStatus)] || {
+      text: normalizeText(exceStatus) || '--',
+      type: 'info'
+    }
+  )
 }
 
 function normalizeRleStatusMeta(rleStatus, rleStatusText) {
   if (rleStatusText) {
-    return PREPARATION_RLE_STATUS_META[Number(rleStatus)] || {
-      text: rleStatusText,
-      type: 'info'
-    }
+    return (
+      PREPARATION_RLE_STATUS_META[Number(rleStatus)] || {
+        text: rleStatusText,
+        type: 'info'
+      }
+    )
   }
 
-  return PREPARATION_RLE_STATUS_META[Number(rleStatus)] || {
-    text: normalizeText(rleStatus) || '--',
-    type: 'info'
-  }
+  return (
+    PREPARATION_RLE_STATUS_META[Number(rleStatus)] || {
+      text: normalizeText(rleStatus) || '--',
+      type: 'info'
+    }
+  )
 }
 
 export function createPreparationSearchState() {
@@ -68,10 +76,14 @@
     condition: '',
     code: '',
     poCode: '',
+    poId: '',
     wkType: '',
     exceStatus: '',
     rleStatus: '',
+    anfme: '',
+    qty: '',
     logisNo: '',
+    arrTime: '',
     customerName: '',
     saleOrgName: '',
     memo: ''
@@ -87,6 +99,7 @@
     'poCode',
     'wkType',
     'logisNo',
+    'arrTime',
     'customerName',
     'saleOrgName',
     'memo'
@@ -104,6 +117,12 @@
   if (params.rleStatus !== '' && params.rleStatus !== undefined && params.rleStatus !== null) {
     result.rleStatus = normalizeNumber(params.rleStatus)
   }
+
+  ;['poId', 'anfme', 'qty'].forEach((key) => {
+    if (params[key] !== '' && params[key] !== undefined && params[key] !== null) {
+      result[key] = normalizeNumber(params[key])
+    }
+  })
 
   return result
 }
@@ -166,7 +185,8 @@
     memo: normalizeText(record.memo) || '--',
     canComplete: Number(record.exceStatus) !== 15,
     canCancel: Number(record.exceStatus) === 10,
-    canDelete: Number(record.exceStatus) !== 15
+    canDelete: Number(record.exceStatus) !== 15,
+    canPublic: Number(record.workQty || 0) < Number(record.anfme || 0)
   }
 }
 
@@ -189,6 +209,13 @@
   return [
     { key: 'view', label: $t('common.actions.detail'), icon: 'ri:eye-line' },
     { key: 'items', label: $t('common.actions.items'), icon: 'ri:list-check-3' },
+    {
+      key: 'public',
+      label: '涓嬪彂鎵ц',
+      icon: 'ri:send-plane-line',
+      color: 'var(--el-color-primary)',
+      disabled: !normalizedRow.canPublic
+    },
     { key: 'print', label: $t('common.actions.print'), icon: 'ri:printer-line' },
     {
       key: 'complete',
@@ -251,3 +278,12 @@
     }
   }
 }
+
+export function buildPreparationGenerateWavePayload(rows = [], waveRuleId) {
+  return {
+    ids: Array.isArray(rows)
+      ? rows.map((row) => Number(row?.id)).filter((id) => Number.isFinite(id))
+      : [],
+    waveRuleId: normalizeNumber(waveRuleId)
+  }
+}
diff --git a/rsf-design/src/views/orders/preparation/preparationTable.columns.js b/rsf-design/src/views/orders/preparation/preparationTable.columns.js
index 95116a5..9dd6e1a 100644
--- a/rsf-design/src/views/orders/preparation/preparationTable.columns.js
+++ b/rsf-design/src/views/orders/preparation/preparationTable.columns.js
@@ -7,12 +7,17 @@
   return [
     { type: 'selection', width: 48, align: 'center' },
     { type: 'globalIndex', label: '搴忓彿', width: 72, align: 'center' },
+    { prop: 'id', label: 'ID', width: 90, align: 'center' },
     { prop: 'code', label: '澶囨枡鍗曞彿', minWidth: 170, showOverflowTooltip: true },
     { prop: 'poCode', label: 'PO鍗曞彿', minWidth: 150, showOverflowTooltip: true },
     { prop: 'typeLabel', label: '鍗曟嵁绫诲瀷', minWidth: 120, showOverflowTooltip: true },
     { prop: 'wkTypeLabel', label: '涓氬姟绫诲瀷', minWidth: 130, showOverflowTooltip: true },
+    { prop: 'saleUserName', label: '閿�鍞憳', minWidth: 120, showOverflowTooltip: true },
+    { prop: 'businessTimeText', label: '鍑哄簱鏃ユ湡', minWidth: 150, showOverflowTooltip: true },
+    { prop: 'customerId', label: '瀹㈡埛缂栫爜', minWidth: 140, showOverflowTooltip: true },
     { prop: 'customerName', label: '瀹㈡埛', minWidth: 160, showOverflowTooltip: true },
     { prop: 'saleOrgName', label: '閿�鍞粍缁�', minWidth: 150, showOverflowTooltip: true },
+    { prop: 'stockOrgName', label: '搴撳瓨缁勭粐', minWidth: 150, showOverflowTooltip: true },
     { prop: 'anfme', label: '搴斿嚭鏁伴噺', width: 100, align: 'right' },
     { prop: 'workQty', label: '鎵ц鏁伴噺', width: 100, align: 'right' },
     { prop: 'qty', label: '宸插嚭鏁伴噺', width: 100, align: 'right' },
@@ -39,11 +44,15 @@
           () => row.exceStatusText || '--'
         )
     },
+    { prop: 'updateByText', label: '鏇存柊浜�', minWidth: 120, showOverflowTooltip: true },
     { prop: 'updateTimeText', label: '鏇存柊鏃堕棿', minWidth: 170, showOverflowTooltip: true },
+    { prop: 'createByText', label: '鍒涘缓浜�', minWidth: 120, showOverflowTooltip: true },
+    { prop: 'createTimeText', label: '鍒涘缓鏃堕棿', minWidth: 170, showOverflowTooltip: true },
+    { prop: 'memo', label: '澶囨敞', minWidth: 150, showOverflowTooltip: true },
     {
       prop: 'operation',
       label: '鎿嶄綔',
-      width: 120,
+      width: 140,
       align: 'center',
       fixed: 'right',
       formatter: (row) =>

--
Gitblit v1.9.1