表单行不再用 label 包 button 型控件

没有 for 的 label 会绑定内部第一个可关联控件。导入 ini 的「内容」行里
那是 DropFile 的按钮:悬停 textarea 会点亮投放区,点到行内空白处直接
弹出文件选择框。SelectBox 的开关按钮同理(InboundForm、RuleGroupForm、
ini 映射行),悬停整行都会点亮它。这些行改用 div,无障碍名换成控件
自己的 aria-label。ComboBox 的行保持 label——它第一个可关联控件是文本
输入框,label 转发过去正是想要的行为。
This commit is contained in:
2026-08-12 03:18:43 +08:00
parent 158dc9fca7
commit 25d60f9718
3 changed files with 39 additions and 16 deletions
+16 -6
View File
@@ -154,13 +154,15 @@ function submit() {
</span> </span>
</label> </label>
<label class="row"> <!-- SelectBox 的行不用 label 包:没有 for 的 label 绑定第一个可关联控件,
正好是它的开关按钮,悬停行内任何位置都会点亮它、点空白处会展开面板 -->
<div class="row">
<span class="k">类型</span> <span class="k">类型</span>
<span class="v"> <span class="v">
<SelectBox v-model="type" :options="typeOptions" /> <SelectBox v-model="type" :options="typeOptions" aria-label="类型" />
<span v-if="typeNote" class="note">{{ typeNote }}</span> <span v-if="typeNote" class="note">{{ typeNote }}</span>
</span> </span>
</label> </div>
<label class="row"> <label class="row">
<span class="k">端口</span> <span class="k">端口</span>
@@ -178,7 +180,7 @@ function submit() {
</span> </span>
</label> </label>
<label v-for="f in requiredExtras" :key="f.key" class="row"> <div v-for="f in requiredExtras" :key="f.key" class="row">
<span class="k">{{ f.key }}</span> <span class="k">{{ f.key }}</span>
<span class="v"> <span class="v">
<SelectBox <SelectBox
@@ -186,11 +188,19 @@ function submit() {
v-model="extras[f.key]" v-model="extras[f.key]"
:options="f.enum.map((opt) => ({ value: String(opt) }))" :options="f.enum.map((opt) => ({ value: String(opt) }))"
placeholder="请选择" placeholder="请选择"
:aria-label="f.key"
/>
<input
v-else
v-model="extras[f.key]"
class="mono"
type="text"
autocomplete="off"
:aria-label="f.key"
/> />
<input v-else v-model="extras[f.key]" class="mono" type="text" autocomplete="off" />
<span v-if="f.note" class="note">{{ f.note }}</span> <span v-if="f.note" class="note">{{ f.note }}</span>
</span> </span>
</label> </div>
</div> </div>
<footer class="dlg-foot"> <footer class="dlg-foot">
+15 -5
View File
@@ -111,7 +111,10 @@ function onBackdrop(event: MouseEvent) {
</header> </header>
<div class="body"> <div class="body">
<label class="row"> <!-- 不能用 label 包:没有 for 的 label 绑定内部第一个可关联控件,
正好是 DropFile 的按钮——悬停 textarea 会点亮投放区,点到行内
空白处还会弹出文件选择框 -->
<div class="row">
<span class="k">内容</span> <span class="k">内容</span>
<span class="v"> <span class="v">
<DropFile accept=".ini,.conf,.txt" @load="(_, content) => (text = content)" /> <DropFile accept=".ini,.conf,.txt" @load="(_, content) => (text = content)" />
@@ -121,9 +124,10 @@ function onBackdrop(event: MouseEvent) {
rows="8" rows="8"
placeholder="或把 ini 文本粘贴到这里" placeholder="或把 ini 文本粘贴到这里"
spellcheck="false" spellcheck="false"
aria-label="ini 内容"
></textarea> ></textarea>
</span> </span>
</label> </div>
<template v-if="entries.length > 0"> <template v-if="entries.length > 0">
<div class="row"> <div class="row">
@@ -138,12 +142,18 @@ function onBackdrop(event: MouseEvent) {
</ol> </ol>
</div> </div>
<label v-for="target in targets" :key="target" class="row"> <!-- 同上:SelectBox 的面板开关也是 button,label 会把悬停和点击都转给它 -->
<div v-for="target in targets" :key="target" class="row">
<span class="k map-name" :title="target">{{ target }}</span> <span class="k map-name" :title="target">{{ target }}</span>
<span class="v"> <span class="v">
<SelectBox v-model="mapping[target]" :options="outboundOptions" placeholder="映射到…" /> <SelectBox
v-model="mapping[target]"
:options="outboundOptions"
placeholder="映射到…"
:aria-label="`${target} 映射到`"
/>
</span> </span>
</label> </div>
</template> </template>
</div> </div>
+8 -5
View File
@@ -158,28 +158,31 @@ function onBackdrop(event: MouseEvent) {
</span> </span>
</label> </label>
<label class="row"> <!-- SelectBox 的行不用 label 包:没有 for 的 label 绑定第一个可关联控件,
正好是它的开关按钮,悬停行内任何位置都会点亮它、点空白处会展开面板 -->
<div class="row">
<span class="k">行为</span> <span class="k">行为</span>
<span class="v"> <span class="v">
<SelectBox v-model="behavior" :options="BEHAVIOR_OPTIONS" /> <SelectBox v-model="behavior" :options="BEHAVIOR_OPTIONS" aria-label="行为" />
<span class="note"> <span class="note">
清单每行带 DOMAIN,、IP-CIDR, 这类前缀的是 classical;纯域名列表是 domain; 清单每行带 DOMAIN,、IP-CIDR, 这类前缀的是 classical;纯域名列表是 domain;
纯 IP 网段列表是 ipcidr 纯 IP 网段列表是 ipcidr
</span> </span>
</span> </span>
</label> </div>
<label class="row"> <div class="row">
<span class="k">格式</span> <span class="k">格式</span>
<span class="v"> <span class="v">
<SelectBox <SelectBox
v-model="format" v-model="format"
:options="FORMAT_OPTIONS" :options="FORMAT_OPTIONS"
aria-label="格式"
@update:model-value="formatTouched = true" @update:model-value="formatTouched = true"
/> />
<span v-if="mrsConflict" class="bad">mrs 格式只支持 domain 和 ipcidr 两种行为</span> <span v-if="mrsConflict" class="bad">mrs 格式只支持 domain 和 ipcidr 两种行为</span>
</span> </span>
</label> </div>
<div class="divide" role="presentation"><span>或从文件导入</span></div> <div class="divide" role="presentation"><span>或从文件导入</span></div>