docs: explain configuration inputs and authentication components
This commit is contained in:
1 parent
dc32a74d7a
commit
3a53d2e58f
19 files changed
+72
No files matched your search
@@ -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 ||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 ?? []) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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,4 +1,5 @@
|
||||
<script>
|
||||
/** 添加磁盘的展示入口;只发出 add 事件,磁盘上限、默认容量和价格由父级配置组件决定。 */
|
||||
import "~/assets/styles/ConfigDiskAdd.C92e9ZuF.css";
|
||||
import { toDisplayString } from "vue";
|
||||
import { createCommentVNode } from "vue";
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in new issue
Block a user