From e9066f0c6478091278cca7131347dbc896e73278 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期五, 11 九月 2026 15:53:53 +0800
Subject: [PATCH] fix: 完善时效领料功能

---
 src/pages/timely/detail.vue         |  368 +++++++++++++++++++++++++++++++--------------
 src/api/timeSensitiveTask.ts        |   27 +++
 src/pages/timely/inspection.vue     |    5 
 src/pages/index/index.vue           |   18 +-
 src/pages/timely/index.vue          |    8 
 src/pages/timely/materialDetail.vue |    6 
 6 files changed, 294 insertions(+), 138 deletions(-)

diff --git a/src/api/timeSensitiveTask.ts b/src/api/timeSensitiveTask.ts
index 7318dd4..b025a91 100644
--- a/src/api/timeSensitiveTask.ts
+++ b/src/api/timeSensitiveTask.ts
@@ -51,6 +51,16 @@
   ironPlateNo: number | string;
 }
 
+export interface TimeSensitiveScannedMaterial {
+  id: number | string;
+  materialType: TimelinessMaterialType | string;
+  batchCode: string;
+  available: boolean;
+  unavailableReason?: string | null;
+  qualitativeProductionId?: number | string;
+  [key: string]: any;
+}
+
 export interface TimelinessInspectionDetailForm {
   id?: number | string;
   routingInspectionUser?: string;
@@ -755,6 +765,23 @@
     });
   },
 
+  /** 鏃舵晥棰嗙敤鎵爜锛氬崟涓濄�佺粸绾跨粺涓�鎸夋祦杞崱鎵规鍙锋煡璇€�� */
+  scanByBatchCode(data: { batchCode: string }) {
+    if (USE_TIME_SENSITIVE_MOCK) {
+      const material = [...mockMaterialPool.鍗曚笣, ...mockMaterialPool.缁炵嚎].find(
+        (item) => mockMaterialCode(item) === data.batchCode
+      );
+      return mockResult<TimeSensitiveScannedMaterial | null>(
+        material ? { ...material, batchCode: mockMaterialCode(material), available: true } : null
+      );
+    }
+    return request<BaseResult<TimeSensitiveScannedMaterial>>({
+      url: "/timeSensitiveTask/scanByBatchCode",
+      method: "POST",
+      data,
+    });
+  },
+
   addItems(data: {
     taskId: number | string;
     itemIds: Array<number | string>;
diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue
index ae6915c..ce38cde 100644
--- a/src/pages/index/index.vue
+++ b/src/pages/index/index.vue
@@ -234,7 +234,7 @@
     icon: "/static/icons/log.png",
     title: "鏃舵晥",
     url: "/pages/timely/index",
-    show: true,
+    show: false,
   },
 ]);
 
@@ -295,14 +295,14 @@
     }
   }
 
