优化订单交接和结账界面
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ChatDotRound, CopyDocument, Loading } from '@element-plus/icons-vue'
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ChatDotRound, CopyDocument, Loading, Service } from '@element-plus/icons-vue'
|
||||
|
||||
import {
|
||||
amountYuan,
|
||||
@@ -18,6 +19,8 @@ import { formatDateTime } from '@/shared/utils/time'
|
||||
import type { PaymentPayWay } from '@/features/orders/api/orders'
|
||||
|
||||
// 支付收银台:二维码渲染 + 轮询查询。PC 不做 App 浏览器跳转,直接渲染二维码弹窗。
|
||||
const route = useRoute()
|
||||
const disputeDialogVisible = ref(false)
|
||||
const cashier = useOrderPaymentCashier({
|
||||
notifySuccess: message => ElMessage.success(message),
|
||||
notifyError: message => ElMessage.error(message),
|
||||
@@ -88,12 +91,25 @@ const {
|
||||
notifySuccess: message => ElMessage.success(message),
|
||||
notifyError: message => ElMessage.error(message),
|
||||
notifyWarning: message => ElMessage.warning(message),
|
||||
confirm: async () => true, // PC 不做二次确认,直接放行。
|
||||
actionsNeedingConfirm: [],
|
||||
confirm: async ({ title, message }) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(message, title, {
|
||||
confirmButtonText: '确认提交',
|
||||
cancelButtonText: '返回检查',
|
||||
type: 'warning',
|
||||
dangerouslyUseHTMLString: false,
|
||||
})
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
},
|
||||
actionsNeedingConfirm: ['submitCheckout'],
|
||||
selectPayWay,
|
||||
ordersPath: '/orders',
|
||||
chatPathPrefix: '/messages/',
|
||||
startCashier: (payment, onPaid) => cashier.openPaymentCashier(payment, onPaid),
|
||||
onCreateDisputeSuccess: closeDisputeDialog,
|
||||
})
|
||||
|
||||
// 支付弹窗别名:保持 template 字段名不变。
|
||||
@@ -104,6 +120,34 @@ const qrGenerating = cashier.qrGenerating
|
||||
const checkingPayment = cashier.checkingPayment
|
||||
const paymentPayURL = cashier.payURL
|
||||
const activePayWayLabel = computedPayWayLabel(activePayment)
|
||||
const handoffFocused = ref(false)
|
||||
const hasSidebarActions = computed(() => {
|
||||
if (!order.value) return false
|
||||
return (
|
||||
(isOwner.value && order.value.status === 'pending_checkout_confirm') ||
|
||||
(isRenter.value && order.value.status === 'pending_checkout_accept')
|
||||
)
|
||||
})
|
||||
const disputeSupportTitle = computed(() => {
|
||||
if (canCancelDispute.value) {
|
||||
return order.value?.status === 'checkout_disputing' ? '结账争议处理中' : '申诉处理中'
|
||||
}
|
||||
return isCheckoutDisputeStage.value ? '结账金额有异议?' : '遇到交接问题?'
|
||||
})
|
||||
const disputeSupportMessage = computed(() => {
|
||||
if (canCancelDispute.value) {
|
||||
return '客服正在处理当前争议,如问题已解决可取消申诉。'
|
||||
}
|
||||
return isCheckoutDisputeStage.value
|
||||
? '可提交结账争议,客服会核查双方证据。'
|
||||
: '无法登录、超时交接等情况可申请客服介入。'
|
||||
})
|
||||
const disputeSupportActionText = computed(() => {
|
||||
if (canCancelDispute.value) {
|
||||
return order.value?.status === 'checkout_disputing' ? '取消结账争议' : '取消申诉'
|
||||
}
|
||||
return isCheckoutDisputeStage.value ? '发起结账争议' : '发起申诉'
|
||||
})
|
||||
|
||||
// selectPayWay 在创建支付单前让用户选择支付宝或微信。
|
||||
// 通过自定义对话框展示两个平级渠道卡片,返回 null 表示用户取消。
|
||||
@@ -161,30 +205,35 @@ function getOrderStep(status: string) {
|
||||
return stepMap[status] ?? 0
|
||||
}
|
||||
|
||||
function scrollToDispute() {
|
||||
const disputeSection = document.querySelector('.dispute-section')
|
||||
if (disputeSection) {
|
||||
disputeSection.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
}
|
||||
function openDisputeDialog() {
|
||||
disputeDialogVisible.value = true
|
||||
}
|
||||
function scrollToCheckout() {
|
||||
const checkoutSection = document.querySelector('.checkout-form-section')
|
||||
if (checkoutSection) {
|
||||
checkoutSection.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
}
|
||||
function closeDisputeDialog() {
|
||||
disputeDialogVisible.value = false
|
||||
}
|
||||
function scrollToCounterCheckout() {
|
||||
const counterSection = document.querySelector('.counter-checkout-section')
|
||||
if (counterSection) {
|
||||
counterSection.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
}
|
||||
scrollToSection('.counter-checkout-section')
|
||||
}
|
||||
function scrollToRejectCheckout() {
|
||||
const rejectSection = document.querySelector('.reject-checkout-section')
|
||||
if (rejectSection) {
|
||||
rejectSection.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
scrollToSection('.reject-checkout-section')
|
||||
}
|
||||
function scrollToHandoff() {
|
||||
scrollToSection('.handoff-workspace')
|
||||
}
|
||||
function scrollToSection(selector: string) {
|
||||
const section = document.querySelector(selector)
|
||||
if (section) {
|
||||
section.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
}
|
||||
}
|
||||
async function focusHandoffFromQuery() {
|
||||
if (handoffFocused.value || loading.value || !order.value || route.query.focus !== 'handoff') {
|
||||
return
|
||||
}
|
||||
handoffFocused.value = true
|
||||
await nextTick()
|
||||
scrollToHandoff()
|
||||
}
|
||||
|
||||
async function copyListingCode() {
|
||||
if (!order.value) return
|
||||
@@ -195,11 +244,19 @@ async function copyListingCode() {
|
||||
ElMessage.error('复制失败')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void focusHandoffFromQuery()
|
||||
})
|
||||
|
||||
watch([() => route.query.focus, order, loading], () => {
|
||||
void focusHandoffFromQuery()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="page order-detail-page" v-loading="loading">
|
||||
<div v-if="order" class="page-header">
|
||||
<div v-if="order" class="page-header" :class="{ centered: !hasSidebarActions }">
|
||||
<div class="header-top">
|
||||
<div class="header-info">
|
||||
<p class="eyebrow">订单号:{{ order.order_no }}</p>
|
||||
@@ -216,7 +273,7 @@ async function copyListingCode() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="order" class="content-layout">
|
||||
<div v-if="order" class="content-layout" :class="{ centered: !hasSidebarActions }">
|
||||
<div class="main-content">
|
||||
<div v-if="order" class="order-progress-section">
|
||||
<el-steps
|
||||
@@ -251,6 +308,61 @@ async function copyListingCode() {
|
||||
</el-steps>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="
|
||||
isOwner &&
|
||||
order.status === 'pending_handoff' &&
|
||||
order.handoff_status === 'pending_owner'
|
||||
"
|
||||
class="form-section handoff-action-section"
|
||||
>
|
||||
<div class="section-header compact">
|
||||
<h2>当前待办:提交交接说明</h2>
|
||||
<span>填写后租客即可确认收号</span>
|
||||
</div>
|
||||
<div class="handoff-action-card">
|
||||
<el-input
|
||||
v-model="handoffContent"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="填写登录方式、注意事项和交接说明"
|
||||
/>
|
||||
<el-button type="primary" :loading="handoffing" @click="handleSubmitHandoff">
|
||||
提交交接
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="
|
||||
isRenter &&
|
||||
order.status === 'pending_handoff' &&
|
||||
order.handoff_status === 'pending_renter_confirm'
|
||||
"
|
||||
class="handoff-confirm-strip"
|
||||
>
|
||||
<div>
|
||||
<strong>当前待办:确认收号</strong>
|
||||
<span>确认账号可以正常登录后,订单会进入使用中并重新计算预计截止时间。</span>
|
||||
</div>
|
||||
<el-button type="primary" :loading="confirming" @click="handleConfirmReceive">
|
||||
确认已收到账号
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<OrderResourceUsageEditor
|
||||
v-if="order && isRenter && ['renting', 'overdue'].includes(order.status)"
|
||||
v-model:checkout-form="checkoutForm"
|
||||
v-model:resource-usage="resourceUsage"
|
||||
:resources="checkoutResources"
|
||||
:resource-charge-amount="resourceChargeAmount"
|
||||
:snapshot-haf-coin-m="snapshotHafCoinM"
|
||||
:remaining-haf-coin-m="remainingHafCoinM"
|
||||
:returning="returning"
|
||||
:resource-line-amount="resourceLineAmount"
|
||||
@submit="handleSubmitCheckout"
|
||||
/>
|
||||
|
||||
<div v-if="order" class="detail-grid">
|
||||
<div class="metric-card primary">
|
||||
<span class="metric-label">订单状态</span>
|
||||
@@ -309,29 +421,43 @@ async function copyListingCode() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="order.status === 'pending_handoff'" class="action-card info">
|
||||
<div class="action-buttons">
|
||||
<div
|
||||
v-if="order.status === 'pending_handoff' || canOpenDispute || canCancelDispute"
|
||||
class="secondary-actions"
|
||||
>
|
||||
<div v-if="order.status === 'pending_handoff'" class="secondary-action-card danger">
|
||||
<div>
|
||||
<strong>不继续交接?</strong>
|
||||
<span>取消后将释放账号,订单会回到可租状态。</span>
|
||||
</div>
|
||||
<el-button type="danger" plain :loading="cancelling" @click="handleCancel">
|
||||
取消订单并释放账号
|
||||
取消订单
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div v-if="canOpenDispute || canCancelDispute" class="secondary-action-card support">
|
||||
<span class="support-dispute-icon">
|
||||
<el-icon><Service /></el-icon>
|
||||
</span>
|
||||
<div class="support-dispute-copy">
|
||||
<strong>{{ disputeSupportTitle }}</strong>
|
||||
<span>{{ disputeSupportMessage }}</span>
|
||||
</div>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
:loading="cancellingDispute"
|
||||
@click="canCancelDispute ? handleCancelDispute() : openDisputeDialog()"
|
||||
>
|
||||
{{ disputeSupportActionText }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<OrderHandoffTimeline v-if="order" :records="handoffRecords" :listing-code="listingCode" />
|
||||
|
||||
<OrderResourceUsageEditor
|
||||
v-if="order && isRenter && ['renting', 'overdue'].includes(order.status)"
|
||||
v-model:checkout-form="checkoutForm"
|
||||
v-model:resource-usage="resourceUsage"
|
||||
:resources="checkoutResources"
|
||||
:resource-charge-amount="resourceChargeAmount"
|
||||
:snapshot-haf-coin-m="snapshotHafCoinM"
|
||||
:remaining-haf-coin-m="remainingHafCoinM"
|
||||
:returning="returning"
|
||||
:resource-line-amount="resourceLineAmount"
|
||||
@submit="handleSubmitCheckout"
|
||||
/>
|
||||
<div class="handoff-workspace">
|
||||
<OrderHandoffTimeline v-if="order" :records="handoffRecords" :listing-code="listingCode" />
|
||||
</div>
|
||||
|
||||
<OrderCheckoutSummary
|
||||
v-if="
|
||||
@@ -442,63 +568,7 @@ async function copyListingCode() {
|
||||
</div>
|
||||
|
||||
<!-- 右侧悬浮操作区 -->
|
||||
<div v-if="order" class="action-sidebar">
|
||||
<!-- 交接操作 -->
|
||||
<div
|
||||
v-if="
|
||||
isOwner &&
|
||||
order.status === 'pending_handoff' &&
|
||||
order.handoff_status === 'pending_owner'
|
||||
"
|
||||
class="sidebar-card"
|
||||
>
|
||||
<h3 class="sidebar-title">提交交接说明</h3>
|
||||
<el-input
|
||||
v-model="handoffContent"
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
placeholder="填写登录方式、注意事项和交接说明"
|
||||
/>
|
||||
<el-button type="primary" size="large" :loading="handoffing" @click="handleSubmitHandoff"
|
||||
>提交交接</el-button
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- 确认收号 -->
|
||||
<div
|
||||
v-if="
|
||||
isRenter &&
|
||||
order.status === 'pending_handoff' &&
|
||||
order.handoff_status === 'pending_renter_confirm'
|
||||
"
|
||||
class="sidebar-card"
|
||||
>
|
||||
<h3 class="sidebar-title">确认收号</h3>
|
||||
<p class="sidebar-hint">
|
||||
确认账号可以正常登录后,订单会进入使用中并重新计算预计截止时间。
|
||||
</p>
|
||||
<el-button type="primary" size="large" :loading="confirming" @click="handleConfirmReceive"
|
||||
>确认已收到账号</el-button
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- 发起结账 -->
|
||||
<div
|
||||
v-if="isRenter && ['renting', 'overdue'].includes(order.status)"
|
||||
class="sidebar-card highlight-card"
|
||||
>
|
||||
<h3 class="sidebar-title">快捷结账</h3>
|
||||
<el-button type="primary" size="large" :loading="returning" @click="handleSubmitCheckout">
|
||||
立即提交结账
|
||||
</el-button>
|
||||
<p class="sidebar-hint">
|
||||
如需修改消耗品、哈夫币等详细信息,请先向下滚动填写完整表单后再提交
|
||||
</p>
|
||||
<el-button type="default" size="small" @click="scrollToCheckout">
|
||||
查看详细结账表单
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div v-if="order && hasSidebarActions" class="action-sidebar">
|
||||
<!-- 确认结账 -->
|
||||
<div v-if="isOwner && order.status === 'pending_checkout_confirm'" class="sidebar-card">
|
||||
<h3 class="sidebar-title">确认结账</h3>
|
||||
@@ -539,38 +609,25 @@ async function copyListingCode() {
|
||||
<p class="sidebar-hint">不同意修正时需填写原因,将进入争议处理</p>
|
||||
</div>
|
||||
|
||||
<!-- 发起申诉 -->
|
||||
<div v-if="canOpenDispute" class="sidebar-card dispute-card">
|
||||
<h3 class="sidebar-title">{{ isCheckoutDisputeStage ? '发起结账争议' : '发起申诉' }}</h3>
|
||||
<el-button type="warning" size="large" @click="scrollToDispute">
|
||||
{{ isCheckoutDisputeStage ? '提交结账争议' : '提交申诉' }}
|
||||
</el-button>
|
||||
<p class="sidebar-hint">点击后滚动到申诉表单填写详细信息</p>
|
||||
</div>
|
||||
|
||||
<div v-if="canCancelDispute" class="sidebar-card dispute-card">
|
||||
<h3 class="sidebar-title">
|
||||
{{ order.status === 'checkout_disputing' ? '取消结账争议' : '取消申诉' }}
|
||||
</h3>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
size="large"
|
||||
:loading="cancellingDispute"
|
||||
@click="handleCancelDispute"
|
||||
>
|
||||
{{ order.status === 'checkout_disputing' ? '取消结账争议' : '取消申诉' }}
|
||||
</el-button>
|
||||
<p class="sidebar-hint">取消后订单将恢复到发起申诉前的流程</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="order && canOpenDispute" class="form-section dispute-section">
|
||||
<div class="section-header">
|
||||
<h2>{{ isCheckoutDisputeStage ? '发起结账争议' : '发起申诉' }}</h2>
|
||||
</div>
|
||||
<div class="form-card">
|
||||
<el-dialog
|
||||
v-model="disputeDialogVisible"
|
||||
:title="isCheckoutDisputeStage ? '发起结账争议' : '发起申诉'"
|
||||
width="720px"
|
||||
append-to-body
|
||||
class="dispute-dialog"
|
||||
>
|
||||
<div v-if="order && canOpenDispute" class="dispute-dialog-body">
|
||||
<div class="dispute-dialog-tip">
|
||||
<span class="support-dispute-icon">
|
||||
<el-icon><Service /></el-icon>
|
||||
</span>
|
||||
<p>
|
||||
{{ isCheckoutDisputeStage ? '提交后客服会核查结账金额和双方证据。' : '请尽量写清楚时间点、问题经过和证据,便于客服快速处理。' }}
|
||||
</p>
|
||||
</div>
|
||||
<el-select
|
||||
v-if="!isCheckoutDisputeStage"
|
||||
v-model="disputeType"
|
||||
@@ -605,11 +662,16 @@ async function copyListingCode() {
|
||||
@change="handleEvidenceUpload"
|
||||
/>
|
||||
</div>
|
||||
<el-button type="warning" size="large" :loading="disputing" @click="handleCreateDispute">
|
||||
{{ isCheckoutDisputeStage ? '提交结账争议' : '提交申诉' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dispute-dialog-footer">
|
||||
<el-button @click="closeDisputeDialog">取消</el-button>
|
||||
<el-button type="warning" :loading="disputing" @click="handleCreateDispute">
|
||||
{{ isCheckoutDisputeStage ? '提交结账争议' : '提交申诉' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="payWayDialogVisible"
|
||||
@@ -684,72 +746,85 @@ async function copyListingCode() {
|
||||
|
||||
<style scoped>
|
||||
.order-detail-page {
|
||||
max-width: 1400px;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 32px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.page-header.centered {
|
||||
max-width: 940px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.header-top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.header-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header-info h1 {
|
||||
margin: 4px 0 0;
|
||||
font-size: 26px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.order-meta {
|
||||
color: #64748b;
|
||||
margin-top: 8px;
|
||||
margin: 6px 0 10px;
|
||||
}
|
||||
|
||||
.content-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 320px;
|
||||
gap: 24px;
|
||||
grid-template-columns: minmax(0, 1fr) 280px;
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.content-layout.centered {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
max-width: 940px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.action-sidebar {
|
||||
position: sticky;
|
||||
top: 24px;
|
||||
top: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.sidebar-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 20px;
|
||||
gap: 10px;
|
||||
padding: 14px;
|
||||
background: #ffffff;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 4px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.sidebar-card.highlight-card {
|
||||
background: #eff6ff;
|
||||
background: #f8fbff;
|
||||
border-color: #3b82f6;
|
||||
}
|
||||
|
||||
.sidebar-card.dispute-card {
|
||||
background: #fef2f2;
|
||||
border-color: #fecaca;
|
||||
}
|
||||
|
||||
.sidebar-title {
|
||||
font-size: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #1f2937;
|
||||
margin: 0;
|
||||
@@ -757,37 +832,69 @@ async function copyListingCode() {
|
||||
|
||||
.sidebar-hint {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
line-height: 1.5;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.sidebar-card .el-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.support-dispute-icon {
|
||||
display: grid;
|
||||
flex: none;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
place-items: center;
|
||||
border-radius: 8px;
|
||||
background: #fff1d6;
|
||||
color: #d97706;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.support-dispute-copy {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.support-dispute-copy strong {
|
||||
color: #1f2937;
|
||||
font-size: 14px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.support-dispute-copy span {
|
||||
color: #7c5a16;
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.order-progress-section {
|
||||
margin-bottom: 32px;
|
||||
padding: 24px;
|
||||
margin-bottom: 12px;
|
||||
padding: 16px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.detail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 16px;
|
||||
margin-bottom: 32px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 20px;
|
||||
gap: 6px;
|
||||
min-height: 86px;
|
||||
padding: 14px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
@@ -839,15 +946,16 @@ async function copyListingCode() {
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: 24px;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #1f2937;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.metric-value.amount {
|
||||
@@ -860,48 +968,46 @@ async function copyListingCode() {
|
||||
}
|
||||
|
||||
.info-section {
|
||||
margin-bottom: 32px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
padding: 20px;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
padding: 14px 16px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
}
|
||||
|
||||
.info-row:last-child {
|
||||
border-bottom: none;
|
||||
gap: 12px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
color: #1f2937;
|
||||
font-weight: 600;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.action-card {
|
||||
margin-top: 16px;
|
||||
padding: 20px;
|
||||
margin-top: 12px;
|
||||
padding: 14px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.action-card.warning {
|
||||
@@ -909,13 +1015,8 @@ async function copyListingCode() {
|
||||
border-color: #fde68a;
|
||||
}
|
||||
|
||||
.action-card.info {
|
||||
background: #eff6ff;
|
||||
border-color: #bfdbfe;
|
||||
}
|
||||
|
||||
.action-message {
|
||||
margin: 0 0 16px 0;
|
||||
margin: 0 0 12px 0;
|
||||
color: #1f2937;
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -930,37 +1031,135 @@ async function copyListingCode() {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.section-header.compact span {
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.section-header h2 {
|
||||
font-size: 20px;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1f2937;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 32px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
padding: 24px;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.handoff-action-section {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.handoff-action-card {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 160px;
|
||||
gap: 12px;
|
||||
align-items: stretch;
|
||||
padding: 16px;
|
||||
border: 1px solid #bfdbfe;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(180deg, #f8fbff 0%, #eff6ff 100%);
|
||||
}
|
||||
|
||||
.handoff-action-card .el-button {
|
||||
height: 100%;
|
||||
min-height: 96px;
|
||||
}
|
||||
|
||||
.handoff-confirm-strip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
padding: 16px;
|
||||
border: 1px solid #bfdbfe;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(180deg, #f8fbff 0%, #eff6ff 100%);
|
||||
}
|
||||
|
||||
.handoff-confirm-strip div {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.handoff-confirm-strip strong {
|
||||
color: #1f2937;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.handoff-confirm-strip span {
|
||||
color: #475569;
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.secondary-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
gap: 10px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.secondary-action-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.secondary-action-card > div {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.secondary-action-card strong {
|
||||
color: #1f2937;
|
||||
font-size: 13px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.secondary-action-card span {
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.secondary-action-card.danger {
|
||||
border: 1px solid #fecaca;
|
||||
background: #fff7f7;
|
||||
}
|
||||
|
||||
.secondary-action-card.support {
|
||||
border: 1px solid #f8d99b;
|
||||
background: #fffaf0;
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
margin: 0;
|
||||
padding: 12px 16px;
|
||||
padding: 10px 12px;
|
||||
background: #f8fafc;
|
||||
border-left: 3px solid #3b82f6;
|
||||
color: #475569;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@@ -1011,16 +1210,16 @@ async function copyListingCode() {
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
font-size: 16px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
margin: 0 0 16px 0;
|
||||
margin: 0 0 6px 0;
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 16px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.field-hint {
|
||||
@@ -1030,9 +1229,32 @@ async function copyListingCode() {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dispute-section .form-card {
|
||||
background: #fef2f2;
|
||||
border-color: #fecaca;
|
||||
.dispute-dialog-body {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dispute-dialog-tip {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #f8d99b;
|
||||
border-radius: 8px;
|
||||
background: #fffaf0;
|
||||
}
|
||||
|
||||
.dispute-dialog-tip p {
|
||||
margin: 0;
|
||||
color: #7c5a16;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.dispute-dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.upload-line {
|
||||
@@ -1047,6 +1269,18 @@ async function copyListingCode() {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:global(.dispute-dialog) {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
:global(.dispute-dialog .el-dialog__body) {
|
||||
padding: 14px 20px 8px;
|
||||
}
|
||||
|
||||
:global(.dispute-dialog .el-dialog__footer) {
|
||||
padding: 12px 20px 18px;
|
||||
}
|
||||
|
||||
.pay-dialog-body {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
@@ -1260,6 +1494,18 @@ async function copyListingCode() {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.handoff-action-card,
|
||||
.handoff-confirm-strip,
|
||||
.secondary-action-card {
|
||||
grid-template-columns: 1fr;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.handoff-action-card .el-button {
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user