From 166ff56efefe6682a9d840fe0f1fa6e456833556 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期五, 04 九月 2026 18:34:23 +0800
Subject: [PATCH] fix: 时效模块原型

---
 .env.development                                        |    8 
 src/pages.json                                          |   14 
 src/api/product/twist.ts                                |   14 
 src/pages/timely/detail.vue                             |  727 +++++++++++++
 src/pages/production/twist/components/SteelCoreCard.vue |    1 
 .env.production                                         |    4 
 src/api/timeSensitiveTask.ts                            |  709 ++++++++++++
 src/pages/timely/inspection.vue                         |  248 ++++
 src/pages/index/index.vue                               |    6 
 src/pages/timely/index.vue                              | 1008 ++++++++++++++++-
 src/pages/production/twist/receive/steelCore/index.vue  |  268 +---
 src/pages/production/twist/receive/steelCore/form.vue   |  240 ++++
 12 files changed, 2,978 insertions(+), 269 deletions(-)

diff --git a/.env.development b/.env.development
index 83c7fe3..4ca4dc4 100644
--- a/.env.development
+++ b/.env.development
@@ -7,13 +7,13 @@
 VITE_APP_PORT = 4096
 
 # API 鍩虹璺緞锛屽紑鍙戠幆澧冧笅鐨勮姹傚墠缂�
-# VITE_APP_BASE_API = 'http://114.132.189.42:7002/mes'
+VITE_APP_BASE_API = 'http://114.132.189.42:7002/mes'
 # VITE_APP_BASE_API = 'http://114.132.189.42:7002/mes'
 # VITE_APP_BASE_API = 'http://192.168.10.119:7002/mes' # 閭硅
-VITE_APP_BASE_API = 'http://192.168.100.131:7002/mes' #
+# VITE_APP_BASE_API = 'http://192.168.100.131:7002/mes' #
 
 # API 鏈嶅姟鍣ㄧ殑 URL
-# VITE_APP_API_URL = 'http://114.132.189.42:7002/mes'
+VITE_APP_API_URL = 'http://114.132.189.42:7002/mes'
 # VITE_APP_API_URL = 'http://192.168.10.119:7002/mes'
-VITE_APP_API_URL = 'http://192.168.100.131:7002/mes' #
+# VITE_APP_API_URL = 'http://192.168.100.131:7002/mes' #
 
diff --git a/.env.production b/.env.production
index 11cf31d..78c2d8b 100644
--- a/.env.production
+++ b/.env.production
@@ -4,7 +4,7 @@
 VITE_APP_BASE_API = '/prod-api'
 # VITE_APP_BASE_API = 'http://114.132.189.42:7002/mes'
 # API 鏈嶅姟鍣ㄧ殑 URL
-# VITE_APP_API_URL = 'http://114.132.189.42:7002/mes'
+VITE_APP_API_URL = 'http://114.132.189.42:7002/mes'
 # 灞变笢
-VITE_APP_API_URL = 'http://192.168.100.131:7002/mes'
+# VITE_APP_API_URL = 'http://192.168.100.131:7002/mes'
 
diff --git a/src/api/product/twist.ts b/src/api/product/twist.ts
index c64fbc4..5677da7 100644
--- a/src/api/product/twist.ts
+++ b/src/api/product/twist.ts
@@ -81,6 +81,20 @@
     });
   },
 