-  if (
-    (data.deviceGroupName && data.deviceGroupName.includes("涓�")) ||
-    (data.deviceGroupName && data.deviceGroupName.includes("缁�")) ||
-    isAdmin.value
-  ) {
-    // 濡傛灉鏄笣鎴栫粸锛屾樉绀虹敓浜х鐞嗚彍鍗�
-    navList[0].show = true;
-  }
+  const deviceGroupName = String(data.deviceGroupName || "");
+  const canAccessProduction =
+    deviceGroupName.includes("涓�") || deviceGroupName.includes("缁�") || isAdmin.value;
+  const canAccessTimely = deviceGroupName.includes("鏃舵晥") || isAdmin.value;
+  const productionNav = navList.find((item) => item.url === "/pages/production/index");
+  const timelyNav = navList.find((item) => item.url === "/pages/timely/index");
+  if (productionNav) productionNav.show = canAccessProduction;
+  if (timelyNav) timelyNav.show = canAccessTimely;
   console.log("isInspector.value", isInspector.value);
   if (isInspector.value || isAdmin.value) {
     // 濡傛灉鏄贰妫�鍛橈紝鏄剧ず宸℃鑿滃崟
diff --git a/src/pages/timely/detail.vue b/src/pages/timely/detail.vue
index 69f966d..84b6beb 100644
--- a/src/pages/timely/detail.vue
+++ b/src/pages/timely/detail.vue
@@ -150,9 +150,21 @@
               :value="String(getCandidateWeight(item))"
             />
           </wd-cell-group>
-          <view class="material-detail-link" @click="openMaterialDetail(item)">
-            <text>鏌ョ湅璇︽儏</text>
-            <wd-icon name="arrow-right" size="14px" color="#0d867f" />
+          <view class="received-item-actions">
+            <wd-button
+              v-if="isWaiting"
+              type="error"
+              plain
+              custom-class="delete-received-button"
+              :loading="String(deletingItemId) === String(getCandidateId(item))"
+              @click="deleteReceivedItem(item)"
+            >
+              鍒犻櫎棰嗙敤
+            </wd-button>
+            <view class="material-detail-link" @click="openMaterialDetail(item)">
+              <text>鏌ョ湅璇︽儏</text>
+              <wd-icon name="arrow-right" size="16px" color="#0d867f" />
+            </view>
           </view>
         </wd-card>
       </view>
@@ -170,7 +182,7 @@
           <view>
             <text class="popup-title">閫夋嫨閾佹灦鍙�</text>
           </view>
-          <wd-icon name="close" size="20px" @click="rackSelectorVisible = false" />
+          <wd-icon name="close" size="20px" @click="closeRackSelector" />
         </view>
 
         <scroll-view class="rack-option-list" scroll-y>
@@ -203,7 +215,7 @@
               type="info"
               plain
               custom-class="footer-action-button"
-              @click="rackSelectorVisible = false"
+              @click="closeRackSelector"
             >
               鍙栨秷
             </wd-button>
@@ -279,8 +291,8 @@
                 label="閾佺墝搴忓彿"
                 type="number"
                 required
-                :readonly="materialType === '鍗曚笣'"
-                placeholder="璇疯緭鍏ユ鏁存暟"
+                readonly
+                placeholder="鑷姩鐢熸垚"
               />
               <wd-cell title="瑙勬牸鍨嬪彿" :value="candidate.model || '-'" />
               <wd-cell
@@ -342,30 +354,28 @@
     </wd-popup>
 
     <Scan ref="scanRef" emitName="scanTimeSensitiveItem" />
+    <wd-message-box />
     <wd-toast />
   </view>
 </template>
 
 <script setup lang="ts">
 import { onHide, onLoad, onShow, onUnload } from "@dcloudio/uni-app";
-import { useToast } from "wot-design-uni";
+import { useMessage, 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";
+import TimeSensitiveTaskApi, { type TimelinessMaterialType } from "@/api/timeSensitiveTask";
 
 const toast = useToast();
+const message = useMessage();
 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 deletingItemId = ref<number | string>("");
 const pageVisible = ref(false);
 const candidatePopupVisible = ref(false);
 const candidateLoading = ref(false);
@@ -382,6 +392,8 @@
 const rackSelectionRows = ref<Array<{ label: string; value: string; selected: boolean }>>([]);
 const activeRackNo = ref("");
 const candidateRackNo = ref("");
+const pendingScanEvent = ref<any>(null);
+const scanAfterRackSelection = ref(false);
 const materialType = computed<TimelinessMaterialType>(() =>
   normalizeMaterialType(task.value.materialType)
 );
@@ -492,17 +504,34 @@
     .length;
 }
 
+function hasSelectedActiveRack() {
+  return Boolean(
+    activeRackNo.value &&
+      activeRackNo.value !== UNASSIGNED_LAYER &&
+      selectedRackNos.value.includes(activeRackNo.value)
+  );
+}
+
 function getMaterialCode(item: any) {
-  return String(
-    item?.flowCode ||
-      item?.batchCode ||
-      item?.monofilamentNumber ||
-      item?.monofilamentnumber ||
-      item?.materialCode ||
-      item?.systemNo ||
-      item?.systemno ||
-      ""
-  ).trim();
+  return getMaterialCodes(item)[0] || "";
+}
+
+function getMaterialCodes(item: any) {
+  const source = item?.data && typeof item.data === "object" ? { ...item, ...item.data } : item;
+  const values = [
+    source?.flowCode,
+    source?.batchCode,
+    source?.batchNo,
+    source?.productionBatchNo,
+    source?.productionBatchNumber,
+    source?.productionBatchCode,
+    source?.monofilamentNumber,
+    source?.monofilamentnumber,
+    source?.materialCode,
+    source?.systemNo,
+    source?.systemno,
+  ];
+  return [...new Set(values.map((value) => String(value ?? "").trim()).filter(Boolean))];
 }
 
 function getCandidateId(item: any): number | string | undefined {
@@ -607,12 +636,43 @@
   }));
 }
 
