From 8aa5f0e094e6ff51aa7c47d5b4e8331a16eb09ff Mon Sep 17 00:00:00 2001
From: leiqunqing <zhengqifeng@hnkhzn.com>
Date: 星期五, 06 二月 2026 15:19:05 +0800
Subject: [PATCH] 提交ddl

---
 代码管理/WIDESEAWCS_Client/src/views/Home.vue |  863 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 851 insertions(+), 12 deletions(-)

diff --git "a/\344\273\243\347\240\201\347\256\241\347\220\206/WIDESEAWCS_Client/src/views/Home.vue" "b/\344\273\243\347\240\201\347\256\241\347\220\206/WIDESEAWCS_Client/src/views/Home.vue"
index 820437a..9a69c91 100644
--- "a/\344\273\243\347\240\201\347\256\241\347\220\206/WIDESEAWCS_Client/src/views/Home.vue"
+++ "b/\344\273\243\347\240\201\347\256\241\347\220\206/WIDESEAWCS_Client/src/views/Home.vue"
@@ -1,24 +1,863 @@
 <template>
-  <div class="title"></div>
+  <div class="container">
+    <!-- 宸︿晶鍖哄煙 - 浠呬繚鐣欎俊鍙风伅+鎴愬搧/闆朵欢鍖哄煙 -->
+    <div class="left-area">
+      <div class="left-top">
+        <div class="signal-status">
+          <div class="signal-item" v-for="(signal, index) in signalStates" :key="index">
+            <div
+              class="signal-light"
+              :class="signal ? 'signal-active' : 'signal-inactive'"
+            >
+              <div class="signal-light-inner"></div>
+            </div>
+            <span class="signal-label">{{ signalLabels[index] }}</span>
+          </div>
+        </div>
+      </div>
+
+      <div class="left-bottom">
+        <div class="form-row finished-product-row">
+          <span class="label">鎴愬搧缂栧彿锛�</span>
+          <input type="text" class="input-box" v-model="finishedProduct" disabled />
+        </div>
+        <div class="parts-list">
+          <div
+            class="form-row part-item"
+            v-for="(item, index) in leftPartCodes"
+            :key="index"
+          >
+            <span class="label">闆朵欢{{ index + 1 }}锛�</span>
+            <input
+              type="text"
+              class="input-box"
+              v-model="leftPartCodes[index]"
+              disabled
+            />
+            <label class="checkbox-container">
+              <input
+                type="checkbox"
+                class="part-checkbox"
+                v-model="leftPartChecked[index]"
+                @change="handlePartCheck(index)"
+                :disabled="checkLoading[index]"
+              />
+              <span class="checkmark"></span>
+              <span class="checkbox-label">{{
+                leftPartChecked[index] ? "鎵爜" : "涓嶆壂"
+              }}</span>
+            </label>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 鍙充晶鍖哄煙 - 淇濈暀鍘熸湁褰曞叆+鎻愪氦+娓呴櫎鍔熻兘 -->
+    <div class="right-area">
+      <div class="right-top">
+        <div class="form-row input-submit-row">
+          <span class="label">褰曞叆妗嗭細</span>
+          <input type="text" class="input-box" v-model="rightTopInput" />
+          <button class="btn save-btn submit-input-btn" @click="saveData">
+            <i class="icon icon-submit"></i>鎻愪氦
+          </button>
+        </div>
+      </div>
+
+      <div class="right-bottom">
+        <div class="parts-list">
+          <div class="form-row part-item finished-product-row">
+            <span class="label">鎴愬搧缂栧彿锛�</span>
+            <input
+              type="text"
+              class="input-box"
+              v-model="finishedProductCode"
+              placeholder="璇疯緭鍏ユ垚鍝佺紪鍙�"
+            />
+            <button class="btn clear-btn" @click="clearFinishedProductCode">
+              <i class="icon icon-clear"></i>娓呴櫎
+            </button>
+          </div>
+          <div
+            class="form-row part-item"
+            v-for="(item, index) in rightPartCodes"
+            :key="index"
+          >
+            <span class="label">闆朵欢{{ index + 1 }}锛�</span>
+            <input
+              type="text"
+              class="input-box"
+              v-model="rightPartCodes[index]"
+              placeholder="璇疯緭鍏ラ浂浠剁紪鍙�"
+            />
+            <button class="btn clear-btn" @click="clearRightPart(index)">
+              <i class="icon icon-clear"></i>娓呴櫎
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
 </template>
 
 <script>
