fix: use fixed web duration choices

This commit is contained in:
Test User
2026-06-06 01:56:21 +08:00
parent ccea9af3af
commit 1a00aa49a8
5 changed files with 101 additions and 5 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ NetStable 是一个独立的 Go Web 测速项目,用于测试“打开网页
- 其他用户发起请求时返回“有其他用户正在测速,请稍等”。
- 启动时可设置程序理论最高带宽,例如 `-bandwidth-limit-mbps 30` 将测试流量限制到 30 Mbps。
- 页面展示当前设置的理论最高限值。
- 一次测试按阶段执行:先连续下载指定时长,再连续上传同样时长。例如页面填 `30`,就是下载 30 秒、上传 30 秒
- 一次测试按阶段执行:先连续下载指定时长,再连续上传同样时长。页面固定提供 `15s``30s` 两档,默认 `30s`
- 保存每次测试的完整样本曲线、摘要、脱敏后的用户 IP、地区和运营商信息;ISP 缺失时显示“未知运营商”。
- 所有历史测试用户数据通过网页和 `/api/records` 提供。
- 历史时序图展示不同时段的平均下载速度变化。
+14
View File
@@ -32,6 +32,20 @@ func TestStaticAssetsIncludeMobileRecordLayout(t *testing.T) {
}
}
func TestStaticAssetsUseFixedDurationChoices(t *testing.T) {
html, err := readAsset("index.html")
if err != nil {
t.Fatalf("read index.html: %v", err)
}
if !contains(html, `name="durationSeconds" type="radio" value="15"`) ||
!contains(html, `name="durationSeconds" type="radio" value="30"`) {
t.Fatalf("index.html missing fixed 15s/30s duration choices")
}
if contains(html, `id="durationSeconds" name="durationSeconds" type="number"`) {
t.Fatalf("index.html should not expose free-form duration number input")
}
}
func contains(text, pattern string) bool {
return strings.Contains(text, pattern)
}
+6 -1
View File
@@ -510,7 +510,12 @@ function value(id) {
}
function numberValue(id) {
return Number(document.getElementById(id).value);
const field = document.getElementById(id);
if (field) {
return Number(field.value);
}
const selected = document.querySelector(`input[name="${id}"]:checked`);
return selected ? Number(selected.value) : 0;
}
function average(values) {
+12 -3
View File
@@ -28,10 +28,19 @@
<input id="httpUrl" name="httpUrl" placeholder="可选,例如 https://example.com/">
</label>
<div class="grid-2">
<label>
<fieldset class="choice-field">
<span>每阶段时长(秒)</span>
<input id="durationSeconds" name="durationSeconds" type="number" min="1" max="600" value="30">
</label>
<div class="segmented-control" role="radiogroup" aria-label="每阶段时长">
<label>
<input name="durationSeconds" type="radio" value="15">
<span>15s</span>
</label>
<label>
<input name="durationSeconds" type="radio" value="30" checked>
<span>30s</span>
</label>
</div>
</fieldset>
<label>
<span>超时(毫秒)</span>
<input id="timeoutMillis" name="timeoutMillis" type="number" min="100" max="30000" value="3000">
+68
View File
@@ -112,6 +112,12 @@ label {
gap: 6px;
}
fieldset {
margin: 0;
padding: 0;
border: 0;
}
input {
width: 100%;
min-height: 40px;
@@ -128,6 +134,60 @@ input:focus {
border-color: var(--blue);
}
.choice-field {
display: grid;
gap: 6px;
}
.choice-field > span {
color: var(--muted);
font-size: 13px;
}
.segmented-control {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 4px;
min-height: 40px;
padding: 4px;
border: 1px solid var(--line);
border-radius: 8px;
background: #fbfdff;
}
.segmented-control label {
position: relative;
display: block;
min-width: 0;
}
.segmented-control input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.segmented-control span {
display: grid;
min-height: 32px;
place-items: center;
border-radius: 6px;
color: var(--muted);
font-size: 14px;
font-weight: 700;
cursor: pointer;
}
.segmented-control input:checked + span {
color: #ffffff;
background: var(--blue);
}
.segmented-control input:focus-visible + span {
outline: 2px solid rgba(37, 99, 235, 0.3);
outline-offset: 1px;
}
.grid-2 {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -332,6 +392,14 @@ tr:last-child td {
min-height: 44px;
}
.segmented-control {
min-height: 44px;
}
.segmented-control span {
min-height: 36px;
}
.metrics {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;