-function confirmRackSelection() {
-  selectedRackNos.value = rackSelectionRows.value
+function closeRackSelector() {
+  rackSelectorVisible.value = false;
+  pendingScanEvent.value = null;
+  scanAfterRackSelection.value = false;
+}
+
+async function confirmRackSelection() {
+  const selectedValues = rackSelectionRows.value
     .filter((item) => item.selected)
     .map((item) => item.value);
+  if ((pendingScanEvent.value || scanAfterRackSelection.value) && !selectedValues.length) {
+    return toast.error("璇疯嚦灏戦�夋嫨涓�涓搧鏋跺彿");
+  }
+
+  selectedRackNos.value = selectedValues;
   syncActiveRackNo();
+  if (
+    (pendingScanEvent.value || scanAfterRackSelection.value) &&
+    (!activeRackNo.value ||
+      activeRackNo.value === UNASSIGNED_LAYER ||
+      !selectedValues.includes(activeRackNo.value))
+  ) {
+    activeRackNo.value = selectedValues[0];
+    candidateRackNo.value = selectedValues[0];
+  }
+
+  const scanEvent = pendingScanEvent.value;
+  const shouldStartScan = scanAfterRackSelection.value;
+  pendingScanEvent.value = null;
+  scanAfterRackSelection.value = false;
   rackSelectorVisible.value = false;
+
+  if (scanEvent) {
+    await handleScan(scanEvent);
+  } else if (shouldStartScan) {
+    scanRef.value?.triggerScan();
+  }
 }
 
 function fillIronPlateNumbers(rows: any[], otherCandidates: any[] = []) {
@@ -626,11 +686,6 @@
   );
   let nextNumber = 1;
   return rows.map((item) => {
-    const original = Number(item.ironPlateNo);
-    if (Number.isInteger(original) && original > 0 && !used.has(original)) {
-      used.add(original);
-      return { ...item, ironPlateNo: original, _selected: false };
-    }
     while (used.has(nextNumber)) nextNumber += 1;
     const result = { ...item, ironPlateNo: nextNumber, _selected: false };
     used.add(nextNumber);
@@ -701,28 +756,6 @@
     return materialType.value === "鍗曚笣" ? "鍚屼竴閾佹灦鍙峰唴閾佺墝搴忓彿涓嶈兘閲嶅" : "閾佺墝搴忓彿涓嶈兘閲嶅";
   }
   return "";
-}
-
-function requestIronPlateNo(defaultValue: number) {
-  return new Promise<number | undefined>((resolve) => {
-    uni.showModal({
-      title: "濉啓閾佺墝搴忓彿",
-      content: String(defaultValue),
-      editable: true,
-      placeholderText: "璇疯緭鍏ユ鏁存暟",
-      confirmText: "纭棰嗙敤",
-      success: (result) => {
-        if (!result.confirm) return resolve(undefined);
-        const value = Number((result as any).content);
-        if (!Number.isInteger(value) || value <= 0) {
-          toast.error("璇峰~鍐欐纭殑閾佺墝搴忓彿");
-          return resolve(undefined);
-        }
-        resolve(value);
-      },
-      fail: () => resolve(undefined),
-    });
-  });
 }
 
 async function loadTask() {
@@ -811,6 +844,15 @@
   successMessage = "鐗╂枡棰嗙敤鎴愬姛",
   layer = candidateRackNo.value
 ) {
+  const mismatchedMaterial = rows.find(
+    (item) => item?.materialType && normalizeMaterialType(item.materialType) !== materialType.value
+  );
+  if (mismatchedMaterial) {
+    toast.error(
+      `${materialType.value}浠诲姟鍙兘棰嗙敤${materialType.value}锛屼笉鑳介鐢�${mismatchedMaterial.materialType}`
+    );
+    return false;
+  }
   const validationMessage = validateIronPlateNumbers(rows, layer);
   if (validationMessage) {
     toast.error(validationMessage);
@@ -853,85 +895,136 @@
   }
 }
 
+async function deleteReceivedItem(item: any) {
+  if (!isWaiting.value) return toast.error("鍙湁寰呭紑濮嬬殑浠诲姟鍙互鍒犻櫎宸查鐢ㄧ墿鏂�");
+  const itemId = getCandidateId(item);
+  if (itemId === undefined || itemId === null || itemId === "") {
+    return toast.error("璇ョ墿鏂欑己灏戞槑缁咺D锛屾棤娉曞垹闄�");
+  }
+  try {
+    await message.confirm({
+      title: "鍒犻櫎棰嗙敤",
+      msg: `纭畾鍒犻櫎宸查鐢ㄧ墿鏂欌��${getMaterialCode(item) || itemId}鈥濆悧锛焋,
+      confirmButtonText: "纭畾鍒犻櫎",
+      cancelButtonText: "鍙栨秷",
+      closeOnClickModal: false,
+    });
+  } catch {
+    return;
+  }
+
+  deletingItemId.value = itemId;
+  try {
+    await TimeSensitiveTaskApi.deleteItems({ taskId: taskId.value, itemIds: [itemId] });
+    toast.success("宸插垹闄ら鐢ㄧ墿鏂�");
+    await loadTask();
+  } catch (error: any) {
+    toast.error(error?.message || "鍒犻櫎棰嗙敤鐗╂枡澶辫触");
+  } finally {
+    deletingItemId.value = "";
+  }
+}
+
+function addSingleWireCodeVariants(codes: string[]) {
+  const result = new Set(codes);
+  codes.forEach((code) => {
+    if (!/^75\d+(?:-\d+)?$/i.test(code)) return;
+    const compactCode = code.replace(/-/g, "");
+    result.add(compactCode);
+    if (compactCode.length > 3) {
+      result.add(`${compactCode.slice(0, -3)}-${compactCode.slice(-3)}`);
+    }
+  });
+  return [...result];
+}
+
 function readScanPayload(event: any) {
-  const rawValue = event?.code ?? event;
-  const raw = typeof rawValue === "string" ? rawValue.trim() : JSON.stringify(rawValue || "");
+  const rawValue = event?.code ?? event?.data ?? event;
+  let raw = typeof rawValue === "string" ? rawValue.trim() : JSON.stringify(rawValue || "");
+  try {
+    raw = decodeURIComponent(raw);
+  } catch {
+    // 鎵弿鍐呭涓嶆槸 URI 缂栫爜鏃剁洿鎺ヤ娇鐢ㄥ師鍊笺��
+  }
+
   let parsed: any = {};
-  if (raw.startsWith("{") || raw.startsWith("[")) {
+  let jsonValue: any = raw;
+  // 鍏煎琚壂鐮佽澶囧啀娆″寘鎴愬瓧绗︿覆鐨� JSON锛�"{\"monofilamentNumber\":\"...\"}"銆�
+  for (let index = 0; index < 2 && typeof jsonValue === "string"; index += 1) {
+    const text = jsonValue.trim();
+    if (!text.startsWith("{") && !text.startsWith("[") && !text.startsWith('"')) break;
     try {
-      parsed = JSON.parse(raw);
-      if (Array.isArray(parsed)) parsed = parsed[0] || {};
+      jsonValue = JSON.parse(text);
     } catch {
-      parsed = {};
+      break;
     }
   }
-  const directCode = getMaterialCode(parsed);
+  if (Array.isArray(jsonValue)) parsed = jsonValue[0] || {};
+  else if (jsonValue && typeof jsonValue === "object") parsed = jsonValue;
+
   const outputId = parsed.id ?? parsed.outPutId ?? parsed.outputId;
   const ironPlateNo = parsed.ironPlateNo ?? parsed.ironPlateNumber;
-  if (directCode) return { raw, parsed, outputId, ironPlateNo, materialCode: directCode };
+  const parsedCodes = getMaterialCodes(parsed);
+  const singleWireMatch = raw.match(/(?:^|[^0-9])(75\d+(?:-\d+)?)(?=$|[^0-9])/i)?.[1];
+  const strandedWireMatch = raw.match(/(?:^|[^0-9])(74\d+)(?=$|[^0-9])/i)?.[1];
+  const typeCode = materialType.value === "鍗曚笣" ? singleWireMatch : strandedWireMatch;
+  let materialCodes = [...parsedCodes, ...(typeCode ? [typeCode] : [])];
 
-  const parts = raw.split(/[锛�,\s]/).filter(Boolean);
-  const singleWireCode = parts.find((part) => part.startsWith("75"));
+  // 娴佽浆鍗℃甯镐负 monofilamentNumber JSON锛涘悓鏃跺吋瀹圭函缂栧彿鍜屸�滃崟涓濈紪鍙凤細...鈥濇枃鏈��
+  if (!materialCodes.length && !Object.keys(parsed).length) {
+    const plainCode = raw.match(/[A-Za-z0-9][A-Za-z0-9._/-]*/)?.[0];
+    if (plainCode) materialCodes.push(plainCode);
+  }
+  materialCodes = [...new Set(materialCodes.map((code) => code.trim()).filter(Boolean))];
+  if (materialType.value === "鍗曚笣") materialCodes = addSingleWireCodeVariants(materialCodes);
+
   return {
     raw,
     parsed,
     outputId,
     ironPlateNo,
-    materialCode: singleWireCode || (!Object.keys(parsed).length ? raw : ""),
+    monofilamentNumber: String(
+      parsed.monofilamentNumber ?? parsed.monofilamentnumber ?? typeCode ?? ""
+    ).trim(),
+    materialCodes,
   };
-}
-
-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("鍙湁寰呭紑濮嬬殑浠诲姟鍙互鎵爜鏂板鏄庣粏");
-  if (
-    materialType.value === "鍗曚笣" &&
-    (!activeRackNo.value || activeRackNo.value === UNASSIGNED_LAYER)
-  ) {
-    return toast.error("璇峰厛閫夋嫨鍗曚笣棰嗙敤閾佹灦鍙�");
+  if (materialType.value === "鍗曚笣" && !hasSelectedActiveRack()) {
+    pendingScanEvent.value = event;
+    await openRackSelector();
+    return;
   }
   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}锛屽彲鑳藉凡娣诲姞鎴栦笉鍦ㄥ緟鏃舵晥鑼冨洿`);
+    const batchCode = scan.monofilamentNumber || scan.materialCodes[0];
+    if (!batchCode) {
+      return toast.error("浜岀淮鐮佹牸寮忛敊璇紝鏈瘑鍒埌鍗曚笣缂栧彿鎴栫敓浜ф壒娆″彿");
     }
-    const suggestedNumber = Number(scan.ironPlateNo) || getNextIronPlateNo(activeRackNo.value);
+
+    const { data: candidate } = await TimeSensitiveTaskApi.scanByBatchCode({ batchCode });
+    if (!candidate) {
+      return toast.error(`鏈壘鍒版壒娆″彿涓� ${batchCode} 鐨勫緟鏃舵晥鐗╂枡`);
+    }
+    if (candidate.available === false) {
+      return toast.error(candidate.unavailableReason || "璇ョ墿鏂欏綋鍓嶄笉鍙鐢�");
+    }
+    if (
+      candidate.materialType &&
+      normalizeMaterialType(candidate.materialType) !== materialType.value
+    ) {
+      return toast.error(
+        `褰撳墠浠诲姟鍙兘棰嗙敤${materialType.value}锛岃娴佽浆鍗′负${candidate.materialType}`
+      );
+    }
     const ironPlateNo =
-      materialType.value === "鍗曚笣" ? suggestedNumber : await requestIronPlateNo(suggestedNumber);
-    if (ironPlateNo === undefined) return;
+      materialType.value === "鍗曚笣"
+        ? Number(scan.ironPlateNo) || getNextIronPlateNo(activeRackNo.value)
+        : getNextIronPlateNo();
     await addCandidates([{ ...candidate, ironPlateNo }], "鎵爜棰嗙敤鎴愬姛", activeRackNo.value);
   } catch (error: any) {
     toast.error(error?.message || "鎵爜棰嗙敤鐗╂枡澶辫触");
@@ -945,9 +1038,9 @@
   if (materialType.value === "鍗曚笣") {
     await loadRackOptions();
     syncActiveRackNo();
-    if (!activeRackNo.value || activeRackNo.value === UNASSIGNED_LAYER) {
-      toast.error("璇峰厛閫夋嫨鍗曚笣棰嗙敤閾佹灦鍙�");
-      openRackSelector();
+    if (!hasSelectedActiveRack()) {
+      scanAfterRackSelection.value = true;
+      await openRackSelector();
       return;
     }
   }
@@ -983,7 +1076,7 @@
 .detail-page {
   display: flex;
   flex-direction: column;
-  height: calc(100vh - 50px);
+  height: 100vh;
   background: #f3f9f8;
 }
 
@@ -1029,17 +1122,26 @@
   font-weight: 600;
 }
 
+.summary-title {
+  font-size: 19px;
+}
+
 .summary-grid {
   display: grid;
   grid-template-columns: 1fr 1fr;
-  gap: 12px 16px;
-  font-size: 13px;
+  gap: 16px 18px;
+  font-size: 17px;
+  font-weight: 500;
+  line-height: 1.45;
+  color: #333333;
 }
 
 .summary-grid > view {
   display: flex;
   flex-direction: column;
-  gap: 3px;
+  gap: 4px;
+  min-width: 0;
+  overflow-wrap: anywhere;
 }
 
 .label,
@@ -1047,6 +1149,12 @@
 .candidate-meta {
   font-size: 12px;
   color: #999999;
+}
+
+.summary-grid .label {
+  font-size: 14px;
+  font-weight: 400;
+  color: #7e8987;
 }
 
 .scan-tip {
@@ -1139,21 +1247,47 @@
 }
 
 .item-list {
-  padding: 0 10px 24px;
+  padding: 0 10px 12px;
 }
 
 .item-list :deep(.wd-card) {
   margin: 0 0 10px;
 }
 
+.received-item-actions {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 10px 12px;
+  background: #f8fbfa;
+  border-top: 1px solid #edf2f0;
+}
+
+.received-item-actions :deep(.delete-received-button) {
+  min-width: 108px;
+  height: 44px;
+  font-size: 16px;
+  font-weight: 600;
+  border-radius: 9px;
+}
+
 .material-detail-link {
   display: flex;
   gap: 6px;
   align-items: center;
-  justify-content: flex-end;
-  padding: 10px 12px;
-  font-size: 13px;
+  min-height: 44px;
+  padding: 0 4px 0 18px;
+  font-size: 15px;
   color: #0d867f;
+}
+
+.received-item-actions .material-detail-link {
+  margin-left: auto;
+}
+
+.candidate-item > .material-detail-link {
+  justify-content: flex-end;
+  padding: 0 12px;
   background: #f8fbfa;
   border-top: 1px solid #edf2f0;
 }
diff --git a/src/pages/timely/index.vue b/src/pages/timely/index.vue
index 1d018e5..94505d9 100644
--- a/src/pages/timely/index.vue
+++ b/src/pages/timely/index.vue
@@ -7,12 +7,6 @@
       :auto-show-back-to-top="true"
       @query="queryTaskList"
     >
-      <template #top>
-        <view class="page-top">
-          <CardTitle title="鏃舵晥浠诲姟鍙拌处" :hideAction="false" />
-        </view>
-      </template>
-
       <view class="task-list">
         <wd-card v-for="task in taskList" :key="task.id" custom-class="task-card">
           <template #title>
@@ -184,7 +178,7 @@
 
 <style lang="scss" scoped>
 .timely-page {
-  height: calc(100vh - 50px);
+  height: 100vh;
   background: #f3f9f8;
 }
 
diff --git a/src/pages/timely/inspection.vue b/src/pages/timely/inspection.vue
index 8b1ccc2..d36b304 100644
--- a/src/pages/timely/inspection.vue
+++ b/src/pages/timely/inspection.vue
@@ -208,8 +208,9 @@
 
 <style lang="scss" scoped>
 .inspection-page {
-  min-height: calc(100vh - 50px);
-  padding-bottom: 24px;
+  min-height: 100vh;
+  padding-bottom: calc(12px + env(safe-area-inset-bottom));
+  box-sizing: border-box;
   background: #f3f9f8;
 }
 
diff --git a/src/pages/timely/materialDetail.vue b/src/pages/timely/materialDetail.vue
index 89d34ef..ef4a0f9 100644
--- a/src/pages/timely/materialDetail.vue
+++ b/src/pages/timely/materialDetail.vue
@@ -36,7 +36,7 @@
         </view>
         <view class="detail-row">
           <text class="detail-label">鍗曚綅</text>
-          <text class="detail-value">{{ material.unit || "-" }}</text>
+          <text class="detail-value">kg</text>
         </view>
         <view class="detail-row">
           <text class="detail-label">鍘傚</text>
@@ -193,12 +193,12 @@
 
 <style lang="scss" scoped>
 .material-page {
-  height: calc(100vh - 50px);
+  min-height: 100vh;
   background: #f3f9f8;
 }
 
 .page-content {
-  padding: 12px 12px calc(24px + env(safe-area-inset-bottom));
+  padding: 12px 12px calc(12px + env(safe-area-inset-bottom));
 }
 
 .hero-card,

--
Gitblit v1.9.3