优化了支付选择界面 ui

This commit is contained in:
yml
2026-06-18 23:19:15 +08:00
parent 550db63d4d
commit ae28858318
7 changed files with 543 additions and 93 deletions
+1 -1
View File
@@ -6,5 +6,5 @@
// biome-ignore lint: disable // biome-ignore lint: disable
export {} export {}
declare global { declare global {
const ElMessageBox: typeof import('element-plus/es').ElMessageBox
} }
@@ -3,7 +3,7 @@ import { readError } from '@/shared/utils/error'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { computed, nextTick, onMounted, ref, watch } from 'vue' import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { Star, StarFilled } from '@element-plus/icons-vue' import { ShoppingCart, Star, StarFilled } from '@element-plus/icons-vue'
import { fetchListing, type Listing } from '@/features/listings/api/listings' import { fetchListing, type Listing } from '@/features/listings/api/listings'
import { useListingCollections } from '@/features/listings/composables/useListingCollections' import { useListingCollections } from '@/features/listings/composables/useListingCollections'
@@ -468,12 +468,21 @@ function handleToggleFavorite() {
</section> </section>
<aside class="order-panel pc-order-card"> <aside class="order-panel pc-order-card">
<div class="order-card-head">
<div>
<span class="order-eyebrow">平台担保</span>
<h2>立即下单</h2> <h2>立即下单</h2>
</div>
<span class="order-state" :class="{ 'is-busy': listing.in_transaction }">
{{ listing.in_transaction ? '交易中' : '可租' }}
</span>
</div>
<p class="order-safe-text">平台托管订单与押金按平台交接流程完成账号使用</p> <p class="order-safe-text">平台托管订单与押金按平台交接流程完成账号使用</p>
<el-form label-position="top">
<el-form label-position="top" class="order-form">
<div class="order-total-box"> <div class="order-total-box">
<div class="order-total-head"> <div class="order-total-head">
<span>租赁价格</span> <span>租赁合计</span>
<strong>¥{{ listingPrice(listing) }}</strong> <strong>¥{{ listingPrice(listing) }}</strong>
</div> </div>
<div class="order-price-breakdown"> <div class="order-price-breakdown">
@@ -486,8 +495,12 @@ function handleToggleFavorite() {
<strong>¥{{ formatMoney(orderPriceBreakdown.consumable) }}</strong> <strong>¥{{ formatMoney(orderPriceBreakdown.consumable) }}</strong>
</div> </div>
</div> </div>
<em>押金另付 ¥{{ formatCent(listing.deposit_amount_cent) }}</em> <div class="order-deposit-row">
<span>押金另付</span>
<strong>¥{{ formatCent(listing.deposit_amount_cent) }}</strong>
</div> </div>
</div>
<dl class="order-check-list"> <dl class="order-check-list">
<div> <div>
<dt>账号区服</dt> <dt>账号区服</dt>
@@ -502,14 +515,17 @@ function handleToggleFavorite() {
<dd>{{ formatEstimatedRentalDuration(listing) }}</dd> <dd>{{ formatEstimatedRentalDuration(listing) }}</dd>
</div> </div>
</dl> </dl>
<div class="order-action-group">
<el-button <el-button
type="warning" type="warning"
size="large" size="large"
:loading="ordering || agreementsLoading" :loading="ordering || agreementsLoading"
:disabled="listing.in_transaction" :disabled="listing.in_transaction"
class="full-control" class="full-control order-primary-btn"
@click="handleCreateOrder" @click="handleCreateOrder"
> >
<el-icon><ShoppingCart /></el-icon>
{{ listing.in_transaction ? '交易中' : '立即下单' }} {{ listing.in_transaction ? '交易中' : '立即下单' }}
</el-button> </el-button>
<el-button <el-button
@@ -517,8 +533,12 @@ function handleToggleFavorite() {
size="large" size="large"
@click="handleToggleFavorite" @click="handleToggleFavorite"
> >
<el-icon>
<component :is="listingFavorited ? StarFilled : Star" />
</el-icon>
{{ listingFavorited ? '取消收藏' : '收藏账号' }} {{ listingFavorited ? '取消收藏' : '收藏账号' }}
</el-button> </el-button>
</div>
</el-form> </el-form>
</aside> </aside>
</div> </div>
@@ -903,10 +923,78 @@ function handleToggleFavorite() {
.pc-order-card { .pc-order-card {
max-width: none; max-width: none;
border-radius: 20px; border-radius: 20px;
padding: 24px;
overflow: hidden;
}
.order-card-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 18px;
margin-bottom: 10px;
}
.order-card-head h2 {
margin: 4px 0 0;
color: #17233d;
font-size: 22px;
line-height: 1.2;
}
.order-eyebrow {
display: inline-flex;
align-items: center;
min-height: 24px;
border-radius: 999px;
background: #f1f5f9;
padding: 0 10px;
color: #64748b;
font-size: 12px;
font-weight: 900;
}
.order-state {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 58px;
min-height: 28px;
border-radius: 999px;
background: #ecfdf5;
color: #16a34a;
font-size: 12px;
font-weight: 900;
white-space: nowrap;
}
.order-state.is-busy {
background: #fff7ed;
color: #ea580c;
}
.order-form {
display: grid;
gap: 14px;
}
.order-safe-text {
margin-bottom: 18px;
color: #7a89a5;
font-size: 15px;
font-weight: 700;
} }
.order-total-box { .order-total-box {
margin-bottom: 14px; position: relative;
display: grid;
gap: 12px;
margin-bottom: 0;
border: 1px solid rgba(245, 158, 11, 0.2);
background:
linear-gradient(135deg, rgba(255, 251, 235, 0.96), rgba(255, 247, 237, 0.92)),
#fffbeb;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.86);
} }
.order-total-head { .order-total-head {
@@ -914,12 +1002,12 @@ function handleToggleFavorite() {
align-items: flex-start; align-items: flex-start;
justify-content: space-between; justify-content: space-between;
gap: 16px; gap: 16px;
padding-bottom: 12px; padding-bottom: 14px;
border-bottom: 1px solid rgba(217, 119, 6, 0.14); border-bottom: 1px solid rgba(217, 119, 6, 0.14);
} }
.order-total-head span { .order-total-head span {
color: #92400e; color: #9a5a16;
font-size: 14px; font-size: 14px;
font-weight: 900; font-weight: 900;
} }
@@ -927,14 +1015,15 @@ function handleToggleFavorite() {
.order-total-head strong { .order-total-head strong {
margin: 0; margin: 0;
color: #ef4444; color: #ef4444;
font-size: 32px; font-size: 34px;
line-height: 1; line-height: 1;
text-align: right;
} }
.order-price-breakdown { .order-price-breakdown {
display: grid; display: grid;
gap: 8px; gap: 10px;
padding: 12px 0; padding: 0;
} }
.order-price-breakdown div { .order-price-breakdown div {
@@ -944,42 +1033,51 @@ function handleToggleFavorite() {
gap: 12px; gap: 12px;
} }
.order-price-breakdown span { .order-price-breakdown span,
.order-deposit-row span {
color: #8a5a12; color: #8a5a12;
font-size: 13px; font-size: 13px;
font-weight: 900; font-weight: 900;
} }
.order-price-breakdown strong { .order-price-breakdown strong,
.order-deposit-row strong {
margin: 0; margin: 0;
color: #17233d; color: #17233d;
font-size: 16px; font-size: 16px;
line-height: 1.2; line-height: 1.2;
} }
.order-total-box > em { .order-deposit-row {
padding-top: 10px; display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding-top: 12px;
border-top: 1px solid rgba(217, 119, 6, 0.14); border-top: 1px solid rgba(217, 119, 6, 0.14);
}
.order-deposit-row strong {
color: #92400e; color: #92400e;
font-size: 13px; font-size: 18px;
font-weight: 900;
} }
.order-check-list { .order-check-list {
display: grid; display: grid;
gap: 10px; gap: 10px;
margin: 0 0 18px; margin: 0;
} }
.order-check-list div { .order-check-list div {
display: flex; display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center; align-items: center;
justify-content: space-between;
gap: 16px; gap: 16px;
min-height: 40px; min-height: 44px;
padding: 0 12px; border: 1px solid #eef2f7;
border-radius: 10px; border-radius: 12px;
background: #f8fafc; background: #f8fafc;
padding: 0 14px;
} }
.order-check-list dt { .order-check-list dt {
@@ -994,16 +1092,57 @@ function handleToggleFavorite() {
font-size: 14px; font-size: 14px;
font-weight: 900; font-weight: 900;
text-align: right; text-align: right;
overflow-wrap: anywhere;
} }
.full-control { .full-control {
width: 100%; width: 100%;
} }
.order-action-group {
display: grid;
gap: 10px;
padding-top: 2px;
}
.order-primary-btn {
min-height: 48px;
border: 0;
background: #ff8a00;
color: #ffffff;
font-size: 15px;
font-weight: 900;
box-shadow: 0 12px 24px rgba(255, 106, 0, 0.24);
}
.order-primary-btn:hover,
.order-primary-btn:focus {
background: #ff7a00;
color: #ffffff;
}
.order-primary-btn.is-disabled,
.order-primary-btn.is-disabled:hover {
background: #f3a94a;
color: rgba(255, 255, 255, 0.86);
box-shadow: none;
}
.favorite-order-btn { .favorite-order-btn {
margin-top: 10px; min-height: 46px;
margin-left: 0;
border-color: #ff6a00; border-color: #ff6a00;
background: #ffffff;
color: #ff6a00; color: #ff6a00;
font-size: 15px;
font-weight: 900;
}
.favorite-order-btn:hover,
.favorite-order-btn:focus {
border-color: #ff6a00;
background: #fff7ed;
color: #ea580c;
} }
.agreement-dialog-body { .agreement-dialog-body {
@@ -0,0 +1,164 @@
<script setup lang="ts">
import type { PaymentPayWay } from '@/features/orders/api/orders'
defineProps<{
show: boolean
}>()
const emit = defineEmits<{
'update:show': [value: boolean]
choose: [payWay: PaymentPayWay]
closed: []
}>()
</script>
<template>
<van-popup
:show="show"
position="bottom"
round
class="mobile-payway-popup"
@update:show="emit('update:show', $event)"
@closed="emit('closed')"
>
<header class="payway-header">
<span>选择支付方式</span>
<button type="button" aria-label="关闭" @click="emit('update:show', false)">
<van-icon name="cross" :size="20" />
</button>
</header>
<p class="payway-tip">选择渠道后将生成对应的支付二维码</p>
<div class="payway-options">
<button type="button" class="payway-option wechat" @click="emit('choose', 'WXZF')">
<span class="payway-icon">
<van-icon name="wechat-pay" :size="28" />
</span>
<span class="payway-text">
<strong>微信支付</strong>
<small>使用微信扫码完成支付</small>
</span>
<van-icon name="arrow" class="payway-arrow" :size="16" />
</button>
<button type="button" class="payway-option alipay" @click="emit('choose', 'ZFBZF')">
<span class="payway-icon">
<van-icon name="alipay" :size="28" />
</span>
<span class="payway-text">
<strong>支付宝支付</strong>
<small>使用支付宝扫码完成支付</small>
</span>
<van-icon name="arrow" class="payway-arrow" :size="16" />
</button>
</div>
</van-popup>
</template>
<style scoped>
.mobile-payway-popup {
overflow: hidden;
padding-bottom: calc(20px + env(safe-area-inset-bottom));
background: #fff;
}
.payway-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 18px 8px;
}
.payway-header span {
color: #111827;
font-size: 16px;
font-weight: 800;
}
.payway-header button {
display: grid;
width: 36px;
height: 36px;
place-items: center;
border: 0;
border-radius: 18px;
background: #f8fafc;
color: #64748b;
}
.payway-tip {
margin: 0 18px 14px;
color: #94a3b8;
font-size: 13px;
}
.payway-options {
display: flex;
flex-direction: column;
gap: 12px;
padding: 0 18px;
}
.payway-option {
display: flex;
align-items: center;
gap: 14px;
padding: 14px 16px;
background: #fff;
border: 2px solid #eef0f3;
border-radius: 14px;
cursor: pointer;
text-align: left;
transition: border-color 0.18s ease;
}
.payway-option:active {
background: #f9fafb;
}
.payway-option.wechat:active {
border-color: #07c160;
}
.payway-option.alipay:active {
border-color: #1677ff;
}
.payway-icon {
display: grid;
place-items: center;
width: 44px;
height: 44px;
border-radius: 12px;
color: #fff;
flex-shrink: 0;
}
.payway-option.wechat .payway-icon {
background: #07c160;
}
.payway-option.alipay .payway-icon {
background: #1677ff;
}
.payway-text {
display: flex;
flex-direction: column;
gap: 3px;
flex: 1;
}
.payway-text strong {
font-size: 15px;
font-weight: 700;
color: #1f2937;
}
.payway-text small {
font-size: 12px;
color: #94a3b8;
}
.payway-arrow {
color: #cbd5e1;
}
</style>
@@ -0,0 +1,33 @@
import { ref } from 'vue'
import type { PaymentPayWay } from '@/features/orders/api/orders'
// useMobilePayWaySelect 提供移动端"选择支付方式"底部弹层的状态与 Promise 化选择逻辑。
// 与 MobilePayWaySelectPopup 配合:select() 打开弹层并返回用户所选渠道,关闭即视为取消。
export function useMobilePayWaySelect() {
const visible = ref(false)
let resolver: ((value: PaymentPayWay | null) => void) | null = null
// select 打开弹层并返回 Promise,resolve 为所选渠道;取消则为 null。
function select(): Promise<PaymentPayWay | null> {
visible.value = true
return new Promise(resolve => {
resolver = resolve
})
}
// choose 用户点选某渠道,关闭弹层并 resolve。
function choose(payWay: PaymentPayWay) {
resolver?.(payWay)
resolver = null
visible.value = false
}
// handleClosed 弹层关闭(含遮罩/返回)时若未选择则按取消处理。
function handleClosed() {
resolver?.(null)
resolver = null
}
return { visible, select, choose, handleClosed }
}
@@ -4,8 +4,10 @@ import { useRouter } from 'vue-router'
import { showDialog, showToast } from 'vant' import { showDialog, showToast } from 'vant'
import MobilePaymentCashierPopup from '@/features/orders/components/MobilePaymentCashierPopup.vue' import MobilePaymentCashierPopup from '@/features/orders/components/MobilePaymentCashierPopup.vue'
import MobilePayWaySelectPopup from '@/features/orders/components/MobilePayWaySelectPopup.vue'
import { useOrderActions, CONFIRMABLE_ACTIONS } from '@/features/orders/composables/useOrderActions' import { useOrderActions, CONFIRMABLE_ACTIONS } from '@/features/orders/composables/useOrderActions'
import { useMobilePaymentCashier } from '@/features/orders/composables/useMobilePaymentCashier' import { useMobilePaymentCashier } from '@/features/orders/composables/useMobilePaymentCashier'
import { useMobilePayWaySelect } from '@/features/orders/composables/useMobilePayWaySelect'
import { import {
amountYuan, amountYuan,
money, money,
@@ -19,11 +21,14 @@ import {
} from '@/features/orders' } from '@/features/orders'
import { handoffStatusLabel, orderStatusLabel } from '@/shared/utils/statusLabels' import { handoffStatusLabel, orderStatusLabel } from '@/shared/utils/statusLabels'
import { formatDateTime } from '@/shared/utils/time' import { formatDateTime } from '@/shared/utils/time'
import type { PaymentPayWay } from '@/features/orders/api/orders'
// 移动端支付收银台(基于通用 useOrderPaymentCashier 的薄封装:注入 vant toast + App 浏览器跳转)。 // 移动端支付收银台(基于通用 useOrderPaymentCashier 的薄封装:注入 vant toast + App 浏览器跳转)。
const cashier = useMobilePaymentCashier() const cashier = useMobilePaymentCashier()
const router = useRouter() const router = useRouter()
// 支付方式选择底部弹层(与 PC 端一致的渠道卡片体验)。
const payWaySelect = useMobilePayWaySelect()
const selectPayWay = payWaySelect.select
const { const {
paymentPopupVisible, paymentPopupVisible,
activePayment, activePayment,
@@ -117,23 +122,6 @@ const showDisputePopup = ref(false)
const showCounterPopup = ref(false) const showCounterPopup = ref(false)
const showRejectPopup = ref(false) const showRejectPopup = ref(false)
// selectPayWay 在创建支付单前让移动端用户选择支付宝或微信。
async function selectPayWay(): Promise<PaymentPayWay | null> {
try {
await showDialog({
title: '选择支付方式',
message: '选择后将生成对应渠道的支付二维码。',
showCancelButton: true,
confirmButtonText: '支付宝支付',
cancelButtonText: '微信支付',
closeOnClickOverlay: false,
})
return 'ZFBZF'
} catch (action) {
return action === 'cancel' ? 'WXZF' : null
}
}
// 账号快照展示(PC 不用,保留在本视图)。 // 账号快照展示(PC 不用,保留在本视图)。
function readSnapshot() { function readSnapshot() {
return readOrderSnapshot(order.value) as Record<string, any> | null return readOrderSnapshot(order.value) as Record<string, any> | null
@@ -465,6 +453,12 @@ async function copyListingCode() {
<van-empty image="search" description="订单不存在或已被删除" /> <van-empty image="search" description="订单不存在或已被删除" />
</div> </div>
<MobilePayWaySelectPopup
v-model:show="payWaySelect.visible.value"
@choose="payWaySelect.choose"
@closed="payWaySelect.handleClosed"
/>
<MobilePaymentCashierPopup <MobilePaymentCashierPopup
v-model:show="paymentPopupVisible" v-model:show="paymentPopupVisible"
:payment="activePayment" :payment="activePayment"
@@ -2,17 +2,18 @@
import { readError } from '@/shared/utils/error' import { readError } from '@/shared/utils/error'
import { onMounted, ref, computed } from 'vue' import { onMounted, ref, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { showDialog, showToast } from 'vant' import { showToast } from 'vant'
import MobileBottomNav from '@/components/MobileBottomNav.vue' import MobileBottomNav from '@/components/MobileBottomNav.vue'
import { import {
fetchOrders, fetchOrders,
startOrderPayment, startOrderPayment,
type Order, type Order,
type PaymentPayWay,
} from '@/features/orders/api/orders' } from '@/features/orders/api/orders'
import MobilePaymentCashierPopup from '@/features/orders/components/MobilePaymentCashierPopup.vue' import MobilePaymentCashierPopup from '@/features/orders/components/MobilePaymentCashierPopup.vue'
import MobilePayWaySelectPopup from '@/features/orders/components/MobilePayWaySelectPopup.vue'
import { useMobilePaymentCashier } from '@/features/orders/composables/useMobilePaymentCashier' import { useMobilePaymentCashier } from '@/features/orders/composables/useMobilePaymentCashier'
import { useMobilePayWaySelect } from '@/features/orders/composables/useMobilePayWaySelect'
import { fetchOrderChat } from '@/features/chats/api/chats' import { fetchOrderChat } from '@/features/chats/api/chats'
import { centToYuan, formatMoney } from '@/shared/utils/money' import { centToYuan, formatMoney } from '@/shared/utils/money'
import { useSessionStore } from '@/stores/session' import { useSessionStore } from '@/stores/session'
@@ -37,6 +38,10 @@ const {
refreshPaymentStatus, refreshPaymentStatus,
} = useMobilePaymentCashier() } = useMobilePaymentCashier()
// 支付方式选择底部弹层。
const payWaySelect = useMobilePayWaySelect()
const selectPayWay = payWaySelect.select
onMounted(() => { onMounted(() => {
loadOrders() loadOrders()
if (route.query.tab) { if (route.query.tab) {
@@ -123,22 +128,7 @@ async function openOrderChatAfterPayment(order: Order) {
} }
} }
// selectPayWay 在列表快捷支付前让用户选择支付宝或微信。 // selectPayWay 在列表快捷支付前让用户选择支付宝或微信(见 payWaySelect
async function selectPayWay(): Promise<PaymentPayWay | null> {
try {
await showDialog({
title: '选择支付方式',
message: '选择后将生成对应渠道的支付二维码。',
showCancelButton: true,
confirmButtonText: '支付宝支付',
cancelButtonText: '微信支付',
closeOnClickOverlay: false,
})
return 'ZFBZF'
} catch (action) {
return action === 'cancel' ? 'WXZF' : null
}
}
function money(value: unknown) { function money(value: unknown) {
return formatMoney(Number(value || 0)) return formatMoney(Number(value || 0))
@@ -293,6 +283,12 @@ async function copyListingCode(order: Order) {
</section> </section>
<!-- 底部导航 --> <!-- 底部导航 -->
<MobilePayWaySelectPopup
v-model:show="payWaySelect.visible.value"
@choose="payWaySelect.choose"
@closed="payWaySelect.handleClosed"
/>
<MobilePaymentCashierPopup <MobilePaymentCashierPopup
v-model:show="paymentPopupVisible" v-model:show="paymentPopupVisible"
:payment="activePayment" :payment="activePayment"
@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage } from 'element-plus'
import { ChatDotRound, CopyDocument, Loading } from '@element-plus/icons-vue' import { ChatDotRound, CopyDocument, Loading } from '@element-plus/icons-vue'
import { import {
@@ -106,19 +106,27 @@ const paymentPayURL = cashier.payURL
const activePayWayLabel = computedPayWayLabel(activePayment) const activePayWayLabel = computedPayWayLabel(activePayment)
// selectPayWay 在创建支付单前让用户选择支付宝或微信。 // selectPayWay 在创建支付单前让用户选择支付宝或微信。
async function selectPayWay(): Promise<PaymentPayWay | null> { // 通过自定义对话框展示两个平级渠道卡片,返回 null 表示用户取消。
try { const payWayDialogVisible = ref(false)
await ElMessageBox.confirm('选择后将生成对应渠道的支付二维码。', '选择支付方式', { let payWayResolver: ((value: PaymentPayWay | null) => void) | null = null
type: 'info',
confirmButtonText: '支付宝支付', function selectPayWay(): Promise<PaymentPayWay | null> {
cancelButtonText: '微信支付', payWayDialogVisible.value = true
distinguishCancelAndClose: true, return new Promise(resolve => {
closeOnClickModal: false, payWayResolver = resolve
}) })
return 'ZFBZF' }
} catch (action) {
return action === 'cancel' ? 'WXZF' : null function choosePayWay(payWay: PaymentPayWay) {
} payWayResolver?.(payWay)
payWayResolver = null
payWayDialogVisible.value = false
}
// 对话框关闭(含点 X、按 ESC)时,未选择则视为取消。
function handlePayWayDialogClosed() {
payWayResolver?.(null)
payWayResolver = null
} }
// computedPayWayLabel 根据当前支付单展示支付方式名称。 // computedPayWayLabel 根据当前支付单展示支付方式名称。
@@ -603,6 +611,38 @@ async function copyListingCode() {
</div> </div>
</div> </div>
<el-dialog
v-model="payWayDialogVisible"
title="选择支付方式"
width="420px"
append-to-body
:z-index="4000"
class="pay-way-dialog"
@closed="handlePayWayDialogClosed"
>
<p class="pay-way-tip">选择渠道后将生成对应的支付二维码</p>
<div class="pay-way-options">
<button type="button" class="pay-way-option wechat" @click="choosePayWay('WXZF')">
<span class="pay-way-icon">
<van-icon name="wechat-pay" :size="30" />
</span>
<span class="pay-way-text">
<strong>微信支付</strong>
<small>使用微信扫码完成支付</small>
</span>
</button>
<button type="button" class="pay-way-option alipay" @click="choosePayWay('ZFBZF')">
<span class="pay-way-icon">
<van-icon name="alipay" :size="30" />
</span>
<span class="pay-way-text">
<strong>支付宝支付</strong>
<small>使用支付宝扫码完成支付</small>
</span>
</button>
</div>
</el-dialog>
<el-dialog <el-dialog
v-model="paymentDialogVisible" v-model="paymentDialogVisible"
title="订单支付" title="订单支付"
@@ -1012,6 +1052,90 @@ async function copyListingCode() {
gap: 20px; gap: 20px;
} }
.pay-way-tip {
margin: 0 0 16px 0;
color: #64748b;
font-size: 14px;
}
.pay-way-options {
display: grid;
gap: 12px;
}
.pay-way-option {
display: flex;
align-items: center;
gap: 16px;
padding: 16px 18px;
background: #ffffff;
border: 2px solid #e5e7eb;
border-radius: 12px;
cursor: pointer;
text-align: left;
transition: all 0.18s ease;
}
.pay-way-option:hover {
transform: translateY(-1px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
}
.pay-way-icon {
display: grid;
place-items: center;
width: 48px;
height: 48px;
border-radius: 12px;
color: #ffffff;
flex-shrink: 0;
}
.pay-way-option.wechat:hover {
border-color: #07c160;
}
.pay-way-option.wechat .pay-way-icon {
background: #07c160;
}
.pay-way-option.alipay:hover {
border-color: #1677ff;
}
.pay-way-option.alipay .pay-way-icon {
background: #1677ff;
}
.pay-way-text {
display: flex;
flex-direction: column;
gap: 3px;
}
.pay-way-text strong {
font-size: 16px;
font-weight: 600;
color: #1f2937;
}
.pay-way-text small {
font-size: 13px;
color: #94a3b8;
}
:global(.pay-way-dialog) {
border-radius: 16px;
}
:global(.pay-way-dialog .el-dialog__header) {
padding: 20px 24px 0;
}
:global(.pay-way-dialog .el-dialog__body) {
padding: 16px 24px 24px;
}
.pay-summary { .pay-summary {
padding: 16px 20px; padding: 16px 20px;
background: #f8fafc; background: #f8fafc;