fix(frontend): 卡密兑换通知接入全局 notyf Toast 固定 id 流

- credits.js initCardPass 从 setTip 内联提示迁移为固定 id 三态 notyf 流
  (aksrt-card-pass-toast:load → success/danger 原位更新),
  成功后延迟 800ms 刷新以保证成功 Toast 可见,对齐其余资产操作。
- credits-wallet.php 删除 data-aksrt-card-tip 死元素;credits.css 删除
  .uc-card-pass__tip 死样式。
- 契约:toast-contract 将卡密兑换纳入固定 id 流清单并禁止 tip 回潮;
  card-pass-universal-redeem 新增 Toast 流断言(27/27 通过)。
- 验证:容器内真实抓取 /user/balance/ 确认弹窗正常渲染且无 tip 残留。
This commit is contained in:
2026-09-17 19:28:47 +08:00
parent 64ed47165d
commit 1889c9f048
5 changed files with 28 additions and 26 deletions
@@ -69,9 +69,25 @@ test('前台唯一兑换入口:页内联块与死文件已删除,JS 不再
assert.doesNotMatch(usersSchemaJs, /asset_points_card_/);
assert.doesNotMatch(chargeCard, /asset_points_card_/);
assert.doesNotMatch(chargeCard, /'pass_charge_desc'/);
// 失去宿主的内联块样式不回潮(弹窗仍在用 __input/__btn/__tip 子元素样式)。
// 失去宿主的内联块样式不回潮(弹窗仍在用 __input/__btn 子元素样式;
// 通知走全局 notyf Toast,__tip 内联提示样式已随迁移删除)。
assert.doesNotMatch(creditsCss, /^\.uc-card-pass\s*\{/m);
assert.doesNotMatch(creditsCss, /uc-card-pass__title|uc-card-pass__desc|\.uc-assets-col > \.uc-card-pass/);
assert.doesNotMatch(creditsCss, /uc-card-pass__title|uc-card-pass__desc|uc-card-pass__tip|\.uc-assets-col > \.uc-card-pass/);
});
test('兑换通知走全局 notyf Toast 固定 id 流,无内联 tip 残留', () => {
assert.match(creditsJs, /notyf\('正在兑换…', 'load', '', 'aksrt-card-pass-toast'\)/);
assert.match(creditsJs, /notyf\([^;]+?'danger', 5, 'aksrt-card-pass-toast'\)/);
assert.match(creditsJs, /notyf\([^;]+?'success', 5, 'aksrt-card-pass-toast'\)/);
// 初始化函数内不得再有 setTip 与 tip 元素查询(initCardPass 段)。
const from = creditsJs.indexOf('function initCardPass(');
const to = creditsJs.indexOf('function initBalanceCharge(', from + 1);
const slice = creditsJs.slice(from, to);
assert.ok(from !== -1 && to !== -1);
assert.doesNotMatch(slice, /setTip|data-aksrt-card-tip/);
assert.doesNotMatch(wallet, /data-aksrt-card-tip/);
// 成功后延迟刷新,保证成功 Toast 可见(与其余资产操作一致)。
assert.match(slice, /notyf\([^;]+?'success', 5, 'aksrt-card-pass-toast'\);[\s\S]{0,120}window\.setTimeout\(function \(\) \{ window\.location\.reload\(\); \}, 800\)/);
});
test('前端:VIP 缓存失效按后端识别类型', () => {
+4 -3
View File
@@ -223,6 +223,7 @@ test('balance center actions use direct fixed-id notyf flows', () => {
const flows = [
[signinController, 'aksrt-balance-signin-toast'],
[section('initCardPass', 'initBalanceCharge'), 'aksrt-card-pass-toast'],
[section('initBalanceCharge', 'initTransfer'), 'aksrt-balance-charge-toast'],
[section('initTransfer', 'initPointsExchange'), 'aksrt-balance-transfer-toast'],
[section('initPointsExchange', 'initWithdraw'), 'aksrt-balance-points-toast'],
@@ -233,7 +234,7 @@ test('balance center actions use direct fixed-id notyf flows', () => {
assert.match(source, new RegExp("notyf\\([^;]+?'load'[^;]+?'" + id + "'\\);"));
assert.match(source, new RegExp("notyf\\([^;]+?'danger'[^;]+?'" + id + "'\\);"));
assert.match(source, new RegExp("notyf\\([^;]+?'success'[^;]+?'" + id + "'\\);"));
assert.doesNotMatch(source, /setTip|data-(?:signin|aksrt-(?:charge|transfer|points-exchange|withdraw))-tip/);
assert.doesNotMatch(source, /setTip|data-(?:signin|aksrt-(?:card|charge|transfer|points-exchange|withdraw))-tip/);
});
assert.match(credits, /notyf\('等待支付结果…', 'load', '', 'aksrt-balance-charge-payment-toast'\)/);
@@ -261,8 +262,8 @@ test('balance center actions use direct fixed-id notyf flows', () => {
assert.match(context, /'signin_signed_today'\s*=>\s*\$signin_signed_today/);
assert.match(shell, /\.uc-header__actions\s*\{[^}]*display:\s*flex;[^}]*gap:\s*6px;/s);
assert.match(shell, /\.uc-header__message-btn\s*\{[^}]*position:\s*relative;/s);
assert.doesNotMatch(renderers, /data-(?:signin|aksrt-(?:charge|transfer|points-exchange|withdraw))-tip/);
assert.doesNotMatch(css, /\.uc-(?:modal|balance-card)__tip/);
assert.doesNotMatch(renderers, /data-(?:signin|aksrt-(?:card|charge|transfer|points-exchange|withdraw))-tip/);
assert.doesNotMatch(css, /\.uc-(?:card-pass|modal|balance-card)__tip/);
});
test('VIP purchase and points exchange use direct fixed-id notyf flows', () => {