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/productionManagement/productionTraceability/index.vue | 1970 ++++++++++++++++++++++++++++++++---------------------------
 1 files changed, 1,056 insertions(+), 914 deletions(-)

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>

--
Gitblit v1.9.3