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