Files
matrix-screen-controller/核桃派软件源代码/app/static/views/wifi-settings.js
T

346 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { announce, apiJson } from "../core.js";
import { setControlBusy, setControlReady, setControlUnavailable } from "../interaction-feedback.js";
import { createLatchedDraftState } from "../wifi-draft-state.js";
const ssidInput = document.getElementById("wifi-ssid");
const passwordInput = document.getElementById("wifi-password");
const dhcpToggle = document.getElementById("wifi-dhcp-enabled");
const dhcpState = document.getElementById("wifi-dhcp-state");
const dhcpRow = dhcpToggle.closest(".wifi-dhcp-row");
const manualFields = document.getElementById("wifi-manual-fields");
const addressInput = document.getElementById("wifi-address");
const gatewayInput = document.getElementById("wifi-gateway");
const prefixInput = document.getElementById("wifi-prefix");
const dnsInput = document.getElementById("wifi-dns");
const immediateButton = document.getElementById("wifi-apply-immediate");
const nextBootButton = document.getElementById("wifi-apply-next-boot");
const activeStatus = document.getElementById("wifi-active-status");
const feedback = document.getElementById("wifi-feedback");
const delayInput = document.getElementById("wifi-prompt-delay");
const delaySaveButton = document.getElementById("wifi-prompt-delay-save");
const delayFeedback = document.getElementById("wifi-prompt-feedback");
const networkFields = new Map([
["ssid", { control: ssidInput, marker: document.getElementById("wifi-ssid-modified"), highlight: ssidInput }],
["password", { control: passwordInput, marker: document.getElementById("wifi-password-modified"), highlight: passwordInput }],
["dhcp", { control: dhcpToggle, marker: document.getElementById("wifi-dhcp-modified"), highlight: dhcpRow }],
["address", { control: addressInput, marker: document.getElementById("wifi-address-modified"), highlight: addressInput }],
["gateway", { control: gatewayInput, marker: document.getElementById("wifi-gateway-modified"), highlight: gatewayInput }],
["prefix", { control: prefixInput, marker: document.getElementById("wifi-prefix-modified"), highlight: prefixInput }],
["dns", { control: dnsInput, marker: document.getElementById("wifi-dns-modified"), highlight: dnsInput }],
]);
const networkDraft = createLatchedDraftState(networkFields.keys());
const delayDraft = createLatchedDraftState(["prompt_delay_seconds"]);
let wifiPending = false;
let delayPending = false;
let settingsLoaded = false;
let loadGeneration = 0;
let baseline = null;
function setFeedback(message, state = "idle") {
feedback.textContent = message;
feedback.dataset.state = state;
feedback.hidden = !message;
}
function setDelayFeedback(message, state = "idle") {
delayFeedback.textContent = message;
delayFeedback.dataset.state = state;
delayFeedback.hidden = !message;
}
function showDhcpMode() {
const enabled = dhcpToggle.checked;
dhcpState.textContent = enabled ? "开启" : "关闭";
manualFields.hidden = enabled;
addressInput.required = !enabled;
gatewayInput.required = !enabled;
}
function splitDns(value) {
return value.split(/[,,\s]+/u).map((item) => item.trim()).filter(Boolean);
}
function showStatus(data) {
const active = data.active || {};
if (active.connected) {
activeStatus.textContent = `当前已连接:${active.ssid || "未知 WiFi"} · ${active.ipv4_address || "等待 IPv4"}`;
} else if (data.available === false) {
activeStatus.textContent = `网络状态不可用:${data.error || "NetworkManager 无响应"}`;
} else {
activeStatus.textContent = "当前未连接 WiFi";
}
}
function formStateFromResponse(data) {
const saved = data.saved || {};
return {
ssid: saved.ssid || "",
password: saved.password || "",
passwordConfigured: Boolean(saved.password_configured),
dhcp: (saved.ipv4_mode || "dhcp") === "dhcp",
address: saved.address || "",
gateway: saved.gateway || "",
prefix: saved.prefix ?? "",
dns: (saved.dns_servers || []).join(", "),
promptDelay: data.prompt_delay_seconds ?? 30,
};
}
function applyFormState(state) {
if (!state) return;
ssidInput.value = state.ssid;
passwordInput.value = state.password;
passwordInput.placeholder = state.passwordConfigured ? "" : "请输入 8..63 字节密码";
dhcpToggle.checked = state.dhcp;
addressInput.value = state.address;
gatewayInput.value = state.gateway;
prefixInput.value = state.prefix;
dnsInput.value = state.dns;
delayInput.value = String(state.promptDelay);
showDhcpMode();
}
function setNetworkMarker(key, modified) {
const field = networkFields.get(key);
field.marker.hidden = !modified;
field.highlight.classList.toggle("wifi-field-modified", modified);
}
function clearNetworkDraft() {
networkDraft.clear();
networkFields.forEach((_field, key) => setNetworkMarker(key, false));
updateNetworkButtons();
}
function clearDelayDraft() {
delayDraft.clear();
updateDelayButton();
}
function updateNetworkButtons() {
if (wifiPending) return;
const ready = settingsLoaded && networkDraft.dirty;
immediateButton.classList.toggle("wifi-action-ready", ready);
nextBootButton.classList.toggle("wifi-action-ready", ready);
if (ready) {
setControlReady(immediateButton);
setControlReady(nextBootButton);
return;
}
const reason = settingsLoaded
? "请先修改 WiFi 或 DHCP 设置,再选择生效方式。"
: "请等待当前 WiFi 设置读取完成。";
setControlUnavailable(immediateButton, reason);
setControlUnavailable(nextBootButton, reason);
}
function updateDelayButton() {
if (delayPending) return;
const ready = settingsLoaded && delayDraft.dirty;
delaySaveButton.classList.toggle("wifi-action-ready", ready);
if (ready) {
setControlReady(delaySaveButton);
return;
}
setControlUnavailable(
delaySaveButton,
settingsLoaded ? "请先修改网络提示等待时间。" : "请等待当前网络提示设置读取完成。",
);
}
function markNetworkField(key) {
if (!settingsLoaded) return;
networkDraft.mark(key);
setNetworkMarker(key, true);
updateNetworkButtons();
}
function markDelayField() {
if (!settingsLoaded) return;
delayDraft.mark("prompt_delay_seconds");
updateDelayButton();
}
function syncForm(data) {
baseline = formStateFromResponse(data);
applyFormState(baseline);
settingsLoaded = true;
clearNetworkDraft();
clearDelayDraft();
showStatus(data);
}
export async function loadWifiSettings() {
const generation = ++loadGeneration;
settingsLoaded = false;
clearNetworkDraft();
clearDelayDraft();
try {
const data = await apiJson("/api/network/wifi");
if (generation !== loadGeneration) return;
syncForm(data);
setFeedback("");
setDelayFeedback("");
} catch (error) {
if (generation !== loadGeneration) return;
setFeedback(`WiFi 设置读取失败:${error.message}`, "error");
announce(`WiFi 设置读取失败:${error.message}`, true);
updateNetworkButtons();
updateDelayButton();
}
}
function buildRequest(activation) {
const ssid = ssidInput.value.trim();
if (!ssid) throw new Error("请输入 WiFi 名称(SSID)");
const password = passwordInput.value;
if (ssid !== baseline.ssid && !password) throw new Error("修改 WiFi 名称时必须填写新密码");
if (!password && !baseline.passwordConfigured) throw new Error("当前没有已保存密码,请填写 WiFi 密码");
const manual = !dhcpToggle.checked;
if (manual && !addressInput.value.trim()) throw new Error("关闭 DHCP 后必须填写静态 IPv4 地址");
if (manual && !gatewayInput.value.trim()) throw new Error("关闭 DHCP 后必须填写网关");
const rawPrefix = prefixInput.value.trim();
const prefix = rawPrefix ? Number(rawPrefix) : null;
if (rawPrefix && (!Number.isInteger(prefix) || prefix < 1 || prefix > 32)) {
throw new Error("子网前缀必须是 1..32 的整数");
}
return {
ssid,
password: password || null,
ipv4_mode: manual ? "manual" : "dhcp",
address: manual ? addressInput.value.trim() : null,
gateway: manual ? gatewayInput.value.trim() : null,
prefix,
dns_servers: manual ? splitDns(dnsInput.value) : [],
activation,
};
}
function captureNetworkBaseline(request) {
baseline = {
...baseline,
ssid: request.ssid,
password: request.password || baseline.password,
passwordConfigured: Boolean(request.password || baseline.passwordConfigured),
dhcp: request.ipv4_mode === "dhcp",
address: request.address || "",
gateway: request.gateway || "",
prefix: request.prefix ?? "",
dns: (request.dns_servers || []).join(", "),
};
}
async function applyWifi(activation) {
if (wifiPending || !networkDraft.dirty) return;
let request;
try {
request = buildRequest(activation);
} catch (error) {
setFeedback(error.message, "error");
announce(error.message, true);
return;
}
wifiPending = true;
setControlBusy(immediateButton, {
label: activation === "immediate" ? "正在保存…" : undefined,
message: "正在安全保存 WiFi 与 IPv4 配置…",
});
setControlBusy(nextBootButton, {
label: activation === "next_boot" ? "正在保存…" : undefined,
});
setFeedback(
activation === "immediate"
? "正在保存;设备随后会切换网络,当前页面可能断开。"
: "正在保存下次断电开机使用的网络配置…",
"pending",
);
try {
const result = await apiJson("/api/network/wifi", {
method: "PUT",
body: JSON.stringify(request),
});
captureNetworkBaseline(request);
clearNetworkDraft();
setFeedback(result.message, "success");
announce(result.message);
if (result.network) showStatus(result.network);
} catch (error) {
setFeedback(`WiFi 设置保存失败:${error.message}`, "error");
announce(`WiFi 设置保存失败:${error.message}`, true);
} finally {
wifiPending = false;
updateNetworkButtons();
}
}
function validatedPromptDelay() {
const delay = Number(delayInput.value);
if (!Number.isInteger(delay) || delay < 1 || delay > 3600) {
throw new Error("网络提示等待时间必须是 1..3600 秒的整数");
}
return delay;
}
async function savePromptDelay() {
if (delayPending || !delayDraft.dirty) return;
let delay;
try {
delay = validatedPromptDelay();
} catch (error) {
setDelayFeedback(error.message, "error");
announce(error.message, true);
return;
}
delayPending = true;
setControlBusy(delaySaveButton, {
label: "正在保存…",
message: "正在保存网络提示等待时间…",
});
setDelayFeedback("正在保存网络提示等待时间…", "pending");
try {
const result = await apiJson("/api/network/wifi/prompt-delay", {
method: "PUT",
body: JSON.stringify({ prompt_delay_seconds: delay }),
});
baseline = { ...baseline, promptDelay: result.prompt_delay_seconds };
delayInput.value = String(result.prompt_delay_seconds);
clearDelayDraft();
setDelayFeedback(result.message, "success");
announce(result.message);
} catch (error) {
setDelayFeedback(`网络提示等待时间保存失败:${error.message}`, "error");
announce(`网络提示等待时间保存失败:${error.message}`, true);
} finally {
delayPending = false;
updateDelayButton();
}
}
export function discardWifiSettingsDrafts() {
loadGeneration += 1;
applyFormState(baseline);
settingsLoaded = false;
clearNetworkDraft();
clearDelayDraft();
setFeedback("");
setDelayFeedback("");
}
for (const [key, field] of networkFields) {
if (key === "dhcp") continue;
field.control.addEventListener("input", () => markNetworkField(key));
}
dhcpToggle.addEventListener("change", () => {
showDhcpMode();
markNetworkField("dhcp");
});
delayInput.addEventListener("input", markDelayField);
immediateButton.addEventListener("click", () => applyWifi("immediate"));
nextBootButton.addEventListener("click", () => applyWifi("next_boot"));
delaySaveButton.addEventListener("click", savePromptDelay);
updateNetworkButtons();
updateDelayButton();