mirror of
https://gitee.com/bootx/dax-pay-h5
synced 2026-08-09 22:45:58 +08:00
refactor(h5/code-pay): 抽取码牌成功结果页为独立组件并对齐聚合上浮卡片风格
This commit is contained in:
288
src/mobile/views/code-pay/shared/CodePayResultCard.vue
Normal file
288
src/mobile/views/code-pay/shared/CodePayResultCard.vue
Normal file
@@ -0,0 +1,288 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 码牌支付 - 成功结果页组件
|
||||
*
|
||||
* 视觉对齐聚合 AggregateResultCard(上浮卡片 + 实心彩色图标 + border-top 摘要表格),
|
||||
* 保留码牌特色:大金额独占行 + 入场动画。
|
||||
* 通道品牌色由父级注入(微信绿 / 支付宝蓝),图标/标题/按钮跟随通道色;
|
||||
* 近黑色品牌(抖音)在深色下主按钮反色为浅底深字。
|
||||
*/
|
||||
import { computed, unref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useDesignSetting } from '@/shared/hooks/setting/useDesignSetting'
|
||||
|
||||
defineOptions({ name: 'CodePayResultCard' })
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
/** 大金额(元字符串),独占一行展示 — 码牌特色 */
|
||||
amount: string
|
||||
/** 标题文案(已 $t,留空走 i18n codePay.paySuccess) */
|
||||
title?: string
|
||||
/** 副提示文案(已 $t,留空走 i18n codePay.paidTip) */
|
||||
tip?: string
|
||||
/** 收款商户名 */
|
||||
merchantName?: string
|
||||
/** 订单号 */
|
||||
orderNo?: string
|
||||
/** 品牌色(浅色) */
|
||||
brandColor?: string
|
||||
/** 品牌色(深色模式压亮,空则用 brandColor) */
|
||||
brandColorDark?: string
|
||||
/** 关闭按钮文案(已 $t,留空走 i18n codePay.closePage) */
|
||||
closeText?: string
|
||||
}>(), {
|
||||
title: '',
|
||||
tip: '',
|
||||
merchantName: '',
|
||||
orderNo: '',
|
||||
brandColor: '#5d9dfe',
|
||||
brandColorDark: '',
|
||||
closeText: '',
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
/** 完成/关闭页面(由父级处理 closeWebview) */
|
||||
close: []
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
const { getDarkMode } = useDesignSetting()
|
||||
|
||||
/** 近黑色品牌(抖音):深色下主按钮需浅底深字 */
|
||||
function isNearBlackBrand(hex?: string) {
|
||||
if (!hex) {
|
||||
return false
|
||||
}
|
||||
const h = hex.replace('#', '')
|
||||
if (h.length !== 6) {
|
||||
return false
|
||||
}
|
||||
const r = Number.parseInt(h.slice(0, 2), 16)
|
||||
const g = Number.parseInt(h.slice(2, 4), 16)
|
||||
const b = Number.parseInt(h.slice(4, 6), 16)
|
||||
return (r + g + b) / 3 < 60
|
||||
}
|
||||
|
||||
/** 当前生效品牌色(深色优先 brandColorDark,用于图标/标题/按钮) */
|
||||
const effectiveBrand = computed(() => {
|
||||
if (unref(getDarkMode) === 'dark' && props.brandColorDark) {
|
||||
return props.brandColorDark
|
||||
}
|
||||
return props.brandColor
|
||||
})
|
||||
|
||||
/** 操作按钮色:深色 + 近黑品牌时反色为浅底 */
|
||||
const effectiveAction = computed(() => {
|
||||
if (unref(getDarkMode) === 'dark' && isNearBlackBrand(props.brandColor)) {
|
||||
return '#f2f2f2'
|
||||
}
|
||||
return effectiveBrand.value
|
||||
})
|
||||
|
||||
/** 操作按钮文字色 */
|
||||
const effectiveActionText = computed(() => {
|
||||
if (unref(getDarkMode) === 'dark' && isNearBlackBrand(props.brandColor)) {
|
||||
return '#161823'
|
||||
}
|
||||
return '#ffffff'
|
||||
})
|
||||
|
||||
/** 标题(默认走 i18n) */
|
||||
const titleText = computed(() => props.title || t('codePay.paySuccess'))
|
||||
|
||||
/** 副提示(默认走 i18n) */
|
||||
const tipText = computed(() => props.tip || t('codePay.paidTip'))
|
||||
|
||||
/** 关闭按钮文案(默认走 i18n) */
|
||||
const closeText = computed(() => props.closeText || t('codePay.closePage'))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="code-pay-result"
|
||||
:style="{
|
||||
'--result-color': effectiveBrand,
|
||||
'--brand-action': effectiveAction,
|
||||
'--brand-action-text': effectiveActionText,
|
||||
}"
|
||||
>
|
||||
<!-- 状态图标:实心彩色圆底 + 白色 SVG(对齐聚合) -->
|
||||
<div class="code-pay-result__icon">
|
||||
<svg
|
||||
viewBox="0 0 48 48"
|
||||
width="28"
|
||||
height="28"
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
stroke-width="4"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<!-- 对勾 -->
|
||||
<path d="M14 24 L21 31 L34 16" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- 支付成功 -->
|
||||
<p class="code-pay-result__title">
|
||||
{{ titleText }}
|
||||
</p>
|
||||
<!-- 副提示 -->
|
||||
<p class="code-pay-result__tip">
|
||||
{{ tipText }}
|
||||
</p>
|
||||
<!-- 大金额(码牌特色,独占行) -->
|
||||
<div class="code-pay-result__amount">
|
||||
<span class="code-pay-result__currency">¥</span>{{ amount }}
|
||||
</div>
|
||||
<!-- 订单摘要(border-top 分割线表格,对齐聚合) -->
|
||||
<div v-if="merchantName || orderNo" class="code-pay-result__summary">
|
||||
<div v-if="merchantName" class="code-pay-result__summary-row">
|
||||
<!-- 收款商户 -->
|
||||
<span class="code-pay-result__summary-label">{{ t('codePay.merchantDefault') }}</span>
|
||||
<span class="code-pay-result__summary-value">{{ merchantName }}</span>
|
||||
</div>
|
||||
<div v-if="orderNo" class="code-pay-result__summary-row">
|
||||
<!-- 订单号 -->
|
||||
<span class="code-pay-result__summary-label">{{ t('codePay.orderNo') }}</span>
|
||||
<span class="code-pay-result__summary-value">{{ orderNo }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 完成/关闭 -->
|
||||
<van-button
|
||||
class="code-pay-result__close"
|
||||
round
|
||||
block
|
||||
type="primary"
|
||||
color="var(--brand-action)"
|
||||
@click="emit('close')"
|
||||
>
|
||||
{{ closeText }}
|
||||
</van-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
// 结果状态卡片:上浮卡片风格,对齐聚合 AggregateResultCard
|
||||
.code-pay-result {
|
||||
margin: -32px 16px 16px;
|
||||
padding: 24px 20px;
|
||||
background: var(--h5-bg-card);
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--h5-shadow-card);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
animation: code-pay-result-up 0.45s ease-out;
|
||||
}
|
||||
|
||||
// 实心彩色圆底图标(对齐聚合,原 80px 半透明底)
|
||||
.code-pay-result__icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--result-color);
|
||||
margin-bottom: 4px;
|
||||
flex-shrink: 0;
|
||||
animation: code-pay-result-pop 0.55s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
// 状态标题:跟随通道品牌色(对齐聚合)
|
||||
.code-pay-result__title {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: var(--result-color);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
// 副提示文案
|
||||
.code-pay-result__tip {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: var(--h5-text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
// 大金额(码牌特色,独占行)
|
||||
.code-pay-result__amount {
|
||||
font-size: 36px;
|
||||
font-weight: 600;
|
||||
color: var(--h5-text-primary);
|
||||
line-height: 1.2;
|
||||
margin: 12px 0 16px;
|
||||
}
|
||||
|
||||
.code-pay-result__currency {
|
||||
font-size: 20px;
|
||||
margin-right: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
// 订单摘要区:border-top 分割线表格(对齐聚合,原独立阴影卡)
|
||||
.code-pay-result__summary {
|
||||
width: 100%;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--h5-border);
|
||||
text-align: left;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.code-pay-result__summary-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
padding: 6px 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.code-pay-result__summary-label {
|
||||
color: var(--h5-text-secondary);
|
||||
flex-shrink: 0;
|
||||
min-width: 72px;
|
||||
}
|
||||
|
||||
.code-pay-result__summary-value {
|
||||
color: var(--h5-text-primary);
|
||||
text-align: right;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
// 关闭按钮:实心品牌色,对齐聚合 280px
|
||||
.code-pay-result__close {
|
||||
margin-top: 16px;
|
||||
width: 100%;
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
@keyframes code-pay-result-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(16px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes code-pay-result-pop {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.6);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -8,6 +8,7 @@
|
||||
import { computed, ref, unref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useDesignSetting } from '@/shared/hooks/setting/useDesignSetting'
|
||||
import CodePayResultCard from './CodePayResultCard.vue'
|
||||
|
||||
defineOptions({ name: 'CodePayShell' })
|
||||
|
||||
@@ -169,47 +170,21 @@ function onPay() {
|
||||
'--brand-action-text': effectiveActionText,
|
||||
}"
|
||||
>
|
||||
<!-- 支付成功结果态:对标收银 cashier__result -->
|
||||
<div v-if="paid" class="code-pay-shell__result">
|
||||
<div class="code-pay-shell__result-icon">
|
||||
<van-icon name="success" size="48" color="#07c160" />
|
||||
</div>
|
||||
<!-- 支付成功 -->
|
||||
<div class="code-pay-shell__result-title">
|
||||
{{ t('codePay.paySuccess') }}
|
||||
</div>
|
||||
<div class="code-pay-shell__result-amount">
|
||||
<span class="code-pay-shell__result-currency">¥</span>{{ displayAmount }}
|
||||
</div>
|
||||
<div v-if="orderNo || merchantName" class="code-pay-shell__result-card">
|
||||
<div v-if="merchantName" class="code-pay-shell__result-row">
|
||||
<!-- 收款商户 -->
|
||||
<span>{{ t('codePay.merchantDefault') }}</span>
|
||||
<span :title="merchantName">{{ merchantName }}</span>
|
||||
</div>
|
||||
<div v-if="orderNo" class="code-pay-shell__result-row">
|
||||
<!-- 订单号 -->
|
||||
<span>{{ t('codePay.orderNo') }}</span>
|
||||
<span :title="orderNo">{{ orderNo }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 完成/关闭 -->
|
||||
<van-button
|
||||
class="code-pay-shell__result-btn"
|
||||
round
|
||||
block
|
||||
type="primary"
|
||||
color="var(--brand-action)"
|
||||
@click="emit('close')"
|
||||
>
|
||||
{{ t('codePay.closePage') }}
|
||||
</van-button>
|
||||
</div>
|
||||
<!-- 品牌顶栏(paid 和非 paid 都渲染,作为结果卡/收款卡负 margin 叠放对象) -->
|
||||
<div class="code-pay-shell__brand" />
|
||||
|
||||
<!-- 支付成功结果态:独立组件,对齐聚合上浮卡片风格 -->
|
||||
<CodePayResultCard
|
||||
v-if="paid"
|
||||
:amount="displayAmount"
|
||||
:merchant-name="merchantName"
|
||||
:order-no="orderNo"
|
||||
:brand-color="brandColor"
|
||||
:brand-color-dark="brandColorNight || brandColor"
|
||||
@close="emit('close')"
|
||||
/>
|
||||
|
||||
<template v-else>
|
||||
<!-- 品牌顶栏(文档流,配合上浮卡) -->
|
||||
<div class="code-pay-shell__brand" />
|
||||
|
||||
<div class="code-pay-shell__card enter-y">
|
||||
<div class="code-pay-shell__merchant">
|
||||
<div class="code-pay-shell__avatar">
|
||||
@@ -522,119 +497,5 @@ function onPay() {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 成功结果态
|
||||
&__result {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 32px 24px calc(24px + env(safe-area-inset-bottom, 0px));
|
||||
text-align: center;
|
||||
animation: code-pay-result-up 0.45s ease-out;
|
||||
}
|
||||
|
||||
&__result-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 20px;
|
||||
background: rgb(7 193 96 / 10%);
|
||||
animation: code-pay-result-pop 0.55s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
&__result-title {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
color: var(--h5-text-primary);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
&__result-amount {
|
||||
font-size: 36px;
|
||||
font-weight: 600;
|
||||
color: var(--h5-text-primary);
|
||||
line-height: 1.2;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
&__result-currency {
|
||||
font-size: 20px;
|
||||
margin-right: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__result-card {
|
||||
width: 100%;
|
||||
max-width: 320px;
|
||||
margin-bottom: 28px;
|
||||
padding: 16px 20px;
|
||||
background: var(--h5-bg-card);
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--h5-shadow-card);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&__result-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
font-size: 13px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
span:first-child {
|
||||
flex-shrink: 0;
|
||||
color: var(--h5-text-secondary);
|
||||
}
|
||||
|
||||
span:last-child {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
color: var(--h5-text-primary);
|
||||
text-align: right;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&__result-btn {
|
||||
width: 100%;
|
||||
max-width: 320px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes code-pay-result-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(16px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes code-pay-result-pop {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.6);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"merchantDefault": "Merchant",
|
||||
"payToMerchant": "Pay to {name}",
|
||||
"paySuccess": "Payment successful",
|
||||
"paidTip": "Payment completed. Thank you",
|
||||
"payFail": "Payment failed, please retry",
|
||||
"payLaunched": "Payment launched",
|
||||
"payPending": "Order created, please complete payment",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"merchantDefault": "Merchant",
|
||||
"payToMerchant": "Bayar ke {name}",
|
||||
"paySuccess": "Pembayaran berhasil",
|
||||
"paidTip": "Pembayaran selesai. Terima kasih",
|
||||
"payFail": "Pembayaran gagal, coba lagi",
|
||||
"payLaunched": "Pembayaran diluncurkan",
|
||||
"payPending": "Pesanan dibuat, selesaikan pembayaran",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"merchantDefault": "加盟店",
|
||||
"payToMerchant": "{name} へ支払い",
|
||||
"paySuccess": "支払い成功",
|
||||
"paidTip": "支払いが完了しました。ありがとうございます",
|
||||
"payFail": "支払いに失敗しました。再試行してください",
|
||||
"payLaunched": "支払いを開始しました",
|
||||
"payPending": "注文が作成されました。支払いを完了してください",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"merchantDefault": "가맹점",
|
||||
"payToMerchant": "{name}에 결제",
|
||||
"paySuccess": "결제 성공",
|
||||
"paidTip": "결제가 완료되었습니다. 감사합니다",
|
||||
"payFail": "결제 실패, 다시 시도하세요",
|
||||
"payLaunched": "결제가 시작되었습니다",
|
||||
"payPending": "주문이 생성되었습니다. 결제를 완료하세요",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"merchantDefault": "Pedagang",
|
||||
"payToMerchant": "Bayar kepada {name}",
|
||||
"paySuccess": "Pembayaran berjaya",
|
||||
"paidTip": "Pembayaran selesai. Terima kasih",
|
||||
"payFail": "Pembayaran gagal, cuba lagi",
|
||||
"payLaunched": "Pembayaran dilancarkan",
|
||||
"payPending": "Pesanan dicipta, sila lengkapkan pembayaran",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"merchantDefault": "ร้านค้า",
|
||||
"payToMerchant": "ชำระให้ {name}",
|
||||
"paySuccess": "ชำระเงินสำเร็จ",
|
||||
"paidTip": "ชำระเงินสำเร็จ ขอบคุณที่ใช้บริการ",
|
||||
"payFail": "ชำระเงินไม่สำเร็จ โปรดลองอีกครั้ง",
|
||||
"payLaunched": "เปิดการชำระเงินแล้ว",
|
||||
"payPending": "สร้างคำสั่งซื้อแล้ว โปรดชำระเงินให้เสร็จ",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"merchantDefault": "Merchant",
|
||||
"payToMerchant": "Thanh toán cho {name}",
|
||||
"paySuccess": "Thanh toán thành công",
|
||||
"paidTip": "Thanh toán hoàn tất. Cảm ơn bạn",
|
||||
"payFail": "Thanh toán thất bại, vui lòng thử lại",
|
||||
"payLaunched": "Đã mở thanh toán",
|
||||
"payPending": "Đơn đã tạo, vui lòng hoàn tất thanh toán",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"merchantDefault": "收款商户",
|
||||
"payToMerchant": "向{name}付款",
|
||||
"paySuccess": "支付成功",
|
||||
"paidTip": "支付已完成,感谢使用",
|
||||
"payFail": "支付失败, 请重试",
|
||||
"payLaunched": "支付已拉起",
|
||||
"payPending": "订单已创建, 请完成支付",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"merchantDefault": "收款商戶",
|
||||
"payToMerchant": "向{name}付款",
|
||||
"paySuccess": "支付成功",
|
||||
"paidTip": "支付已完成,感謝使用",
|
||||
"payFail": "支付失敗,請重試",
|
||||
"payLaunched": "支付已拉起",
|
||||
"payPending": "訂單已建立,請完成支付",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"merchantDefault": "收款商戶",
|
||||
"payToMerchant": "向{name}付款",
|
||||
"paySuccess": "支付成功",
|
||||
"paidTip": "支付已完成,感謝使用",
|
||||
"payFail": "支付失敗,請重試",
|
||||
"payLaunched": "支付已拉起",
|
||||
"payPending": "訂單已建立,請完成支付",
|
||||
|
||||
Reference in New Issue
Block a user