From c5842658c8bc44bd604090192c0e8857bb22e596 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期三, 06 五月 2026 16:01:23 +0800
Subject: [PATCH] 生产追溯模块开发,及生产订单跳转生产追溯

---
 src/pages.json                                                  |    7 
 src/pages/productionManagement/productionTraceability/index.vue |  947 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/pages/works.vue                                             |    9 
 src/api/productionManagement/productionOrder.js                 |    9 
 src/pages/productionManagement/productionOrder/index.vue        |   18 
 5 files changed, 987 insertions(+), 3 deletions(-)

diff --git a/src/api/productionManagement/productionOrder.js b/src/api/productionManagement/productionOrder.js
index bbe6162..3038550 100644
--- a/src/api/productionManagement/productionOrder.js
+++ b/src/api/productionManagement/productionOrder.js
@@ -10,6 +10,15 @@
   });
 }
 
+// 鐢熶骇璁㈠崟婧簮璇︽儏
+export function getOrderDetail(npsNo) {
+  return request({
+    url: "/productionOrder/ordeDetail",
+    method: "get",
+    params: { npsNo },
+  });
+}
+
 // 鑾峰彇鐢熶骇璁㈠崟鏉ユ簮鏁版嵁
 export function getProductOrderSource(id) {
   return request({
diff --git a/src/pages.json b/src/pages.json
index 55521e4..78e18df 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -866,6 +866,13 @@
       }
     },
     {
+      "path": "pages/productionManagement/productionTraceability/index",
+      "style": {
+        "navigationBarTitleText": "鐢熶骇杩芥函",
+        "navigationStyle": "custom"
+      }
+    },
+    {
       "path": "pages/inventoryManagement/receiptManagement/index",
       "style": {
         "navigationBarTitleText": "鑷畾涔夊叆搴�",
diff --git a/src/pages/productionManagement/productionOrder/index.vue b/src/pages/productionManagement/productionOrder/index.vue
index 9cea91f..5f31fb1 100644
--- a/src/pages/productionManagement/productionOrder/index.vue
+++ b/src/pages/productionManagement/productionOrder/index.vue
@@ -100,17 +100,22 @@
           <view class="item-footer">
             <view class="action-btns">
               <up-button type="info"
-                         size="mini"
+                         size="small"
+                         plain
+                         text="鐢熶骇杩芥函"
+                         @click="goTraceability(item)"></up-button>
+              <up-button type="info"
+                         size="small"
                          plain
                          text="宸ヨ壓璺嚎"
                          @click="goProcessRoute(item)"></up-button>
               <up-button type="primary"
-                         size="mini"
+                         size="small"
                          plain
                          text="鏉ユ簮"
                          @click="goSource(item)"></up-button>
               <up-button type="success"
-                         size="mini"
+                         size="small"
                          plain
                          text="棰嗘枡璇︽儏"
                          @click="goPickingDetail(item)"></up-button>
@@ -340,6 +345,13 @@
     });
   };
 
