| | |
| | | <template> |
| | | <div class="title"></div> |
| | | <div class="container"> |
| | | <!-- 左侧åºå - æ 任使 颿 注 --> |
| | | <div class="left-area"> |
| | | <div class="left-top"> |
| | | <!-- æé®+ä¿¡å· æ¨ªåæåå®¹å¨ --> |
| | | <div class="btn-signal-group"> |
| | | <!-- æé®ç» - ä¿®æ¹ä¸ºä¸ä¸æå --> |
| | | <div class="btn-group"> |
| | | <button |
| | | class="btn" |
| | | :class="isPLCStarted ? 'stop-btn' : 'start-btn'" |
| | | @click="handleToggle" |
| | | > |
| | | <i class="icon" :class="isPLCStarted ? 'icon-stop' : 'icon-start'"></i> |
| | | {{ isPLCStarted ? "å
³é" : "å¯å¨" }} |
| | | </button> |
| | | <button |
| | | class="btn" |
| | | :class="isPLCPaused ? 'resume-btn' : 'pause-btn'" |
| | | @click="handlePauseToggle" |
| | | :disabled="!isPLCStarted" |
| | | > |
| | | <i class="icon" :class="isPLCPaused ? 'icon-resume' : 'icon-pause'"></i> |
| | | {{ isPLCPaused ? "æ¢å¤" : "æå" }} |
| | | </button> |
| | | </div> |
| | | |
| | | <!-- ä¿¡å·ç¯ç» å 两个æé®å®½åº¦ + æ´ä½æ¾å¤§ --> |
| | | <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> |
| | | |
| | | <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)" |
| | | /> |
| | | <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"> |
| | | <span class="label">å½å
¥æ¡ï¼</span> |
| | | <!-- â
åªä¿ç纯å½å
¥æ¡ï¼æ¸
空/ä¿åæé®å·²å é¤ --> |
| | | <input type="text" class="input-box" v-model="rightTopInput" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="right-bottom"> |
| | | <div class="form-row tooling-board-row"> |
| | | <span class="short-label">å·¥è£
æ¿ç¼å·ï¼</span> |
| | | <input |
| | | type="text" |
| | | class="input-box short-input" |
| | | v-model="toolingBoardNo" |
| | | placeholder="请è¾å
¥å·¥è£
æ¿ç¼å·" |
| | | /> |
| | | <button class="btn clear-btn" @click="clearToolingBoardNo"> |
| | | <i class="icon icon-clear"></i>æ¸
é¤ |
| | | </button> |
| | | <button class="btn save-btn" @click="saveToolingBoardNo"> |
| | | <i class="icon icon-submit"></i>æäº¤ |
| | | </button> |
| | | </div> |
| | | <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 { |
| | | // åºç¡æ°æ®å®ä¹ - 1:1ç²¾å对æ¥å端 æåç¼å·+é¶ä»¶ç¼å· æ åä½å
¼å®¹ |
| | | const finishedProduct = ref(""); // 左侧æåç¼å·ï¼GetLeftInitialDataæ¥å£è¿åï¼ |
| | | const finishedProductId = ref(""); |
| | | const rightTopInput = ref(""); |
| | | const leftPartCodes = ref(Array(10).fill("")); // 左侧é¶ä»¶ç¼å·æ°ç» |
| | | const rightPartCodes = ref(Array(10).fill("")); // å³ä¾§é¶ä»¶ç¼å·æ°ç» |
| | | const leftPartChecked = ref(Array(10).fill(false)); |
| | | const toolingBoardNo = ref(""); |
| | | const fillIndex = ref(-1); |
| | | const leftPartIds = ref(Array(10).fill("")); |
| | | const finishedProductCode = ref(""); // å³ä¾§æåç¼å·ï¼å·¥è£
æ¿æ¥å£è¿åï¼ |
| | | |
| | | } |
| | | } |
| | | } |
| | | // PLCç¶æ |
| | | const isPLCStarted = ref(false); |
| | | const isPLCPaused = ref(false); |
| | | |
| | | // ä¿¡å·ç¸å
³ |
| | | const signalStates = ref([false, false, false, false, false]); |
| | | const signalLabels = ref([ |
| | | "å¿è·³ä¿¡å·", |
| | | "æ¥åä¿¡å·", |
| | | "èªå¨è¿è¡ä¿¡å·", |
| | | "å¨çº¿æ¨¡å¼ä¿¡å·", |
| | | "æ
éä¿¡å·", |
| | | ]); |
| | | |
| | | // 宿¶è½®è¯¢æ ¸å¿é
ç½® |
| | | let pollingTimer = null; |
| | | const pollingInterval = 5000; |
| | | let checkDebounceTimer = null; |
| | | let destroyDelayTimer = null; |
| | | const destroyDelayTime = 500; // â
æ ¸å¿ï¼å¡«å
+æ¸
空 é½å»¶è¿500æ¯«ç§ |
| | | let boardCodeDebounceTimer = null; |
| | | // â
æ°å¢ï¼èªå¨æäº¤é²æå®æ¶å¨ï¼é²æ¢éå¤æäº¤ |
| | | let autoSubmitDebounceTimer = null; |
| | | // â
æ°å¢ï¼æäº¤éï¼é²æ¢æ å¾éæ¶éå¤è§¦åæäº¤ |
| | | let submitLock = ref(false); |
| | | |
| | | // â
â
â
æ ¸å¿æ°å¢1ï¼è®¡ç®å±æ§ - 宿¶ç»è®¡å·¦ä¾§å¾éçå¤éæ¡æ°é (èªå¨æ´æ°) |
| | | const checkedCount = computed(() => { |
| | | // ç»è®¡leftPartCheckedæ°ç»ä¸ä¸ºtrueçæ°é |
| | | return leftPartChecked.value.filter((checked) => checked === true).length; |
| | | }); |
| | | |
| | | // â
â
â
æ ¸å¿æ°å¢2ï¼è®¡ç®å±æ§ - 宿¶ç»è®¡å³ä¾§å·²å¡«å
çé¶ä»¶æ°é (èªå¨æ´æ°) |
| | | const filledPartCount = computed(() => { |
| | | // ç»è®¡rightPartCodesæ°ç»ä¸æå¼(é空)çé¶ä»¶æ°é |
| | | return rightPartCodes.value.filter((code) => code.trim() !== "").length; |
| | | }); |
| | | |
| | | // â
è·å左侧åå§æ°æ® - å¯¹æ¥ /api/scanStation/GetLeftInitialData |
| | | const fetchLeftInitialData = async () => { |
| | | try { |
| | | console.log("æ£å¨è·å左侧åå§æ°æ®ï¼æåç¼å·+é¶ä»¶ç¼å·+å¾éç¶æ+é¶ä»¶IDï¼..."); |
| | | 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); |
| | | } |
| | | }; |
| | | |
| | | // â
è·åä¿¡å·+PLCç¶æ |
| | | 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; |
| | | const plcStatus = data.plcStatus || data.plc_status || {}; |
| | | isPLCStarted.value = plcStatus.isStarted ?? isPLCStarted.value; |
| | | // â
ä¿®å¤BUGï¼åä»£ç æ¯ isPLCStarted.value å¯¼è´æåç¶æèµå¼é误 |
| | | isPLCPaused.value = plcStatus.isPaused ?? isPLCPaused.value; |
| | | } |
| | | } catch (error) { |
| | | console.error("è·åä¿¡å·åPLCç¶æå¤±è´¥ï¼", error); |
| | | } |
| | | }; |
| | | |
| | | // â
ãæ ¸å¿ä¿®æ¹ãå·¥è£
æ¿æ¥è¯¢æ¥å£ - ææ°æ®å°±å¡«å
ï¼æ æ°æ®/失败 å®å
¨ä¿çåæå
容ï¼ä¸å任使¸
空æä½ |
| | | const fetchProductAndPartsByBoardCode = async (boardCode) => { |
| | | if (!boardCode.trim()) return; |
| | | try { |
| | | console.log(`å·¥è£
æ¿ç¼å·åæ´ï¼è¯·æ±æ°æ®ï¼${boardCode}`); |
| | | const response = await axios.get( |
| | | "/api/boxingDetail/GetProductAndPartsByBoardNo", |
| | | { |
| | | params: { palletCode: boardCode.trim() }, |
| | | timeout: 5000, |
| | | } |
| | | ); |
| | | const resData = response.data; |
| | | const isSuccess = resData.Status === true || resData.status === true; |
| | | // â
åªæãæ¥å£æå+æè¿åæ°æ®ãçæ¶åï¼ææ§è¡èµå¼è¦ç |
| | | if (isSuccess) { |
| | | const data = resData.Data || resData.data || {}; |
| | | // ææåç¼å·å°±èµå¼ï¼æ²¡æå°±ä¸æä½ |
| | | if (data.finishedProductCode) { |
| | | finishedProductCode.value = data.finishedProductCode; |
| | | } |
| | | // æé¶ä»¶å表就填å
ï¼æ²¡æå°±ä¸æä½ |
| | | const partsList = Array.isArray(data.partsList) ? data.partsList : []; |
| | | if (partsList.length > 0) { |
| | | for (let i = 0; i < 10; i++) { |
| | | if (partsList[i]) { |
| | | rightPartCodes.value[i] = partsList[i]; |
| | | } |
| | | } |
| | | } |
| | | console.log("â
å·¥è£
æ¿æ¥è¯¢æåï¼æåç¼å·+é¶ä»¶ç¼å·å¡«å
宿"); |
| | | } else { |
| | | // â
æ å¯¹åºæ°æ®ï¼åªå¼¹æç¤ºï¼ä¸æ¸
空任ä½å
容 |
| | | alert( |
| | | "è·åæ°æ®å¤±è´¥ï¼" + (resData.Message || resData.message || "æ 对åºå·¥è£
æ¿æ°æ®") |
| | | ); |
| | | } |
| | | } catch (error) { |
| | | // â
请æ±å¤±è´¥ï¼åªå¼¹æç¤ºï¼ä¸æ¸
空任ä½å
容 |
| | | alert("å·¥è£
æ¿æ°æ®è¯·æ±å¤±è´¥ï¼è¯·æ£æ¥ç½ç»ææ¥å£ï¼"); |
| | | console.error("å·¥è£
æ¿æ¥å£è¯·æ±å¤±è´¥ï¼", error); |
| | | } finally { |
| | | boardCodeDebounceTimer = null; |
| | | } |
| | | }; |
| | | |
| | | // å¯å¨/忢宿¶è½®è¯¢ |
| | | const startPolling = () => { |
| | | if (pollingTimer) clearInterval(pollingTimer); |
| | | fetchSignalAndPLCStates(); |
| | | pollingTimer = setInterval(fetchSignalAndPLCStates, pollingInterval); |
| | | }; |
| | | const stopPolling = () => { |
| | | if (pollingTimer) clearInterval(pollingTimer); |
| | | pollingTimer = null; |
| | | }; |
| | | |
| | | // PLCå¯å¨/å
³éé»è¾ |
| | | const handleToggle = async () => { |
| | | try { |
| | | const response = await axios.get("/api/scanStation/StartPLC", { |
| | | params: { isStop: isPLCStarted.value }, |
| | | timeout: 5000, |
| | | }); |
| | | const resData = response.data; |
| | | const isSuccess = resData.Status === true || resData.status === true; |
| | | if (isSuccess) { |
| | | isPLCStarted.value = !isPLCStarted.value; |
| | | isPLCPaused.value = false; |
| | | fetchSignalAndPLCStates(); |
| | | } else { |
| | | alert( |
| | | resData.Message || |
| | | resData.message || |
| | | (isPLCStarted.value ? "å
³é失败" : "å¯å¨å¤±è´¥") |
| | | ); |
| | | } |
| | | } catch (error) { |
| | | alert(isPLCStarted.value ? "å
³éPLC失败" : "å¯å¨PLC失败"); |
| | | console.error("PLCå¯å失败ï¼", error); |
| | | } |
| | | }; |
| | | |
| | | // PLCæå/æ¢å¤é»è¾ |
| | | const handlePauseToggle = async () => { |
| | | try { |
| | | const response = await axios.get("/api/scanStation/PausePLC", { |
| | | params: { isPause: !isPLCPaused.value }, |
| | | timeout: 5000, |
| | | }); |
| | | const resData = response.data; |
| | | const isSuccess = resData.Status === true || resData.status === true; |
| | | if (isSuccess) { |
| | | isPLCPaused.value = !isPLCPaused.value; |
| | | fetchSignalAndPLCStates(); |
| | | } else { |
| | | alert( |
| | | resData.Message || |
| | | resData.message || |
| | | (isPLCPaused.value ? "æ¢å¤å¤±è´¥" : "æå失败") |
| | | ); |
| | | } |
| | | } catch (error) { |
| | | alert(isPLCPaused.value ? "æ¢å¤PLC失败" : "æåPLC失败"); |
| | | console.error("PLCæåæ¢å¤å¤±è´¥ï¼", error); |
| | | } |
| | | }; |
| | | |
| | | // é¶ä»¶å¾éç¶æåæ´å¤ç |
| | | 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}æ°æ®åºIDä¸åå¨ï¼æ æ³æ´æ°æ«ç ç¶æï¼`); |
| | | leftPartChecked.value[index] = !isChecked; |
| | | return; |
| | | } |
| | | if (!partCode.trim()) { |
| | | alert(`é¶ä»¶${index + 1}ç¼å·ä¸ºç©ºï¼æ æ³æ´æ°æ«ç ç¶æï¼`); |
| | | leftPartChecked.value[index] = !isChecked; |
| | | return; |
| | | } |
| | | |
| | | if (checkDebounceTimer) clearTimeout(checkDebounceTimer); |
| | | checkDebounceTimer = setTimeout(async () => { |
| | | 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}æ«ç ç¶ææ´æ°æå`); |
| | | // â
å¾éç¶æååæ¶ï¼éç½®æäº¤é |
| | | submitLock.value = false; |
| | | } 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 { |
| | | checkDebounceTimer = null; |
| | | } |
| | | }, 500); |
| | | }; |
| | | |
| | | // â
æ ¸å¿ä¿®æ¹ï¼å»¶è¿0.5ç§å¡«å
+ å»¶è¿0.5ç§æ¸
空 忥æ§è¡ æ éæå¨æé® |
| | | const fillContent = () => { |
| | | if (!rightTopInput.value.trim()) return; |
| | | const inputValue = rightTopInput.value.trim(); |
| | | |
| | | // æ¸
餿§å®æ¶å¨ï¼é²æ¢é夿§è¡ |
| | | if (destroyDelayTimer) clearTimeout(destroyDelayTimer); |
| | | // ç»ä¸å»¶è¿500msæ§è¡ãå¡«å
+æ¸
空ã |
| | | destroyDelayTimer = setTimeout(() => { |
| | | if (!toolingBoardNo.value.trim()) { |
| | | toolingBoardNo.value = inputValue; |
| | | } else 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 } |
| | | ); |
| | | |
| | | // çå¬å·¥è£
æ¿ç¼å·ååæ¥è¯¢æ°æ® |
| | | watch( |
| | | toolingBoardNo, |
| | | (newVal) => { |
| | | const boardCode = newVal.trim(); |
| | | if (boardCode) { |
| | | if (boardCodeDebounceTimer) clearTimeout(boardCodeDebounceTimer); |
| | | boardCodeDebounceTimer = setTimeout( |
| | | () => fetchProductAndPartsByBoardCode(boardCode), |
| | | 300 |
| | | ); |
| | | } else { |
| | | if (boardCodeDebounceTimer) clearTimeout(boardCodeDebounceTimer); |
| | | } |
| | | }, |
| | | { immediate: false } |
| | | ); |
| | | |
| | | // å³ä¾§é¡µé¢æä½æ¹æ³ (å·²å 餿¸
空/ä¿åå½å
¥æ¡çæ¹æ³ï¼æ åä½) |
| | | const clearToolingBoardNo = () => (toolingBoardNo.value = ""); |
| | | const clearRightPart = (index) => (rightPartCodes.value[index] = ""); |
| | | const clearFinishedProductCode = () => (finishedProductCode.value = ""); |
| | | |
| | | // æäº¤å·¥è£
æ¿æ°æ®å°å端 |
| | | const saveToolingBoardNo = async () => { |
| | | if (!toolingBoardNo.value.trim()) { |
| | | alert("å·¥è£
æ¿ç¼å·ä¸è½ä¸ºç©ºï¼è¯·è¾å
¥ååæäº¤ï¼"); |
| | | return; |
| | | } |
| | | if (!finishedProductCode.value.trim()) { |
| | | alert("æåç¼å·ä¸è½ä¸ºç©ºï¼è¯·è¾å
¥ååæäº¤ï¼"); |
| | | return; |
| | | } |
| | | try { |
| | | const submitData = { |
| | | toolingBoardNo: toolingBoardNo.value.trim(), |
| | | finishedProductCode: finishedProductCode.value.trim(), |
| | | partsList: rightPartCodes.value.map((item) => item.trim()), |
| | | }; |
| | | console.log("â
æäº¤å·¥è£
æ¿æ°æ®ï¼", submitData); |
| | | const response = await axios.post( |
| | | "/api/boxingDetail/SaveToolingBoardNo", |
| | | submitData, |
| | | { timeout: 5000 } |
| | | ); |
| | | const resData = response.data; |
| | | const isSuccess = resData.Status === true || resData.status === true; |
| | | if (isSuccess) { |
| | | alert("â
æäº¤æåï¼"); |
| | | toolingBoardNo.value = ""; |
| | | finishedProductCode.value = ""; |
| | | rightPartCodes.value = Array(10).fill(""); |
| | | rightTopInput.value = ""; |
| | | fillIndex.value = -1; |
| | | // â
æäº¤æååï¼éç½®æäº¤é |
| | | submitLock.value = false; |
| | | } else { |
| | | alert("æäº¤å¤±è´¥ï¼" + (resData.Message || resData.message || "æªç¥é误")); |
| | | } |
| | | } catch (error) { |
| | | alert("æäº¤è¯·æ±å¤±è´¥ï¼è¯·æ£æ¥ç½ç»ææ¥å£ï¼"); |
| | | console.error("æäº¤æ¥å£å¤±è´¥ï¼", error); |
| | | // â
请æ±å¤±è´¥ä¹éç½®é |
| | | submitLock.value = false; |
| | | } |
| | | }; |
| | | |
| | | // â
â
â
æ ¸å¿å级ï¼èªå¨æäº¤å¤æé»è¾ - æ°å¢æ å¾éæ¶æåç¼å·å¡«å
å³æäº¤ |
| | | const checkAutoSubmit = () => { |
| | | // 鲿ï¼é²æ¢çæ¶é´å
夿¬¡è§¦åæäº¤ |
| | | if (autoSubmitDebounceTimer) clearTimeout(autoSubmitDebounceTimer); |
| | | autoSubmitDebounceTimer = setTimeout(() => { |
| | | const needCheckNum = checkedCount.value; // 左侧å¾éçæ°é |
| | | const filledNum = filledPartCount.value; // å³ä¾§å¡«å
çé¶ä»¶æ°é |
| | | const hasBoardNo = toolingBoardNo.value.trim() !== ""; // å·¥è£
æ¿æå¼ |
| | | const hasProductCode = finishedProductCode.value.trim() !== ""; // æåæå¼ |
| | | |
| | | console.log(`â
èªå¨æäº¤æ ¡éªï¼å·¦ä¾§å¾é${needCheckNum}个ï¼å³ä¾§å¡«å
${filledNum}个`); |
| | | |
| | | // 忝1ï¼å·¦ä¾§æå¾é â åæé»è¾ï¼é¶ä»¶å¡«å
æ°â¥å¾éæ° ææäº¤ |
| | | if (needCheckNum > 0) { |
| | | if (hasBoardNo && hasProductCode && filledNum >= needCheckNum) { |
| | | console.log("â
满足å¾éæ°éæ¡ä»¶ï¼æ§è¡èªå¨æäº¤ï¼"); |
| | | saveToolingBoardNo(); |
| | | } |
| | | } |
| | | // 忝2ï¼å·¦ä¾§æ å¾é â æ°å¢é»è¾ï¼å·¥è£
æ¿+æåé½æå¼ å°±æäº¤ (å éé²éå¤) |
| | | else { |
| | | if (hasBoardNo && hasProductCode && !submitLock.value) { |
| | | console.log("â
左侧æ å¾éï¼æåç¼å·å¡«å
å®æï¼æ§è¡èªå¨æäº¤ï¼"); |
| | | submitLock.value = true; // å é鲿¢éå¤æäº¤ |
| | | saveToolingBoardNo(); |
| | | } |
| | | } |
| | | autoSubmitDebounceTimer = null; |
| | | }, 300); |
| | | }; |
| | | |
| | | // â
â
â
æ ¸å¿æ°å¢4ï¼çå¬å
³é®æ°æ®ååï¼è§¦åèªå¨æäº¤æ ¡éª |
| | | watch( |
| | | [checkedCount, filledPartCount, toolingBoardNo, finishedProductCode], |
| | | () => { |
| | | checkAutoSubmit(); |
| | | }, |
| | | { deep: true, immediate: false } |
| | | ); |
| | | |
| | | // èªå¨æ£æµå¡«å
ç´¢å¼é»è¾ |
| | | const detectFillIndex = () => { |
| | | if (!toolingBoardNo.value.trim() || !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( |
| | | [toolingBoardNo, finishedProductCode, () => [...rightPartCodes.value]], |
| | | detectFillIndex, |
| | | { |
| | | immediate: true, |
| | | deep: true, |
| | | } |
| | | ); |
| | | |
| | | // é¡µé¢æè½½/å¸è½½çå½å¨æ |
| | | onMounted(async () => { |
| | | await fetchLeftInitialData(); |
| | | startPolling(); |
| | | detectFillIndex(); |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | stopPolling(); |
| | | if (checkDebounceTimer) clearTimeout(checkDebounceTimer); |
| | | if (destroyDelayTimer) clearTimeout(destroyDelayTimer); |
| | | if (boardCodeDebounceTimer) clearTimeout(boardCodeDebounceTimer); |
| | | if (autoSubmitDebounceTimer) clearTimeout(autoSubmitDebounceTimer); |
| | | }); |
| | | |
| | | return { |
| | | finishedProduct, |
| | | finishedProductId, |
| | | rightTopInput, |
| | | leftPartCodes, |
| | | rightPartCodes, |
| | | leftPartChecked, |
| | | leftPartIds, |
| | | toolingBoardNo, |
| | | isPLCStarted, |
| | | isPLCPaused, |
| | | signalStates, |
| | | signalLabels, |
| | | finishedProductCode, |
| | | handleToggle, |
| | | handlePauseToggle, |
| | | handlePartCheck, |
| | | clearToolingBoardNo, |
| | | saveToolingBoardNo, |
| | | 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", "Inter", sans-serif; |
| | | scrollbar-width: none; |
| | | -ms-overflow-style: none; |
| | | } |
| | | </style> |
| | | *::-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; |
| | | margin: 0; |
| | | 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); |
| | | } |
| | | |
| | | /* æé®+ä¿¡å· æ¨ªåæåå®¹å¨ - æ ¸å¿å¸å± */ |
| | | .btn-signal-group { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 20px; |
| | | width: 100%; |
| | | } |
| | | .btn-group { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | flex-shrink: 0; |
| | | } |
| | | .signal-status { |
| | | display: flex; |
| | | justify-content: flex-start; |
| | | align-items: center; |
| | | gap: 18px; |
| | | flex-shrink: 0; |
| | | padding: 0; |
| | | } |
| | | |
| | | .left-top { |
| | | background: #f8fafc; |
| | | padding: 15px; |
| | | border-radius: 12px; |
| | | 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%; |
| | | } |
| | | |
| | | .form-row:hover { |
| | | background: #f8fafc; |
| | | } |
| | | |
| | | .finished-product-row { |
| | | background: #eff6ff; |
| | | border-left: 4px solid #3b82f6; |
| | | } |
| | | |
| | | .tooling-board-row { |
| | | background: #f0fdf4; |
| | | border-left: 4px solid #22c55e; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | |
| | | .short-label { |
| | | width: 110px; |
| | | 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; |
| | | } |
| | | |
| | | .short-input { |
| | | width: 150px !important; |
| | | flex: none !important; |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | |
| | | .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-start { |
| | | 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='M8 5v14l11-7z'/%3E%3C/svg%3E"); |
| | | } |
| | | |
| | | .icon-stop { |
| | | 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='M6 6h12v12H6z'/%3E%3C/svg%3E"); |
| | | } |
| | | |
| | | .icon-pause { |
| | | 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='M6 19h4V5H6v14zm8-14v14h4V5h-4z'/%3E%3C/svg%3E"); |
| | | } |
| | | |
| | | .icon-resume { |
| | | 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='M11 5L6 9H2v6h4l5 4V5zm7 0v14c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2z'/%3E%3C/svg%3E"); |
| | | } |
| | | |
| | | .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"); |
| | | } |
| | | |
| | | /* æé®ç±»åæ ·å¼ - ä¸å */ |
| | | .start-btn { |
| | | background: linear-gradient(135deg, #10b981 0%, #059669 100%); |
| | | color: #fff; |
| | | } |
| | | .start-btn:hover:not(:disabled) { |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 4px 8px rgba(16, 185, 129, 0.2); |
| | | } |
| | | |
| | | .stop-btn { |
| | | background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); |
| | | color: #fff; |
| | | } |
| | | .stop-btn:hover:not(:disabled) { |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 4px 8px rgba(239, 68, 68, 0.2); |
| | | } |
| | | |
| | | .pause-btn { |
| | | background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); |
| | | color: #fff; |
| | | } |
| | | .pause-btn:hover:not(:disabled) { |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 4px 8px rgba(245, 158, 11, 0.2); |
| | | } |
| | | |
| | | .resume-btn { |
| | | background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%); |
| | | color: #fff; |
| | | } |
| | | .resume-btn:hover:not(:disabled) { |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 4px 8px rgba(139, 92, 246, 0.2); |
| | | } |
| | | |
| | | .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; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | width: 100%; |
| | | 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; |
| | | } |
| | | .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; |
| | | } |
| | | .btn-signal-group { |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | } |
| | | .signal-status { |
| | | width: 100%; |
| | | justify-content: flex-start; |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 768px) { |
| | | .form-row { |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | height: auto; |
| | | line-height: normal; |
| | | } |
| | | .label, |
| | | .short-label { |
| | | width: 100%; |
| | | text-align: left; |
| | | margin-bottom: 6px; |
| | | } |
| | | .input-box { |
| | | width: 100%; |
| | | } |
| | | .short-input { |
| | | width: 100% !important; |
| | | } |
| | | .btn { |
| | | width: 100%; |
| | | margin-top: 6px; |
| | | } |
| | | .btn-group { |
| | | flex-direction: column; |
| | | gap: 10px; |
| | | width: 100%; |
| | | } |
| | | } |
| | | </style> |