-import { ref, reactive } from 'vue'
+import { ref, onMounted, onUnmounted, watch, computed } from "vue";
+import axios from "axios";
 
 export default {
   setup() {
-    return {
+    // 鍩虹鏁版嵁瀹氫箟
+    const finishedProduct = ref(""); // 宸︿晶鎴愬搧缂栧彿锛堟帴鍙h繑鍥烇級
+    const finishedProductId = ref(""); // 宸︿晶鎴愬搧ID锛堟帴鍙h繑鍥烇級
+    const rightTopInput = ref(""); // 鍙充晶鏍稿績褰曞叆妗�
+    const leftPartCodes = ref(Array(10).fill("")); // 宸︿晶闆朵欢缂栧彿鏁扮粍
+    const rightPartCodes = ref(Array(10).fill("")); // 鍙充晶闆朵欢缂栧彿鏁扮粍
+    const leftPartChecked = ref(Array(10).fill(false)); // 宸︿晶闆朵欢鍕鹃�夌姸鎬�
+    const leftPartIds = ref(Array(10).fill("")); // 宸︿晶闆朵欢ID锛堟帴鍙h繑鍥烇級
+    const finishedProductCode = ref(""); // 鍙充晶鎴愬搧缂栧彿杈撳叆妗�
+    const fillIndex = ref(-1); // 褰曞叆妗嗚嚜鍔ㄥ~鍏呯储寮�
+    const checkLoading = ref(Array(10).fill(false)); // 闆朵欢鍕鹃�夊姞杞介攣
 
-    }
-  }
-}
+    // 淇″彿鐏浉鍏�
+    const signalStates = ref([false, false, false, false, false]);
+    const signalLabels = ref([
+      "蹇冭烦淇″彿",
+      "鎬ュ仠淇″彿",
+      "鑷姩杩愯淇″彿",
+      "鍦ㄧ嚎妯″紡淇″彿",
+      "鏁呴殰淇″彿",
+    ]);
+
+    // 瀹氭椂/闃叉姈鐩稿叧鍙橀噺
+    let pollingTimer = null;
+    const pollingInterval = 5000; // 淇″彿鐏疆璇㈤棿闅�5绉�
+    let checkDebounceTimer = null;
+    let destroyDelayTimer = null;
+    const destroyDelayTime = 500; // 褰曞叆妗嗗~鍏呭欢杩�500ms
+    let autoSubmitDebounceTimer = null;
+    let submitLock = ref(false); // 鎻愪氦閿侊紝闃叉閲嶅鎻愪氦
+
+    // 璁$畻灞炴�� - 缁熻鏈夋晥鍕鹃��/濉厖鏁伴噺锛堣繃婊ょ┖鍊硷級
+    const checkedCount = computed(() => {
+      return leftPartChecked.value.filter((checked) => checked === true).length;
+    });
+    const filledPartCount = computed(() => {
+      return rightPartCodes.value.filter((code) => code.trim() !== "").length;
+    });
+
+    // 鑾峰彇宸︿晶鍒濆鏁版嵁锛堟垚鍝�+闆朵欢+鍕鹃�夌姸鎬侊級
+    const fetchLeftInitialData = async () => {
+      try {
+        console.log("姝e湪鑾峰彇宸︿晶鍒濆鏁版嵁...");
+        const response = await axios.get("/api/scanStation/GetLeftInitialData", {
+          timeout: 5000,
+        });
+        const resData = response.data;
+        const isSuccess = resData.Status === true || resData.status === true;
+        if (isSuccess) {
+          const data = resData.Data || resData.data || {};
+          if (data.finishedProductId) finishedProductId.value = data.finishedProductId;
+          if (data.finishedProduct) finishedProduct.value = data.finishedProduct;
+          if (Array.isArray(data.leftPartCodes) && data.leftPartCodes.length >= 10) {
+            for (let i = 0; i < 10; i++) {
+              leftPartCodes.value[i] = data.leftPartCodes[i] || "";
+              leftPartIds.value[i] = data.leftPartIds?.[i] || "";
+            }
+          }
+          if (Array.isArray(data.leftPartChecked) && data.leftPartChecked.length >= 10) {
+            for (let i = 0; i < 10; i++) {
+              leftPartChecked.value[i] = !!data.leftPartChecked[i];
+            }
+          }
+        }
+      } catch (error) {
+        console.error("鑾峰彇宸︿晶鍒濆鏁版嵁澶辫触锛�", error);
+      }
+    };
+
+    // 鑾峰彇淇″彿鐏姸鎬侊紙瀹氭椂杞锛�
+    const fetchSignalAndPLCStates = async () => {
+      try {
+        const response = await axios.get("/api/scanStation/GetSignalStates", {
+          timeout: 5000,
+        });
+        const resData = response.data;
+        const isSuccess = resData.Status === true || resData.status === true;
+        if (isSuccess) {
+          const data = resData.Data || resData.data || {};
+          const newSignalStates = data.signalStates || [];
+          for (let i = 0; i < 5; i++) signalStates.value[i] = newSignalStates[i] ?? false;
+        }
+      } catch (error) {
+        console.error("鑾峰彇淇″彿鐘舵�佸け璐ワ細", error);
+      }
+    };
+
+    // 鍚姩/鍋滄淇″彿鐏疆璇�
+    const startPolling = () => {
+      if (pollingTimer) clearInterval(pollingTimer);
+      fetchSignalAndPLCStates();
+      pollingTimer = setInterval(fetchSignalAndPLCStates, pollingInterval);
+    };
+    const stopPolling = () => {
+      if (pollingTimer) clearInterval(pollingTimer);
+      pollingTimer = null;
+    };
+
+    // 宸︿晶闆朵欢鍕鹃�夌姸鎬佸彉鏇村鐞嗭紙鏇存柊鍚庣鐘舵�侊級
+    const handlePartCheck = async (index) => {
+      const isChecked = leftPartChecked.value[index];
+      const partCode = leftPartCodes.value[index];
+      const partId = leftPartIds.value[index];
+
+      // 鍓嶇疆鏍¢獙
+      if (!finishedProductId.value) {
+        alert("鎴愬搧ID涓嶅瓨鍦紝鏃犳硶鏇存柊闆朵欢鎵爜鐘舵�侊紒");
+        leftPartChecked.value[index] = !isChecked;
+        return;
+      }
+      if (!partId) {
+        alert(`闆朵欢${index + 1}鏁版嵁搴揑D涓嶅瓨鍦紝鏃犳硶鏇存柊鎵爜鐘舵�侊紒`);
+        leftPartChecked.value[index] = !isChecked;
+        return;
+      }
+      if (!partCode.trim()) {
+        alert(`闆朵欢${index + 1}缂栧彿涓虹┖锛屾棤娉曟洿鏂版壂鐮佺姸鎬侊紒`);
+        leftPartChecked.value[index] = !isChecked;
+        return;
+      }
+
+      checkLoading.value[index] = true;
+      try {
+        const response = await axios.post(
+          "/api/scanStation/UpdatePartScannedStatus",
+          { Id: partId, IsScanned: isChecked ? 1 : 0 },
+          { timeout: 5000 }
+        );
+        const resData = response.data;
+        const isSuccess = resData.Status === true || resData.status === true;
+        if (isSuccess) {
+          console.log(`闆朵欢${index + 1}鎵爜鐘舵�佹洿鏂版垚鍔焋);
+        } else {
+          leftPartChecked.value[index] = !isChecked;
+          alert(
+            `闆朵欢${index + 1}鐘舵�佹洿鏂板け璐ワ細${
+              resData.Message || resData.message || "鏈煡閿欒"
+            }`
+          );
+        }
+      } catch (error) {
+        leftPartChecked.value[index] = !isChecked;
+        alert(`闆朵欢${index + 1}鐘舵�佹洿鏂拌姹傚け璐ワ紝璇锋鏌ョ綉缁滄垨鎺ュ彛锛乣);
+        console.error(`鏇存柊闆朵欢${index + 1}鎵爜鐘舵�佸け璐ワ細`, error);
+      } finally {
+        checkLoading.value[index] = false;
+        if (checkDebounceTimer) clearTimeout(checkDebounceTimer);
+        checkDebounceTimer = null;
+      }
+    };
+
+    // 鍙充晶褰曞叆妗嗗欢杩熷~鍏呴�昏緫锛堟垚鍝佲啋闆朵欢渚濇濉厖锛�
+    const fillContent = () => {
+      if (!rightTopInput.value.trim()) return;
+      const inputValue = rightTopInput.value.trim();
+
+      if (destroyDelayTimer) clearTimeout(destroyDelayTimer);
+      destroyDelayTimer = setTimeout(() => {
+        if (!finishedProductCode.value.trim()) {
+          finishedProductCode.value = inputValue;
+        } else if (fillIndex.value < 10) {
+          rightPartCodes.value[fillIndex.value] = inputValue;
+          fillIndex.value++;
+        } else {
+          alert("鎴愬搧缂栧彿鍜岄浂浠�1-10宸插叏閮ㄥ~鍏呭畬鎴�,鏃犳硶缁х画褰曞叆!");
+          rightTopInput.value = "";
+          destroyDelayTimer = null;
+          return;
+        }
+        rightTopInput.value = "";
+        destroyDelayTimer = null;
+      }, destroyDelayTime);
+    };
+
+    // 鐩戝惉鍙充晶褰曞叆妗嗚緭鍏ワ紝瑙﹀彂鑷姩濉厖
+    watch(
+      rightTopInput,
+      (newVal) => {
+        if (newVal.trim()) fillContent();
+      },
+      { immediate: false }
+    );
+
+    // 鍙充晶杈撳叆妗嗘竻闄ゆ柟娉�
+    const clearRightPart = (index) => (rightPartCodes.value[index] = "");
+    const clearFinishedProductCode = () => (finishedProductCode.value = "");
+
+    // 鏍稿績淇敼锛氭仮澶嶆垚鍝佺紪鍙峰繀濉牎楠岋紝鏈~鍐欑洿鎺ユ彁绀哄苟缁堟鎻愪氦
+    const saveData = async () => {
+      // 1. 鎴愬搧缂栧彿蹇呭~鏍¢獙銆愭牳蹇冩柊澧炪��
+      const productCode = finishedProductCode.value.trim();
+      if (!productCode) {
+        alert("璇峰厛濉啓鎴愬搧缂栧彿锛屾垚鍝佺紪鍙蜂负蹇呭~椤癸紒");
+        return;
+      }
+      // 2. 鎻愪氦閿侊細闃叉閲嶅鐐瑰嚮
+      if (submitLock.value) return;
+      submitLock.value = true;
+
+      try {
+        // 3. 浼犲弬閫傞厤锛氬悗绔疍to鏄ぇ椹煎嘲瀛楁锛佸繀椤讳弗鏍煎尮閰嶏紙FinishedProductCode/PartsList锛�
+        const submitData = {
+          FinishedProductCode: productCode, // 鐩存帴浣跨敤宸叉牎楠岀殑闈炵┖鍊�
+          PartsList: rightPartCodes.value.map((item) => item.trim()),
+        };
+        console.log("馃摛 鎻愪氦鍒癝aveToolingBoardNo鐨勫弬鏁帮紙鍖归厤鍚庣Dto锛夛細", submitData);
+
+        // 4. 璋冪敤鍚庣鎺ュ彛锛屽欢闀胯秴鏃舵椂闂达紙鍚庣鏈塒LC浜や簰锛�500ms浼戠湢锛岃涓�10绉掞級
+        const response = await axios.post(
+          "/api/boxingDetail/SaveToolingBoardNo",
+          submitData,
+          { timeout: 10000 }
+        );
+        const resData = response.data;
+        console.log("馃摜 SaveToolingBoardNo鎺ュ彛杩斿洖锛�", resData);
+
+        // 5. 閫傞厤鍚庣杩斿洖鏍煎紡锛歴tatus涓簍rue琛ㄧず鎴愬姛锛屽惁鍒欏彇message閿欒淇℃伅
+        if (resData.status === true) {
+          finishedProductCode.value = "";
+          rightPartCodes.value = Array(10).fill("");
+          rightTopInput.value = "";
+          fillIndex.value = -1;
+        } else {
+          // 涓氬姟閿欒锛氱洿鎺ュ睍绀哄悗绔繑鍥炵殑message锛堝鐗╂枡閿欒銆佹棤閰嶆柟锛�
+          const errorMsg = resData.message || "鎻愪氦澶辫触锛屾湭鐭ヤ笟鍔¢敊璇�";
+          alert(`鎻愪氦澶辫触锛�${errorMsg}`);
+        }
+      } catch (error) {
+        // 6. 寮傚父鎹曡幏锛氬吋瀹瑰悗绔湭鎹曡幏寮傚父锛堝PLC閫氳寮傚父銆佹暟缁勮秺鐣屻��500閿欒锛�
+        let errorMsg = "鎻愪氦璇锋眰寮傚父锛�";
+        if (error.code === "ECONNABORTED") {
+          errorMsg = "鎻愪氦璇锋眰瓒呮椂锛佸悗绔疨LC浜や簰/鏁版嵁搴撴搷浣滆�楁椂杩囬暱";
+        } else if (error.response) {
+          // 鏈嶅姟鍣�500閿欒锛氬悗绔姏鍑烘湭鎹曡幏寮傚父锛堝PLC鏈繛鎺ャ�侀浂浠朵笉鍖归厤锛�
+          errorMsg = `鏈嶅姟鍣ㄩ敊璇細${error.response.status} - ${error.response.statusText}锛岃妫�鏌LC閫氳鎴栭厤鏂归厤缃甡;
+          console.error("鉂� 鍚庣鏈嶅姟鍣ㄩ敊璇鎯咃細", error.response.data);
+        } else if (error.request) {
+          errorMsg = "缃戠粶寮傚父锛佹湭鏀跺埌鍚庣鍝嶅簲锛岃妫�鏌ユ帴鍙e湴鍧�鍜岀綉缁�";
+        } else {
+          errorMsg = `璇锋眰閿欒锛�${error.message}`;
+        }
+        alert(errorMsg);
+        console.error("鉂� 鎻愪氦鎺ュ彛寮傚父璇︽儏锛�", error);
+      } finally {
+        // 7. 閲婃斁鎻愪氦閿侊細鏃犺鎴愬姛/澶辫触閮借閲婃斁
+        submitLock.value = false;
+      }
+    };
+
+    // 鏍稿績淇敼锛氳嚜鍔ㄦ彁浜ら�昏緫鍚屾澧炲姞鎴愬搧缂栧彿蹇呭~鏍¢獙锛堟棤鎴愬搧缂栧彿涓嶈Е鍙戣嚜鍔ㄦ彁浜わ級
+    const checkAutoSubmit = () => {
+      if (autoSubmitDebounceTimer) clearTimeout(autoSubmitDebounceTimer);
+      autoSubmitDebounceTimer = setTimeout(() => {
+        const needCheckNum = checkedCount.value; // 宸︿晶鍕鹃�夐浂浠舵暟
+        const filledNum = filledPartCount.value; // 鍙充晶鏈夋晥濉厖闆朵欢鏁�
+        const productCode = finishedProductCode.value.trim(); // 鎴愬搧缂栧彿锛堝幓绌烘牸锛�
+        const hasProductCode = !!productCode; // 鎴愬搧缂栧彿鏄惁鏈夋晥銆愪繚鐣欏師閫昏緫锛屽疄闄呮槸蹇呭~鏍¢獙銆�
+
+        console.log(
+          `鉁� 鑷姩鎻愪氦鏍¢獙锛氬嬀閫�${needCheckNum}涓� | 濉厖${filledNum}涓� | 鎴愬搧宸插~${hasProductCode}`
+        );
+
+        // 鍓嶇疆鎬绘牎楠岋細鎴愬搧缂栧彿鏈~鍐欙紝鐩存帴涓嶈Е鍙戜换浣曡嚜鍔ㄦ彁浜ゃ�愭牳蹇冨己鍖栥��
+        if (!hasProductCode) {
+          console.log("鈿狅笍 鎴愬搧缂栧彿鏈~鍐欙紝璺宠繃鑷姩鎻愪氦");
+          autoSubmitDebounceTimer = null;
+          return;
+        }
+
+        // 鍦烘櫙1锛氬乏渚ф棤鍕鹃�� 鈫� 浠呮垚鍝佺紪鍙锋湁鏁堝嵆鎻愪氦锛堝凡婊¤冻hasProductCode锛�
+        if (needCheckNum === 0) {
+          if (!submitLock.value) {
+            console.log("鉁� 鏃犻浂浠跺嬀閫夛紝鎴愬搧宸插~锛屾墽琛岃嚜鍔ㄦ彁浜わ紒");
+            saveData();
+          }
+        }
+        // 鍦烘櫙2锛氬乏渚ф湁鍕鹃�� 鈫� 鎴愬搧鏈夋晥 + 濉厖鏁�=鍕鹃�夋暟 鎵嶆彁浜わ紙宸叉弧瓒砲asProductCode锛�
+        else {
+          if (filledNum === needCheckNum && !submitLock.value) {
+            console.log("鉁� 闆朵欢鏁伴噺鍖归厤锛屾垚鍝佸凡濉紝鎵ц鑷姩鎻愪氦锛�");
+            saveData();
+          }
+        }
+        autoSubmitDebounceTimer = null;
+      }, 300); // 300ms闃叉姈锛岄伩鍏嶈緭鍏ラ绻佽Е鍙�
+    };
+
+    // 鐩戝惉鑷姩鎻愪氦鐩稿叧鏁版嵁鍙樺寲锛岃Е鍙戞牎楠�
+    watch([checkedCount, filledPartCount, finishedProductCode], () => checkAutoSubmit(), {
+      deep: true,
+      immediate: false,
+    });
+
+    // 鑷姩妫�娴嬪~鍏呯储寮曪紙鎴愬搧濉畬鍚庯紝鎸囧悜绗竴涓┖闆朵欢妗嗭級
+    const detectFillIndex = () => {
+      if (!finishedProductCode.value.trim()) {
+        fillIndex.value = -1;
+        return;
+      }
+      for (let i = 0; i < 10; i++) {
+        if (!rightPartCodes.value[i].trim()) {
+          fillIndex.value = i;
+          return;
+        }
+      }
+      fillIndex.value = 10;
+    };
+
+    // 鐩戝惉鎴愬搧/闆朵欢杈撳叆锛屾洿鏂板~鍏呯储寮�
+    watch([finishedProductCode, () => [...rightPartCodes.value]], detectFillIndex, {
+      immediate: true,
+      deep: true,
+    });
+
+    // 鐢熷懡鍛ㄦ湡锛氭寕杞芥椂鍔犺浇鍒濆鏁版嵁+鍚姩杞
+    onMounted(async () => {
+      await fetchLeftInitialData();
+      startPolling();
+      detectFillIndex();
+    });
+
+    // 鐢熷懡鍛ㄦ湡锛氬嵏杞芥椂娓呴櫎鎵�鏈夊畾鏃跺櫒/闃叉姈锛岄槻姝㈠唴瀛樻硠婕�
+    onUnmounted(() => {
+      stopPolling();
+      [checkDebounceTimer, destroyDelayTimer, autoSubmitDebounceTimer].forEach(
+        (t) => t && clearTimeout(t)
+      );
+    });
+
+    // 鏆撮湶妯℃澘鎵�闇�灞炴��/鏂规硶
+    return {
+      finishedProduct,
+      rightTopInput,
+      leftPartCodes,
+      rightPartCodes,
+      leftPartChecked,
+      signalStates,
+      signalLabels,
+      finishedProductCode,
+      checkLoading,
+      handlePartCheck,
+      saveData,
+      clearRightPart,
+      clearFinishedProductCode,
+    };
+  },
+};
 </script>
 
 <style scoped>
-.title {
-  line-height: 70vh;
-  text-align: center;
-  font-size: 28px;
-  color: orange;
+/* 鍩虹鏍峰紡閲嶇疆 */
+* {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+  font-family: "Microsoft Yahei", "PingFang SC", sans-serif;
+  scrollbar-width: none;
+  -ms-overflow-style: none;
 }
-</style>
\ No newline at end of file
+*::-webkit-scrollbar {
+  display: none;
+}
+
+body {
+  background: linear-gradient(135deg, #f0f4f8 0%, #e9ecef 100%);
+  min-height: 100vh;
+  overflow: hidden;
+  font-size: 14px;
+}
+
+/* 涓诲鍣� */
+.container {
+  display: flex;
+  width: 100%;
+  height: 100vh;
+  gap: 15px;
+  padding: 15px;
+  overflow: hidden;
+}
+
+/* 宸﹀彸鍖哄煙閫氱敤鏍峰紡 */
+.left-area,
+.right-area {
+  flex: 1;
+  width: 50%;
+  display: flex;
+  flex-direction: column;
+  gap: 15px;
+  padding: 18px;
+  background: #ffffff;
+  border: 1px solid #e2e8f0;
+  border-radius: 15px;
+  box-shadow: 0 6px 16px rgba(149, 157, 165, 0.15);
+  transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+}
+
+/* 宸︿晶椤堕儴-淇″彿鐏尯鍩� */
+.left-top {
+  background: #f8fafc;
+  padding: 20px 15px;
+  border-radius: 12px;
+  flex-shrink: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.signal-status {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  gap: 25px;
+  flex-shrink: 0;
+  width: 100%;
+}
+
+/* 涓婁笅鍖哄煙閫氱敤 */
+.left-bottom,
+.right-bottom {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  flex: 1;
+  overflow: hidden !important;
+}
+.right-top {
+  padding: 15px;
+  background: #f8fafc;
+  border-radius: 12px;
+  flex-shrink: 0;
+}
+
+/* 琛ㄥ崟琛岄�氱敤鏍峰紡 */
+.form-row {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  flex-wrap: nowrap;
+  padding: 6px 10px;
+  border-radius: 8px;
+  transition: all 0.2s ease;
+  height: 48px;
+  line-height: 48px;
+  flex-shrink: 0;
+  width: 100%;
+}
+.input-submit-row {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  flex-wrap: nowrap;
+  padding: 6px 10px;
+  border-radius: 8px;
+  transition: all 0.2s ease;
+  height: 48px;
+  line-height: 48px;
+  flex-shrink: 0;
+  width: 100%;
+}
+.form-row:hover {
+  background: #f8fafc;
+}
+
+/* 鎴愬搧缂栧彿琛屾牱寮� */
+.right-bottom .finished-product-row {
+  margin-top: 16px;
+  background: #eff6ff;
+  border-left: 4px solid #3b82f6;
+  padding-top: 2px;
+  padding-bottom: 2px;
+}
+.left-bottom .finished-product-row {
+  background: #eff6ff;
+  border-left: 4px solid #3b82f6;
+}
+
+/* 闆朵欢椤规牱寮� */
+.part-item {
+  border-bottom: 1px solid #f1f5f9;
+  margin-bottom: 3px;
+}
+.part-item:last-child {
+  border-bottom: none;
+}
+
+/* 鏍囩鏍峰紡 */
+.label {
+  width: 90px;
+  text-align: right;
+  color: #334155;
+  font-size: 15px;
+  font-weight: 600;
+  flex-shrink: 0;
+}
+
+/* 杈撳叆妗嗘牱寮� */
+.input-box {
+  flex: 1;
+  min-width: 100px;
+  height: 42px;
+  padding: 0 15px;
+  border: 1px solid #e2e8f0;
+  border-radius: 8px;
+  outline: none;
+  font-size: 15px;
+  transition: all 0.2s ease;
+  background-color: #ffffff;
+}
+.input-box:focus {
+  border-color: #3b82f6;
+  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);
+}
+.input-box:disabled {
+  background-color: #f1f5f9;
+  color: #64748b;
+  cursor: not-allowed;
+}
+.input-box::placeholder {
+  color: #94a3b8;
+  font-size: 14px;
+}
+
+/* 鎸夐挳閫氱敤鏍峰紡 */
+.btn {
+  width: 120px;
+  height: 42px;
+  padding: 0 16px;
+  border: none;
+  border-radius: 8px;
+  cursor: pointer;
+  font-size: 14px;
+  font-weight: 600;
+  transition: all 0.2s ease;
+  flex-shrink: 0;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  gap: 8px;
+  position: relative;
+  overflow: hidden;
+}
+.submit-input-btn {
+  width: 110px !important;
+  height: 42px !important;
+  flex: none !important;
+  padding: 0 15px !important;
+  font-size: 15px !important;
+}
+.submit-input-btn .icon {
+  width: 20px !important;
+  height: 20px !important;
+}
+.btn:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+  transform: none !important;
+  box-shadow: none !important;
+}
+.btn::after {
+  content: "";
+  position: absolute;
+  top: 0;
+  left: -100%;
+  width: 100%;
+  height: 100%;
+  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+  transition: all 0.5s ease;
+}
+.btn:hover::after {
+  left: 100%;
+}
+
+/* 鍥炬爣鏍峰紡 */
+.icon {
+  display: inline-block;
+  width: 18px;
+  height: 18px;
+  background-size: contain;
+  background-repeat: no-repeat;
+  background-position: center;
+}
+.icon-clear {
+  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/%3E%3C/svg%3E");
+}
+.icon-submit {
+  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M2.01 21L15 13.4 23 21V5H2.01V21zM17 15l-5-5-5 5V7h10v8z'/%3E%3C/svg%3E");
+}
+
+/* 鎸夐挳涓婚鏍峰紡 */
+.clear-btn {
+  background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
+  color: #fff;
+  padding: 0 12px;
+}
+.clear-btn:hover:not(:disabled) {
+  transform: translateY(-1px);
+  box-shadow: 0 4px 8px rgba(239, 68, 68, 0.15);
+}
+.save-btn {
+  background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
+  color: #fff;
+  padding: 0 12px;
+}
+.save-btn:hover:not(:disabled) {
+  transform: translateY(-1px);
+  box-shadow: 0 4px 8px rgba(59, 130, 246, 0.15);
+}
+
+/* 淇″彿鐏牱寮� */
+.signal-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 5px;
+  min-width: 60px;
+}
+.signal-label {
+  font-size: 14px;
+  color: #334155;
+  white-space: nowrap;
+  text-align: center;
+  font-weight: 600;
+}
+.signal-light {
+  width: 32px;
+  height: 32px;
+  border-radius: 50%;
+  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
+  transition: all 0.3s ease;
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.signal-light-inner {
+  width: 14px;
+  height: 14px;
+  border-radius: 50%;
+  background: white;
+  opacity: 0.9;
+  transition: all 0.3s ease;
+}
+.signal-active {
+  background: linear-gradient(135deg, #10b981 0%, #059669 100%);
+  box-shadow: 0 0 18px rgba(16, 185, 129, 0.7);
+  animation: pulse 2s infinite;
+}
+.signal-inactive {
+  background: linear-gradient(135deg, #94a3b8 0%, #64748b 100%);
+}
+@keyframes pulse {
+  0% {
+    box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.8);
+  }
+  70% {
+    box-shadow: 0 0 0 15px rgba(16, 185, 129, 0);
+  }
+  100% {
+    box-shadow: 0 0 0 0 rgba(16, 185, 129, 0);
+  }
+}
+
+/* 鑷畾涔夊閫夋鏍峰紡 */
+.checkbox-container {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  cursor: pointer;
+  font-size: 14px;
+  color: #334155;
+  flex-shrink: 0;
+}
+.part-checkbox {
+  display: none;
+}
+.checkbox-container:has(.part-checkbox:disabled) {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+.checkmark {
+  width: 22px;
+  height: 22px;
+  background-color: #f1f5f9;
+  border: 1px solid #cbd5e1;
+  border-radius: 4px;
+  transition: all 0.2s ease;
+  position: relative;
+}
+.part-checkbox:checked ~ .checkmark {
+  background-color: #3b82f6;
+  border-color: #3b82f6;
+}
+.checkmark:after {
+  content: "";
+  position: absolute;
+  display: none;
+  left: 7px;
+  top: 2px;
+  width: 6px;
+  height: 12px;
+  border: solid white;
+  border-width: 0 3px 3px 0;
+  transform: rotate(45deg);
+}
+.part-checkbox:checked ~ .checkmark:after {
+  display: block;
+}
+.checkbox-label {
+  font-size: 13px;
+  color: #475569;
+  width: 50px;
+}
+
+/* 鍝嶅簲寮忛�傞厤 */
+@media (max-width: 1200px) {
+  .container {
+    flex-direction: column;
+    height: auto;
+  }
+  .left-area,
+  .right-area {
+    width: 100%;
+    flex: none;
+  }
+  .signal-status {
+    flex-wrap: wrap;
+    justify-content: center;
+  }
+}
+
+@media (max-width: 768px) {
+  .form-row,
+  .input-submit-row {
+    flex-direction: column;
+    align-items: flex-start;
+    height: auto;
+    line-height: normal;
+  }
+  .label {
+    width: 100%;
+    text-align: left;
+    margin-bottom: 6px;
+  }
+  .input-box {
+    width: 100%;
+  }
+  .btn,
+  .submit-input-btn {
+    width: 100% !important;
+    margin-top: 6px;
+  }
+  .right-bottom .finished-product-row {
+    margin-top: 0 !important;
+  }
+}
+</style>

--
Gitblit v1.9.3