+  // 璺宠浆鐢熶骇杩芥函
+  const goTraceability = item => {
+    uni.navigateTo({
+      url: `/pages/productionManagement/productionTraceability/index?npsNo=${item.npsNo}`,
+    });
+  };
+
   // 椤甸潰鏄剧ず鏃跺姞杞芥暟鎹�
   onShow(() => {
     handleQuery();
diff --git a/src/pages/productionManagement/productionTraceability/index.vue b/src/pages/productionManagement/productionTraceability/index.vue
new file mode 100644
index 0000000..b761cb6
--- /dev/null
+++ b/src/pages/productionManagement/productionTraceability/index.vue
@@ -0,0 +1,947 @@
+<template>
+  <view class="production-traceability">
+    <PageHeader title="鐢熶骇杩芥函"
+                @back="goBack" />
+    <!-- 鎼滅储鍖哄煙 -->
+    <view class="search-section">
+      <view class="search-bar"
+            @click="openNpsNoSelector">
+        <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"
+                   color="#999"></up-icon>
+        </view>
+      </view>
+    </view>
+    <!-- 鍐呭鍖哄煙 -->
+    <view class="content-container"
+          v-if="rowData.productionOrderDto">
+      <!-- 鍩虹淇℃伅 -->
+      <view class="info-card">
+        <view class="card-title">鍩虹淇℃伅</view>
+        <view class="info-grid">
+          <view class="info-item">
+            <text class="label">鐢熶骇璁㈠崟鍙�</text>
+            <text class="value">{{ rowData.productionOrderDto.npsNo || '-' }}</text>
+          </view>
+          <view class="info-item">
+            <text class="label">浜у搧鍚嶇О</text>
+            <text class="value">{{ rowData.productionOrderDto.productName || '-' }}</text>
+          </view>
+          <view class="info-item">
+            <text class="label">浜у搧瑙勬牸</text>
+            <text class="value">{{ rowData.productionOrderDto.model || '-' }}</text>
+          </view>
+          <view class="info-item">
+            <text class="label">璁″垝鏁伴噺</text>
+            <text class="value">{{ rowData.productionOrderDto.quantity || 0 }} {{ rowData.productionOrderDto.unit || '' }}</text>
+          </view>
+          <view class="info-item">
+            <text class="label">褰撳墠鐘舵��</text>
+            <up-tag :text="getStatusText(rowData.productionOrderDto.status)"
+                    style="width:100rpx"
+                    :type="getStatusType(rowData.productionOrderDto.status)"
+                    size="mini" />
+          </view>
+          <view class="info-item">
+            <text class="label">瀹㈡埛鍚嶇О</text>
+            <text class="value">{{ rowData.productionOrderDto.customerName || '-' }}</text>
+          </view>
+          <view class="info-item">
+            <text class="label">寮�濮嬫棩鏈�</text>
+            <text class="value">{{ formatDate(rowData.productionOrderDto.startTime) }}</text>
+          </view>
+          <view class="info-item full-width">
+            <text class="label">瀹屾垚杩涘害</text>
+            <view class="progress-container">
+              <up-line-progress :percentage="formatProgress(rowData.productionOrderDto.completionStatus)"
+                                :activeColor="progressColor(rowData.productionOrderDto.completionStatus)"
+                                height="8"></up-line-progress>
+              <text class="progress-text">{{ formatProgress(rowData.productionOrderDto.completionStatus) }}%</text>
+            </view>
+          </view>
+        </view>
+      </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.planQuantity }} / {{ item.workOrder.completeQuantity }}</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>
+        </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.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="input-item">
+              <view class="input-row"><text class="label">鐗╂枡鍚嶇О锛�</text><text class="value">{{ item.materialName }}</text></view>
+              <view class="input-row"><text class="label">瑙勬牸鍨嬪彿锛�</text><text class="value">{{ item.model }}</text></view>
+              <view class="input-row"><text class="label">鎶曞叆鏁伴噺锛�</text><text class="value">{{ item.quantity }} {{ item.unit }}</text></view>
+              <view class="input-row"><text class="label">鎵规鍙凤細</text><text class="value">{{ item.batchNo }}</text></view>
+            </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>
+            <view class="params-table">
+              <view class="table-header">
+                <text class="col">鎸囨爣</text>
+                <text class="col">鏍囧噯鍊�</text>
+                <text class="col">瀹為檯鍊�</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.standardValue }}</text>
+                <text class="col"
+                      :class="{ 'error-text': param.testValue != param.standardValue }">{{ param.testValue }}</text>
+              </view>
+            </view>
+          </view>
+          <view v-if="!qualityRecords || qualityRecords.length === 0"
+                class="no-data-minor">鏆傛棤璐ㄦ璁板綍</view>
+        </scroll-view>
+      </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>
+  </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";
+
+  // 閫夋嫨鍣ㄧ浉鍏�
+  const showNpsNoSelector = ref(false);
+  const npsNoQuery = ref("");
+  const npsNoOptions = ref([]);
+  const npsNoLoading = ref(false);
+  const selectedNpsNo = ref(null);
+  const selectedNpsNoLabel = ref("");
+
+  const rowData = reactive({
+    productionOrderDto: null,
+    productionRecords: [],
+  });
+
+  // 鎶ュ伐璇︽儏
+  const reportPopupVisible = ref(false);
+  const detailData = ref({ workOrder: {}, reports: [] });
+
+  // 鎶曞叆璇︽儏
+  const inputPopupVisible = ref(false);
+  const inputListData = ref([]);
+  const inputLoading = ref(false);
+
+  // 璐ㄦ璇︽儏
+  const qualityPopupVisible = ref(false);
+  const qualityRecords = ref([]);
+
+  // 鍙傛暟璇︽儏
+  const paramModalVisible = ref(false);
+  const currentParams = ref([]);
+
+  const goBack = () => {
+    uni.navigateBack();
+  };
+
+  const openNpsNoSelector = () => {
+    showNpsNoSelector.value = true;
+    if (npsNoOptions.value.length === 0) {
+      handleNpsNoSearch();
+    }
+  };
+
+  const handleNpsNoSearch = async () => {
+    npsNoLoading.value = true;
+    try {
+      const res = await productOrderListPage({
+        npsNo: npsNoQuery.value || "",
+        pageNum: 1,
+        pageSize: 50,
+      });
+      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();
+    }
+  };
+
+  onLoad(async options => {
+    if (options.npsNo) {
+      uni.showLoading({ title: "鍔犺浇涓�..." });
+      try {
+        const res = await productOrderListPage({
+          npsNo: options.npsNo,
+          pageNum: 1,
+          pageSize: 10,
+        });
+        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();
+      }
+    }
+  });
+
+  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,
+        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,
+      userName: i.reportMain?.userName || "-",
+      inspectParamList: i.inspectParamList || [],
+    }));
+    qualityPopupVisible.value = true;
+  };
+
+  const showParams = params => {
+    currentParams.value = params || [];
+    paramModalVisible.value = true;
+  };
+</script>
+
+<style scoped lang="scss">
+  @import "@/styles/procurement-common.scss";
+
+  .production-traceability {
+    min-height: 100vh;
+    background-color: #f5f7fa;
+  }
+
+  .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;
+
+    .search-input {
+      flex: 1;
+      display: flex;
+      align-items: center;
+
+      .placeholder {
+        font-size: 28rpx;
+        color: #999;
+      }
+
+      .value {
+        font-size: 28rpx;
+        color: #333;
+        font-weight: 500;
+      }
+    }
+
+    .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-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;
+      color: #999;
+      font-size: 26rpx;
+    }
+  }
+
+  .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;
+      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;
+    }
+  }
+
+  .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;
+      }
+    }
+
+    .popup-scroll {
+      flex: 1;
+      overflow: hidden;
+    }
+  }
+
+  .detail-info {
+    background: #f8f9fa;
+    padding: 24rpx;
+    border-radius: 16rpx;
+    margin-bottom: 30rpx;
+    flex-direction: column;
+
+    .info-row {
+      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;
+    color: #999;
+    font-size: 28rpx;
+  }
+</style>
diff --git a/src/pages/works.vue b/src/pages/works.vue
index 04aeb3c..c6f3cc6 100644
--- a/src/pages/works.vue
+++ b/src/pages/works.vue
@@ -597,6 +597,10 @@
       icon: "/static/images/icon/shengchanbaogong1.svg",
       label: "鐢熶骇鏍哥畻",
     },
+    {
+      icon: "/static/images/icon/shengchanbaogong1.svg",
+      label: "鐢熶骇杩芥函",
+    },
   ]);
 
   // 璁惧绠$悊鍔熻兘鏁版嵁
@@ -870,6 +874,11 @@
           url: "/pages/productionManagement/productionAccounting/index",
         });
         break;
+      case "鐢熶骇杩芥函":
+        uni.navigateTo({
+          url: "/pages/productionManagement/productionTraceability/index",
+        });
+        break;
       case "璁惧鍙拌处":
         uni.navigateTo({
           url: "/pages/equipmentManagement/ledger/index",

--
Gitblit v1.9.3