+  // 鏌ヨ鍙鐢ㄧ殑鍘熸潗鏂欐壒娆★紙涓� PC 绔挗鑺鐢ㄤ娇鐢ㄥ悓涓�鎺ュ彛锛�
+  getMaterialBatchStockPage(params: {
+    current?: number;
+    size?: number;
+    batchCode?: string;
+    materialType?: string;
+  }) {
+    return request<BaseResult<any>>({
+      url: "/material/batch/list",
+      method: "POST",
+      data: params,
+    });
+  },
+
   // 鏍规嵁鍗曚笣缂栧彿鏌ヨ
   selectByMonofilamentNumber(params: { monofilamentNumber: string }) {
     return request<BaseResult<any>>({
diff --git a/src/api/timeSensitiveTask.ts b/src/api/timeSensitiveTask.ts
new file mode 100644
index 0000000..b24d32e
--- /dev/null
+++ b/src/api/timeSensitiveTask.ts
@@ -0,0 +1,709 @@
+import request from "@/utils/request";
+import type { BaseResult, PageResult } from "@/models/base";
+
+export type TimelinessMaterialType = "鍗曚笣" | "缁炵嚎";
+
+export interface TimeSensitiveTaskQuery {
+  current: number;
+  size?: number;
+  initiatorName?: string;
+  machineName?: string;
+  materialType?: TimelinessMaterialType | string;
+  taskStatus?: number | string;
+  startTime?: string;
+  endTime?: string;
+}
+
+export interface TimeSensitiveTaskForm {
+  materialType: TimelinessMaterialType;
+  initiatorName: string;
+  initiateTime: string;
+  setTemperature: number | string;
+  agingDuration: number | string;
+  machineName: string;
+  inspectorName: string;
+}
+
+export interface TimeSensitiveTaskStartForm extends TimeSensitiveTaskForm {
+  id: number | string;
+  furnaceInTime: string;
+}
+
+export interface TimeSensitiveCandidateQuery {
+  current: number;
+  size?: number;
+  taskId: number | string;
+  materialType: TimelinessMaterialType;
+  model?: string;
+  materialCode?: string;
+}
+
+export interface TimelinessInspectionDetailForm {
+  id?: number | string;
+  routingInspectionUser?: string;
+  zoneOne?: number | string;
+  zoneTwo?: number | string;
+  zoneThree?: number | string;
+  zoneFour?: number | string;
+  zoneFive?: number | string;
+  zoneSix?: number | string;
+  timelinessInspectionId?: number | string;
+}
+
+/** 鑱旇皟瀹屾垚鍚庢敼涓� false 鍗冲彲鎭㈠涓嬫柟鐪熷疄鎺ュ彛銆� */
+export const USE_TIME_SENSITIVE_MOCK = true;
+
+const mockTasks: any[] = [
+  {
+    id: 9001,
+    taskCode: "SX-20260904-001",
+    materialType: "鍗曚笣",
+    initiatorName: "寮犲伐",
+    initiateTime: "2026-09-04 09:10:00",
+    setTemperature: 180,
+    agingDuration: "2+4+1",
+    machineName: "鏃舵晥鐐�-01",
+    itemCount: 2,
+    furnaceInTime: "",
+    furnaceOutTime: "",
+    inspectorName: "鏉庡笀鍌�",
+    taskStatus: 0,
+  },
+  {
+    id: 9002,
+    taskCode: "JX-20260904-001",
+    materialType: "缁炵嚎",
+    initiatorName: "鐜嬪伐",
+    initiateTime: "2026-09-04 08:30:00",
+    setTemperature: 175,
+    agingDuration: "2+3+1",
+    machineName: "鏃舵晥鐐�-02",
+    itemCount: 2,
+    furnaceInTime: "2026-09-04 10:00:00",
+    furnaceOutTime: "",
+    inspectorName: "璧靛笀鍌�",
+    taskStatus: 1,
+  },
+  {
+    id: 9003,
+    taskCode: "SX-20260903-001",
+    materialType: "鍗曚笣",
+    initiatorName: "鍒樺伐",
+    initiateTime: "2026-09-03 08:00:00",
+    setTemperature: 180,
+    agingDuration: "2+4+1",
+    machineName: "鏃舵晥鐐�-01",
+    itemCount: 1,
+    furnaceInTime: "2026-09-03 08:30:00",
+    furnaceOutTime: "2026-09-03 15:30:00",
+    inspectorName: "瀛欏笀鍌�",
+    taskStatus: 2,
+  },
+];
+
+const mockMaterialPool: Record<TimelinessMaterialType, any[]> = {
+  鍗曚笣: [
+    {
+      id: 9101,
+      materialType: "鍗曚笣",
+      model: "桅5.6",
+      poleModel: "1A60 H12 桅9.5 A2",
+      rodNo: "720526083102-014",
+      batchCode: "750826090101-005",
+      monofilamentNumber: "750826090101-005",
+      weight: 2875,
+      supplier: "灞变笢鍏冩椇鐢靛伐绉戞妧鏈夐檺鍏徃",
+    },
+    {
+      id: 9102,
+      materialType: "鍗曚笣",
+      model: "L-3.45",
+      poleModel: "1A60-H14 桅9.5 A6",
+      rodNo: "710726081602-003",
+      batchCode: "750426083002-037",
+      monofilamentNumber: "750426083002-037",
+      weight: 2650,
+      supplier: "灞变笢鍏冩椇鐢靛伐绉戞妧鏈夐檺鍏徃",
+    },
+    {
+      id: 9301,
+      outputId: 9301,
+      materialType: "鍗曚笣",
+      model: "8030-95L",
+      poleModel: "8030",
+      rodNo: "720626082404-001",
+      batchCode: "750626083101-001",
+      monofilamentNumber: "750626083101-001",
+      weight: 2400,
+      supplier: "灞变笢鍏冩椇鐢靛伐绉戞妧鏈夐檺鍏徃",
+    },
+    {
+      id: 9302,
+      outputId: 9302,
+      materialType: "鍗曚笣",
+      model: "桅3.20",
+      poleModel: "1A60 H14 桅9.5 A6",
+      rodNo: "710726090401-002",
+      batchCode: "750726090401-002",
+      monofilamentNumber: "750726090401-002",
+      weight: 2520,
+      supplier: "灞变笢鍏冩椇鐢靛伐绉戞妧鏈夐檺鍏徃",
+    },
+    {
+      id: 9501,
+      materialType: "鍗曚笣",
+      model: "桅4.50",
+      poleModel: "1A60 H12 桅9.5 A2",
+      rodNo: "720526083102-018",
+      batchCode: "750826090201-001",
+      monofilamentNumber: "750826090201-001",
+      weight: 2780,
+      supplier: "灞变笢鍏冩椇鐢靛伐绉戞妧鏈夐檺鍏徃",
+    },
+  ],
+  缁炵嚎: [
+    {
+      id: 9201,
+      outputId: 9201,
+      materialType: "缁炵嚎",
+      model: "JLHA1/G1A-16/3-6/1",
+      batchCode: "JX260904001-01",
+      weight: 1180,
+    },
+    {
+      id: 9202,
+      outputId: 9202,
+      materialType: "缁炵嚎",
+      model: "JLHA1/G1A-25/4-6/1",
+      batchCode: "JX260904001-02",
+      weight: 1260,
+    },
+    {
+      id: 9401,
+      outputId: 9401,
+      materialType: "缁炵嚎",
+      model: "JLHA1/G1A-35/6-6/1",
+      batchCode: "JX260904002-01",
+      weight: 1380,
+    },
+    {
+      id: 9402,
+      outputId: 9402,
+      materialType: "缁炵嚎",
+      model: "JLHA1/G1A-50/8-6/1",
+      batchCode: "JX260904002-02",
+      weight: 1460,
+    },
+  ],
+};
+
+const findMockMaterial = (id: number | string) =>
+  [...mockMaterialPool.鍗曚笣, ...mockMaterialPool.缁炵嚎].find(
+    (item) => String(item.id) === String(id)
+  );
+
+const cloneMockMaterial = (id: number | string) => {
+  const item = findMockMaterial(id);
+  return item ? { ...item } : undefined;
+};
+
+const mockItems: Record<string, any[]> = {
+  "9001": [cloneMockMaterial(9101), cloneMockMaterial(9102)].filter(Boolean),
+  "9002": [cloneMockMaterial(9201), cloneMockMaterial(9202)].filter(Boolean),
+  "9003": [cloneMockMaterial(9501)].filter(Boolean),
+};
+
+const mockInspections: Record<string, any[]> = {
+  "9002": [
+    {
+      id: 9601,
+      routingInspectionUser: "璧靛笀鍌�",
+      zoneOne: 175,
+      zoneTwo: 175.2,
+      zoneThree: 174.8,
+      zoneFour: 175.1,
+      zoneFive: 175,
+      zoneSix: 174.9,
+      createTime: "2026-09-04 11:00:00",
+      timelinessInspectionId: 9002,
+    },
+  ],
+  "9003": [
+    {
+      id: 9602,
+      routingInspectionUser: "瀛欏笀鍌�",
+      zoneOne: 180,
+      zoneTwo: 180,
+      zoneThree: 179.8,
+      zoneFour: 180.1,
+      zoneFive: 180,
+      zoneSix: 179.9,
+      createTime: "2026-09-03 10:30:00",
+      timelinessInspectionId: 9003,
+    },
+  ],
+};
+
+const mockOperations: Record<string, any[]> = {
+  "9001": [
+    {
+      id: 9701,
+      operationType: 1,
+      operationTypeDesc: "鏂板",
+      beforeStatus: "",
+      afterStatus: "寰呭紑濮�",
+      operationDetail: "寮犲伐鍒涘缓鏃舵晥浠诲姟",
+      operatorName: "寮犲伐",
+      operateTime: "2026-09-04 09:10:00",
+    },
+  ],
+  "9002": [
+    {
+      id: 9702,
+      operationType: 2,
+      operationTypeDesc: "寮�濮嬫椂鏁�",
+      beforeStatus: "寰呭紑濮�",
+      afterStatus: "鏃舵晥涓�",
+      operationDetail: "璧靛笀鍌呯‘璁や换鍔′俊鎭苟寮�濮嬫椂鏁�",
+      operatorName: "璧靛笀鍌�",
+      operateTime: "2026-09-04 10:00:00",
+    },
+  ],
+  "9003": [
+    {
+      id: 9703,
+      operationType: 3,
+      operationTypeDesc: "缁撴潫鏃舵晥",
+      beforeStatus: "鏃舵晥涓�",
+      afterStatus: "鏃舵晥缁撴潫",
+      operationDetail: "瀛欏笀鍌呯粨鏉熸椂鏁堜换鍔�",
+      operatorName: "瀛欏笀鍌�",
+      operateTime: "2026-09-03 15:30:00",
+    },
+  ],
+};
+
+const mockUsers = [
+  { userId: 1, nickName: "寮犲伐" },
+  { userId: 2, nickName: "鐜嬪伐" },
+  { userId: 3, nickName: "鍒樺伐" },
+  { userId: 4, nickName: "鏉庡笀鍌�" },
+  { userId: 5, nickName: "璧靛笀鍌�" },
+  { userId: 6, nickName: "瀛欏笀鍌�" },
+];
+
+const mockMachines = [
+  { deviceId: 1, deviceModel: "鏃舵晥鐐�-01" },
+  { deviceId: 2, deviceModel: "鏃舵晥鐐�-02" },
+  { deviceId: 3, deviceModel: "鏃舵晥鐐�-03" },
+];
+
+const pad = (value: number) => String(value).padStart(2, "0");
+const mockDateTime = () => {
+  const date = new Date();
+  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(
+    date.getHours()
+  )}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
+};
+
+const mockResult = <T>(data: T, msg = "success") =>
+  new Promise<BaseResult<T>>((resolve) => {
+    setTimeout(() => resolve({ code: 200, msg, data }), 120);
+  });
+
+const mockMaterialCode = (item: any) =>
+  String(item?.flowCode || item?.batchCode || item?.monofilamentNumber || "").trim();
+
+const updateMockItemCount = (taskId: number | string) => {
+  const task = mockTasks.find((item) => String(item.id) === String(taskId));
+  if (task) task.itemCount = (mockItems[String(taskId)] || []).length;
+};
+
+const addMockOperation = (
+  taskId: number | string,
+  operationType: number,
+  operationTypeDesc: string,
+  beforeStatus: string,
+  afterStatus: string,
+  operationDetail: string,
+  operatorName: string
+) => {
+  const key = String(taskId);
+  mockOperations[key] ||= [];
+  mockOperations[key].unshift({
+    id: Date.now(),
+    operationType,
+    operationTypeDesc,
+    beforeStatus,
+    afterStatus,
+    operationDetail,
+    operatorName,
+    operateTime: mockDateTime(),
+  });
+};
+
+const TimeSensitiveTaskApi = {
+  queryPage(params: TimeSensitiveTaskQuery) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      let rows = [...mockTasks];
+      if (params.initiatorName) {
+        rows = rows.filter((item) => item.initiatorName.includes(params.initiatorName!));
+      }
+      if (params.machineName) {
+        rows = rows.filter((item) => item.machineName.includes(params.machineName!));
+      }
+      if (params.materialType) {
+        rows = rows.filter((item) => item.materialType === params.materialType);
+      }
+      if (params.taskStatus !== undefined && params.taskStatus !== "") {
+        rows = rows.filter((item) => Number(item.taskStatus) === Number(params.taskStatus));
+      }
+      if (params.startTime) {
+        rows = rows.filter((item) => item.initiateTime >= params.startTime!);
+      }
+      if (params.endTime) {
+        rows = rows.filter((item) => item.initiateTime <= params.endTime!);
+      }
+      const current = Number(params.current || 1);
+      const size = Number(params.size || 10);
+      return mockResult<PageResult>({
+        records: rows.slice((current - 1) * size, current * size),
+        total: rows.length,
+        current,
+        size,
+        pages: current * size < rows.length,
+      });
+    }
+    return request<BaseResult<PageResult>>({
+      url: "/timeSensitiveTask/queryPage",
+      method: "GET",
+      data: params,
+    });
+  },
+
+  getDetail(id: number | string) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      return mockResult(mockTasks.find((item) => String(item.id) === String(id)) || null);
+    }
+    return request<BaseResult<any>>({
+      url: "/timeSensitiveTask/getDetailById",
+      method: "GET",
+      data: { id },
+    });
+  },
+
+  getItems(taskId: number | string) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      return mockResult<any[]>((mockItems[String(taskId)] || []).map((item) => ({ ...item })));
+    }
+    return request<BaseResult<any[]>>({
+      url: "/timeSensitiveTask/getItems",
+      method: "GET",
+      data: { taskId },
+    });
+  },
+
+  create(data: TimeSensitiveTaskForm) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      const id = Date.now();
+      const taskCode = `${data.materialType === "鍗曚笣" ? "SX" : "JX"}-${String(id).slice(-10)}`;
+      mockTasks.unshift({
+        id,
+        taskCode,
+        ...data,
+        itemCount: 0,
+        furnaceInTime: "",
+        furnaceOutTime: "",
+        taskStatus: 0,
+      });
+      mockItems[String(id)] = [];
+      addMockOperation(
+        id,
+        1,
+        "鏂板",
+        "",
+        "寰呭紑濮�",
+        `${data.initiatorName}鍒涘缓鏃舵晥浠诲姟`,
+        data.initiatorName
+      );
+      return mockResult({ id, taskCode }, "鏃舵晥浠诲姟鏂板鎴愬姛");
+    }
+    return request<BaseResult<any>>({
+      url: "/timeSensitiveTask/add",
+      method: "POST",
+      data,
+    });
+  },
+
+  update(data: TimeSensitiveTaskForm & { id: number | string }) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      const task = mockTasks.find((item) => String(item.id) === String(data.id));
+      if (task) Object.assign(task, data);
+      addMockOperation(
+        data.id,
+        2,
+        "淇敼",
+        "寰呭紑濮�",
+        "寰呭紑濮�",
+        "淇敼鏃舵晥浠诲姟鍩虹淇℃伅",
+        data.initiatorName
+      );
+      return mockResult(task, "鏃舵晥浠诲姟淇敼鎴愬姛");
+    }
+    return request<BaseResult<any>>({
+      url: "/timeSensitiveTask/update",
+      method: "POST",
+      data,
+    });
+  },
+
+  delete(id: number | string) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      const index = mockTasks.findIndex((item) => String(item.id) === String(id));
+      if (index >= 0) mockTasks.splice(index, 1);
+      delete mockItems[String(id)];
+      delete mockOperations[String(id)];
+      delete mockInspections[String(id)];
+      return mockResult(true, "鏃舵晥浠诲姟鍒犻櫎鎴愬姛");
+    }
+    return request<BaseResult<any>>({
+      url: "/timeSensitiveTask/delete",
+      method: "DELETE",
+      data: { id },
+    });
+  },
+
+  start(data: TimeSensitiveTaskStartForm) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      const task = mockTasks.find((item) => String(item.id) === String(data.id));
+      if (task) Object.assign(task, data, { taskStatus: 1 });
+      addMockOperation(
+        data.id,
+        2,
+        "寮�濮嬫椂鏁�",
+        "寰呭紑濮�",
+        "鏃舵晥涓�",
+        `${data.inspectorName}纭浠诲姟淇℃伅骞跺紑濮嬫椂鏁坄,
+        data.inspectorName
+      );
+      return mockResult(task, "鏃舵晥宸插紑濮�");
+    }
+    return request<BaseResult<any>>({
+      url: "/timeSensitiveTask/start",
+      method: "POST",
+      data,
+    });
+  },
+
+  checkEnd(id: number | string) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      return mockResult({
+        canEnd: false,
+        reachedDuration: false,
+        elapsedMinutes: 120,
+        requiredMinutes: 360,
+        message: "Mock锛氬皻鏈揪鍒拌瀹氭椂鏁堟椂闀裤��",
+      });
+    }
+    return request<BaseResult<any>>({
+      url: "/timeSensitiveTask/checkEnd",
+      method: "GET",
+      data: { id },
+    });
+  },
+
+  end(data: { id: number | string; forceEnd: boolean }) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      const task = mockTasks.find((item) => String(item.id) === String(data.id));
+      if (task) Object.assign(task, { taskStatus: 2, furnaceOutTime: mockDateTime() });
+      addMockOperation(
+        data.id,
+        3,
+        "缁撴潫鏃舵晥",
+        "鏃舵晥涓�",
+        "鏃舵晥缁撴潫",
+        data.forceEnd ? "鏈揪鍒版椂闀匡紝纭寮哄埗缁撴潫鏃舵晥" : "缁撴潫鏃舵晥浠诲姟",
+        task?.inspectorName || "鎿嶄綔浜�"
+      );
+      return mockResult(task, "鏃舵晥宸茬粨鏉�");
+    }
+    return request<BaseResult<any>>({
+      url: "/timeSensitiveTask/end",
+      method: "POST",
+      data,
+    });
+  },
+
+  queryCandidates(params: TimeSensitiveCandidateQuery) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      const existingIds = new Set(
+        (mockItems[String(params.taskId)] || []).map((item) => String(item.id))
+      );
+      let rows = mockMaterialPool[params.materialType]
+        .filter((item) => !existingIds.has(String(item.id)))
+        .map((item) => ({ ...item }));
+      if (params.model) {
+        const model = params.model.trim().toUpperCase();
+        rows = rows.filter((item) =>
+          String(item.model || "")
+            .toUpperCase()
+            .includes(model)
+        );
+      }
+      if (params.materialCode) {
+        const code = params.materialCode.trim().toUpperCase();
+        rows = rows.filter(
+          (item) =>
+            mockMaterialCode(item).toUpperCase().includes(code) ||
+            String(item.id).toUpperCase() === code ||
+            String(item.outputId || "").toUpperCase() === code
+        );
+      }
+      const current = Number(params.current || 1);
+      const size = Number(params.size || 10);
+      return mockResult<PageResult>({
+        records: rows.slice((current - 1) * size, current * size),
+        total: rows.length,
+        current,
+        size,
+        pages: current * size < rows.length,
+      });
+    }
+    return request<BaseResult<PageResult>>({
+      url: "/timeSensitiveTask/queryCandidates",
+      method: "GET",
+      data: params,
+    });
+  },
+
+  addItems(data: { taskId: number | string; itemIds: Array<number | string> }) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      const key = String(data.taskId);
+      mockItems[key] ||= [];
+      const existingIds = new Set(mockItems[key].map((item) => String(item.id)));
+      data.itemIds.forEach((id) => {
+        const material = cloneMockMaterial(id);
+        if (material && !existingIds.has(String(id))) mockItems[key].push(material);
+      });
+      updateMockItemCount(data.taskId);
+      const task = mockTasks.find((item) => String(item.id) === key);
+      addMockOperation(
+        data.taskId,
+        1,
+        "鏂板鏄庣粏",
+        "寰呭紑濮�",
+        "寰呭紑濮�",
+        `鏂板 ${data.itemIds.length} 鏉℃椂鏁堟槑缁哷,
+        task?.initiatorName || "鎿嶄綔浜�"
+      );
+      return mockResult(true, "鏃舵晥鏄庣粏鏂板鎴愬姛");
+    }
+    return request<BaseResult<any>>({
+      url: "/timeSensitiveTask/addItems",
+      method: "POST",
+      data,
+    });
+  },
+
+  deleteItems(data: { taskId: number | string; itemIds: Array<number | string> }) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      const key = String(data.taskId);
+      const idSet = new Set(data.itemIds.map(String));
+      mockItems[key] = (mockItems[key] || []).filter((item) => !idSet.has(String(item.id)));
+      updateMockItemCount(data.taskId);
+      const task = mockTasks.find((item) => String(item.id) === key);
+      addMockOperation(
+        data.taskId,
+        4,
+        "鍒犻櫎鏄庣粏",
+        "寰呭紑濮�",
+        "寰呭紑濮�",
+        `鍒犻櫎 ${data.itemIds.length} 鏉℃椂鏁堟槑缁哷,
+        task?.initiatorName || "鎿嶄綔浜�"
+      );
+      return mockResult(true, "鏃舵晥鏄庣粏鍒犻櫎鎴愬姛");
+    }
+    return request<BaseResult<any>>({
+      url: "/timeSensitiveTask/deleteItems",
+      method: "POST",
+      data,
+    });
+  },
+
+  getMachines() {
+    if (USE_TIME_SENSITIVE_MOCK) return mockResult(mockMachines.map((item) => ({ ...item })));
+    return request<BaseResult<any[]>>({
+      url: "/device/querydeviceSx",
+      method: "GET",
+    });
+  },
+
+  getUsers() {
+    if (USE_TIME_SENSITIVE_MOCK) return mockResult(mockUsers.map((item) => ({ ...item })));
+    return request<BaseResult<any[]>>({
+      url: "/team/getUser",
+      method: "GET",
+    });
+  },
+
+  getOperationRecords(recordId: number | string) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      return mockResult<any[]>(
+        (mockOperations[String(recordId)] || []).map((item) => ({ ...item }))
+      );
+    }
+    return request<BaseResult<any[]>>({
+      url: "/operationRecord/list",
+      method: "POST",
+      data: { tableName: "time_sensitive_task", recordId },
+    });
+  },
+
+  getInspectionDetails(id: number | string) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      return mockResult<any[]>((mockInspections[String(id)] || []).map((item) => ({ ...item })));
+    }
+    return request<BaseResult<any>>({
+      url: "/timelinessInspection/getDetailById",
+      method: "GET",
+      data: { id },
+    });
+  },
+
+  saveInspectionDetails(data: TimelinessInspectionDetailForm[]) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      const inspectionId = data[0]?.timelinessInspectionId;
+      if (inspectionId !== undefined) {
+        mockInspections[String(inspectionId)] = data.map((item, index) => ({
+          ...item,
+          id: item.id || Date.now() + index,
+          createTime: (item as any).createTime || mockDateTime(),
+        }));
+      }
+      return mockResult(true, "宸℃璁板綍淇濆瓨鎴愬姛");
+    }
+    return request<BaseResult<any>>({
+      url: "/timelinessInspectionDetail/add",
+      method: "POST",
+      data,
+    });
+  },
+
+  deleteInspectionDetail(id: number | string) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      Object.keys(mockInspections).forEach((key) => {
+        mockInspections[key] = mockInspections[key].filter(
+          (item) => String(item.id) !== String(id)
+        );
+      });
+      return mockResult(true, "宸℃璁板綍鍒犻櫎鎴愬姛");
+    }
+    return request<BaseResult<any>>({
+      url: "/timelinessInspectionDetail/delete",
+      method: "DELETE",
+      data: { id },
+    });
+  },
+};
+
+export default TimeSensitiveTaskApi;
diff --git a/src/pages.json b/src/pages.json
index 3f648e0..09dedb4 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -268,7 +268,19 @@
     {
       "path": "pages/timely/index",
       "style": {
-        "navigationBarTitleText": "鏃舵晥鎶ュ伐"
+        "navigationBarTitleText": "鏃舵晥浠诲姟"
+      }
+    },
+    {
+      "path": "pages/timely/detail",
+      "style": {
+        "navigationBarTitleText": "鏃舵晥浠诲姟鏄庣粏"
+      }
+    },
+    {
+      "path": "pages/timely/inspection",
+      "style": {
+        "navigationBarTitleText": "鏃舵晥宸℃璁板綍"
       }
     },
     {
diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue
index 6dd4b40..ae6915c 100644
--- a/src/pages/index/index.vue
+++ b/src/pages/index/index.vue
@@ -230,6 +230,12 @@
     url: "/pages/outbound/index",
     show: false,
   },
+  {
+    icon: "/static/icons/log.png",
+    title: "鏃舵晥",
+    url: "/pages/timely/index",
+    show: true,
+  },
 ]);
 
 // 鍔犺浇璁块棶缁熻鏁版嵁
diff --git a/src/pages/production/twist/components/SteelCoreCard.vue b/src/pages/production/twist/components/SteelCoreCard.vue
index 9e03def..baa1389 100644
--- a/src/pages/production/twist/components/SteelCoreCard.vue
+++ b/src/pages/production/twist/components/SteelCoreCard.vue
@@ -10,6 +10,7 @@
       <wd-card>
         <wd-cell-group :border="true">
           <wd-cell title="鑺嚎绫诲瀷" :value="data.diskMaterial || '-'" />
+          <wd-cell title="鎵规鍙�" :value="data.batchNo || data.batchCode || '-'" />
           <wd-cell title="瑙勬牸鍨嬪彿" :value="data.model || '-'" />
           <wd-cell title="鐩樺彿" :value="data.monofilamentNumber || '-'" />
           <wd-cell title="鏁伴噺" :value="formatAmount" />
diff --git a/src/pages/production/twist/receive/steelCore/form.vue b/src/pages/production/twist/receive/steelCore/form.vue
index 7d31a3c..b853066 100644
--- a/src/pages/production/twist/receive/steelCore/form.vue
+++ b/src/pages/production/twist/receive/steelCore/form.vue
@@ -11,13 +11,23 @@
         clearable
         @confirm="handleDiskMaterialChange"
       />
+      <wd-cell
+        title="鎵规鍙�"
+        title-width="100px"
+        :value="model.batchNo || '璇烽�夋嫨鎵规鍙�'"
+        :value-class="model.batchNo ? '' : 'batch-placeholder'"
+        is-link
+        required
+        clickable
+        @click="openBatchSelector"
+      />
       <wd-input
         v-model="model.model"
         label="瑙勬牸鍨嬪彿"
         label-width="100px"
         prop="model"
-        clearable
-        placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+        readonly
+        placeholder="閫夋嫨鎵规鍚庤嚜鍔ㄥ甫鍑�"
       />
       <wd-input
         v-model="model.monofilamentNumber"
@@ -37,11 +47,11 @@
       />
       <wd-input
         v-model="model.weight"
-        label="閲嶉噺"
+        label="棰嗙敤閲嶉噺"
         label-width="100px"
         prop="weight"
         clearable
-        placeholder="璇疯緭鍏ラ噸閲�"
+        placeholder="璇疯緭鍏ラ鐢ㄩ噸閲�(kg)"
       />
       <wd-picker
         v-model="model.supplier"
@@ -55,12 +65,57 @@
       />
     </wd-cell-group>
   </wd-form>
+
+  <wd-popup
+    v-model="showBatchSelector"
+    position="bottom"
+    :z-index="30"
+    :close-on-click-modal="false"
+    custom-style="height: 72vh; border-radius: 24rpx 24rpx 0 0;"
+  >
+    <view class="batch-selector">
+      <view class="batch-header">
+        <text class="batch-title">閫夋嫨鎵规鍙�</text>
+        <wd-icon name="close" size="20px" @click="showBatchSelector = false" />
+      </view>
+      <wd-search
+        v-model="batchKeyword"
+        placeholder="璇疯緭鍏ユ壒娆″彿鎼滅储"
+        hide-cancel
+        @search="searchBatchRemote"
+        @clear="searchBatchRemote('')"
+      />
+      <scroll-view class="batch-list" scroll-y>
+        <view v-if="batchSearching" class="batch-state">姝e湪鍔犺浇鎵规...</view>
+        <template v-else-if="filteredBatchOptions.length">
+          <view
+            v-for="batch in filteredBatchOptions"
+            :key="batch.batchCode"
+            class="batch-item"
+            :class="{ active: batch.batchCode === model.batchNo }"
+            @click="selectBatch(batch)"
+          >
+            <view class="batch-item-header">
+              <text class="batch-code">{{ batch.batchCode }}</text>
+              <text class="batch-weight">褰撳墠 {{ stockWeightKg(batch) ?? "-" }} kg</text>
+            </view>
+            <view class="batch-meta">
+              <text>{{ batch.materialName || "-" }}</text>
+              <text>{{ getBatchModel(batch) }}</text>
+            </view>
+          </view>
+        </template>
+        <view v-else class="batch-state">鏆傛棤鍙鐢ㄧ殑鍚堟牸鎵规</view>
+      </scroll-view>
+    </view>
+  </wd-popup>
 </template>
 
 <script lang="ts" setup>
 import { onMounted, watch } from "vue";
 import useFormData from "@/hooks/useFormData";
 import ManageApi from "@/api/product/manage";
+import TwistApi from "@/api/product/twist";
 import { useToast } from "wot-design-uni";
 
 const props = defineProps({
@@ -82,6 +137,8 @@
 const toast = useToast();
 const { form: model } = useFormData({
   diskMaterial: undefined, // 鑺嚎绫诲瀷
+  batchNo: undefined, // 鎵规鍙凤紙灞曠ず鍙婇挗鑺鐢ㄥ瓧娈碉級
+  batchCode: undefined, // 鍏煎搴撳瓨棰嗙敤瀛楁
   model: undefined, // 瑙勬牸鍨嬪彿
   monofilamentNumber: undefined, // 鐩樺彿
   amount: undefined, // 鏁伴噺
@@ -89,6 +146,75 @@
   supplier: undefined, // 鍘傚
   type: "閽㈣姱",
 });
+
+interface MaterialBatch {
+  batchCode: string;
+  materialName?: string;
+  resolvedSpecModel?: string;
+  mesSpec?: string;
+  ncSpec?: string;
+  currentWeight?: number;
+  mainUnit?: string;
+  qualityStatus?: string;
+  ncReturnFlag?: string;
+}
+
+const showBatchSelector = ref(false);
+const batchKeyword = ref("");
+const batchOptions = ref<MaterialBatch[]>([]);
+const batchSearching = ref(false);
+
+const filteredBatchOptions = computed(() =>
+  batchOptions.value.filter((batch) => {
+    const qualified = batch.qualityStatus === "鍚堟牸" || batch.qualityStatus === "OK";
+    return qualified && batch.ncReturnFlag !== "Y";
+  })
+);
+
+const getBatchModel = (batch: MaterialBatch) =>
+  batch.resolvedSpecModel || batch.mesSpec || batch.ncSpec || "-";
+
+const stockWeightKg = (batch: MaterialBatch) => {
+  if (batch.currentWeight === undefined || batch.currentWeight === null) return undefined;
+  const weight = Number(batch.currentWeight);
+  if (!Number.isFinite(weight)) return undefined;
+  return batch.mainUnit === "鍚�" ? weight * 1000 : weight;
+};
+
+const searchBatchRemote = async (keyword: string | { value?: string } = batchKeyword.value) => {
+  const searchValue =
+    typeof keyword === "string" ? keyword : String(keyword?.value || batchKeyword.value || "");
+  batchKeyword.value = searchValue;
+  batchSearching.value = true;
+  try {
+    const { code, data } = await TwistApi.getMaterialBatchStockPage({
+      batchCode: searchValue.trim() || undefined,
+      materialType: "鍘熸潗鏂�",
+      current: 1,
+      size: 20,
+    });
+    batchOptions.value = code === 200 && Array.isArray(data?.records) ? data.records : [];
+  } catch (error: any) {
+    batchOptions.value = [];
+    toast.error(error?.message || "鎵规鏌ヨ澶辫触");
+  } finally {
+    batchSearching.value = false;
+  }
+};
+
+const openBatchSelector = () => {
+  showBatchSelector.value = true;
+  batchKeyword.value = model.batchNo || "";
+  searchBatchRemote(batchKeyword.value);
+};
+
+const selectBatch = (batch: MaterialBatch) => {
+  const batchModel = getBatchModel(batch);
+  model.batchNo = batch.batchCode;
+  model.batchCode = batch.batchCode;
+  model.model = batchModel === "-" ? "" : batchModel;
+  showBatchSelector.value = false;
+};
 
 // 鑺嚎绫诲瀷瀛楀吀鏁版嵁
 const diskMaterialOptions = ref<Array<{ label: string; value: string }>>([]);
@@ -148,13 +274,37 @@
 
 // 鏀堕泦琛ㄥ崟鏁版嵁锛堜笉璋� API锛岃繑鍥炵粰鐖剁粍浠跺鐞嗭級
 const collectData = () => {
+  if (!model.batchNo) {
+    toast.error("璇烽�夋嫨鎵规鍙�");
+    return null;
+  }
+  if (!model.diskMaterial) {
+    toast.error("璇烽�夋嫨鑺嚎绫诲瀷");
+    return null;
+  }
+  if (!model.monofilamentNumber) {
+    toast.error("璇疯緭鍏ョ洏鍙�");
+    return null;
+  }
+  if (!Number.isFinite(Number(model.weight)) || Number(model.weight) <= 0) {
+    toast.error("璇疯緭鍏ュぇ浜�0鐨勯鐢ㄩ噸閲�");
+    return null;
+  }
+  if (!model.supplier) {
+    toast.error("璇烽�夋嫨鍘傚");
+    return null;
+  }
+
   return {
     diskMaterial: model.diskMaterial,
+    batchNo: model.batchNo,
+    batchCode: model.batchNo,
     model: model.model,
     monofilamentNumber: model.monofilamentNumber,
     amount: model.amount,
     weight: model.weight,
     supplier: model.supplier,
+    unit: "kg",
     type: model.type,
   };
 };
@@ -172,6 +322,8 @@
 // 閲嶇疆琛ㄥ崟鏁版嵁
 const resetFormData = () => {
   model.diskMaterial = undefined;
+  model.batchNo = undefined;
+  model.batchCode = undefined;
   model.model = undefined;
   model.monofilamentNumber = undefined;
   model.amount = undefined;
@@ -179,12 +331,16 @@
   model.supplier = undefined;
   model.type = "閽㈣姱";
   diskMaterialValue.value = "";
+  batchKeyword.value = "";
+  batchOptions.value = [];
 };
 
 // 濉厖琛ㄥ崟鏁版嵁锛堢敤浜庢壂鐮佸悗鍥炴樉锛�
 const fillFormData = (data: any) => {
   if (data) {
     model.diskMaterial = data.diskMaterial || "";
+    model.batchNo = data.batchNo || data.batchCode || "";
+    model.batchCode = data.batchCode || data.batchNo || "";
     model.model = data.model || "";
     model.monofilamentNumber = data.monofilamentNumber || "";
     model.amount = data.amount || data.oneLength || "";
@@ -199,6 +355,8 @@
 const setFormData = (item: any) => {
   if (item) {
     model.diskMaterial = item.diskMaterial || "";
+    model.batchNo = item.batchNo || item.batchCode || "";
+    model.batchCode = item.batchCode || item.batchNo || "";
     model.model = item.model || "";
     model.monofilamentNumber = item.monofilamentNumber || "";
     model.amount = item.amount || "";
@@ -241,4 +399,78 @@
   bottom: 0;
   width: 100%;
 }
+
+:deep(.batch-placeholder) {
+  color: #bfbfbf;
+}
+
+.batch-selector {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+}
+
+.batch-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 16px;
+  border-bottom: 1px solid #eeeeee;
+}
+
+.batch-title {
+  font-size: 17px;
+  font-weight: 600;
+  color: #333333;
+}
+
+.batch-list {
+  flex: 1;
+  height: 0;
+  padding: 0 12px 16px;
+  box-sizing: border-box;
+}
+
+.batch-item {
+  padding: 14px 12px;
+  margin-bottom: 8px;
+  background: #ffffff;
+  border: 1px solid #e5e5e5;
+  border-radius: 8px;
+}
+
+.batch-item.active {
+  background: #f0fbfa;
+  border-color: #0d867f;
+}
+
+.batch-item-header,
+.batch-meta {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+}
+
+.batch-code {
+  font-weight: 600;
+  color: #222222;
+}
+
+.batch-weight {
+  font-size: 12px;
+  color: #0d867f;
+}
+
+.batch-meta {
+  margin-top: 8px;
+  font-size: 12px;
+  color: #888888;
+}
+
+.batch-state {
+  padding: 48px 16px;
+  color: #999999;
+  text-align: center;
+}
 </style>
diff --git a/src/pages/production/twist/receive/steelCore/index.vue b/src/pages/production/twist/receive/steelCore/index.vue
index 77e607a..d4f8c7e 100644
--- a/src/pages/production/twist/receive/steelCore/index.vue
+++ b/src/pages/production/twist/receive/steelCore/index.vue
@@ -2,6 +2,7 @@
   <view class="list_box">
     <z-paging
       ref="pagingRef"
+      v-model="steelData"
       :fixed="false"
       :auto-show-back-to-top="true"
       :loading-more-enabled="false"
@@ -11,38 +12,34 @@
         <CardTitle title="鑺嚎棰嗙敤" :hideAction="false">
           <template #action>
             <wd-button type="icon" icon="scan" color="#0D867F" @click="openScan"></wd-button>
-            <wd-button type="icon" icon="add-circle" color="#0D867F" @click="openAddForm"></wd-button>
+            <wd-button
+              type="icon"
+              icon="add-circle"
+              color="#0D867F"
+              @click="openAddForm"
+            ></wd-button>
           </template>
         </CardTitle>
       </template>
 
-      <!-- 灞傜骇 tabs -->
-      <wd-tabs v-model="tab" slidable="always" class="tabs-container">
-        <block v-for="item in nodeList" :key="item.twistedLayer">
-          <wd-tab :title="item.twistedLayer" :name="item.twistedLayer">
-            <scroll-view class="content" scroll-y>
-              <SteelCoreCard
-                v-for="(m, i) in item.strandedWireDish"
-                :key="i"
-                :data="m"
-                @delete="handleDeleteCard(item, m)"
-              />
-            </scroll-view>
-          </wd-tab>
-        </block>
-      </wd-tabs>
-
-      <template #bottom>
-        <view class="flex justify-center items-center">
-          <wd-button block @click="save">
-            <text class="text-[#fff]">淇濆瓨</text>
-          </wd-button>
-        </view>
-      </template>
+      <view class="content">
+        <SteelCoreCard
+          v-for="item in steelData"
+          :key="item.id || item.monofilamentNumber"
+          :data="item"
+          @delete="handleDeleteCard(item)"
+        />
+      </view>
     </z-paging>
 
+    <view class="save-footer">
+      <wd-button block @click="save">
+        <text class="text-[#fff]">淇濆瓨</text>
+      </wd-button>
+    </view>
+
     <!-- 鏂板閽㈣姱寮圭獥 -->
-    <wd-popup v-model="showAddForm" position="bottom" custom-class="yl-popup">
+    <wd-popup v-model="showAddForm" position="bottom" :z-index="30" custom-class="yl-popup">
       <view class="action px-3">
         <wd-button type="text" @click="cancelAdd">鍙栨秷</wd-button>
         <wd-button type="text" @click="submitAdd">纭畾</wd-button>
@@ -70,59 +67,37 @@
 const addFormRef = ref();
 const scanRef = ref();
 const toast = useToast();
-const tab = ref("");
-const nodeList = ref<any[]>([]);
+const steelData = ref<any[]>([]);
 const showAddForm = ref(false);
 const isPageVisible = ref(false);
 
-// 鐩戝惉鏍囩鍒囨崲
-watch(tab, () => {
-  if (tab.value) {
-    getList();
-  }
-});
-
-// 鑾峰彇閽㈣姱灞傜骇鏁版嵁锛堝弬鐓C绔� getSteelCoreDishTypeByWire锛�
+// 閽㈣姱涓� PC 绔繚鎸佷竴鑷达紝鐩存帴浣跨敤鈥滃叾浠栤�濇暟鎹紝涓嶅啀鎸夌粸绾垮眰绾ч鐢ㄣ��
 const fetchSteelCoreData = async (wireId: number) => {
   const { code, data, msg } = await TwistApi.getSteelCoreDishTypeByWire(wireId);
   if (code === 200 && data) {
-    nodeList.value = data.nodeList || [];
-
-    // 杩藉姞"鍏朵粬"灞�
-    if (data.otherStrandedWireDish && data.otherStrandedWireDish.length > 0) {
-      nodeList.value.push({
-        strandedWireDish: data.otherStrandedWireDish,
-        twistedLayer: "鍏朵粬",
-        twistId: null,
-      });
-    }
-
-    // 璁剧疆榛樿绗竴灞�
-    if (nodeList.value.length > 0 && !tab.value) {
-      tab.value = nodeList.value[0].twistedLayer;
-      getList();
-    }
+    const list = Array.isArray(data.otherStrandedWireDish)
+      ? data.otherStrandedWireDish.map((item: any) => ({
+          ...item,
+          batchNo: item.batchNo || item.batchCode || "",
+          batchCode: item.batchCode || item.batchNo || "",
+          type: item.type || "閽㈣姱",
+          saleTwistId: null,
+        }))
+      : [];
+    pagingRef.value?.complete(list);
   } else {
-    toast.error(msg || "鑾峰彇閽㈣姱灞傜骇鏁版嵁澶辫触");
+    pagingRef.value?.complete([]);
+    toast.error(msg || "鑾峰彇閽㈣姱棰嗙敤鏁版嵁澶辫触");
   }
 };
 
-// 浠庡綋鍓嶉�変腑灞傝幏鍙栧垪琛ㄦ暟鎹�
 const getList = async () => {
-  const currentLayer = nodeList.value.find((node) => node.twistedLayer === tab.value);
-  if (currentLayer && currentLayer.strandedWireDish) {
-    pagingRef.value.complete(currentLayer.strandedWireDish);
-  } else {
-    pagingRef.value.complete([]);
-  }
+  if (!paramsId.value) return;
+  await fetchSteelCoreData(Number(paramsId.value));
 };
 
 // 鎵撳紑鏂板琛ㄥ崟
 const openAddForm = () => {
-  if (!tab.value) {
-    toast.error("璇峰厛閫夋嫨涓�涓眰");
-    return;
-  }
   if (addFormRef.value) {
     addFormRef.value.resetFormData();
   }
@@ -134,16 +109,8 @@
   const formData = await addFormRef.value.submit();
   if (!formData) return;
 
-  // 鏌ユ壘褰撳墠灞�
-  const currentLayer = nodeList.value.find((node) => node.twistedLayer === tab.value);
-  if (!currentLayer) {
-    toast.error("鏈壘鍒板綋鍓嶉�変腑鐨勫眰");
-    return;
-  }
-
-  // 鍚屽眰閲嶅妫�鏌�
   if (formData.monofilamentNumber) {
-    const exists = currentLayer.strandedWireDish?.some(
+    const exists = steelData.value.some(
       (item: any) => item.monofilamentNumber === formData.monofilamentNumber
     );
     if (exists) {
@@ -152,16 +119,12 @@
     }
   }
 
-  // 娣诲姞鍒板綋鍓嶅眰
-  if (!currentLayer.strandedWireDish) {
-    currentLayer.strandedWireDish = [];
-  }
-  currentLayer.strandedWireDish.push({
+  steelData.value.push({
     ...formData,
     wireId: paramsId.value,
+    saleTwistId: null,
   });
 
-  getList();
   showAddForm.value = false;
   toast.success("娣诲姞鎴愬姛");
 };
@@ -177,18 +140,13 @@
   }
 
   try {
-    if (!tab.value) {
-      toast.error("璇峰厛閫夋嫨涓�涓眰");
-      return;
-    }
-
-    const currentLayer = nodeList.value.find((node) => node.twistedLayer === tab.value);
-    if (!currentLayer) {
-      toast.error("鏈壘鍒板綋鍓嶉�変腑鐨勫眰");
-      return;
-    }
-
     const parseData = JSON.parse(code.code);
+
+    const batchNo = parseData.batchNo || parseData.batchCode || "";
+    if (!batchNo) {
+      toast.error("浜岀淮鐮侀敊璇紝缂哄皯鎵规鍙凤紒");
+      return;
+    }
 
     // 妫�鏌ュ繀闇�瀛楁
     const requiredFields = ["model", "supplier", "diskMaterial"];
@@ -198,9 +156,8 @@
       return;
     }
 
-    // 鍚屽眰閲嶅妫�鏌�
     if (parseData.monofilamentNumber) {
-      const exists = currentLayer.strandedWireDish?.some(
+      const exists = steelData.value.some(
         (item: any) => item.monofilamentNumber === parseData.monofilamentNumber
       );
       if (exists) {
@@ -213,19 +170,18 @@
     const newItem = {
       wireId: paramsId.value,
       diskMaterial: parseData.diskMaterial || "",
+      batchNo,
+      batchCode: batchNo,
       model: parseData.model || "",
       monofilamentNumber: parseData.monofilamentNumber || "",
       amount: parseData.oneLength || parseData.amount || "",
       weight: parseData.weight || "",
       supplier: parseData.supplier || "",
       type: "閽㈣姱",
+      saleTwistId: null,
     };
 
-    if (!currentLayer.strandedWireDish) {
-      currentLayer.strandedWireDish = [];
-    }
-    currentLayer.strandedWireDish.push(newItem);
-    getList();
+    steelData.value.push(newItem);
     toast.success("鎵爜鎴愬姛");
   } catch (error) {
     toast.error("浜岀淮鐮佸紓甯革紝璇锋洿鎹簩缁寸爜锛�");
@@ -239,25 +195,11 @@
 
 // 淇濆瓨锛堟壒閲忔彁浜わ級
 const save = () => {
-  // 妫�鏌ユ槸鍚︽湁鏂版暟鎹渶瑕佷繚瀛�
-  let hasNewData = false;
-  let newCount = 0;
+  const newCount = steelData.value.filter(
+    (item: { id?: number }) => item.id === undefined || item.id === null
+  ).length;
 
-  nodeList.value.forEach((node) => {
-    if (node.strandedWireDish && Array.isArray(node.strandedWireDish)) {
-      const hasNewInLayer = node.strandedWireDish.some(
-        (item: { id?: number }) => item.id === undefined || item.id === null
-      );
-      if (hasNewInLayer) {
-        hasNewData = true;
-        newCount += node.strandedWireDish.filter(
-          (item: { id?: number }) => item.id === undefined || item.id === null
-        ).length;
-      }
-    }
-  });
-
-  if (!hasNewData) {
+  if (!newCount) {
     toast.error("娌℃湁鏂扮殑閽㈣姱鏁版嵁闇�瑕佷繚瀛�");
     return;
   }
@@ -276,23 +218,21 @@
 
 // 鎵ц鎵归噺淇濆瓨
 const handleConfirmSave = async () => {
-  const newData: any[] = [];
-
-  nodeList.value.forEach((node) => {
-    // 闄勫姞涓婁笅绾� twistId
-    if (node.strandedWireDish && Array.isArray(node.strandedWireDish)) {
-      node.strandedWireDish.forEach((item: any) => {
-        item.saleTwistId = node.twistId;
-      });
-      const layerNewData = node.strandedWireDish.filter(
-        (item: { id?: number }) => item.id === undefined || item.id === null
-      );
-      newData.push(...layerNewData);
-    }
-  });
+  const newData = steelData.value
+    .filter((item: { id?: number }) => item.id === undefined || item.id === null)
+    .map((item) => ({
+      ...item,
+      saleTwistId: null,
+    }));
 
   if (newData.length === 0) {
     toast.error("娌℃湁鏂扮殑閽㈣姱鏁版嵁闇�瑕佷繚瀛�");
+    return;
+  }
+
+  const invalidBatchItem = newData.find((item) => !(item.batchNo || item.batchCode));
+  if (invalidBatchItem) {
+    toast.error(`${invalidBatchItem.monofilamentNumber || "閽㈣姱"}锛氳閫夋嫨鎵规鍙穈);
     return;
   }
 
