311 lines
10 KiB
Vue
311 lines
10 KiB
Vue
<script>
|
||
/** 带宽输入/滑块:按后端 options 的合法区间和步长吸附,单位为 Mbps,固定带宽不可调。 */
|
||
import "~/assets/styles/ConfigBandwidth.CWSH1WJZ.css";
|
||
import { nextTick } from "vue";
|
||
import { normalizeClass } from "vue";
|
||
import { computed } from "vue";
|
||
import { createElementBlock } from "vue";
|
||
import { withDirectives } from "vue";
|
||
import { createElementVNode } from "vue";
|
||
import { unref } from "vue";
|
||
import { defineComponent } from "vue";
|
||
import { createVNode } from "vue";
|
||
import { openBlock } from "vue";
|
||
import { t as _plugin_vue_export_helper_t } from "~/lib/_plugin-vue_export-helper.js";
|
||
import inputnumber from "primevue/inputnumber";
|
||
import tooltip from "primevue/tooltip";
|
||
var p = {
|
||
class: `bandwidth-field`,
|
||
},
|
||
m = {
|
||
class: `bandwidth-label`,
|
||
},
|
||
h = [`aria-label`],
|
||
g = [`aria-valuetext`, `disabled`, `value`, `min`, `max`],
|
||
_ = Object.assign(
|
||
_plugin_vue_export_helper_t(
|
||
defineComponent({
|
||
__name: `ConfigBandwidth`,
|
||
props: {
|
||
modelValue: {},
|
||
options: {},
|
||
disabled: {
|
||
type: Boolean,
|
||
},
|
||
},
|
||
emits: [`update:modelValue`, `change`],
|
||
setup(s, { emit: u }) {
|
||
let _ = s,
|
||
v = u,
|
||
y = computed(() =>
|
||
_.options
|
||
.map((e) => {
|
||
let t = e.type === `step` || e.type === `total`,
|
||
n = Number(t ? (e.min_value ?? e.value) : e.value),
|
||
r = Number(
|
||
t ? (e.max_value ?? e.min_value ?? e.value) : e.value,
|
||
),
|
||
i = Number(e.step),
|
||
a = Number.isFinite(i) && i > 0 ? i : 1;
|
||
return {
|
||
min: n,
|
||
max: Number(
|
||
(n + Math.floor((r - n) / a + 1e-7) * a).toFixed(8),
|
||
),
|
||
step: a,
|
||
};
|
||
})
|
||
.filter(
|
||
(e) =>
|
||
Number.isFinite(e.min) &&
|
||
Number.isFinite(e.max) &&
|
||
e.max >= e.min,
|
||
),
|
||
),
|
||
b = computed(() =>
|
||
y.value.length ? Math.min(...y.value.map((e) => e.min)) : 0,
|
||
),
|
||
x = computed(() =>
|
||
y.value.length ? Math.max(...y.value.map((e) => e.max)) : 0,
|
||
),
|
||
S = computed(() => b.value === x.value),
|
||
C = computed(() =>
|
||
_.modelValue == null ||
|
||
_.modelValue === `` ||
|
||
!Number.isFinite(Number(_.modelValue))
|
||
? null
|
||
: Number(_.modelValue),
|
||
),
|
||
w = computed(() =>
|
||
y.value.length
|
||
? S.value
|
||
? `当前线路带宽固定为 ${b.value} Mbps。`
|
||
: `设置服务器的带宽上限,可输入数值或拖动滑块。可选范围 ${b.value}–${x.value} Mbps,数值会自动匹配可用档位。`
|
||
: `当前线路暂无可用带宽。`,
|
||
);
|
||
function T(e) {
|
||
// 将任意输入映射到最近合法档位,并限制浮点小数精度,避免步长计算产生漂移。
|
||
let t = b.value,
|
||
n = 1 / 0;
|
||
for (let r of y.value) {
|
||
let i = Number(
|
||
Math.min(
|
||
r.max,
|
||
Math.max(
|
||
r.min,
|
||
r.min + Math.round((e - r.min) / r.step) * r.step,
|
||
),
|
||
).toFixed(8),
|
||
),
|
||
a = Math.abs(i - e);
|
||
a < n && ((t = i), (n = a));
|
||
}
|
||
return t;
|
||
}
|
||
let E = !1;
|
||
function D(e, t = !0) {
|
||
if (
|
||
_.disabled ||
|
||
S.value ||
|
||
e === null ||
|
||
!Number.isFinite(e) ||
|
||
!y.value.length
|
||
)
|
||
return;
|
||
let n = T(e);
|
||
C.value !== n &&
|
||
(v(`update:modelValue`, n), t ? v(`change`) : (E = !0));
|
||
}
|
||
function O(e) {
|
||
let t = e.target;
|
||
_.disabled ||
|
||
S.value ||
|
||
(D(Number(t.value), !1), (t.value = String(T(Number(t.value)))));
|
||
}
|
||
function k() {
|
||
// E 记录滑块期间是否真正改值,松开后只触发一次报价重算。
|
||
(E && !_.disabled && !S.value && v(`change`), (E = !1));
|
||
}
|
||
function A(e) {
|
||
// Home/End 选边界,方向键与翻页键选择跨区间的相邻有效档位。
|
||
if (_.disabled || S.value) return;
|
||
if (e.key === `Home` || e.key === `End`) {
|
||
(e.preventDefault(), D(e.key === `Home` ? b.value : x.value));
|
||
return;
|
||
}
|
||
let t = [`ArrowRight`, `ArrowUp`, `PageUp`].includes(e.key)
|
||
? 1
|
||
: [`ArrowLeft`, `ArrowDown`, `PageDown`].includes(e.key)
|
||
? -1
|
||
: 0;
|
||
if (!t) return;
|
||
e.preventDefault();
|
||
let n = C.value ?? b.value,
|
||
r = y.value.flatMap((e) => {
|
||
let r =
|
||
t > 0
|
||
? Math.max(
|
||
e.min,
|
||
e.min +
|
||
(Math.floor((n - e.min) / e.step + 1e-7) + 1) *
|
||
e.step,
|
||
)
|
||
: Math.min(
|
||
e.max,
|
||
e.min +
|
||
(Math.ceil((n - e.min) / e.step - 1e-7) - 1) * e.step,
|
||
);
|
||
return r >= e.min && r <= e.max ? [r] : [];
|
||
});
|
||
r.length && D(t > 0 ? Math.min(...r) : Math.max(...r));
|
||
}
|
||
async function j(t) {
|
||
// 等待组件内部事件结束,再把已归一化数值同步回输入框和读屏属性。
|
||
(await Promise.resolve(), await nextTick());
|
||
let n = t.originalEvent.target;
|
||
((n.value = C.value === null ? `` : String(C.value)),
|
||
C.value === null
|
||
? n.removeAttribute(`aria-valuenow`)
|
||
: n.setAttribute(`aria-valuenow`, String(C.value)));
|
||
}
|
||
function M(e) {
|
||
if (e.key === `ArrowUp` || e.key === `ArrowDown`)
|
||
return (
|
||
e.stopPropagation(),
|
||
A(e),
|
||
j({
|
||
originalEvent: e,
|
||
})
|
||
);
|
||
if (e.key === `Enter`)
|
||
return new Promise((t) => {
|
||
setTimeout(() => {
|
||
j({
|
||
originalEvent: e,
|
||
}).then(t);
|
||
}, 0);
|
||
});
|
||
}
|
||
return (e, n) => {
|
||
let u = inputnumber,
|
||
_ = tooltip;
|
||
return (
|
||
openBlock(),
|
||
createElementBlock(`div`, p, [
|
||
createElementVNode(`div`, m, [
|
||
(n[1] ||= createElementVNode(`span`, null, `带宽上限`, -1)),
|
||
withDirectives(
|
||
(openBlock(),
|
||
createElementBlock(
|
||
`button`,
|
||
{
|
||
class: `bandwidth-help`,
|
||
type: `button`,
|
||
"aria-label": unref(w),
|
||
},
|
||
[
|
||
...(n[0] ||= [
|
||
createElementVNode(
|
||
`i`,
|
||
{
|
||
class: `pi pi-question-circle`,
|
||
"aria-hidden": `true`,
|
||
},
|
||
null,
|
||
-1,
|
||
),
|
||
]),
|
||
],
|
||
8,
|
||
h,
|
||
)),
|
||
[
|
||
[
|
||
_,
|
||
{
|
||
value: unref(w),
|
||
autoHide: !1,
|
||
},
|
||
void 0,
|
||
{
|
||
top: !0,
|
||
},
|
||
],
|
||
],
|
||
),
|
||
]),
|
||
createElementVNode(
|
||
`div`,
|
||
{
|
||
class: normalizeClass([
|
||
`bandwidth-capacity`,
|
||
{
|
||
"is-disabled": s.disabled,
|
||
},
|
||
]),
|
||
onKeydownCapture: M,
|
||
},
|
||
[
|
||
createVNode(
|
||
u,
|
||
{
|
||
"aria-label": `带宽上限`,
|
||
disabled: s.disabled || unref(S),
|
||
"model-value": unref(C),
|
||
min: unref(b),
|
||
max: unref(x),
|
||
"use-grouping": !1,
|
||
"onUpdate:modelValue": D,
|
||
onBlur: j,
|
||
},
|
||
null,
|
||
8,
|
||
[`disabled`, `model-value`, `min`, `max`],
|
||
),
|
||
(n[2] ||= createElementVNode(
|
||
`span`,
|
||
{
|
||
class: `bandwidth-unit`,
|
||
"aria-hidden": `true`,
|
||
},
|
||
`Mbps`,
|
||
-1,
|
||
)),
|
||
],
|
||
34,
|
||
),
|
||
createElementVNode(
|
||
`input`,
|
||
{
|
||
class: `bandwidth-slider`,
|
||
type: `range`,
|
||
"aria-label": `带宽上限滑块`,
|
||
"aria-valuetext": `${unref(C) ?? unref(b)} Mbps`,
|
||
disabled: s.disabled || unref(S),
|
||
value: unref(C) ?? unref(b),
|
||
min: unref(b),
|
||
max: unref(x),
|
||
step: `any`,
|
||
onInput: O,
|
||
onChange: k,
|
||
onKeydown: A,
|
||
},
|
||
null,
|
||
40,
|
||
g,
|
||
),
|
||
])
|
||
);
|
||
};
|
||
},
|
||
}),
|
||
[[`__scopeId`, `data-v-45a9c613`]],
|
||
),
|
||
{
|
||
__name: `ConfigBandwidth`,
|
||
},
|
||
);
|
||
export {};
|
||
export default _;
|
||
</script>
|