@herongxhr/network-status
Version:
A Vue 3 composable and component for monitoring network status.
203 lines (202 loc) • 6.98 kB
JavaScript
import { computed as g, ref as N, onMounted as O, onUnmounted as A, watch as P, defineComponent as z, createElementBlock as C, openBlock as S, normalizeStyle as u, normalizeClass as c, createElementVNode as d, createCommentVNode as V, toDisplayString as L, unref as f, Fragment as W, renderList as F } from "vue";
function D(v = {}) {
const {
initialStatus: p = 0,
checkUrl: s,
alertPosition: m = "bottom-right",
statusTexts: k = ["No Signal", "Very Weak", "Weak", "Good", "Excellent"],
alertMessages: y = [
"You are offline!",
"Network signal is lost!",
"Network signal is very weak!"
],
checkInterval: x = 8e3,
decimalPlaces: T = 0
} = v, B = g(() => s || (console.warn(
"No checkUrl provided. Using current host as default. For accurate network detection, please specify a custom URL (e.g., a health check endpoint)."
), `${window.location.origin}/`)), t = N(p), r = N(0), o = N(!1);
let e, l;
const i = g(
() => r.value.toFixed(Math.max(0, T))
), a = g(() => {
const n = Math.max(0, Math.min(4, Math.round(t.value)));
return Array.from({ length: 4 }, (U, M) => M < n);
}), b = g(() => {
const n = t.value;
return k[n];
}), E = g(() => {
if (!navigator.onLine) return y[0];
const n = t.value;
return n === 0 ? y[1] : n === 1 ? y[2] : "";
}), h = async () => {
if (!navigator.onLine) {
t.value = 0, r.value = 0;
return;
}
try {
const n = performance.now();
if (!(await fetch(B.value, { method: "HEAD", cache: "no-store" })).ok) throw new Error("Response not OK");
const M = performance.now();
r.value = M - n;
const w = r.value;
w > 1e3 ? t.value = 0 : w > 500 ? t.value = 1 : w > 300 ? t.value = 2 : w > 200 ? t.value = 3 : t.value = 4;
} catch {
t.value = 0, r.value = 0;
}
}, I = () => {
o.value = !1, e && clearTimeout(e);
};
return O(() => {
h(), l = setInterval(h, x), window.addEventListener("online", h), window.addEventListener("offline", () => {
t.value = 0, r.value = 0, o.value = !0, e = setTimeout(() => {
o.value = !1;
}, 3e3);
});
}), A(() => {
l && clearInterval(l), window.removeEventListener("online", h), window.removeEventListener("offline", () => {
t.value = 0;
});
}), P(
() => t.value,
(n) => {
n <= 1 || !navigator.onLine ? (o.value = !0, e = setTimeout(() => {
o.value = !1;
}, 3e3)) : (o.value = !1, e && clearTimeout(e));
}
), {
networkStatus: t,
latency: r,
// 返回请求耗时
formattedLatency: i,
signalBars: a,
statusText: b,
showAlert: o,
alertMessage: E,
checkNetworkStatus: h,
closeAlert: I,
alertPosition: m
};
}
const G = /* @__PURE__ */ z({
__name: "NetworkStatusTemplate",
props: {
position: { default: "top-right" },
positionOffset: { default: "1rem" },
containerClass: { default: "h-4 flex items-center gap-2 rounded text-xs" },
containerStyle: {},
signalBarsContainerClass: { default: "h-full py-[2px] flex items-end gap-[2px]" },
signalBarsContainerStyle: {},
signalBarClass: { default: "transition-all duration-300 flex-none" },
signalBarStyle: {},
statusTextContainerClass: { default: "flex flex-col" },
statusTextContainerStyle: {},
statusTextClass: { default: "h-full flex items-center text-gray-900 dark:text-gray-100" },
statusTextStyle: {},
latencyTextClass: { default: "text-xs text-gray-600 dark:text-gray-400" },
latencyTextStyle: {},
alertClass: { default: "p-2 rounded" },
alertStyle: {},
alertMessageClass: { default: "" },
alertMessageStyle: {},
closeButtonClass: { default: "absolute top-1 right-1 w-4 h-4 bg-transparent border-none cursor-pointer" },
closeButtonStyle: {},
statusTexts: { default: () => ["No Signal", "Very Weak", "Weak", "Good", "Excellent"] },
alertMessages: { default: () => [
"You are offline!",
"Network signal is lost!",
"Network signal is very weak!"
] },
checkUrl: { default: void 0 },
checkInterval: { default: 5e3 }
},
setup(v) {
const p = (e) => {
const [l, i] = e.split("-"), a = {};
return l === "top" ? a.top = s.positionOffset : l === "bottom" && (a.bottom = s.positionOffset), i === "left" ? a.left = s.positionOffset : i === "right" ? a.right = s.positionOffset : i === "center" && (a.left = "50%", a.transform = "translateX(-50%)"), a;
}, s = v, {
networkStatus: m,
formattedLatency: k,
signalBars: y,
statusText: x,
showAlert: T,
alertMessage: B,
closeAlert: t,
alertPosition: r
} = D({
initialStatus: 0,
checkUrl: s.checkUrl,
alertPosition: s.position,
statusTexts: s.statusTexts,
alertMessages: s.alertMessages,
checkInterval: s.checkInterval
}), o = g(() => {
const e = m.value;
return e === 0 ? "#6b7280" : e <= 1 ? "#ef4444" : e === 2 ? "#eab308" : e === 3 ? "#22c55e" : "#46cd7c";
});
return (e, l) => (S(), C("div", {
class: c([e.containerClass]),
style: u(e.containerStyle)
}, [
d("div", {
class: c([e.statusTextClass]),
style: u(e.statusTextStyle)
}, L(f(x)), 7),
d("div", {
class: c([e.signalBarsContainerClass]),
style: u(e.signalBarsContainerStyle)
}, [
(S(!0), C(W, null, F(f(y), (i, a) => (S(), C("div", {
key: a,
class: c([e.signalBarClass]),
style: u({
...e.signalBarStyle,
width: "2px",
height: (a + 1) / f(y).length * 100 + "%",
backgroundColor: i ? o.value : "var(--ground-100)"
})
}, null, 6))), 128))
], 6),
d("div", {
class: c([e.statusTextContainerClass]),
style: u(e.statusTextContainerStyle)
}, [
d("span", {
class: c([e.latencyTextClass]),
style: u({ color: o.value, ...e.latencyTextStyle })
}, L(f(k)) + "ms", 7)
], 6),
f(T) ? (S(), C("div", {
key: 0,
class: c([e.alertClass]),
style: u({
...e.alertStyle,
position: "fixed",
zIndex: "50",
...p(f(r))
})
}, [
d("span", {
class: c([e.alertMessageClass]),
style: u(e.alertMessageStyle)
}, L(f(B)), 7),
d("button", {
class: c([e.closeButtonClass]),
style: u(e.closeButtonStyle),
onClick: l[0] || (l[0] = //@ts-ignore
(...i) => f(t) && f(t)(...i))
}, l[1] || (l[1] = [
d("span", { class: "close-icon" }, null, -1)
]), 6)
], 6)) : V("", !0)
], 6));
}
}), R = (v, p) => {
const s = v.__vccOpts || v;
for (const [m, k] of p)
s[m] = k;
return s;
}, H = /* @__PURE__ */ R(G, [["__scopeId", "data-v-9684ad4c"]]);
export {
H as NetworkStatusTemplate,
D as useNetworkStatus
};