From 03a05815d7ea5612206b6cfe5d6537b4d4cedc0a Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 15 九月 2026 11:41:32 +0800
Subject: [PATCH] 天津潜宇塑胶app 1.添加采购需求、库存盘点、计件单价配置、产量统计、材料消耗核算、成本核算分析页面

---
 src/pages.json                                                  |   49 
 src/api/basicData/product.js                                    |   16 
 src/api/inventoryManagement/stockCheck.js                       |   72 
 src/pages/productionDesign/bom/index.vue                        |  279 +
 src/api/qualityManagement/materialInspection.js                 |   18 
 src/pages/qualityManagement/finalInspection/add.vue             |   57 
 src/pages/qualityManagement/materialInspection/add.vue          |  124 
 src/api/productionManagement/pieceRateConfig.js                 |   47 
 src/pages/productionManagement/materialConsumption/index.vue    |  507 +++
 src/api/inventoryManagement/stockInventory.js                   |   10 
 src/pages/procurementManagement/procurementDemand/index.vue     |  315 ++
 src/utils/common.ts                                             |   15 
 src/pages/procurementManagement/procurementLedger/detail.vue    |   68 
 src/api/productionManagement/materialConsumption.js             |   11 
 src/pages/productionManagement/productionTraceability/index.vue | 1970 +++++++------
 src/pages/qualityManagement/processInspection/index.vue         |  316 ++
 src/pages/productionManagement/productionAccounting/index.vue   |   12 
 src/pages/productionManagement/costAccounting/index.vue         |  662 ++++
 src/api/productionManagement/bom.js                             |   29 
 src/pages/inventoryManagement/stockCheck/index.vue              |  832 +++++
 src/pages/inventoryManagement/stockCheck/detail.vue             |  626 ++++
 src/pages/qualityManagement/materialInspection/detail.vue       |   14 
 src/pages/qualityManagement/processInspection/add.vue           |   61 
 src/pages/qualityManagement/finalInspection/detail.vue          |   14 
 src/api/qualityManagement/productionTrace.js                    |   28 
 src/pages/works.vue                                             |   54 
 src/pages/qualityManagement/finalInspection/index.vue           |  315 ++
 src/pages/inventoryManagement/stockManagement/index.vue         |  264 +
 src/pages/productionManagement/outputStatistics/index.vue       |  412 ++
 src/pages/qualityManagement/processInspection/detail.vue        |   14 
 src/pages/productionManagement/productionReport/index.vue       |   46 
 src/api/productionManagement/productionProductMain.js           |   18 
 src/api/productionManagement/costAccounting.js                  |   29 
 src/api/procurementManagement/procurementDemand.js              |   28 
 src/config.js                                                   |    4 
 src/pages/productionManagement/productionReporting/ledger.vue   |   74 
 src/pages/qualityManagement/materialInspection/index.vue        |  298 +
 src/pages/productionManagement/pieceRateConfig/index.vue        |  698 ++++
 38 files changed, 7,345 insertions(+), 1,061 deletions(-)

diff --git a/src/api/basicData/product.js b/src/api/basicData/product.js
index 10f8fd1..f6512fe 100644
--- a/src/api/basicData/product.js
+++ b/src/api/basicData/product.js
@@ -56,3 +56,19 @@
         params: query
     })
 }
+// 鏉$爜绮剧‘鏌ヨ瑙勬牸锛堟壂鐮佸洖濉級
+export function modelByBarcode(query) {
+    return request({
+        url: '/basic/product/model/byBarcode',
+        method: 'get',
+        params: query
+    })
+}
+// 瑙勬牸鍒嗛〉鏌ヨ锛堟敮鎸佹潯鐮佹ā绯婏級
+export function pageModel(query) {
+    return request({
+        url: '/basic/product/pageModel',
+        method: 'get',
+        params: query
+    })
+}
diff --git a/src/api/inventoryManagement/stockCheck.js b/src/api/inventoryManagement/stockCheck.js
new file mode 100644
index 0000000..d2985a9
--- /dev/null
+++ b/src/api/inventoryManagement/stockCheck.js
@@ -0,0 +1,72 @@
+import request from "@/utils/request";
+
+// 鍙戣捣鐩樼偣锛堜笉浼� productModelIds = 鍏ㄩ儴搴撳瓨琛屽揩鐓э級
+export const start = params => {
+  return request({
+    url: "/stockCheck/start",
+    method: "post",
+    data: params,
+  });
+};
+
+// 鐩樼偣鍗曞垎椤靛垪琛�
+export const listPage = params => {
+  return request({
+    url: "/stockCheck/listPage",
+    method: "get",
+    params,
+  });
+};
+
+// 鐩樼偣鍗曡鎯�
+export const getDetail = id => {
+  return request({
+    url: `/stockCheck/${id}`,
+    method: "get",
+  });
+};
+
+// 鐩樼偣鏄庣粏鍒嗛〉锛坥nlyDiff=true 鍙湅宸插綍鍏ヤ笖鏈夊樊寮傜殑琛岋級
+export const detailPage = params => {
+  return request({
+    url: "/stockCheck/detailPage",
+    method: "get",
+    params,
+  });
+};
+
+// 鎵归噺褰曞叆瀹炵洏锛坆ody 涓烘暟缁勶級
+export const entry = params => {
+  return request({
+    url: "/stockCheck/entry",
+    method: "put",
+    data: params,
+  });
+};
+
+// 瀹屾垚鐩樼偣锛堝樊寮傜敓鎴愬緟瀹℃牳璋冭处鍗曟嵁锛�
+export const finish = params => {
+  return request({
+    url: "/stockCheck/finish",
+    method: "post",
+    data: params,
+  });
+};
+
+// 鍙栨秷鐩樼偣
+export const cancel = params => {
+  return request({
+    url: "/stockCheck/cancel",
+    method: "post",
+    data: params,
+  });
+};
+
+// 宸紓鍒嗘瀽鍒楄〃
+export const diffList = params => {
+  return request({
+    url: "/stockCheck/diffList",
+    method: "get",
+    params,
+  });
+};
diff --git a/src/api/inventoryManagement/stockInventory.js b/src/api/inventoryManagement/stockInventory.js
index 328657c..26a049a 100644
--- a/src/api/inventoryManagement/stockInventory.js
+++ b/src/api/inventoryManagement/stockInventory.js
@@ -1,4 +1,5 @@
 import request from "@/utils/request";
+
 // 鍒嗛〉鏌ヨ搴撳瓨璁板綍鍒楄〃
 export const getStockInventoryListPage = params => {
   return request({
@@ -76,3 +77,12 @@
     params: { productModelId },
   });
 };
+
+// 鍒嗛〉鏌ヨ搴撳瓨棰勮鍒楄〃锛堝彲鐢ㄦ暟閲� 鈮� 棰勮鍊硷紝棰勮鍊�>0锛�
+export const getStockWarnPage = params => {
+  return request({
+    url: "/stockInventory/warnPage",
+    method: "get",
+    params,
+  });
+};
diff --git a/src/api/procurementManagement/procurementDemand.js b/src/api/procurementManagement/procurementDemand.js
new file mode 100644
index 0000000..a90840d
--- /dev/null
+++ b/src/api/procurementManagement/procurementDemand.js
@@ -0,0 +1,28 @@
+import request from "@/utils/request";
+
+// 閲囪喘闇�姹傚垎椤垫煡璇�
+export const listPage = params => {
+  return request({
+    url: "/procurementDemand/listPage",
+    method: "get",
+    params,
+  });
+};
+
+// 閲囪喘闇�姹傜姸鎬佸彉鏇达紙params: { ids, status }锛宨ds 鏀寔閫楀彿鍒嗛殧鐨勫涓� id锛�
+export const changeStatus = params => {
+  return request({
+    url: "/procurementDemand/changeStatus",
+    method: "put",
+    params,
+  });
+};
+
+// 鍒犻櫎閲囪喘闇�姹傦紙body 涓� id 鏁扮粍锛屼粎寰呭鐞嗗彲鍒狅級
+export const del = ids => {
+  return request({
+    url: "/procurementDemand/del",
+    method: "delete",
+    data: ids,
+  });
+};
diff --git a/src/api/productionManagement/bom.js b/src/api/productionManagement/bom.js
index cb42000..e232630 100644
--- a/src/api/productionManagement/bom.js
+++ b/src/api/productionManagement/bom.js
@@ -80,3 +80,32 @@
     method: "delete",
   });
 }
+
+// --- BOM 鐗堟湰鍚敤/鍋滅敤銆侀綈濂楀垎鏋� ---
+
+// BOM 鍚敤/鍋滅敤锛坰tatus: 1鍚敤 0鍋滅敤锛屽惎鐢ㄦ椂鍚庣鑷姩鍋滅敤鍚岃鏍煎叾瀹冪増鏈級
+export function changeStatus(data) {
+  return request({
+    url: "/technologyBom/changeStatus",
+    method: "put",
+    data: data,
+  });
+}
+
+// BOM 榻愬鍒嗘瀽锛坉ata: { bomId, demandQty }锛�
+export function kitCheck(data) {
+  return request({
+    url: "/technologyBom/kitCheck",
+    method: "post",
+    data: data,
+  });
+}
+
+// 缂哄彛鐢熸垚閲囪喘闇�姹傦紙data 鍚� kitCheck锛�
+export function generateDemand(data) {
+  return request({
+    url: "/technologyBom/kitCheck/generateDemand",
+    method: "post",
+    data: data,
+  });
+}
diff --git a/src/api/productionManagement/costAccounting.js b/src/api/productionManagement/costAccounting.js
new file mode 100644
index 0000000..73c20ca
--- /dev/null
+++ b/src/api/productionManagement/costAccounting.js
@@ -0,0 +1,29 @@
+// 鎴愭湰鏍哥畻鍒嗘瀽椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鎴愭湰鏍哥畻鏄庣粏锛堟寜鐢熶骇璁㈠崟锛�
+export function costStat(query) {
+  return request({
+    url: "/costAccounting/stat",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鎴愭湰鏋勬垚姹囨�伙紙groupBy: month / product锛�
+export function costSummary(query) {
+  return request({
+    url: "/costAccounting/summary",
+    method: "get",
+    params: query,
+  });
+}
+
+// 缂轰环鐗╂枡娓呭崟
+export function costMissingPrice(query) {
+  return request({
+    url: "/costAccounting/missingPrice",
+    method: "get",
+    params: query,
+  });
+}
diff --git a/src/api/productionManagement/materialConsumption.js b/src/api/productionManagement/materialConsumption.js
new file mode 100644
index 0000000..13cadee
--- /dev/null
+++ b/src/api/productionManagement/materialConsumption.js
@@ -0,0 +1,11 @@
+// 鏉愭枡娑堣�楁牳绠楅〉闈㈡帴鍙�
+import request from "@/utils/request";
+
+// 鏉愭枡娑堣�楁牳绠楀垪琛紙鎸夌敓浜ц鍗暶楃墿鏂欒繑鍥烇紝闈炲垎椤碉級
+export function stat(query) {
+  return request({
+    url: "/materialConsumption/stat",
+    method: "get",
+    params: query,
+  });
+}
diff --git a/src/api/productionManagement/pieceRateConfig.js b/src/api/productionManagement/pieceRateConfig.js
new file mode 100644
index 0000000..cfabb24
--- /dev/null
+++ b/src/api/productionManagement/pieceRateConfig.js
@@ -0,0 +1,47 @@
+// 璁′欢鍗曚环閰嶇疆鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ璁′欢鍗曚环閰嶇疆
+export const listPage = params => {
+  return request({
+    url: "/pieceRateConfig/listPage",
+    method: "get",
+    params,
+  });
+};
+
+// 鏂板璁′欢鍗曚环閰嶇疆
+export const add = data => {
+  return request({
+    url: "/pieceRateConfig/add",
+    method: "post",
+    data,
+  });
+};
+
+// 淇敼璁′欢鍗曚环閰嶇疆
+export const update = data => {
+  return request({
+    url: "/pieceRateConfig/update",
+    method: "put",
+    data,
+  });
+};
+
+// 鍚敤/鍋滅敤
+export const changeStatus = data => {
+  return request({
+    url: "/pieceRateConfig/changeStatus",
+    method: "put",
+    data,
+  });
+};
+
+// 鎵归噺鍒犻櫎锛宒ata 涓� id 鏁扮粍
+export const batchDelete = data => {
+  return request({
+    url: "/pieceRateConfig/batchDelete",
+    method: "delete",
+    data,
+  });
+};
diff --git a/src/api/productionManagement/productionProductMain.js b/src/api/productionManagement/productionProductMain.js
index 4242e1b..1145965 100644
--- a/src/api/productionManagement/productionProductMain.js
+++ b/src/api/productionManagement/productionProductMain.js
@@ -27,3 +27,21 @@
         params: query,
     });
 }
+
+// 璁′欢宸ヨ祫姹囨�伙紙鎸変汉鍛橈級
+export function wageSummary(query) {
+    return request({
+        url: "/productionProductMain/wageSummary",
+        method: "get",
+        params: query,
+    });
+}
+
+// 浜ч噺缁熻锛坉imension: device/operation/product锛�
+export function outputStat(query) {
+    return request({
+        url: "/productionProductMain/outputStat",
+        method: "get",
+        params: query,
+    });
+}
diff --git a/src/api/qualityManagement/materialInspection.js b/src/api/qualityManagement/materialInspection.js
index 6f458b4..e1a2b9e 100644
--- a/src/api/qualityManagement/materialInspection.js
+++ b/src/api/qualityManagement/materialInspection.js
@@ -163,4 +163,22 @@
         url: "/productProcess/list",
         method: "get",
     });
+}
+
+// 鏌ヨ涓嶅悎鏍艰褰曞垪琛紙inspectState锛�0寰呭鐞�/1宸插鐞嗭級
+export function qualityUnqualifiedListPage(query) {
+    return request({
+        url: '/quality/qualityUnqualified/listPage',
+        method: 'get',
+        params: query,
+    })
+}
+
+// 澶勭悊涓嶅悎鏍艰褰曪紙涓嶄紶 dealName/dealTime锛屽悗绔嚜鍔ㄥ彇褰撳墠鐧诲綍浜�/鏃堕棿锛�
+export function qualityUnqualifiedDeal(data) {
+    return request({
+        url: '/quality/qualityUnqualified/deal',
+        method: 'post',
+        data: data,
+    })
 }
\ No newline at end of file
diff --git a/src/api/qualityManagement/productionTrace.js b/src/api/qualityManagement/productionTrace.js
new file mode 100644
index 0000000..f900032
--- /dev/null
+++ b/src/api/qualityManagement/productionTrace.js
@@ -0,0 +1,28 @@
+import request from "@/utils/request";
+
+// 姝e悜杩芥函锛氭垚鍝佹壒鍙� -> 鍏ㄩ摼璺�
+export const traceForward = params => {
+  return request({
+    url: "/quality/trace/forward",
+    method: "get",
+    params,
+  });
+};
+
+// 鍙嶅悜杩芥函锛氬師鏂欐壒鍙� -> 鍙楀奖鍝嶆垚鍝�
+export const traceReverse = params => {
+  return request({
+    url: "/quality/trace/reverse",
+    method: "get",
+    params,
+  });
+};
+
+// 鎵瑰彿鍊欓�変笅鎷夛紙type: finished | material锛�
+export const traceBatchOptions = params => {
+  return request({
+    url: "/quality/trace/batchOptions",
+    method: "get",
+    params,
+  });
+};
diff --git a/src/config.js b/src/config.js
index cc37beb..9fe25a9 100644
--- a/src/config.js
+++ b/src/config.js
@@ -1,7 +1,7 @@
 // 搴旂敤鍏ㄥ眬閰嶇疆
 const config = {
-  baseUrl: "http://1.15.17.182:9072",
-  fileUrl: "http://1.15.17.182:9071",
+  baseUrl: "http://1.15.17.182:9116",
+  fileUrl: "http://1.15.17.182:9115",
   // 搴旂敤淇℃伅
   appInfo: {
     // 搴旂敤鍚嶇О
diff --git a/src/pages.json b/src/pages.json
index 1aa49ad..ce3fc60 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -353,6 +353,13 @@
       }
     },
     {
+      "path": "pages/procurementManagement/procurementDemand/index",
+      "style": {
+        "navigationBarTitleText": "閲囪喘闇�姹�",
+        "navigationStyle": "custom"
+      }
+    },
+    {
       "path": "pages/basicData/supplierManage/index",
       "style": {
         "navigationBarTitleText": "渚涘簲鍟嗙鐞�",
@@ -894,6 +901,34 @@
       }
     },
     {
+      "path": "pages/productionManagement/pieceRateConfig/index",
+      "style": {
+        "navigationBarTitleText": "璁′欢鍗曚环閰嶇疆",
+        "navigationStyle": "custom"
+      }
+    },
+    {
+      "path": "pages/productionManagement/outputStatistics/index",
+      "style": {
+        "navigationBarTitleText": "浜ч噺缁熻",
+        "navigationStyle": "custom"
+      }
+    },
+    {
+      "path": "pages/productionManagement/materialConsumption/index",
+      "style": {
+        "navigationBarTitleText": "鏉愭枡娑堣�楁牳绠�",
+        "navigationStyle": "custom"
+      }
+    },
+    {
+      "path": "pages/productionManagement/costAccounting/index",
+      "style": {
+        "navigationBarTitleText": "鎴愭湰鏍哥畻鍒嗘瀽",
+        "navigationStyle": "custom"
+      }
+    },
+    {
       "path": "pages/inventoryManagement/receiptManagement/index",
       "style": {
         "navigationBarTitleText": "鑷畾涔夊叆搴�",
@@ -922,6 +957,20 @@
       }
     },
     {
+      "path": "pages/inventoryManagement/stockCheck/index",
+      "style": {
+        "navigationBarTitleText": "搴撳瓨鐩樼偣",
+        "navigationStyle": "custom"
+      }
+    },
+    {
+      "path": "pages/inventoryManagement/stockCheck/detail",
+      "style": {
+        "navigationBarTitleText": "鐩樼偣璇︽儏",
+        "navigationStyle": "custom"
+      }
+    },
+    {
       "path": "pages/safeProduction/safeQualifications/index",
       "style": {
         "navigationBarTitleText": "瑙勭▼涓庤祫璐�",
diff --git a/src/pages/inventoryManagement/stockCheck/detail.vue b/src/pages/inventoryManagement/stockCheck/detail.vue
new file mode 100644
index 0000000..d320220
--- /dev/null
+++ b/src/pages/inventoryManagement/stockCheck/detail.vue
@@ -0,0 +1,626 @@
+<template>
+  <view class="stock-check-detail">
+    <PageHeader title="鐩樼偣璇︽儏"
+                @back="goBack" />
+
+    <scroll-view class="scroll-body"
+                 scroll-y
+                 @scrolltolower="loadMore">
+      <!-- 涓昏〃淇℃伅 -->
+      <view class="main-card">
+        <view class="main-header">
+          <text class="main-no">{{ mainInfo.checkNo || '-' }}</text>
+          <up-tag :text="getStatusText(mainInfo.status)"
+                  :type="getStatusType(mainInfo.status)"
+                  size="mini" />
+        </view>
+        <view class="detail-row">
+          <text class="detail-label">鐩樼偣鍚嶇О</text>
+          <text class="detail-value">{{ mainInfo.checkName || '-' }}</text>
+        </view>
+        <view class="detail-row">
+          <text class="detail-label">鐩樼偣鏃ユ湡</text>
+          <text class="detail-value">{{ formatDate(mainInfo.checkDate) }}</text>
+        </view>
+        <view class="detail-row">
+          <text class="detail-label">鐩樼偣浜�</text>
+          <text class="detail-value">{{ mainInfo.checker || '-' }}</text>
+        </view>
+        <view class="detail-row">
+          <text class="detail-label">鐩樼偣绉嶇被鏁�</text>
+          <text class="detail-value">{{ Number(mainInfo.totalKinds) || 0 }}</text>
+        </view>
+        <view class="detail-row">
+          <text class="detail-label">宸紓绉嶇被鏁�</text>
+          <text class="detail-value"
+                :class="{ danger: Number(mainInfo.diffKinds) > 0 }">{{ Number(mainInfo.diffKinds) || 0 }}</text>
+        </view>
+        <view class="detail-row"
+              v-if="mainInfo.remark">
+          <text class="detail-label">澶囨敞</text>
+          <text class="detail-value">{{ mainInfo.remark }}</text>
+        </view>
+      </view>
+
+      <!-- 宸ュ叿鏉� -->
+      <view class="toolbar">
+        <view class="switch-row">
+          <text class="switch-label">鍙湅宸紓</text>
+          <up-switch v-model="onlyDiff"
+                     size="small"
+                     activeColor="#2979ff"
+                     @change="handleQuery"></up-switch>
+        </view>
+        <view class="toolbar-btns">
+          <up-button type="primary"
+                     size="small"
+                     plain
+                     text="宸紓鍒嗘瀽"
+                     @click="openDiffPopup"></up-button>
+        </view>
+      </view>
+
+      <!-- 鏄庣粏鍒楄〃 -->
+      <view v-if="detailList.length > 0">
+        <view v-for="row in detailList"
+              :key="row.id"
+              class="ledger-item">
+          <view class="item-header">
+            <view class="item-left">
+              <view class="document-icon">
+                <up-icon name="file-text"
+                         size="16"
+                         color="#ffffff"></up-icon>
+              </view>
+              <text class="item-id">{{ row.productName || '-' }}</text>
+            </view>
+            <up-tag v-if="Number(row.adjusted) === 1"
+                    text="宸茬敓鎴愯皟璐�"
+                    type="success"
+                    size="mini" />
+          </view>
+          <up-divider></up-divider>
+          <view class="item-details">
+            <view class="detail-row">
+              <text class="detail-label">瑙勬牸鍨嬪彿</text>
+              <text class="detail-value">{{ row.productModel || '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鍗曚綅</text>
+              <text class="detail-value">{{ row.unit || '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鎵瑰彿</text>
+              <text class="detail-value">{{ row.batchNo || '-' }}</text>
+            </view>
+            <view class="qty-row">
+              <view class="qty-item">
+                <text class="qty-label">璐﹂潰鏁伴噺</text>
+                <text class="qty-value">{{ formatQty(row.bookQty) }}</text>
+              </view>
+              <view class="qty-item">
+                <text class="qty-label">瀹炵洏鏁伴噺</text>
+                <up-input class="qty-input"
+                          type="digit"
+                          :disabled="isFinished"
+                          v-model="row.actualQty"
+                          placeholder="鏈綍鍏�"
+                          @change="onQtyChange(row)"
+                          @blur="onQtyBlur(row)" />
+              </view>
+              <view class="qty-item">
+                <text class="qty-label">宸紓</text>
+                <text class="qty-value"
+                      :class="diffClass(row)">{{ formatDiff(row) }}</text>
+              </view>
+            </view>
+            <view class="detail-row remark-row">
+              <text class="detail-label">澶囨敞</text>
+              <up-input class="remark-input"
+                        :disabled="isFinished"
+                        v-model="row.remark"
+                        placeholder="璇疯緭鍏ュ娉�"
+                        maxlength="200"
+                        @change="onRemarkChange(row)" />
+            </view>
+          </view>
+        </view>
+        <up-loadmore :status="loadStatus" />
+      </view>
+      <view v-else-if="!loading"
+            class="no-data">
+        <up-empty mode="data"
+                  text="鏆傛棤鐩樼偣鏄庣粏"></up-empty>
+      </view>
+      <view class="tip-text"
+            v-if="!isFinished">
+        鎻愮ず锛氬畬鎴愮洏鐐瑰悗宸紓灏嗕互寰呭鏍稿嚭鍏ュ簱璁板綍褰㈠紡瀛樺湪锛岄渶鍦ㄥ叆搴撹褰�/鍑哄簱璁板綍椤靛鏍搁�氳繃鍚庡簱瀛樻墠鍙樺姩銆�
+      </view>
+    </scroll-view>
+
+    <!-- 搴曢儴鎿嶄綔 -->
+    <view class="footer-btns">
+      <up-button type="primary"
+                 text="鎵归噺淇濆瓨"
+                 :loading="saving"
+                 :disabled="isFinished"
+                 @click="handleSave"></up-button>
+      <up-button v-if="!isFinished"
+                 type="success"
+                 text="瀹屾垚鐩樼偣"
+                 @click="handleFinish"></up-button>
+    </view>
+
+    <!-- 宸紓鍒嗘瀽寮圭獥 -->
+    <up-popup :show="showDiffPopup"
+              mode="bottom"
+              :round="10"
+              closeable
+              @close="showDiffPopup = false">
+      <view class="form-popup">
+        <view class="popup-header">
+          <text class="popup-title">宸紓鍒嗘瀽</text>
+        </view>
+        <scroll-view class="popup-content"
+                     scroll-y>
+          <view v-if="diffRows.length">
+            <view v-for="(row, index) in diffRows"
+                  :key="row.id || index"
+                  class="diff-item">
+              <view class="diff-head">
+                <text class="diff-name">{{ row.productName || '-' }}</text>
+                <text class="diff-qty"
+                      :class="Number(row.diffQty) > 0 ? 'diff-up' : 'diff-down'">{{ formatSigned(row.diffQty) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">瑙勬牸鍨嬪彿</text>
+                <text class="detail-value">{{ row.productModel || '-' }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鎵瑰彿</text>
+                <text class="detail-value">{{ row.batchNo || '-' }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">璐﹂潰 / 瀹炵洏</text>
+                <text class="detail-value">{{ formatQty(row.bookQty) }} / {{ formatQty(row.actualQty) }}</text>
+              </view>
+            </view>
+          </view>
+          <view v-else
+                class="spec-empty-box">
+            <up-empty mode="data"
+                      text="鏆傛棤宸紓鏁版嵁"></up-empty>
+          </view>
+        </scroll-view>
+      </view>
+    </up-popup>
+  </view>
+</template>
+
+<script setup>
+  import { ref, reactive, computed } from "vue";
+  import { onLoad } from "@dcloudio/uni-app";
+  import dayjs from "dayjs";
+  import PageHeader from "@/components/PageHeader.vue";
+  import { toast } from "@/utils/common";
+  import {
+    getDetail,
+    detailPage,
+    entry,
+    finish,
+    diffList,
+  } from "@/api/inventoryManagement/stockCheck.js";
+
+  const mainId = ref(null);
+  const mainInfo = ref({});
+  const detailList = ref([]);
+  const loading = ref(false);
+  const saving = ref(false);
+  const loadStatus = ref("loadmore");
+  const page = reactive({ current: 1, size: 10, total: 0 });
+  const onlyDiff = ref(false);
+  const dirtyIds = ref([]);
+
+  const showDiffPopup = ref(false);
+  const diffRows = ref([]);
+
+  const isFinished = computed(() => Number(mainInfo.value.status) !== 0);
+
+  const goBack = () => {
+    uni.navigateBack();
+  };
+
+  const formatDate = val => (val ? dayjs(val).format("YYYY-MM-DD") : "-");
+
+  const getStatusText = status => {
+    const map = { 0: "鐩樼偣涓�", 1: "宸插畬鎴�", 2: "宸插彇娑�" };
+    return map[status] || "鏈煡";
+  };
+
+  const getStatusType = status => {
+    const map = { 0: "warning", 1: "success", 2: "info" };
+    return map[status] || "info";
+  };
+
+  const formatQty = val => {
+    if (val === null || val === undefined || val === "") return "-";
+    return Number(val) || 0;
+  };
+
+  const formatSigned = val => {
+    const n = Number(val) || 0;
+    return n > 0 ? `+${n}` : `${n}`;
+  };
+
+  const formatDiff = row => {
+    const a = row.actualQty;
+    if (a === null || a === undefined || a === "") return "-";
+    const d = Number(a) - (Number(row.bookQty) || 0);
+    if (!Number.isFinite(d)) return "-";
+    return formatSigned(Number(d.toFixed(4)));
+  };
+
+  const diffClass = row => {
+    const a = row.actualQty;
+    if (a === null || a === undefined || a === "") return "";
+    const d = Number(a) - (Number(row.bookQty) || 0);
+    if (d > 0) return "diff-up";
+    if (d < 0) return "diff-down";
+    return "";
+  };
+
+  const loadMain = async () => {
+    try {
+      const res = await getDetail(mainId.value);
+      mainInfo.value = res?.data || {};
+    } catch (e) {
+      // 璇锋眰澶辫触宸茬敱 request 缁熶竴鎻愮ず
+    }
+  };
+
+  const getList = () => {
+    if (loading.value) return;
+    loading.value = true;
+    loadStatus.value = "loading";
+    detailPage({
+      mainId: mainId.value,
+      onlyDiff: onlyDiff.value,
+      current: page.current,
+      size: page.size,
+    })
+      .then(res => {
+        const records = res?.data?.records || [];
+        detailList.value =
+          page.current === 1 ? records : [...detailList.value, ...records];
+        page.total = res?.data?.total || 0;
+        loadStatus.value =
+          detailList.value.length >= page.total ? "nomore" : "loadmore";
+      })
+      .catch(() => {
+        loadStatus.value = "loadmore";
+      })
+      .finally(() => {
+        loading.value = false;
+      });
+  };
+
+  const handleQuery = () => {
+    page.current = 1;
+    detailList.value = [];
+    dirtyIds.value = [];
+    getList();
+  };
+
+  const loadMore = () => {
+    if (loadStatus.value === "nomore" || loading.value) return;
+    page.current++;
+    getList();
+  };
+
+  const markDirty = row => {
+    if (row.id == null) return;
+    if (!dirtyIds.value.includes(row.id)) dirtyIds.value.push(row.id);
+  };
+
+  const onQtyChange = row => {
+    markDirty(row);
+  };
+
+  const onRemarkChange = row => {
+    markDirty(row);
+  };
+
+  // 瀹炵洏鏁伴噺涓嶅厑璁歌礋鏁�
+  const onQtyBlur = row => {
+    const val = row.actualQty;
+    if (val === null || val === undefined || val === "") return;
+    let n = Number(val);
+    if (!Number.isFinite(n)) {
+      row.actualQty = "";
+      return;
+    }
+    if (n < 0) {
+      n = 0;
+      toast("瀹炵洏鏁伴噺涓嶈兘涓鸿礋鏁帮紝宸茬疆涓� 0");
+    }
+    row.actualQty = n;
+    markDirty(row);
+  };
+
+  const handleSave = async () => {
+    if (isFinished.value) {
+      toast("鐩樼偣宸茬粨鏉燂紝鏃犳硶褰曞叆");
+      return;
+    }
+    const rows = detailList.value.filter(
+      r => dirtyIds.value.includes(r.id) && r.actualQty !== "" && r.actualQty !== null && r.actualQty !== undefined
+    );
+    if (!rows.length) {
+      toast("娌℃湁闇�瑕佷繚瀛樼殑瀹炵洏褰曞叆");
+      return;
+    }
+    const negative = rows.find(r => Number(r.actualQty) < 0);
+    if (negative) {
+      toast("瀹炵洏鏁伴噺涓嶈兘涓鸿礋鏁�");
+      return;
+    }
+    saving.value = true;
+    uni.showLoading({ title: "鎻愪氦涓�...", mask: true });
+    try {
+      await entry(
+        rows.map(r => ({
+          id: r.id,
+          actualQty: Number(r.actualQty) || 0,
+          remark: r.remark || undefined,
+        }))
+      );
+      uni.hideLoading();
+      uni.showToast({ title: "淇濆瓨鎴愬姛", icon: "success" });
+      dirtyIds.value = [];
+      loadMain();
+      handleQuery();
+    } catch (e) {
+      uni.hideLoading();
+    } finally {
+      saving.value = false;
+    }
+  };
+
+  const handleFinish = () => {
+    uni.showModal({
+      title: "瀹屾垚鐩樼偣",
+      content: "宸紓灏嗙敓鎴愯皟璐﹀崟鎹紝瀹℃牳閫氳繃鍚庤处闈㈢敓鏁堬紝鏄惁缁х画锛�",
+      success: async res => {
+        if (!res.confirm) return;
+        uni.showLoading({ title: "澶勭悊涓�...", mask: true });
+        try {
+          const r = await finish({ id: mainId.value });
+          uni.hideLoading();
+          uni.showToast({ title: r?.msg || "鐩樼偣宸插畬鎴�", icon: "none" });
+          loadMain();
+          handleQuery();
+        } catch (e) {
+          uni.hideLoading();
+        }
+      },
+    });
+  };
+
+  const openDiffPopup = async () => {
+    showDiffPopup.value = true;
+    try {
+      const res = await diffList({ mainId: mainId.value });
+      diffRows.value = res?.data || [];
+    } catch (e) {
+      diffRows.value = [];
+    }
+  };
+
+  onLoad(options => {
+    mainId.value = options?.id;
+    if (!mainId.value) {
+      toast("缂哄皯鐩樼偣鍗曞弬鏁�");
+      return;
+    }
+    loadMain();
+    getList();
+  });
+</script>
+
+<style scoped lang="scss">
+  @import "@/styles/procurement-common.scss";
+
+  .stock-check-detail {
+    height: 100vh;
+    background: #f8f9fa;
+    display: flex;
+    flex-direction: column;
+  }
+
+  .scroll-body {
+    flex: 1;
+    height: 0;
+    padding: 20rpx 20rpx 120rpx;
+    box-sizing: border-box;
+  }
+
+  .main-card {
+    background: #fff;
+    border-radius: 16rpx;
+    padding: 24rpx;
+    margin-bottom: 20rpx;
+    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+
+    .main-header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding-bottom: 16rpx;
+      margin-bottom: 16rpx;
+      border-bottom: 1rpx solid #f0f0f0;
+
+      .main-no {
+        font-size: 30rpx;
+        font-weight: bold;
+        color: #303133;
+      }
+    }
+  }
+
+  .detail-value.danger {
+    color: #ee0a24;
+    font-weight: 600;
+  }
+
+  .toolbar {
+    background: #fff;
+    border-radius: 16rpx;
+    padding: 16rpx 24rpx;
+    margin-bottom: 20rpx;
+
+    .switch-row {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 0 0 16rpx;
+
+      .switch-label {
+        font-size: 28rpx;
+        color: #333;
+      }
+    }
+
+    .toolbar-btns {
+      display: flex;
+      gap: 20rpx;
+      justify-content: flex-end;
+    }
+  }
+
+  .qty-row {
+    display: flex;
+    align-items: center;
+    gap: 16rpx;
+    margin: 16rpx 0;
+
+    .qty-item {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+
+      .qty-label {
+        font-size: 22rpx;
+        color: #999;
+        margin-bottom: 8rpx;
+      }
+
+      .qty-value {
+        font-size: 30rpx;
+        font-weight: bold;
+        color: #303133;
+
+        &.diff-up {
+          color: #52c41a;
+        }
+
+        &.diff-down {
+          color: #f56c6c;
+        }
+      }
+
+      .qty-input {
+        width: 100%;
+        background: #f5f7fa;
+        border-radius: 8rpx;
+        padding: 0 8rpx;
+      }
+    }
+  }
+
+  .remark-row {
+    align-items: center;
+
+    .remark-input {
+      flex: 1;
+      background: #f5f7fa;
+      border-radius: 8rpx;
+      padding: 0 12rpx;
+      margin-left: 16rpx;
+    }
+  }
+
+  .no-data {
+    padding-top: 150rpx;
+  }
+
+  .tip-text {
+    font-size: 22rpx;
+    color: #909399;
+    line-height: 1.6;
+    padding: 16rpx 8rpx 40rpx;
+  }
+
+  .form-popup {
+    height: 70vh;
+    display: flex;
+    flex-direction: column;
+    background: #f5f5f5;
+  }
+
+  .popup-content {
+    flex: 1;
+    height: 0;
+    padding: 12px;
+  }
+
+  .diff-item {
+    background: #fff;
+    border-radius: 10px;
+    padding: 12px;
+    margin-bottom: 10px;
+
+    .diff-head {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      margin-bottom: 10px;
+
+      .diff-name {
+        font-size: 14px;
+        font-weight: 600;
+        color: #333;
+      }
+
+      .diff-qty {
+        font-size: 16px;
+        font-weight: 600;
+      }
+    }
+  }
+
+  .diff-up {
+    color: #52c41a;
+  }
+
+  .diff-down {
+    color: #f56c6c;
+  }
+
+  .spec-empty-box {
+    padding-top: 60px;
+  }
+
+  .popup-footer {
+    display: flex;
+    gap: 12px;
+    padding: 12px 16px;
+    background: #fff;
+    border-top: 1px solid #f0f0f0;
+  }
+
+  .btn-cancel,
+  .btn-confirm {
+    flex: 1;
+  }
+</style>
diff --git a/src/pages/inventoryManagement/stockCheck/index.vue b/src/pages/inventoryManagement/stockCheck/index.vue
new file mode 100644
index 0000000..48fd49d
--- /dev/null
+++ b/src/pages/inventoryManagement/stockCheck/index.vue
@@ -0,0 +1,832 @@
+<template>
+  <view class="stock-check-page">
+    <PageHeader title="搴撳瓨鐩樼偣"
+                @back="goBack" />
+
+    <!-- 鎼滅储鍖哄煙 -->
+    <view class="search-section">
+      <view class="search-row">
+        <view class="search-input">
+          <up-input class="search-text"
+                    v-model="searchForm.checkNo"
+                    placeholder="鐩樼偣鍗曞彿"
+                    clearable
+                    @confirm="handleQuery" />
+        </view>
+        <view class="search-input">
+          <up-input class="search-text"
+                    v-model="searchForm.checkName"
+                    placeholder="鐩樼偣鍚嶇О"
+                    clearable
+                    @confirm="handleQuery" />
+        </view>
+      </view>
+      <view class="filter-row">
+        <view class="filter-item"
+              @click="showStatusSheet = true">
+          <text class="filter-text">{{ statusText }}</text>
+          <up-icon name="arrow-down"
+                   size="14"
+                   color="#909399"></up-icon>
+        </view>
+        <view class="filter-item date-item"
+              @click="showCalendar = true">
+          <up-icon name="calendar"
+                   size="16"
+                   color="#909399"></up-icon>
+          <text class="filter-text"
+                :class="{ placeholder: !searchForm.beginTime }">{{ dateRangeText }}</text>
+          <up-icon v-if="searchForm.beginTime"
+                   name="close-circle-fill"
+                   size="16"
+                   color="#c0c4cc"
+                   @click.stop="handleClearDate"></up-icon>
+        </view>
+        <view class="search-btn">
+          <up-button type="primary"
+                     size="small"
+                     text="鎼滅储"
+                     @click="handleQuery"></up-button>
+        </view>
+      </view>
+    </view>
+
+    <!-- 鍒楄〃鍖哄煙 -->
+    <scroll-view scroll-y
+                 class="list-container"
+                 v-if="tableData.length > 0"
+                 @scrolltolower="loadMore">
+      <view v-for="item in tableData"
+            :key="item.id"
+            class="ledger-item">
+        <view class="item-header">
+          <view class="item-left">
+            <view class="document-icon">
+              <up-icon name="file-text"
+                       size="16"
+                       color="#ffffff"></up-icon>
+            </view>
+            <text class="item-id">{{ item.checkNo || '-' }}</text>
+          </view>
+          <view class="item-right">
+            <up-tag :text="getStatusText(item.status)"
+                    :type="getStatusType(item.status)"
+                    size="mini" />
+          </view>
+        </view>
+        <up-divider></up-divider>
+        <view class="item-details">
+          <view class="detail-row">
+            <text class="detail-label">鐩樼偣鍚嶇О</text>
+            <text class="detail-value">{{ item.checkName || '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">鐩樼偣鏃ユ湡</text>
+            <text class="detail-value">{{ formatDate(item.checkDate) }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">鐩樼偣浜�</text>
+            <text class="detail-value">{{ item.checker || '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">鐩樼偣绉嶇被鏁�</text>
+            <text class="detail-value">{{ Number(item.totalKinds) || 0 }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">宸紓绉嶇被鏁�</text>
+            <text class="detail-value"
+                  :class="{ danger: Number(item.diffKinds) > 0 }">{{ Number(item.diffKinds) || 0 }}</text>
+          </view>
+        </view>
+        <view class="action-buttons">
+          <up-button type="info"
+                     size="small"
+                     plain
+                     text="璇︽儏"
+                     @click="goDetail(item)"></up-button>
+          <up-button v-if="item.status === 0"
+                     type="success"
+                     size="small"
+                     plain
+                     text="瀹屾垚"
+                     @click="handleFinish(item)"></up-button>
+          <up-button v-if="item.status === 0"
+                     type="error"
+                     size="small"
+                     plain
+                     text="鍙栨秷"
+                     @click="handleCancel(item)"></up-button>
+        </view>
+      </view>
+      <up-loadmore :status="loadStatus" />
+    </scroll-view>
+    <view v-else-if="!loading"
+          class="no-data">
+      <up-empty mode="data"
+                text="鏆傛棤鐩樼偣鏁版嵁"></up-empty>
+    </view>
+
+    <!-- 搴曢儴鎿嶄綔 -->
+    <view class="footer-btns">
+      <up-button type="primary"
+                 text="鍙戣捣鐩樼偣"
+                 @click="openAdd"></up-button>
+    </view>
+
+    <!-- 鍙戣捣鐩樼偣寮圭獥 -->
+    <up-popup :show="showAddPopup"
+              mode="bottom"
+              :round="10"
+              closeable
+              @close="showAddPopup = false">
+      <view class="form-popup">
+        <view class="popup-header">
+          <text class="popup-title">鍙戣捣鐩樼偣</text>
+        </view>
+        <scroll-view class="popup-content"
+                     scroll-y>
+          <view class="form-field">
+            <text class="field-label required">鐩樼偣鍚嶇О</text>
+            <up-input v-model="form.checkName"
+                      placeholder="璇疯緭鍏ョ洏鐐瑰悕绉�"
+                      maxlength="100" />
+          </view>
+          <view class="form-field">
+            <text class="field-label">鐩樼偣鏃ユ湡</text>
+            <view class="pick-field"
+                  @click="showDatePicker = true">
+              <up-input class="pick-input"
+                        v-model="form.checkDate"
+                        readonly
+                        placeholder="璇烽�夋嫨鐩樼偣鏃ユ湡" />
+              <up-icon name="arrow-right"
+                       color="#c0c4cc"></up-icon>
+            </view>
+          </view>
+          <view class="form-field">
+            <text class="field-label">鐩樼偣浜�</text>
+            <up-input v-model="form.checker"
+                      placeholder="璇疯緭鍏ョ洏鐐逛汉" />
+          </view>
+          <view class="form-field">
+            <text class="field-label">澶囨敞</text>
+            <up-input v-model="form.remark"
+                      placeholder="璇疯緭鍏ュ娉�"
+                      maxlength="200" />
+          </view>
+          <view class="form-field">
+            <text class="field-label">鎸囧畾浜у搧瑙勬牸</text>
+            <view class="spec-box">
+              <view v-if="selectedModels.length"
+                    class="spec-tags">
+                <view v-for="m in selectedModels"
+                      :key="m.id"
+                      class="spec-tag"
+                      @click="removeModel(m.id)">
+                  <text class="spec-tag-text">{{ m.model || m.productName }}</text>
+                  <up-icon name="close"
+                           size="12"
+                           color="#2979ff"></up-icon>
+                </view>
+              </view>
+              <text v-else
+                    class="spec-empty">鏈�夋嫨鏃跺鍏ㄩ儴搴撳瓨琛屽仛蹇収</text>
+              <up-button type="primary"
+                         size="small"
+                         plain
+                         text="閫夋嫨瑙勬牸"
+                         @click="openSpecPicker"></up-button>
+            </view>
+          </view>
+        </scroll-view>
+        <view class="popup-footer">
+          <up-button class="btn-cancel"
+                     text="鍙栨秷"
+                     @click="showAddPopup = false"></up-button>
+          <up-button class="btn-confirm"
+                     type="primary"
+                     text="纭"
+                     :loading="submitting"
+                     @click="handleSubmit"></up-button>
+        </view>
+      </view>
+    </up-popup>
+
+    <!-- 瑙勬牸閫夋嫨寮圭獥 -->
+    <up-popup :show="showSpecPopup"
+              mode="bottom"
+              :round="10"
+              closeable
+              @close="showSpecPopup = false">
+      <view class="form-popup">
+        <view class="popup-header">
+          <text class="popup-title">閫夋嫨浜у搧瑙勬牸</text>
+        </view>
+        <view class="spec-category">
+          <scroll-view scroll-x
+                       class="category-scroll">
+            <view class="category-list">
+              <view v-for="p in productOptions"
+                    :key="p.id"
+                    class="category-tag"
+                    :class="{ active: p.id === activeProductId }"
+                    @click="selectProduct(p)">
+                <text>{{ p.productName }}</text>
+              </view>
+            </view>
+          </scroll-view>
+        </view>
+        <scroll-view class="popup-content"
+                     scroll-y>
+          <view v-if="modelOptions.length">
+            <view v-for="m in modelOptions"
+                  :key="m.id"
+                  class="model-item"
+                  @click="toggleModel(m)">
+              <view class="model-info">
+                <text class="model-name">{{ m.model || '-' }}</text>
+                <text class="model-unit">{{ m.unit || '' }}</text>
+              </view>
+              <up-icon :name="isSelected(m.id) ? 'checkmark-circle-fill' : 'circle'"
+                       size="22"
+                       :color="isSelected(m.id) ? '#2979ff' : '#c0c4cc'"></up-icon>
+            </view>
+          </view>
+          <view v-else
+                class="spec-empty-box">
+            <up-empty mode="data"
+                      text="鏆傛棤瑙勬牸鏁版嵁"></up-empty>
+          </view>
+        </scroll-view>
+        <view class="popup-footer">
+          <up-button class="btn-cancel"
+                     text="娓呯┖"
+                     @click="selectedModels = []"></up-button>
+          <up-button class="btn-confirm"
+                     type="primary"
+                     text="纭畾"
+                     @click="showSpecPopup = false"></up-button>
+        </view>
+      </view>
+    </up-popup>
+
+    <!-- 鐘舵�佺瓫閫� -->
+    <up-action-sheet :show="showStatusSheet"
+                     :actions="statusActions"
+                     title="鐩樼偣鐘舵��"
+                     @select="onStatusSelect"
+                     @close="showStatusSheet = false"></up-action-sheet>
+
+    <!-- 鏃ユ湡鍖洪棿 -->
+    <up-calendar :show="showCalendar"
+                 mode="range"
+                 :maxDate="maxDate"
+                 minDate="2022-01-01"
+                 :monthNum="monthNum"
+                 @confirm="onDateConfirm"
+                 @close="showCalendar = false"></up-calendar>
+
+    <!-- 鐩樼偣鏃ユ湡 -->
+    <up-popup :show="showDatePicker"
+              mode="bottom"
+              @close="showDatePicker = false">
+      <up-datetime-picker :show="true"
+                          v-model="dateValue"
+                          mode="date"
+                          @confirm="onCheckDateConfirm"
+                          @cancel="showDatePicker = false" />
+    </up-popup>
+  </view>
+</template>
+
+<script setup>
+  import { ref, reactive, computed } from "vue";
+  import { onShow } from "@dcloudio/uni-app";
+  import dayjs from "dayjs";
+  import PageHeader from "@/components/PageHeader.vue";
+  import { toast } from "@/utils/common";
+  import { productTreeList, modelList } from "@/api/basicData/product.js";
+  import {
+    start,
+    listPage,
+    finish,
+    cancel,
+  } from "@/api/inventoryManagement/stockCheck.js";
+
+  const loading = ref(false);
+  const submitting = ref(false);
+  const loadStatus = ref("loadmore");
+  const tableData = ref([]);
+  const page = reactive({ current: 1, size: 10, total: 0 });
+
+  const searchForm = reactive({
+    checkNo: "",
+    checkName: "",
+    status: null,
+    beginTime: "",
+    endTime: "",
+  });
+
+  const maxDate = dayjs().format("YYYY-MM-DD");
+  const monthNum = computed(() => {
+    const min = dayjs("2022-01-01");
+    const max = dayjs(maxDate);
+    return max.diff(min, "month") + 1;
+  });
+  const showCalendar = ref(false);
+  const showStatusSheet = ref(false);
+  const showDatePicker = ref(false);
+  const dateValue = ref(new Date().getTime());
+
+  const statusActions = ref([
+    { name: "鍏ㄩ儴", value: null },
+    { name: "鐩樼偣涓�", value: 0 },
+    { name: "宸插畬鎴�", value: 1 },
+    { name: "宸插彇娑�", value: 2 },
+  ]);
+
+  const statusText = computed(() => {
+    const hit = statusActions.value.find(a => a.value === searchForm.status);
+    return hit ? hit.name : "鍏ㄩ儴";
+  });
+
+  const dateRangeText = computed(() => {
+    if (searchForm.beginTime && searchForm.endTime) {
+      return `${searchForm.beginTime.slice(0, 10)} 鑷� ${searchForm.endTime.slice(0, 10)}`;
+    }
+    return "閫夋嫨鏃ユ湡鍖洪棿";
+  });
+
+  // 鍙戣捣鐩樼偣琛ㄥ崟
+  const showAddPopup = ref(false);
+  const form = reactive({
+    checkName: "",
+    checkDate: dayjs().format("YYYY-MM-DD"),
+    checker: "",
+    remark: "",
+  });
+
+  // 瑙勬牸閫夋嫨
+  const showSpecPopup = ref(false);
+  const productOptions = ref([]);
+  const modelOptions = ref([]);
+  const activeProductId = ref(null);
+  const selectedModels = ref([]);
+
+  const goBack = () => {
+    uni.navigateBack();
+  };
+
+  const formatDate = val => (val ? dayjs(val).format("YYYY-MM-DD") : "-");
+
+  const getStatusText = status => {
+    const map = { 0: "鐩樼偣涓�", 1: "宸插畬鎴�", 2: "宸插彇娑�" };
+    return map[status] || "鏈煡";
+  };
+
+  const getStatusType = status => {
+    const map = { 0: "warning", 1: "success", 2: "info" };
+    return map[status] || "info";
+  };
+
+  const getList = () => {
+    loading.value = true;
+    loadStatus.value = "loading";
+    listPage({
+      current: page.current,
+      size: page.size,
+      checkNo: searchForm.checkNo,
+      checkName: searchForm.checkName,
+      status: searchForm.status,
+      beginTime: searchForm.beginTime,
+      endTime: searchForm.endTime,
+    })
+      .then(res => {
+        const records = res?.data?.records || [];
+        tableData.value =
+          page.current === 1 ? records : [...tableData.value, ...records];
+        page.total = res?.data?.total || 0;
+        loadStatus.value =
+          tableData.value.length >= page.total ? "nomore" : "loadmore";
+      })
+      .catch(() => {
+        loadStatus.value = "loadmore";
+      })
+      .finally(() => {
+        loading.value = false;
+      });
+  };
+
+  const handleQuery = () => {
+    page.current = 1;
+    tableData.value = [];
+    getList();
+  };
+
+  const loadMore = () => {
+    if (loadStatus.value === "nomore" || loading.value) return;
+    page.current++;
+    getList();
+  };
+
+  const onStatusSelect = item => {
+    searchForm.status = item.value;
+    showStatusSheet.value = false;
+    handleQuery();
+  };
+
+  const onDateConfirm = e => {
+    const list = Array.isArray(e) ? e : [e];
+    searchForm.beginTime = `${list[0]} 00:00:00`;
+    searchForm.endTime = `${list[list.length - 1]} 23:59:59`;
+    showCalendar.value = false;
+    handleQuery();
+  };
+
+  const handleClearDate = () => {
+    searchForm.beginTime = "";
+    searchForm.endTime = "";
+    handleQuery();
+  };
+
+  const onCheckDateConfirm = e => {
+    form.checkDate = dayjs(e.value).format("YYYY-MM-DD");
+    showDatePicker.value = false;
+  };
+
+  const openAdd = () => {
+    form.checkName = "";
+    form.checkDate = dayjs().format("YYYY-MM-DD");
+    form.checker = "";
+    form.remark = "";
+    selectedModels.value = [];
+    showAddPopup.value = true;
+  };
+
+  const openSpecPicker = async () => {
+    showSpecPopup.value = true;
+    if (productOptions.value.length) return;
+    try {
+      const res = await productTreeList();
+      const list = Array.isArray(res) ? res : res?.data || [];
+      productOptions.value = list
+        .filter(item => item.parentId === null)
+        .map(item => ({ id: item.id, productName: item.productName }));
+      if (productOptions.value.length) {
+        selectProduct(productOptions.value[0]);
+      }
+    } catch (e) {
+      // 璇锋眰澶辫触宸茬敱 request 缁熶竴鎻愮ず
+    }
+  };
+
+  const selectProduct = async p => {
+    activeProductId.value = p.id;
+    modelOptions.value = [];
+    try {
+      const res = await modelList({ id: p.id });
+      const list = Array.isArray(res) ? res : res?.data || [];
+      modelOptions.value = list.map(m => ({
+        id: m.id,
+        model: m.model,
+        unit: m.unit,
+        productName: p.productName,
+      }));
+    } catch (e) {
+      // 璇锋眰澶辫触宸茬敱 request 缁熶竴鎻愮ず
+    }
+  };
+
+  const isSelected = id => selectedModels.value.some(m => m.id === id);
+
+  const toggleModel = m => {
+    const index = selectedModels.value.findIndex(item => item.id === m.id);
+    if (index > -1) {
+      selectedModels.value.splice(index, 1);
+    } else {
+      selectedModels.value.push(m);
+    }
+  };
+
+  const removeModel = id => {
+    const index = selectedModels.value.findIndex(item => item.id === id);
+    if (index > -1) selectedModels.value.splice(index, 1);
+  };
+
+  const handleSubmit = async () => {
+    if (!form.checkName || !form.checkName.trim()) {
+      toast("璇疯緭鍏ョ洏鐐瑰悕绉�");
+      return;
+    }
+    submitting.value = true;
+    uni.showLoading({ title: "鎻愪氦涓�...", mask: true });
+    try {
+      await start({
+        checkName: form.checkName.trim(),
+        checkDate: form.checkDate,
+        checker: form.checker,
+        remark: form.remark,
+        productModelIds: selectedModels.value.map(m => m.id),
+      });
+      uni.hideLoading();
+      uni.showToast({ title: "鍙戣捣鎴愬姛", icon: "success" });
+      showAddPopup.value = false;
+      handleQuery();
+    } catch (e) {
+      uni.hideLoading();
+    } finally {
+      submitting.value = false;
+    }
+  };
+
+  const goDetail = item => {
+    uni.navigateTo({
+      url: `/pages/inventoryManagement/stockCheck/detail?id=${item.id}`,
+    });
+  };
+
+  const handleFinish = item => {
+    uni.showModal({
+      title: "瀹屾垚鐩樼偣",
+      content: "宸紓灏嗙敓鎴愯皟璐﹀崟鎹紝瀹℃牳閫氳繃鍚庤处闈㈢敓鏁堬紝鏄惁缁х画锛�",
+      success: async res => {
+        if (!res.confirm) return;
+        uni.showLoading({ title: "澶勭悊涓�...", mask: true });
+        try {
+          const r = await finish({ id: item.id });
+          uni.hideLoading();
+          uni.showToast({ title: r?.msg || "鐩樼偣宸插畬鎴�", icon: "none" });
+          handleQuery();
+        } catch (e) {
+          uni.hideLoading();
+        }
+      },
+    });
+  };
+
+  const handleCancel = item => {
+    uni.showModal({
+      title: "鍙栨秷鐩樼偣",
+      content: "鍙栨秷鍚庤鐩樼偣鍗曚笉鍙啀褰曞叆锛屾槸鍚︾户缁紵",
+      success: async res => {
+        if (!res.confirm) return;
+        uni.showLoading({ title: "澶勭悊涓�...", mask: true });
+        try {
+          const r = await cancel({ id: item.id });
+          uni.hideLoading();
+          uni.showToast({ title: r?.msg || "宸插彇娑�", icon: "none" });
+          handleQuery();
+        } catch (e) {
+          uni.hideLoading();
+        }
+      },
+    });
+  };
+
+  onShow(() => {
+    handleQuery();
+  });
+</script>
+
+<style scoped lang="scss">
+  @import "@/styles/procurement-common.scss";
+
+  .stock-check-page {
+    min-height: 100vh;
+    background: #f8f9fa;
+    display: flex;
+    flex-direction: column;
+  }
+
+  .search-section {
+    background: #fff;
+    padding: 10px 12px;
+  }
+
+  .search-row {
+    display: flex;
+    gap: 10px;
+    margin-bottom: 10px;
+
+    .search-input {
+      flex: 1;
+      background: #f5f5f5;
+      border-radius: 8px;
+      padding: 0 12px;
+    }
+  }
+
+  .filter-row {
+    display: flex;
+    align-items: center;
+    gap: 10px;
+
+    .filter-item {
+      display: flex;
+      align-items: center;
+      gap: 4px;
+      background: #f5f5f5;
+      border-radius: 8px;
+      padding: 0 12px;
+      height: 34px;
+      font-size: 13px;
+      color: #333;
+
+      &.date-item {
+        flex: 1;
+        overflow: hidden;
+      }
+
+      .filter-text {
+        font-size: 13px;
+        color: #333;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+
+        &.placeholder {
+          color: #999;
+        }
+      }
+    }
+
+    .search-btn {
+      width: 70px;
+    }
+  }
+
+  .list-container {
+    flex: 1;
+    height: 0;
+    padding: 20rpx 20rpx 140rpx;
+    box-sizing: border-box;
+  }
+
+  .detail-value.danger {
+    color: #ee0a24;
+    font-weight: 600;
+  }
+
+  .action-buttons {
+    display: flex;
+    gap: 12px;
+    padding: 16rpx 0 0;
+    justify-content: flex-end;
+  }
+
+  .no-data {
+    padding-top: 150rpx;
+  }
+
+  .form-popup {
+    height: 70vh;
+    display: flex;
+    flex-direction: column;
+    background: #f5f5f5;
+  }
+
+  .popup-content {
+    flex: 1;
+    height: 0;
+    padding: 12px;
+  }
+
+  .form-field {
+    background: #fff;
+    border-radius: 10px;
+    padding: 12px;
+    margin-bottom: 12px;
+  }
+
+  // readonly 鐨� up-input 浼氭妸鍘熺敓 input 缃负 disabled锛宒isabled 鍏冪礌浼氬悶鎺夌偣鍑讳簨浠讹紝
+  // 鏁呰 input 涓嶅弬涓庢寚閽堜簨浠讹紝鐢卞灞� .pick-field 鎺ョ鐐瑰嚮
+  .pick-field {
+    display: flex;
+    align-items: center;
+
+    .pick-input {
+      flex: 1;
+    }
+
+    :deep(input) {
+      pointer-events: none;
+    }
+  }
+
+  .field-label {
+    display: block;
+    font-size: 14px;
+    color: #666;
+    margin-bottom: 8px;
+
+    &.required::before {
+      content: "*";
+      color: #ff0000;
+      margin-right: 4px;
+    }
+  }
+
+  .spec-box {
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+  }
+
+  .spec-tags {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+  }
+
+  .spec-tag {
+    display: flex;
+    align-items: center;
+    gap: 6px;
+    background: #ecf5ff;
+    color: #2979ff;
+    border-radius: 6px;
+    padding: 4px 10px;
+
+    .spec-tag-text {
+      font-size: 12px;
+    }
+  }
+
+  .spec-empty {
+    font-size: 12px;
+    color: #999;
+  }
+
+  .spec-category {
+    background: #fff;
+    padding: 8px 0;
+    border-bottom: 1px solid #f0f0f0;
+  }
+
+  .category-scroll {
+    white-space: nowrap;
+  }
+
+  .category-list {
+    display: inline-flex;
+    gap: 10px;
+    padding: 0 12px;
+  }
+
+  .category-tag {
+    padding: 6px 14px;
+    border-radius: 20px;
+    background: #f5f5f5;
+    color: #666;
+    font-size: 13px;
+    white-space: nowrap;
+
+    &.active {
+      background: #2979ff;
+      color: #fff;
+    }
+  }
+
+  .model-item {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    background: #fff;
+    border-radius: 10px;
+    padding: 14px 12px;
+    margin-bottom: 8px;
+
+    .model-info {
+      display: flex;
+      flex-direction: column;
+      gap: 4px;
+    }
+
+    .model-name {
+      font-size: 14px;
+      color: #333;
+    }
+
+    .model-unit {
+      font-size: 12px;
+      color: #999;
+    }
+  }
+
+  .spec-empty-box {
+    padding-top: 60px;
+  }
+
+  .popup-footer {
+    display: flex;
+    gap: 12px;
+    padding: 12px 16px;
+    background: #fff;
+    border-top: 1px solid #f0f0f0;
+  }
+
+  .btn-cancel,
+  .btn-confirm {
+    flex: 1;
+  }
+</style>
diff --git a/src/pages/inventoryManagement/stockManagement/index.vue b/src/pages/inventoryManagement/stockManagement/index.vue
index 45d27ad..49e1758 100644
--- a/src/pages/inventoryManagement/stockManagement/index.vue
+++ b/src/pages/inventoryManagement/stockManagement/index.vue
@@ -2,39 +2,141 @@
   <view class="app-container">
     <PageHeader title="搴撳瓨绠$悊"
                 @back="goBack" />
-    <view v-if="loading"
-          class="loading-state">
-      <up-loading-icon text="鍔犺浇涓�..."></up-loading-icon>
+    <!-- 搴撳瓨棰勮寮�鍏� -->
+    <view class="warn-bar">
+      <view class="warn-left">
+        <text class="warn-label">鍙湅棰勮</text>
+        <up-switch v-model="warnOnly"
+                   size="small"
+                   activeColor="#f56c6c"
+                   @change="handleWarnToggle"></up-switch>
+      </view>
     </view>
+    <!-- 棰勮鍒楄〃 -->
+    <template v-if="warnOnly">
+      <view class="warn-search">
+        <view class="search-input">
+          <up-input class="search-text"
+                    v-model="warnSearch.productName"
+                    placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
+                    clearable
+                    @confirm="handleWarnQuery" />
+        </view>
+        <view class="filter-button"
+              @click="handleWarnQuery">
+          <up-icon name="search"
+                   size="24"
+                   color="#999"></up-icon>
+        </view>
+      </view>
+      <scroll-view scroll-y
+                   class="warn-list"
+                   v-if="warnList.length > 0"
+                   @scrolltolower="loadWarnMore">
+        <view v-for="item in warnList"
+              :key="item.id"
+              class="ledger-item warn-item"
+              :class="{ 'is-warn': isWarnRow(item) }">
+          <view class="item-header">
+            <view class="item-left">
+              <view class="document-icon warn-icon">
+                <up-icon name="warning"
+                         size="16"
+                         color="#ffffff"></up-icon>
+              </view>
+              <text class="item-id">{{ item.productName || '-' }}</text>
+            </view>
+            <up-tag text="棰勮"
+                    type="error"
+                    size="mini"
+                    v-if="isWarnRow(item)" />
+          </view>
+          <up-divider></up-divider>
+          <view class="item-details">
+            <view class="detail-row">
+              <text class="detail-label">瑙勬牸鍨嬪彿</text>
+              <text class="detail-value">{{ item.model || '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鍗曚綅</text>
+              <text class="detail-value">{{ item.unit || '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鏁伴噺</text>
+              <text class="detail-value">{{ formatQty(item.qualitity) }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">閿佸畾鏁伴噺</text>
+              <text class="detail-value">{{ formatQty(item.lockedQuantity) }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鍙敤鏁伴噺</text>
+              <text class="detail-value"
+                    :class="{ 'warn-value': isWarnRow(item) }">{{ formatQty(item.un_locked_quantity ?? item.unLockedQuantity) }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">棰勮鍊�</text>
+              <text class="detail-value">{{ formatQty(item.warnNum) }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鎵瑰彿</text>
+              <text class="detail-value">{{ item.batchNo || '-' }}</text>
+            </view>
+          </view>
+        </view>
+        <up-loadmore :status="warnStatus" />
+      </scroll-view>
+      <view v-else-if="!warnLoading"
+            class="no-data">
+        <up-empty mode="data"
+                  text="鏆傛棤棰勮搴撳瓨"></up-empty>
+      </view>
+    </template>
+    <!-- 搴撳瓨鍒楄〃 -->
     <template v-else>
-      <up-tabs :list="tabs"
-               @click="handleTabClick"
-               :current="activeTab" />
-      <swiper class="swiper-box"
-              :current="activeTab"
-              @change="handleSwiperChange">
-        <swiper-item class="swiper-item"
-                     v-for="tab in products"
-                     :key="tab.id">
-          <record :product-id="tab.id"
-                  v-if="activeTab === products.indexOf(tab) || initializedTabs.includes(tab.id)" />
-        </swiper-item>
-      </swiper>
+      <view v-if="loading"
+            class="loading-state">
+        <up-loading-icon text="鍔犺浇涓�..."></up-loading-icon>
+      </view>
+      <template v-else>
+        <up-tabs :list="tabs"
+                 @click="handleTabClick"
+                 :current="activeTab" />
+        <swiper class="swiper-box"
+                :current="activeTab"
+                @change="handleSwiperChange">
+          <swiper-item class="swiper-item"
+                       v-for="tab in products"
+                       :key="tab.id">
+            <record :product-id="tab.id"
+                    v-if="activeTab === products.indexOf(tab) || initializedTabs.includes(tab.id)" />
+          </swiper-item>
+        </swiper>
+      </template>
     </template>
   </view>
 </template>
 
 <script setup>
-  import { ref, onMounted } from "vue";
+  import { ref, reactive, onMounted } from "vue";
   import PageHeader from "@/components/PageHeader.vue";
   import Record from "./Record.vue";
   import { productTreeList } from "@/api/basicData/product.js";
+  import { getStockWarnPage } from "@/api/inventoryManagement/stockInventory.js";
 
   const activeTab = ref(0);
   const tabs = ref([]);
   const products = ref([]);
   const loading = ref(false);
   const initializedTabs = ref([]);
+
+  // 棰勮
+  const warnOnly = ref(false);
+  const warnLoading = ref(false);
+  const warnStatus = ref("loadmore");
+  const warnList = ref([]);
+  const warnPage = reactive({ current: 1, size: 10, total: 0 });
+  const warnSearch = reactive({ productName: "" });
 
   const handleTabClick = item => {
     activeTab.value = item.index;
@@ -74,12 +176,74 @@
     uni.navigateBack();
   };
 
+  // 棰勮鐩稿叧
+  const formatQty = val => {
+    if (val === null || val === undefined || val === "") return "-";
+    return Number(val) || 0;
+  };
+
+  const getAvailable = item => {
+    const val = item.un_locked_quantity ?? item.unLockedQuantity;
+    return Number(val) || 0;
+  };
+
+  // 鍙敤鏁伴噺 鈮� 棰勮鍊� 涓� 棰勮鍊�>0
+  const isWarnRow = item => {
+    const warnNum = Number(item.warnNum) || 0;
+    if (warnNum <= 0) return false;
+    return getAvailable(item) <= warnNum;
+  };
+
+  const getWarnList = () => {
+    warnLoading.value = true;
+    warnStatus.value = "loading";
+    getStockWarnPage({
+      current: warnPage.current,
+      size: warnPage.size,
+      productName: warnSearch.productName,
+    })
+      .then(res => {
+        const records = res?.data?.records || [];
+        warnList.value =
+          warnPage.current === 1 ? records : [...warnList.value, ...records];
+        warnPage.total = res?.data?.total || 0;
+        warnStatus.value =
+          warnList.value.length >= warnPage.total ? "nomore" : "loadmore";
+      })
+      .catch(() => {
+        warnStatus.value = "loadmore";
+      })
+      .finally(() => {
+        warnLoading.value = false;
+      });
+  };
+
+  const handleWarnQuery = () => {
+    warnPage.current = 1;
+    warnList.value = [];
+    getWarnList();
+  };
+
+  const loadWarnMore = () => {
+    if (warnStatus.value === "nomore" || warnLoading.value) return;
+    warnPage.current++;
+    getWarnList();
+  };
+
+  const handleWarnToggle = () => {
+    if (warnOnly.value && !warnList.value.length) {
+      handleWarnQuery();
+    }
+  };
+
   onMounted(() => {
     fetchProducts();
   });
 </script>
 
 <style scoped lang="scss">
+  @import "@/styles/procurement-common.scss";
+
   .app-container {
     display: flex;
     flex-direction: column;
@@ -101,4 +265,70 @@
   :deep(.up-tabs) {
     background-color: #fff;
   }
+
+  .warn-bar {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    background: #fff;
+    padding: 16rpx 30rpx;
+
+    .warn-left {
+      display: flex;
+      align-items: center;
+      gap: 12rpx;
+    }
+
+    .warn-label {
+      font-size: 28rpx;
+      color: #333;
+    }
+  }
+
+  .warn-search {
+    display: flex;
+    align-items: center;
+    gap: 12rpx;
+    background: #fff;
+    padding: 16rpx 30rpx;
+
+    .search-input {
+      flex: 1;
+      background: #f5f5f5;
+      border-radius: 40rpx;
+      padding: 0 24rpx;
+      height: 72rpx;
+      display: flex;
+      align-items: center;
+    }
+  }
+
+  .warn-list {
+    flex: 1;
+    height: 0;
+    padding: 20rpx;
+    box-sizing: border-box;
+  }
+
+  .warn-item {
+    padding: 24rpx;
+
+    &.is-warn {
+      border: 2rpx solid #f56c6c;
+    }
+
+    .warn-icon {
+      background: linear-gradient(135deg, #f56c6c, #c0392b);
+    }
+
+    .warn-value {
+      color: #f56c6c;
+      font-weight: 600;
+    }
+  }
+
+  .no-data {
+    flex: 1;
+    padding-top: 150rpx;
+  }
 </style>
diff --git a/src/pages/procurementManagement/procurementDemand/index.vue b/src/pages/procurementManagement/procurementDemand/index.vue
new file mode 100644
index 0000000..b55f77b
--- /dev/null
+++ b/src/pages/procurementManagement/procurementDemand/index.vue
@@ -0,0 +1,315 @@
+<template>
+  <view class="demand-page">
+    <PageHeader title="閲囪喘闇�姹�"
+                @back="goBack" />
+
+    <!-- 绛涢�夊尯鍩� -->
+    <view class="search-section">
+      <view class="search-bar">
+        <view class="search-input">
+          <up-input class="search-text"
+                    v-model="queryParams.productName"
+                    placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
+                    clearable
+                    @change="handleSearch" />
+        </view>
+      </view>
+      <view class="search-bar">
+        <view class="search-input">
+          <up-input class="search-text"
+                    v-model="queryParams.productModel"
+                    placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+                    clearable
+                    @change="handleSearch" />
+        </view>
+        <view class="filter-button"
+              @click="showStatusSheet = true">
+          <text class="filter-text">{{ statusLabel }}</text>
+          <up-icon name="arrow-down"
+                   size="14"
+                   color="#999999"></up-icon>
+        </view>
+      </view>
+    </view>
+
+    <!-- 鍒楄〃鍖哄煙 -->
+    <view v-if="list.length > 0"
+          class="ledger-list">
+      <view v-for="item in list"
+            :key="item.id"
+            class="ledger-item">
+        <view class="item-header">
+          <view class="item-left">
+            <view class="document-icon">
+              <up-icon name="shopping-cart"
+                       size="16"
+                       color="#ffffff"></up-icon>
+            </view>
+            <text class="item-id">{{ item.sourceBomNo || "鏃犳潵婧怋OM" }}</text>
+          </view>
+          <up-tag :text="statusText(item.status)"
+                  :type="statusType(item.status)"
+                  size="mini" />
+        </view>
+        <up-divider></up-divider>
+        <view class="item-details">
+          <view class="detail-row">
+            <text class="detail-label">浜у搧鍚嶇О</text>
+            <text class="detail-value">{{ item.productName || "-" }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">瑙勬牸鍨嬪彿</text>
+            <text class="detail-value">{{ item.productModel || "-" }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">鍗曚綅</text>
+            <text class="detail-value">{{ item.unit || "-" }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">缂哄彛闇�姹傞噺</text>
+            <text class="detail-value danger">{{ num(item.demandQuantity) }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">搴撳瓨鍌ㄥ蹇収</text>
+            <text class="detail-value">{{ num(item.stockQuantity) }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">鏉ユ簮BOM缂栧彿</text>
+            <text class="detail-value">{{ item.sourceBomNo || "-" }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">鍒涘缓鏃堕棿</text>
+            <text class="detail-value">{{ item.createTime || "-" }}</text>
+          </view>
+        </view>
+        <view class="action-buttons">
+          <up-button v-if="isPending(item)"
+                     class="action-btn"
+                     size="small"
+                     type="primary"
+                     @click="handleChangeStatus(item, 1)">鏍囪宸茶浆閲囪喘</up-button>
+          <up-button v-if="isPending(item)"
+                     class="action-btn"
+                     size="small"
+                     type="warning"
+                     @click="handleChangeStatus(item, 2)">鍙栨秷</up-button>
+          <up-button class="action-btn"
+                     size="small"
+                     type="error"
+                     plain
+                     :disabled="!isPending(item)"
+                     @click="handleDelete(item)">鍒犻櫎</up-button>
+        </view>
+      </view>
+      <up-loadmore :status="pageStatus" />
+    </view>
+    <view v-else
+          class="no-data">
+      <up-empty text="鏆傛棤閲囪喘闇�姹�"
+                mode="data"></up-empty>
+    </view>
+
+    <!-- 鐘舵�佺瓫閫� -->
+    <up-action-sheet :show="showStatusSheet"
+                     :actions="statusOptions"
+                     title="閫夋嫨鐘舵��"
+                     @select="handleStatusSelect"
+                     @close="showStatusSheet = false" />
+  </view>
+</template>
+
+<script setup>
+  import { reactive, ref } from "vue";
+  import { onReachBottom, onShow } from "@dcloudio/uni-app";
+  import { listPage, changeStatus, del } from "@/api/procurementManagement/procurementDemand";
+  import PageHeader from "@/components/PageHeader.vue";
+
+  const STATUS_MAP = {
+    0: { text: "寰呭鐞�", type: "warning" },
+    1: { text: "宸茶浆閲囪喘", type: "success" },
+    2: { text: "宸插彇娑�", type: "info" },
+  };
+
+  const queryParams = reactive({
+    productName: "",
+    productModel: "",
+    status: "",
+  });
+  const list = ref([]);
+  const pageStatus = ref("loadmore");
+  const page = reactive({
+    current: 1,
+    size: 10,
+    total: 0,
+  });
+
+  const showStatusSheet = ref(false);
+  const statusOptions = [
+    { name: "鍏ㄩ儴", value: "" },
+    { name: "寰呭鐞�", value: 0 },
+    { name: "宸茶浆閲囪喘", value: 1 },
+    { name: "宸插彇娑�", value: 2 },
+  ];
+  const statusLabel = ref("鍏ㄩ儴鐘舵��");
+
+  // decimal 鍙兘鏄瓧绗︿覆锛岀粺涓�杞暟瀛楀厹搴�
+  const num = val => Number(val) || 0;
+
+  const statusText = status => (STATUS_MAP[Number(status)] || STATUS_MAP[0]).text;
+  const statusType = status => (STATUS_MAP[Number(status)] || STATUS_MAP[0]).type;
+  // 浠呭緟澶勭悊鍙搷浣滐紙鍒犻櫎涔熺敱鍚庣鍏滃簳鏍¢獙锛�
+  const isPending = item => Number(item.status) === 0;
+
+  const goBack = () => {
+    uni.navigateBack();
+  };
+
+  const handleSearch = () => {
+    page.current = 1;
+    pageStatus.value = "loadmore";
+    list.value = [];
+    getList();
+  };
+
+  const getList = () => {
+    if (pageStatus.value === "loading" || pageStatus.value === "nomore") return;
+
+    pageStatus.value = "loading";
+    listPage({
+      current: page.current,
+      size: page.size,
+      productName: queryParams.productName,
+      productModel: queryParams.productModel,
+      status: queryParams.status,
+    })
+      .then(res => {
+        const records = res?.data?.records || res?.records || [];
+        const total = res?.data?.total || res?.total || 0;
+
+        if (page.current === 1) {
+          list.value = records;
+        } else {
+          list.value = [...list.value, ...records];
+        }
+
+        page.total = total;
+        if (list.value.length >= total) {
+          pageStatus.value = "nomore";
+        } else {
+          pageStatus.value = "loadmore";
+          page.current++;
+        }
+      })
+      .catch(() => {
+        // 閿欒鎻愮ず鐢� request 缁熶竴澶勭悊
+        pageStatus.value = "loadmore";
+      });
+  };
+
+  const handleStatusSelect = action => {
+    queryParams.status = action.value;
+    statusLabel.value = action.value === "" ? "鍏ㄩ儴鐘舵��" : action.name;
+    showStatusSheet.value = false;
+    handleSearch();
+  };
+
+  const handleChangeStatus = (item, status) => {
+    const actionText = status === 1 ? "鏍囪涓哄凡杞噰璐�" : "鍙栨秷";
+    uni.showModal({
+      title: "鎻愮ず",
+      content: `纭畾灏嗚閲囪喘闇�姹�${actionText}鍚楋紵`,
+      success: res => {
+        if (!res.confirm) return;
+        uni.showLoading({ title: "澶勭悊涓�...", mask: true });
+        changeStatus({ ids: item.id, status })
+          .then(() => {
+            uni.hideLoading();
+            uni.showToast({ title: "鎿嶄綔鎴愬姛", icon: "none" });
+            handleSearch();
+          })
+          .catch(() => {
+            uni.hideLoading();
+          });
+      },
+    });
+  };
+
+  const handleDelete = item => {
+    uni.showModal({
+      title: "鍒犻櫎纭",
+      content: "纭畾鍒犻櫎璇ラ噰璐渶姹傚悧锛�",
+      success: res => {
+        if (!res.confirm) return;
+        uni.showLoading({ title: "澶勭悊涓�...", mask: true });
+        del([item.id])
+          .then(() => {
+            uni.hideLoading();
+            uni.showToast({ title: "鍒犻櫎鎴愬姛", icon: "none" });
+            handleSearch();
+          })
+          .catch(() => {
+            // 闈炲緟澶勭悊鐘舵�佸悗绔細杩斿洖鎻愮ず锛屾澶勪笉鍐嶉噸澶嶅脊绐�
+            uni.hideLoading();
+          });
+      },
+    });
+  };
+
+  onReachBottom(() => {
+    getList();
+  });
+
+  onShow(() => {
+    handleSearch();
+  });
+</script>
+
+<style scoped lang="scss">
+  @import "@/styles/procurement-common.scss";
+
+  .demand-page {
+    min-height: 100vh;
+    background: #f8f9fa;
+  }
+
+  .search-bar + .search-bar {
+    margin-top: 16rpx;
+  }
+
+  // 鍏叡鏍峰紡閲� .filter-button 鏄� 40px 瑙佹柟鐨勫浘鏍囨寜閽紝杩欓噷瑕佹斁鐘舵�佹枃妗堬紝闇�鎸夊唴瀹规拺寮�
+  .filter-button {
+    width: auto;
+    min-width: 40px;
+    padding: 0 12px;
+    flex-shrink: 0;
+    background: #f5f5f5;
+  }
+
+  .filter-text {
+    font-size: 24rpx;
+    color: #333;
+    margin-right: 4rpx;
+    white-space: nowrap;
+  }
+
+  .no-data {
+    padding-top: 100rpx;
+    text-align: center;
+    color: #999;
+    font-size: 28rpx;
+  }
+
+  .action-buttons {
+    display: flex;
+    justify-content: flex-end;
+    gap: 15rpx;
+    padding: 0 30rpx 30rpx;
+    flex-wrap: wrap;
+  }
+
+  .action-btn {
+    width: calc(33.33% - 10rpx);
+    margin: 0 !important;
+    margin-bottom: 15rpx !important;
+  }
+</style>
diff --git a/src/pages/procurementManagement/procurementLedger/detail.vue b/src/pages/procurementManagement/procurementLedger/detail.vue
index 397d863..72cfc73 100644
--- a/src/pages/procurementManagement/procurementLedger/detail.vue
+++ b/src/pages/procurementManagement/procurementLedger/detail.vue
@@ -204,6 +204,14 @@
                         :disabled="!canEditProducts"
                         @click="openSpecificationPicker(idx)" />
               <template #right>
+                <view v-if="canEditProducts"
+                      class="scan-entry"
+                      @click.stop="scanBarcode(idx)">
+                  <up-icon name="scan"
+                           size="18"
+                           color="#3c9cff"></up-icon>
+                  <text class="scan-entry-text">鎵爜</text>
+                </view>
                 <up-icon name="arrow-right"
                          v-if="canEditProducts"
                          @click="openSpecificationPicker(idx)"></up-icon>
@@ -338,7 +346,11 @@
 
 <script setup>
   import { onMounted, ref, computed } from "vue";
-  import { modelList, productTreeList } from "@/api/basicData/product.js";
+  import {
+    modelList,
+    productTreeList,
+    modelByBarcode,
+  } from "@/api/basicData/product.js";
   import useUserStore from "@/store/modules/user";
   import { calculateTaxExclusiveTotalPrice } from "@/utils/summarizeTable";
   import { formatDateToYMD } from "@/utils/ruoyi";
@@ -447,6 +459,7 @@
       name: model.text,
       value: model.value,
       unit: model.unit,
+      subname: model.barcode ? `鏉$爜锛�${model.barcode}` : "",
     }));
   });
 
@@ -670,14 +683,48 @@
     showCategoryPicker.value = false;
   };
 
-  // 鑾峰彇瑙勬牸鍨嬪彿
+  // 鑾峰彇瑙勬牸鍨嬪彿锛堣繑鍥� Promise锛屼究浜庢壂鐮佸悗椤哄簭鍥炲~锛�
   const getModels = value => {
-    modelList({ id: value }).then(res => {
-      modelOptions.value = res.map(user => ({
+    return modelList({ id: value }).then(res => {
+      const list = res?.data || res || [];
+      modelOptions.value = list.map(user => ({
         text: user.model,
         value: user.id,
         unit: user.unit,
+        barcode: user.barcode,
       }));
+    });
+  };
+
+  // 鎵爜鎸夋潯鐮佺簿纭洖濉骇鍝佸ぇ绫�+瑙勬牸鍨嬪彿
+  const scanBarcode = idx => {
+    if (!canEditProducts.value) return;
+    uni.scanCode({
+      scanType: ["barCode", "qrCode"],
+      success: async res => {
+        const barcode = (res.result || "").trim();
+        if (!barcode) return;
+        uni.showLoading({ title: "鍖归厤涓�...", mask: true });
+        try {
+          const resp = await modelByBarcode({ barcode });
+          const info = resp?.data || resp;
+          if (info && info.productId) {
+            currentProductIndex.value = idx;
+            // 鍏堝姞杞借浜у搧涓嬬殑瑙勬牸鍒楄〃锛屼繚璇佷笌鎵嬪姩閫夋嫨鏃剁殑 UI 鐘舵�佷竴鑷�
+            await getModels(info.productId);
+            const row = productData.value[idx];
+            row.productCategory = info.productName || row.productCategory;
+            row.specificationModel = info.model || "";
+            row.productModelId = info.id || "";
+            row.unit = info.unit || "";
+          }
+          uni.hideLoading();
+          uni.showToast({ title: "宸插尮閰嶈鏍�", icon: "success" });
+        } catch (e) {
+          // 鏈尮閰嶇瓑閿欒宸叉湁鍏ㄥ眬 toast锛岃繖閲屽彧鎭㈠ loading
+          uni.hideLoading();
+        }
+      },
     });
   };
 
@@ -1119,6 +1166,19 @@
 <style scoped lang="scss">
   @import "@/static/scss/form-common.scss";
 
+  // 瑙勬牸琛屾壂鐮佸叆鍙�
+  .scan-entry {
+    display: flex;
+    align-items: center;
+    margin-right: 8px;
+
+    .scan-entry-text {
+      font-size: 13px;
+      color: #3c9cff;
+      margin-left: 2px;
+    }
+  }
+
   .approval-process {
     background: #fff;
     margin: 16px;
diff --git a/src/pages/productionDesign/bom/index.vue b/src/pages/productionDesign/bom/index.vue
index 97ba5f6..ea79eba 100644
--- a/src/pages/productionDesign/bom/index.vue
+++ b/src/pages/productionDesign/bom/index.vue
@@ -33,9 +33,14 @@
             </view>
             <text class="item-id">{{ item.bomNo || "-" }}</text>
           </view>
-          <up-tag :text="'V' + (item.version || '1.0')"
-                  type="primary"
-                  size="mini" />
+          <view class="item-right">
+            <up-tag :text="'V' + (item.version || '1.0')"
+                    type="primary"
+                    size="mini" />
+            <up-tag :text="isEnabled(item) ? '鍚敤' : '鍋滅敤'"
+                    :type="isEnabled(item) ? 'success' : 'info'"
+                    size="mini" />
+          </view>
         </view>
         <up-divider></up-divider>
         <view class="item-details">
@@ -57,6 +62,15 @@
                      size="small"
                      type="primary"
                      @click="goStructure(item)">鏌ョ湅璇︽儏</up-button>
+          <up-button class="action-btn"
+                     size="small"
+                     :type="isEnabled(item) ? 'warning' : 'success'"
+                     @click="toggleStatus(item)">{{ isEnabled(item) ? "鍋滅敤" : "鍚敤" }}</up-button>
+          <up-button class="action-btn"
+                     size="small"
+                     type="primary"
+                     plain
+                     @click="openKitCheck(item)">榻愬鍒嗘瀽</up-button>
         </view>
       </view>
       <up-loadmore :status="pageStatus" />
@@ -66,13 +80,92 @@
       <up-empty text="鏆傛棤BOM鏁版嵁"
                 mode="list"></up-empty>
     </view>
+
+    <!-- 榻愬鍒嗘瀽寮圭獥 -->
+    <up-popup :show="showKitPopup"
+              mode="bottom"
+              round="10"
+              @close="showKitPopup = false">
+      <view class="kit-popup">
+        <view class="popup-header">
+          <text class="popup-title">榻愬鍒嗘瀽 - {{ currentBom.bomNo || "" }}</text>
+        </view>
+        <view class="kit-form">
+          <text class="kit-form-label">闇�姹傛暟閲�</text>
+          <view class="kit-form-input">
+            <up-number-box v-model="demandQty"
+                           :min="0"
+                           :step="1"></up-number-box>
+          </view>
+          <up-button type="primary"
+                     size="small"
+                     :loading="kitLoading"
+                     text="鍒嗘瀽"
+                     @click="handleKitCheck"></up-button>
+        </view>
+        <scroll-view scroll-y
+                     class="kit-result">
+          <view v-if="kitResult.length > 0">
+            <view v-for="(row, index) in kitResult"
+                  :key="index"
+                  class="kit-item">
+              <view class="kit-item-header">
+                <text class="kit-name">{{ row.productName || "-" }}</text>
+                <text class="kit-model">{{ row.model || "-" }}</text>
+                <up-tag v-if="num(row.shortageQty) > 0"
+                        text="缂哄彛"
+                        type="error"
+                        size="mini" />
+              </view>
+              <view class="kit-grid">
+                <view class="kit-cell">
+                  <text class="kit-label">鍗曚綅</text>
+                  <text class="kit-value">{{ row.unit || "-" }}</text>
+                </view>
+                <view class="kit-cell">
+                  <text class="kit-label">闇�姹傛暟閲�</text>
+                  <text class="kit-value">{{ num(row.requiredQty) }}</text>
+                </view>
+                <view class="kit-cell">
+                  <text class="kit-label">鍙敤搴撳瓨</text>
+                  <text class="kit-value">{{ num(row.availableQty) }}</text>
+                </view>
+                <view class="kit-cell">
+                  <text class="kit-label">缂哄彛鏁伴噺</text>
+                  <text class="kit-value"
+                        :class="{ danger: num(row.shortageQty) > 0 }">{{ num(row.shortageQty) }}</text>
+                </view>
+              </view>
+            </view>
+          </view>
+          <view v-else
+                class="kit-empty">
+            <up-empty mode="data"
+                      :text="kitDone ? '搴撳瓨榻愬锛屾棤缂哄彛' : '璇疯緭鍏ラ渶姹傛暟閲忓悗鐐瑰嚮鍒嗘瀽'"></up-empty>
+          </view>
+        </scroll-view>
+        <view class="filter-actions">
+          <up-button type="primary"
+                     size="default"
+                     :disabled="!kitDone"
+                     text="鐢熸垚閲囪喘闇�姹�"
+                     @click="handleGenerateDemand"></up-button>
+        </view>
+      </view>
+    </up-popup>
   </view>
 </template>
 
 <script setup>
   import { reactive, ref } from "vue";
   import { onReachBottom, onShow } from "@dcloudio/uni-app";
-  import { listPage } from "@/api/productionManagement/bom";
+  import {
+    listPage,
+    changeStatus,
+    kitCheck,
+    generateDemand,
+  } from "@/api/productionManagement/bom";
+  import PageHeader from "@/components/PageHeader.vue";
 
   const queryParams = reactive({
     productName: "",
@@ -85,6 +178,15 @@
     size: 3,
     total: 0,
   });
+
+  // 鍚庣涓嶈繑鍥� status 鏃舵寜鍚敤灞曠ず锛堝吋瀹硅�佹暟鎹級
+  const isEnabled = item =>
+    item.status === undefined ||
+    item.status === null ||
+    Number(item.status) === 1;
+
+  // decimal 鍙兘鏄瓧绗︿覆锛岀粺涓�杞暟瀛楀厹搴�
+  const num = val => Number(val) || 0;
 
   const goBack = () => {
     uni.navigateBack();
@@ -125,7 +227,7 @@
         }
       })
       .catch(() => {
-        uni.showToast({ title: "鏌ヨ澶辫触", icon: "error" });
+        // 閿欒鎻愮ず鐢� request 缁熶竴澶勭悊
         pageStatus.value = "loadmore";
       });
   };
@@ -142,6 +244,87 @@
         item.remark || ""
       )}&version=${encodeURIComponent(item.version || 1)}`,
     });
+  };
+
+  // 鍚敤/鍋滅敤锛宻tatus=1 鏃跺悗绔嚜鍔ㄥ仠鐢ㄥ悓瑙勬牸鍏跺畠鐗堟湰
+  const toggleStatus = item => {
+    const enabled = isEnabled(item);
+    const nextStatus = enabled ? 0 : 1;
+    const actionText = enabled ? "鍋滅敤" : "鍚敤";
+    uni.showModal({
+      title: "鎻愮ず",
+      content: `纭畾${actionText}璇OM鐗堟湰鍚楋紵`,
+      success: res => {
+        if (!res.confirm) return;
+        uni.showLoading({ title: "澶勭悊涓�...", mask: true });
+        changeStatus({ id: item.id, status: nextStatus })
+          .then(() => {
+            uni.hideLoading();
+            uni.showToast({ title: `${actionText}鎴愬姛`, icon: "none" });
+            handleSearch();
+          })
+          .catch(() => {
+            uni.hideLoading();
+          });
+      },
+    });
+  };
+
+  // --- 榻愬鍒嗘瀽 ---
+  const showKitPopup = ref(false);
+  const kitLoading = ref(false);
+  const kitDone = ref(false);
+  const currentBom = ref({});
+  const demandQty = ref(1);
+  const kitResult = ref([]);
+
+  const openKitCheck = item => {
+    currentBom.value = item;
+    demandQty.value = 1;
+    kitResult.value = [];
+    kitDone.value = false;
+    showKitPopup.value = true;
+  };
+
+  const handleKitCheck = () => {
+    const qty = Number(demandQty.value);
+    if (!Number.isFinite(qty) || qty <= 0) {
+      uni.showToast({ title: "闇�姹傛暟閲忓繀椤诲ぇ浜�0", icon: "none" });
+      return;
+    }
+    kitLoading.value = true;
+    kitCheck({ bomId: currentBom.value.id, demandQty: qty })
+      .then(res => {
+        kitResult.value = res?.data || [];
+        kitDone.value = true;
+      })
+      .catch(() => {
+        // 閿欒鎻愮ず鐢� request 缁熶竴澶勭悊
+        kitResult.value = [];
+        kitDone.value = false;
+      })
+      .finally(() => {
+        kitLoading.value = false;
+      });
+  };
+
+  const handleGenerateDemand = () => {
+    uni.showLoading({ title: "澶勭悊涓�...", mask: true });
+    generateDemand({
+      bomId: currentBom.value.id,
+      demandQty: Number(demandQty.value),
+    })
+      .then(res => {
+        uni.hideLoading();
+        uni.showModal({
+          title: "鎻愮ず",
+          content: String(res?.data ?? "閲囪喘闇�姹傜敓鎴愬畬鎴�"),
+          showCancel: false,
+        });
+      })
+      .catch(() => {
+        uni.hideLoading();
+      });
   };
 
   onReachBottom(() => {
@@ -172,8 +355,92 @@
   }
 
   .action-btn {
-    width: calc(50% - 15rpx);
+    width: calc(33.33% - 10rpx);
     margin: 0 !important;
     margin-bottom: 15rpx !important;
   }
+
+  // --- 榻愬鍒嗘瀽寮圭獥 ---
+  .kit-popup {
+    padding-bottom: 20rpx;
+  }
+
+  .kit-form {
+    display: flex;
+    align-items: center;
+    gap: 16rpx;
+    padding: 20rpx 30rpx;
+
+    .kit-form-label {
+      font-size: 28rpx;
+      color: #333;
+    }
+
+    .kit-form-input {
+      flex: 1;
+    }
+  }
+
+  .kit-result {
+    height: 600rpx;
+    padding: 0 30rpx;
+  }
+
+  .kit-item {
+    border: 1rpx solid #f0f0f0;
+    border-radius: 12rpx;
+    padding: 16rpx;
+    margin-bottom: 16rpx;
+  }
+
+  .kit-item-header {
+    display: flex;
+    align-items: center;
+    gap: 12rpx;
+    margin-bottom: 12rpx;
+
+    .kit-name {
+      font-size: 28rpx;
+      color: #333;
+      font-weight: 500;
+    }
+
+    .kit-model {
+      font-size: 24rpx;
+      color: #999;
+      flex: 1;
+    }
+  }
+
+  .kit-grid {
+    display: flex;
+    flex-wrap: wrap;
+
+    .kit-cell {
+      width: 50%;
+      display: flex;
+      align-items: center;
+      margin-bottom: 8rpx;
+
+      .kit-label {
+        font-size: 24rpx;
+        color: #777;
+        min-width: 110rpx;
+      }
+
+      .kit-value {
+        font-size: 24rpx;
+        color: #333;
+
+        &.danger {
+          color: #ee0a24;
+          font-weight: 500;
+        }
+      }
+    }
+  }
+
+  .kit-empty {
+    padding: 40rpx 0;
+  }
 </style>
diff --git a/src/pages/productionManagement/costAccounting/index.vue b/src/pages/productionManagement/costAccounting/index.vue
new file mode 100644
index 0000000..d3465b7
--- /dev/null
+++ b/src/pages/productionManagement/costAccounting/index.vue
@@ -0,0 +1,662 @@
+<template>
+  <view class="cost-accounting">
+    <PageHeader title="鎴愭湰鏍哥畻鍒嗘瀽"
+                @back="goBack" />
+
+    <!-- 椤堕儴椤电 -->
+    <view class="main-tabs">
+      <up-tabs :list="mainTabs"
+               :current="mainTabIndex"
+               @change="handleMainTabChange"
+               :activeStyle="{ color: '#2979ff', fontWeight: 'bold' }"
+               lineWidth="40"
+               lineHeight="3" />
+    </view>
+
+    <!-- 鍏叡绛涢�� -->
+    <view class="search-section">
+      <view class="filter-bar">
+        <view class="date-input"
+              @click="showCalendar = true">
+          <up-icon name="calendar"
+                   size="18"
+                   color="#999"></up-icon>
+          <text class="date-text"
+                :class="{ placeholder: !query.startDate }">{{ dateRangeText }}</text>
+          <up-icon v-if="query.startDate"
+                   name="close-circle-fill"
+                   size="16"
+                   color="#c0c4cc"
+                   @click.stop="handleClearDate"></up-icon>
+        </view>
+      </view>
+      <view class="filter-bar">
+        <view class="search-input">
+          <up-input class="search-text"
+                    v-model="query.npsNo"
+                    placeholder="鐢熶骇璁㈠崟鍙�"
+                    clearable
+                    @change="handleQuery" />
+        </view>
+        <view class="search-input">
+          <up-input class="search-text"
+                    v-model="query.productName"
+                    placeholder="鎴愬搧鍚嶇О/瑙勬牸"
+                    clearable
+                    @change="handleQuery" />
+        </view>
+      </view>
+      <view class="filter-actions-top">
+        <up-button type="primary"
+                   size="small"
+                   text="鎼滅储"
+                   @click="handleQuery"></up-button>
+      </view>
+    </view>
+
+    <view v-if="loading"
+          class="loading-box">
+      <up-loading-icon text="鍔犺浇涓�..."></up-loading-icon>
+    </view>
+
+    <template v-else>
+      <!-- ============ 1. 鏍哥畻鏄庣粏 ============ -->
+      <template v-if="mainTabIndex === 0">
+        <view v-if="visibleStatRows.length > 0"
+              class="ledger-list">
+          <view v-for="row in visibleStatRows"
+                :key="row.productionOrderId ?? row.npsNo"
+                class="ledger-item no-click">
+            <view class="item-header">
+              <view class="item-left">
+                <view class="document-icon">
+                  <up-icon name="rmb-circle"
+                           size="16"
+                           color="#ffffff"></up-icon>
+                </view>
+                <text class="item-id">{{ row.npsNo || "-" }}</text>
+              </view>
+              <text class="item-index">{{ row.orderDate || "鏃犳棩鏈�" }}</text>
+            </view>
+            <up-divider></up-divider>
+            <view class="item-details">
+              <view class="detail-row">
+                <text class="detail-label">鎴愬搧</text>
+                <text class="detail-value">{{ row.finishedProduct || "-" }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">浜ч噺</text>
+                <text class="detail-value">{{ fmtNum(row.outputQty) }}{{ row.unit ? ` ${row.unit}` : "" }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鏉愭枡鎴愭湰</text>
+                <text class="detail-value">{{ fmtMoney(row.materialCost) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">浜哄伐鎴愭湰</text>
+                <text class="detail-value"
+                      :class="{ warn: isPositive(row.noRateRows) }">{{ fmtMoney(row.laborCost) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鑳借�楁垚鏈�</text>
+                <text class="detail-value">{{ fmtMoney(row.energyCost) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鎬绘垚鏈�</text>
+                <text class="detail-value highlight">{{ fmtMoney(row.totalCost) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鍗曚綅鎴愭湰</text>
+                <text class="detail-value">{{ fmtUnitCost(row.unitCost) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">閿�鍞崟浠�</text>
+                <text class="detail-value">{{ fmtMoney(row.salePrice) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">姣涘埄</text>
+                <text class="detail-value"
+                      :class="{ danger: isNegative(row.grossProfit) }">{{ fmtMoney(row.grossProfit) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">姣涘埄鐜�</text>
+                <text class="detail-value"
+                      :class="{ danger: isNegative(row.grossProfitRate) }">{{ fmtPercent(row.grossProfitRate) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">浠锋牸瑕嗙洊鐜�</text>
+                <text class="detail-value"
+                      :class="{ warn: isLowCoverage(row.priceCoverageRate) }">{{ fmtPercent(row.priceCoverageRate) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">缂轰环鐗╂枡鏁�</text>
+                <text class="detail-value"
+                      :class="{ warn: isPositive(row.missingPriceKinds) }">{{ fmtCount(row.missingPriceKinds) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鏃犲伐璧勬爣鍑嗚褰�</text>
+                <text class="detail-value"
+                      :class="{ warn: isPositive(row.noRateRows) }">{{ fmtCount(row.noRateRows) }}</text>
+              </view>
+              <view v-if="isPositive(row.missingPriceKinds)"
+                    class="warn-tip">瀛樺湪缂轰环鐗╂枡锛屾潗鏂欐垚鏈浣庝及锛屽彲鍦ㄣ�岀己浠风墿鏂欍�嶉〉鏌ユ竻鍗曞悗琛ュ綍閲囪喘鍗曚环</view>
+              <view v-if="isPositive(row.noRateRows)"
+                    class="warn-tip">瀛樺湪鏃犲伐璧勬爣鍑嗙殑鎶ュ伐璁板綍锛屼汉宸ユ垚鏈笉鍙俊锛岄渶琛ュ綍宸ユ椂鎴栫淮鎶よ浠跺崟浠�</view>
+            </view>
+          </view>
+          <up-loadmore :status="statStatus" />
+        </view>
+        <view v-else
+              class="no-data">
+          <up-empty mode="data"
+                    text="鏆傛棤鎴愭湰鏍哥畻鏁版嵁"></up-empty>
+        </view>
+      </template>
+
+      <!-- ============ 2. 鏋勬垚姹囨�� ============ -->
+      <template v-else-if="mainTabIndex === 1">
+        <view class="sub-tabs">
+          <up-tabs :list="groupTabs"
+                   :current="groupIndex"
+                   @change="handleGroupChange"
+                   :activeStyle="{ color: '#2979ff', fontWeight: 'bold' }"
+                   lineWidth="30"
+                   lineHeight="3" />
+        </view>
+        <view v-if="summaryList.length > 0"
+              class="ledger-list">
+          <view v-for="(row, index) in summaryList"
+                :key="index"
+                class="ledger-item no-click">
+            <view class="item-header">
+              <view class="item-left">
+                <view class="document-icon">
+                  <up-icon name="grid"
+                           size="16"
+                           color="#ffffff"></up-icon>
+                </view>
+                <text class="item-id">{{ row.groupKey || "鏈~鏃ユ湡" }}</text>
+              </view>
+              <text class="item-index">{{ fmtCount(row.orderCount) }} 鍗�</text>
+            </view>
+            <up-divider></up-divider>
+            <view class="item-details">
+              <!-- 鎴愭湰鏋勬垚鍗犳瘮 -->
+              <view class="rate-bar">
+                <view class="rate-seg seg-material"
+                      :style="{ width: segWidth(row.materialRate) }"></view>
+                <view class="rate-seg seg-labor"
+                      :style="{ width: segWidth(row.laborRate) }"></view>
+                <view class="rate-seg seg-energy"
+                      :style="{ width: segWidth(row.energyRate) }"></view>
+              </view>
+              <view class="rate-legend">
+                <view class="legend-item">
+                  <text class="legend-dot seg-material"></text>
+                  <text class="legend-text">鏉愭枡 {{ fmtPercent(row.materialRate) }}</text>
+                </view>
+                <view class="legend-item">
+                  <text class="legend-dot seg-labor"></text>
+                  <text class="legend-text">浜哄伐 {{ fmtPercent(row.laborRate) }}</text>
+                </view>
+                <view class="legend-item">
+                  <text class="legend-dot seg-energy"></text>
+                  <text class="legend-text">鑳借�� {{ fmtPercent(row.energyRate) }}</text>
+                </view>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">浜ч噺</text>
+                <text class="detail-value">{{ fmtNum(row.outputQty) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鏉愭枡鎴愭湰</text>
+                <text class="detail-value">{{ fmtMoney(row.materialCost) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">浜哄伐鎴愭湰</text>
+                <text class="detail-value">{{ fmtMoney(row.laborCost) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鑳借�楁垚鏈�</text>
+                <text class="detail-value">{{ fmtMoney(row.energyCost) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鎬绘垚鏈�</text>
+                <text class="detail-value highlight">{{ fmtMoney(row.totalCost) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鍗曚綅鎴愭湰</text>
+                <text class="detail-value">{{ fmtUnitCost(row.unitCost) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">姣涘埄</text>
+                <text class="detail-value"
+                      :class="{ danger: isNegative(row.grossProfit) }">{{ fmtMoney(row.grossProfit) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">姣涘埄鐜�</text>
+                <text class="detail-value"
+                      :class="{ danger: isNegative(row.grossProfitRate) }">{{ fmtPercent(row.grossProfitRate) }}</text>
+              </view>
+            </view>
+          </view>
+        </view>
+        <view v-else
+              class="no-data">
+          <up-empty mode="data"
+                    text="鏆傛棤姹囨�绘暟鎹�"></up-empty>
+        </view>
+      </template>
+
+      <!-- ============ 3. 缂轰环鐗╂枡 ============ -->
+      <template v-else>
+        <view v-if="missingList.length > 0"
+              class="ledger-list">
+          <view v-for="(row, index) in missingList"
+                :key="row.productModelId ?? index"
+                class="ledger-item no-click">
+            <view class="item-header">
+              <view class="item-left">
+                <view class="document-icon warn-icon">
+                  <up-icon name="warning"
+                           size="16"
+                           color="#ffffff"></up-icon>
+                </view>
+                <text class="item-id">{{ row.materialName || "-" }}</text>
+              </view>
+              <text class="item-index">{{ fmtCount(row.orderCount) }} 鍗�</text>
+            </view>
+            <up-divider></up-divider>
+            <view class="item-details">
+              <view class="detail-row">
+                <text class="detail-label">瑙勬牸鍨嬪彿</text>
+                <text class="detail-value">{{ row.materialModel || "-" }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鍗曚綅</text>
+                <text class="detail-value">{{ row.unit || "-" }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">娑堣�楅噺</text>
+                <text class="detail-value"
+                      :class="{ warn: true }">{{ fmtNum(row.inputQty) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鏈�鍚庢姇鍏ユ椂闂�</text>
+                <text class="detail-value">{{ row.lastInputTime || "-" }}</text>
+              </view>
+            </view>
+          </view>
+        </view>
+        <view v-else
+              class="no-data">
+          <up-empty mode="data"
+                    text="鏆傛棤缂轰环鐗╂枡"></up-empty>
+        </view>
+      </template>
+    </template>
+
+    <!-- 鏃ユ湡鍖洪棿閫夋嫨 -->
+    <up-calendar :show="showCalendar"
+                 mode="range"
+                 :maxDate="maxDate"
+                 :minDate="minDate"
+                 :monthNum="monthNum"
+                 :defaultDate="defaultDateRange"
+                 @confirm="onDateConfirm"
+                 @close="showCalendar = false"></up-calendar>
+  </view>
+</template>
+
+<script setup>
+  import { ref, reactive, computed } from "vue";
+  import { onReachBottom, onShow } from "@dcloudio/uni-app";
+  import dayjs from "dayjs";
+  import PageHeader from "@/components/PageHeader.vue";
+  import { getWeekRange } from "@/utils/common";
+  import {
+    costStat,
+    costSummary,
+    costMissingPrice,
+  } from "@/api/productionManagement/costAccounting.js";
+
+  const mainTabs = [{ name: "鏍哥畻鏄庣粏" }, { name: "鏋勬垚姹囨��" }, { name: "缂轰环鐗╂枡" }];
+  const mainTabIndex = ref(0);
+
+  // 鏃ユ湡鍖洪棿锛氶粯璁ゆ湰鍛紙鍛ㄤ竴 ~ 浠婂ぉ锛�
+  const maxDate = dayjs().format("YYYY-MM-DD");
+  const minDate = "2022-01-01";
+  // 鏈堜唤鏁伴渶涓� minDate 涓ユ牸涓�鑷达紝灏戜竴涓湀浼氭妸褰撳墠鏈堟尋鍑哄垪琛ㄥ鑷存棩鍘嗗仠鍦ㄨ捣濮嬫湀
+  const monthNum = computed(() => {
+    const max = dayjs(maxDate);
+    const min = dayjs(minDate);
+    return (max.year() - min.year()) * 12 + (max.month() - min.month()) + 1;
+  });
+  const showCalendar = ref(false);
+  const weekRange = getWeekRange();
+  const query = reactive({
+    startDate: weekRange.startDate,
+    endDate: weekRange.endDate,
+    npsNo: "",
+    productName: "",
+  });
+
+  const dateRangeText = computed(() =>
+    query.startDate && query.endDate
+      ? `${query.startDate} 鑷� ${query.endDate}`
+      : "璇烽�夋嫨鏃ユ湡鍖洪棿"
+  );
+  // 鎵撳紑鏃ュ巻鏃跺洖鏄惧綋鍓嶅尯闂�
+  const defaultDateRange = computed(() =>
+    query.startDate && query.endDate
+      ? [query.startDate, query.endDate]
+      : []
+  );
+
+  const loading = ref(false);
+
+  // 鏄庣粏锛堟帴鍙h繑鍥炲叏閲忔暟缁勶紝鍓嶇鍒嗘壒娓叉煋锛�
+  const statRows = ref([]);
+  const pageSize = 10;
+  const showCount = ref(pageSize);
+  const statStatus = ref("loadmore");
+  const visibleStatRows = computed(() => statRows.value.slice(0, showCount.value));
+
+  // 姹囨��
+  const groupTabs = [
+    { name: "鎸夋湀浠�", value: "month" },
+    { name: "鎸夋垚鍝�", value: "product" },
+  ];
+  const groupIndex = ref(0);
+  const groupBy = computed(() => groupTabs[groupIndex.value].value);
+  const summaryList = ref([]);
+
+  // 缂轰环鐗╂枡
+  const missingList = ref([]);
+
+  const goBack = () => {
+    uni.navigateBack();
+  };
+
+  // ============ 鏁板�兼牸寮忓寲 ============
+  const EMPTY = "鈥�";
+  const isBlank = v => v === null || v === undefined || v === "";
+  const toFinite = v => {
+    if (isBlank(v)) return null;
+    const n = Number(v);
+    return Number.isFinite(n) ? n : null;
+  };
+  const fmtNum = v => {
+    const n = toFinite(v);
+    return n === null ? EMPTY : String(n);
+  };
+  const fmtCount = v => {
+    const n = toFinite(v);
+    return n === null ? EMPTY : String(n);
+  };
+  const fmtMoney = v => {
+    const n = toFinite(v);
+    return n === null ? EMPTY : n.toFixed(2);
+  };
+  // 鍗曚綅鎴愭湰鍚庣淇濈暀 4 浣�
+  const fmtUnitCost = v => {
+    const n = toFinite(v);
+    return n === null ? EMPTY : n.toFixed(4);
+  };
+  const fmtPercent = v => {
+    const n = toFinite(v);
+    return n === null ? EMPTY : `${n.toFixed(2)}%`;
+  };
+  const isPositive = v => (toFinite(v) ?? 0) > 0;
+  const isNegative = v => (toFinite(v) ?? 0) < 0;
+  const isLowCoverage = v => {
+    const n = toFinite(v);
+    return n !== null && n < 100;
+  };
+  // 鍗犳瘮鏉℃瀹斤紙鍗犳瘮涓� null 鏃舵寜 0 澶勭悊锛�
+  const segWidth = v => {
+    const n = toFinite(v) ?? 0;
+    return `${Math.max(0, Math.min(n, 100))}%`;
+  };
+
+  // ============ 鏌ヨ ============
+  const buildParams = () => {
+    const params = {};
+    if (query.startDate) params.startDate = query.startDate;
+    if (query.endDate) params.endDate = query.endDate;
+    if (query.npsNo) params.npsNo = query.npsNo;
+    if (query.productName) params.productName = query.productName;
+    return params;
+  };
+
+  const syncStatStatus = () => {
+    statStatus.value =
+      showCount.value >= statRows.value.length ? "nomore" : "loadmore";
+  };
+
+  const fetchStat = async () => {
+    try {
+      const res = await costStat(buildParams());
+      const rows = res?.data ?? res ?? [];
+      statRows.value = Array.isArray(rows) ? rows : [];
+      showCount.value = pageSize;
+      syncStatStatus();
+    } catch (e) {
+      // 閿欒鏂囨宸茬敱 request 缁熶竴 toast
+      statRows.value = [];
+    }
+  };
+
+  const fetchSummary = async () => {
+    try {
+      const res = await costSummary({ ...buildParams(), groupBy: groupBy.value });
+      const rows = res?.data ?? res ?? [];
+      summaryList.value = Array.isArray(rows) ? rows : [];
+    } catch (e) {
+      summaryList.value = [];
+    }
+  };
+
+  const fetchMissing = async () => {
+    try {
+      const res = await costMissingPrice(buildParams());
+      const rows = res?.data ?? res ?? [];
+      missingList.value = Array.isArray(rows) ? rows : [];
+    } catch (e) {
+      missingList.value = [];
+    }
+  };
+
+  const loadCurrentTab = async () => {
+    loading.value = true;
+    try {
+      if (mainTabIndex.value === 0) await fetchStat();
+      else if (mainTabIndex.value === 1) await fetchSummary();
+      else await fetchMissing();
+    } finally {
+      loading.value = false;
+    }
+  };
+
+  const handleQuery = () => {
+    loadCurrentTab();
+  };
+
+  const handleMainTabChange = item => {
+    mainTabIndex.value = item.index;
+    loadCurrentTab();
+  };
+
+  const handleGroupChange = item => {
+    groupIndex.value = item.index;
+    fetchSummary();
+  };
+
+  const onDateConfirm = e => {
+    query.startDate = e[0];
+    query.endDate = e[e.length - 1];
+    showCalendar.value = false;
+    handleQuery();
+  };
+
+  const handleClearDate = () => {
+    query.startDate = "";
+    query.endDate = "";
+    handleQuery();
+  };
+
+  onShow(() => {
+    loadCurrentTab();
+  });
+
+  // 浠呮槑缁嗛渶瑕佸垎椤垫覆鏌�
+  onReachBottom(() => {
+    if (mainTabIndex.value !== 0) return;
+    if (statStatus.value === "nomore") return;
+    showCount.value += pageSize;
+    syncStatStatus();
+  });
+</script>
+
+<style scoped lang="scss">
+  @import "@/styles/procurement-common.scss";
+
+  .cost-accounting {
+    min-height: 100vh;
+    background: #f8f9fa;
+  }
+
+  .main-tabs {
+    background: #fff;
+    margin-bottom: 10px;
+  }
+
+  .sub-tabs {
+    background: #fff;
+    padding: 0 10px 8px;
+  }
+
+  .loading-box {
+    padding-top: 150rpx;
+    display: flex;
+    justify-content: center;
+  }
+
+  .filter-bar {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+
+    & + .filter-bar {
+      margin-top: 12px;
+    }
+  }
+
+  .date-input,
+  .search-input {
+    flex: 1;
+    height: 40px;
+    background: #f5f5f5;
+    border-radius: 8px;
+    padding: 0 12px;
+    display: flex;
+    align-items: center;
+    gap: 8px;
+  }
+
+  .date-input .date-text {
+    flex: 1;
+    font-size: 14px;
+    color: #333;
+  }
+
+  .placeholder {
+    color: #999;
+  }
+
+  .filter-actions-top {
+    display: flex;
+    justify-content: flex-end;
+    gap: 12px;
+    margin-top: 12px;
+  }
+
+  .item-index {
+    font-size: 12px;
+    color: #999;
+    background: #f5f5f5;
+    padding: 2px 8px;
+    border-radius: 12px;
+  }
+
+  .detail-value.warn {
+    color: #ff9800;
+    font-weight: 500;
+  }
+
+  .warn-tip {
+    margin-top: 8px;
+    font-size: 11px;
+    color: #ff9800;
+    line-height: 1.6;
+  }
+
+  .warn-icon {
+    background: linear-gradient(135deg, #ff9800, #f57c00);
+  }
+
+  // 鎴愭湰鏋勬垚鍗犳瘮鏉�
+  .rate-bar {
+    display: flex;
+    height: 12px;
+    border-radius: 6px;
+    overflow: hidden;
+    background: #f0f0f0;
+    margin-bottom: 8px;
+
+    .rate-seg {
+      height: 100%;
+    }
+  }
+
+  .rate-legend {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 12px;
+    margin-bottom: 12px;
+
+    .legend-item {
+      display: flex;
+      align-items: center;
+      gap: 4px;
+    }
+
+    .legend-dot {
+      width: 8px;
+      height: 8px;
+      border-radius: 2px;
+    }
+
+    .legend-text {
+      font-size: 11px;
+      color: #777;
+    }
+  }
+
+  .seg-material {
+    background: #2979ff;
+  }
+
+  .seg-labor {
+    background: #ff9800;
+  }
+
+  .seg-energy {
+    background: #4caf50;
+  }
+
+  .ledger-item.no-click:active {
+    transform: none;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+  }
+</style>
diff --git a/src/pages/productionManagement/materialConsumption/index.vue b/src/pages/productionManagement/materialConsumption/index.vue
new file mode 100644
index 0000000..04ccb29
--- /dev/null
+++ b/src/pages/productionManagement/materialConsumption/index.vue
@@ -0,0 +1,507 @@
+<template>
+  <view class="material-consumption">
+    <PageHeader title="鏉愭枡娑堣�楁牳绠�"
+                @back="goBack" />
+    <!-- 绛涢�夊尯鍩� -->
+    <view class="search-section">
+      <view class="search-bar">
+        <view class="search-input">
+          <up-input class="search-text"
+                    placeholder="璇疯緭鍏ョ敓浜ц鍗曞彿"
+                    v-model="searchForm.npsNo"
+                    @change="handleQuery"
+                    clearable />
+        </view>
+      </view>
+      <view class="search-bar mt-16">
+        <view class="search-input">
+          <up-input class="search-text"
+                    placeholder="璇疯緭鍏ユ垚鍝佸悕绉�/瑙勬牸"
+                    v-model="searchForm.productName"
+                    @change="handleQuery"
+                    clearable />
+        </view>
+      </view>
+      <view class="filter-actions-row">
+        <view class="switch-item">
+          <text class="switch-label">鍙湅宸紓</text>
+          <up-switch v-model="searchForm.onlyDiff"
+                     size="18"
+                     @change="handleQuery" />
+        </view>
+      </view>
+      <view class="date-picker-container">
+        <view class="date-input"
+              @click="showCalendar = true">
+          <up-icon name="calendar"
+                   size="20"
+                   color="#999"></up-icon>
+          <text class="date-text"
+                :class="{ 'placeholder': !searchForm.startDate }">{{ dateRangeText }}</text>
+          <view v-if="searchForm.startDate"
+                class="clear-icon-wrapper"
+                @click.stop="handleClearDate">
+            <up-icon name="close-circle-fill"
+                     size="18"
+                     color="#c0c4cc"></up-icon>
+          </view>
+        </view>
+        <view class="search-btn-wrapper">
+          <up-button type="primary"
+                     size="small"
+                     text="鎼滅储"
+                     @click.stop="handleQuery"></up-button>
+        </view>
+      </view>
+    </view>
+    <!-- 姹囨�� -->
+    <view class="summary-bar"
+          v-if="groupList.length > 0">
+      <text class="summary-text">鍏� {{ groupList.length }} 涓鍗� / {{ allRows.length }} 琛岀墿鏂�</text>
+    </view>
+    <!-- 鍒楄〃鍖哄煙锛堟寜鐢熶骇璁㈠崟鍒嗙粍锛� -->
+    <view v-if="loading && groupList.length === 0"
+          class="loading-box">
+      <up-loading-icon text="鍔犺浇涓�..."></up-loading-icon>
+    </view>
+    <view class="ledger-list"
+          v-else-if="groupList.length > 0">
+      <view class="ledger-item"
+            v-for="group in visibleGroups"
+            :key="group.key">
+        <view class="item-header">
+          <view class="item-left">
+            <view class="document-icon">
+              <up-icon name="file-text"
+                       size="16"
+                       color="#ffffff"></up-icon>
+            </view>
+            <text class="item-id">{{ group.npsNo }}</text>
+          </view>
+          <view class="item-right">
+            <text class="item-index">{{ group.items.length }} 椤圭墿鏂�</text>
+          </view>
+        </view>
+        <view class="order-product">
+          <text class="order-product-label">鎴愬搧</text>
+          <text class="order-product-name">{{ group.finishedProduct }}</text>
+        </view>
+        <up-divider></up-divider>
+        <view class="material-list">
+          <view class="material-item"
+                :class="{ 'row-warn': isDiffWarn(m) }"
+                v-for="(m, mi) in group.items"
+                :key="mi">
+            <view class="material-header">
+              <view class="material-name-box">
+                <text class="material-name">{{ m.materialName || "-" }}</text>
+                <text class="material-model">{{ m.materialModel || "-" }}</text>
+              </view>
+              <view class="material-tags">
+                <text class="off-bom-tag"
+                      v-if="isOffBom(m)">BOM澶�</text>
+                <text class="diff-tag"
+                      :class="{ warn: isDiffWarn(m) }">{{ fmtRate(m.diffRate) }}</text>
+              </view>
+            </view>
+            <view class="material-grid">
+              <view class="detail-cell">
+                <text class="cell-label">鍗曚綅</text>
+                <text class="cell-value">{{ m.unit || "-" }}</text>
+              </view>
+              <view class="detail-cell">
+                <text class="cell-label">BOM鍗曡��</text>
+                <text class="cell-value">{{ fmtQty(m.unitQuantity) }}</text>
+              </view>
+              <view class="detail-cell">
+                <text class="cell-label">瀹屽伐鏁伴噺</text>
+                <text class="cell-value">{{ fmtQty(m.completeQty) }}</text>
+              </view>
+              <view class="detail-cell">
+                <text class="cell-label">鏍囧噯娑堣��</text>
+                <text class="cell-value">{{ fmtQty(m.standardQty) }}</text>
+              </view>
+              <view class="detail-cell">
+                <text class="cell-label">瀹為檯娑堣��</text>
+                <text class="cell-value">{{ fmtQty(m.actualQty) }}</text>
+              </view>
+              <view class="detail-cell">
+                <text class="cell-label">宸紓鏁伴噺</text>
+                <text class="cell-value"
+                      :class="{ warn: isDiffWarn(m) }">{{ fmtQty(m.diffQty) }}</text>
+              </view>
+            </view>
+          </view>
+        </view>
+      </view>
+      <up-loadmore :status="pageStatus"
+                   v-if="groupList.length >= pageSize" />
+    </view>
+    <view v-else
+          class="no-data">
+      <up-empty mode="data"
+                text="鏆傛棤鏁版嵁"></up-empty>
+    </view>
+    <!-- 鏃ュ巻閫夋嫨鍣� -->
+    <up-calendar :show="showCalendar"
+                 mode="range"
+                 :maxDate="maxDate"
+                 minDate="2026-01-01"
+                 :monthNum="monthNum"
+                 :defaultDate="defaultDateRange"
+                 @confirm="onDateConfirm"
+                 @close="showCalendar = false"></up-calendar>
+  </view>
+</template>
+
+<script setup>
+  import { ref, reactive, computed } from "vue";
+  import { onReachBottom, onShow } from "@dcloudio/uni-app";
+  import dayjs from "dayjs";
+  import PageHeader from "@/components/PageHeader.vue";
+  import { stat } from "@/api/productionManagement/materialConsumption.js";
+  import { getWeekRange } from "@/utils/common";
+
+  const loading = ref(false);
+  const showCalendar = ref(false);
+  const maxDate = dayjs().format("YYYY-MM-DD");
+  const monthNum = computed(() => {
+    const min = dayjs("2022-02-01");
+    const max = dayjs(maxDate);
+    return max.diff(min, "month") + 1;
+  });
+
+  // 榛樿鏌ヨ鏈懆锛堝懆涓� ~ 浠婂ぉ锛�
+  const weekRange = getWeekRange();
+  const searchForm = reactive({
+    startDate: weekRange.startDate,
+    endDate: weekRange.endDate,
+    npsNo: "",
+    productName: "",
+    onlyDiff: false,
+  });
+
+  // 鍏ㄩ噺鏄庣粏琛岋紙鎺ュ彛闈炲垎椤碉紝鍓嶇鎸夎鍗曞垎缁勫悗鍒嗘壒娓叉煋锛�
+  const allRows = ref([]);
+  const pageSize = 10;
+  const showCount = ref(pageSize);
+  const pageStatus = ref("loadmore");
+
+  const dateRangeText = computed(() => {
+    if (searchForm.startDate && searchForm.endDate) {
+      return `${searchForm.startDate} 鑷� ${searchForm.endDate}`;
+    }
+    return "璇烽�夋嫨鏃ユ湡鍖洪棿";
+  });
+
+  // 鎵撳紑鏃ュ巻鏃跺洖鏄惧綋鍓嶅尯闂�
+  const defaultDateRange = computed(() =>
+    searchForm.startDate && searchForm.endDate
+      ? [searchForm.startDate, searchForm.endDate]
+      : []
+  );
+
+  // 鎸夌敓浜ц鍗曞垎缁�
+  const groupList = computed(() => {
+    const map = new Map();
+    (allRows.value || []).forEach(row => {
+      const key = String(row.productionOrderId ?? row.npsNo ?? "unknown");
+      if (!map.has(key)) {
+        map.set(key, {
+          key,
+          npsNo: row.npsNo || "-",
+          finishedProduct: row.finishedProduct || "-",
+          items: [],
+        });
+      }
+      map.get(key).items.push(row);
+    });
+    return Array.from(map.values());
+  });
+
+  const visibleGroups = computed(() => groupList.value.slice(0, showCount.value));
+
+  const goBack = () => {
+    uni.navigateBack();
+  };
+
+  // 鏁板�煎厹搴�
+  const toNum = v => Number(v) || 0;
+  const fmtQty = v => {
+    if (v === null || v === undefined || v === "") return "-";
+    const n = Number(v);
+    if (Number.isNaN(n)) return "-";
+    return n.toFixed(2);
+  };
+  const fmtRate = v => {
+    if (v === null || v === undefined || v === "") return "-";
+    const n = Number(v);
+    if (Number.isNaN(n)) return "-";
+    return (n > 0 ? "+" : "") + n.toFixed(2) + "%";
+  };
+  // 宸紓鐜囩粷瀵瑰�� > 10 鏍囩孩
+  const isDiffWarn = row => {
+    if (row?.diffRate === null || row?.diffRate === undefined || row?.diffRate === "") {
+      return false;
+    }
+    return Math.abs(Number(row.diffRate)) > 10;
+  };
+  // BOM澶栨姇鍏ワ細鍗曡�椾笌鏍囧噯娑堣�楀潎涓�0
+  const isOffBom = row => toNum(row?.unitQuantity) === 0 && toNum(row?.standardQty) === 0;
+
+  const buildParams = () => {
+    const params = {};
+    if (searchForm.startDate) params.startDate = searchForm.startDate;
+    if (searchForm.endDate) params.endDate = searchForm.endDate;
+    if (searchForm.npsNo) params.npsNo = searchForm.npsNo;
+    if (searchForm.productName) params.productName = searchForm.productName;
+    if (searchForm.onlyDiff) params.onlyDiff = true;
+    return params;
+  };
+
+  const syncStatus = () => {
+    pageStatus.value =
+      showCount.value >= groupList.value.length ? "nomore" : "loadmore";
+  };
+
+  const getList = async () => {
+    loading.value = true;
+    try {
+      const res = await stat(buildParams());
+      const rows = res?.data ?? res ?? [];
+      allRows.value = Array.isArray(rows) ? rows : [];
+      showCount.value = pageSize;
+      syncStatus();
+    } catch (e) {
+      // 閿欒鏂囨宸茬敱 request 缁熶竴 toast
+    } finally {
+      loading.value = false;
+    }
+  };
+
+  const handleQuery = () => {
+    getList();
+  };
+
+  const onDateConfirm = e => {
+    searchForm.startDate = e[0];
+    searchForm.endDate = e[e.length - 1];
+    showCalendar.value = false;
+    getList();
+  };
+
+  const handleClearDate = () => {
+    searchForm.startDate = "";
+    searchForm.endDate = "";
+    getList();
+  };
+
+  onShow(() => {
+    getList();
+  });
+
+  onReachBottom(() => {
+    if (pageStatus.value === "nomore" || pageStatus.value === "loading") return;
+    pageStatus.value = "loading";
+    setTimeout(() => {
+      showCount.value += pageSize;
+      syncStatus();
+    }, 200);
+  });
+</script>
+
+<style scoped lang="scss">
+  @import "@/styles/procurement-common.scss";
+
+  .material-consumption {
+    min-height: 100vh;
+    background: #f8f9fa;
+    display: flex;
+    flex-direction: column;
+  }
+
+  .mt-16 {
+    margin-top: 16rpx;
+  }
+
+  .filter-actions-row {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin-top: 16rpx;
+  }
+
+  .switch-item {
+    display: flex;
+    align-items: center;
+  }
+
+  .switch-label {
+    font-size: 14px;
+    color: #333;
+    margin-right: 12rpx;
+  }
+
+  .date-picker-container {
+    display: flex;
+    align-items: center;
+    width: 100%;
+    margin-top: 16rpx;
+
+    .date-input {
+      flex: 1;
+      height: 80rpx;
+      background-color: #f5f7fa;
+      border: 1rpx solid #e4e7ed;
+      border-radius: 12rpx;
+      display: flex;
+      align-items: center;
+      padding: 0 24rpx;
+      margin-right: 20rpx;
+
+      .date-text {
+        font-size: 28rpx;
+        color: #303133;
+        margin-left: 16rpx;
+        flex: 1;
+
+        &.placeholder {
+          color: #c0c4cc;
+        }
+      }
+    }
+
+    .search-btn-wrapper {
+      width: 140rpx;
+    }
+  }
+
+  .loading-box {
+    display: flex;
+    justify-content: center;
+    padding-top: 100rpx;
+  }
+
+  .summary-bar {
+    padding: 0 20px 4px;
+
+    .summary-text {
+      font-size: 12px;
+      color: #999;
+    }
+  }
+
+  .order-product {
+    display: flex;
+    align-items: center;
+    padding-bottom: 12px;
+
+    .order-product-label {
+      font-size: 12px;
+      color: #999;
+      background: #f5f5f5;
+      padding: 2px 8px;
+      border-radius: 4px;
+      margin-right: 8px;
+    }
+
+    .order-product-name {
+      font-size: 14px;
+      color: #333;
+      font-weight: 500;
+      flex: 1;
+    }
+  }
+
+  .material-item {
+    padding: 12px 0;
+    border-bottom: 1px solid #f5f5f5;
+
+    &:last-child {
+      border-bottom: none;
+    }
+
+    &.row-warn {
+      background: rgba(238, 10, 36, 0.04);
+    }
+
+    .material-header {
+      display: flex;
+      align-items: flex-start;
+      justify-content: space-between;
+      margin-bottom: 10px;
+
+      .material-name-box {
+        display: flex;
+        align-items: center;
+        flex: 1;
+        flex-wrap: wrap;
+
+        .material-name {
+          font-size: 14px;
+          color: #333;
+          font-weight: 500;
+          margin-right: 8px;
+        }
+
+        .material-model {
+          font-size: 12px;
+          color: #999;
+        }
+      }
+
+      .material-tags {
+        display: flex;
+        align-items: center;
+        flex-shrink: 0;
+      }
+
+      .off-bom-tag {
+        font-size: 11px;
+        color: #ff9800;
+        border: 1px solid #ffcc80;
+        border-radius: 4px;
+        padding: 0 6px;
+        margin-right: 8px;
+      }
+
+      .diff-tag {
+        font-size: 12px;
+        color: #999;
+
+        &.warn {
+          color: #ee0a24;
+          font-weight: 600;
+        }
+      }
+    }
+
+    .material-grid {
+      display: flex;
+      flex-wrap: wrap;
+
+      .detail-cell {
+        width: 33.33%;
+        display: flex;
+        flex-direction: column;
+        margin-bottom: 8px;
+
+        .cell-label {
+          font-size: 11px;
+          color: #999;
+          margin-bottom: 2px;
+        }
+
+        .cell-value {
+          font-size: 13px;
+          color: #333;
+
+          &.warn {
+            color: #ee0a24;
+            font-weight: 600;
+          }
+        }
+      }
+    }
+  }
+</style>
diff --git a/src/pages/productionManagement/outputStatistics/index.vue b/src/pages/productionManagement/outputStatistics/index.vue
new file mode 100644
index 0000000..d35ecb2
--- /dev/null
+++ b/src/pages/productionManagement/outputStatistics/index.vue
@@ -0,0 +1,412 @@
+<template>
+  <view class="output-statistics">
+    <PageHeader title="浜ч噺缁熻"
+                @back="goBack" />
+
+    <!-- 椤堕儴椤电锛氫骇閲忕粺璁� / 璁′欢宸ヨ祫姹囨�� -->
+    <view class="main-tabs">
+      <up-tabs :list="mainTabs"
+               :current="mainTabIndex"
+               @change="handleMainTabChange"
+               :activeStyle="{ color: '#2979ff', fontWeight: 'bold' }"
+               lineWidth="40"
+               lineHeight="3" />
+    </view>
+
+    <!-- ============ 浜ч噺缁熻 ============ -->
+    <template v-if="mainTabIndex === 0">
+      <view class="search-section">
+        <view class="dimension-tabs">
+          <up-tabs :list="dimensionTabs"
+                   :current="dimensionIndex"
+                   @change="handleDimensionChange"
+                   :activeStyle="{ color: '#2979ff', fontWeight: 'bold' }"
+                   lineWidth="30"
+                   lineHeight="3" />
+        </view>
+        <view class="filter-bar">
+          <view class="date-input"
+                @click="showOutputCalendar = true">
+            <up-icon name="calendar"
+                     size="18"
+                     color="#999"></up-icon>
+            <text class="date-text"
+                  :class="{ placeholder: !outputForm.startDate }">{{ outputDateText }}</text>
+          </view>
+          <view v-if="dimension === 'device'"
+                class="device-input"
+                @click="openDeviceSheet">
+            <text :class="{ placeholder: !outputForm.deviceName }">{{ outputForm.deviceName || "閫夋嫨璁惧" }}</text>
+            <up-icon name="arrow-down"
+                     size="14"
+                     color="#999"></up-icon>
+          </view>
+        </view>
+        <view class="filter-actions-top">
+          <up-button type="primary"
+                     size="small"
+                     text="鎼滅储"
+                     @click="handleOutputSearch"></up-button>
+        </view>
+      </view>
+
+      <view v-if="outputList.length > 0"
+            class="ledger-list">
+        <view v-for="(item, index) in outputList"
+              :key="index"
+              class="ledger-item no-click">
+          <view class="item-header">
+            <view class="item-left">
+              <view class="document-icon">
+                <up-icon name="list-dot"
+                         size="16"
+                         color="#ffffff"></up-icon>
+              </view>
+              <text class="item-id">{{ item.groupName || "鏈寚瀹�" }}</text>
+            </view>
+            <text class="item-index">{{ item.workCount || 0 }} 鍗�</text>
+          </view>
+          <up-divider></up-divider>
+          <view class="item-details">
+            <view class="detail-row">
+              <text class="detail-label">浜ч噺</text>
+              <text class="detail-value highlight">{{ Number(item.quantity) || 0 }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鎶ュ簾鏁伴噺</text>
+              <text class="detail-value danger">{{ Number(item.scrapQty) || 0 }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">宸ユ椂(h)</text>
+              <text class="detail-value">{{ Number(item.workHour) || 0 }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+      <view v-else
+            class="no-data">
+        <up-empty mode="data"
+                  text="鏆傛棤鏁版嵁"></up-empty>
+      </view>
+    </template>
+
+    <!-- ============ 璁′欢宸ヨ祫姹囨�� ============ -->
+    <template v-else>
+      <view class="search-section">
+        <view class="filter-bar">
+          <view class="date-input"
+                @click="showWageCalendar = true">
+            <up-icon name="calendar"
+                     size="18"
+                     color="#999"></up-icon>
+            <text class="date-text"
+                  :class="{ placeholder: !wageForm.startDate }">{{ wageDateText }}</text>
+          </view>
+          <up-input class="name-input"
+                    v-model="wageForm.userName"
+                    placeholder="浜哄憳濮撳悕"
+                    clearable
+                    @change="handleWageSearch" />
+        </view>
+        <view class="filter-actions-top">
+          <up-button type="primary"
+                     size="small"
+                     text="鎼滅储"
+                     @click="handleWageSearch"></up-button>
+        </view>
+      </view>
+
+      <view v-if="wageList.length > 0"
+            class="ledger-list">
+        <view v-for="(item, index) in wageList"
+              :key="index"
+              class="ledger-item no-click">
+          <view class="item-header">
+            <view class="item-left">
+              <view class="document-icon">
+                <up-icon name="account"
+                         size="16"
+                         color="#ffffff"></up-icon>
+              </view>
+              <text class="item-id">{{ item.userName || "鏈煡" }}</text>
+            </view>
+            <text class="item-index">{{ item.workCount || 0 }} 鍗�</text>
+          </view>
+          <up-divider></up-divider>
+          <view class="item-details">
+            <view class="detail-row">
+              <text class="detail-label">鐢熶骇鏁伴噺</text>
+              <text class="detail-value">{{ Number(item.quantity) || 0 }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鎬诲伐鏃�(h)</text>
+              <text class="detail-value">{{ Number(item.workHour) || 0 }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">璁′欢宸ヨ祫</text>
+              <text class="detail-value">楼{{ Number(item.pieceWage) || 0 }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">搴斿彂鍚堣</text>
+              <text class="detail-value highlight">楼{{ Number(item.wages) || 0 }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+      <view v-else
+            class="no-data">
+        <up-empty mode="data"
+                  text="鏆傛棤鏁版嵁"></up-empty>
+      </view>
+    </template>
+
+    <!-- 鏃ユ湡鍖洪棿閫夋嫨 -->
+    <up-calendar :show="showOutputCalendar"
+                 mode="range"
+                 :maxDate="maxDate"
+                 minDate="2022-01-01"
+                 :monthNum="monthNum"
+                 @confirm="onOutputDateConfirm"
+                 @close="showOutputCalendar = false"></up-calendar>
+    <up-calendar :show="showWageCalendar"
+                 mode="range"
+                 :maxDate="maxDate"
+                 minDate="2022-01-01"
+                 :monthNum="monthNum"
+                 @confirm="onWageDateConfirm"
+                 @close="showWageCalendar = false"></up-calendar>
+
+    <!-- 璁惧绛涢�� -->
+    <up-action-sheet :show="showDeviceSheet"
+                     :actions="deviceOptions"
+                     title="閫夋嫨璁惧"
+                     @select="selectDevice"
+                     @close="showDeviceSheet = false" />
+  </view>
+</template>
+
+<script setup>
+  import { ref, reactive, computed } from "vue";
+  import { onShow } from "@dcloudio/uni-app";
+  import dayjs from "dayjs";
+  import PageHeader from "@/components/PageHeader.vue";
+  import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
+  import {
+    outputStat,
+    wageSummary,
+  } from "@/api/productionManagement/productionProductMain.js";
+
+  const goBack = () => {
+    uni.navigateBack();
+  };
+
+  // 椤堕儴椤电
+  const mainTabs = [{ name: "浜ч噺缁熻" }, { name: "璁′欢宸ヨ祫姹囨��" }];
+  const mainTabIndex = ref(0);
+  const handleMainTabChange = item => {
+    mainTabIndex.value = item.index;
+    if (mainTabIndex.value === 0) {
+      handleOutputSearch();
+    } else {
+      handleWageSearch();
+    }
+  };
+
+  // 缁村害锛歞evice / operation / product
+  const dimensionTabs = [
+    { name: "鎸夎澶�", value: "device" },
+    { name: "鎸夊伐搴�", value: "operation" },
+    { name: "鎸変骇鍝�", value: "product" },
+  ];
+  const dimensionIndex = ref(0);
+  const dimension = computed(() => dimensionTabs[dimensionIndex.value].value);
+  const handleDimensionChange = item => {
+    dimensionIndex.value = item.index;
+    handleOutputSearch();
+  };
+
+  // 鏃ユ湡鍖洪棿
+  const maxDate = dayjs().format("YYYY-MM-DD");
+  const monthNum = computed(() => dayjs(maxDate).diff(dayjs("2022-02-01"), "month") + 1);
+  const showOutputCalendar = ref(false);
+  const showWageCalendar = ref(false);
+
+  // 浜ч噺缁熻
+  const outputForm = reactive({
+    startDate: dayjs().startOf("month").format("YYYY-MM-DD"),
+    endDate: dayjs().endOf("month").format("YYYY-MM-DD"),
+    deviceId: "",
+    deviceName: "",
+  });
+  const outputList = ref([]);
+  const outputDateText = computed(() =>
+    outputForm.startDate && outputForm.endDate
+      ? `${outputForm.startDate} 鑷� ${outputForm.endDate}`
+      : "璇烽�夋嫨鏃ユ湡鍖洪棿"
+  );
+  const onOutputDateConfirm = e => {
+    outputForm.startDate = e[0];
+    outputForm.endDate = e[e.length - 1];
+    showOutputCalendar.value = false;
+    handleOutputSearch();
+  };
+
+  // 璁′欢宸ヨ祫姹囨��
+  const wageForm = reactive({
+    startDate: dayjs().startOf("month").format("YYYY-MM-DD"),
+    endDate: dayjs().endOf("month").format("YYYY-MM-DD"),
+    userName: "",
+  });
+  const wageList = ref([]);
+  const wageDateText = computed(() =>
+    wageForm.startDate && wageForm.endDate
+      ? `${wageForm.startDate} 鑷� ${wageForm.endDate}`
+      : "璇烽�夋嫨鏃ユ湡鍖洪棿"
+  );
+  const onWageDateConfirm = e => {
+    wageForm.startDate = e[0];
+    wageForm.endDate = e[e.length - 1];
+    showWageCalendar.value = false;
+    handleWageSearch();
+  };
+
+  // 璁惧涓嬫媺
+  const showDeviceSheet = ref(false);
+  const deviceOptions = ref([]);
+  const loadDevices = async () => {
+    if (deviceOptions.value.length > 0) return;
+    try {
+      const { data } = await getDeviceLedger();
+      const rows = Array.isArray(data) ? data : data?.records || [];
+      deviceOptions.value = rows.map(item => ({
+        name: item.deviceName || item.name || "-",
+        value: item.id,
+      }));
+    } catch (e) {
+      console.error("鍔犺浇璁惧鍒楄〃澶辫触", e);
+    }
+  };
+  const openDeviceSheet = () => {
+    loadDevices();
+    showDeviceSheet.value = true;
+  };
+  const selectDevice = e => {
+    outputForm.deviceId = e.value;
+    outputForm.deviceName = e.name;
+    showDeviceSheet.value = false;
+    handleOutputSearch();
+  };
+
+  // 鏌ヨ锛氫骇閲忕粺璁�
+  const handleOutputSearch = () => {
+    outputStat({
+      dimension: dimension.value,
+      startDate: outputForm.startDate || undefined,
+      endDate: outputForm.endDate || undefined,
+      deviceId: dimension.value === "device" ? outputForm.deviceId || undefined : undefined,
+    })
+      .then(res => {
+        outputList.value = Array.isArray(res.data) ? res.data : res.data?.records || [];
+      })
+      .catch(() => {
+        outputList.value = [];
+      });
+  };
+
+  // 鏌ヨ锛氳浠跺伐璧勬眹鎬�
+  const handleWageSearch = () => {
+    wageSummary({
+      startDate: wageForm.startDate || undefined,
+      endDate: wageForm.endDate || undefined,
+      userName: wageForm.userName || undefined,
+    })
+      .then(res => {
+        wageList.value = Array.isArray(res.data) ? res.data : res.data?.records || [];
+      })
+      .catch(() => {
+        wageList.value = [];
+      });
+  };
+
+  onShow(() => {
+    handleOutputSearch();
+  });
+</script>
+
+<style scoped lang="scss">
+  @import "@/styles/procurement-common.scss";
+
+  .output-statistics {
+    min-height: 100vh;
+    background: #f8f9fa;
+  }
+
+  .main-tabs {
+    background: #fff;
+    margin-bottom: 10px;
+  }
+
+  .dimension-tabs {
+    margin-bottom: 16px;
+  }
+
+  .filter-bar {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+  }
+
+  .date-input,
+  .device-input {
+    flex: 1;
+    height: 40px;
+    background: #f5f5f5;
+    border-radius: 8px;
+    padding: 0 12px;
+    display: flex;
+    align-items: center;
+    gap: 8px;
+  }
+
+  .date-input .date-text,
+  .device-input {
+    font-size: 14px;
+    color: #333;
+  }
+
+  .device-input {
+    justify-content: space-between;
+  }
+
+  .placeholder {
+    color: #999;
+  }
+
+  .name-input {
+    flex: 1;
+    height: 40px;
+    background: #f5f5f5;
+    border-radius: 8px;
+    padding: 0 12px;
+  }
+
+  .filter-actions-top {
+    display: flex;
+    justify-content: flex-end;
+    gap: 12px;
+    margin-top: 12px;
+  }
+
+  .item-index {
+    font-size: 12px;
+    color: #999;
+    background: #f5f5f5;
+    padding: 2px 8px;
+    border-radius: 12px;
+  }
+
+  .ledger-item.no-click:active {
+    transform: none;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+  }
+</style>
diff --git a/src/pages/productionManagement/pieceRateConfig/index.vue b/src/pages/productionManagement/pieceRateConfig/index.vue
new file mode 100644
index 0000000..3688220
--- /dev/null
+++ b/src/pages/productionManagement/pieceRateConfig/index.vue
@@ -0,0 +1,698 @@
+<template>
+  <view class="sales-account">
+    <PageHeader title="璁′欢鍗曚环閰嶇疆"
+                @back="goBack" />
+
+    <!-- 鎼滅储鍖哄煙 -->
+    <view class="search-section">
+      <view class="filter-row">
+        <up-input class="filter-input"
+                  v-model="queryParams.operationName"
+                  placeholder="宸ュ簭鍚嶇О"
+                  clearable
+                  @change="handleSearch" />
+        <up-input class="filter-input"
+                  v-model="queryParams.productModel"
+                  placeholder="浜у搧瑙勬牸"
+                  clearable
+                  @change="handleSearch" />
+        <view class="filter-status"
+              @click="showStatusSheet = true">
+          <text :class="{ placeholder: statusText === '' }">{{ statusText || "鍏ㄩ儴鐘舵��" }}</text>
+          <up-icon name="arrow-down"
+                   size="14"
+                   color="#999"></up-icon>
+        </view>
+      </view>
+      <view class="filter-actions-top">
+        <up-button type="primary"
+                   size="small"
+                   text="鎼滅储"
+                   @click="handleSearch"></up-button>
+      </view>
+    </view>
+
+    <!-- 鍒楄〃鍖哄煙 -->
+    <view v-if="list.length > 0"
+          class="ledger-list">
+      <view v-for="item in list"
+            :key="item.id"
+            class="ledger-item">
+        <view class="item-header">
+          <view class="item-left">
+            <view class="document-icon">
+              <up-icon name="setting"
+                       size="16"
+                       color="#ffffff"></up-icon>
+            </view>
+            <text class="item-id">{{ item.productModel || "-" }}</text>
+          </view>
+          <up-tag :text="item.status == 1 ? '鍚敤' : '鍋滅敤'"
+                  :type="item.status == 1 ? 'success' : 'info'"
+                  size="mini" />
+        </view>
+        <up-divider></up-divider>
+        <view class="item-details">
+          <view class="detail-row">
+            <text class="detail-label">璁′欢鍗曚环</text>
+            <text class="detail-value highlight">楼{{ Number(item.unitPrice) || 0 }} / 浠�</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">宸ュ簭鍚嶇О</text>
+            <text class="detail-value">{{ item.operationName || "-" }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">澶囨敞</text>
+            <text class="detail-value">{{ item.remark || "-" }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">鍒涘缓鏃堕棿</text>
+            <text class="detail-value">{{ item.createTime || "-" }}</text>
+          </view>
+        </view>
+        <view class="action-buttons">
+          <up-button class="action-btn"
+                     size="small"
+                     :type="item.status == 1 ? 'warning' : 'success'"
+                     @click="handleChangeStatus(item)">{{ item.status == 1 ? "鍋滅敤" : "鍚敤" }}</up-button>
+          <up-button class="action-btn"
+                     size="small"
+                     type="primary"
+                     @click="handleEdit(item)">缂栬緫</up-button>
+          <up-button class="action-btn"
+                     size="small"
+                     type="error"
+                     @click="handleDelete(item)">鍒犻櫎</up-button>
+        </view>
+      </view>
+      <up-loadmore :status="pageStatus" />
+    </view>
+    <view v-else
+          class="no-data">
+      <up-empty mode="data"
+                text="鏆傛棤鏁版嵁"></up-empty>
+    </view>
+
+    <!-- 鏂板鎸夐挳 -->
+    <view class="fab-button"
+          @click="handleAdd">
+      <up-icon name="plus"
+               size="28"
+               color="#ffffff"></up-icon>
+    </view>
+
+    <!-- 鏂板/缂栬緫寮圭獥 -->
+    <up-popup v-model:show="showForm"
+              mode="bottom"
+              :round="20"
+              :safeAreaInsetBottom="true"
+              @close="showForm = false">
+      <view class="form-popup">
+        <view class="popup-header">
+          <text class="popup-title">{{ form.id ? "缂栬緫璁′欢鍗曚环" : "鏂板璁′欢鍗曚环" }}</text>
+        </view>
+        <view class="popup-body">
+          <up-form labelWidth="120">
+            <up-form-item label="浜у搧"
+                          required>
+              <view class="pick-field"
+                    @click="showProductTree = true">
+                <up-input class="pick-input"
+                          v-model="form.productName"
+                          placeholder="璇烽�夋嫨浜у搧"
+                          readonly />
+                <up-icon name="arrow-right"
+                         color="#999999"></up-icon>
+              </view>
+            </up-form-item>
+            <up-form-item label="瑙勬牸鍨嬪彿"
+                          required>
+              <view class="pick-field"
+                    @click="openModelSheet">
+                <up-input class="pick-input"
+                          v-model="form.model"
+                          placeholder="璇烽�夋嫨瑙勬牸鍨嬪彿"
+                          readonly />
+                <up-icon name="arrow-right"
+                         color="#999999"></up-icon>
+              </view>
+            </up-form-item>
+            <up-form-item label="宸ュ簭鍚嶇О"
+                          required>
+              <up-input v-model="form.operationName"
+                        placeholder="璇疯緭鍏ュ伐搴忓悕绉�"
+                        clearable />
+            </up-form-item>
+            <up-form-item label="璁′欢鍗曚环"
+                          required>
+              <up-input v-model="form.unitPrice"
+                        type="number"
+                        placeholder="璇疯緭鍏ュ崟浠凤紙鍏�/浠讹級" />
+              <text class="unit-text">鍏�/浠�</text>
+            </up-form-item>
+            <up-form-item label="澶囨敞">
+              <up-input v-model="form.remark"
+                        placeholder="璇疯緭鍏ュ娉�"
+                        clearable />
+            </up-form-item>
+          </up-form>
+        </view>
+        <view class="popup-footer">
+          <up-button text="鍙栨秷"
+                     type="info"
+                     plain
+                     :customStyle="{ flex: 1, marginRight: '12px' }"
+                     @click="showForm = false" />
+          <up-button text="淇濆瓨"
+                     type="primary"
+                     :customStyle="{ flex: 1 }"
+                     :loading="submitting"
+                     @click="handleSubmit" />
+        </view>
+      </view>
+    </up-popup>
+
+    <!-- 浜у搧鏍戦�夋嫨鍣� -->
+    <up-popup v-model:show="showProductTree"
+              mode="bottom"
+              :round="true"
+              :closeable="true"
+              @close="showProductTree = false">
+      <view class="tree-selector">
+        <view class="tree-header">
+          <text class="tree-title">閫夋嫨浜у搧</text>
+        </view>
+        <scroll-view scroll-y
+                     class="tree-content">
+          <view v-for="(node, index) in productOptions"
+                :key="index"
+                class="tree-node">
+            <view v-if="node.children && node.children.length > 0"
+                  class="tree-node-header"
+                  @click="toggleNode(node)">
+              <up-icon :name="node.expanded ? 'arrow-down' : 'arrow-right'"
+                       class="tree-node-icon" />
+              <text class="tree-node-label">{{ node.label }}</text>
+            </view>
+            <view v-else
+                  class="tree-node-header"
+                  @click="selectTreeNode(node)">
+              <text class="tree-node-icon-placeholder"></text>
+              <text class="tree-node-label">{{ node.label }}</text>
+              <up-icon name="checkmark"
+                       v-if="form.productId == node.value"
+                       class="tree-node-check" />
+            </view>
+            <view v-if="node.children && node.children.length > 0 && node.expanded"
+                  class="tree-node-children">
+              <view v-for="(child, childIndex) in node.children"
+                    :key="childIndex"
+                    class="tree-node">
+                <view v-if="child.children && child.children.length > 0"
+                      class="tree-node-header"
+                      @click="toggleNode(child)">
+                  <up-icon :name="child.expanded ? 'arrow-down' : 'arrow-right'"
+                           class="tree-node-icon" />
+                  <text class="tree-node-label">{{ child.label }}</text>
+                </view>
+                <view v-else
+                      class="tree-node-header"
+                      @click="selectTreeNode(child)">
+                  <text class="tree-node-icon-placeholder"></text>
+                  <text class="tree-node-label">{{ child.label }}</text>
+                  <up-icon name="checkmark"
+                           v-if="form.productId == child.value"
+                           class="tree-node-check" />
+                </view>
+                <view v-if="child.children && child.children.length > 0 && child.expanded"
+                      class="tree-node-children">
+                  <view v-for="(leaf, leafIndex) in child.children"
+                        :key="leafIndex"
+                        class="tree-node-header"
+                        @click="selectTreeNode(leaf)">
+                    <text class="tree-node-icon-placeholder"></text>
+                    <text class="tree-node-label">{{ leaf.label }}</text>
+                    <up-icon name="checkmark"
+                             v-if="form.productId == leaf.value"
+                             class="tree-node-check" />
+                  </view>
+                </view>
+              </view>
+            </view>
+          </view>
+        </scroll-view>
+      </view>
+    </up-popup>
+
+    <!-- 瑙勬牸鍨嬪彿閫夋嫨鍣� -->
+    <up-action-sheet :show="showModelSheet"
+                     :actions="modelOptions"
+                     title="閫夋嫨瑙勬牸鍨嬪彿"
+                     @select="selectModel"
+                     @close="showModelSheet = false" />
+
+    <!-- 鐘舵�佺瓫閫� -->
+    <up-action-sheet :show="showStatusSheet"
+                     :actions="statusOptions"
+                     title="閫夋嫨鐘舵��"
+                     @select="selectStatus"
+                     @close="showStatusSheet = false" />
+  </view>
+</template>
+
+<script setup>
+  import { ref, reactive } from "vue";
+  import { onReachBottom, onShow } from "@dcloudio/uni-app";
+  import PageHeader from "@/components/PageHeader.vue";
+  import { productTreeList, modelList } from "@/api/basicData/product.js";
+  import {
+    listPage,
+    add,
+    update,
+    changeStatus,
+    batchDelete,
+  } from "@/api/productionManagement/pieceRateConfig.js";
+
+  const goBack = () => {
+    uni.navigateBack();
+  };
+
+  // 鍒楄〃涓庡垎椤�
+  const list = ref([]);
+  const pageStatus = ref("loadmore");
+  const loading = ref(false);
+  const page = reactive({ current: 1, size: 10, total: 0 });
+
+  const queryParams = reactive({
+    operationName: "",
+    productModel: "",
+    status: "",
+  });
+  const statusText = ref("");
+
+  // 鐘舵�佺瓫閫夐�夐」
+  const statusOptions = [
+    { name: "鍏ㄩ儴鐘舵��", value: "" },
+    { name: "鍚敤", value: 1 },
+    { name: "鍋滅敤", value: 0 },
+  ];
+
+  const showStatusSheet = ref(false);
+
+  const selectStatus = e => {
+    queryParams.status = e.value;
+    statusText.value = e.name === "鍏ㄩ儴鐘舵��" ? "" : e.name;
+    showStatusSheet.value = false;
+    handleSearch();
+  };
+
+  // 鍏煎涓嶅悓灞傜骇鐨勫垪琛ㄨ繑鍥炵粨鏋�
+  const pickList = res => {
+    const rows = res?.data?.records || res?.data || res?.records || res;
+    return Array.isArray(rows) ? rows : [];
+  };
+
+  const getList = () => {
+    if (loading.value || pageStatus.value === "loading") return;
+    loading.value = true;
+    pageStatus.value = "loading";
+    const params = {
+      current: page.current,
+      size: page.size,
+      operationName: queryParams.operationName || undefined,
+      productModel: queryParams.productModel || undefined,
+      status: queryParams.status === "" ? undefined : queryParams.status,
+    };
+    listPage(params)
+      .then(res => {
+        const records = res?.data?.records || res?.records || [];
+        const total = res?.data?.total || res?.total || 0;
+        list.value =
+          page.current === 1 ? records : [...list.value, ...records];
+        page.total = total;
+        if (list.value.length >= total || records.length < page.size) {
+          pageStatus.value = "nomore";
+        } else {
+          pageStatus.value = "loadmore";
+          page.current++;
+        }
+      })
+      .catch(() => {
+        pageStatus.value = "loadmore";
+      })
+      .finally(() => {
+        loading.value = false;
+      });
+  };
+
+  const handleSearch = () => {
+    page.current = 1;
+    list.value = [];
+    pageStatus.value = "loadmore";
+    getList();
+  };
+
+  // ============ 琛ㄥ崟 ============
+  const showForm = ref(false);
+  const submitting = ref(false);
+  const form = ref({
+    id: null,
+    productId: "",
+    productName: "",
+    productModelId: "",
+    model: "",
+    operationName: "",
+    unitPrice: "",
+    remark: "",
+  });
+
+  // 浜у搧鏍�
+  const showProductTree = ref(false);
+  const productOptions = ref([]);
+  const showModelSheet = ref(false);
+  const modelOptions = ref([]);
+
+  const convertIdToValue = data => {
+    return (data || []).map(item => {
+      const { id, children, ...rest } = item;
+      const newItem = { ...rest, value: id };
+      if (children && children.length > 0) {
+        newItem.children = convertIdToValue(children);
+      }
+      return newItem;
+    });
+  };
+
+  const findNodeById = (nodes, id) => {
+    for (let i = 0; i < nodes.length; i++) {
+      if (nodes[i].value === id) return nodes[i].label;
+      if (nodes[i].children && nodes[i].children.length > 0) {
+        const found = findNodeById(nodes[i].children, id);
+        if (found) return found;
+      }
+    }
+    return null;
+  };
+
+  const getProductOptions = () => {
+    if (productOptions.value.length > 0) return;
+    productTreeList()
+      .then(res => {
+        productOptions.value = convertIdToValue(pickList(res));
+      })
+      .catch(() => {});
+  };
+
+  const toggleNode = node => {
+    node.expanded = !node.expanded;
+  };
+
+  const selectTreeNode = node => {
+    if (node.children && node.children.length > 0) return;
+    form.value.productId = node.value;
+    form.value.productName = node.label;
+    showProductTree.value = false;
+    getModels(node.value);
+  };
+
+  const getModels = productId => {
+    form.value.productModelId = "";
+    form.value.model = "";
+    modelOptions.value = [];
+    modelList({ id: productId })
+      .then(res => {
+        modelOptions.value = pickList(res).map(item => ({
+          name: item.model,
+          value: item.id,
+        }));
+      })
+      .catch(() => {
+        modelOptions.value = [];
+      });
+  };
+
+  const openModelSheet = () => {
+    if (!form.value.productId) {
+      uni.showToast({ title: "璇峰厛閫夋嫨浜у搧", icon: "none" });
+      return;
+    }
+    showModelSheet.value = true;
+  };
+
+  const selectModel = e => {
+    form.value.productModelId = e.value;
+    form.value.model = e.name;
+    showModelSheet.value = false;
+  };
+
+  const handleAdd = () => {
+    form.value = {
+      id: null,
+      productId: "",
+      productName: "",
+      productModelId: "",
+      model: "",
+      operationName: "",
+      unitPrice: "",
+      remark: "",
+    };
+    getProductOptions();
+    showForm.value = true;
+  };
+
+  const handleEdit = item => {
+    form.value = {
+      id: item.id,
+      productId: item.productId || "",
+      productName: item.productName || "",
+      productModelId: item.productModelId || "",
+      model: item.productModel || "",
+      operationName: item.operationName || "",
+      unitPrice: item.unitPrice ?? "",
+      remark: item.remark || "",
+    };
+    getProductOptions();
+    if (item.productModelId) {
+      modelList({ id: item.productId })
+        .then(res => {
+          modelOptions.value = pickList(res).map(m => ({
+            name: m.model,
+            value: m.id,
+          }));
+        })
+        .catch(() => {});
+    }
+    showForm.value = true;
+  };
+
+  const handleSubmit = () => {
+    if (!form.value.productModelId) {
+      uni.showToast({ title: "璇烽�夋嫨瑙勬牸鍨嬪彿", icon: "none" });
+      return;
+    }
+    if (!form.value.operationName) {
+      uni.showToast({ title: "璇疯緭鍏ュ伐搴忓悕绉�", icon: "none" });
+      return;
+    }
+    const unitPrice = Number(form.value.unitPrice);
+    if (!(unitPrice > 0)) {
+      uni.showToast({ title: "璁′欢鍗曚环蹇呴』澶т簬0", icon: "none" });
+      return;
+    }
+    submitting.value = true;
+    const payload = {
+      productModelId: form.value.productModelId,
+      productModel: form.value.model,
+      operationName: form.value.operationName,
+      unitPrice: unitPrice,
+      remark: form.value.remark,
+    };
+    const req = form.value.id
+      ? update({ ...payload, id: form.value.id })
+      : add(payload);
+    req
+      .then(() => {
+        uni.showToast({ title: form.value.id ? "淇敼鎴愬姛" : "鏂板鎴愬姛" });
+        showForm.value = false;
+        handleSearch();
+      })
+      .catch(() => {})
+      .finally(() => {
+        submitting.value = false;
+      });
+  };
+
+  const handleChangeStatus = item => {
+    const nextStatus = item.status == 1 ? 0 : 1;
+    changeStatus({ id: item.id, status: nextStatus })
+      .then(() => {
+        uni.showToast({ title: nextStatus == 1 ? "宸插惎鐢�" : "宸插仠鐢�" });
+        handleSearch();
+      })
+      .catch(() => {});
+  };
+
+  const handleDelete = item => {
+    uni.showModal({
+      title: "鎻愮ず",
+      content: "纭畾瑕佸垹闄よ璁′欢鍗曚环閰嶇疆鍚楋紵",
+      success: res => {
+        if (res.confirm) {
+          batchDelete([item.id])
+            .then(() => {
+              uni.showToast({ title: "鍒犻櫎鎴愬姛" });
+              handleSearch();
+            })
+            .catch(() => {});
+        }
+      },
+    });
+  };
+
+  onReachBottom(() => {
+    if (pageStatus.value === "nomore") return;
+    if (pageStatus.value === "loading") return;
+    getList();
+  });
+
+  onShow(() => {
+    handleSearch();
+  });
+</script>
+
+<style scoped lang="scss">
+  @import "@/styles/procurement-common.scss";
+
+  .filter-row {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+
+    .filter-input {
+      flex: 1;
+      background: #f5f5f5;
+      border-radius: 8px;
+      padding: 0 12px;
+      height: 40px;
+    }
+
+    .filter-status {
+      flex: 1;
+      height: 40px;
+      background: #f5f5f5;
+      border-radius: 8px;
+      padding: 0 12px;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      font-size: 14px;
+      color: #333;
+
+      .placeholder {
+        color: #999;
+      }
+    }
+  }
+
+  .filter-actions-top {
+    display: flex;
+    justify-content: flex-end;
+    gap: 12px;
+    margin-top: 12px;
+  }
+
+  .form-popup {
+    background: #fff;
+
+    .popup-header {
+      padding: 16px;
+      border-bottom: 1px solid #f5f5f5;
+      text-align: center;
+    }
+
+    .popup-title {
+      font-size: 16px;
+      font-weight: 500;
+      color: #333;
+    }
+
+    .popup-body {
+      padding: 8px 16px;
+      max-height: 60vh;
+      overflow-y: auto;
+    }
+
+    .unit-text {
+      margin-left: 8px;
+      color: #909399;
+      font-size: 12px;
+    }
+
+    // 閫夋嫨绫诲瓧娈碉細readonly 鐨� up-input 浼氭妸鍘熺敓 input 缃负 disabled锛�
+    // disabled 鍏冪礌浼氬悶鎺夌偣鍑讳簨浠跺鑷村灞傛敹涓嶅埌锛屾晠璁� input 涓嶅弬涓庢寚閽堜簨浠讹紝鐢� .pick-field 鎺ョ
+    .pick-field {
+      flex: 1;
+      display: flex;
+      align-items: center;
+
+      .pick-input {
+        flex: 1;
+      }
+
+      :deep(input) {
+        pointer-events: none;
+      }
+    }
+
+    .popup-footer {
+      display: flex;
+      padding: 12px 16px 20px;
+    }
+  }
+
+  .tree-selector {
+    padding: 16px;
+
+    .tree-header {
+      text-align: center;
+      padding-bottom: 12px;
+      border-bottom: 1px solid #f5f5f5;
+    }
+
+    .tree-title {
+      font-size: 16px;
+      font-weight: 500;
+      color: #333;
+    }
+
+    .tree-content {
+      max-height: 60vh;
+    }
+
+    .tree-node-header {
+      display: flex;
+      align-items: center;
+      padding: 12px 4px;
+      border-bottom: 1px solid #f7f7f7;
+
+      .tree-node-icon {
+        margin-right: 8px;
+      }
+
+      .tree-node-icon-placeholder {
+        width: 16px;
+        margin-right: 8px;
+      }
+
+      .tree-node-label {
+        flex: 1;
+        font-size: 14px;
+        color: #333;
+      }
+    }
+
+    .tree-node-children {
+      padding-left: 24px;
+    }
+  }
+</style>
diff --git a/src/pages/productionManagement/productionAccounting/index.vue b/src/pages/productionManagement/productionAccounting/index.vue
index 87ec382..ad6b4de 100644
--- a/src/pages/productionManagement/productionAccounting/index.vue
+++ b/src/pages/productionManagement/productionAccounting/index.vue
@@ -124,6 +124,18 @@
               <text class="detail-value">{{ item.process }}</text>
             </view>
             <view class="detail-row">
+              <text class="detail-label">宸ヨ祫鍙e緞</text>
+              <view class="detail-value">
+                <up-tag :text="item.wageMode || '璁℃椂'"
+                        :type="item.wageMode === '璁′欢' ? 'success' : 'info'"
+                        size="mini" />
+              </view>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">浣跨敤璁惧</text>
+              <text class="detail-value">{{ item.deviceName || "-" }}</text>
+            </view>
+            <view class="detail-row">
               <text class="detail-label">鐢熶骇鏁伴噺</text>
               <text class="detail-value">{{ item.quantity }} {{ item.unit }}</text>
             </view>
diff --git a/src/pages/productionManagement/productionReport/index.vue b/src/pages/productionManagement/productionReport/index.vue
index ac92038..1d20f0a 100644
--- a/src/pages/productionManagement/productionReport/index.vue
+++ b/src/pages/productionManagement/productionReport/index.vue
@@ -40,6 +40,15 @@
                    @click="openProducerPicker"
                    suffix-icon="arrow-down" />
         </u-form-item>
+        <!-- 浣跨敤璁惧 -->
+        <u-form-item label="浣跨敤璁惧"
+                     prop="deviceId">
+          <u-input v-model="form.deviceName"
+                   placeholder="璇烽�夋嫨浣跨敤璁惧锛堥�夊~锛�"
+                   readonly
+                   @click="openDevicePicker"
+                   suffix-icon="arrow-down" />
+        </u-form-item>
         <!-- 宸ユ椂 -->
         <u-form-item label="宸ユ椂"
                      v-if="form.type == 0"
@@ -115,6 +124,12 @@
                      title="閫夋嫨鐢熶骇浜�"
                      @select="onProducerConfirm"
                      @close="showProducerPicker = false" />
+    <!-- 浣跨敤璁惧閫夋嫨鍣� -->
+    <up-action-sheet :show="showDevicePicker"
+                     :actions="deviceList"
+                     title="閫夋嫨浣跨敤璁惧"
+                     @select="onDeviceConfirm"
+                     @close="showDevicePicker = false" />
     <!-- 鍙傛暟閫夋嫨鍣� -->
     <up-action-sheet :show="showParamSelect"
                      :actions="paramOptions"
@@ -142,6 +157,7 @@
     });
   };
   import { addProductMain } from "@/api/productionManagement/productionReporting";
+  import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
   import { getInfo } from "@/api/login";
   import { userListNoPageByTenantId } from "@/api/system/user";
   import {
@@ -167,10 +183,39 @@
     workHour: 0,
     type: null,
     paramGroups: {},
+    deviceId: "",
+    deviceName: "",
   });
 
   const showProducerPicker = ref(false);
   const producerList = ref([]);
+
+  // 浣跨敤璁惧閫夋嫨
+  const showDevicePicker = ref(false);
+  const deviceList = ref([]);
+
+  const openDevicePicker = async () => {
+    if (deviceList.value.length === 0) {
+      try {
+        const { data } = await getDeviceLedger();
+        const rows = Array.isArray(data) ? data : data?.records || [];
+        deviceList.value = rows.map(item => ({
+          name: item.deviceName || item.name || "-",
+          value: item.id,
+        }));
+      } catch (error) {
+        console.error("鍔犺浇璁惧鍒楄〃澶辫触:", error);
+        return;
+      }
+    }
+    showDevicePicker.value = true;
+  };
+
+  const onDeviceConfirm = e => {
+    form.value.deviceId = e.value;
+    form.value.deviceName = e.name;
+    showDevicePicker.value = false;
+  };
 
   const params = ref([]);
   const dictOptions = ref({});
@@ -361,6 +406,7 @@
         form.value.productionOrderRoutingOperationId,
       productionOrderId: form.value.productionOrderId,
       workHour: form.value.workHour,
+      deviceId: form.value.deviceId || undefined,
       productionOperationParamList: productionOperationParamList,
     };
 
diff --git a/src/pages/productionManagement/productionReporting/ledger.vue b/src/pages/productionManagement/productionReporting/ledger.vue
index fb5d1dd..98d0250 100644
--- a/src/pages/productionManagement/productionReporting/ledger.vue
+++ b/src/pages/productionManagement/productionReporting/ledger.vue
@@ -19,6 +19,14 @@
                    color="#999"></up-icon>
         </view>
       </view>
+      <!-- 璁惧绛涢�� -->
+      <view class="device-filter"
+            @click="openDeviceFilter">
+        <text :class="{ placeholder: !searchForm.deviceName }">{{ searchForm.deviceName || "鍏ㄩ儴璁惧" }}</text>
+        <up-icon name="arrow-down"
+                 size="14"
+                 color="#999"></up-icon>
+      </view>
     </view>
     <!-- 鍒楄〃鍖哄煙 -->
     <scroll-view scroll-y
@@ -47,6 +55,10 @@
               <view class="detail-row">
                 <text class="detail-label">鎶ュ伐浜哄憳</text>
                 <text class="detail-value highlight">{{ item.nickName || '-' }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">浣跨敤璁惧</text>
+                <text class="detail-value">{{ item.deviceName || "-" }}</text>
               </view>
               <view class="detail-row">
                 <text class="detail-label">宸ユ椂(h)</text>
@@ -169,6 +181,12 @@
                   text="鏆傛棤鍙傛暟鏁版嵁" />
       </view>
     </up-modal>
+    <!-- 璁惧绛涢�� -->
+    <up-action-sheet :show="showDeviceSheet"
+                     :actions="deviceOptions"
+                     title="閫夋嫨璁惧"
+                     @select="selectDevice"
+                     @close="showDeviceSheet = false" />
   </view>
 </template>
 
@@ -181,6 +199,7 @@
     productionProductInputListPage,
   } from "@/api/productionManagement/productionProductMain.js";
   import PageHeader from "@/components/PageHeader.vue";
+  import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
   import modal from "@/plugins/modal";
 
   const tableData = ref([]);
@@ -195,7 +214,44 @@
 
   const searchForm = reactive({
     keyword: "",
+    deviceId: "",
+    deviceName: "",
   });
+
+  // 璁惧绛涢��
+  const showDeviceSheet = ref(false);
+  const deviceOptions = ref([
+    { name: "鍏ㄩ儴璁惧", value: "" },
+  ]);
+
+  const loadDevices = async () => {
+    if (deviceOptions.value.length > 1) return;
+    try {
+      const { data } = await getDeviceLedger();
+      const rows = Array.isArray(data) ? data : data?.records || [];
+      deviceOptions.value = [
+        { name: "鍏ㄩ儴璁惧", value: "" },
+        ...rows.map(item => ({
+          name: item.deviceName || item.name || "-",
+          value: item.id,
+        })),
+      ];
+    } catch (error) {
+      console.error("鍔犺浇璁惧鍒楄〃澶辫触:", error);
+    }
+  };
+
+  const openDeviceFilter = () => {
+    loadDevices();
+    showDeviceSheet.value = true;
+  };
+
+  const selectDevice = e => {
+    searchForm.deviceId = e.value;
+    searchForm.deviceName = e.value ? e.name : "";
+    showDeviceSheet.value = false;
+    handleQuery();
+  };
 
   // 鎶曞叆璇︽儏鐩稿叧
   const inputVisible = ref(false);
@@ -233,6 +289,7 @@
       current: page.current,
       size: page.size,
       workOrderNo: searchForm.keyword,
+      deviceId: searchForm.deviceId || undefined,
     };
 
     productionProductMainListPage(params)
@@ -321,6 +378,23 @@
     height: 0;
   }
 
+  .device-filter {
+    margin-top: 12px;
+    height: 40px;
+    background: #f5f5f5;
+    border-radius: 8px;
+    padding: 0 12px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    font-size: 14px;
+    color: #333;
+
+    .placeholder {
+      color: #999;
+    }
+  }
+
   .ledger-item {
     .item-header {
       .item-tag {
diff --git a/src/pages/productionManagement/productionTraceability/index.vue b/src/pages/productionManagement/productionTraceability/index.vue
index d187543..7a55b5f 100644
--- a/src/pages/productionManagement/productionTraceability/index.vue
+++ b/src/pages/productionManagement/productionTraceability/index.vue
@@ -2,510 +2,887 @@
   <view class="production-traceability">
     <PageHeader title="鐢熶骇杩芥函"
                 @back="goBack" />
+    <!-- 鏂瑰悜椤电 -->
+    <view class="trace-tabs">
+      <view class="tab-item"
+            :class="{ active: direction === 'forward' }"
+            @click="switchDirection('forward')">姝e悜杩芥函</view>
+      <view class="tab-item"
+            :class="{ active: direction === 'reverse' }"
+            @click="switchDirection('reverse')">鍙嶅悜杩芥函</view>
+    </view>
     <!-- 鎼滅储鍖哄煙 -->
     <view class="search-section">
-      <view class="search-bar"
-            @click="openNpsNoSelector">
+      <view class="search-bar">
         <view class="search-input">
-          <text v-if="!selectedNpsNo"
-                class="placeholder">璇烽�夋嫨鐢熶骇璁㈠崟鍙�</text>
-          <text v-else
-                class="value">{{ selectedNpsNoLabel }}</text>
-        </view>
-        <view class="search-button">
-          <up-icon name="arrow-down"
-                   size="20"
+          <up-icon name="search"
+                   size="18"
                    color="#999"></up-icon>
+          <up-input class="search-text"
+                    v-model="batchNo"
+                    :placeholder="placeholder"
+                    border="none"
+                    clearable
+                    @input="onBatchInput"
+                    @focus="onBatchFocus"
+                    @clear="onBatchClear" />
         </view>
       </view>
+      <!-- 鎵瑰彿鍊欓�変笅鎷� -->
+      <view class="option-panel"
+            v-if="showOptions">
+        <view class="option-item"
+              v-for="(opt, index) in batchOptions"
+              :key="index"
+              @click="onSelectOption(opt)">
+          <view class="option-top">
+            <text class="option-no">{{ opt.batchNo || '-' }}</text>
+            <text class="option-qty">{{ num(opt.qty) }}{{ opt.unit || '' }}</text>
+          </view>
+          <view class="option-bottom">
+            <text class="option-name">{{ text(opt.productName) }} / {{ text(opt.productModel) }}</text>
+            <text class="option-time">{{ opt.inTime || '-' }}</text>
+          </view>
+        </view>
+        <view class="option-empty"
+              v-if="!optionsLoading && batchOptions.length === 0">鏃犲尮閰嶆壒鍙凤紝鍙洿鎺ヨ緭鍏ュ悗鏌ヨ</view>
+      </view>
+      <view class="search-tip">{{ direction === 'forward' ? '杈撳叆鎴愬搧鎵瑰彿锛岃拷婧敓浜у叏閾捐矾' : '杈撳叆鍘熸枡鎵瑰彿锛屽畾浣嶅彈褰卞搷鐨勬垚鍝佽寖鍥�' }}</view>
+      <view class="btn-row">
+        <up-button type="primary"
+                   size="small"
+                   text="鏌ヨ"
+                   @click="handleQuery"></up-button>
+      </view>
     </view>
-    <!-- 鍐呭鍖哄煙 -->
-    <view class="content-container"
-          v-if="rowData.productionOrderDto">
-      <!-- 鍩虹淇℃伅 -->
-      <view class="info-card">
-        <view class="card-title">鍩虹淇℃伅</view>
-        <view class="base-info">
-          <view class="info-row">
-            <text class="label">鐢熶骇璁㈠崟鍙凤細</text>
-            <text class="value">{{ rowData.productionOrderDto?.npsNo || '-' }}</text>
-          </view>
-          <view class="info-row">
-            <text class="label">浜у搧鍚嶇О锛�</text>
-            <text class="value">{{ rowData.productionOrderDto?.productName || '-' }}</text>
-          </view>
-          <view class="info-row">
-            <text class="label">瑙勬牸鍨嬪彿锛�</text>
-            <text class="value">{{ rowData.productionOrderDto?.model || '-' }}</text>
-          </view>
-          <view class="info-row">
-            <text class="label">璁″垝鏁伴噺锛�</text>
-            <text class="value">{{ rowData.productionOrderDto?.quantity || 0 }} {{ rowData.productionOrderDto?.unit }}</text>
-          </view>
-          <view class="info-row">
-            <text class="label">褰撳墠鐘舵�侊細</text>
-            <view class="value">
-              <up-tag :text="getStatusText(rowData.productionOrderDto?.status)"
-                      :type="getStatusType(rowData.productionOrderDto?.status)"
+    <!-- 鍔犺浇涓� -->
+    <view v-if="loading"
+          class="loading-box">
+      <up-loading-icon text="鍔犺浇涓�..."></up-loading-icon>
+    </view>
+    <template v-else>
+      <!-- 鏈煡璇㈡彁绀� -->
+      <view v-if="!queried"
+            class="no-data">
+        <up-empty mode="search"
+                  :text="direction === 'forward' ? '璇疯緭鍏ユ垚鍝佹壒鍙锋煡璇㈣拷婧摼璺�' : '璇疯緭鍏ュ師鏂欐壒鍙锋煡璇㈠奖鍝嶈寖鍥�'"></up-empty>
+      </view>
+      <!-- ================= 姝e悜杩芥函 ================= -->
+      <view v-else-if="direction === 'forward'"
+            class="result-container">
+        <view v-if="forwardEmpty"
+              class="no-data">
+          <up-empty mode="data"
+                    text="鏈煡璇㈠埌璇ユ壒鍙风殑杩芥函璁板綍"></up-empty>
+        </view>
+        <template v-else>
+          <view class="result-title">鐢熶骇璁㈠崟閾捐矾锛坽{ forwardData.chains.length }}锛�</view>
+          <view v-for="(chain, ci) in forwardData.chains"
+                :key="ci"
+                class="ledger-item">
+            <view class="item-header">
+              <view class="item-left">
+                <view class="document-icon">
+                  <up-icon name="file-text"
+                           size="16"
+                           color="#ffffff"></up-icon>
+                </view>
+                <text class="item-id">{{ text(chain.npsNo) }}</text>
+              </view>
+              <up-tag :text="orderStatusText(chain.orderStatus)"
+                      :type="orderStatusType(chain.orderStatus)"
                       size="mini" />
             </view>
-          </view>
-          <view class="info-row">
-            <text class="label">瀹㈡埛鍚嶇О锛�</text>
-            <text class="value">{{ rowData.productionOrderDto?.customerName || '-' }}</text>
-          </view>
-          <view class="info-row">
-            <text class="label">寮�濮嬫棩鏈燂細</text>
-            <text class="value">{{ formatDate(rowData.productionOrderDto?.startTime) }}</text>
-          </view>
-          <view class="info-row">
-            <text class="label">瀹屾垚杩涘害锛�</text>
-            <view class="value progress-box">
-              <up-line-progress :percentage="formatProgress(rowData.productionOrderDto?.completionStatus)"
-                                :activeColor="progressColor(formatProgress(rowData.productionOrderDto?.completionStatus))"
-                                :showText="true" />
+            <up-divider></up-divider>
+            <view class="item-details">
+              <view class="detail-row">
+                <text class="detail-label">鎴愬搧</text>
+                <text class="detail-value font-bold">{{ text(chain.finishedProduct) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鍗曚綅</text>
+                <text class="detail-value">{{ text(chain.unit) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">璁″垝鏁伴噺</text>
+                <text class="detail-value">{{ num(chain.planQty) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">瀹屽伐鏁伴噺</text>
+                <text class="detail-value highlight">{{ num(chain.completeQty) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">璁㈠崟鍒涘缓鏃堕棿</text>
+                <text class="detail-value">{{ text(chain.orderCreateTime) }}</text>
+              </view>
+            </view>
+            <!-- 鍛戒腑鐨勫叆搴撴壒娆� -->
+            <view class="chip-wrap"
+                  v-if="chain.batches && chain.batches.length">
+              <text class="chip"
+                    v-for="(b, bi) in chain.batches"
+                    :key="bi">{{ text(b.batchNo) }} 路 {{ recordTypeText(b.recordType) }} 路 {{ num(b.qty) }}{{ chain.unit || '' }}</text>
+            </view>
+            <!-- 鎶ュ伐宸ュ簭 -->
+            <view class="collapse-block">
+              <view class="collapse-head"
+                    @click="toggleSection(ci, 'reports')">
+                <text class="collapse-title">鎶ュ伐宸ュ簭</text>
+                <text class="collapse-count">{{ (chain.reports || []).length }}</text>
+                <up-icon :name="isSectionOpen(ci, 'reports') ? 'arrow-up' : 'arrow-down'"
+                         size="14"
+                         color="#999"></up-icon>
+              </view>
+              <view class="collapse-body"
+                    v-show="isSectionOpen(ci, 'reports')">
+                <view class="sub-item"
+                      v-for="(r, ri) in chain.reports"
+                      :key="ri">
+                  <view class="detail-row">
+                    <text class="detail-label">宸ュ崟鍙�</text>
+                    <text class="detail-value font-bold">{{ text(r.workOrderNo) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">宸ュ簭</text>
+                    <text class="detail-value">{{ text(r.operationName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">鎶ュ伐浜�</text>
+                    <text class="detail-value">{{ text(r.userName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">璁惧</text>
+                    <text class="detail-value">{{ text(r.deviceName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">鎶ュ伐鍗曞彿</text>
+                    <text class="detail-value">{{ text(r.productNo) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">浜у嚭鏁伴噺</text>
+                    <text class="detail-value">{{ num(r.outputQty) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">鎶ュ簾鏁伴噺</text>
+                    <text class="detail-value danger">{{ num(r.scrapQty) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">鎶ュ伐鏃堕棿</text>
+                    <text class="detail-value">{{ text(r.reportTime) }}</text>
+                  </view>
+                </view>
+                <view class="sub-empty"
+                      v-if="!(chain.reports || []).length">鏆傛棤鎶ュ伐璁板綍</view>
+              </view>
+            </view>
+            <!-- 鎶曞叆鐗╂枡 -->
+            <view class="collapse-block">
+              <view class="collapse-head"
+                    @click="toggleSection(ci, 'inputs')">
+                <text class="collapse-title">鎶曞叆鐗╂枡</text>
+                <text class="collapse-count">{{ (chain.inputs || []).length }}</text>
+                <up-icon :name="isSectionOpen(ci, 'inputs') ? 'arrow-up' : 'arrow-down'"
+                         size="14"
+                         color="#999"></up-icon>
+              </view>
+              <view class="collapse-body"
+                    v-show="isSectionOpen(ci, 'inputs')">
+                <view class="sub-item"
+                      v-for="(m, mi) in chain.inputs"
+                      :key="mi">
+                  <view class="detail-row">
+                    <text class="detail-label">鐗╂枡</text>
+                    <text class="detail-value font-bold">{{ text(m.materialName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">瑙勬牸鍨嬪彿</text>
+                    <text class="detail-value">{{ text(m.materialModel) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">鎶曞叆鏁伴噺</text>
+                    <text class="detail-value">{{ num(m.inputQty) }} {{ text(m.unit) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">宸ュ簭</text>
+                    <text class="detail-value">{{ text(m.operationName) }}</text>
+                  </view>
+                </view>
+                <view class="sub-empty"
+                      v-if="!(chain.inputs || []).length">鏆傛棤鎶曞叆鐗╂枡</view>
+              </view>
+            </view>
+            <!-- 棰嗘枡鎵规 -->
+            <view class="collapse-block">
+              <view class="collapse-head"
+                    @click="toggleSection(ci, 'picks')">
+                <text class="collapse-title">棰嗘枡鎵规</text>
+                <text class="collapse-count">{{ (chain.picks || []).length }}</text>
+                <up-icon :name="isSectionOpen(ci, 'picks') ? 'arrow-up' : 'arrow-down'"
+                         size="14"
+                         color="#999"></up-icon>
+              </view>
+              <view class="collapse-body"
+                    v-show="isSectionOpen(ci, 'picks')">
+                <view class="sub-item"
+                      v-for="(p, pi) in chain.picks"
+                      :key="pi">
+                  <view class="detail-row">
+                    <text class="detail-label">鍘熸枡鎵瑰彿</text>
+                    <text class="detail-value link"
+                          @click="jumpToReverse(p.batchNo)">{{ text(p.batchNo) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">鐗╂枡</text>
+                    <text class="detail-value">{{ text(p.materialName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">瑙勬牸鍨嬪彿</text>
+                    <text class="detail-value">{{ text(p.materialModel) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">棰嗙敤鏁伴噺</text>
+                    <text class="detail-value">{{ num(p.quantity) }} {{ text(p.unit) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">宸ュ簭</text>
+                    <text class="detail-value">{{ text(p.operationName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">棰嗘枡鏃堕棿</text>
+                    <text class="detail-value">{{ text(p.pickTime) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">渚涘簲鍟�</text>
+                    <text class="detail-value">{{ text(p.supplierName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">閲囪喘鍚堝悓鍙�</text>
+                    <text class="detail-value">{{ text(p.contractNo) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">鍘熸枡妫�楠岀粨璁�</text>
+                    <text class="detail-value">{{ text(p.checkResult) }}</text>
+                  </view>
+                </view>
+                <view class="sub-empty"
+                      v-if="!(chain.picks || []).length">鏆傛棤棰嗘枡璁板綍</view>
+              </view>
+            </view>
+            <!-- 妫�楠屽崟 -->
+            <view class="collapse-block">
+              <view class="collapse-head"
+                    @click="toggleSection(ci, 'inspects')">
+                <text class="collapse-title">妫�楠屽崟</text>
+                <text class="collapse-count">{{ (chain.inspects || []).length }}</text>
+                <up-icon :name="isSectionOpen(ci, 'inspects') ? 'arrow-up' : 'arrow-down'"
+                         size="14"
+                         color="#999"></up-icon>
+              </view>
+              <view class="collapse-body"
+                    v-show="isSectionOpen(ci, 'inspects')">
+                <view class="sub-item"
+                      v-for="(ins, ii) in chain.inspects"
+                      :key="ii">
+                  <view class="detail-row">
+                    <text class="detail-label">妫�楠岀被鍨�</text>
+                    <text class="detail-value font-bold">{{ text(ins.inspectTypeName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">妫�楠屽憳</text>
+                    <text class="detail-value">{{ text(ins.checkName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">鏁伴噺</text>
+                    <text class="detail-value">{{ num(ins.quantity) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">鍚堟牸鏁伴噺</text>
+                    <text class="detail-value">{{ num(ins.qualifiedQuantity) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">涓嶅悎鏍兼暟閲�</text>
+                    <text class="detail-value danger">{{ num(ins.unqualifiedQuantity) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">妫�楠岀粨璁�</text>
+                    <text class="detail-value">{{ text(ins.checkResult) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">妫�楠屾椂闂�</text>
+                    <text class="detail-value">{{ text(ins.checkTime) }}</text>
+                  </view>
+                  <!-- 涓嶅悎鏍煎鐞� -->
+                  <view class="unqualified-box"
+                        v-if="(ins.unqualifiedList || []).length">
+                    <view class="unqualified-title">涓嶅悎鏍煎鐞�</view>
+                    <view class="inner-item"
+                          v-for="(u, ui) in ins.unqualifiedList"
+                          :key="ui">
+                      <view class="detail-row">
+                        <text class="detail-label">缂洪櫡鐜拌薄</text>
+                        <text class="detail-value danger">{{ text(u.defectivePhenomena) }}</text>
+                      </view>
+                      <view class="detail-row">
+                        <text class="detail-label">鏁伴噺</text>
+                        <text class="detail-value">{{ num(u.quantity) }}</text>
+                      </view>
+                      <view class="detail-row">
+                        <text class="detail-label">澶勭悊缁撴灉</text>
+                        <text class="detail-value">{{ text(u.dealResult) }}</text>
+                      </view>
+                      <view class="detail-row">
+                        <text class="detail-label">澶勭悊浜�</text>
+                        <text class="detail-value">{{ text(u.dealName) }}</text>
+                      </view>
+                      <view class="detail-row">
+                        <text class="detail-label">澶勭悊鏃堕棿</text>
+                        <text class="detail-value">{{ text(u.dealTime) }}</text>
+                      </view>
+                    </view>
+                  </view>
+                </view>
+                <view class="sub-empty"
+                      v-if="!(chain.inspects || []).length">鏆傛棤妫�楠岃褰�</view>
+              </view>
             </view>
           </view>
-        </view>
+          <!-- 鍑哄簱娴佸悜 -->
+          <view class="result-title">鍑哄簱娴佸悜锛坽{ forwardData.outbounds.length }}锛�</view>
+          <view v-for="(o, oi) in forwardData.outbounds"
+                :key="oi"
+                class="ledger-item">
+            <view class="item-header">
+              <view class="item-left">
+                <view class="document-icon out">
+                  <up-icon name="arrow-upward"
+                           size="16"
+                           color="#ffffff"></up-icon>
+                </view>
+                <text class="item-id">{{ text(o.batchNo) }}</text>
+              </view>
+              <text class="type-tag">{{ recordTypeText(o.recordType) }}</text>
+            </view>
+            <up-divider></up-divider>
+            <view class="item-details">
+              <view class="detail-row">
+                <text class="detail-label">浜у搧鍚嶇О</text>
+                <text class="detail-value font-bold">{{ text(o.productName) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">瑙勬牸鍨嬪彿</text>
+                <text class="detail-value">{{ text(o.productModel) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鍑哄簱鏁伴噺</text>
+                <text class="detail-value highlight">{{ num(o.qty) }} {{ text(o.unit) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鍑哄簱鏃堕棿</text>
+                <text class="detail-value">{{ text(o.outTime) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">澶囨敞</text>
+                <text class="detail-value">{{ text(o.remark) }}</text>
+              </view>
+            </view>
+          </view>
+          <view class="sub-empty card"
+                v-if="!forwardData.outbounds.length">鏆傛棤鍑哄簱娴佸悜璁板綍</view>
+        </template>
       </view>
-      <!-- 宸ュ崟淇℃伅 -->
-      <view class="work-order-section"
-            v-if="rowData.productionRecords && rowData.productionRecords.length > 0">
-        <view class="section-title">宸ュ崟淇℃伅</view>
-        <view v-for="(item, index) in rowData.productionRecords"
-              :key="index"
-              class="work-order-card">
-          <view class="card-header">
-            <text class="work-order-no">{{ item.workOrder.workOrderNo }}</text>
-            <text class="progress-tag"
-                  :style="{ color: progressColor(item.workOrder.completionStatus) }">{{ item.workOrder.completionStatus || 0 }}%</text>
-          </view>
-          <view class="card-content">
-            <view class="content-row">
-              <text class="label">浜у搧/瑙勬牸锛�</text>
-              <text class="value">{{ item.workOrder.productName }} / {{ item.workOrder.model }}</text>
-            </view>
-            <view class="content-row">
-              <text class="label">褰撳墠宸ュ簭锛�</text>
-              <text class="value">{{ item.workOrder.operationName || '-' }}</text>
-            </view>
-            <view class="content-row">
-              <text class="label">闇�姹�/瀹屾垚锛�</text>
-              <text class="value">{{ item.workOrder.planQuantity }} / {{ item.workOrder.completeQuantity }}</text>
-            </view>
-            <view class="content-row">
-              <text class="label">鎶ュ簾鏁伴噺锛�</text>
-              <text class="value error-text">{{ item.workOrder.scrapQty || 0 }}</text>
-            </view>
-          </view>
-          <view class="card-footer">
-            <up-button type="primary"
-                       size="small"
-                       plain
-                       text="鎶ュ伐璁板綍"
-                       @click="handleShowReports(item)"></up-button>
-            <up-button type="success"
-                       size="small"
-                       plain
-                       text="璐ㄦ淇℃伅"
-                       @click="handleShowQuality(item)"></up-button>
-          </view>
-        </view>
-      </view>
+      <!-- ================= 鍙嶅悜杩芥函 ================= -->
       <view v-else
-            class="no-data-minor">
-        <up-empty mode="data"
-                  text="鏆傛棤宸ュ崟淇℃伅"
-                  icon-size="40"></up-empty>
-      </view>
-    </view>
-    <view v-else
-          class="no-data">
-      <up-empty mode="search"
-                text="璇烽�夋嫨鐢熶骇璁㈠崟鍙锋煡鐪嬭拷婧俊鎭�"></up-empty>
-    </view>
-    <!-- 鐢熶骇璁㈠崟鍙烽�夋嫨寮圭獥 -->
-    <up-popup :show="showNpsNoSelector"
-              mode="bottom"
-              @close="showNpsNoSelector = false"
-              round="10">
-      <view class="selector-popup">
-        <view class="popup-header">
-          <text class="popup-title">閫夋嫨鐢熶骇璁㈠崟鍙�</text>
-          <up-icon name="close"
-                   size="20"
-                   @click="showNpsNoSelector = false"></up-icon>
+            class="result-container">
+        <view v-if="reverseEmpty"
+              class="no-data">
+          <up-empty mode="data"
+                    text="鏈煡璇㈠埌璇ユ壒鍙风殑杩芥函璁板綍"></up-empty>
         </view>
-        <view class="search-box">
-          <up-search placeholder="杈撳叆鍏抽敭瀛楁悳绱�"
-                     v-model="npsNoQuery"
-                     :show-action="false"
-                     @change="handleNpsNoSearch"
-                     @search="handleNpsNoSearch"
-                     :loading="npsNoLoading"></up-search>
-        </view>
-        <scroll-view scroll-y
-                     class="options-list">
-          <view v-for="item in npsNoOptions"
-                :key="item.id"
-                class="option-item"
-                @click="onSelectNpsNo(item)">
-            <view class="option-main">
-              <text class="nps-no">{{ item.npsNo }}</text>
-              <text class="product-info">{{ item.productName }} / {{ item.model }}</text>
-            </view>
-            <up-icon v-if="selectedNpsNo === item.id"
-                     name="checkbox-mark"
-                     color="#3c9cff"
-                     size="20"></up-icon>
-          </view>
-          <view v-if="npsNoOptions.length === 0"
-                class="no-options">
-            <text>{{ npsNoLoading ? '鍔犺浇涓�...' : '鏆傛棤閫夐」' }}</text>
-          </view>
-        </scroll-view>
-      </view>
-    </up-popup>
-    <!-- 鎶ュ伐璇︽儏寮圭獥 -->
-    <up-popup :show="reportPopupVisible"
-              mode="bottom"
-              @close="reportPopupVisible = false"
-              round="10">
-      <view class="popup-content">
-        <view class="popup-header">
-          <text class="popup-title">鐢熶骇鎶ュ伐璇︽儏</text>
-          <up-icon name="close"
-                   size="20"
-                   @click="reportPopupVisible = false"></up-icon>
-        </view>
-        <scroll-view scroll-y
-                     class="popup-scroll">
-          <view class="detail-info">
-            <view class="info-row"><text class="label">宸ュ崟鍙凤細</text><text class="value">{{ detailData.workOrder.workOrderNo }}</text></view>
-            <view class="info-row"><text class="label">璁″垝/瀹屾垚锛�</text><text class="value">{{ detailData.workOrder.planQuantity }} / {{ detailData.workOrder.completeQuantity }}</text></view>
-            <view class="info-row"><text class="label">瀹為檯鏃堕棿锛�</text><text class="value">{{ formatDate(detailData.workOrder.actualStartTime) }} 鑷� {{ formatDate(detailData.workOrder.actualEndTime) }}</text></view>
-          </view>
-          <view class="list-title">鎶ュ伐鏄庣粏</view>
-          <view v-for="(report, idx) in detailData.reports"
-                :key="idx"
-                class="detail-item">
-            <view class="item-main">
-              <view class="item-row"><text class="label">鎶ュ伐鍗曞彿锛�</text><text class="value">{{ report.productNo }}</text></view>
-              <view class="item-row"><text class="label">浜у嚭鏁伴噺锛�</text><text class="value">{{ report.quantity || 0 }}</text></view>
-              <view class="item-row"><text class="label">鎶ュ簾鏁伴噺锛�</text><text class="value error-text">{{ report.scrapQty || 0 }}</text></view>
-              <view class="item-row"><text class="label">宸ユ椂(h)锛�</text><text class="value">{{ report.workHour || 0 }}</text></view>
-              <view class="item-row"><text class="label">鍒涘缓浜猴細</text><text class="value">{{ report.userName }}</text></view>
-              <view class="item-row"><text class="label">鍒涘缓鏃堕棿锛�</text><text class="value">{{ formatDate(report.createTime, '{y}-{m}-{d} {h}:{i}') }}</text></view>
-            </view>
-            <view class="item-actions">
-              <text class="action-link"
-                    @click="showParams(report.productionOperationParamList)">鍙傛暟璇︽儏</text>
-              <text class="action-link green"
-                    @click="handleShowInput(report.id)">鎶曞叆璇︽儏</text>
-            </view>
-          </view>
-          <view v-if="!detailData.reports || detailData.reports.length === 0"
-                class="no-data-minor">鏆傛棤鎶ュ伐鏄庣粏</view>
-        </scroll-view>
-      </view>
-    </up-popup>
-    <!-- 鎶曞叆璇︽儏寮圭獥 -->
-    <up-popup :show="inputPopupVisible"
-              mode="bottom"
-              @close="inputPopupVisible = false"
-              round="10">
-      <view class="popup-content">
-        <view class="popup-header">
-          <text class="popup-title">鎶曞叆淇℃伅璇︽儏</text>
-          <up-icon name="close"
-                   size="20"
-                   @click="inputPopupVisible = false"></up-icon>
-        </view>
-        <scroll-view scroll-y
-                     class="popup-scroll">
-          <view class="input-list-popup">
-            <view v-for="(item, idx) in inputListData"
-                  :key="idx"
-                  class="quality-record">
-              <view class="record-title">鎶曞叆璁板綍 {{ idx + 1 }}</view>
-              <view class="info-grid">
-                <view class="info-item"><text class="label">鎶ュ伐鍗曞彿</text><text class="value">{{ item.productNo || '-' }}</text></view>
-                <view class="info-item"><text class="label">鎶曞叆鏁伴噺</text><text class="value">{{ item.quantity || 0 }} {{ item.unit || '' }}</text></view>
-                <view class="info-item full-width"><text class="label">鎶曞叆浜у搧鍚嶇О</text><text class="value">{{ item.productName || '-' }}</text></view>
-                <view class="info-item full-width"><text class="label">鎶曞叆浜у搧鍨嬪彿</text><text class="value">{{ item.model || '-' }}</text></view>
+        <template v-else>
+          <!-- 鍘熸枡鏉ユ簮 -->
+          <view class="result-title">鍘熸枡鏉ユ簮</view>
+          <view class="ledger-item"
+                v-if="reverseData.material">
+            <view class="item-header">
+              <view class="item-left">
+                <view class="document-icon material">
+                  <up-icon name="cube"
+                           size="16"
+                           color="#ffffff"></up-icon>
+                </view>
+                <text class="item-id">{{ text(reverseData.material.materialName) }}</text>
               </view>
+              <text class="type-tag">{{ recordTypeText(reverseData.material.recordType) }}</text>
             </view>
-            <view v-if="!inputListData || inputListData.length === 0"
-                  class="no-data-minor">{{ inputLoading ? '鍔犺浇涓�...' : '鏆傛棤鎶曞叆璁板綍' }}</view>
-          </view>
-        </scroll-view>
-      </view>
-    </up-popup>
-    <!-- 璐ㄦ璇︽儏寮圭獥 -->
-    <up-popup :show="qualityPopupVisible"
-              mode="bottom"
-              @close="qualityPopupVisible = false"
-              round="10">
-      <view class="popup-content">
-        <view class="popup-header">
-          <text class="popup-title">璐ㄦ璇︽儏</text>
-          <up-icon name="close"
-                   size="20"
-                   @click="qualityPopupVisible = false"></up-icon>
-        </view>
-        <scroll-view scroll-y
-                     class="popup-scroll">
-          <view v-for="(record, idx) in qualityRecords"
-                :key="idx"
-                class="quality-record">
-            <view class="record-title">妫�娴嬭褰� {{ idx + 1 }}</view>
-            <view class="info-grid">
-              <view class="info-item"><text class="label">妫�娴嬫棩鏈�</text><text class="value">{{ formatDate(record.createTime) }}</text></view>
-              <view class="info-item"><text class="label">妫�娴嬬粨鏋�</text><up-tag style="width:100rpx"
-                        :text="record.checkResult || '寰呮娴�'"
-                        :type="record.checkResult === '鍚堟牸' ? 'success' : 'error'"
-                        size="mini" /></view>
-              <view class="info-item"><text class="label">妫�楠屽憳</text><text class="value">{{ record.userName }}</text></view>
-              <view class="info-item"><text class="label">鏁伴噺</text><text class="value">{{ record.quantity }} {{ record.unit }}</text></view>
-              <view class="info-item"><text class="label">鎶ュ伐鍗曞彿</text><text class="value">{{ record.reportNo || '-' }}</text></view>
-              <view class="info-item"><text class="label">浜у搧鍚嶇О</text><text class="value">{{ record.productName || '-' }}</text></view>
-              <view class="info-item"><text class="label">瑙勬牸鍨嬪彿</text><text class="value">{{ record.model || '-' }}</text></view>
-              <view class="info-item"><text class="label">妫�娴嬪崟浣�</text><text class="value">{{ record.checkCompany || '-' }}</text></view>
-            </view>
-            <view class="params-table">
-              <view class="table-header">
-                <text class="col">鎸囨爣</text>
-                <text class="col">鍗曚綅</text>
-                <text class="col">鏍囧噯鍊�</text>
-                <text class="col">鍐呮帶鍊�</text>
-                <text class="col">瀹為檯鍊�</text>
+            <up-divider></up-divider>
+            <view class="item-details">
+              <view class="detail-row">
+                <text class="detail-label">鍘熸枡鎵瑰彿</text>
+                <text class="detail-value font-bold">{{ text(reverseData.material.batchNo) }}</text>
               </view>
-              <view v-for="(param, pIdx) in record.inspectParamList"
-                    :key="pIdx"
-                    class="table-row">
-                <text class="col">{{ param.parameterItem }}</text>
-                <text class="col">{{ param.unit || '-' }}</text>
-                <text class="col">{{ param.standardValue }}</text>
-                <text class="col">{{ param.controlValue || '-' }}</text>
-                <text class="col"
-                      :class="{ 'error-text': param.testValue != param.standardValue }">{{ param.testValue }}</text>
+              <view class="detail-row">
+                <text class="detail-label">瑙勬牸鍨嬪彿</text>
+                <text class="detail-value">{{ text(reverseData.material.materialModel) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鍗曚綅</text>
+                <text class="detail-value">{{ text(reverseData.material.unit) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鍏ュ簱鏁伴噺</text>
+                <text class="detail-value">{{ num(reverseData.material.qty) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鍏ュ簱鏃堕棿</text>
+                <text class="detail-value">{{ text(reverseData.material.inTime) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">妫�楠岀粨璁�</text>
+                <text class="detail-value">{{ text(reverseData.material.checkResult) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">妫�楠屾暟閲�</text>
+                <text class="detail-value">{{ num(reverseData.material.inspectQty) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鍚堟牸鏁伴噺</text>
+                <text class="detail-value">{{ num(reverseData.material.qualifiedQty) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">涓嶅悎鏍兼暟閲�</text>
+                <text class="detail-value danger">{{ num(reverseData.material.unqualifiedQty) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">妫�楠屾椂闂�</text>
+                <text class="detail-value">{{ text(reverseData.material.checkTime) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">渚涘簲鍟�</text>
+                <text class="detail-value">{{ text(reverseData.material.supplierName) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">閲囪喘鍚堝悓鍙�</text>
+                <text class="detail-value">{{ text(reverseData.material.contractNo) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">閲囪喘鏃ユ湡</text>
+                <text class="detail-value">{{ text(reverseData.material.entryDate) }}</text>
               </view>
             </view>
           </view>
-          <view v-if="!qualityRecords || qualityRecords.length === 0"
-                class="no-data-minor">鏆傛棤璐ㄦ璁板綍</view>
-        </scroll-view>
+          <view class="ledger-item"
+                v-else>
+            <view class="sub-empty">璇ユ壒鍙锋棤鍏ュ簱璁板綍</view>
+          </view>
+          <!-- 鍙楀奖鍝嶇殑鐢熶骇璁㈠崟 -->
+          <view class="result-title">鍙楀奖鍝嶇敓浜ц鍗曪紙{{ reverseData.orders.length }}锛�</view>
+          <view v-for="(order, oi) in reverseData.orders"
+                :key="oi"
+                class="ledger-item">
+            <view class="item-header">
+              <view class="item-left">
+                <view class="document-icon">
+                  <up-icon name="file-text"
+                           size="16"
+                           color="#ffffff"></up-icon>
+                </view>
+                <text class="item-id">{{ text(order.npsNo) }}</text>
+              </view>
+              <up-tag :text="orderStatusText(order.orderStatus)"
+                      :type="orderStatusType(order.orderStatus)"
+                      size="mini" />
+            </view>
+            <up-divider></up-divider>
+            <view class="item-details">
+              <view class="detail-row">
+                <text class="detail-label">鎴愬搧</text>
+                <text class="detail-value font-bold">{{ text(order.finishedProduct) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">璁″垝鏁伴噺</text>
+                <text class="detail-value">{{ num(order.planQty) }} {{ text(order.unit) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">瀹屽伐鏁伴噺</text>
+                <text class="detail-value">{{ num(order.completeQty) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">璁㈠崟鍒涘缓鏃堕棿</text>
+                <text class="detail-value">{{ text(order.orderCreateTime) }}</text>
+              </view>
+              <view class="detail-row">
+                <text class="detail-label">鏈鍗曢鐢ㄦ暟閲�</text>
+                <text class="detail-value highlight">{{ num(order.pickedQty) }}</text>
+              </view>
+            </view>
+            <!-- 棰嗙敤鏄庣粏 -->
+            <view class="collapse-block">
+              <view class="collapse-head"
+                    @click="toggleSection(oi, 'rpicks')">
+                <text class="collapse-title">棰嗙敤鏄庣粏</text>
+                <text class="collapse-count">{{ (order.picks || []).length }}</text>
+                <up-icon :name="isSectionOpen(oi, 'rpicks') ? 'arrow-up' : 'arrow-down'"
+                         size="14"
+                         color="#999"></up-icon>
+              </view>
+              <view class="collapse-body"
+                    v-show="isSectionOpen(oi, 'rpicks')">
+                <view class="sub-item"
+                      v-for="(p, pi) in order.picks"
+                      :key="pi">
+                  <view class="detail-row">
+                    <text class="detail-label">鎵瑰彿</text>
+                    <text class="detail-value font-bold">{{ text(p.batchNo) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">鐗╂枡</text>
+                    <text class="detail-value">{{ text(p.materialName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">瑙勬牸鍨嬪彿</text>
+                    <text class="detail-value">{{ text(p.materialModel) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">棰嗙敤鏁伴噺</text>
+                    <text class="detail-value">{{ num(p.quantity) }} {{ text(p.unit) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">宸ュ簭</text>
+                    <text class="detail-value">{{ text(p.operationName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">棰嗘枡鏃堕棿</text>
+                    <text class="detail-value">{{ text(p.pickTime) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">渚涘簲鍟�</text>
+                    <text class="detail-value">{{ text(p.supplierName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">閲囪喘鍚堝悓鍙�</text>
+                    <text class="detail-value">{{ text(p.contractNo) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">妫�楠岀粨璁�</text>
+                    <text class="detail-value">{{ text(p.checkResult) }}</text>
+                  </view>
+                </view>
+                <view class="sub-empty"
+                      v-if="!(order.picks || []).length">鏆傛棤棰嗙敤璁板綍</view>
+              </view>
+            </view>
+            <!-- 鎶ュ伐宸ュ簭 -->
+            <view class="collapse-block">
+              <view class="collapse-head"
+                    @click="toggleSection(oi, 'rreports')">
+                <text class="collapse-title">鎶ュ伐宸ュ簭</text>
+                <text class="collapse-count">{{ (order.reports || []).length }}</text>
+                <up-icon :name="isSectionOpen(oi, 'rreports') ? 'arrow-up' : 'arrow-down'"
+                         size="14"
+                         color="#999"></up-icon>
+              </view>
+              <view class="collapse-body"
+                    v-show="isSectionOpen(oi, 'rreports')">
+                <view class="sub-item"
+                      v-for="(r, ri) in order.reports"
+                      :key="ri">
+                  <view class="detail-row">
+                    <text class="detail-label">宸ュ崟鍙�</text>
+                    <text class="detail-value font-bold">{{ text(r.workOrderNo) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">宸ュ簭</text>
+                    <text class="detail-value">{{ text(r.operationName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">鎶ュ伐浜�</text>
+                    <text class="detail-value">{{ text(r.userName) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">浜у嚭鏁伴噺</text>
+                    <text class="detail-value">{{ num(r.outputQty) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">鎶ュ簾鏁伴噺</text>
+                    <text class="detail-value danger">{{ num(r.scrapQty) }}</text>
+                  </view>
+                  <view class="detail-row">
+                    <text class="detail-label">鎶ュ伐鏃堕棿</text>
+                    <text class="detail-value">{{ text(r.reportTime) }}</text>
+                  </view>
+                </view>
+                <view class="sub-empty"
+                      v-if="!(order.reports || []).length">鏆傛棤鎶ュ伐璁板綍</view>
+              </view>
+            </view>
+            <!-- 浜у嚭鎴愬搧鎵瑰彿 -->
+            <view class="collapse-block">
+              <view class="collapse-head"
+                    @click="toggleSection(oi, 'batches')">
+                <text class="collapse-title">浜у嚭鎴愬搧鎵瑰彿</text>
+                <text class="collapse-count">{{ (order.finishedBatches || []).length }}</text>
+                <up-icon :name="isSectionOpen(oi, 'batches') ? 'arrow-up' : 'arrow-down'"
+                         size="14"
+                         color="#999"></up-icon>
+              </view>
+              <view class="collapse-body"
+                    v-show="isSectionOpen(oi, 'batches')">
+                <view class="chip-wrap">
+                  <text class="chip link-chip"
+                        v-for="(b, bi) in order.finishedBatches"
+                        :key="bi"
+                        @click="jumpToForward(b.batchNo)">{{ text(b.batchNo) }} 路 {{ num(b.qty) }}{{ order.unit || '' }}</text>
+                </view>
+                <view class="sub-empty"
+                      v-if="!(order.finishedBatches || []).length">鏆傛棤鎴愬搧鎵规</view>
+              </view>
+            </view>
+          </view>
+          <view class="sub-empty card"
+                v-if="!reverseData.orders.length">璇ユ壒鍙锋殏鏃犵敓浜ц鍗曢鐢ㄨ褰�</view>
+        </template>
       </view>
-    </up-popup>
-    <!-- 鍙傛暟璇︽儏寮圭獥 -->
-    <up-modal :show="paramModalVisible"
-              title="鍙傛暟璇︽儏"
-              @confirm="paramModalVisible = false">
-      <view class="modal-content">
-        <view v-for="(param, idx) in currentParams"
-              :key="idx"
-              class="param-row">
-          <text class="label">{{ param.paramName }}锛�</text>
-          <text class="value">{{ param.inputValue }} {{ param.unit && param.unit !== '/' ? param.unit : '' }}</text>
-        </view>
-        <view v-if="!currentParams || currentParams.length === 0"
-              class="no-data-minor">鏆傛棤鍙傛暟鏁版嵁</view>
-      </view>
-    </up-modal>
+    </template>
   </view>
 </template>
 
 <script setup>
   import { ref, reactive, computed } from "vue";
   import { onLoad } from "@dcloudio/uni-app";
-  import {
-    getOrderDetail,
-    productOrderListPage,
-  } from "@/api/productionManagement/productionOrder";
-  import { productionProductInputListPage } from "@/api/productionManagement/productionProductMain";
   import PageHeader from "@/components/PageHeader.vue";
-  import { parseTime } from "@/utils/ruoyi";
+  import {
+    traceForward,
+    traceReverse,
+    traceBatchOptions,
+  } from "@/api/qualityManagement/productionTrace.js";
 
-  // 閫夋嫨鍣ㄧ浉鍏�
-  const showNpsNoSelector = ref(false);
-  const npsNoQuery = ref("");
-  const npsNoOptions = ref([]);
-  const npsNoLoading = ref(false);
-  const selectedNpsNo = ref(null);
-  const selectedNpsNoLabel = ref("");
+  // 褰撳墠鏂瑰悜锛歠orward 姝e悜 / reverse 鍙嶅悜
+  const direction = ref("forward");
+  const batchNo = ref("");
+  const loading = ref(false);
+  // 鏄惁宸叉墽琛岃繃鏌ヨ
+  const queried = ref(false);
 
-  const rowData = reactive({
-    productionOrderDto: null,
-    productionRecords: [],
-  });
+  // 鎵瑰彿鍊欓�変笅鎷�
+  const batchOptions = ref([]);
+  const showOptions = ref(false);
+  const optionsLoading = ref(false);
+  let searchTimer = null;
 
-  // 鎶ュ伐璇︽儏
-  const reportPopupVisible = ref(false);
-  const detailData = ref({ workOrder: {}, reports: [] });
+  const forwardData = reactive({ batchNo: "", chains: [], outbounds: [] });
+  const reverseData = reactive({ batchNo: "", material: null, orders: [] });
+  // 鎶樺彔闈㈡澘灞曞紑鐘舵��
+  const openMap = reactive({});
 
-  // 鎶曞叆璇︽儏
-  const inputPopupVisible = ref(false);
-  const inputListData = ref([]);
-  const inputLoading = ref(false);
+  const placeholder = computed(() =>
+    direction.value === "forward" ? "璇疯緭鍏ユ垨閫夋嫨鎴愬搧鎵瑰彿" : "璇疯緭鍏ユ垨閫夋嫨鍘熸枡鎵瑰彿"
+  );
 
-  // 璐ㄦ璇︽儏
-  const qualityPopupVisible = ref(false);
-  const qualityRecords = ref([]);
+  const forwardEmpty = computed(
+    () =>
+      forwardData.chains.length === 0 && forwardData.outbounds.length === 0
+  );
+  const reverseEmpty = computed(
+    () => !reverseData.material && reverseData.orders.length === 0
+  );
 
-  // 鍙傛暟璇︽儏
-  const paramModalVisible = ref(false);
-  const currentParams = ref([]);
+  // 鐢熶骇璁㈠崟鐘舵�佸瓧鍏革紙涓庣敓浜ц鍗曞垪琛ㄩ〉淇濇寔涓�鑷达級
+  const ORDER_STATUS_TEXT = {
+    1: "寰呭紑濮�",
+    2: "杩涜涓�",
+    3: "宸插畬鎴�",
+    4: "宸插彇娑�",
+    5: "宸茬粨鏉�",
+  };
+  const ORDER_STATUS_TYPE = {
+    1: "primary",
+    2: "warning",
+    3: "success",
+    4: "info",
+    5: "error",
+  };
+  // 鍏ュ簱/鍑哄簱绫诲瀷瀛楀吀
+  const RECORD_TYPE_TEXT = {
+    0: "鑷畾涔夊叆搴�",
+    1: "鑷畾涔夊嚭搴�",
+    2: "鐢熶骇鎶ュ伐鍏ュ簱",
+    3: "鐢熶骇鎶ュ伐鍑哄簱",
+    6: "璐ㄦ鍚堟牸鍏ュ簱",
+    7: "閲囪喘鍏ュ簱",
+    8: "閿�鍞嚭搴�",
+    10: "閲囪喘鍏ュ簱",
+    13: "閿�鍞彂璐у嚭搴�",
+  };
+
+  const num = val => {
+    const n = Number(val);
+    return Number.isFinite(n) ? n : 0;
+  };
+  const text = val =>
+    val === null || val === undefined || val === "" ? "-" : val;
+  const orderStatusText = status =>
+    ORDER_STATUS_TEXT[status] ?? text(status);
+  const orderStatusType = status => ORDER_STATUS_TYPE[status] || "info";
+  const recordTypeText = type => {
+    if (type === null || type === undefined || type === "") return "-";
+    return RECORD_TYPE_TEXT[String(type)] || `绫诲瀷${type}`;
+  };
 
   const goBack = () => {
     uni.navigateBack();
   };
 
-  const openNpsNoSelector = () => {
-    showNpsNoSelector.value = true;
-    if (npsNoOptions.value.length === 0) {
-      handleNpsNoSearch();
-    }
+  // 鎶樺彔闈㈡澘
+  const isSectionOpen = (key, name) => !!openMap[key + "-" + name];
+  const toggleSection = (key, name) => {
+    const k = key + "-" + name;
+    openMap[k] = !openMap[k];
   };
 
-  const handleNpsNoSearch = async () => {
-    npsNoLoading.value = true;
-    try {
-      const res = await productOrderListPage({
-        npsNo: npsNoQuery.value || "",
-        pageNum: 1,
-        pageSize: 50,
+  // 鍔犺浇鎵瑰彿鍊欓��
+  const loadBatchOptions = () => {
+    optionsLoading.value = true;
+    traceBatchOptions({
+      type: direction.value === "forward" ? "finished" : "material",
+      keyword: batchNo.value || "",
+      limit: 50,
+    })
+      .then(res => {
+        batchOptions.value = res.data || [];
+      })
+      .catch(() => {
+        batchOptions.value = [];
+      })
+      .finally(() => {
+        optionsLoading.value = false;
       });
-      npsNoOptions.value = res.data?.records || res.rows || [];
-    } catch (error) {
-      console.error(error);
-    } finally {
-      npsNoLoading.value = false;
-    }
   };
 
-  const onSelectNpsNo = async item => {
-    selectedNpsNo.value = item.id;
-    selectedNpsNoLabel.value = item.npsNo;
-    showNpsNoSelector.value = false;
-
-    uni.showLoading({ title: "鍔犺浇涓�..." });
-    try {
-      const res = await getOrderDetail(item.npsNo);
-      if (res.code === 200 && res.data) {
-        const { productionOrder, workOrderList } = res.data;
-        rowData.productionOrderDto = productionOrder || item;
-        rowData.productionRecords = workOrderList || [];
-      } else {
-        rowData.productionOrderDto = item;
-        rowData.productionRecords = [];
-      }
-    } catch (error) {
-      console.error(error);
-      rowData.productionOrderDto = item;
-      rowData.productionRecords = [];
-      uni.showToast({ title: "鑾峰彇璇︽儏澶辫触", icon: "none" });
-    } finally {
-      uni.hideLoading();
-    }
+  const onBatchInput = () => {
+    showOptions.value = true;
+    if (searchTimer) clearTimeout(searchTimer);
+    searchTimer = setTimeout(() => {
+      loadBatchOptions();
+    }, 300);
   };
 
-  onLoad(async options => {
-    if (options.npsNo) {
-      uni.showLoading({ title: "鍔犺浇涓�..." });
-      try {
-        const res = await productOrderListPage({
-          npsNo: options.npsNo,
-          pageNum: 1,
-          pageSize: 10,
+  const onBatchFocus = () => {
+    showOptions.value = true;
+    loadBatchOptions();
+  };
+
+  const onBatchClear = () => {
+    batchNo.value = "";
+    batchOptions.value = [];
+    showOptions.value = false;
+  };
+
+  const onSelectOption = opt => {
+    batchNo.value = opt.batchNo || "";
+    showOptions.value = false;
+    handleQuery();
+  };
+
+  // 鍒囨崲鏂瑰悜
+  const switchDirection = dir => {
+    if (direction.value === dir) return;
+    direction.value = dir;
+    batchNo.value = "";
+    batchOptions.value = [];
+    showOptions.value = false;
+    queried.value = false;
+    forwardData.chains = [];
+    forwardData.outbounds = [];
+    reverseData.material = null;
+    reverseData.orders = [];
+  };
+
+  // 鏌ヨ杩芥函鏁版嵁
+  const handleQuery = () => {
+    const no = (batchNo.value || "").trim();
+    if (!no) {
+      uni.showToast({ title: "璇疯緭鍏ユ壒鍙�", icon: "none" });
+      return;
+    }
+    showOptions.value = false;
+    loading.value = true;
+    if (direction.value === "forward") {
+      traceForward({ batchNo: no })
+        .then(res => {
+          const data = res.data || {};
+          forwardData.batchNo = data.batchNo || no;
+          forwardData.chains = data.chains || [];
+          forwardData.outbounds = data.outbounds || [];
+          queried.value = true;
+        })
+        .catch(() => {
+          // 閿欒鏂囨鐢� request 缁熶竴鎻愮ず
+        })
+        .finally(() => {
+          loading.value = false;
         });
-        const records = res.data?.records || res.rows || [];
-        if (records.length > 0) {
-          onSelectNpsNo(records[0]);
-        } else {
-          uni.showToast({ title: "鏈壘鍒扮浉鍏宠鍗�", icon: "none" });
-        }
-      } catch (error) {
-        console.error(error);
-      } finally {
-        uni.hideLoading();
+    } else {
+      traceReverse({ batchNo: no })
+        .then(res => {
+          const data = res.data || {};
+          reverseData.batchNo = data.batchNo || no;
+          reverseData.material = data.material || null;
+          reverseData.orders = data.orders || [];
+          queried.value = true;
+        })
+        .catch(() => {
+          // 閿欒鏂囨鐢� request 缁熶竴鎻愮ず
+        })
+        .finally(() => {
+          loading.value = false;
+        });
+    }
+  };
+
+  // 璺宠浆鍙嶅悜杩芥函骞跺洖濉師鏂欐壒鍙�
+  const jumpToReverse = no => {
+    if (!no) return;
+    direction.value = "reverse";
+    batchNo.value = no;
+    batchOptions.value = [];
+    showOptions.value = false;
+    queried.value = false;
+    forwardData.chains = [];
+    forwardData.outbounds = [];
+    reverseData.material = null;
+    reverseData.orders = [];
+    uni.pageScrollTo({ scrollTop: 0, duration: 0 });
+    handleQuery();
+  };
+
+  // 璺宠浆姝e悜杩芥函骞跺洖濉垚鍝佹壒鍙�
+  const jumpToForward = no => {
+    if (!no) return;
+    direction.value = "forward";
+    batchNo.value = no;
+    batchOptions.value = [];
+    showOptions.value = false;
+    queried.value = false;
+    forwardData.chains = [];
+    forwardData.outbounds = [];
+    reverseData.material = null;
+    reverseData.orders = [];
+    uni.pageScrollTo({ scrollTop: 0, duration: 0 });
+    handleQuery();
+  };
+
+  onLoad(options => {
+    // 鏀寔浠庡叾瀹冮〉闈㈠甫鎵瑰彿杩涘叆
+    if (options && options.batchNo) {
+      try {
+        batchNo.value = decodeURIComponent(options.batchNo);
+      } catch (e) {
+        batchNo.value = options.batchNo;
       }
+      direction.value = options.type === "material" ? "reverse" : "forward";
+      handleQuery();
     }
   });
-
-  const getStatusText = status => {
-    const statusMap = { 1: "寰呭紑濮�", 2: "杩涜涓�", 3: "宸插畬鎴�", 5: "宸茬粨鏉�" };
-    return statusMap[status] || "宸插彇娑�";
-  };
-
-  const getStatusType = status => {
-    const typeMap = { 1: "primary", 2: "warning", 3: "success", 5: "error" };
-    return typeMap[status] || "info";
-  };
-
-  const formatDate = (date, pattern = "{y}-{m}-{d}") => {
-    return parseTime(date, pattern) || "-";
-  };
-
-  const formatProgress = val => {
-    const p = parseFloat(val || 0);
-    return p >= 100 ? 100 : p;
-  };
-
-  const progressColor = percentage => {
-    if (percentage < 30) return "#f56c6c";
-    if (percentage < 70) return "#e6a23c";
-    return "#67c23a";
-  };
-
-  const handleShowReports = row => {
-    detailData.value = {
-      workOrder: row.workOrder || {},
-      reports: (row.reportList || []).map(r => ({
-        ...r.reportMain,
-        ...(r.reportOutputList ? r.reportOutputList[0] : {}),
-        id: r.reportMain.id,
-        productionOperationParamList: r.reportParamList || [],
-      })),
-    };
-    reportPopupVisible.value = true;
-  };
-
-  const handleShowInput = async reportId => {
-    inputPopupVisible.value = true;
-    inputLoading.value = true;
-    inputListData.value = [];
-    try {
-      const res = await productionProductInputListPage({
-        productMainId: reportId,
-        pageNum: 1,
-        pageSize: 100,
-      });
-      inputListData.value = res.data?.records || res.rows || [];
-    } catch (error) {
-      console.error(error);
-      uni.showToast({ title: "鑾峰彇鎶曞叆淇℃伅澶辫触", icon: "none" });
-    } finally {
-      inputLoading.value = false;
-    }
-  };
-
-  const handleShowQuality = row => {
-    const inspects = row.inspectList || [];
-    qualityRecords.value = inspects.map(i => ({
-      ...i.inspect,
-      reportNo: i.reportNo,
-      productName: row.workOrder?.productName || "-",
-      model: row.workOrder?.model || "-",
-      userName: i.reportMain?.userName || "-",
-      inspectParamList: i.inspectParamList || [],
-    }));
-    qualityPopupVisible.value = true;
-  };
-
-  const showParams = params => {
-    currentParams.value = params || [];
-    paramModalVisible.value = true;
-  };
 </script>
 
 <style scoped lang="scss">
@@ -513,520 +890,285 @@
 
   .production-traceability {
     min-height: 100vh;
-    background-color: #f5f7fa;
+    background: #f8f9fa;
+    padding-bottom: 40rpx;
+  }
+
+  // 鏂瑰悜椤电
+  .trace-tabs {
+    display: flex;
+    background: #ffffff;
+
+    .tab-item {
+      flex: 1;
+      text-align: center;
+      padding: 24rpx 0;
+      font-size: 28rpx;
+      color: #666;
+      position: relative;
+
+      &.active {
+        color: #2979ff;
+        font-weight: 600;
+
+        &::after {
+          content: "";
+          position: absolute;
+          left: 50%;
+          bottom: 0;
+          transform: translateX(-50%);
+          width: 80rpx;
+          height: 6rpx;
+          background: #2979ff;
+          border-radius: 4rpx;
+        }
+      }
+    }
   }
 
   .search-section {
-    background-color: #fff;
-    padding: 20rpx 24rpx;
-    margin-bottom: 20rpx;
-  }
-
-  .search-bar {
-    display: flex;
-    align-items: center;
-    background-color: #f2f2f2;
-    border-radius: 8rpx;
-    padding: 0 20rpx;
-    height: 80rpx;
+    position: relative;
+    border-bottom: 1rpx solid #f0f0f0;
 
     .search-input {
-      flex: 1;
-      display: flex;
-      align-items: center;
-
-      .placeholder {
-        font-size: 28rpx;
-        color: #999;
-      }
-
-      .value {
-        font-size: 28rpx;
-        color: #333;
-        font-weight: 500;
-      }
+      height: 76rpx;
     }
 
-    .search-button {
-      padding: 0 10rpx;
-    }
-  }
-
-  .selector-popup {
-    background: #fff;
-    padding: 30rpx;
-    max-height: 70vh;
-    display: flex;
-    flex-direction: column;
-
-    .popup-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 24rpx;
-
-      .popup-title {
-        font-size: 32rpx;
-        font-weight: bold;
-      }
+    .search-text {
+      font-size: 28rpx;
     }
 
-    .search-box {
-      margin-bottom: 20rpx;
-    }
-
-    .options-list {
-      flex: 1;
-      overflow: hidden;
-    }
-
-    .option-item {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      padding: 24rpx 0;
-      border-bottom: 1rpx solid #f0f0f0;
-
-      .option-main {
-        flex: 1;
-        .nps-no {
-          font-size: 28rpx;
-          font-weight: bold;
-          color: #333;
-          display: block;
-          margin-bottom: 4rpx;
-        }
-        .product-info {
-          font-size: 24rpx;
-          color: #999;
-        }
-      }
-    }
-
-    .no-options {
-      text-align: center;
-      padding: 40rpx;
+    .search-tip {
+      font-size: 22rpx;
       color: #999;
-      font-size: 26rpx;
+      margin-top: 12rpx;
     }
-  }
 
-  .content-container {
-    padding: 0 24rpx 40rpx;
-  }
-
-  .info-card {
-    background: #fff;
-    border-radius: 16rpx;
-    padding: 24rpx;
-    margin-bottom: 24rpx;
-    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
-
-    .card-title {
-      font-size: 32rpx;
-      font-weight: bold;
-      color: #333;
-      margin-bottom: 24rpx;
-      padding-left: 16rpx;
-      border-left: 8rpx solid #3c9cff;
-    }
-  }
-
-  .info-grid {
-    display: flex;
-    flex-wrap: wrap;
-
-    .info-item {
-      width: 50%;
-      margin-bottom: 20rpx;
+    .btn-row {
       display: flex;
-      flex-direction: column;
-
-      &.full-width {
-        width: 100%;
-      }
-
-      .label {
-        font-size: 24rpx;
-        color: #999;
-        margin-bottom: 8rpx;
-      }
-
-      .value {
-        font-size: 28rpx;
-        color: #333;
-        word-break: break-all;
-      }
-    }
-  }
-
-  .progress-container {
-    display: flex;
-    align-items: center;
-    gap: 20rpx;
-
-    up-line-progress {
-      flex: 1;
-    }
-
-    .progress-text {
-      font-size: 24rpx;
-      color: #666;
-      min-width: 60rpx;
-    }
-  }
-
-  .section-title {
-    font-size: 32rpx;
-    font-weight: bold;
-    color: #333;
-    margin: 32rpx 0 20rpx;
-  }
-
-  .work-order-card {
-    background: #fff;
-    border-radius: 16rpx;
-    padding: 24rpx;
-    margin-bottom: 20rpx;
-    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
-
-    .card-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 20rpx;
-      padding-bottom: 16rpx;
-      border-bottom: 1rpx solid #f0f0f0;
-
-      .work-order-no {
-        font-size: 28rpx;
-        font-weight: bold;
-        color: #3c9cff;
-      }
-
-      .progress-tag {
-        font-size: 28rpx;
-        font-weight: bold;
-      }
-    }
-
-    .card-content {
-      .content-row {
-        margin-bottom: 12rpx;
-        font-size: 26rpx;
-
-        .label {
-          color: #999;
-        }
-
-        .value {
-          color: #333;
-        }
-      }
-    }
-
-    .card-footer {
-      display: flex;
-      justify-content: flex-end;
       gap: 20rpx;
       margin-top: 20rpx;
     }
   }
 
-  .base-info {
+  // 鎵瑰彿鍊欓�変笅鎷�
+  .option-panel {
+    margin-top: 12rpx;
     background: #fff;
-    padding: 24rpx;
-    border-radius: 16rpx;
-    margin-bottom: 30rpx;
+    border: 1rpx solid #ebeef5;
+    border-radius: 12rpx;
+    max-height: 520rpx;
+    overflow-y: auto;
 
-    .info-row {
-      margin-bottom: 16rpx;
-      font-size: 28rpx;
-      display: flex;
-      align-items: center;
+    .option-item {
+      padding: 18rpx 20rpx;
+      border-bottom: 1rpx solid #f5f5f5;
 
       &:last-child {
-        margin-bottom: 0;
+        border-bottom: none;
       }
 
-      .label {
-        color: #999;
-        min-width: 180rpx;
+      &:active {
+        background: #f5f7fa;
       }
-      .value {
-        color: #333;
-        flex: 1;
-        font-weight: 500;
 
-        &.progress-box {
+      .option-top {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+
+        .option-no {
+          font-size: 28rpx;
+          color: #333;
+          font-weight: 600;
           flex: 1;
+          margin-right: 16rpx;
+        }
+
+        .option-qty {
+          font-size: 24rpx;
+          color: #2979ff;
+        }
+      }
+
+      .option-bottom {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-top: 8rpx;
+
+        .option-name {
+          font-size: 24rpx;
+          color: #666;
+          flex: 1;
+          margin-right: 16rpx;
+        }
+
+        .option-time {
+          font-size: 22rpx;
+          color: #999;
         }
       }
     }
+
+    .option-empty {
+      padding: 30rpx 20rpx;
+      text-align: center;
+      font-size: 24rpx;
+      color: #999;
+    }
   }
 
-  .info-grid {
+  .loading-box {
+    display: flex;
+    justify-content: center;
+    padding-top: 120rpx;
+  }
+
+  .result-container {
+    padding: 20rpx;
+  }
+
+  .result-title {
+    font-size: 28rpx;
+    font-weight: 600;
+    color: #333;
+    margin: 10rpx 0 16rpx;
+
+    &::before {
+      content: "";
+      display: inline-block;
+      width: 6rpx;
+      height: 26rpx;
+      background: #2979ff;
+      border-radius: 4rpx;
+      margin-right: 12rpx;
+      vertical-align: middle;
+    }
+  }
+
+  .type-tag {
+    font-size: 22rpx;
+    color: #2979ff;
+    background: #ecf5ff;
+    border-radius: 6rpx;
+    padding: 4rpx 12rpx;
+  }
+
+  .document-icon {
+    &.out {
+      background: #ff9900;
+    }
+    &.material {
+      background: #52c41a;
+    }
+  }
+
+  .font-bold {
+    font-weight: 500;
+  }
+
+  .detail-value.link,
+  .chip.link-chip {
+    color: #2979ff;
+    text-decoration: underline;
+  }
+
+  // 鎵规鏍囩
+  .chip-wrap {
     display: flex;
     flex-wrap: wrap;
-    padding: 10rpx 0;
+    gap: 12rpx;
+    padding-bottom: 12rpx;
 
-    .info-item {
-      width: 50%;
-      margin-bottom: 20rpx;
+    .chip {
+      font-size: 22rpx;
+      color: #333;
+      background: #f5f7fa;
+      border-radius: 8rpx;
+      padding: 8rpx 16rpx;
+    }
+  }
+
+  // 鎶樺彔闈㈡澘
+  .collapse-block {
+    border-top: 1rpx solid #f0f0f0;
+
+    .collapse-head {
       display: flex;
-      flex-direction: column;
+      align-items: center;
+      padding: 24rpx 0;
 
-      &.full-width {
-        width: 100%;
-      }
-
-      .label {
-        font-size: 24rpx;
-        color: #999;
-        margin-bottom: 4rpx;
-      }
-      .value {
+      .collapse-title {
         font-size: 28rpx;
         color: #333;
         font-weight: 500;
       }
-    }
-  }
 
-  .popup-content {
-    background: #fff;
-    padding: 30rpx;
-    max-height: 80vh;
-    display: flex;
-    flex-direction: column;
-    border-radius: 20rpx 20rpx 0 0;
-
-    .popup-header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: 30rpx;
-
-      .popup-title {
-        font-size: 34rpx;
-        font-weight: bold;
-        color: #333;
+      .collapse-count {
+        font-size: 22rpx;
+        color: #2979ff;
+        background: #ecf5ff;
+        border-radius: 20rpx;
+        padding: 2rpx 14rpx;
+        margin: 0 12rpx 0 auto;
       }
     }
 
-    .popup-scroll {
-      flex: 1;
-      overflow: hidden;
+    .collapse-body {
+      padding-bottom: 16rpx;
     }
   }
 
-  .detail-info {
+  .sub-item {
     background: #f8f9fa;
-    padding: 24rpx;
-    border-radius: 16rpx;
-    margin-bottom: 30rpx;
-    flex-direction: column;
+    border-radius: 10rpx;
+    padding: 16rpx;
+    margin-bottom: 12rpx;
 
-    .info-row {
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  .inner-item {
+    padding: 12rpx;
+    margin-top: 10rpx;
+    background: #fff5f5;
+    border-radius: 8rpx;
+
+    &:first-child {
+      margin-top: 0;
+    }
+  }
+
+  .unqualified-box {
+    margin-top: 16rpx;
+    padding-top: 16rpx;
+    border-top: 1rpx dashed #e4e7ed;
+
+    .unqualified-title {
+      font-size: 24rpx;
+      color: #ee0a24;
+      font-weight: 500;
       margin-bottom: 12rpx;
-      font-size: 28rpx;
-      display: flex;
-
-      &:last-child {
-        margin-bottom: 0;
-      }
-
-      .label {
-        color: #999;
-        min-width: 140rpx;
-      }
-      .value {
-        color: #333;
-        flex: 1;
-        font-weight: 500;
-      }
     }
   }
 
-  .list-title {
-    font-size: 30rpx;
-    font-weight: bold;
-    margin-bottom: 20rpx;
-    color: #333;
-    display: flex;
-    align-items: center;
-
-    &::before {
-      content: "";
-      width: 6rpx;
-      height: 28rpx;
-      background: #3c9cff;
-      margin-right: 12rpx;
-      border-radius: 4rpx;
-    }
-  }
-
-  .detail-item {
-    background: #fff;
-    border: 1rpx solid #f0f0f0;
-    border-radius: 12rpx;
-    padding: 24rpx;
-    margin-bottom: 20rpx;
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-
-    .item-main {
-      flex: 1;
-      .item-row {
-        font-size: 26rpx;
-        margin-bottom: 8rpx;
-        display: flex;
-
-        &:last-child {
-          margin-bottom: 0;
-        }
-
-        .label {
-          color: #999;
-          min-width: 130rpx;
-        }
-        .value {
-          color: #333;
-          flex: 1;
-        }
-      }
-    }
-
-    .item-actions {
-      display: flex;
-      flex-direction: column;
-      gap: 16rpx;
-      padding-left: 20rpx;
-      border-left: 1rpx solid #f0f0f0;
-
-      .action-link {
-        font-size: 26rpx;
-        color: #3c9cff;
-        white-space: nowrap;
-
-        &.green {
-          color: #52c41a;
-        }
-      }
-    }
-  }
-
-  .quality-record {
-    background: #fff;
-    border: 1rpx solid #f0f0f0;
-    border-radius: 16rpx;
-    padding: 24rpx;
-    margin-bottom: 30rpx;
-
-    .record-title {
-      font-size: 30rpx;
-      font-weight: bold;
-      color: #3c9cff;
-      margin-bottom: 24rpx;
-      display: flex;
-      justify-content: space-between;
-    }
-  }
-
-  .params-table {
-    margin-top: 24rpx;
-    border: 1rpx solid #f0f0f0;
-    border-radius: 12rpx;
-    overflow: hidden;
-
-    .table-header {
-      display: flex;
-      background: #f8f9fa;
-      padding: 20rpx 16rpx;
-      font-size: 26rpx;
-      font-weight: bold;
-      color: #666;
-    }
-
-    .table-row {
-      display: flex;
-      padding: 20rpx 16rpx;
-      font-size: 26rpx;
-      border-top: 1rpx solid #f0f0f0;
-      color: #333;
-
-      &:nth-child(even) {
-        background: #fafafa;
-      }
-    }
-
-    .col {
-      flex: 1;
-      text-align: center;
-      word-break: break-all;
-    }
-  }
-
-  .modal-content {
-    padding: 30rpx;
-    .param-row {
-      margin-bottom: 20rpx;
-      font-size: 28rpx;
-      display: flex;
-
-      &:last-child {
-        margin-bottom: 0;
-      }
-
-      .label {
-        color: #666;
-        min-width: 160rpx;
-      }
-      .value {
-        color: #333;
-        font-weight: 500;
-        flex: 1;
-      }
-    }
-  }
-
-  .input-list-popup {
-    .input-item {
-      background: #fff;
-      border: 1rpx solid #f0f0f0;
-      border-radius: 12rpx;
-      padding: 20rpx;
-      margin-bottom: 20rpx;
-
-      .input-row {
-        display: flex;
-        font-size: 26rpx;
-        margin-bottom: 8rpx;
-        &:last-child {
-          margin-bottom: 0;
-        }
-        .label {
-          color: #999;
-          min-width: 160rpx;
-        }
-        .value {
-          color: #333;
-          flex: 1;
-        }
-      }
-    }
-  }
-
-  .error-text {
-    color: #f56c6c;
-    font-weight: bold;
-  }
-
-  .no-data-minor {
-    text-align: center;
-    padding: 60rpx 40rpx;
+  .sub-empty {
+    font-size: 24rpx;
     color: #999;
-    font-size: 28rpx;
+    text-align: center;
+    padding: 24rpx 0;
+
+    &.card {
+      background: #fff;
+      border-radius: 12rpx;
+      margin-bottom: 16rpx;
+    }
+  }
+
+  .no-data {
+    padding-top: 100rpx;
   }
 </style>
diff --git a/src/pages/qualityManagement/finalInspection/add.vue b/src/pages/qualityManagement/finalInspection/add.vue
index 94321dd..5bd64db 100644
--- a/src/pages/qualityManagement/finalInspection/add.vue
+++ b/src/pages/qualityManagement/finalInspection/add.vue
@@ -29,10 +29,10 @@
         <up-input v-model="form.productName"
                   placeholder="璇烽�夋嫨浜у搧"
                   readonly
-                  @click="showProductTree = true"
-                  :disabled="isEdit" />
+                  @click="!isReadonly && (showProductTree = true)"
+                  :disabled="isEdit || isReadonly" />
         <template #right>
-          <up-icon @click="showProductTree = true"
+          <up-icon @click="!isReadonly && (showProductTree = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -43,9 +43,9 @@
         <up-input v-model="form.model"
                   placeholder="璇烽�夋嫨瑙勬牸鍨嬪彿"
                   readonly
-                  :disabled="isEdit" />
+                  :disabled="isEdit || isReadonly" />
         <template #right>
-          <up-icon @click="showModelSheet = true"
+          <up-icon @click="!isReadonly && (showModelSheet = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -55,7 +55,8 @@
                     border-bottom>
         <up-input v-model="testStandardDisplay"
                   placeholder="璇烽�夋嫨鎸囨爣"
-                  readonly />
+                  readonly
+                  :disabled="isReadonly" />
         <template #right>
           <up-icon @click="openTestStandardSheet"
                    name="arrow-right" />
@@ -75,13 +76,14 @@
         <up-input v-model="form.quantity"
                   type="number"
                   placeholder="璇疯緭鍏ユ暟閲�"
-                  :disabled="processQuantityDisabled" />
+                  :disabled="processQuantityDisabled || isReadonly" />
       </up-form-item>
       <up-form-item label="妫�娴嬪崟浣�"
                     prop="checkCompany"
                     border-bottom>
         <up-input v-model="form.checkCompany"
                   placeholder="璇疯緭鍏ユ娴嬪崟浣�"
+                  :disabled="isReadonly"
                   clearable />
       </up-form-item>
       <up-form-item label="妫�娴嬬粨鏋�"
@@ -91,9 +93,10 @@
         <up-input v-model="form.checkResult"
                   placeholder="璇烽�夋嫨妫�娴嬬粨鏋�"
                   readonly
+                  :disabled="isReadonly"
                   @click="showResultSheet" />
         <template #right>
-          <up-icon @click="showResultSheet = true"
+          <up-icon @click="!isReadonly && (showResultSheet = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -103,9 +106,10 @@
         <up-input v-model="form.checkName"
                   placeholder="璇烽�夋嫨妫�楠屽憳"
                   readonly
+                  :disabled="isReadonly"
                   @click="showInspectorSheet" />
         <template #right>
-          <up-icon @click="showInspectorSheet = true"
+          <up-icon @click="!isReadonly && (showInspectorSheet = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -115,7 +119,8 @@
                     border-bottom>
         <up-input v-model="form.checkTime"
                   placeholder="璇烽�夋嫨妫�娴嬫棩鏈�"
-                  readonly />
+                  readonly
+                  :disabled="isReadonly" />
         <!-- <template #right>
           <up-icon name="calendar"
                    @click="showDatePicker"></up-icon>
@@ -155,6 +160,7 @@
                 <text class="step-label">妫�楠屽�硷細</text>
                 <up-input v-model="item.testValue"
                           placeholder="璇疯緭鍏ユ楠屽��"
+                          :disabled="isReadonly"
                           clearable
                           border-bottom
                           class="step-input" />
@@ -174,14 +180,16 @@
                  size="default"
                  @click="goBack"
                  class="bottom-btn">
-        鍙栨秷
+        {{ isReadonly ? '杩斿洖' : '鍙栨秷' }}
       </up-button>
-      <up-button type="primary"
+      <!-- 宸叉彁浜ゅ崟鎹彧璇伙紝闅愯棌淇濆瓨/鎻愪氦 -->
+      <up-button v-if="!isReadonly"
+                 type="primary"
                  size="default"
                  @click="submitForm"
                  :loading="loading"
                  class="bottom-btn">
-        {{ isEdit ? '淇濆瓨' : '鎻愪氦' }}
+        淇濆瓨
       </up-button>
     </view>
     <!-- 鏃ユ湡閫夋嫨鍣� -->
@@ -465,6 +473,9 @@
     return !!(v.productMainId != null || v.purchaseLedgerId != null);
   });
 
+  // 宸叉彁浜わ紙inspectState=1锛夊崟鎹暣浣撳彧璇�
+  const isReadonly = computed(() => Number(form.value?.inspectState) === 1);
+
   // 杩斿洖涓婁竴椤�
   const goBack = () => {
     uni.navigateBack();
@@ -472,11 +483,13 @@
 
   // 鏄剧ず鏃ユ湡閫夋嫨鍣�
   const showDatePicker = () => {
+    if (isReadonly.value) return;
     showDate.value = true;
   };
 
   // 纭鏃ユ湡閫夋嫨
   const confirmDate = e => {
+    if (isReadonly.value) return;
     form.value.checkTime = dayjs(e.value).format("YYYY-MM-DD");
   };
 
@@ -597,7 +610,7 @@
       });
   };
   const openTestStandardSheet = () => {
-    console.log("openTestStandardSheet");
+    if (isReadonly.value) return;
     showTestStandardSheet.value = true;
   };
 
@@ -674,6 +687,11 @@
   // 鎻愪氦琛ㄥ崟
   const submitForm = async () => {
     console.log("submitForm", form.value, tableData.value);
+    // 宸叉彁浜ゅ崟鎹姝繚瀛�/鎻愪氦
+    if (isReadonly.value) {
+      showToast("妫�楠屽崟宸叉彁浜わ紝涓嶅厑璁镐慨鏀�");
+      return;
+    }
     try {
       // await formRef.value.validate();
       if (!form.value.productModelId) {
@@ -713,21 +731,22 @@
       const data = { ...form.value, qualityInspectParams: tableData.value };
       data.quantity = Number(data.quantity);
       if (isEdit.value) {
-        const res = await qualityInspectUpdate(data);
+        await qualityInspectUpdate(data);
         showToast("淇濆瓨鎴愬姛");
         setTimeout(() => {
           uni.navigateBack();
         }, 1500);
       } else {
-        const res = await qualityInspectAdd(data);
-        showToast("鎻愪氦鎴愬姛");
+        // 鏂板浠呬繚瀛樿崏绋匡紝姝e紡鎻愪氦鍦ㄥ垪琛ㄩ〉銆屾彁浜ゃ�嶆寜閽紙浼氱敓鎴愬叆搴撹褰曪級
+        await qualityInspectAdd(data);
+        showToast("淇濆瓨鎴愬姛");
         setTimeout(() => {
           uni.navigateBack();
         }, 1500);
       }
     } catch (error) {
-      console.error("琛ㄥ崟楠岃瘉澶辫触:", error);
-      showToast("鎻愪氦澶辫触锛岃閲嶈瘯");
+      // request.ts 宸茬粺涓� toast 鍚庣 msg锛岃繖閲屽彧璁板綍
+      console.error("鎻愪氦澶辫触:", error);
     } finally {
       loading.value = false;
     }
diff --git a/src/pages/qualityManagement/finalInspection/detail.vue b/src/pages/qualityManagement/finalInspection/detail.vue
index d274a28..574147e 100644
--- a/src/pages/qualityManagement/finalInspection/detail.vue
+++ b/src/pages/qualityManagement/finalInspection/detail.vue
@@ -24,7 +24,7 @@
             <u-tag :type="getStateTagType(detailData.inspectState)"
                    size="small"
                    class="status-tag">
-              {{ detailData.inspectState ? '宸叉彁浜�' : '鏈彁浜�' }}
+              {{ detailData.inspectState ? '宸叉彁浜�' : '寰呮彁浜�' }}
             </u-tag>
           </view>
         </view>
@@ -66,6 +66,18 @@
             <text class="detail-label">妫�娴嬪崟浣�</text>
             <text class="detail-value">{{ detailData.checkCompany || '-' }}</text>
           </view>
+          <view class="detail-row">
+            <text class="detail-label">鍚堟牸鏁伴噺</text>
+            <text class="detail-value">{{ detailData.qualifiedQuantity != null ? detailData.qualifiedQuantity : '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">涓嶅悎鏍兼暟閲�</text>
+            <text class="detail-value">{{ detailData.unqualifiedQuantity != null ? detailData.unqualifiedQuantity : '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">妫�楠岀粨璁�</text>
+            <text class="detail-value">{{ detailData.checkResult || '-' }}</text>
+          </view>
           <!-- <view class="detail-row">
             <text class="detail-label">妫�楠屾爣鍑�</text>
             <text class="detail-value">{{ detailData.testStandardName || '-' }}</text>
diff --git a/src/pages/qualityManagement/finalInspection/index.vue b/src/pages/qualityManagement/finalInspection/index.vue
index 0e860ff..a8692d1 100644
--- a/src/pages/qualityManagement/finalInspection/index.vue
+++ b/src/pages/qualityManagement/finalInspection/index.vue
@@ -26,6 +26,17 @@
                    color="#999"></up-icon>
         </view>
       </view>
+      <!-- 妫�楠岀姸鎬佺瓫閫� -->
+      <view class="filter-row">
+        <view class="filter-item"
+              @click="showStateSheet = true">
+          <text class="filter-label">妫�楠岀姸鎬�</text>
+          <text class="filter-value">{{ stateFilterLabel }}</text>
+          <up-icon name="arrow-down"
+                   size="14"
+                   color="#999"></up-icon>
+        </view>
+      </view>
       <!-- 鏃ユ湡閫夋嫨 -->
       <!-- <view class="date-range"
             v-if="searchForm.entryDate">
@@ -85,7 +96,7 @@
               <u-tag :type="getStateTagType(item.inspectState)"
                      size="mini"
                      class="status-tag">
-                {{ item.inspectState ? '宸叉彁浜�' : '鏈彁浜�' }}
+                {{ item.inspectState ? '宸叉彁浜�' : '寰呮彁浜�' }}
               </u-tag>
             </view>
           </view>
@@ -114,6 +125,18 @@
             <view class="detail-row">
               <text class="detail-label">妫�娴嬪崟浣�</text>
               <text class="detail-value">{{ item.checkCompany || '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鍚堟牸鏁伴噺</text>
+              <text class="detail-value">{{ item.qualifiedQuantity != null ? item.qualifiedQuantity : '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">涓嶅悎鏍兼暟閲�</text>
+              <text class="detail-value">{{ item.unqualifiedQuantity != null ? item.unqualifiedQuantity : '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">妫�楠岀粨璁�</text>
+              <text class="detail-value">{{ item.checkResult || '-' }}</text>
             </view>
           </view>
           <!-- 鎿嶄綔鎸夐挳 -->
@@ -152,6 +175,15 @@
                       :disabled="item.inspectState || item.checkName !== ''"
                       @click.stop="assignInspector(item)">
               鍒嗛厤妫�楠屽憳
+            </u-button>
+          </view>
+          <view class="action-buttons">
+            <u-button type="error"
+                      size="small"
+                      class="action-btn"
+                      :disabled="!(Number(item.unqualifiedQuantity) > 0)"
+                      @click.stop="openUnqualified(item)">
+              涓嶅悎鏍煎鐞�
             </u-button>
           </view>
         </view>
@@ -223,6 +255,56 @@
                      :actions="userSheetOptions"
                      @select="selectInspector"
                      title="閫夋嫨妫�楠屽憳" />
+    <!-- 妫�楠岀姸鎬佺瓫閫� -->
+    <up-action-sheet :show="showStateSheet"
+                     :actions="stateSheetOptions"
+                     @select="selectState"
+                     @close="showStateSheet = false"
+                     title="閫夋嫨妫�楠岀姸鎬�" />
+    <!-- 涓嶅悎鏍煎鐞嗗脊绐� -->
+    <up-popup v-model:show="showUnqualifiedPopup"
+              mode="bottom"
+              round>
+      <view class="unqualified-dialog">
+        <view class="dialog-header">
+          <text class="dialog-title">涓嶅悎鏍煎鐞�</text>
+          <up-icon name="close"
+                   size="20"
+                   color="#999"
+                   @click="showUnqualifiedPopup = false"></up-icon>
+        </view>
+        <view class="dialog-content">
+          <view v-if="unqualifiedLoading"
+                class="unqualified-empty">鍔犺浇涓�...</view>
+          <view v-else-if="unqualifiedList.length === 0"
+                class="unqualified-empty">鏆傛棤寰呭鐞嗕笉鍚堟牸璁板綍</view>
+          <view v-for="(row, idx) in unqualifiedList"
+                :key="idx"
+                class="unqualified-item">
+            <view class="unqualified-info">
+              <text class="unqualified-label">涓嶅悎鏍肩幇璞�</text>
+              <text class="unqualified-value">{{ row.defectivePhenomena || '-' }}</text>
+            </view>
+            <view class="unqualified-info">
+              <text class="unqualified-label">鏁伴噺</text>
+              <text class="unqualified-value">{{ row.quantity != null ? row.quantity : '-' }}</text>
+            </view>
+            <u-button type="primary"
+                      size="small"
+                      class="unqualified-btn"
+                      @click="openDealSheet(row)">
+              澶勭悊
+            </u-button>
+          </view>
+        </view>
+      </view>
+    </up-popup>
+    <!-- 澶勭悊缁撴灉閫夋嫨 -->
+    <up-action-sheet :show="showDealSheet"
+                     :actions="dealSheetOptions"
+                     @select="selectDeal"
+                     @close="showDealSheet = false"
+                     title="閫夋嫨澶勭悊缁撴灉" />
   </view>
 </template>
 
@@ -235,8 +317,13 @@
     submitQualityInspect,
     qualityInspectUpdate,
     qualityInspectListPage,
+    qualityUnqualifiedListPage,
+    qualityUnqualifiedDeal,
   } from "@/api/qualityManagement/materialInspection.js";
   import { userListNoPage } from "@/api/system/user.js";
+
+  // 鍑哄巶/鎴愬搧妫�楠� inspectType=2
+  const currentInspectType = 2;
 
   // 鏄剧ず鎻愮ず淇℃伅
   const showToast = message => {
@@ -294,6 +381,121 @@
       value: item.nickName,
     }));
   });
+
+  // 妫�楠岀姸鎬佺瓫閫�
+  const showStateSheet = ref(false);
+  const stateFilter = ref("");
+  const stateSheetOptions = [
+    { name: "鍏ㄩ儴", value: "" },
+    { name: "寰呮彁浜�", value: 0 },
+    { name: "宸叉彁浜�", value: 1 },
+  ];
+  const stateFilterLabel = computed(() => {
+    const hit = stateSheetOptions.find(o => o.value === stateFilter.value);
+    return hit ? hit.name : "鍏ㄩ儴";
+  });
+  const selectState = e => {
+    stateFilter.value = e.value;
+    showStateSheet.value = false;
+    getList();
+  };
+
+  // 涓嶅悎鏍煎鐞嗙浉鍏�
+  const showUnqualifiedPopup = ref(false);
+  const unqualifiedLoading = ref(false);
+  const unqualifiedList = ref([]);
+  const currentInspectRow = ref(null);
+  const showDealSheet = ref(false);
+  const currentUnqualifiedRow = ref(null);
+  // 杩囩▼/鎴愬搧绫绘敮鎸� 杩斾慨/杩斿伐/鎶ュ簾/璁╂鏀捐
+  const dealSheetOptions = computed(() => {
+    return ["杩斾慨", "杩斿伐", "鎶ュ簾", "璁╂鏀捐"].map(name => ({
+      name,
+      value: name,
+    }));
+  });
+
+  // 鎵撳紑涓嶅悎鏍煎鐞嗗脊绐楋細鎸夋楠屽崟id鎷夊彇寰呭鐞嗕笉鍚堟牸璁板綍
+  const openUnqualified = async item => {
+    currentInspectRow.value = item;
+    showUnqualifiedPopup.value = true;
+    unqualifiedLoading.value = true;
+    unqualifiedList.value = [];
+    try {
+      const res = await qualityUnqualifiedListPage({
+        inspectType: currentInspectType,
+        inspectState: 0,
+        inspectId: item.id,
+        current: -1,
+        size: -1,
+      });
+      let records = res.data?.records || res.data || [];
+      // 鑻ュ悗绔湭鎸夋楠屽崟杩囨护锛屽墠绔厹搴曟寜妫�楠屽崟id杩囨护
+      const hasInspectRef = records.some(
+        r => r.inspectId != null || r.qualityInspectId != null
+      );
+      if (hasInspectRef) {
+        records = records.filter(
+          r => (r.inspectId ?? r.qualityInspectId) == item.id
+        );
+      }
+      // 鍙繚鐣欏緟澶勭悊璁板綍
+      unqualifiedList.value = records.filter(
+        r => Number(r.inspectState) !== 1
+      );
+    } catch (error) {
+      // request.ts 宸茬粺涓� toast 鍚庣 msg锛岃繖閲屽彧璁板綍
+      console.error("鑾峰彇涓嶅悎鏍艰褰曞け璐�:", error);
+      unqualifiedList.value = [];
+    } finally {
+      unqualifiedLoading.value = false;
+    }
+  };
+
+  // 鎵撳紑澶勭悊缁撴灉閫夋嫨
+  const openDealSheet = row => {
+    currentUnqualifiedRow.value = row;
+    showDealSheet.value = true;
+  };
+
+  // 閫夋嫨澶勭悊缁撴灉
+  const selectDeal = e => {
+    showDealSheet.value = false;
+    if (!currentUnqualifiedRow.value) return;
+    const result = e.value;
+    // 杩斾慨/杩斿伐浼氳嚜鍔ㄥ垱寤鸿繑宸ョ敓浜ц鍗曪紝鍏堜簩娆$‘璁�
+    if (result === "杩斾慨" || result === "杩斿伐") {
+      uni.showModal({
+        title: "鎻愮ず",
+        content: "灏嗚嚜鍔ㄥ垱寤鸿繑宸ョ敓浜ц鍗曪紙FG鍗曞彿锛夛紝鏄惁缁х画锛�",
+        success: res => {
+          if (res.confirm) {
+            doDeal(currentUnqualifiedRow.value, result);
+          }
+        },
+      });
+      return;
+    }
+    doDeal(currentUnqualifiedRow.value, result);
+  };
+
+  // 鎻愪氦涓嶅悎鏍煎鐞�
+  const doDeal = async (row, dealResult) => {
+    try {
+      await qualityUnqualifiedDeal({ id: row.id, dealResult });
+      showToast("澶勭悊鎴愬姛");
+      if (currentInspectRow.value) {
+        await openUnqualified(currentInspectRow.value);
+      }
+      if (unqualifiedList.value.length === 0) {
+        showUnqualifiedPopup.value = false;
+      }
+      getList();
+    } catch (error) {
+      // request.ts 宸茬粺涓� toast 鍚庣 msg锛岃繖閲屽彧璁板綍
+      console.error("涓嶅悎鏍煎鐞嗗け璐�:", error);
+    }
+  };
 
   // 杩斿洖涓婁竴椤�
   const goBack = () => {
@@ -371,7 +573,9 @@
     tableLoading.value = true;
     const params = { ...searchForm.value, ...page.value };
     params.entryDate = undefined;
-    qualityInspectListPage({ ...params, inspectType: 2 })
+    params.inspectState =
+      stateFilter.value === "" ? undefined : stateFilter.value;
+    qualityInspectListPage({ ...params, inspectType: currentInspectType })
       .then(res => {
         tableLoading.value = false;
         inspectionList.value = res.data.records || [];
@@ -430,25 +634,30 @@
   };
 
   // 鎻愪氦妫�楠�
-  const submitInspection = async item => {
+  const submitInspection = item => {
     if (!item) {
       showToast("鍙傛暟閿欒");
       return;
     }
-    try {
-      const res = await submitQualityInspect({ id: item.id });
-      if (res.code === 200) {
-        showToast("鎻愪氦鎴愬姛");
-        setTimeout(() => {
-          getList();
-        }, 1000);
-      } else {
-        showToast("鎻愪氦澶辫触锛�" + (res.msg || "鏈煡閿欒"));
-      }
-    } catch (error) {
-      console.error("鎻愪氦澶辫触:", error);
-      showToast("鎻愪氦澶辫触锛岃閲嶈瘯");
-    }
+    uni.showModal({
+      title: "鎻愮ず",
+      content: "鎻愪氦鍚庡皢鐢熸垚鍏ュ簱璁板綍涓斾笉鍙慨鏀癸紝鏄惁缁х画锛�",
+      success: async res => {
+        if (!res.confirm) return;
+        try {
+          await submitQualityInspect({ id: item.id });
+          showToast(
+            "鎻愪氦鎴愬姛锛屽悎鏍奸儴鍒嗗凡鐢熸垚鍏ュ簱璁板綍锛堝緟瀹℃牳锛夛紝涓嶅悎鏍奸儴鍒嗗凡鐢熸垚澶勭悊鍗�"
+          );
+          setTimeout(() => {
+            getList();
+          }, 1500);
+        } catch (error) {
+          // request.ts 宸茬粺涓� toast 鍚庣 msg锛岃繖閲屽彧璁板綍
+          console.error("鎻愪氦澶辫触:", error);
+        }
+      },
+    });
   };
 
   // 鍒嗛厤妫�楠屽憳
@@ -551,6 +760,78 @@
     padding: 5px;
   }
 
+  // 妫�楠岀姸鎬佺瓫閫�
+  .filter-row {
+    display: flex;
+    align-items: center;
+    margin-top: 10px;
+  }
+
+  .filter-item {
+    display: flex;
+    align-items: center;
+    padding: 4px 10px;
+    background: #f2f3f5;
+    border-radius: 12px;
+  }
+
+  .filter-label {
+    font-size: 12px;
+    color: #666;
+    margin-right: 4px;
+  }
+
+  .filter-value {
+    font-size: 12px;
+    color: #2979ff;
+    margin-right: 4px;
+  }
+
+  // 涓嶅悎鏍煎鐞嗗脊绐�
+  .unqualified-dialog {
+    background: #ffffff;
+    border-radius: 16px 16px 0 0;
+    padding: 20px;
+    max-height: 70vh;
+    overflow-y: auto;
+  }
+
+  .unqualified-empty {
+    padding: 30px 0;
+    text-align: center;
+    font-size: 13px;
+    color: #999;
+  }
+
+  .unqualified-item {
+    display: flex;
+    align-items: center;
+    padding: 12px 0;
+    border-bottom: 1px solid #f0f0f0;
+  }
+
+  .unqualified-info {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    margin-right: 10px;
+  }
+
+  .unqualified-label {
+    font-size: 12px;
+    color: #999;
+  }
+
+  .unqualified-value {
+    font-size: 13px;
+    color: #333;
+    margin-top: 2px;
+  }
+
+  .unqualified-btn {
+    width: 70px;
+  }
+
   // 缁熻鍗$墖
   .stats-cards {
     display: flex;
diff --git a/src/pages/qualityManagement/materialInspection/add.vue b/src/pages/qualityManagement/materialInspection/add.vue
index e04391f..3ed5666 100644
--- a/src/pages/qualityManagement/materialInspection/add.vue
+++ b/src/pages/qualityManagement/materialInspection/add.vue
@@ -16,9 +16,9 @@
         <up-input v-model="form.supplier"
                   placeholder="璇烽�夋嫨渚涘簲鍟�"
                   readonly
-                  :disabled="supplierQuantityDisabled" />
+                  :disabled="supplierQuantityDisabled || isReadonly" />
         <template #right>
-          <up-icon @click="showSupplierSheet = true"
+          <up-icon @click="!isReadonly && (showSupplierSheet = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -29,10 +29,10 @@
         <up-input v-model="form.productName"
                   placeholder="璇烽�夋嫨浜у搧"
                   readonly
-                  @click="showProductTree = true"
-                  :disabled="isEdit" />
+                  @click="!isReadonly && (showProductTree = true)"
+                  :disabled="isEdit || isReadonly" />
         <template #right>
-          <up-icon @click="showProductTree = true"
+          <up-icon @click="!isReadonly && (showProductTree = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -43,9 +43,17 @@
         <up-input v-model="form.model"
                   placeholder="璇烽�夋嫨瑙勬牸鍨嬪彿"
                   readonly
-                  :disabled="isEdit" />
+                  :disabled="isEdit || isReadonly" />
         <template #right>
-          <up-icon @click="showModelSheet = true"
+          <view v-if="!isEdit"
+                class="scan-entry"
+                @click.stop="scanBarcode">
+            <up-icon name="scan"
+                     size="18"
+                     color="#3c9cff" />
+            <text class="scan-entry-text">鎵爜</text>
+          </view>
+          <up-icon @click="!isReadonly && (showModelSheet = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -55,7 +63,8 @@
                     border-bottom>
         <up-input v-model="testStandardDisplay"
                   placeholder="璇烽�夋嫨鎸囨爣"
-                  readonly />
+                  readonly
+                  :disabled="isReadonly" />
         <template #right>
           <up-icon @click="openTestStandardSheet"
                    name="arrow-right" />
@@ -75,13 +84,14 @@
         <up-input v-model="form.quantity"
                   type="number"
                   placeholder="璇疯緭鍏ユ暟閲�"
-                  :disabled="supplierQuantityDisabled" />
+                  :disabled="supplierQuantityDisabled || isReadonly" />
       </up-form-item>
       <up-form-item label="妫�娴嬪崟浣�"
                     prop="checkCompany"
                     border-bottom>
         <up-input v-model="form.checkCompany"
                   placeholder="璇疯緭鍏ユ娴嬪崟浣�"
+                  :disabled="isReadonly"
                   clearable />
       </up-form-item>
       <up-form-item label="妫�娴嬬粨鏋�"
@@ -91,9 +101,10 @@
         <up-input v-model="form.checkResult"
                   placeholder="璇烽�夋嫨妫�娴嬬粨鏋�"
                   readonly
+                  :disabled="isReadonly"
                   @click="showResultSheet" />
         <template #right>
-          <up-icon @click="showResultSheet = true"
+          <up-icon @click="!isReadonly && (showResultSheet = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -103,9 +114,10 @@
         <up-input v-model="form.checkName"
                   placeholder="璇烽�夋嫨妫�楠屽憳"
                   readonly
+                  :disabled="isReadonly"
                   @click="showInspectorSheet" />
         <template #right>
-          <up-icon @click="showInspectorSheet = true"
+          <up-icon @click="!isReadonly && (showInspectorSheet = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -116,6 +128,7 @@
         <up-input v-model="form.checkTime"
                   placeholder="璇烽�夋嫨妫�娴嬫棩鏈�"
                   readonly
+                  :disabled="isReadonly"
                   @click="showDatePicker" />
         <!-- <template #right>
           <up-icon name="arrow-right"
@@ -163,6 +176,7 @@
                 <text class="step-label">妫�楠屽�硷細</text>
                 <up-input v-model="item.testValue"
                           placeholder="璇疯緭鍏ユ楠屽��"
+                          :disabled="isReadonly"
                           clearable
                           border-bottom
                           class="step-input" />
@@ -182,14 +196,16 @@
                  size="default"
                  @click="goBack"
                  class="bottom-btn">
-        鍙栨秷
+        {{ isReadonly ? '杩斿洖' : '鍙栨秷' }}
       </up-button>
-      <up-button type="primary"
+      <!-- 宸叉彁浜ゅ崟鎹彧璇伙紝闅愯棌淇濆瓨/鎻愪氦 -->
+      <up-button v-if="!isReadonly"
+                 type="primary"
                  size="default"
                  @click="submitForm"
                  :loading="loading"
                  class="bottom-btn">
-        {{ isEdit ? '淇濆瓨' : '鎻愪氦' }}
+        淇濆瓨
       </up-button>
     </view>
     <!-- 鏃ユ湡閫夋嫨鍣� -->
@@ -319,7 +335,11 @@
   import PageHeader from "@/components/PageHeader.vue";
   import dayjs from "dayjs";
   import { getOptions } from "@/api/procurementManagement/procurementLedger.js";
-  import { modelList, productTreeList } from "@/api/basicData/product.js";
+  import {
+    modelList,
+    productTreeList,
+    modelByBarcode,
+  } from "@/api/basicData/product.js";
   import {
     qualityInspectAdd,
     qualityInspectUpdate,
@@ -421,6 +441,7 @@
     return modelOptions.value.map(item => ({
       name: item.model,
       value: item.id,
+      subname: item.barcode ? `鏉$爜锛�${item.barcode}` : "",
     }));
   });
 
@@ -477,6 +498,9 @@
     return !!(v.productMainId != null || v.purchaseLedgerId != null);
   });
 
+  // 宸叉彁浜わ紙inspectState=1锛夊崟鎹暣浣撳彧璇�
+  const isReadonly = computed(() => Number(form.value?.inspectState) === 1);
+
   // 杩斿洖涓婁竴椤�
   const goBack = () => {
     uni.navigateBack();
@@ -484,6 +508,7 @@
 
   // 鏄剧ず鏃ユ湡閫夋嫨鍣�
   const showDatePicker = () => {
+    if (isReadonly.value) return;
     showDate.value = true;
   };
 
@@ -610,7 +635,7 @@
       });
   };
   const openTestStandardSheet = () => {
-    console.log("openTestStandardSheet");
+    if (isReadonly.value) return;
     showTestStandardSheet.value = true;
   };
 
@@ -640,19 +665,51 @@
     }
   };
 
-  // 鑾峰彇鍨嬪彿鍒楄〃
+  // 鑾峰彇鍨嬪彿鍒楄〃锛堣繑鍥� Promise锛屼究浜庢壂鐮佸悗椤哄簭鍥炲~锛�
   const getModels = value => {
     form.value.productModelId = "";
     form.value.unit = "";
     modelOptions.value = [];
     currentProductId.value = value;
     form.value.productName = findNodeById(productOptions.value, value);
-    modelList({ id: value }).then(res => {
-      modelOptions.value = res;
+    const task = modelList({ id: value }).then(res => {
+      modelOptions.value = res?.data || res || [];
     });
     if (currentProductId.value) {
       getList();
     }
+    return task;
+  };
+
+  // 鎵爜鎸夋潯鐮佺簿纭洖濉骇鍝�+瑙勬牸鍨嬪彿
+  const scanBarcode = () => {
+    if (isEdit.value) return;
+    uni.scanCode({
+      scanType: ["barCode", "qrCode"],
+      success: async res => {
+        const barcode = (res.result || "").trim();
+        if (!barcode) return;
+        uni.showLoading({ title: "鍖归厤涓�...", mask: true });
+        try {
+          const resp = await modelByBarcode({ barcode });
+          const info = resp?.data || resp;
+          if (info && info.productId) {
+            // 鍏堟寜 productId 鑱斿姩鍑轰骇鍝佸眰锛屼繚璇� UI 鐘舵�佷笌鎵嬪姩閫夋嫨涓�鑷�
+            await getModels(info.productId);
+            form.value.productId = info.productId;
+            form.value.productName = info.productName || form.value.productName;
+            form.value.productModelId = info.id;
+            form.value.model = info.model || "";
+            form.value.unit = info.unit || "";
+          }
+          uni.hideLoading();
+          uni.showToast({ title: "宸插尮閰嶈鏍�", icon: "success" });
+        } catch (e) {
+          // 鏈尮閰嶇瓑閿欒宸叉湁鍏ㄥ眬 toast锛岃繖閲屽彧鎭㈠ loading
+          uni.hideLoading();
+        }
+      },
+    });
   };
 
   // 鑾峰彇鎸囨爣鍒楄〃
@@ -687,6 +744,11 @@
   // 鎻愪氦琛ㄥ崟
   const submitForm = async () => {
     console.log("submitForm", form.value, tableData.value);
+    // 宸叉彁浜ゅ崟鎹姝繚瀛�/鎻愪氦
+    if (isReadonly.value) {
+      showToast("妫�楠屽崟宸叉彁浜わ紝涓嶅厑璁镐慨鏀�");
+      return;
+    }
     try {
       // await formRef.value.validate();
       if (!form.value.productModelId) {
@@ -726,21 +788,22 @@
       const data = { ...form.value, qualityInspectParams: tableData.value };
       data.quantity = Number(data.quantity);
       if (isEdit.value) {
-        const res = await qualityInspectUpdate(data);
+        await qualityInspectUpdate(data);
         showToast("淇濆瓨鎴愬姛");
         setTimeout(() => {
           uni.navigateBack();
         }, 1500);
       } else {
-        const res = await qualityInspectAdd(data);
-        showToast("鎻愪氦鎴愬姛");
+        // 鏂板浠呬繚瀛樿崏绋匡紝姝e紡鎻愪氦鍦ㄥ垪琛ㄩ〉銆屾彁浜ゃ�嶆寜閽紙浼氱敓鎴愬叆搴撹褰曪級
+        await qualityInspectAdd(data);
+        showToast("淇濆瓨鎴愬姛");
         setTimeout(() => {
           uni.navigateBack();
         }, 1500);
       }
     } catch (error) {
-      console.error("琛ㄥ崟楠岃瘉澶辫触:", error);
-      showToast("鎻愪氦澶辫触锛岃閲嶈瘯");
+      // request.ts 宸茬粺涓� toast 鍚庣 msg锛岃繖閲屽彧璁板綍
+      console.error("鎻愪氦澶辫触:", error);
     } finally {
       loading.value = false;
     }
@@ -912,6 +975,19 @@
 <style scoped lang="scss">
   @import "@/static/scss/form-common.scss";
 
+  // 瑙勬牸琛屾壂鐮佸叆鍙�
+  .scan-entry {
+    display: flex;
+    align-items: center;
+    margin-right: 8px;
+
+    .scan-entry-text {
+      font-size: 13px;
+      color: #3c9cff;
+      margin-left: 2px;
+    }
+  }
+
   .material-inspection-add {
     min-height: 100vh;
     background: #f8f9fa;
diff --git a/src/pages/qualityManagement/materialInspection/detail.vue b/src/pages/qualityManagement/materialInspection/detail.vue
index 12ff7a3..c215539 100644
--- a/src/pages/qualityManagement/materialInspection/detail.vue
+++ b/src/pages/qualityManagement/materialInspection/detail.vue
@@ -24,7 +24,7 @@
             <u-tag :type="getStateTagType(detailData.inspectState)"
                    size="small"
                    class="status-tag">
-              {{ detailData.inspectState ? '宸叉彁浜�' : '鏈彁浜�' }}
+              {{ detailData.inspectState ? '宸叉彁浜�' : '寰呮彁浜�' }}
             </u-tag>
           </view>
         </view>
@@ -66,6 +66,18 @@
             <text class="detail-label">妫�娴嬪崟浣�</text>
             <text class="detail-value">{{ detailData.checkCompany || '-' }}</text>
           </view>
+          <view class="detail-row">
+            <text class="detail-label">鍚堟牸鏁伴噺</text>
+            <text class="detail-value">{{ detailData.qualifiedQuantity != null ? detailData.qualifiedQuantity : '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">涓嶅悎鏍兼暟閲�</text>
+            <text class="detail-value">{{ detailData.unqualifiedQuantity != null ? detailData.unqualifiedQuantity : '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">妫�楠岀粨璁�</text>
+            <text class="detail-value">{{ detailData.checkResult || '-' }}</text>
+          </view>
           <!-- <view class="detail-row">
             <text class="detail-label">妫�楠屾爣鍑�</text>
             <text class="detail-value">{{ detailData.testStandardName || '-' }}</text>
diff --git a/src/pages/qualityManagement/materialInspection/index.vue b/src/pages/qualityManagement/materialInspection/index.vue
index eb4a140..7b44b22 100644
--- a/src/pages/qualityManagement/materialInspection/index.vue
+++ b/src/pages/qualityManagement/materialInspection/index.vue
@@ -26,6 +26,17 @@
                    color="#999"></up-icon>
         </view>
       </view>
+      <!-- 妫�楠岀姸鎬佺瓫閫� -->
+      <view class="filter-row">
+        <view class="filter-item"
+              @click="showStateSheet = true">
+          <text class="filter-label">妫�楠岀姸鎬�</text>
+          <text class="filter-value">{{ stateFilterLabel }}</text>
+          <up-icon name="arrow-down"
+                   size="14"
+                   color="#999"></up-icon>
+        </view>
+      </view>
       <!-- 鏃ユ湡閫夋嫨 -->
       <!-- <view class="date-range"
             v-if="searchForm.entryDate">
@@ -85,7 +96,7 @@
               <u-tag :type="getStateTagType(item.inspectState)"
                      size="mini"
                      class="status-tag">
-                {{ item.inspectState ? '宸叉彁浜�' : '鏈彁浜�' }}
+                {{ item.inspectState ? '宸叉彁浜�' : '寰呮彁浜�' }}
               </u-tag>
             </view>
           </view>
@@ -114,6 +125,18 @@
             <view class="detail-row">
               <text class="detail-label">妫�娴嬪崟浣�</text>
               <text class="detail-value">{{ item.checkCompany || '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鍚堟牸鏁伴噺</text>
+              <text class="detail-value">{{ item.qualifiedQuantity != null ? item.qualifiedQuantity : '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">涓嶅悎鏍兼暟閲�</text>
+              <text class="detail-value">{{ item.unqualifiedQuantity != null ? item.unqualifiedQuantity : '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">妫�楠岀粨璁�</text>
+              <text class="detail-value">{{ item.checkResult || '-' }}</text>
             </view>
           </view>
           <!-- 鎿嶄綔鎸夐挳 -->
@@ -152,6 +175,15 @@
                       :disabled="item.inspectState || item.checkName !== ''"
                       @click.stop="assignInspector(item)">
               鍒嗛厤妫�楠屽憳
+            </u-button>
+          </view>
+          <view class="action-buttons">
+            <u-button type="error"
+                      size="small"
+                      class="action-btn"
+                      :disabled="!(Number(item.unqualifiedQuantity) > 0)"
+                      @click.stop="openUnqualified(item)">
+              涓嶅悎鏍煎鐞�
             </u-button>
           </view>
         </view>
@@ -223,6 +255,56 @@
                      :actions="userSheetOptions"
                      @select="selectInspector"
                      title="閫夋嫨妫�楠屽憳" />
+    <!-- 妫�楠岀姸鎬佺瓫閫� -->
+    <up-action-sheet :show="showStateSheet"
+                     :actions="stateSheetOptions"
+                     @select="selectState"
+                     @close="showStateSheet = false"
+                     title="閫夋嫨妫�楠岀姸鎬�" />
+    <!-- 涓嶅悎鏍煎鐞嗗脊绐� -->
+    <up-popup v-model:show="showUnqualifiedPopup"
+              mode="bottom"
+              round>
+      <view class="unqualified-dialog">
+        <view class="dialog-header">
+          <text class="dialog-title">涓嶅悎鏍煎鐞�</text>
+          <up-icon name="close"
+                   size="20"
+                   color="#999"
+                   @click="showUnqualifiedPopup = false"></up-icon>
+        </view>
+        <view class="dialog-content">
+          <view v-if="unqualifiedLoading"
+                class="unqualified-empty">鍔犺浇涓�...</view>
+          <view v-else-if="unqualifiedList.length === 0"
+                class="unqualified-empty">鏆傛棤寰呭鐞嗕笉鍚堟牸璁板綍</view>
+          <view v-for="(row, idx) in unqualifiedList"
+                :key="idx"
+                class="unqualified-item">
+            <view class="unqualified-info">
+              <text class="unqualified-label">涓嶅悎鏍肩幇璞�</text>
+              <text class="unqualified-value">{{ row.defectivePhenomena || '-' }}</text>
+            </view>
+            <view class="unqualified-info">
+              <text class="unqualified-label">鏁伴噺</text>
+              <text class="unqualified-value">{{ row.quantity != null ? row.quantity : '-' }}</text>
+            </view>
+            <u-button type="primary"
+                      size="small"
+                      class="unqualified-btn"
+                      @click="openDealSheet(row)">
+              澶勭悊
+            </u-button>
+          </view>
+        </view>
+      </view>
+    </up-popup>
+    <!-- 澶勭悊缁撴灉閫夋嫨 -->
+    <up-action-sheet :show="showDealSheet"
+                     :actions="dealSheetOptions"
+                     @select="selectDeal"
+                     @close="showDealSheet = false"
+                     title="閫夋嫨澶勭悊缁撴灉" />
   </view>
 </template>
 
@@ -235,8 +317,13 @@
     submitQualityInspect,
     qualityInspectUpdate,
     qualityInspectListPage,
+    qualityUnqualifiedListPage,
+    qualityUnqualifiedDeal,
   } from "@/api/qualityManagement/materialInspection.js";
   import { userListNoPage } from "@/api/system/user.js";
+
+  // 鍘熸枡妫�楠� inspectType=0
+  const currentInspectType = 0;
 
   // 鏄剧ず鎻愮ず淇℃伅
   const showToast = message => {
@@ -294,6 +381,104 @@
       value: item.nickName,
     }));
   });
+
+  // 妫�楠岀姸鎬佺瓫閫�
+  const showStateSheet = ref(false);
+  const stateFilter = ref("");
+  const stateSheetOptions = [
+    { name: "鍏ㄩ儴", value: "" },
+    { name: "寰呮彁浜�", value: 0 },
+    { name: "宸叉彁浜�", value: 1 },
+  ];
+  const stateFilterLabel = computed(() => {
+    const hit = stateSheetOptions.find(o => o.value === stateFilter.value);
+    return hit ? hit.name : "鍏ㄩ儴";
+  });
+  const selectState = e => {
+    stateFilter.value = e.value;
+    showStateSheet.value = false;
+    getList();
+  };
+
+  // 涓嶅悎鏍煎鐞嗙浉鍏�
+  const showUnqualifiedPopup = ref(false);
+  const unqualifiedLoading = ref(false);
+  const unqualifiedList = ref([]);
+  const currentInspectRow = ref(null);
+  const showDealSheet = ref(false);
+  const currentUnqualifiedRow = ref(null);
+  // 鍘熸枡绫讳粎鏀寔 鎶ュ簾/璁╂鏀捐
+  const dealSheetOptions = computed(() => {
+    return ["鎶ュ簾", "璁╂鏀捐"].map(name => ({ name, value: name }));
+  });
+
+  // 鎵撳紑涓嶅悎鏍煎鐞嗗脊绐楋細鎸夋楠屽崟id鎷夊彇寰呭鐞嗕笉鍚堟牸璁板綍
+  const openUnqualified = async item => {
+    currentInspectRow.value = item;
+    showUnqualifiedPopup.value = true;
+    unqualifiedLoading.value = true;
+    unqualifiedList.value = [];
+    try {
+      const res = await qualityUnqualifiedListPage({
+        inspectType: currentInspectType,
+        inspectState: 0,
+        inspectId: item.id,
+        current: -1,
+        size: -1,
+      });
+      let records = res.data?.records || res.data || [];
+      // 鑻ュ悗绔湭鎸夋楠屽崟杩囨护锛屽墠绔厹搴曟寜妫�楠屽崟id杩囨护
+      const hasInspectRef = records.some(
+        r => r.inspectId != null || r.qualityInspectId != null
+      );
+      if (hasInspectRef) {
+        records = records.filter(
+          r => (r.inspectId ?? r.qualityInspectId) == item.id
+        );
+      }
+      // 鍙繚鐣欏緟澶勭悊璁板綍
+      unqualifiedList.value = records.filter(
+        r => Number(r.inspectState) !== 1
+      );
+    } catch (error) {
+      // request.ts 宸茬粺涓� toast 鍚庣 msg锛岃繖閲屽彧璁板綍
+      console.error("鑾峰彇涓嶅悎鏍艰褰曞け璐�:", error);
+      unqualifiedList.value = [];
+    } finally {
+      unqualifiedLoading.value = false;
+    }
+  };
+
+  // 鎵撳紑澶勭悊缁撴灉閫夋嫨
+  const openDealSheet = row => {
+    currentUnqualifiedRow.value = row;
+    showDealSheet.value = true;
+  };
+
+  // 閫夋嫨澶勭悊缁撴灉
+  const selectDeal = e => {
+    showDealSheet.value = false;
+    if (!currentUnqualifiedRow.value) return;
+    doDeal(currentUnqualifiedRow.value, e.value);
+  };
+
+  // 鎻愪氦涓嶅悎鏍煎鐞�
+  const doDeal = async (row, dealResult) => {
+    try {
+      await qualityUnqualifiedDeal({ id: row.id, dealResult });
+      showToast("澶勭悊鎴愬姛");
+      if (currentInspectRow.value) {
+        await openUnqualified(currentInspectRow.value);
+      }
+      if (unqualifiedList.value.length === 0) {
+        showUnqualifiedPopup.value = false;
+      }
+      getList();
+    } catch (error) {
+      // request.ts 宸茬粺涓� toast 鍚庣 msg锛岃繖閲屽彧璁板綍
+      console.error("涓嶅悎鏍煎鐞嗗け璐�:", error);
+    }
+  };
 
   // 杩斿洖涓婁竴椤�
   const goBack = () => {
@@ -371,7 +556,9 @@
     tableLoading.value = true;
     const params = { ...searchForm.value, ...page.value };
     params.entryDate = undefined;
-    qualityInspectListPage({ ...params, inspectType: 0 })
+    params.inspectState =
+      stateFilter.value === "" ? undefined : stateFilter.value;
+    qualityInspectListPage({ ...params, inspectType: currentInspectType })
       .then(res => {
         tableLoading.value = false;
         inspectionList.value = res.data.records || [];
@@ -429,25 +616,30 @@
   };
 
   // 鎻愪氦妫�楠�
-  const submitInspection = async item => {
+  const submitInspection = item => {
     if (!item) {
       showToast("鍙傛暟閿欒");
       return;
     }
-    try {
-      const res = await submitQualityInspect({ id: item.id });
-      if (res.code === 200) {
-        showToast("鎻愪氦鎴愬姛");
-        setTimeout(() => {
-          getList();
-        }, 1000);
-      } else {
-        showToast("鎻愪氦澶辫触锛�" + (res.msg || "鏈煡閿欒"));
-      }
-    } catch (error) {
-      console.error("鎻愪氦澶辫触:", error);
-      showToast("鎻愪氦澶辫触锛岃閲嶈瘯");
-    }
+    uni.showModal({
+      title: "鎻愮ず",
+      content: "鎻愪氦鍚庡皢鐢熸垚鍏ュ簱璁板綍涓斾笉鍙慨鏀癸紝鏄惁缁х画锛�",
+      success: async res => {
+        if (!res.confirm) return;
+        try {
+          await submitQualityInspect({ id: item.id });
+          showToast(
+            "鎻愪氦鎴愬姛锛屽悎鏍奸儴鍒嗗凡鐢熸垚鍏ュ簱璁板綍锛堝緟瀹℃牳锛夛紝涓嶅悎鏍奸儴鍒嗗凡鐢熸垚澶勭悊鍗�"
+          );
+          setTimeout(() => {
+            getList();
+          }, 1500);
+        } catch (error) {
+          // request.ts 宸茬粺涓� toast 鍚庣 msg锛岃繖閲屽彧璁板綍
+          console.error("鎻愪氦澶辫触:", error);
+        }
+      },
+    });
   };
 
   // 鍒嗛厤妫�楠屽憳
@@ -550,6 +742,78 @@
     padding: 5px;
   }
 
+  // 妫�楠岀姸鎬佺瓫閫�
+  .filter-row {
+    display: flex;
+    align-items: center;
+    margin-top: 10px;
+  }
+
+  .filter-item {
+    display: flex;
+    align-items: center;
+    padding: 4px 10px;
+    background: #f2f3f5;
+    border-radius: 12px;
+  }
+
+  .filter-label {
+    font-size: 12px;
+    color: #666;
+    margin-right: 4px;
+  }
+
+  .filter-value {
+    font-size: 12px;
+    color: #2979ff;
+    margin-right: 4px;
+  }
+
+  // 涓嶅悎鏍煎鐞嗗脊绐�
+  .unqualified-dialog {
+    background: #ffffff;
+    border-radius: 16px 16px 0 0;
+    padding: 20px;
+    max-height: 70vh;
+    overflow-y: auto;
+  }
+
+  .unqualified-empty {
+    padding: 30px 0;
+    text-align: center;
+    font-size: 13px;
+    color: #999;
+  }
+
+  .unqualified-item {
+    display: flex;
+    align-items: center;
+    padding: 12px 0;
+    border-bottom: 1px solid #f0f0f0;
+  }
+
+  .unqualified-info {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    margin-right: 10px;
+  }
+
+  .unqualified-label {
+    font-size: 12px;
+    color: #999;
+  }
+
+  .unqualified-value {
+    font-size: 13px;
+    color: #333;
+    margin-top: 2px;
+  }
+
+  .unqualified-btn {
+    width: 70px;
+  }
+
   // 缁熻鍗$墖
   .stats-cards {
     display: flex;
diff --git a/src/pages/qualityManagement/processInspection/add.vue b/src/pages/qualityManagement/processInspection/add.vue
index 2f6a6d0..2fd5984 100644
--- a/src/pages/qualityManagement/processInspection/add.vue
+++ b/src/pages/qualityManagement/processInspection/add.vue
@@ -16,9 +16,9 @@
         <up-input v-model="form.process"
                   placeholder="璇烽�夋嫨宸ュ簭"
                   readonly
-                  :disabled="processQuantityDisabled" />
+                  :disabled="processQuantityDisabled || isReadonly" />
         <template #right>
-          <up-icon @click="showprocessSheet = true"
+          <up-icon @click="!isReadonly && (showprocessSheet = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -29,10 +29,10 @@
         <up-input v-model="form.productName"
                   placeholder="璇烽�夋嫨浜у搧"
                   readonly
-                  @click="showProductTree = true"
-                  :disabled="isEdit" />
+                  @click="!isReadonly && (showProductTree = true)"
+                  :disabled="isEdit || isReadonly" />
         <template #right>
-          <up-icon @click="showProductTree = true"
+          <up-icon @click="!isReadonly && (showProductTree = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -43,9 +43,9 @@
         <up-input v-model="form.model"
                   placeholder="璇烽�夋嫨瑙勬牸鍨嬪彿"
                   readonly
-                  :disabled="isEdit" />
+                  :disabled="isEdit || isReadonly" />
         <template #right>
-          <up-icon @click="showModelSheet = true"
+          <up-icon @click="!isReadonly && (showModelSheet = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -55,7 +55,8 @@
                     border-bottom>
         <up-input v-model="testStandardDisplay"
                   placeholder="璇烽�夋嫨鎸囨爣"
-                  readonly />
+                  readonly
+                  :disabled="isReadonly" />
         <template #right>
           <up-icon @click="openTestStandardSheet"
                    name="arrow-right" />
@@ -75,13 +76,14 @@
         <up-input v-model="form.quantity"
                   type="number"
                   placeholder="璇疯緭鍏ユ暟閲�"
-                  :disabled="processQuantityDisabled" />
+                  :disabled="processQuantityDisabled || isReadonly" />
       </up-form-item>
       <up-form-item label="妫�娴嬪崟浣�"
                     prop="checkCompany"
                     border-bottom>
         <up-input v-model="form.checkCompany"
                   placeholder="璇疯緭鍏ユ娴嬪崟浣�"
+                  :disabled="isReadonly"
                   clearable />
       </up-form-item>
       <up-form-item label="妫�娴嬬粨鏋�"
@@ -91,9 +93,10 @@
         <up-input v-model="form.checkResult"
                   placeholder="璇烽�夋嫨妫�娴嬬粨鏋�"
                   readonly
+                  :disabled="isReadonly"
                   @click="showResultSheet" />
         <template #right>
-          <up-icon @click="showResultSheet = true"
+          <up-icon @click="!isReadonly && (showResultSheet = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -103,9 +106,10 @@
         <up-input v-model="form.checkName"
                   placeholder="璇烽�夋嫨妫�楠屽憳"
                   readonly
+                  :disabled="isReadonly"
                   @click="showInspectorSheet" />
         <template #right>
-          <up-icon @click="showInspectorSheet = true"
+          <up-icon @click="!isReadonly && (showInspectorSheet = true)"
                    name="arrow-right" />
         </template>
       </up-form-item>
@@ -115,7 +119,8 @@
                     border-bottom>
         <up-input v-model="form.checkTime"
                   placeholder="璇烽�夋嫨妫�娴嬫棩鏈�"
-                  readonly />
+                  readonly
+                  :disabled="isReadonly" />
         <!-- <template #right>
           <up-icon name="calendar"
                    @click="showDatePicker"></up-icon>
@@ -155,6 +160,7 @@
                 <text class="step-label">妫�楠屽�硷細</text>
                 <up-input v-model="item.testValue"
                           placeholder="璇疯緭鍏ユ楠屽��"
+                          :disabled="isReadonly"
                           clearable
                           border-bottom
                           class="step-input" />
@@ -174,14 +180,16 @@
                  size="default"
                  @click="goBack"
                  class="bottom-btn">
-        鍙栨秷
+        {{ isReadonly ? '杩斿洖' : '鍙栨秷' }}
       </up-button>
-      <up-button type="primary"
+      <!-- 宸叉彁浜ゅ崟鎹彧璇伙紝闅愯棌淇濆瓨/鎻愪氦 -->
+      <up-button v-if="!isReadonly"
+                 type="primary"
                  size="default"
                  @click="submitForm"
                  :loading="loading"
                  class="bottom-btn">
-        {{ isEdit ? '淇濆瓨' : '鎻愪氦' }}
+        淇濆瓨
       </up-button>
     </view>
     <!-- 鏃ユ湡閫夋嫨鍣� -->
@@ -469,6 +477,9 @@
     return !!(v.productMainId != null || v.purchaseLedgerId != null);
   });
 
+  // 宸叉彁浜わ紙inspectState=1锛夊崟鎹暣浣撳彧璇�
+  const isReadonly = computed(() => Number(form.value?.inspectState) === 1);
+
   // 杩斿洖涓婁竴椤�
   const goBack = () => {
     uni.navigateBack();
@@ -476,11 +487,13 @@
 
   // 鏄剧ず鏃ユ湡閫夋嫨鍣�
   const showDatePicker = () => {
+    if (isReadonly.value) return;
     showDate.value = true;
   };
 
   // 纭鏃ユ湡閫夋嫨
   const confirmDate = e => {
+    if (isReadonly.value) return;
     form.value.checkTime = dayjs(e.value).format("YYYY-MM-DD");
   };
 
@@ -601,7 +614,7 @@
       });
   };
   const openTestStandardSheet = () => {
-    console.log("openTestStandardSheet");
+    if (isReadonly.value) return;
     showTestStandardSheet.value = true;
   };
 
@@ -678,6 +691,11 @@
   // 鎻愪氦琛ㄥ崟
   const submitForm = async () => {
     console.log("submitForm", form.value, tableData.value);
+    // 宸叉彁浜ゅ崟鎹姝繚瀛�/鎻愪氦
+    if (isReadonly.value) {
+      showToast("妫�楠屽崟宸叉彁浜わ紝涓嶅厑璁镐慨鏀�");
+      return;
+    }
     try {
       // await formRef.value.validate();
       if (!form.value.productModelId) {
@@ -717,21 +735,22 @@
       const data = { ...form.value, qualityInspectParams: tableData.value };
       data.quantity = Number(data.quantity);
       if (isEdit.value) {
-        const res = await qualityInspectUpdate(data);
+        await qualityInspectUpdate(data);
         showToast("淇濆瓨鎴愬姛");
         setTimeout(() => {
           uni.navigateBack();
         }, 1500);
       } else {
-        const res = await qualityInspectAdd(data);
-        showToast("鎻愪氦鎴愬姛");
+        // 鏂板浠呬繚瀛樿崏绋匡紝姝e紡鎻愪氦鍦ㄥ垪琛ㄩ〉銆屾彁浜ゃ�嶆寜閽�
+        await qualityInspectAdd(data);
+        showToast("淇濆瓨鎴愬姛");
         setTimeout(() => {
           uni.navigateBack();
         }, 1500);
       }
     } catch (error) {
-      console.error("琛ㄥ崟楠岃瘉澶辫触:", error);
-      showToast("鎻愪氦澶辫触锛岃閲嶈瘯");
+      // request.ts 宸茬粺涓� toast 鍚庣 msg锛岃繖閲屽彧璁板綍
+      console.error("鎻愪氦澶辫触:", error);
     } finally {
       loading.value = false;
     }
diff --git a/src/pages/qualityManagement/processInspection/detail.vue b/src/pages/qualityManagement/processInspection/detail.vue
index 798aeee..06ebd41 100644
--- a/src/pages/qualityManagement/processInspection/detail.vue
+++ b/src/pages/qualityManagement/processInspection/detail.vue
@@ -24,7 +24,7 @@
             <u-tag :type="getStateTagType(detailData.inspectState)"
                    size="small"
                    class="status-tag">
-              {{ detailData.inspectState ? '宸叉彁浜�' : '鏈彁浜�' }}
+              {{ detailData.inspectState ? '宸叉彁浜�' : '寰呮彁浜�' }}
             </u-tag>
           </view>
         </view>
@@ -66,6 +66,18 @@
             <text class="detail-label">妫�娴嬪崟浣�</text>
             <text class="detail-value">{{ detailData.checkCompany || '-' }}</text>
           </view>
+          <view class="detail-row">
+            <text class="detail-label">鍚堟牸鏁伴噺</text>
+            <text class="detail-value">{{ detailData.qualifiedQuantity != null ? detailData.qualifiedQuantity : '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">涓嶅悎鏍兼暟閲�</text>
+            <text class="detail-value">{{ detailData.unqualifiedQuantity != null ? detailData.unqualifiedQuantity : '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">妫�楠岀粨璁�</text>
+            <text class="detail-value">{{ detailData.checkResult || '-' }}</text>
+          </view>
           <!-- <view class="detail-row">
             <text class="detail-label">妫�楠屾爣鍑�</text>
             <text class="detail-value">{{ detailData.testStandardName || '-' }}</text>
diff --git a/src/pages/qualityManagement/processInspection/index.vue b/src/pages/qualityManagement/processInspection/index.vue
index 40358ef..8a58c07 100644
--- a/src/pages/qualityManagement/processInspection/index.vue
+++ b/src/pages/qualityManagement/processInspection/index.vue
@@ -26,6 +26,17 @@
                    color="#999"></up-icon>
         </view>
       </view>
+      <!-- 妫�楠岀姸鎬佺瓫閫� -->
+      <view class="filter-row">
+        <view class="filter-item"
+              @click="showStateSheet = true">
+          <text class="filter-label">妫�楠岀姸鎬�</text>
+          <text class="filter-value">{{ stateFilterLabel }}</text>
+          <up-icon name="arrow-down"
+                   size="14"
+                   color="#999"></up-icon>
+        </view>
+      </view>
       <!-- 鏃ユ湡閫夋嫨 -->
       <!-- <view class="date-range"
             v-if="searchForm.entryDate">
@@ -85,7 +96,7 @@
               <u-tag :type="getStateTagType(item.inspectState)"
                      size="mini"
                      class="status-tag">
-                {{ item.inspectState ? '宸叉彁浜�' : '鏈彁浜�' }}
+                {{ item.inspectState ? '宸叉彁浜�' : '寰呮彁浜�' }}
               </u-tag>
             </view>
           </view>
@@ -114,6 +125,18 @@
             <view class="detail-row">
               <text class="detail-label">妫�娴嬪崟浣�</text>
               <text class="detail-value">{{ item.checkCompany || '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鍚堟牸鏁伴噺</text>
+              <text class="detail-value">{{ item.qualifiedQuantity != null ? item.qualifiedQuantity : '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">涓嶅悎鏍兼暟閲�</text>
+              <text class="detail-value">{{ item.unqualifiedQuantity != null ? item.unqualifiedQuantity : '-' }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">妫�楠岀粨璁�</text>
+              <text class="detail-value">{{ item.checkResult || '-' }}</text>
             </view>
           </view>
           <!-- 鎿嶄綔鎸夐挳 -->
@@ -152,6 +175,15 @@
                       :disabled="item.inspectState || item.checkName !== ''"
                       @click.stop="assignInspector(item)">
               鍒嗛厤妫�楠屽憳
+            </u-button>
+          </view>
+          <view class="action-buttons">
+            <u-button type="error"
+                      size="small"
+                      class="action-btn"
+                      :disabled="!(Number(item.unqualifiedQuantity) > 0)"
+                      @click.stop="openUnqualified(item)">
+              涓嶅悎鏍煎鐞�
             </u-button>
           </view>
         </view>
@@ -223,6 +255,56 @@
                      :actions="userSheetOptions"
                      @select="selectInspector"
                      title="閫夋嫨妫�楠屽憳" />
+    <!-- 妫�楠岀姸鎬佺瓫閫� -->
+    <up-action-sheet :show="showStateSheet"
+                     :actions="stateSheetOptions"
+                     @select="selectState"
+                     @close="showStateSheet = false"
+                     title="閫夋嫨妫�楠岀姸鎬�" />
+    <!-- 涓嶅悎鏍煎鐞嗗脊绐� -->
+    <up-popup v-model:show="showUnqualifiedPopup"
+              mode="bottom"
+              round>
+      <view class="unqualified-dialog">
+        <view class="dialog-header">
+          <text class="dialog-title">涓嶅悎鏍煎鐞�</text>
+          <up-icon name="close"
+                   size="20"
+                   color="#999"
+                   @click="showUnqualifiedPopup = false"></up-icon>
+        </view>
+        <view class="dialog-content">
+          <view v-if="unqualifiedLoading"
+                class="unqualified-empty">鍔犺浇涓�...</view>
+          <view v-else-if="unqualifiedList.length === 0"
+                class="unqualified-empty">鏆傛棤寰呭鐞嗕笉鍚堟牸璁板綍</view>
+          <view v-for="(row, idx) in unqualifiedList"
+                :key="idx"
+                class="unqualified-item">
+            <view class="unqualified-info">
+              <text class="unqualified-label">涓嶅悎鏍肩幇璞�</text>
+              <text class="unqualified-value">{{ row.defectivePhenomena || '-' }}</text>
+            </view>
+            <view class="unqualified-info">
+              <text class="unqualified-label">鏁伴噺</text>
+              <text class="unqualified-value">{{ row.quantity != null ? row.quantity : '-' }}</text>
+            </view>
+            <u-button type="primary"
+                      size="small"
+                      class="unqualified-btn"
+                      @click="openDealSheet(row)">
+              澶勭悊
+            </u-button>
+          </view>
+        </view>
+      </view>
+    </up-popup>
+    <!-- 澶勭悊缁撴灉閫夋嫨 -->
+    <up-action-sheet :show="showDealSheet"
+                     :actions="dealSheetOptions"
+                     @select="selectDeal"
+                     @close="showDealSheet = false"
+                     title="閫夋嫨澶勭悊缁撴灉" />
   </view>
 </template>
 
@@ -235,8 +317,13 @@
     submitQualityInspect,
     qualityInspectUpdate,
     qualityInspectListPage,
+    qualityUnqualifiedListPage,
+    qualityUnqualifiedDeal,
   } from "@/api/qualityManagement/materialInspection.js";
   import { userListNoPage } from "@/api/system/user.js";
+
+  // 杩囩▼妫�楠� inspectType=1
+  const currentInspectType = 1;
 
   // 鏄剧ず鎻愮ず淇℃伅
   const showToast = message => {
@@ -294,6 +381,121 @@
       value: item.nickName,
     }));
   });
+
+  // 妫�楠岀姸鎬佺瓫閫�
+  const showStateSheet = ref(false);
+  const stateFilter = ref("");
+  const stateSheetOptions = [
+    { name: "鍏ㄩ儴", value: "" },
+    { name: "寰呮彁浜�", value: 0 },
+    { name: "宸叉彁浜�", value: 1 },
+  ];
+  const stateFilterLabel = computed(() => {
+    const hit = stateSheetOptions.find(o => o.value === stateFilter.value);
+    return hit ? hit.name : "鍏ㄩ儴";
+  });
+  const selectState = e => {
+    stateFilter.value = e.value;
+    showStateSheet.value = false;
+    getList();
+  };
+
+  // 涓嶅悎鏍煎鐞嗙浉鍏�
+  const showUnqualifiedPopup = ref(false);
+  const unqualifiedLoading = ref(false);
+  const unqualifiedList = ref([]);
+  const currentInspectRow = ref(null);
+  const showDealSheet = ref(false);
+  const currentUnqualifiedRow = ref(null);
+  // 杩囩▼/鎴愬搧绫绘敮鎸� 杩斾慨/杩斿伐/鎶ュ簾/璁╂鏀捐
+  const dealSheetOptions = computed(() => {
+    return ["杩斾慨", "杩斿伐", "鎶ュ簾", "璁╂鏀捐"].map(name => ({
+      name,
+      value: name,
+    }));
+  });
+
+  // 鎵撳紑涓嶅悎鏍煎鐞嗗脊绐楋細鎸夋楠屽崟id鎷夊彇寰呭鐞嗕笉鍚堟牸璁板綍
+  const openUnqualified = async item => {
+    currentInspectRow.value = item;
+    showUnqualifiedPopup.value = true;
+    unqualifiedLoading.value = true;
+    unqualifiedList.value = [];
+    try {
+      const res = await qualityUnqualifiedListPage({
+        inspectType: currentInspectType,
+        inspectState: 0,
+        inspectId: item.id,
+        current: -1,
+        size: -1,
+      });
+      let records = res.data?.records || res.data || [];
+      // 鑻ュ悗绔湭鎸夋楠屽崟杩囨护锛屽墠绔厹搴曟寜妫�楠屽崟id杩囨护
+      const hasInspectRef = records.some(
+        r => r.inspectId != null || r.qualityInspectId != null
+      );
+      if (hasInspectRef) {
+        records = records.filter(
+          r => (r.inspectId ?? r.qualityInspectId) == item.id
+        );
+      }
+      // 鍙繚鐣欏緟澶勭悊璁板綍
+      unqualifiedList.value = records.filter(
+        r => Number(r.inspectState) !== 1
+      );
+    } catch (error) {
+      // request.ts 宸茬粺涓� toast 鍚庣 msg锛岃繖閲屽彧璁板綍
+      console.error("鑾峰彇涓嶅悎鏍艰褰曞け璐�:", error);
+      unqualifiedList.value = [];
+    } finally {
+      unqualifiedLoading.value = false;
+    }
+  };
+
+  // 鎵撳紑澶勭悊缁撴灉閫夋嫨
+  const openDealSheet = row => {
+    currentUnqualifiedRow.value = row;
+    showDealSheet.value = true;
+  };
+
+  // 閫夋嫨澶勭悊缁撴灉
+  const selectDeal = e => {
+    showDealSheet.value = false;
+    if (!currentUnqualifiedRow.value) return;
+    const result = e.value;
+    // 杩斾慨/杩斿伐浼氳嚜鍔ㄥ垱寤鸿繑宸ョ敓浜ц鍗曪紝鍏堜簩娆$‘璁�
+    if (result === "杩斾慨" || result === "杩斿伐") {
+      uni.showModal({
+        title: "鎻愮ず",
+        content: "灏嗚嚜鍔ㄥ垱寤鸿繑宸ョ敓浜ц鍗曪紙FG鍗曞彿锛夛紝鏄惁缁х画锛�",
+        success: res => {
+          if (res.confirm) {
+            doDeal(currentUnqualifiedRow.value, result);
+          }
+        },
+      });
+      return;
+    }
+    doDeal(currentUnqualifiedRow.value, result);
+  };
+
+  // 鎻愪氦涓嶅悎鏍煎鐞�
+  const doDeal = async (row, dealResult) => {
+    try {
+      await qualityUnqualifiedDeal({ id: row.id, dealResult });
+      showToast("澶勭悊鎴愬姛");
+      if (currentInspectRow.value) {
+        await openUnqualified(currentInspectRow.value);
+      }
+      if (unqualifiedList.value.length === 0) {
+        showUnqualifiedPopup.value = false;
+      }
+      getList();
+    } catch (error) {
+      // request.ts 宸茬粺涓� toast 鍚庣 msg锛岃繖閲屽彧璁板綍
+      console.error("涓嶅悎鏍煎鐞嗗け璐�:", error);
+    }
+  };
 
   // 杩斿洖涓婁竴椤�
   const goBack = () => {
@@ -371,7 +573,9 @@
     tableLoading.value = true;
     const params = { ...searchForm.value, ...page.value };
     params.entryDate = undefined;
-    qualityInspectListPage({ ...params, inspectType: 1 })
+    params.inspectState =
+      stateFilter.value === "" ? undefined : stateFilter.value;
+    qualityInspectListPage({ ...params, inspectType: currentInspectType })
       .then(res => {
         tableLoading.value = false;
         inspectionList.value = res.data.records || [];
@@ -429,25 +633,31 @@
   };
 
   // 鎻愪氦妫�楠�
-  const submitInspection = async item => {
+  const submitInspection = item => {
     if (!item) {
       showToast("鍙傛暟閿欒");
       return;
     }
-    try {
-      const res = await submitQualityInspect({ id: item.id });
-      if (res.code === 200) {
-        showToast("鎻愪氦鎴愬姛");
-        setTimeout(() => {
-          getList();
-        }, 1000);
-      } else {
-        showToast("鎻愪氦澶辫触锛�" + (res.msg || "鏈煡閿欒"));
-      }
-    } catch (error) {
-      console.error("鎻愪氦澶辫触:", error);
-      showToast("鎻愪氦澶辫触锛岃閲嶈瘯");
-    }
+    uni.showModal({
+      title: "鎻愮ず",
+      content: "鎻愪氦鍚庡皢鐢熸垚涓嶅悎鏍煎鐞嗗崟涓斾笉鍙慨鏀癸紝鏄惁缁х画锛�",
+      success: async res => {
+        if (!res.confirm) return;
+        try {
+          await submitQualityInspect({ id: item.id });
+          // 杩囩▼妫�鎻愪氦涓嶄骇鐢熷叆搴撹褰�
+          showToast(
+            "鎻愪氦鎴愬姛锛屼笉鍚堟牸閮ㄥ垎宸茬敓鎴愬鐞嗗崟锛堣繃绋嬫楠屼笉浜х敓鍏ュ簱璁板綍锛�"
+          );
+          setTimeout(() => {
+            getList();
+          }, 1500);
+        } catch (error) {
+          // request.ts 宸茬粺涓� toast 鍚庣 msg锛岃繖閲屽彧璁板綍
+          console.error("鎻愪氦澶辫触:", error);
+        }
+      },
+    });
   };
 
   // 鍒嗛厤妫�楠屽憳
@@ -550,6 +760,78 @@
     padding: 5px;
   }
 
+  // 妫�楠岀姸鎬佺瓫閫�
+  .filter-row {
+    display: flex;
+    align-items: center;
+    margin-top: 10px;
+  }
+
+  .filter-item {
+    display: flex;
+    align-items: center;
+    padding: 4px 10px;
+    background: #f2f3f5;
+    border-radius: 12px;
+  }
+
+  .filter-label {
+    font-size: 12px;
+    color: #666;
+    margin-right: 4px;
+  }
+
+  .filter-value {
+    font-size: 12px;
+    color: #2979ff;
+    margin-right: 4px;
+  }
+
+  // 涓嶅悎鏍煎鐞嗗脊绐�
+  .unqualified-dialog {
+    background: #ffffff;
+    border-radius: 16px 16px 0 0;
+    padding: 20px;
+    max-height: 70vh;
+    overflow-y: auto;
+  }
+
+  .unqualified-empty {
+    padding: 30px 0;
+    text-align: center;
+    font-size: 13px;
+    color: #999;
+  }
+
+  .unqualified-item {
+    display: flex;
+    align-items: center;
+    padding: 12px 0;
+    border-bottom: 1px solid #f0f0f0;
+  }
+
+  .unqualified-info {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    margin-right: 10px;
+  }
+
+  .unqualified-label {
+    font-size: 12px;
+    color: #999;
+  }
+
+  .unqualified-value {
+    font-size: 13px;
+    color: #333;
+    margin-top: 2px;
+  }
+
+  .unqualified-btn {
+    width: 70px;
+  }
+
   // 缁熻鍗$墖
   .stats-cards {
     display: flex;
diff --git a/src/pages/works.vue b/src/pages/works.vue
index 9e89ddf..60e1a9a 100644
--- a/src/pages/works.vue
+++ b/src/pages/works.vue
@@ -391,6 +391,10 @@
       icon: "/static/images/icon/gongyingshangdangan.svg",
       label: "渚涘簲鍟嗘。妗�",
     },
+    {
+      icon: "/static/images/icon/caigouguanli.svg",
+      label: "閲囪喘闇�姹�",
+    },
   ]);
 
   // 璐㈠姟绠$悊鍔熻兘鏁版嵁
@@ -470,6 +474,10 @@
     {
       icon: "/static/images/icon/kucunguanli.svg",
       label: "搴撳瓨绠$悊",
+    },
+    {
+      icon: "/static/images/icon/kucunguanli.svg",
+      label: "搴撳瓨鐩樼偣",
     },
   ]);
 
@@ -647,6 +655,22 @@
       icon: "/static/images/icon/shengchanhesuan.svg",
       label: "鐢熶骇鏍哥畻",
     },
+    {
+      icon: "/static/images/icon/shengchanbaogong.svg",
+      label: "璁′欢鍗曚环閰嶇疆",
+    },
+    {
+      icon: "/static/images/icon/shengchanshikuang.svg",
+      label: "浜ч噺缁熻",
+    },
+    {
+      icon: "/static/images/icon/shengchanhesuan.svg",
+      label: "鏉愭枡娑堣�楁牳绠�",
+    },
+    {
+      icon: "/static/images/icon/shengchanshikuang.svg",
+      label: "鎴愭湰鏍哥畻鍒嗘瀽",
+    },
   ]);
 
   // 璁惧绠$悊鍔熻兘鏁版嵁
@@ -774,6 +798,11 @@
       case "渚涘簲鍟嗘。妗�":
         uni.navigateTo({
           url: "/pages/basicData/supplierManage/index",
+        });
+        break;
+      case "閲囪喘闇�姹�":
+        uni.navigateTo({
+          url: "/pages/procurementManagement/procurementDemand/index",
         });
         break;
       case "鍏嚭绠$悊":
@@ -924,6 +953,26 @@
           url: "/pages/productionManagement/productionAccounting/index",
         });
         break;
+      case "璁′欢鍗曚环閰嶇疆":
+        uni.navigateTo({
+          url: "/pages/productionManagement/pieceRateConfig/index",
+        });
+        break;
+      case "浜ч噺缁熻":
+        uni.navigateTo({
+          url: "/pages/productionManagement/outputStatistics/index",
+        });
+        break;
+      case "鏉愭枡娑堣�楁牳绠�":
+        uni.navigateTo({
+          url: "/pages/productionManagement/materialConsumption/index",
+        });
+        break;
+      case "鎴愭湰鏍哥畻鍒嗘瀽":
+        uni.navigateTo({
+          url: "/pages/productionManagement/costAccounting/index",
+        });
+        break;
       case "鐢熶骇杩芥函":
         uni.navigateTo({
           url: "/pages/productionManagement/productionTraceability/index",
@@ -1054,6 +1103,11 @@
           url: "/pages/inventoryManagement/stockManagement/index",
         });
         break;
+      case "搴撳瓨鐩樼偣":
+        uni.navigateTo({
+          url: "/pages/inventoryManagement/stockCheck/index",
+        });
+        break;
       case "鍙嶉鐧昏":
         uni.navigateTo({
           url: "/pages/customerService/feedbackRegistration/index",
diff --git a/src/utils/common.ts b/src/utils/common.ts
index 25d4147..737af34 100644
--- a/src/utils/common.ts
+++ b/src/utils/common.ts
@@ -1,3 +1,5 @@
+import dayjs from 'dayjs'
+
 /**
 * 鏄剧ず娑堟伅鎻愮ず妗�
 * @param content 鎻愮ず鐨勬爣棰�
@@ -10,6 +12,19 @@
 }
 
 /**
+* 鏈懆鏃ユ湡鍖洪棿锛堝懆涓� ~ 浠婂ぉ锛�
+* dayjs 鏈厤缃� locale 鏃朵竴鍛ㄤ粠鍛ㄦ棩绠楄捣锛岃繖閲屾樉寮忔寜鍛ㄤ竴璧风畻
+*/
+export function getWeekRange() {
+  const today = dayjs()
+  const offset = (today.day() + 6) % 7
+  return {
+    startDate: today.subtract(offset, 'day').format('YYYY-MM-DD'),
+    endDate: today.format('YYYY-MM-DD')
+  }
+}
+
+/**
 * 鏄剧ず妯℃�佸脊绐�
 * @param content 鎻愮ず鐨勬爣棰�
 */

--
Gitblit v1.9.3