docs: explain configuration inputs and authentication components

This commit is contained in:
cieanyau committed 2026-11-06 00:29:13 +08:00
1 parent dc32a74d7a
commit 3a53d2e58f
19 files changed
+72

No files matched your search

+10
View File
@@ -1,4 +1,8 @@
<script>
/**
* 登录页的微信扫码入口:申请二维码、轮询扫码状态、选择关联账号或转入绑定流程。
* 二维码轮询与倒计时共享代次 K;切换入口、收窄视口或卸载都使旧请求失效。
*/
import "~/assets/styles/AuthProviders.CgAWouSf.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
@@ -137,6 +141,7 @@ var fe = {
(G !== null && (clearInterval(G), (G = null)), (U.value = 0));
}
function Ee() {
// 扫码状态每两秒查询;二维码按服务端到期时间提前五秒刷新,缺省有效期为五分钟。
(J(), Y(), (W = window.setInterval(De, 2e3)));
let e = Math.floor(Date.now() / 1e3),
t = I.value?.expire_time ? I.value.expire_time - e : Ce;
@@ -146,6 +151,7 @@ var fe = {
}, 1e3)));
}
async function X() {
// 只在桌面展示区域申请新二维码,并核对图片、ticket 与 token 的完整性。
if (!k.value || L.value) return;
let e = ++K;
(Z(),
@@ -180,6 +186,7 @@ var fe = {
}
}
async function De() {
// 同一代次最多一个查询在途;Wait/Success/Expired/SelectClient/WaitBind 分别处理。
if (!I.value || q === K) return;
let e = K,
t = I.value;
@@ -259,6 +266,7 @@ var fe = {
(J(), Y());
}
function Q() {
// 同时废弃二维码和关联账号选择,不能让旧 ticket 在新一次扫码中继续使用。
(K++,
Z(),
(I.value = null),
@@ -270,6 +278,7 @@ var fe = {
(V.value = null));
}
function $(e) {
// 成功响应仍需经过 JWT 格式校验,登录完成后的返回路径由统一安全跳转规则约束。
if ((Z(), !isValidJwt(e))) {
z.value = `登录响应无效,请重新扫码;若持续失败,请联系客服。`;
return;
@@ -277,6 +286,7 @@ var fe = {
(C.loginWithJwt(e), K++, T.push(auth_redirect_r(w.query.redirect)));
}
async function Oe() {
// 多账号扫码只能提交当前返回列表中明确可登录的账号,且请求绑定当前二维码代次。
if (H.value) return;
if (
!I.value ||
+4
View File
@@ -1,4 +1,8 @@
<script>
/**
* 人机验证展示适配层:按 provider 呈现图形验证码或极验,输入通过 v-model 回传。
* refresh/verified/invalid 交由父级挑战状态处理,组件本身不判断业务接口是否允许操作。
*/
import "~/assets/styles/GeetestField.DiieTibK.css";
import "~/assets/styles/CaptchaField.BSN3ZbsD.css";
import { toDisplayString } from "vue";
+2
View File
@@ -1,4 +1,5 @@
<script>
/** 会员等级说明弹窗:按打开动作读取 /client_level,显示升级条件与各等级商品折扣。 */
import "~/assets/styles/ClientLevelDialog.DcYabp-W.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
@@ -93,6 +94,7 @@ var E = {
V = ref(``),
H = computed(() => z.value?.levels ?? []),
U = computed(() => {
// 将每个等级的 discount_list 转置为“商品行 × 等级列”,便于并排比较。
let e = {};
for (let t of H.value)
for (let n of t.discount_list ?? []) {
+6
View File
@@ -1,4 +1,5 @@
<script>
/** 极验 V4 浏览器适配:脚本加载复用,但每轮挑战独立创建/销毁,通过 verified 回传四项证明字段。 */
import "~/assets/styles/GeetestField.DiieTibK.css";
import { toDisplayString } from "vue";
import { onMounted } from "vue";
@@ -22,6 +23,7 @@ function _() {
return typeof e == `function` ? e : void 0;
}
function v() {
// 合并并发脚本加载,超时或失败后清空共享 Promise,使用户可重新加载而非永久卡住。
if (typeof window > `u` || typeof document > `u`)
return Promise.reject(Error(`请在浏览器中完成人机验证`));
let e = _();
@@ -57,6 +59,7 @@ function v() {
g);
}
function y(e) {
// 仅挑选后端协议要求的非空验证字段,不透传第三方对象的额外属性。
if (!e || typeof e != `object` || Array.isArray(e)) return null;
let t = e,
n = [`lot_number`, `captcha_output`, `pass_token`, `gen_time`];
@@ -79,6 +82,7 @@ function b(e) {
].every((e) => typeof t[e] == `function`);
}
function x(e, t) {
// 单轮挑战生命周期;销毁幂等,迟到或重复的 SDK 实例也要释放,避免遗留验证浮层。
let n = !1,
r = !1,
i = !1,
@@ -259,6 +263,7 @@ var S = {
(v++, _?.destroy(), (_ = null));
}
function j(e = !1) {
// 重建时先通知父级旧证明无效,再以代次验证 SDK 回调,拒绝旧挑战覆盖新状态。
if ((A(), m(`invalid`), (g.value = ``), !y || p.disabled)) {
h.value = `idle`;
return;
@@ -290,6 +295,7 @@ var S = {
});
}
function M() {
// 必须由用户点击显示 bind 验证面板;组件未就绪时先加载,准备完成后再打开。
if (!(p.disabled || k.value)) {
if (!_) {
j(!0);
+2
View File
@@ -1,4 +1,5 @@
<script>
/** 根据站点 oauth 配置显示第三方登录入口;启动跳转及错误处理复用 useOAuth。 */
import "~/assets/styles/GenericOAuthProviders.Bv8oNwXP.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
@@ -43,6 +44,7 @@ var x = {
onMounted(() => {
_.load();
try {
// 只读取浏览器会话中的待完成提示,不将该标志当作登录身份或授权凭证。
O.value =
sessionStorage.getItem(`console-oauth-pending`) === `1`;
} catch {}
@@ -1,4 +1,5 @@
<script>
/** 账号短信/邮件提醒偏好:available 表示渠道可用,enabled 表示用户选择,两者不能混为一项。 */
import "~/assets/styles/NotificationPreferences.DcH8Noxd.css";
import { toDisplayString } from "vue";
import { onMounted } from "vue";
@@ -71,6 +72,7 @@ var x = [`aria-labelledby`],
U = 0,
W = !1;
function G(e) {
// 四个字段必须是真正布尔值,避免字符串 "false" 被界面当作已开启。
let t = [
`email_enabled`,
`sms_enabled`,
@@ -99,6 +101,7 @@ var x = [`aria-labelledby`],
}
}
async function q(e, t) {
// 开启前确认渠道可用;乐观切换失败时回滚原值,成功时采用后端完整状态。
let n = e === `email_enabled` ? I.email_available : I.sms_available;
if (
W ||
+2
View File
@@ -1,4 +1,5 @@
<script>
/** 国际电话区号输入;地区列表按需请求,加载失败仍允许直接填写区号,不阻塞账号表单。 */
import "~/assets/styles/PhoneCodeField.CwyHXfML.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
@@ -56,6 +57,7 @@ var m = {
k = !0;
});
async function A() {
// 只接受 1–4 位且首位非零的区号,列表加载后复用;卸载后的结果不回写。
if (!(D.value || E.value.length)) {
((D.value = !0), (O.value = ``));
try {
@@ -1,4 +1,8 @@
<script>
/**
* 通用安全挑战弹窗:challenge 提供允许的方式,context 提供操作场景与验证令牌。
* verified 仅回传验证字段供父级重试原操作;非登录场景暂不开放 certification 认证。
*/
import "~/assets/styles/GeetestField.DiieTibK.css";
import "~/assets/styles/CaptchaField.BSN3ZbsD.css";
import "~/assets/styles/SecurityVerificationDialog.skYFo4v7.css";
@@ -128,6 +132,7 @@ var w = {
(clearInterval(z), clearTimeout(B), (z = void 0), (B = void 0));
}
function q() {
// 切换挑战/验证方式时统一清空密码、验证码、认证二维码及定时器,并废弃旧请求代次。
(L++,
ve(),
(A.value = ``),
@@ -168,6 +173,7 @@ var w = {
() => k.value === `email_code` || k.value === `phone_code`,
);
async function ye() {
// 登录异常使用 exception_login,其余操作必须传 action;图形挑战通过后才能发送短信/邮件。
if (K.value || P.value || !Y.value || !G.value || !E.visible)
return;
let e =
@@ -225,6 +231,7 @@ var w = {
}
}
function X() {
// 操作密码不能 trim,其他验证码去除边缘空白;验证令牌必须和所选挑战方式一同提交。
return {
security_verify_method: k.value,
security_verify_value:
@@ -247,6 +254,7 @@ var w = {
};
}
async function Q(e) {
// 实名认证结果串行查询,弹窗关闭或挑战改变后停止续排,不把旧认证结果交给新操作。
if (J(e) && j.value) {
try {
let t = await O.get(
@@ -274,6 +282,7 @@ var w = {
}
}
async function be() {
// 认证链接要求 HTTPS;二维码在浏览器按需生成,认证完成仍以状态接口为准。
if (
K.value ||
G.value?.disabled ||
+3
View File
@@ -1,4 +1,5 @@
<script>
/** 账号设置中的微信关注与推送开关;仅在站点启用扫码功能时读取关联状态。 */
import "~/assets/styles/WechatConnection.DI5ko1xy.css";
import { toDisplayString } from "vue";
import { onMounted } from "vue";
@@ -66,6 +67,7 @@ var w = {
I = !0,
L = 0;
async function R() {
// 状态字段限定为 0/1,重试或关闭功能会推进代次,旧响应不能恢复已隐藏的关联信息。
if (!j.scanLoginEnabled.value || !I) return;
let e = ++L;
((F.value = !0), (N.value = ``));
@@ -91,6 +93,7 @@ var w = {
}
}
async function z() {
// 仅已关注账号可切换推送,保存后重新回读服务端状态,不假设开关已生效。
if (!(!I || P.value || F.value || M.value?.is_subscribe !== 1)) {
((P.value = !0), (N.value = ``));
try {
@@ -1,4 +1,5 @@
<script>
/** 带宽输入/滑块:按后端 options 的合法区间和步长吸附,单位为 Mbps,固定带宽不可调。 */
import "~/assets/styles/ConfigBandwidth.CWSH1WJZ.css";
import { nextTick } from "vue";
import { normalizeClass } from "vue";
@@ -83,6 +84,7 @@ var p = {
: `当前线路暂无可用带宽。`,
);
function T(e) {
// 将任意输入映射到最近合法档位,并限制浮点小数精度,避免步长计算产生漂移。
let t = b.value,
n = 1 / 0;
for (let r of y.value) {
@@ -121,9 +123,11 @@ var p = {
(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));
@@ -156,6 +160,7 @@ var p = {
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)),
+6
View File
@@ -1,4 +1,8 @@
<script>
/**
* 配置页右侧结算摘要:父级负责报价和真正提交,本组件负责按钮状态、登录跳转及限购提示。
* cart/buy/recalc/retry 是交给父级处理的意图事件;展示价格不是可直接信任的最终成交金额。
*/
import "~/assets/styles/PromoCodeField.qFEkB6Hm.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
@@ -158,6 +162,7 @@ var O = {
X = computed(() => Y.value && (!J.checked.value || !J.allowed.value)),
Z;
function Q() {
// 编辑购物车需要携带 position;商品、数量或账号变化时重新验证对应购买场景。
if (!Y.value) {
J.reset();
return;
@@ -199,6 +204,7 @@ var O = {
(Z?.(), J.reset());
}));
function $(e) {
// 报价未就绪、请求进行中或限购未通过时均不触发提交;未登录保留当前配置页跳转目标。
if (H.canSubmit && H.quoteReady && !H.priceLoading && !q.value) {
if (!G.isLoggedIn)
return navigateTo({
@@ -1,4 +1,5 @@
<script>
/** 通用配置单选卡片组:父级提供 options,点击新值同时发出 v-model 更新与 change。 */
import "~/assets/styles/OsIcon.CAi4Iz2S.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
@@ -38,6 +39,7 @@ var d = [`aria-label`],
let h = c,
g = m;
function _(e) {
// 禁用或重复点击当前选项均不触发重算,避免无效报价请求。
h.disabled ||
h.modelValue === e ||
(g(`update:modelValue`, e), g(`change`));
+1
View File
@@ -1,4 +1,5 @@
<script>
/** 添加磁盘的展示入口;只发出 add 事件,磁盘上限、默认容量和价格由父级配置组件决定。 */
import "~/assets/styles/ConfigDiskAdd.C92e9ZuF.css";
import { toDisplayString } from "vue";
import { createCommentVNode } from "vue";
+5
View File
@@ -1,4 +1,5 @@
<script>
/** 磁盘容量选择卡:options 定义离散值或步长区间,v-model 保存容量,change 通知父级重算报价。 */
import "~/assets/styles/ConfigDiskCard.Dx9IqqQ_.css";
import { nextTick } from "vue";
import { toDisplayString } from "vue";
@@ -96,6 +97,7 @@ var m = {
: `容量 ${T.value}–${E.value} ${S.unit}`,
);
function A(e) {
// 在所有有效区间中寻找最近合法档位,不能只按全局 min/max 截断后提交任意数值。
let t = T.value,
n = 1 / 0;
for (let r of w.value) {
@@ -127,6 +129,7 @@ var m = {
(C(`update:modelValue`, n), t && C(`change`));
}
function M(e) {
// 拖动时更新容量但不逐帧重算;结束交互由 N 单独发出 change。
let t = e.target,
n = Number(t.value);
(j(n, !1), (t.value = String(A(n))));
@@ -135,6 +138,7 @@ var m = {
!S.disabled && !D.value && C(`change`);
}
function P(e) {
// 方向键跨区间寻找下一合法档位,避免跨越不允许购买的容量空档。
let t = [`ArrowRight`, `ArrowUp`].includes(e.key)
? 1
: [`ArrowLeft`, `ArrowDown`].includes(e.key)
@@ -165,6 +169,7 @@ var m = {
!S.disabled && S.removable && C(`remove`);
}
async function I(t) {
// PrimeVue 完成内部输入更新后再同步合法值与 aria 状态,避免显示值和实际容量不一致。
(await Promise.resolve(), await nextTick());
let n = t.originalEvent.target;
((n.value = String(O.value)),
@@ -1,4 +1,5 @@
<script>
/** 商品配置的占位骨架;busy 仅表示加载状态,真实配置/报价仍由对应产品组件负责。 */
import { renderList } from "vue";
import { renderSlot } from "vue";
import { createElementBlock } from "vue";
@@ -1,4 +1,5 @@
<script>
/** CPU/内存等资源档位选择器:离散资源显示卡片,区间资源显示范围选择和数值输入。 */
import "~/assets/styles/OsIcon.CAi4Iz2S.css";
import "~/assets/styles/ConfigResourceOption.Dj4VopnA.css";
import { toDisplayString } from "vue";
@@ -91,6 +92,7 @@ var h = {
: Number(_.modelValue),
);
function C(e) {
// 在当前区间按步长吸附,并将最大值收敛到实际可达档位后再通知父级。
if (_.disabled || e === null || !Number.isFinite(e) || !x.value)
return;
let { min: t, max: n, step: r } = x.value,
@@ -105,6 +107,7 @@ var h = {
(v(`update:modelValue`, a), v(`change`));
}
function w(e) {
// 选择新的范围时使用该范围最小值;当前值仍属于同一合法档位时不重复发事件。
if (_.disabled) return;
let t = y.value.find((t) => t.value === e);
if (!t) return;
@@ -1,4 +1,5 @@
<script>
/** 按商品 fields 渲染订购信息;explain 只读,其他字段按 id 写入父级 modelValue。 */
import "~/assets/styles/ProductCustomFields.B2QMnPil.css";
import { toDisplayString } from "vue";
import { renderList } from "vue";
@@ -46,6 +47,7 @@ var _ = {
S = f,
C = computed(() => x.fields.filter((e) => e.kind !== `explain`));
function w(e, t) {
// 后端自定义字段采用字符串值;复制对象更新,不能直接修改父级传入的对象。
S(`update:modelValue`, {
...x.modelValue,
[e]: t == null ? `` : String(t),
+3
View File
@@ -1,4 +1,5 @@
<script>
/** 优惠码输入框:提交后更新 v-model 并请求父级 recalc,不在组件内自行计算折扣。 */
import "~/assets/styles/PromoCodeField.qFEkB6Hm.css";
import { toDisplayString } from "vue";
import { normalizeClass } from "vue";
@@ -53,6 +54,7 @@ var y = [`open`],
w = ref(!1),
T = ref(`idle`);
function E() {
// 仅提交去空白的非空优惠码,w 用于把下一轮报价结束关联到本次应用动作。
if (x.disabled || x.loading) return;
let e = C.value.trim();
e &&
@@ -69,6 +71,7 @@ var y = [`open`],
}
return (
watch(
// 等父级 loading 从真变假后,根据实际折扣判断成功或无效,不把发事件当验证成功。
() => x.loading,
(e, t) => {
t &&
@@ -1,4 +1,5 @@
<script>
/** 实例敏感操作的短信验证码输入:先完成人机验证,再向当前账号发送 verify 场景验证码。 */
import "~/assets/styles/GeetestField.DiieTibK.css";
import "~/assets/styles/CaptchaField.BSN3ZbsD.css";
import "~/assets/styles/InstanceSmsCode.CsITfIEU.css";
@@ -73,6 +74,7 @@ var T = {
V,
H = !1;
async function U() {
// 发送成功才启动 60 秒倒计时;验证码挑战在使用后作废,失败交给统一挑战处理器更新。
if (
!(m.disabled || R.value || B.value || H) &&
((z.value = k.validationError()), !z.value)
@@ -106,6 +108,7 @@ var T = {
}
return (
onBeforeUnmount(() => {
// 离开实例操作时停止倒计时、废弃挑战并清空父级验证码,不能带到下一次操作。
((H = !0), clearInterval(V), k.invalidate(), (y.value = ``));
}),
(t, n) => {