实训台按宽度自适应布局,可以收起
- 传感器卡片:宽时排一行,中等宽度分两行(开关卡片一行、滑块卡片一行),窄时每行 2 个 - 收起后只留一行状态(警示灯、照明灯、温湿度光照读数、LED 文字),传感器开关仍可点击;收起状态记在浏览器本地 - 设计器的撤销 / 重做按钮不再在窄屏下折行 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4c53caf7fa
commit
081ac6f924
2 files changed
+161
-5
No files matched your search
@@ -566,6 +566,8 @@ const noop = () => {}
|
|||||||
}
|
}
|
||||||
|
|
||||||
.actions button {
|
.actions button {
|
||||||
|
flex: none;
|
||||||
|
white-space: nowrap;
|
||||||
padding: 2px 10px;
|
padding: 2px 10px;
|
||||||
border: 1px solid #c5ccd5;
|
border: 1px solid #c5ccd5;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
@@ -580,7 +582,11 @@ const noop = () => {}
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tips {
|
.tips {
|
||||||
|
min-width: 0;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
color: #7a838e;
|
color: #7a838e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,27 @@ const DIGITAL_INPUTS: { key: DigitalKey; label: string }[] = [
|
|||||||
|
|
||||||
const showMore = ref(false)
|
const showMore = ref(false)
|
||||||
|
|
||||||
|
// 收起后只留一行状态,给窗体显示区腾地方;收起与否记在浏览器本地
|
||||||
|
const COLLAPSED_KEY = 'iot-sim:board-collapsed'
|
||||||
|
const collapsed = ref(readCollapsed())
|
||||||
|
|
||||||
|
function readCollapsed() {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem(COLLAPSED_KEY) === '1'
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCollapsed(value: boolean) {
|
||||||
|
collapsed.value = value
|
||||||
|
try {
|
||||||
|
localStorage.setItem(COLLAPSED_KEY, value ? '1' : '0')
|
||||||
|
} catch {
|
||||||
|
// 浏览器禁用本地存储时只是记不住,不影响使用
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 拖动滑块时先显示本地值,避免服务器回传前滑块跳动;发送做节流
|
// 拖动滑块时先显示本地值,避免服务器回传前滑块跳动;发送做节流
|
||||||
const dragging = ref<Partial<Record<AnalogKey, number>>>({})
|
const dragging = ref<Partial<Record<AnalogKey, number>>>({})
|
||||||
let pending: Partial<Record<AnalogKey, number>> = {}
|
let pending: Partial<Record<AnalogKey, number>> = {}
|
||||||
@@ -59,7 +80,37 @@ const ledText = computed(() => props.board?.ledText ?? '')
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="board" :class="{ disconnected: !board }">
|
<!-- 收起时:一行状态,传感器开关仍然可以点 -->
|
||||||
|
<div v-if="collapsed" class="board collapsed" :class="{ disconnected: !board }">
|
||||||
|
<span class="board-name">虚拟实训台</span>
|
||||||
|
<span class="io out warn" :class="{ lit: alarmOn }"><i />警示灯</span>
|
||||||
|
<span class="io out" :class="{ lit: streetLampOn }"><i />照明灯 1</span>
|
||||||
|
<span class="summary-readings">
|
||||||
|
<span v-for="key in ['temperature', 'humidity', 'light'] as AnalogKey[]" :key="key">
|
||||||
|
{{ ANALOG_INFO[key].label }} <b>{{ analog(key) }}{{ ANALOG_INFO[key].unit }}</b>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
v-for="input in DIGITAL_INPUTS"
|
||||||
|
:key="input.key"
|
||||||
|
class="io chip"
|
||||||
|
:class="{ lit: digital[input.key] }"
|
||||||
|
:disabled="!board"
|
||||||
|
:title="`点击切换${input.label}`"
|
||||||
|
@click="toggle(input.key)"
|
||||||
|
>
|
||||||
|
<i />{{ input.label }}
|
||||||
|
</button>
|
||||||
|
<span v-if="ledText" class="summary-led" :title="ledText">LED:{{ ledText }}</span>
|
||||||
|
<button class="link expand" @click="setCollapsed(false)">展开 ▾</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="board" :class="{ disconnected: !board }">
|
||||||
|
<div class="board-header">
|
||||||
|
<span class="board-name">虚拟实训台</span>
|
||||||
|
<button class="link" title="收起后只显示一行状态,给窗体留出更多地方" @click="setCollapsed(true)">收起 ▴</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 顶棚:红外对射、警示灯(DO0)、摄像头 -->
|
<!-- 顶棚:红外对射、警示灯(DO0)、摄像头 -->
|
||||||
<div class="ceiling">
|
<div class="ceiling">
|
||||||
<button class="device beam-end" :class="{ active: digital.infrared }" title="点击遮挡/恢复光束" @click="toggle('infrared')">
|
<button class="device beam-end" :class="{ active: digital.infrared }" title="点击遮挡/恢复光束" @click="toggle('infrared')">
|
||||||
@@ -218,6 +269,62 @@ const ledText = computed(() => props.board?.ledText ?? '')
|
|||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
/* 布局按实训台自身宽度调整(打开设计器时右栏会变窄) */
|
||||||
|
container-type: inline-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin: -2px 0 -4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-name {
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.board.collapsed {
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px 12px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-readings {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-readings b {
|
||||||
|
color: #9fd3ff;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip {
|
||||||
|
padding: 1px 6px;
|
||||||
|
background: var(--card);
|
||||||
|
border: 1px solid #4a566a;
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip:hover:not(:disabled) {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-led {
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 16em;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
color: #ff6b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand {
|
||||||
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board.disconnected {
|
.board.disconnected {
|
||||||
@@ -319,14 +426,33 @@ button {
|
|||||||
animation: led-scroll 12s linear infinite;
|
animation: led-scroll 12s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 传感器 */
|
/* 传感器:中等宽度时 4 个开关卡片一行、两个滑块卡片一行;够宽时按实训台顺序排成一行;更窄时每行 2 格 */
|
||||||
.sensors {
|
.sensors {
|
||||||
display: grid;
|
display: grid;
|
||||||
/* 照明灯、人体红外、温湿度(2 格)、光照(2 格)、烟雾、火焰排成一行 */
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
grid-template-columns: repeat(8, minmax(0, 1fr));
|
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sensors .wide {
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (min-width: 1100px) {
|
||||||
|
.sensors {
|
||||||
|
grid-template-columns: repeat(8, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.sensors .wide {
|
||||||
|
order: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (max-width: 560px) {
|
||||||
|
.sensors {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -424,11 +550,15 @@ button {
|
|||||||
|
|
||||||
.slider {
|
.slider {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 3em 1fr 4.5em;
|
grid-template-columns: 4.5em minmax(60px, 1fr) 4.5em;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.slider span {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.slider input {
|
.slider input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
accent-color: var(--accent);
|
accent-color: var(--accent);
|
||||||
@@ -446,6 +576,21 @@ button {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 窄时串口服务器单独占一行,四个串口横排 */
|
||||||
|
@container (max-width: 760px) {
|
||||||
|
.modules {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ports {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ports .port-list {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.module {
|
.module {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -505,6 +650,11 @@ button {
|
|||||||
box-shadow: 0 0 6px #51cf66;
|
box-shadow: 0 0 6px #51cf66;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.io.warn.lit i {
|
||||||
|
background: #ff3b30;
|
||||||
|
box-shadow: 0 0 6px #ff3b30;
|
||||||
|
}
|
||||||
|
|
||||||
.port-list {
|
.port-list {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
|
|||||||
Reference in new issue
Block a user