@@ -307,7 +247,7 @@
 };
 
 // 鍒犻櫎鍗$墖
-const handleDeleteCard = async (layer: any, cardData: any) => {
+const handleDeleteCard = async (cardData: any) => {
   uni.showModal({
     title: "鎻愮ず",
     content: "纭畾瑕佸垹闄よ閽㈣姱鍚楋紵",
@@ -323,16 +263,12 @@
             }
           }
 
-          // 鍓嶇绉婚櫎
-          if (layer.strandedWireDish && Array.isArray(layer.strandedWireDish)) {
-            const index = layer.strandedWireDish.findIndex(
-              (item: any) => item.monofilamentNumber === cardData.monofilamentNumber
-            );
-            if (index !== -1) {
-              layer.strandedWireDish.splice(index, 1);
-              toast.success("鍒犻櫎鎴愬姛");
-              getList();
-            }
+          const index = steelData.value.findIndex(
+            (item: any) => item.monofilamentNumber === cardData.monofilamentNumber
+          );
+          if (index !== -1) {
+            steelData.value.splice(index, 1);
+            toast.success("鍒犻櫎鎴愬姛");
           }
         } catch (error: any) {
           toast.error(error.msg || "鍒犻櫎澶辫触");
@@ -345,7 +281,7 @@
 onLoad(async (options: any) => {
   uni.$on("scanSteelCore", getScanCode);
   paramsId.value = options.id;
-  fetchSteelCoreData(options.id);
+  fetchSteelCoreData(Number(options.id));
 });
 
 onShow(() => {
@@ -379,15 +315,22 @@
   color: #0d867f;
 }
 
-.tabs-container {
-  height: calc(100vh - 200px);
-  display: flex;
-  flex-direction: column;
+.content {
+  width: 100%;
+  padding-bottom: 76px;
+  box-sizing: border-box;
 }
 
-.content {
-  height: calc(100vh - 200px);
-  width: 100%;
+.save-footer {
+  position: fixed;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 20;
+  padding: 8px 12px calc(8px + env(safe-area-inset-bottom));
+  background: #ffffff;
+  border-top: 1px solid #eeeeee;
+  box-shadow: 0 -2px 10px rgb(0 0 0 / 6%);
 }
 
 :deep(.zp-paging-container) {
@@ -404,38 +347,7 @@
   flex-direction: column;
 }
 
-:deep(.wd-tabs) {
-  background: transparent !important;
-  height: 100%;
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-}
-
-:deep(.wd-tabs__nav) {
-  margin-bottom: 10px;
-  flex-shrink: 0;
-  position: sticky;
-  top: 0;
-  z-index: 10;
-  background: #f3f9f8;
-}
-
-:deep(.wd-tabs__content) {
-  flex: 1;
-  overflow: visible;
-}
-
-:deep(.wd-tab__pane) {
-  height: 100%;
-}
-
 :deep(.zp-paging-container-top) {
   flex-shrink: 0;
-}
-
-:deep(.zp-paging-container-bottom) {
-  flex-shrink: 0;
-  padding: 8px 12px;
 }
 </style>
diff --git a/src/pages/timely/detail.vue b/src/pages/timely/detail.vue
new file mode 100644
index 0000000..da96719
--- /dev/null
+++ b/src/pages/timely/detail.vue
@@ -0,0 +1,727 @@
+<template>
+  <view class="detail-page">
+    <view class="detail-header">
+      <CardTitle title="鏃舵晥浠诲姟鏄庣粏" :hideAction="false">
+        <template #action>
+          <wd-button
+            v-if="isWaiting && USE_TIME_SENSITIVE_MOCK"
+            size="small"
+            plain
+            @click="simulateScan"
+          >
+            妯℃嫙鎵爜
+          </wd-button>
+          <wd-button
+            v-if="isWaiting"
+            type="icon"
+            icon="scan"
+            color="#0D867F"
+            :loading="scanSubmitting"
+            @click="openScan"
+          />
+          <wd-button
+            v-if="isWaiting"
+            type="icon"
+            icon="add-circle"
+            color="#0D867F"
+            @click="openCandidatePopup"
+          />
+        </template>
+      </CardTitle>
+    </view>
+
+    <scroll-view class="detail-scroll" scroll-y>
+      <wd-card v-if="task.id" custom-class="summary-card">
+        <template #title>
+          <view class="summary-title">
+            <text>{{ task.taskCode || `浠诲姟-${task.id}` }}</text>
+            <wd-tag :type="statusType" size="small">{{ statusLabel }}</wd-tag>
+          </view>
+        </template>
+        <view class="summary-grid">
+          <view>
+            <text class="label">绫诲瀷</text>
+            {{ materialType }}
+          </view>
+          <view>
+            <text class="label">鏄庣粏</text>
+            {{ items.length }} 浠�
+          </view>
+          <view>
+            <text class="label">娓╁害</text>
+            {{ temperatureText }}
+          </view>
+          <view>
+            <text class="label">鏃堕暱</text>
+            {{ durationText }}
+          </view>
+          <view>
+            <text class="label">鏈哄彴</text>
+            {{ task.machineName || task.machine || "-" }}
+          </view>
+          <view>
+            <text class="label">宸℃鍛�</text>
+            {{ inspectorName }}
+          </view>
+        </view>
+      </wd-card>
+
+      <view class="section-title">
+        <text>鏃舵晥鏄庣粏锛坽{ items.length }}锛�</text>
+        <view v-if="isWaiting" class="section-actions">
+          <text class="section-link" @click="toggleSelectAll">
+            {{ allItemsSelected ? "鍙栨秷鍏ㄩ��" : "鍏ㄩ��" }}
+          </text>
+          <text class="section-link danger" @click="deleteSelectedItems">
+            鍒犻櫎鎵�閫�({{ selectedItems.length }})
+          </text>
+          <text class="section-link" @click="openCandidatePopup">鎵嬪姩娣诲姞</text>
+        </view>
+      </view>
+
+      <view v-if="loading" class="state-box">
+        <wd-loading />
+        <text>鍔犺浇涓�...</text>
+      </view>
+      <view v-else-if="!items.length" class="state-box">
+        <wd-status-tip image="content" tip="鏆傛棤鏃舵晥鏄庣粏" />
+      </view>
+      <view v-else class="item-list">
+        <wd-card v-for="(item, index) in items" :key="getItemKey(item, index)">
+          <template #title>
+            <view class="item-title">
+              <view class="item-title-main">
+                <wd-checkbox v-if="isWaiting" v-model="item._selected" @click.stop />
+                <text>{{ getMaterialCode(item) || `鏄庣粏${index + 1}` }}</text>
+                <wd-tag
+                  size="small"
+                  :type="
+                    normalizeMaterialType(item.materialType || materialType) === '鍗曚笣'
+                      ? 'primary'
+                      : 'success'
+                  "
+                >
+                  {{ normalizeMaterialType(item.materialType || materialType) }}
+                </wd-tag>
+              </view>
+              <wd-button
+                v-if="isWaiting"
+                type="text"
+                size="small"
+                custom-class="delete-button"
+                @click="deleteItem(item)"
+              >
+                鍒犻櫎
+              </wd-button>
+            </view>
+          </template>
+          <wd-cell-group>
+            <wd-cell title="瑙勬牸鍨嬪彿" :value="item.model || '-'" />
+            <wd-cell
+              v-if="normalizeMaterialType(item.materialType || materialType) === '鍗曚笣'"
+              title="閾濇潌鍨嬪彿"
+              :value="item.poleModel || item.rodModel || '-'"
+            />
+            <wd-cell
+              v-if="normalizeMaterialType(item.materialType || materialType) === '鍗曚笣'"
+              title="閾濇潌鍙�"
+              :value="item.rodNo || item.poleNumber || '-'"
+            />
+            <wd-cell title="娴佽浆鐮�/鎵规鍙�" :value="getMaterialCode(item) || '-'" />
+            <wd-cell title="閲嶉噺(kg)" :value="String(item.weight ?? '-')" />
+            <wd-cell v-if="item.supplier" title="鍘傚" :value="item.supplier" />
+          </wd-cell-group>
+        </wd-card>
+      </view>
+    </scroll-view>
+
+    <wd-popup
+      v-model="candidatePopupVisible"
+      position="bottom"
+      :z-index="30"
+      :close-on-click-modal="false"
+      custom-style="height: 86vh; border-radius: 24rpx 24rpx 0 0;"
+    >
+      <view class="popup-layout">
+        <view class="popup-header">
+          <view>
+            <text class="popup-title">鏂板鏃舵晥鏄庣粏</text>
+            <text class="popup-subtitle">{{ materialType }}</text>
+          </view>
+          <wd-icon name="close" size="20px" @click="candidatePopupVisible = false" />
+        </view>
+
+        <scroll-view class="candidate-list" scroll-y>
+          <view v-if="candidateLoading && !candidates.length" class="state-box">
+            <wd-loading />
+            <text>鏌ヨ涓�...</text>
+          </view>
+          <view v-else-if="!candidates.length" class="state-box">
+            <wd-status-tip image="search" tip="鏆傛棤鍙姞鍏ョ殑寰呮椂鏁堢墿鏂�" />
+          </view>
+          <view
+            v-for="(candidate, index) in candidates"
+            :key="getItemKey(candidate, index)"
+            class="candidate-item"
+            @click="candidate._selected = !candidate._selected"
+          >
+            <wd-checkbox v-model="candidate._selected" @click.stop />
+            <view class="candidate-info">
+              <text class="candidate-code">{{ getMaterialCode(candidate) || "-" }}</text>
+              <text>{{ candidate.model || "-" }} 路 {{ candidate.weight ?? "-" }} kg</text>
+              <text v-if="materialType === '鍗曚笣'" class="candidate-meta">
+                {{ candidate.poleModel || candidate.rodModel || "-" }} 路
+                {{ candidate.rodNo || candidate.poleNumber || "-" }}
+              </text>
+            </view>
+          </view>
+          <wd-button
+            v-if="candidates.length < candidateTotal"
+            block
+            plain
+            :loading="candidateLoading"
+            @click="loadMoreCandidates"
+          >
+            鍔犺浇鏇村
+          </wd-button>
+        </scroll-view>
+
+        <view class="popup-footer">
+          <text>宸查�� {{ selectedCandidates.length }} 鏉�</text>
+          <view class="footer-buttons">
+            <wd-button type="info" plain @click="candidatePopupVisible = false">鍙栨秷</wd-button>
+            <wd-button :loading="candidateSubmitting" @click="addSelectedCandidates">
+              纭畾娣诲姞
+            </wd-button>
+          </view>
+        </view>
+      </view>
+    </wd-popup>
+
+    <Scan ref="scanRef" emitName="scanTimeSensitiveItem" />
+    <wd-toast />
+  </view>
+</template>
+
+<script setup lang="ts">
+import { onHide, onLoad, onShow, onUnload } from "@dcloudio/uni-app";
+import { useToast } from "wot-design-uni";
+import type { TagType } from "wot-design-uni/components/wd-tag/types";
+import CardTitle from "@/components/card-title/index.vue";
+import Scan from "@/components/scan/index.vue";
+import TimeSensitiveTaskApi, {
+  USE_TIME_SENSITIVE_MOCK,
+  type TimelinessMaterialType,
+} from "@/api/timeSensitiveTask";
+import TwistApi from "@/api/product/twist";
+import OutboundApi from "@/api/product/outbound";
+
+const toast = useToast();
+const scanRef = ref();
+const taskId = ref<number | string>("");
+const task = ref<any>({});
+const items = ref<any[]>([]);
+const loading = ref(false);
+const scanSubmitting = ref(false);
+const pageVisible = ref(false);
+const candidatePopupVisible = ref(false);
+const candidateLoading = ref(false);
+const candidateSubmitting = ref(false);
+const candidates = ref<any[]>([]);
+const candidateTotal = ref(0);
+const candidatePage = ref(1);
+const candidatePageSize = 20;
+const materialType = computed<TimelinessMaterialType>(() =>
+  normalizeMaterialType(task.value.materialType)
+);
+const status = computed(() => normalizeStatus(task.value));
+const isWaiting = computed(() => status.value === 0);
+const statusLabel = computed(() => ["寰呭紑濮�", "鏃舵晥涓�", "鏃舵晥缁撴潫"][status.value] || "鏈煡");
+const statusType = computed<TagType>(() => {
+  if (status.value === 0) return "warning";
+  if (status.value === 1) return "primary";
+  if (status.value === 2) return "success";
+  return "default";
+});
+const inspectorName = computed(
+  () => task.value.inspectorName || task.value.routingInspectionUser || "-"
+);
+const temperatureText = computed(() => {
+  const value = task.value.setTemperature ?? task.value.temperature;
+  return value === undefined || value === "" ? "-" : `${value}鈩僠;
+});
+const durationText = computed(() => {
+  const value = task.value.agingDuration ?? task.value.duration;
+  return value === undefined || value === "" ? "-" : `${value} 灏忔椂`;
+});
+const selectedItems = computed(() => items.value.filter((item) => item._selected));
+const allItemsSelected = computed(
+  () => items.value.length > 0 && selectedItems.value.length === items.value.length
+);
+const selectedCandidates = computed(() => candidates.value.filter((item) => item._selected));
+
+function normalizeStatus(value: any) {
+  const key = String(value?.taskStatus ?? value?.timelinessStatus ?? value?.status ?? value ?? "")
+    .trim()
+    .toUpperCase();
+  if (["0", "WAITING", "PENDING", "寰呭紑濮�", "寰呮椂鏁�"].includes(key)) return 0;
+  if (["1", "RUNNING", "PROCESSING", "鏃舵晥涓�"].includes(key)) return 1;
+  if (["2", "FINISHED", "COMPLETED", "鏃舵晥缁撴潫"].includes(key)) return 2;
+  return Number(key);
+}
+
+function normalizeMaterialType(value: unknown): TimelinessMaterialType {
+  const text = String(value ?? "");
+  return text.includes("缁�") || text === "鎴愬搧" ? "缁炵嚎" : "鍗曚笣";
+}
+
+function getMaterialCode(item: any) {
+  return String(
+    item?.flowCode ||
+      item?.batchCode ||
+      item?.monofilamentNumber ||
+      item?.monofilamentnumber ||
+      item?.materialCode ||
+      item?.systemNo ||
+      item?.systemno ||
+      ""
+  ).trim();
+}
+
+function getCandidateId(item: any): number | string | undefined {
+  return item?.id ?? item?.awaitTimelinessId ?? item?.qualitativeProductionId ?? item?.outputId;
+}
+
+function getItemKey(item: any, index: number) {
+  return getCandidateId(item) || getMaterialCode(item) || index;
+}
+
+async function loadTask() {
+  if (!taskId.value) return;
+  loading.value = true;
+  try {
+    const [detailResult, itemsResult] = await Promise.all([
+      TimeSensitiveTaskApi.getDetail(taskId.value),
+      TimeSensitiveTaskApi.getItems(taskId.value),
+    ]);
+    task.value = detailResult.data || { id: taskId.value };
+    const itemData = itemsResult.data as any;
+    const list = Array.isArray(itemData)
+      ? itemData
+      : itemData?.records || itemData?.list || itemData?.items || [];
+    items.value = list.map((item: any) => ({ ...item, _selected: false }));
+  } catch (error: any) {
+    toast.error(error?.message || "鑾峰彇鏃舵晥浠诲姟鏄庣粏澶辫触");
+  } finally {
+    loading.value = false;
+  }
+}
+
+function showConfirm(title: string, content: string, confirmText = "纭畾") {
+  return new Promise<boolean>((resolve) => {
+    uni.showModal({
+      title,
+      content,
+      confirmText,
+      success: (result) => resolve(Boolean(result.confirm)),
+      fail: () => resolve(false),
+    });
+  });
+}
+
+async function deleteItems(rows: any[]) {
+  if (!isWaiting.value) return toast.error("鍙湁寰呭紑濮嬬殑浠诲姟鍙互鍒犻櫎鏄庣粏");
+  if (!rows.length) return toast.error("璇疯嚦灏戦�夋嫨涓�鏉℃椂鏁堟槑缁�");
+  const itemIds = rows
+    .map((item) => getCandidateId(item))
+    .filter((id): id is number | string => id !== undefined && id !== null && id !== "");
+  if (!itemIds.length) return toast.error("鏄庣粏ID涓嶅瓨鍦紝鏃犳硶鍒犻櫎");
+  const confirmed = await showConfirm(
+    "鍒犻櫎鏄庣粏",
+    `纭畾鍒犻櫎閫変腑鐨� ${itemIds.length} 鏉℃椂鏁堟槑缁嗗悧锛熷垹闄ゅ悗鍙噸鏂版坊鍔犮�俙,
+    "鍒犻櫎"
+  );
+  if (!confirmed) return;
+  try {
+    await TimeSensitiveTaskApi.deleteItems({ taskId: taskId.value, itemIds });
+    toast.success("鏃舵晥鏄庣粏鍒犻櫎鎴愬姛");
+    await loadTask();
+  } catch (error: any) {
+    toast.error(error?.message || "鍒犻櫎鏃舵晥鏄庣粏澶辫触");
+  }
+}
+
+function deleteItem(item: any) {
+  deleteItems([item]);
+}
+
+function deleteSelectedItems() {
+  deleteItems(selectedItems.value);
+}
+
+function toggleSelectAll() {
+  const nextValue = !allItemsSelected.value;
+  items.value.forEach((item) => {
+    item._selected = nextValue;
+  });
+}
+
+function openCandidatePopup() {
+  if (!isWaiting.value) return toast.error("鍙湁寰呭紑濮嬬殑浠诲姟鍙互鏂板鏄庣粏");
+  candidatePopupVisible.value = true;
+  loadCandidates(true);
+}
+
+async function loadCandidates(reset = false) {
+  if (reset) {
+    candidatePage.value = 1;
+    candidates.value = [];
+  }
+  candidateLoading.value = true;
+  try {
+    const { data } = await TimeSensitiveTaskApi.queryCandidates({
+      current: candidatePage.value,
+      size: candidatePageSize,
+      taskId: taskId.value,
+      materialType: materialType.value,
+    });
+    const rows = (data?.records || []).map((item: any) => ({ ...item, _selected: false }));
+    candidates.value = reset ? rows : [...candidates.value, ...rows];
+    candidateTotal.value = Number(data?.total || candidates.value.length);
+  } catch (error: any) {
+    toast.error(error?.message || "鏌ヨ鍙�夋椂鏁堟槑缁嗗け璐�");
+  } finally {
+    candidateLoading.value = false;
+  }
+}
+
+function loadMoreCandidates() {
+  if (candidateLoading.value || candidates.value.length >= candidateTotal.value) return;
+  candidatePage.value += 1;
+  loadCandidates(false);
+}
+
+async function addCandidates(rows: any[], successMessage = "鏃舵晥鏄庣粏鏂板鎴愬姛") {
+  const itemIds = rows
+    .map((item) => getCandidateId(item))
+    .filter((id): id is number | string => id !== undefined && id !== null && id !== "");
+  if (!itemIds.length) return toast.error("鎵�閫夌墿鏂欑己灏戞槑缁咺D");
+  await TimeSensitiveTaskApi.addItems({ taskId: taskId.value, itemIds });
+  toast.success(successMessage);
+  await loadTask();
+}
+
+async function addSelectedCandidates() {
+  if (!selectedCandidates.value.length) return toast.error("璇疯嚦灏戦�夋嫨涓�鏉″緟鏃舵晥鏁版嵁");
+  candidateSubmitting.value = true;
+  try {
+    await addCandidates(selectedCandidates.value);
+    candidatePopupVisible.value = false;
+  } catch (error: any) {
+    toast.error(error?.message || "鏂板鏃舵晥鏄庣粏澶辫触");
+  } finally {
+    candidateSubmitting.value = false;
+  }
+}
+
+function readScanPayload(event: any) {
+  const rawValue = event?.code ?? event;
+  const raw = typeof rawValue === "string" ? rawValue.trim() : JSON.stringify(rawValue || "");
+  let parsed: any = {};
+  if (raw.startsWith("{") || raw.startsWith("[")) {
+    try {
+      parsed = JSON.parse(raw);
+      if (Array.isArray(parsed)) parsed = parsed[0] || {};
+    } catch {
+      parsed = {};
+    }
+  }
+  const directCode = getMaterialCode(parsed);
+  const outputId = parsed.id ?? parsed.outPutId ?? parsed.outputId;
+  if (directCode) return { raw, parsed, outputId, materialCode: directCode };
+
+  const parts = raw.split(/[锛�,\s]/).filter(Boolean);
+  const singleWireCode = parts.find((part) => part.startsWith("75"));
+  return {
+    raw,
+    parsed,
+    outputId,
+    materialCode: singleWireCode || (!Object.keys(parsed).length ? raw : ""),
+  };
+}
+
+async function resolveScannedMaterialCode(scan: ReturnType<typeof readScanPayload>) {
+  if (scan.materialCode) return scan.materialCode;
+  if (scan.outputId === undefined || scan.outputId === null || scan.outputId === "") return "";
+  if (USE_TIME_SENSITIVE_MOCK) return String(scan.outputId);
+  try {
+    const result =
+      materialType.value === "鍗曚笣"
+        ? await TwistApi.getTagByIdLs({ outPutId: scan.outputId })
+        : await OutboundApi.getTagByIdJx({ outPutId: scan.outputId });
+    const data = Array.isArray(result.data) ? result.data[0] : result.data;
+    return getMaterialCode(data) || String(scan.outputId);
+  } catch {
+    return String(scan.outputId);
+  }
+}
+
+async function handleScan(event: any) {
+  if (!pageVisible.value || scanSubmitting.value) return;
+  if (!isWaiting.value) return toast.error("鍙湁寰呭紑濮嬬殑浠诲姟鍙互鎵爜鏂板鏄庣粏");
+  scanSubmitting.value = true;
+  try {
+    const scan = readScanPayload(event);
+    const materialCode = await resolveScannedMaterialCode(scan);
+    if (!materialCode) return toast.error("浜岀淮鐮佹牸寮忛敊璇紝鏈瘑鍒埌娴佽浆鐮佹垨鎵规鍙�");
+
+    const { data } = await TimeSensitiveTaskApi.queryCandidates({
+      current: 1,
+      size: 20,
+      taskId: taskId.value,
+      materialType: materialType.value,
+      materialCode,
+    });
+    const rows = data?.records || [];
+    const normalizedCode = materialCode.trim().toUpperCase();
+    const candidate =
+      rows.find((item: any) => getMaterialCode(item).toUpperCase() === normalizedCode) ||
+      rows.find((item: any) => String(getCandidateId(item)) === String(scan.outputId)) ||
+      (rows.length === 1 ? rows[0] : undefined);
+    if (!candidate) {
+      return toast.error(`鏈壘鍒板彲娣诲姞鐨�${materialType.value}锛屽彲鑳藉凡娣诲姞鎴栦笉鍦ㄥ緟鏃舵晥鑼冨洿`);
+    }
+    await addCandidates([candidate], "鎵爜娣诲姞鎴愬姛");
+  } catch (error: any) {
+    toast.error(error?.message || "鎵爜娣诲姞鏃舵晥鏄庣粏澶辫触");
+  } finally {
+    scanSubmitting.value = false;
+  }
+}
+
+function openScan() {
+  if (!isWaiting.value) return toast.error("鍙湁寰呭紑濮嬬殑浠诲姟鍙互鎵爜鏂板鏄庣粏");
+  scanRef.value?.triggerScan();
+}
+
+function simulateScan() {
+  const mockIds = materialType.value === "鍗曚笣" ? [9301, 9302, 9501] : [9401, 9402, 9201, 9202];
+  const existingIds = new Set(items.value.map((item) => String(getCandidateId(item))));
+  const nextId = mockIds.find((id) => !existingIds.has(String(id)));
+  if (!nextId) return toast.error(`娌℃湁鏇村鍙ā鎷熸壂鐮佺殑${materialType.value}`);
+  handleScan({ code: JSON.stringify({ id: nextId }) });
+}
+
+onLoad((options: any) => {
+  taskId.value = options?.id || "";
+  if (!taskId.value) {
+    toast.error("鏃舵晥浠诲姟ID涓嶈兘涓虹┖");
+    return;
+  }
+  uni.$off("scanTimeSensitiveItem", handleScan);
+  uni.$on("scanTimeSensitiveItem", handleScan);
+  loadTask();
+});
+
+onShow(() => {
+  pageVisible.value = true;
+});
+
+onHide(() => {
+  pageVisible.value = false;
+});
+
+onUnload(() => {
+  pageVisible.value = false;
+  uni.$off("scanTimeSensitiveItem", handleScan);
+});
+</script>
+
+<style lang="scss" scoped>
+.detail-page {
+  display: flex;
+  flex-direction: column;
+  height: calc(100vh - 50px);
+  background: #f3f9f8;
+}
+
+.detail-header {
+  flex-shrink: 0;
+  background: #ffffff;
+}
+
+.detail-scroll {
+  flex: 1;
+  height: 0;
+}
+
+:deep(.summary-card) {
+  margin: 10px;
+}
+
+.summary-title,
+.item-title,
+.item-title-main,
+.popup-header,
+.popup-footer,
+.footer-buttons {
+  display: flex;
+  gap: 8px;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.summary-title,
+.item-title-main {
+  min-width: 0;
+  font-weight: 600;
+}
+
+.summary-grid {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 12px 16px;
+  font-size: 13px;
+}
+
+.summary-grid > view {
+  display: flex;
+  flex-direction: column;
+  gap: 3px;
+}
+
+.label,
+.popup-subtitle,
+.candidate-meta {
+  font-size: 12px;
+  color: #999999;
+}
+
+.scan-tip {
+  display: flex;
+  gap: 8px;
+  align-items: flex-start;
+  padding: 10px 12px;
+  margin: 0 10px 10px;
+  font-size: 12px;
+  line-height: 1.5;
+  color: #287b75;
+  background: #eaf8f6;
+  border-radius: 8px;
+}
+
+.section-title {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 4px 12px 10px;
+  font-weight: 600;
+}
+
+.section-actions {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+  justify-content: flex-end;
+}
+
+.section-link {
+  font-size: 13px;
+  font-weight: 400;
+  color: #0d867f;
+}
+
+.section-link.danger {
+  color: #fa4350;
+}
+
+.item-list {
+  padding: 0 10px 24px;
+}
+
+.item-list :deep(.wd-card) {
+  margin: 0 0 10px;
+}
+
+:deep(.delete-button .wd-button__content) {
+  color: #fa4350;
+}
+
+.state-box {
+  display: flex;
+  gap: 8px;
+  align-items: center;
+  justify-content: center;
+  min-height: 130px;
+  color: #999999;
+}
+
+.popup-layout {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  background: #f7f8fa;
+}
+
+.popup-header {
+  flex-shrink: 0;
+  padding: 14px 16px;
+  background: #ffffff;
+  border-bottom: 1px solid #eeeeee;
+}
+
+.popup-header > view {
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+}
+
+.popup-title {
+  font-size: 17px;
+  font-weight: 600;
+}
+
+.candidate-list {
+  flex: 1;
+  height: 0;
+  padding: 10px 12px;
+  box-sizing: border-box;
+}
+
+.candidate-item {
+  display: flex;
+  gap: 10px;
+  align-items: center;
+  padding: 12px;
+  margin-bottom: 8px;
+  background: #ffffff;
+  border: 1px solid #eeeeee;
+  border-radius: 8px;
+}
+
+.candidate-info {
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  gap: 5px;
+  min-width: 0;
+  font-size: 13px;
+  color: #666666;
+}
+
+.candidate-code {
+  overflow: hidden;
+  font-weight: 600;
+  color: #222222;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.popup-footer {
+  flex-shrink: 0;
+  padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
+  font-size: 13px;
+  background: #ffffff;
+  border-top: 1px solid #eeeeee;
+}
+</style>
diff --git a/src/pages/timely/index.vue b/src/pages/timely/index.vue
index ab55d7b..3063f91 100644
--- a/src/pages/timely/index.vue
+++ b/src/pages/timely/index.vue
@@ -1,108 +1,956 @@
 <template>
-  <view class="list_box">
-    <z-paging ref="pagingRef" v-model="cardList" :fixed="false" :auto-show-back-to-top="true">
+  <view class="timely-page">
+    <z-paging
+      ref="pagingRef"
+      v-model="taskList"
+      :fixed="false"
+      :auto-show-back-to-top="true"
+      @query="queryTaskList"
+    >
       <template #top>
-        <CardTitle title="鏃舵晥鎶ュ伐" :hideAction="false">
-          <template #action>
-            <wd-button type="icon" icon="scan" color="#0D867F" @click="openScan"></wd-button>
-          </template>
-        </CardTitle>
-      </template>
-      <MonofilCard v-for="(item, index) in cardList" :key="index" :data="item" />
-      <template #bottom>
-        <view class="flex justify-center items-center">
-          <wd-button block @click="save">
-            <text class="text-[#fff]">淇濆瓨</text>
-          </wd-button>
+        <view class="page-top">
+          <CardTitle title="鏃舵晥浠诲姟鍙拌处" :hideAction="false">
+            <template #action>
+              <wd-tag type="warning" size="small">Mock</wd-tag>
+              <wd-button size="small" icon="add" @click="openCreatePopup">鏂板浠诲姟</wd-button>
+            </template>
+          </CardTitle>
+
+          <view class="date-filter-card">
+            <view class="date-filter-heading">
+              <view class="date-filter-title">
+                <view class="date-filter-icon">
+                  <wd-icon name="calendar" size="18px" color="#078568" />
+                </view>
+                <view>
+                  <view class="date-filter-name">閫夋嫨鏃ユ湡</view>
+                  <view class="date-filter-tip">鎸変换鍔″彂璧锋棩鏈熸煡璇�</view>
+                </view>
+              </view>
+              <text v-if="hasDateFilter" class="date-filter-clear" @click="resetFilters">娓呴櫎</text>
+            </view>
+            <view class="date-filter-fields">
+              <view class="date-filter-field">
+                <wd-datetime-picker v-model="filters.startTimestamp" type="date">
+                  <view class="date-picker-trigger">
+                    <text class="date-picker-label">寮�濮嬫棩鏈�</text>
+                    <view class="date-picker-value">
+                      <text :class="{ 'is-placeholder': !filters.startTimestamp }">
+                        {{
+                          filters.startTimestamp
+                            ? formatDateOnly(Number(filters.startTimestamp))
+                            : "璇烽�夋嫨"
+                        }}
+                      </text>
+                      <wd-icon name="arrow-right" size="13px" color="#aebbb7" />
+                    </view>
+                  </view>
+                </wd-datetime-picker>
+              </view>
+              <view class="date-filter-separator">鑷�</view>
+              <view class="date-filter-field">
+                <wd-datetime-picker v-model="filters.endTimestamp" type="date">
+                  <view class="date-picker-trigger">
+                    <text class="date-picker-label">缁撴潫鏃ユ湡</text>
+                    <view class="date-picker-value">
+                      <text :class="{ 'is-placeholder': !filters.endTimestamp }">
+                        {{
+                          filters.endTimestamp
+                            ? formatDateOnly(Number(filters.endTimestamp))
+                            : "璇烽�夋嫨"
+                        }}
+                      </text>
+                      <wd-icon name="arrow-right" size="13px" color="#aebbb7" />
+                    </view>
+                  </view>
+                </wd-datetime-picker>
+              </view>
+            </view>
+            <view class="date-filter-actions">
+              <wd-button size="small" type="info" plain @click="resetFilters">閲嶇疆</wd-button>
+              <wd-button size="small" icon="search" @click="applyFilters">鏌ヨ浠诲姟</wd-button>
+            </view>
+          </view>
         </view>
       </template>
+
+      <view class="task-list">
+        <wd-card v-for="task in taskList" :key="task.id" custom-class="task-card">
+          <template #title>
+            <view class="card-title" @click="openDetail(task)">
+              <view class="title-main">
+                <text class="task-code">{{ task.taskCode || `浠诲姟-${task.id}` }}</text>
+                <wd-tag
+                  size="small"
+                  :type="
+                    normalizeMaterialType(task.materialType) === '鍗曚笣' ? 'primary' : 'success'
+                  "
+                >
+                  {{ normalizeMaterialType(task.materialType) }}
+                </wd-tag>
+              </view>
+              <wd-tag size="small" :type="getStatusType(task)">
+                {{ getStatusLabel(task) }}
+              </wd-tag>
+            </view>
+          </template>
+
+          <view @click="openDetail(task)">
+            <view class="info-grid">
+              <view class="info-item">
+                <text class="info-label">鍙戣捣浠诲姟浜�</text>
+                <text>{{ getInitiator(task) }}</text>
+              </view>
+              <view class="info-item">
+                <text class="info-label">宸℃鍛�</text>
+                <text>{{ getInspector(task) }}</text>
+              </view>
+              <view class="info-item">
+                <text class="info-label">璁惧畾娓╁害</text>
+                <text>{{ getTemperature(task) }}</text>
+              </view>
+              <view class="info-item">
+                <text class="info-label">鏃舵晥鏃堕暱</text>
+                <text>{{ getDuration(task) }}</text>
+              </view>
+              <view class="info-item">
+                <text class="info-label">璁惧畾鏈哄彴</text>
+                <text>{{ task.machineName || task.machine || "-" }}</text>
+              </view>
+              <view class="info-item">
+                <text class="info-label">鏃舵晥浠舵暟</text>
+                <text>{{ task.itemCount ?? task.count ?? task.detailCount ?? 0 }} 浠�</text>
+              </view>
+            </view>
+            <view class="time-row">
+              <text class="info-label">鍙戣捣鏃堕棿</text>
+              <text>{{ task.initiateTime || task.startTime || task.createTime || "-" }}</text>
+            </view>
+            <view v-if="task.furnaceInTime || task.inTime" class="time-row">
+              <text class="info-label">杩涚倝鏃堕棿</text>
+              <text>{{ task.furnaceInTime || task.inTime }}</text>
+            </view>
+            <view v-if="task.furnaceOutTime || task.outTime" class="time-row">
+              <text class="info-label">鍑虹倝鏃堕棿</text>
+              <text>{{ task.furnaceOutTime || task.outTime }}</text>
+            </view>
+          </view>
+
+          <template #footer>
+            <view class="task-actions">
+              <wd-button size="small" plain @click="openDetail(task)">鏄庣粏</wd-button>
+              <wd-button
+                v-if="isWaiting(task)"
+                size="small"
+                plain
+                type="warning"
+                @click="openEditPopup(task)"
+              >
+                缂栬緫
+              </wd-button>
+              <wd-button v-if="isWaiting(task)" size="small" plain @click="startTask(task)">
+                寮�濮嬫椂鏁�
+              </wd-button>
+              <wd-button
+                v-if="isWaiting(task)"
+                size="small"
+                plain
+                type="error"
+                @click="deleteTask(task)"
+              >
+                鍒犻櫎
+              </wd-button>
+              <wd-button
+                v-if="isRunning(task)"
+                size="small"
+                plain
+                type="error"
+                @click="endTask(task)"
+              >
+                缁撴潫鏃舵晥
+              </wd-button>
+              <wd-button
+                v-if="!isWaiting(task)"
+                size="small"
+                plain
+                type="success"
+                @click="openInspection(task)"
+              >
+                宸℃璁板綍
+              </wd-button>
+            </view>
+          </template>
+        </wd-card>
+      </view>
     </z-paging>
-    <Scan ref="scanRef" emitName="scanTime" />
+
+    <wd-popup
+      v-model="taskPopupVisible"
+      position="bottom"
+      :z-index="30"
+      :close-on-click-modal="false"
+      custom-style="height: 88vh; border-radius: 24rpx 24rpx 0 0;"
+    >
+      <view class="popup-layout">
+        <view class="popup-header">
+          <text class="popup-title">{{ editingTaskId ? "缂栬緫鏃舵晥浠诲姟" : "鏂板鏃舵晥浠诲姟" }}</text>
+          <wd-icon name="close" size="20px" @click="taskPopupVisible = false" />
+        </view>
+        <scroll-view class="popup-body" scroll-y>
+          <wd-form ref="taskFormRef" :model="taskForm">
+            <wd-cell-group border>
+              <wd-picker
+                v-model="taskForm.materialType"
+                :columns="materialTypeOptions"
+                label="浠诲姟绫诲瀷"
+                required
+                :z-index="40"
+              />
+              <wd-picker
+                v-model="taskForm.initiatorName"
+                :columns="userOptions"
+                label="鍙戣捣浠诲姟浜�"
+                placeholder="璇烽�夋嫨"
+                required
+                :loading="optionsLoading"
+                :z-index="40"
+              />
+              <wd-datetime-picker
+                v-model="taskForm.initiateTimestamp"
+                label="鍙戣捣鏃堕棿"
+                type="datetime"
+                required
+                use-second
+                :z-index="40"
+              />
+              <wd-picker
+                v-model="taskForm.inspectorName"
+                :columns="userOptions"
+                label="宸℃鍛�"
+                placeholder="璇烽�夋嫨"
+                required
+                :loading="optionsLoading"
+                :z-index="40"
+              />
+              <wd-input
+                v-model="taskForm.setTemperature"
+                label="璁惧畾娓╁害(鈩�)"
+                type="digit"
+                required
+                placeholder="璇疯緭鍏�"
+              />
+              <wd-cell title="鏃舵晥鏃堕暱(灏忔椂)" required />
+              <view class="duration-row">
+                <wd-input
+                  v-model="taskForm.durationRise"
+                  type="digit"
+                  placeholder="鍗囨俯"
+                  no-border
+                />
+                <text>+</text>
+                <wd-input
+                  v-model="taskForm.durationHold"
+                  type="digit"
+                  placeholder="淇濇俯"
+                  no-border
+                />
+                <text>+</text>
+                <wd-input
+                  v-model="taskForm.durationSoak"
+                  type="digit"
+                  placeholder="鐒栫倝"
+                  no-border
+                />
+              </view>
+              <wd-picker
+                v-model="taskForm.machineName"
+                :columns="machineOptions"
+                label="璁惧畾鏈哄彴"
+                placeholder="璇烽�夋嫨鏃舵晥鏈哄彴"
+                required
+                :loading="optionsLoading"
+                :z-index="40"
+              />
+            </wd-cell-group>
+          </wd-form>
+        </scroll-view>
+        <view class="popup-footer">
+          <wd-button type="info" plain @click="taskPopupVisible = false">鍙栨秷</wd-button>
+          <wd-button :loading="taskSubmitting" @click="submitTask">
+            {{ editingTaskId ? "淇濆瓨淇敼" : "纭畾鏂板" }}
+          </wd-button>
+        </view>
+      </view>
+    </wd-popup>
+
     <wd-toast />
   </view>
 </template>
 
 <script setup lang="ts">
+import { onShow } from "@dcloudio/uni-app";
+import { useToast } from "wot-design-uni";
+import type { TagType } from "wot-design-uni/components/wd-tag/types";
 import CardTitle from "@/components/card-title/index.vue";
 import zPaging from "@/components/z-paging/z-paging.vue";
-import Scan from "@/components/scan/index.vue";
-import MonofilCard from "../production/twist/components/MonofilCard.vue";
-import TwistApi from "@/api/product/twist";
-import { useToast } from "wot-design-uni";
+import TimeSensitiveTaskApi, {
+  type TimelinessMaterialType,
+  type TimeSensitiveTaskForm,
+} from "@/api/timeSensitiveTask";
+import { useUserStore } from "@/store/modules/user";
 
-const pagingRef = ref();
-const scanRef = ref();
 const toast = useToast();
-const cardList = ref<any>([]);
+const userStore = useUserStore();
+const pagingRef = ref();
+const taskFormRef = ref();
+const taskList = ref<any[]>([]);
+const taskPopupVisible = ref(false);
+const taskSubmitting = ref(false);
+const optionsLoading = ref(false);
+const editingTaskId = ref<number | string | undefined>();
+const pageReady = ref(false);
 
-const getScanCode = async (code: any) => {
-  console.log("鏃舵晥鎵爜鍥炶皟:", code);
-  console.log("json:", JSON.parse(code.code));
-  console.log("id:=============", JSON.parse(code.code).id);
-  const { data } = await TwistApi.getScarn({
-    outPutId: JSON.parse(code.code).id,
-  });
-  console.log("=======璇锋眰========", data);
-  const exists = cardList.value.some(
-    (item: any) => item.monofilamentNumber === data.monofilamentNumber
-  );
-  if (!exists) {
-    const { id, outPutId, wireId, oneLength, ...rest } = data;
-    console.log("sb", {
-      outputId: id,
-      amount: oneLength,
-      ongLength: oneLength,
-      ...rest,
-    });
-    cardList.value.push({
-      outputId: id,
-      amount: oneLength,
-      ongLength: oneLength,
-      ...rest,
-    });
-    pagingRef.value.complete(cardList.value);
-  } else {
-    toast.error("璇ュ崟涓濆凡棰嗙敤锛岃鍕块噸澶嶆壂鐮�");
-  }
-};
-const save = async () => {
-  const { code } = await TwistApi.saveAge(cardList.value);
-  if (code == 200) {
-    toast.success("淇濆瓨鎴愬姛");
-    pagingRef.value.refresh();
-  } else {
-    toast.error("淇濆瓨澶辫触");
-  }
-};
+const materialTypeOptions = [
+  { label: "鍗曚笣", value: "鍗曚笣" },
+  { label: "缁炵嚎", value: "缁炵嚎" },
+];
 
-const openScan = () => {
-  scanRef.value.triggerScan();
-};
+const statusOptions = [
+  { label: "寰呭紑濮�", value: 0 },
+  { label: "鏃舵晥涓�", value: 1 },
+  { label: "鏃舵晥缁撴潫", value: 2 },
+];
 
-// 纭繚鍏堢Щ闄ゅ啀娣诲姞鐩戝惉
-const setupScanListener = () => {
-  uni.$off("scanTime", getScanCode); // 鍏堢Щ闄ゆ棫鐨�
-  uni.$on("scanTime", getScanCode); // 鍐嶆坊鍔犳柊鐨�
-};
+const filters = reactive({
+  startTimestamp: "" as "" | number,
+  endTimestamp: "" as "" | number,
+});
+const hasDateFilter = computed(() => Boolean(filters.startTimestamp || filters.endTimestamp));
 
-onMounted(() => {
-  setupScanListener();
-  console.log("椤甸潰鏄剧ず - 鎵爜鐩戝惉宸茶缃�");
+const taskForm = reactive({
+  materialType: "鍗曚笣" as TimelinessMaterialType,
+  initiatorName: "",
+  initiateTimestamp: Date.now() as number,
+  setTemperature: "" as string | number,
+  durationRise: 2 as string | number,
+  durationHold: "" as string | number,
+  durationSoak: "" as string | number,
+  machineName: "",
+  inspectorName: "",
 });
 
-onUnmounted(() => {
-  // 寮�鍚箍鎾洃鍚簨浠�
-  uni.$off("scanTime", getScanCode);
-  console.log("椤甸潰鍗歌浇 - 鎵爜鐩戝惉宸茬Щ闄�");
+const userOptions = ref<Array<{ label: string; value: string }>>([]);
+const machineOptions = ref<Array<{ label: string; value: string }>>([]);
+
+function pad(value: number) {
+  return String(value).padStart(2, "0");
+}
+
+function formatDateTime(value: number | Date) {
+  const date = value instanceof Date ? value : new Date(value);
+  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(
+    date.getHours()
+  )}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
+}
+
+function formatDate(value: number, endOfDay = false) {
+  const date = new Date(value);
+  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${
+    endOfDay ? "23:59:59" : "00:00:00"
+  }`;
+}
+
+function formatDateOnly(value: number) {
+  return formatDate(value).slice(0, 10);
+}
+
+function parseDateTime(value: unknown) {
+  if (!value) return Date.now();
+  const timestamp = new Date(String(value).replace(/-/g, "/")).getTime();
+  return Number.isFinite(timestamp) ? timestamp : Date.now();
+}
+
+function normalizeStatus(task: any) {
+  const key = String(task?.taskStatus ?? task?.timelinessStatus ?? task?.status ?? "")
+    .trim()
+    .toUpperCase();
+  if (["0", "WAITING", "PENDING", "寰呭紑濮�", "寰呮椂鏁�"].includes(key)) return 0;
+  if (["1", "RUNNING", "PROCESSING", "鏃舵晥涓�"].includes(key)) return 1;
+  if (["2", "FINISHED", "COMPLETED", "鏃舵晥缁撴潫"].includes(key)) return 2;
+  return Number(key);
+}
+
+function normalizeMaterialType(value: unknown): TimelinessMaterialType {
+  const text = String(value ?? "");
+  return text.includes("缁�") || text === "鎴愬搧" ? "缁炵嚎" : "鍗曚笣";
+}
+
+function isWaiting(task: any) {
+  return normalizeStatus(task) === 0;
+}
+
+function isRunning(task: any) {
+  return normalizeStatus(task) === 1;
+}
+
+function getStatusLabel(task: any) {
+  return statusOptions.find((item) => item.value === normalizeStatus(task))?.label || "鏈煡";
+}
+
+function getStatusType(task: any): TagType {
+  const status = normalizeStatus(task);
+  if (status === 0) return "warning";
+  if (status === 1) return "primary";
+  if (status === 2) return "success";
+  return "default";
+}
+
+function getInitiator(task: any) {
+  return task.initiatorName || task.userName || task.createUserName || "-";
+}
+
+function getInspector(task: any) {
+  return task.inspectorName || task.routingInspectionUser || "-";
+}
+
+function getTemperature(task: any) {
+  const value = task.setTemperature ?? task.temperature;
+  return value === undefined || value === "" ? "-" : `${value}鈩僠;
+}
+
+function getDuration(task: any) {
+  const value = task.agingDuration ?? task.duration;
+  return value === undefined || value === "" ? "-" : `${value} 灏忔椂`;
+}
+
+async function queryTaskList(pageNo: number, pageSize: number) {
+  try {
+    const { data } = await TimeSensitiveTaskApi.queryPage({
+      current: pageNo,
+      size: pageSize,
+      startTime: filters.startTimestamp ? formatDate(Number(filters.startTimestamp)) : undefined,
+      endTime: filters.endTimestamp ? formatDate(Number(filters.endTimestamp), true) : undefined,
+    });
+    pagingRef.value?.complete(data?.records || []);
+  } catch (error: any) {
+    pagingRef.value?.complete(false);
+    toast.error(error?.message || "鑾峰彇鏃舵晥浠诲姟澶辫触");
+  } finally {
+    pageReady.value = true;
+  }
+}
+
+function applyFilters() {
+  if (
+    filters.startTimestamp &&
+    filters.endTimestamp &&
+    Number(filters.startTimestamp) > Number(filters.endTimestamp)
+  ) {
+    toast.error("寮�濮嬫棩鏈熶笉鑳芥櫄浜庣粨鏉熸棩鏈�");
+    return;
+  }
+  pagingRef.value?.reload();
+}
+
+function resetFilters() {
+  Object.assign(filters, {
+    startTimestamp: "",
+    endTimestamp: "",
+  });
+  applyFilters();
+}
+
+function getCurrentPerson() {
+  const user = userStore.userInfo as any;
+  return user?.nickName || user?.nickname || user?.userName || user?.username || "";
+}
+
+function getPersonName(item: any) {
+  return String(item?.nickName || item?.nickname || item?.userName || item?.username || "");
+}
+
+function getMachineName(item: any) {
+  return String(item?.deviceModel || item?.device_model || item?.deviceName || item?.name || "");
+}
+
+async function loadOptions() {
+  if (userOptions.value.length && machineOptions.value.length) return;
+  optionsLoading.value = true;
+  try {
+    const [usersResult, machinesResult] = await Promise.all([
+      TimeSensitiveTaskApi.getUsers(),
+      TimeSensitiveTaskApi.getMachines(),
+    ]);
+    userOptions.value = (usersResult.data || [])
+      .map((item: any) => getPersonName(item))
+      .filter(Boolean)
+      .map((name: string) => ({ label: name, value: name }));
+    machineOptions.value = (machinesResult.data || [])
+      .map((item: any) => getMachineName(item))
+      .filter(Boolean)
+      .map((name: string) => ({ label: name, value: name }));
+  } catch (error: any) {
+    toast.error(error?.message || "鑾峰彇浜哄憳鎴栨椂鏁堟満鍙板け璐�");
+  } finally {
+    optionsLoading.value = false;
+  }
+}
+
+function resetTaskForm() {
+  const currentPerson = getCurrentPerson();
+  Object.assign(taskForm, {
+    materialType: "鍗曚笣",
+    initiatorName: currentPerson,
+    initiateTimestamp: Date.now(),
+    setTemperature: "",
+    durationRise: 2,
+    durationHold: "",
+    durationSoak: "",
+    machineName: "",
+    inspectorName: currentPerson,
+  });
+}
+
+function parseDuration(value: unknown) {
+  const parts = String(value ?? "")
+    .replace(/灏忔椂/g, "")
+    .split("+")
+    .map((item) => Number(item.trim()));
+  if (parts.length >= 3 && parts.every(Number.isFinite)) return parts;
+  return [2, Number.isFinite(parts[0]) ? parts[0] : "", ""];
+}
+
+async function openCreatePopup() {
+  editingTaskId.value = undefined;
+  resetTaskForm();
+  taskPopupVisible.value = true;
+  await loadOptions();
+}
+
+async function openEditPopup(task: any) {
+  if (!isWaiting(task)) return;
+  try {
+    const { data } = await TimeSensitiveTaskApi.getDetail(task.id);
+    const detail = { ...task, ...(data || {}) };
+    const duration = parseDuration(detail.agingDuration ?? detail.duration);
+    editingTaskId.value = detail.id;
+    Object.assign(taskForm, {
+      materialType: normalizeMaterialType(detail.materialType),
+      initiatorName: getInitiator(detail) === "-" ? "" : getInitiator(detail),
+      initiateTimestamp: parseDateTime(
+        detail.initiateTime || detail.startTime || detail.createTime
+      ),
+      setTemperature: detail.setTemperature ?? detail.temperature ?? "",
+      durationRise: duration[0],
+      durationHold: duration[1],
+      durationSoak: duration[2],
+      machineName: detail.machineName || detail.machine || "",
+      inspectorName: getInspector(detail) === "-" ? "" : getInspector(detail),
+    });
+    taskPopupVisible.value = true;
+    await loadOptions();
+  } catch (error: any) {
+    toast.error(error?.message || "鑾峰彇鏃舵晥浠诲姟璇︽儏澶辫触");
+  }
+}
+
+function buildTaskPayload(): TimeSensitiveTaskForm | null {
+  if (!taskForm.materialType) return (toast.error("璇烽�夋嫨浠诲姟绫诲瀷"), null);
+  if (!taskForm.initiatorName) return (toast.error("璇烽�夋嫨鍙戣捣浠诲姟浜�"), null);
+  if (!taskForm.initiateTimestamp) return (toast.error("璇烽�夋嫨鍙戣捣鏃堕棿"), null);
+  if (taskForm.setTemperature === "") return (toast.error("璇疯緭鍏ヨ瀹氭俯搴�"), null);
+  if (taskForm.durationHold === "") return (toast.error("璇疯緭鍏ヤ繚娓╂椂闀�"), null);
+  if (!taskForm.machineName) return (toast.error("璇烽�夋嫨璁惧畾鏈哄彴"), null);
+  if (!taskForm.inspectorName) return (toast.error("璇烽�夋嫨宸℃鍛�"), null);
+
+  return {
+    materialType: taskForm.materialType,
+    initiatorName: taskForm.initiatorName,
+    initiateTime: formatDateTime(Number(taskForm.initiateTimestamp)),
+    setTemperature: taskForm.setTemperature,
+    agingDuration: `${taskForm.durationRise || 0}+${taskForm.durationHold || 0}+${
+      taskForm.durationSoak || 0
+    }`,
+    machineName: taskForm.machineName,
+    inspectorName: taskForm.inspectorName,
+  };
+}
+
+async function submitTask() {
+  const payload = buildTaskPayload();
+  if (!payload || taskSubmitting.value) return;
+  taskSubmitting.value = true;
+  try {
+    const result = editingTaskId.value
+      ? await TimeSensitiveTaskApi.update({ id: editingTaskId.value, ...payload })
+      : await TimeSensitiveTaskApi.create(payload);
+    taskPopupVisible.value = false;
+    toast.success(editingTaskId.value ? "鏃舵晥浠诲姟淇敼鎴愬姛" : "鏂板鎴愬姛锛岃娣诲姞鏃舵晥鏄庣粏");
+    pagingRef.value?.reload();
+    if (!editingTaskId.value) {
+      const resultData = result.data;
+      const newId =
+        resultData && typeof resultData === "object"
+          ? resultData.id || resultData.taskId
+          : resultData;
+      if (newId) {
+        setTimeout(() => {
+          uni.navigateTo({ url: `/pages/timely/detail?id=${newId}` });
+        }, 500);
+      }
+    }
+  } catch (error: any) {
+    toast.error(error?.message || `${editingTaskId.value ? "淇敼" : "鏂板"}鏃舵晥浠诲姟澶辫触`);
+  } finally {
+    taskSubmitting.value = false;
+  }
+}
+
+function showConfirm(title: string, content: string, confirmText = "纭畾") {
+  return new Promise<boolean>((resolve) => {
+    uni.showModal({
+      title,
+      content,
+      confirmText,
+      success: (result) => resolve(Boolean(result.confirm)),
+      fail: () => resolve(false),
+    });
+  });
+}
+
+async function deleteTask(task: any) {
+  if (!isWaiting(task)) return toast.error("鍙湁寰呭紑濮嬬殑浠诲姟鍙互鍒犻櫎");
+  const confirmed = await showConfirm(
+    "鍒犻櫎浠诲姟",
+    `纭畾鍒犻櫎鏃舵晥浠诲姟鈥�${task.taskCode || task.id}鈥濆悧锛熶换鍔℃槑缁嗕篃浼氫竴骞跺垹闄ゃ�俙,
+    "鍒犻櫎"
+  );
+  if (!confirmed) return;
+  try {
+    await TimeSensitiveTaskApi.delete(task.id);
+    toast.success("鏃舵晥浠诲姟鍒犻櫎鎴愬姛");
+    pagingRef.value?.reload();
+  } catch (error: any) {
+    toast.error(error?.message || "鍒犻櫎鏃舵晥浠诲姟澶辫触");
+  }
+}
+
+async function startTask(task: any) {
+  if (!isWaiting(task)) return;
+  try {
+    const [detailResult, itemsResult] = await Promise.all([
+      TimeSensitiveTaskApi.getDetail(task.id),
+      TimeSensitiveTaskApi.getItems(task.id),
+    ]);
+    const detail = { ...task, ...(detailResult.data || {}) };
+    const items = Array.isArray(itemsResult.data)
+      ? itemsResult.data
+      : (itemsResult.data as any)?.records || [];
+    const payload = {
+      id: task.id,
+      materialType: normalizeMaterialType(detail.materialType),
+      initiatorName: getInitiator(detail) === "-" ? "" : getInitiator(detail),
+      initiateTime: detail.initiateTime || detail.startTime || detail.createTime || "",
+      setTemperature: detail.setTemperature ?? detail.temperature ?? "",
+      agingDuration: detail.agingDuration ?? detail.duration ?? "",
+      machineName: detail.machineName || detail.machine || "",
+      furnaceInTime: detail.furnaceInTime || formatDateTime(new Date()),
+      inspectorName: getInspector(detail) === "-" ? "" : getInspector(detail),
+    };
+    if (
+      Object.values(payload).some(
+        (value) => value === undefined || value === null || String(value) === ""
+      )
+    ) {
+      return toast.error("浠诲姟鍩虹淇℃伅涓嶅畬鏁达紝璇峰厛缂栬緫瀹屽杽");
+    }
+    if (!items.length) return toast.error("褰撳墠浠诲姟娌℃湁鏃舵晥鏄庣粏锛屼笉鑳藉紑濮嬫椂鏁�");
+    const confirmed = await showConfirm(
+      "寮�濮嬫椂鏁�",
+      `浠诲姟锛�${task.taskCode || task.id}\n鏄庣粏锛�${items.length}浠禱n鏈哄彴锛�${payload.machineName}\n纭寮�濮嬫椂鏁堝悧锛焋,
+      "寮�濮�"
+    );
+    if (!confirmed) return;
+    await TimeSensitiveTaskApi.start(payload);
+    toast.success("鏃舵晥宸插紑濮�");
+    pagingRef.value?.reload();
+  } catch (error: any) {
+    toast.error(error?.message || "寮�濮嬫椂鏁堝け璐�");
+  }
+}
+
+function parseCanEnd(data: any) {
+  if (typeof data === "boolean") return data;
+  return Boolean(data?.canEnd ?? data?.reachedDuration);
+}
+
+async function endTask(task: any) {
+  if (!isRunning(task)) return;
+  try {
+    const { data } = await TimeSensitiveTaskApi.checkEnd(task.id);
+    const canEnd = parseCanEnd(data);
+    const durationText =
+      data?.elapsedMinutes != null && data?.requiredMinutes != null
+        ? `褰撳墠宸叉椂鏁� ${data.elapsedMinutes} 鍒嗛挓锛岃瀹氭椂闀� ${data.requiredMinutes} 鍒嗛挓銆俙
+        : "褰撳墠灏氭湭杈惧埌璁惧畾鏃舵晥鏃堕暱銆�";
+    const confirmed = await showConfirm(
+      canEnd ? "缁撴潫鏃舵晥" : "鏃堕暱鏈揪鍒�",
+      canEnd
+        ? "宸茶揪鍒拌瀹氭椂鏁堟椂闀匡紝纭畾缁撴潫鏃舵晥鍚楋紵"
+        : `${data?.message || durationText}鏄惁浠嶈缁撴潫鏃舵晥锛焋,
+      canEnd ? "缁撴潫" : "浠嶇劧缁撴潫"
+    );
+    if (!confirmed) return;
+    await TimeSensitiveTaskApi.end({ id: task.id, forceEnd: !canEnd });
+    toast.success("鏃舵晥宸茬粨鏉�");
+    pagingRef.value?.reload();
+  } catch (error: any) {
+    toast.error(error?.message || "缁撴潫鏃舵晥澶辫触");
+  }
+}
+
+function openDetail(task: any) {
+  uni.navigateTo({ url: `/pages/timely/detail?id=${task.id}` });
+}
+
+function openInspection(task: any) {
+  const inspectionId = task.timelinessInspectionId || task.inspectionId || task.id;
+  const readonly = normalizeStatus(task) === 2 ? 1 : 0;
+  uni.navigateTo({
+    url: `/pages/timely/inspection?id=${inspectionId}&readonly=${readonly}`,
+  });
+}
+
+onShow(() => {
+  if (pageReady.value) pagingRef.value?.reload();
 });
 </script>
 
 <style lang="scss" scoped>
-.list_box {
-  height: calc(100vh - 100px);
+.timely-page {
+  height: calc(100vh - 50px);
   background: #f3f9f8;
 }
-:deep(.wd-button__content) {
-  color: #0d867f;
+
+.page-top {
+  background: #ffffff;
+}
+
+.date-filter-card {
+  padding: 12px;
+  margin: 0 10px 10px;
+  background: linear-gradient(135deg, #f3fbf8 0%, #ffffff 72%);
+  border: 1px solid rgba(7, 133, 104, 0.12);
+  border-radius: 12px;
+  box-shadow: 0 4px 14px rgba(19, 94, 78, 0.06);
+}
+
+.date-filter-heading,
+.date-filter-title,
+.date-filter-fields,
+.date-filter-actions,
+.popup-footer,
+.task-actions {
+  display: flex;
+  align-items: center;
+}
+
+.date-filter-heading {
+  justify-content: space-between;
+  margin-bottom: 10px;
+}
+
+.date-filter-title {
+  gap: 8px;
+}
+
+.date-filter-icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 32px;
+  height: 32px;
+  background: rgba(7, 133, 104, 0.1);
+  border-radius: 9px;
+}
+
+.date-filter-name {
+  font-size: 14px;
+  font-weight: 600;
+  color: #243832;
+}
+
+.date-filter-tip {
+  margin-top: 2px;
+  font-size: 11px;
+  color: #91a09c;
+}
+
+.date-filter-clear {
+  padding: 5px 0 5px 10px;
+  font-size: 12px;
+  color: #078568;
+}
+
+.date-filter-fields {
+  gap: 7px;
+}
+
+.date-filter-field {
+  flex: 1;
+  min-width: 0;
+  background: #ffffff;
+  border: 1px solid #e5efec;
+  border-radius: 8px;
+}
+
+.date-picker-trigger {
+  padding: 9px 10px;
+}
+
+.date-picker-label {
+  display: block;
+  margin-bottom: 5px;
+  font-size: 11px;
+  color: #8c9b97;
+}
+
+.date-picker-value {
+  display: flex;
+  gap: 4px;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 13px;
+  font-weight: 500;
+  color: #2b3c37;
+}
+
+.is-placeholder {
+  font-weight: 400;
+  color: #b7c1be;
+}
+
+.date-filter-separator {
+  flex: none;
+  font-size: 12px;
+  color: #8c9b97;
+}
+
+.date-filter-actions {
+  gap: 8px;
+  justify-content: flex-end;
+  margin-top: 10px;
+}
+
+.popup-footer,
+.task-actions {
+  flex-wrap: wrap;
+  gap: 8px;
+  justify-content: flex-end;
+}
+
+.task-list {
+  padding: 10px 10px 24px;
+}
+
+:deep(.task-card) {
+  margin: 0 0 10px;
+}
+
+.card-title,
+.title-main {
+  display: flex;
+  gap: 8px;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.title-main {
+  min-width: 0;
+  justify-content: flex-start;
+}
+
+.task-code {
+  overflow: hidden;
+  font-weight: 600;
+  color: #222222;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.info-grid {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 10px 16px;
+}
+
+.info-item,
+.time-row {
+  display: flex;
+  flex-direction: column;
+  gap: 3px;
+  min-width: 0;
+  font-size: 13px;
+  color: #333333;
+}
+
+.time-row {
+  flex-direction: row;
+  justify-content: space-between;
+  padding-top: 10px;
+  margin-top: 10px;
+  border-top: 1px solid #f2f2f2;
+}
+
+.info-label {
+  font-size: 12px;
+  color: #999999;
+}
+
+.popup-layout {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  background: #f7f8fa;
+}
+
+.popup-header {
+  display: flex;
+  flex-shrink: 0;
+  align-items: center;
+  justify-content: space-between;
+  padding: 16px;
+  background: #ffffff;
+  border-bottom: 1px solid #eeeeee;
+}
+
+.popup-title {
+  font-size: 17px;
+  font-weight: 600;
+}
+
+.popup-body {
+  flex: 1;
+  height: 0;
+}
+
+.popup-footer {
+  flex-shrink: 0;
+  padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
+  background: #ffffff;
+  border-top: 1px solid #eeeeee;
+}
+
+.duration-row {
+  display: grid;
+  grid-template-columns: 1fr auto 1fr auto 1fr;
+  gap: 4px;
+  align-items: center;
+  padding: 0 12px 10px;
+  background: #ffffff;
+}
+
+.duration-row :deep(.wd-input) {
+  padding: 0;
 }
 </style>
diff --git a/src/pages/timely/inspection.vue b/src/pages/timely/inspection.vue
new file mode 100644
index 0000000..8b1ccc2
--- /dev/null
+++ b/src/pages/timely/inspection.vue
@@ -0,0 +1,248 @@
+<template>
+  <view class="inspection-page">
+    <CardTitle title="宸℃鑼冨洿鍙婃俯搴﹁鎯�" :hideAction="false">
+      <template v-if="!readonly" #action>
+        <view class="header-actions">
+          <wd-button size="small" plain icon="add" @click="addRecord">鏂板</wd-button>
+          <wd-button size="small" plain type="warning" @click="editRecords">缂栬緫</wd-button>
+          <wd-button size="small" :loading="saving" @click="saveRecords">淇濆瓨</wd-button>
+        </view>
+      </template>
+    </CardTitle>
+
+    <view v-if="loading" class="state-box">
+      <wd-loading />
+      <text>鍔犺浇涓�...</text>
+    </view>
+    <wd-status-tip v-else-if="!records.length" image="content" tip="鏆傛棤宸℃娓╁害璁板綍" />
+    <view v-else class="record-list">
+      <wd-card v-for="(record, index) in records" :key="record.id || index">
+        <template #title>
+          <view class="record-title">
+            <text>宸℃璁板綍 {{ index + 1 }}</text>
+            <wd-button
+              v-if="!readonly"
+              type="text"
+              size="small"
+              custom-class="delete-button"
+              @click="deleteRecord(record, index)"
+            >
+              鍒犻櫎
+            </wd-button>
+          </view>
+        </template>
+
+        <wd-cell-group v-if="readonly || (record.id && !record._editing)">
+          <wd-cell title="宸℃鍛�" :value="record.routingInspectionUser || '-'" />
+          <wd-cell title="涓�鍖�" :value="temperatureValue(record.zoneOne)" />
+          <wd-cell title="浜屽尯" :value="temperatureValue(record.zoneTwo)" />
+          <wd-cell title="涓夊尯" :value="temperatureValue(record.zoneThree)" />
+          <wd-cell title="鍥涘尯" :value="temperatureValue(record.zoneFour)" />
+          <wd-cell title="浜斿尯" :value="temperatureValue(record.zoneFive)" />
+          <wd-cell title="鍏尯" :value="temperatureValue(record.zoneSix)" />
+          <wd-cell title="宸℃鏃堕棿" :value="record.createTime || record.inspectionTime || '-'" />
+        </wd-cell-group>
+
+        <wd-cell-group v-else border>
+          <wd-input v-model="record.routingInspectionUser" label="宸℃鍛�" readonly />
+          <wd-input v-model="record.zoneOne" label="涓�鍖�(鈩�)" type="digit" placeholder="璇疯緭鍏�" />
+          <wd-input v-model="record.zoneTwo" label="浜屽尯(鈩�)" type="digit" placeholder="璇疯緭鍏�" />
+          <wd-input v-model="record.zoneThree" label="涓夊尯(鈩�)" type="digit" placeholder="璇疯緭鍏�" />
+          <wd-input v-model="record.zoneFour" label="鍥涘尯(鈩�)" type="digit" placeholder="璇疯緭鍏�" />
+          <wd-input v-model="record.zoneFive" label="浜斿尯(鈩�)" type="digit" placeholder="璇疯緭鍏�" />
+          <wd-input v-model="record.zoneSix" label="鍏尯(鈩�)" type="digit" placeholder="璇疯緭鍏�" />
+        </wd-cell-group>
+      </wd-card>
+    </view>
+
+    <wd-toast />
+  </view>
+</template>
+
+<script setup lang="ts">
+import { onLoad } from "@dcloudio/uni-app";
+import { useToast } from "wot-design-uni";
+import CardTitle from "@/components/card-title/index.vue";
+import TimeSensitiveTaskApi, {
+  USE_TIME_SENSITIVE_MOCK,
+  type TimelinessInspectionDetailForm,
+} from "@/api/timeSensitiveTask";
+import { useUserStore } from "@/store/modules/user";
+import { getPrepareId } from "@/utils/cache";
+
+type InspectionRecord = TimelinessInspectionDetailForm & {
+  createTime?: string;
+  inspectionTime?: string;
+  _editing?: boolean;
+};
+
+const toast = useToast();
+const userStore = useUserStore();
+const inspectionId = ref<number | string>("");
+const readonly = ref(false);
+const loading = ref(false);
+const saving = ref(false);
+const records = ref<InspectionRecord[]>([]);
+
+function currentPerson() {
+  const user = userStore.userInfo as any;
+  return user?.nickName || user?.nickname || user?.userName || user?.username || "";
+}
+
+function temperatureValue(value: unknown) {
+  return value === undefined || value === null || value === "" ? "-" : `${value}鈩僠;
+}
+
+function ensurePrepared(action: string) {
+  if (USE_TIME_SENSITIVE_MOCK || getPrepareId()) return true;
+  toast.error(`璇峰畬鎴愮敓浜у噯澶囩‘璁わ紝鍐嶈繘琛�${action}鎿嶄綔`);
+  return false;
+}
+
+async function loadRecords() {
+  loading.value = true;
+  try {
+    const { data } = await TimeSensitiveTaskApi.getInspectionDetails(inspectionId.value);
+    const list = Array.isArray(data) ? data : data ? [data] : [];
+    records.value = list.map((item: any) => ({
+      ...item,
+      routingInspectionUser: item.routingInspectionUser || currentPerson(),
+      timelinessInspectionId: item.timelinessInspectionId || inspectionId.value,
+      _editing: false,
+    }));
+  } catch (error: any) {
+    records.value = [];
+    toast.error(error?.message || "鍔犺浇宸℃璇︽儏澶辫触");
+  } finally {
+    loading.value = false;
+  }
+}
+
+function addRecord() {
+  if (readonly.value || !ensurePrepared("鏂板宸℃鑼冨洿娓╁害")) return;
+  records.value.push({
+    routingInspectionUser: currentPerson(),
+    zoneOne: "",
+    zoneTwo: "",
+    zoneThree: "",
+    zoneFour: "",
+    zoneFive: "",
+    zoneSix: "",
+    timelinessInspectionId: inspectionId.value,
+    _editing: true,
+  });
+}
+
+function editRecords() {
+  if (readonly.value || !ensurePrepared("缂栬緫宸℃鑼冨洿娓╁害")) return;
+  if (!records.value.some((item) => item.id)) return toast.error("娌℃湁宸蹭繚瀛樼殑鏁版嵁鍙紪杈�");
+  records.value.forEach((item) => {
+    if (item.id) item._editing = true;
+  });
+  toast.show("宸茶繘鍏ョ紪杈戞ā寮忥紝淇敼鍚庤鐐瑰嚮淇濆瓨");
+}
+
+async function saveRecords() {
+  if (readonly.value || !ensurePrepared("淇濆瓨宸℃鑼冨洿娓╁害")) return;
+  if (!records.value.length) return toast.error("娌℃湁鏁版嵁闇�瑕佷繚瀛�");
+  saving.value = true;
+  try {
+    const payload = records.value.map((item) => ({
+      id: item.id,
+      routingInspectionUser: item.routingInspectionUser || currentPerson(),
+      zoneOne: item.zoneOne,
+      zoneTwo: item.zoneTwo,
+      zoneThree: item.zoneThree,
+      zoneFour: item.zoneFour,
+      zoneFive: item.zoneFive,
+      zoneSix: item.zoneSix,
+      timelinessInspectionId: item.timelinessInspectionId || inspectionId.value,
+    }));
+    await TimeSensitiveTaskApi.saveInspectionDetails(payload);
+    toast.success("淇濆瓨鎴愬姛");
+    await loadRecords();
+  } catch (error: any) {
+    toast.error(error?.message || "淇濆瓨澶辫触");
+  } finally {
+    saving.value = false;
+  }
+}
+
+function showDeleteConfirm() {
+  return new Promise<boolean>((resolve) => {
+    uni.showModal({
+      title: "鎻愮ず",
+      content: "纭畾鍒犻櫎璇ュ贰妫�璇︽儏鍚楋紵",
+      confirmText: "鍒犻櫎",
+      success: (result) => resolve(Boolean(result.confirm)),
+      fail: () => resolve(false),
+    });
+  });
+}
+
+async function deleteRecord(record: InspectionRecord, index: number) {
+  if (readonly.value) return;
+  const confirmed = await showDeleteConfirm();
+  if (!confirmed) return;
+  if (!record.id) {
+    records.value.splice(index, 1);
+    toast.success("鍒犻櫎鎴愬姛");
+    return;
+  }
+  try {
+    await TimeSensitiveTaskApi.deleteInspectionDetail(record.id);
+    toast.success("鍒犻櫎鎴愬姛");
+    await loadRecords();
+  } catch (error: any) {
+    toast.error(error?.message || "鍒犻櫎澶辫触");
+  }
+}
+
+onLoad((options: any) => {
+  inspectionId.value = options?.id || "";
+  readonly.value = String(options?.readonly || "0") === "1";
+  if (!inspectionId.value) return toast.error("宸℃璁板綍ID涓嶈兘涓虹┖");
+  loadRecords();
+});
+</script>
+
+<style lang="scss" scoped>
+.inspection-page {
+  min-height: calc(100vh - 50px);
+  padding-bottom: 24px;
+  background: #f3f9f8;
+}
+
+.header-actions,
+.record-title {
+  display: flex;
+  gap: 6px;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.record-list {
+  padding: 10px;
+}
+
+.record-list :deep(.wd-card) {
+  margin: 0 0 10px;
+}
+
+.record-title {
+  font-weight: 600;
+}
+
+:deep(.delete-button .wd-button__content) {
+  color: #fa4350;
+}
+
+.state-box {
+  display: flex;
+  gap: 8px;
+  align-items: center;
+  justify-content: center;
+  min-height: 180px;
+  color: #999999;
+}
+</style>

--
Gitblit v1.9.3