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