feat: update COM port terminology and refine button styling for touch support
This commit is contained in:
@@ -255,7 +255,7 @@ sudo systemctl restart dea.service
|
||||
.venv/bin/python -m unittest discover -s tests
|
||||
```
|
||||
|
||||
### MCU 串口清單是空的
|
||||
### MCU COM Port清單是空的
|
||||
|
||||
1. 確認 USB 裝置已插入。
|
||||
2. 確認執行服務的使用者有 serial port 權限。
|
||||
@@ -265,4 +265,4 @@ sudo systemctl restart dea.service
|
||||
echo $MCU_SERIAL_PORT
|
||||
```
|
||||
|
||||
4. 可在頁面按「刷新」重新讀取 serial ports。
|
||||
4. 可在頁面按「更新」重新讀取 serial ports。
|
||||
|
||||
+4
-4
@@ -13,7 +13,7 @@ def _serial_module():
|
||||
try:
|
||||
import serial
|
||||
except ImportError as exc:
|
||||
raise HTTPException(status_code=500, detail="MCU 串口功能需要安裝 pyserial") from exc
|
||||
raise HTTPException(status_code=500, detail="MCU COM Port功能需要安裝 pyserial") from exc
|
||||
return serial
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ def write_mcu_command_response(params: MCUWriteParams):
|
||||
|
||||
target_port = (params.port or MCU_SERIAL_PORT).strip()
|
||||
if not target_port:
|
||||
raise HTTPException(status_code=400, detail="MCU 串口不可為空")
|
||||
raise HTTPException(status_code=400, detail="MCU COM Port不可為空")
|
||||
|
||||
serial = _serial_module()
|
||||
payload = (cmd + "\r\n").encode("ascii", errors="strict")
|
||||
@@ -39,7 +39,7 @@ def write_mcu_command_response(params: MCUWriteParams):
|
||||
written = ser.write(payload)
|
||||
ser.flush()
|
||||
except serial.SerialException as exc:
|
||||
raise HTTPException(status_code=500, detail=f"MCU 串口傳送失敗: {str(exc)}") from exc
|
||||
raise HTTPException(status_code=500, detail=f"MCU COM Port傳送失敗: {str(exc)}") from exc
|
||||
|
||||
return {
|
||||
"status": "ok",
|
||||
@@ -55,7 +55,7 @@ def list_mcu_ports_response():
|
||||
try:
|
||||
from serial.tools import list_ports
|
||||
except ImportError as exc:
|
||||
raise HTTPException(status_code=500, detail="MCU 串口清單功能需要安裝 pyserial") from exc
|
||||
raise HTTPException(status_code=500, detail="MCU COM Port清單功能需要安裝 pyserial") from exc
|
||||
|
||||
ports = [
|
||||
{
|
||||
|
||||
+1
-1
@@ -111,7 +111,7 @@ def list_mcu_ports():
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"MCU 串口清單讀取失敗: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"MCU COM Port清單讀取失敗: {str(e)}")
|
||||
|
||||
|
||||
@app.post("/api/mcu/write")
|
||||
|
||||
+23
-23
@@ -1,4 +1,4 @@
|
||||
<template>
|
||||
<template>
|
||||
<div id="app" class="flex flex-col h-screen overflow-hidden">
|
||||
<!-- 頂部導覽列 -->
|
||||
<header
|
||||
@@ -87,7 +87,7 @@
|
||||
<select v-model="mcuPort"
|
||||
class="flex-1 bg-slate-50 dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-2 text-xs role-focus-primary focus:ring-1 role-focus-ring-primary outline-none transition-all text-slate-900 dark:text-gray-100 truncate">
|
||||
<option v-if="mcuPorts.length === 0" :value="mcuPort">
|
||||
{{ loadingMCUPorts ? '偵測串口中...' : (mcuPort || '無可用串口') }}
|
||||
{{ loadingMCUPorts ? '偵測COM Port中...' : (mcuPort || '無可用COM Port') }}
|
||||
</option>
|
||||
<option v-for="port in mcuPorts" :key="port.device" :value="port.device">
|
||||
{{ port.device }} - {{ port.description || 'Unknown' }}
|
||||
@@ -96,7 +96,7 @@
|
||||
<button @click="fetchMCUPorts"
|
||||
:disabled="loadingMCUPorts"
|
||||
class="text-[10px] font-bold bg-slate-200 dark:bg-gray-800 text-slate-600 dark:text-gray-300 hover:bg-slate-300 dark:hover:bg-gray-700 px-2 py-2 rounded transition-colors uppercase disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
刷新
|
||||
更新
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="mcuStatus" class="text-xs text-slate-500 dark:text-gray-400">{{ mcuStatus }}</p>
|
||||
@@ -407,37 +407,37 @@
|
||||
{{ isTouchInput ? '觸控拖曳微調' : '直接輸入 / shift + 滾輪微調' }}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<label v-if="!isTouchInput" class="block">
|
||||
<article v-if="!isTouchInput" class="block">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">δ</span>
|
||||
<div class="grid grid-cols-[2rem_1fr_2rem] gap-1">
|
||||
<button type="button" @pointerdown.prevent="startA1A2Repeat('delta', -1)" @pointerup="stopA1A2Repeat" @pointerleave="stopA1A2Repeat" @pointercancel="stopA1A2Repeat"
|
||||
class="stepper-button rounded-lg role-bg-secondary-soft role-text-secondary text-sm font-bold transition-colors">-</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
<input type="number" :value="formatFineCoeff(currentDelta)" :step="aFineStep"
|
||||
@change="setAFineDirect('delta', $event.target.value)" @keydown="onlyNumberKey"
|
||||
@wheel="handleAFineWheel('delta', $event)"
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-xs font-mono text-center role-text-secondary">
|
||||
<button type="button" @pointerdown.prevent="startA1A2Repeat('delta', 1)" @pointerup="stopA1A2Repeat" @pointerleave="stopA1A2Repeat" @pointercancel="stopA1A2Repeat"
|
||||
class="stepper-button rounded-lg role-bg-secondary-soft role-text-secondary text-sm font-bold transition-colors">+</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
</div>
|
||||
</label>
|
||||
</article>
|
||||
<button v-else type="button" @pointerdown.prevent="startAFineDrag('delta', $event)"
|
||||
class="touch-none select-none w-28 mx-auto bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center role-active-border-secondary active:ring-2 role-active-ring-secondary">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">δ</span>
|
||||
<span class="block max-w-full truncate text-base font-mono font-bold role-text-secondary" :title="formatFineCoeff(currentDelta)">{{ formatFineCoeff(currentDelta) }}</span>
|
||||
</button>
|
||||
<label v-if="!isTouchInput" class="block">
|
||||
<article v-if="!isTouchInput" class="block">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r</span>
|
||||
<div class="grid grid-cols-[2rem_1fr_2rem] gap-1">
|
||||
<button type="button" @pointerdown.prevent="startA1A2Repeat('r', -1)" @pointerup="stopA1A2Repeat" @pointerleave="stopA1A2Repeat" @pointercancel="stopA1A2Repeat"
|
||||
class="stepper-button rounded-lg bg-slate-200 dark:bg-gray-800 text-slate-700 dark:text-gray-300 text-sm font-bold transition-colors">-</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
<input type="number" :value="formatFineCoeff(currentR)" :step="aFineStep"
|
||||
@change="setAFineDirect('r', $event.target.value)" @keydown="onlyNumberKey"
|
||||
@wheel="handleAFineWheel('r', $event)"
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-xs font-mono text-center text-slate-700 dark:text-gray-300">
|
||||
<button type="button" @pointerdown.prevent="startA1A2Repeat('r', 1)" @pointerup="stopA1A2Repeat" @pointerleave="stopA1A2Repeat" @pointercancel="stopA1A2Repeat"
|
||||
class="stepper-button rounded-lg bg-slate-200 dark:bg-gray-800 text-slate-700 dark:text-gray-300 text-sm font-bold transition-colors">+</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
</div>
|
||||
</label>
|
||||
</article>
|
||||
<button v-else type="button" @pointerdown.prevent="startAFineDrag('r', $event)"
|
||||
class="touch-none select-none w-28 mx-auto bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center active:border-slate-400 active:ring-2 active:ring-slate-400/20">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r</span>
|
||||
@@ -505,13 +505,13 @@
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 mb-1 block">b 左移位元數 (Shift Bits)</label>
|
||||
<div v-if="!isTouchInput" class="grid grid-cols-[2rem_1fr_2rem] gap-1">
|
||||
<button type="button" @pointerdown.prevent="startShiftBitRepeat('b', -1)" @pointerup="stopShiftBitRepeat" @pointerleave="stopShiftBitRepeat" @pointercancel="stopShiftBitRepeat"
|
||||
class="rounded-lg role-bg-primary-soft role-hover-primary-soft role-text-primary text-sm font-bold transition-colors">-</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
<input type="number" :value="shiftBitsB" min="0"
|
||||
@change="setShiftBits('b', $event.target.value)" @keydown="onlyNumberKey"
|
||||
@wheel="handleShiftBitWheel('b', $event)"
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-base role-focus-primary outline-none transition-all text-slate-900 dark:text-gray-100">
|
||||
<button type="button" @pointerdown.prevent="startShiftBitRepeat('b', 1)" @pointerup="stopShiftBitRepeat" @pointerleave="stopShiftBitRepeat" @pointercancel="stopShiftBitRepeat"
|
||||
class="rounded-lg role-bg-primary-soft role-hover-primary-soft role-text-primary text-sm font-bold transition-colors">+</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
</div>
|
||||
<button v-else type="button" @pointerdown.prevent="startShiftBitDrag('b', $event)"
|
||||
class="touch-none select-none block w-24 mx-auto bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center role-active-border-primary active:ring-2 role-active-ring-primary">
|
||||
@@ -524,13 +524,13 @@
|
||||
<label class="text-sm text-slate-500 dark:text-gray-400 mb-1 block">a 左移位元數 (Shift Bits)</label>
|
||||
<div v-if="!isTouchInput" class="grid grid-cols-[2rem_1fr_2rem] gap-1">
|
||||
<button type="button" @pointerdown.prevent="startShiftBitRepeat('a', -1)" @pointerup="stopShiftBitRepeat" @pointerleave="stopShiftBitRepeat" @pointercancel="stopShiftBitRepeat"
|
||||
class="rounded-lg role-bg-secondary-soft role-hover-secondary-soft role-text-secondary text-sm font-bold transition-colors">-</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
<input type="number" :value="shiftBitsA" min="0"
|
||||
@change="setShiftBits('a', $event.target.value)" @keydown="onlyNumberKey"
|
||||
@wheel="handleShiftBitWheel('a', $event)"
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-base role-focus-primary outline-none transition-all text-slate-900 dark:text-gray-100">
|
||||
<button type="button" @pointerdown.prevent="startShiftBitRepeat('a', 1)" @pointerup="stopShiftBitRepeat" @pointerleave="stopShiftBitRepeat" @pointercancel="stopShiftBitRepeat"
|
||||
class="rounded-lg role-bg-secondary-soft role-hover-secondary-soft role-text-secondary text-sm font-bold transition-colors">+</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
</div>
|
||||
<button v-else type="button" @pointerdown.prevent="startShiftBitDrag('a', $event)"
|
||||
class="touch-none select-none block w-24 mx-auto bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center role-active-border-secondary active:ring-2 role-active-ring-secondary">
|
||||
@@ -591,37 +591,37 @@
|
||||
{{ isTouchInput ? '觸控拖曳微調' : '直接輸入 / shift + 滾輪微調' }}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<label v-if="!isTouchInput" class="block">
|
||||
<article v-if="!isTouchInput" class="block">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">δ</span>
|
||||
<div class="grid grid-cols-[2rem_1fr_2rem] gap-1">
|
||||
<button type="button" @pointerdown.prevent="startFixedA1A2Repeat('delta', -1)" @pointerup="stopFixedA1A2Repeat" @pointerleave="stopFixedA1A2Repeat" @pointercancel="stopFixedA1A2Repeat"
|
||||
class="stepper-button rounded-lg role-bg-secondary-soft role-text-secondary text-sm font-bold transition-colors">-</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
<input type="number" :value="formatFixedFineCoeff(fixedCurrentDelta)" :step="fixedAFineStep"
|
||||
@change="setFixedAFineDirect('delta', $event.target.value)" @keydown="onlyNumberKey"
|
||||
@wheel="handleFixedAFineWheel('delta', $event)"
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-xs font-mono text-center role-text-secondary">
|
||||
<button type="button" @pointerdown.prevent="startFixedA1A2Repeat('delta', 1)" @pointerup="stopFixedA1A2Repeat" @pointerleave="stopFixedA1A2Repeat" @pointercancel="stopFixedA1A2Repeat"
|
||||
class="stepper-button rounded-lg role-bg-secondary-soft role-text-secondary text-sm font-bold transition-colors">+</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
</div>
|
||||
</label>
|
||||
</article>
|
||||
<button v-else type="button" @pointerdown.prevent="startFixedAFineDrag('delta', $event)"
|
||||
class="touch-none select-none w-28 mx-auto bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center role-active-border-secondary active:ring-2 role-active-ring-secondary">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">δ</span>
|
||||
<span class="block max-w-full truncate text-base font-mono font-bold role-text-secondary" :title="formatFixedFineCoeff(fixedCurrentDelta)">{{ formatFixedFineCoeff(fixedCurrentDelta) }}</span>
|
||||
</button>
|
||||
<label v-if="!isTouchInput" class="block">
|
||||
<article v-if="!isTouchInput" class="block">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r</span>
|
||||
<div class="grid grid-cols-[2rem_1fr_2rem] gap-1">
|
||||
<button type="button" @pointerdown.prevent="startFixedA1A2Repeat('r', -1)" @pointerup="stopFixedA1A2Repeat" @pointerleave="stopFixedA1A2Repeat" @pointercancel="stopFixedA1A2Repeat"
|
||||
class="stepper-button rounded-lg bg-slate-200 dark:bg-gray-800 text-slate-700 dark:text-gray-300 text-sm font-bold transition-colors">-</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
<input type="number" :value="formatFixedFineCoeff(fixedCurrentR)" :step="fixedAFineStep"
|
||||
@change="setFixedAFineDirect('r', $event.target.value)" @keydown="onlyNumberKey"
|
||||
@wheel="handleFixedAFineWheel('r', $event)"
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-xs font-mono text-center text-slate-700 dark:text-gray-300">
|
||||
<button type="button" @pointerdown.prevent="startFixedA1A2Repeat('r', 1)" @pointerup="stopFixedA1A2Repeat" @pointerleave="stopFixedA1A2Repeat" @pointercancel="stopFixedA1A2Repeat"
|
||||
class="stepper-button rounded-lg bg-slate-200 dark:bg-gray-800 text-slate-700 dark:text-gray-300 text-sm font-bold transition-colors">+</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
</div>
|
||||
</label>
|
||||
</article>
|
||||
<button v-else type="button" @pointerdown.prevent="startFixedAFineDrag('r', $event)"
|
||||
class="touch-none select-none w-28 mx-auto bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center active:border-slate-400 active:ring-2 active:ring-slate-400/20">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r</span>
|
||||
|
||||
+1057
-1057
File diff suppressed because it is too large
Load Diff
+1506
-1491
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user