You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

5.4 KiB

账户中心套餐卡片与支付二维码弹窗设计

日期:2026-09-01
范围:只改 laic-frontendAccountView(及必要样式),不改后台、不改代理。

背景

账户中心流量购买与 SIM 充值已接上「选套餐 → 创建业务订单 → POST payments」主链路,但 UI 仍有两处缺口:

  1. 套餐选择仍是默认横排 t-radio 文本行,视觉上像未完成;卡片网格此前只定了方向,代码未落地。
  2. POST /v1/account/payments 已返回 providerPayload(实测为 AgPay 二维码 PNG),前端只 toast「请按返回凭证完成支付」,用户看不到码。

目标

  1. 流量页与 SIM 充值弹窗的套餐选择改为卡片网格(名称 / 容量 / 价格 / 有效期分区;选中高亮)。
  2. 购买并支付 / 去支付成功后弹出独立支付弹窗:金额、业务类型、订单号、二维码、关闭;提供「我已支付,刷新状态」。
  3. 流量与 SIM 共用同一支付弹窗逻辑。
  4. 验收:本地看卡片样式;走通流量下单 → 弹窗出码;刷新状态可读支付/订单结果。

非目标

  • 不引入微信支付 JS SDK / 原生扫码 SDK。
  • 不做自动轮询支付结果(仅手动刷新)。
  • 不改后端订单/支付协议、不修 payment_idempotency_key 空串唯一索引问题。
  • 不 seed SIM 卡/套餐;SIM 无数据时弹窗与卡片样式仍可用,但充值主路径可缺卡。
  • 不改发票、账号安全、概览其它区块。

现状

  • 文件:src/views/AccountView/AccountView.vue
  • 流量套餐:t-radio-group + t-radio,文案单行 名称 · GB · ¥x / 天
  • SIM 套餐:充值弹窗内同样横排 radio
  • 支付:createPaymentdescribePayment toast;urls.PAYMENT(id) 已存在但未用于 UI 刷新
  • 原型参考:jcWeb 3 通信卡充值有 carrier-package-grid 按钮卡;流量页旧稿是按 GB 快捷量,不是套餐卡

设计

1. 套餐卡片网格

保留 t-radio-group / t-radio 的无障碍选择与 v-model,只改结构与样式:

<t-radio-group class="package-card-grid" v-model="selectedTrafficPackageId" ...>
  <t-radio
    v-for="pkg in trafficPackages"
    :key="pkg.id"
    :value="pkg.id"
    class="package-card"
  >
    <strong>{{ pkg.name }}</strong>
    <b>{{ amountLabel }}</b>
    <em>{{ money(pkg.price) }}</em>
    <small v-if="pkg.validityDays">有效期 {{ pkg.validityDays }} </small>
  </t-radio>
</t-radio-group>

样式要点:

  • 网格:repeat(auto-fill, minmax(140px, 1fr)),间距 8–10px
  • 卡片:白底、细边框、圆角;选中态蓝边 + 浅蓝底(对齐现有 --blue / #f3f8fc
  • 隐藏原生 radio 圆点,整卡可点
  • SIM 充值弹窗复用同一 class;字段映射:amountGbmoneyFromFen(saleFeeFen)validityMonths

右侧「订单概要」不变;选中卡片后摘要可显示当前套餐金额(可选增强:有则显示,无则保持现文案)。

2. 支付二维码弹窗

新增状态:

payDialogOpen
payDialogLoading
payDialogRefreshing
payDialogError
payDialogMeta = {
  businessType,      // traffic_order | sim_recharge_order
  businessOrderId,
  transactionId,
  amountText,
  status,
  qrSrc,             // data:image/png;base64,... 或可直接用于 <img>
  title
}

触发:

  • createTrafficOrderAndPay / payTrafficOrder
  • SIM:createSimRechargeAndPay / payPendingSimOrder

成功拿到 payment 后:

  1. 解析 providerPayloadqrSrc
  2. 打开 t-dialogheader=微信支付 / 扫码支付
  3. toast 可保留一句短提示,或以弹窗为主、toast 降级为失败提示

弹窗内容:

  • 金额(大字)
  • 业务说明(云媒体流量 / 通信卡充值)+ 订单号
  • 二维码 <img :src="qrSrc">;无图时展示错误文案与「重新拉起支付」
  • 底部:关闭我已支付,刷新状态

刷新:

  • GET urls.PAYMENT(transactionId)
  • status === 'paid' | 'success':toast 成功,关弹窗,刷新订单/余额/SIM
  • 若仍 processing/unpaid:更新文案「尚未到账,可稍后重试」
  • 同步刷新对应业务列表(loadOrders / loadSim / balance)

3. providerPayload 解析

实测 payload 为 PNG 二进制经 JSON 字符串化后的内容。前端兼容:

  1. 已是 data:image/... → 直接用
  2. 已是 http(s):///... URL → 直接用
  3. 看起来像 base64(无空白或标准 base64)→ data:image/png;base64, + 原文
  4. 其它:标记无图,弹窗提示「支付已创建,但二维码无法展示」

不引入第三方 QR 库。

验收

  1. 账户 → 云媒体流量:套餐为卡片网格,选中高亮,不再是单行默认 radio。
  2. 点击「购买并支付」:创建订单 + 支付成功后弹出二维码弹窗,图可见。
  3. 「去支付」同样弹窗。
  4. 「我已支付,刷新状态」会请求支付详情并刷新订单列表。
  5. SIM 充值弹窗套餐同为卡片;有支付结果时共用支付弹窗(无卡时不阻塞流量验收)。

风险

  • 若后端偶发返回非 PNG/非 base64 的 payload,弹窗需有降级文案,不能白屏。
  • 取消订单后若仍写入空 payment_idempotency_key,可能再次误报「存在未付款订单」——属后端问题,本设计不修;测试前可清空串或取消后置 NULL。
  • 真机微信扫码入账依赖外部支付回调,本轮以「出码 + 刷新接口」为前端验收标准。