901 lines
22 KiB
Vue
901 lines
22 KiB
Vue
<script setup lang="ts">
|
||
import {
|
||
Check,
|
||
Clock,
|
||
DocumentChecked,
|
||
Money,
|
||
Refresh,
|
||
Search,
|
||
Tickets,
|
||
User,
|
||
View,
|
||
Wallet,
|
||
} from '@element-plus/icons-vue'
|
||
import { computed, onMounted, ref } from 'vue'
|
||
import { useRoute, useRouter } from 'vue-router'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
|
||
import {
|
||
fetchAdminWithdrawals,
|
||
reviewWithdrawal,
|
||
confirmPayment,
|
||
type WithdrawalDetail,
|
||
} from '@/features/admin/api/adminWithdrawal'
|
||
import { formatCentWithSymbol } from '@/shared/utils/money'
|
||
import { formatDateTime } from '@/shared/utils/time'
|
||
import { adminPath } from '@/shared/utils/adminPath'
|
||
import AdminTablePagination from '../components/AdminTablePagination.vue'
|
||
import WithdrawalDetailDialog from '../components/WithdrawalDetailDialog.vue'
|
||
|
||
const route = useRoute()
|
||
const router = useRouter()
|
||
|
||
const loading = ref(false)
|
||
const withdrawals = ref<WithdrawalDetail[]>([])
|
||
const total = ref(0)
|
||
const currentPage = ref(1)
|
||
const pageSize = ref(20)
|
||
|
||
const filters = ref({
|
||
status: '',
|
||
user_id: undefined as number | undefined,
|
||
})
|
||
|
||
const showDetailDialog = ref(false)
|
||
const selectedWithdrawal = ref<WithdrawalDetail | null>(null)
|
||
|
||
const statusOptions = [
|
||
{ label: '全部状态', value: '' },
|
||
{ label: '待审核', value: 'pending' },
|
||
{ label: '处理中 (待打款)', value: 'processing' },
|
||
{ label: '已完成', value: 'completed' },
|
||
{ label: '已拒绝', value: 'rejected' },
|
||
{ label: '已取消', value: 'cancelled' },
|
||
]
|
||
|
||
const withdrawalReturnQuery = computed<Record<string, string>>(() => {
|
||
const query: Record<string, string> = {
|
||
from: 'withdrawals',
|
||
withdrawal_page: String(currentPage.value),
|
||
withdrawal_page_size: String(pageSize.value),
|
||
}
|
||
if (filters.value.status) query.withdrawal_status = filters.value.status
|
||
if (filters.value.user_id) query.withdrawal_user_id = String(filters.value.user_id)
|
||
return query
|
||
})
|
||
|
||
const pendingCount = computed(() => withdrawals.value.filter(w => w.status === 'pending').length)
|
||
const processingCount = computed(
|
||
() => withdrawals.value.filter(w => w.status === 'processing').length
|
||
)
|
||
const completedAmount = computed(() =>
|
||
withdrawals.value
|
||
.filter(w => w.status === 'completed')
|
||
.reduce((sum, w) => sum + Number(w.actual_amount_cent || w.amount_cent || 0), 0)
|
||
)
|
||
|
||
onMounted(() => {
|
||
restoreListState()
|
||
void loadWithdrawals()
|
||
})
|
||
|
||
function routeQueryString(value: unknown) {
|
||
return typeof value === 'string' ? value : ''
|
||
}
|
||
|
||
function restoreListState() {
|
||
const status = routeQueryString(route.query.withdrawal_status)
|
||
if (statusOptions.some(item => item.value === status)) {
|
||
filters.value.status = status
|
||
}
|
||
|
||
const userID = Number(routeQueryString(route.query.withdrawal_user_id))
|
||
if (Number.isSafeInteger(userID) && userID > 0) {
|
||
filters.value.user_id = userID
|
||
}
|
||
|
||
const restoredPage = Number(routeQueryString(route.query.withdrawal_page))
|
||
if (Number.isSafeInteger(restoredPage) && restoredPage > 0) {
|
||
currentPage.value = restoredPage
|
||
}
|
||
|
||
const restoredPageSize = Number(routeQueryString(route.query.withdrawal_page_size))
|
||
if ([10, 20, 50, 100].includes(restoredPageSize)) {
|
||
pageSize.value = restoredPageSize
|
||
}
|
||
}
|
||
|
||
async function loadWithdrawals() {
|
||
loading.value = true
|
||
try {
|
||
const result = await fetchAdminWithdrawals({
|
||
status: filters.value.status || undefined,
|
||
user_id: filters.value.user_id,
|
||
page: currentPage.value,
|
||
page_size: pageSize.value,
|
||
})
|
||
withdrawals.value = result.items
|
||
total.value = result.total
|
||
} catch (error: any) {
|
||
ElMessage.error(error.response?.data?.message || '提现列表加载失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function handleFilter() {
|
||
currentPage.value = 1
|
||
loadWithdrawals()
|
||
}
|
||
|
||
function resetFilters() {
|
||
filters.value.status = ''
|
||
filters.value.user_id = undefined
|
||
currentPage.value = 1
|
||
void loadWithdrawals()
|
||
}
|
||
|
||
function openDetail(withdrawal: WithdrawalDetail) {
|
||
selectedWithdrawal.value = withdrawal
|
||
showDetailDialog.value = true
|
||
}
|
||
|
||
function goToOwnerOrders(withdrawal: WithdrawalDetail) {
|
||
const keyword = withdrawal.user_phone?.trim() || String(withdrawal.user_id || '')
|
||
void router.push({
|
||
path: adminPath('orders'),
|
||
query: {
|
||
...withdrawalReturnQuery.value,
|
||
...(keyword ? { keyword } : {}),
|
||
},
|
||
})
|
||
}
|
||
|
||
async function handleReview(withdrawal: WithdrawalDetail, approved: boolean) {
|
||
const action = approved ? '通过' : '拒绝'
|
||
try {
|
||
const { value: remark } = await ElMessageBox.prompt(
|
||
`请输入${action}原因(可选)`,
|
||
`${action}审核`,
|
||
{
|
||
confirmButtonText: `确认${action}`,
|
||
cancelButtonText: '取消',
|
||
inputType: 'textarea',
|
||
inputPlaceholder: '输入备注信息...',
|
||
}
|
||
)
|
||
|
||
await reviewWithdrawal(withdrawal.id, {
|
||
approved,
|
||
remark: remark || '',
|
||
})
|
||
|
||
ElMessage.success(`审核${action}`)
|
||
await loadWithdrawals()
|
||
} catch (error: any) {
|
||
if (error !== 'cancel') {
|
||
ElMessage.error(error.response?.data?.message || '操作失败')
|
||
}
|
||
}
|
||
}
|
||
|
||
async function handleConfirmPayment(withdrawal: WithdrawalDetail) {
|
||
try {
|
||
const { value: remark } = await ElMessageBox.prompt(
|
||
'请输入打款备注(如:已通过支付宝/微信转账)',
|
||
'确认打款',
|
||
{
|
||
confirmButtonText: '确认完成',
|
||
cancelButtonText: '取消',
|
||
inputType: 'textarea',
|
||
inputPlaceholder: '输入打款备注...',
|
||
inputValidator: value => {
|
||
return !!(value && value.trim().length > 0)
|
||
},
|
||
inputErrorMessage: '请输入打款备注',
|
||
}
|
||
)
|
||
|
||
await confirmPayment(withdrawal.id, {
|
||
remark: remark || '',
|
||
})
|
||
|
||
ElMessage.success('打款完成')
|
||
await loadWithdrawals()
|
||
} catch (error: any) {
|
||
if (error !== 'cancel') {
|
||
ElMessage.error(error.response?.data?.message || '操作失败')
|
||
}
|
||
}
|
||
}
|
||
|
||
function statusLabel(status: string) {
|
||
const labels: Record<string, string> = {
|
||
pending: '待审核',
|
||
processing: '待打款',
|
||
completed: '已打款',
|
||
rejected: '已拒绝',
|
||
cancelled: '已取消',
|
||
}
|
||
return labels[status] || status
|
||
}
|
||
|
||
function statusType(status: string): 'success' | 'warning' | 'danger' | 'info' | 'primary' {
|
||
const types: Record<string, 'success' | 'warning' | 'danger' | 'info' | 'primary'> = {
|
||
pending: 'warning',
|
||
processing: 'primary',
|
||
completed: 'success',
|
||
rejected: 'danger',
|
||
cancelled: 'info',
|
||
}
|
||
return types[status] || 'info'
|
||
}
|
||
|
||
function accountTypeLabel(type: string) {
|
||
const labels: Record<string, string> = {
|
||
alipay: '支付宝',
|
||
wechat: '微信',
|
||
bank: '银行卡',
|
||
}
|
||
return labels[type] || type
|
||
}
|
||
|
||
function onDetailDialogSaved() {
|
||
showDetailDialog.value = false
|
||
loadWithdrawals()
|
||
}
|
||
|
||
function money(cent: number | undefined | null) {
|
||
return formatCentWithSymbol(cent || 0)
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="finance-page-container">
|
||
<!-- 头部卡片 -->
|
||
<header class="page-header-card">
|
||
<div class="header-main">
|
||
<div class="title-group">
|
||
<div class="eyebrow-tag">
|
||
<el-icon><Money /></el-icon>
|
||
<span>WITHDRAWALS</span>
|
||
</div>
|
||
<h1 class="page-title">提现审核</h1>
|
||
<p class="page-subtitle">审核号主用户钱包提现申请,核验转账账户并执行打款放款处理。</p>
|
||
</div>
|
||
|
||
<div class="toolbar-actions">
|
||
<el-button :icon="Refresh" @click="resetFilters">重置</el-button>
|
||
<el-button type="primary" :icon="Search" :loading="loading" @click="handleFilter">
|
||
查询
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- 汇总指标卡片 -->
|
||
<section class="summary-metric-grid">
|
||
<div class="metric-card metric-card--review">
|
||
<div class="metric-icon-wrap amber">
|
||
<el-icon><Clock /></el-icon>
|
||
</div>
|
||
<div class="metric-info">
|
||
<span class="metric-label">本页待初审</span>
|
||
<strong class="metric-val amber-val">{{ pendingCount }} 笔</strong>
|
||
<span class="metric-sub-pill warning">待审核</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="metric-card metric-card--pay">
|
||
<div class="metric-icon-wrap indigo">
|
||
<el-icon><Wallet /></el-icon>
|
||
</div>
|
||
<div class="metric-info">
|
||
<span class="metric-label">本页待打款</span>
|
||
<strong class="metric-val indigo-val">{{ processingCount }} 笔</strong>
|
||
<span class="metric-sub-pill info">已过审待放款</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="metric-card metric-card--paid">
|
||
<div class="metric-icon-wrap emerald">
|
||
<el-icon><Check /></el-icon>
|
||
</div>
|
||
<div class="metric-info">
|
||
<span class="metric-label">本页已打款总额</span>
|
||
<strong class="metric-val emerald-val">{{ money(completedAmount) }}</strong>
|
||
<span class="metric-sub-pill success">已完成出款</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="metric-card metric-card--total">
|
||
<div class="metric-icon-wrap slate">
|
||
<el-icon><Tickets /></el-icon>
|
||
</div>
|
||
<div class="metric-info">
|
||
<span class="metric-label">总申请记录</span>
|
||
<strong class="metric-val">{{ total }} 笔</strong>
|
||
<span class="metric-sub-text">所有提现记录</span>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 筛选卡片 -->
|
||
<section class="section-card filter-card">
|
||
<el-form class="filter-grid" label-position="top" @keyup.enter="handleFilter">
|
||
<el-form-item label="提现状态">
|
||
<el-select
|
||
v-model="filters.status"
|
||
placeholder="全部状态"
|
||
class="full-control"
|
||
@change="handleFilter"
|
||
>
|
||
<el-option
|
||
v-for="opt in statusOptions"
|
||
:key="opt.value"
|
||
:label="opt.label"
|
||
:value="opt.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="用户 ID">
|
||
<el-input
|
||
v-model.number="filters.user_id"
|
||
placeholder="输入用户 ID 检索"
|
||
clearable
|
||
@clear="handleFilter"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
</section>
|
||
|
||
<!-- 提现列表表格 -->
|
||
<section class="section-card table-section-card">
|
||
<div class="table-card-header">
|
||
<div class="table-header-title">
|
||
<el-icon class="header-icon"><DocumentChecked /></el-icon>
|
||
<span>提现申请明细</span>
|
||
</div>
|
||
<span class="count-badge">共 {{ total }} 条申请</span>
|
||
</div>
|
||
|
||
<div class="table-container">
|
||
<el-table
|
||
v-loading="loading"
|
||
class="custom-enterprise-table"
|
||
:data="withdrawals"
|
||
stripe
|
||
empty-text="暂无提现记录"
|
||
>
|
||
<el-table-column label="提现单号" min-width="190" fixed="left">
|
||
<template #default="{ row }">
|
||
<div class="withdraw-no-cell">
|
||
<span class="mono-code">{{ row.withdraw_no }}</span>
|
||
<small class="id-tag">#ID: {{ row.id }}</small>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column label="申请用户" min-width="160">
|
||
<template #default="{ row }">
|
||
<div class="user-cell">
|
||
<div class="user-name-line">
|
||
<el-icon class="user-icon"><User /></el-icon>
|
||
<span class="user-name">{{ row.user_nickname || '-' }}</span>
|
||
</div>
|
||
<small class="user-sub">{{ row.user_phone || `UID: ${row.user_id}` }}</small>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column label="提现金额" min-width="140" align="right">
|
||
<template #default="{ row }">
|
||
<div class="amount-stack">
|
||
<strong class="withdraw-amount">{{ money(row.amount_cent) }}</strong>
|
||
<small v-if="row.fee_cent" class="fee-text">扣费 {{ money(row.fee_cent) }}</small>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column label="收款账户信息" min-width="210">
|
||
<template #default="{ row }">
|
||
<div class="account-cell">
|
||
<div class="account-header">
|
||
<el-tag size="small" effect="plain" class="acc-tag">
|
||
{{ accountTypeLabel(row.account_type) }}
|
||
</el-tag>
|
||
<span class="acc-name">{{ row.account_name || '-' }}</span>
|
||
</div>
|
||
<small class="acc-no">{{ row.account_no }}</small>
|
||
<small v-if="row.bank_name" class="bank-name">{{ row.bank_name }}</small>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column label="状态" width="110" align="center">
|
||
<template #default="{ row }">
|
||
<el-tag :type="statusType(row.status)" class="status-pill">
|
||
{{ statusLabel(row.status) }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column label="申请时间" min-width="165">
|
||
<template #default="{ row }">
|
||
<span class="time-cell">{{ formatDateTime(row.created_at) }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
|
||
<el-table-column label="操作" width="240" fixed="right" align="center">
|
||
<template #default="{ row }">
|
||
<div class="action-cell">
|
||
<el-button link type="primary" :icon="View" @click="openDetail(row)">
|
||
详情
|
||
</el-button>
|
||
<el-button link type="primary" @click="goToOwnerOrders(row)"> 号主订单 </el-button>
|
||
<el-button
|
||
v-if="row.status === 'pending'"
|
||
link
|
||
type="success"
|
||
@click="handleReview(row, true)"
|
||
>
|
||
通过
|
||
</el-button>
|
||
<el-button
|
||
v-if="row.status === 'pending'"
|
||
link
|
||
type="danger"
|
||
@click="handleReview(row, false)"
|
||
>
|
||
拒绝
|
||
</el-button>
|
||
<el-button
|
||
v-if="row.status === 'processing'"
|
||
link
|
||
type="primary"
|
||
@click="handleConfirmPayment(row)"
|
||
>
|
||
确认打款
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
|
||
<AdminTablePagination
|
||
v-if="total > 0"
|
||
v-model:current-page="currentPage"
|
||
v-model:page-size="pageSize"
|
||
:total="total"
|
||
@page-change="loadWithdrawals"
|
||
/>
|
||
</section>
|
||
|
||
<!-- 详情对话框 -->
|
||
<WithdrawalDetailDialog
|
||
v-model="showDetailDialog"
|
||
:withdrawal="selectedWithdrawal"
|
||
:return-query="withdrawalReturnQuery"
|
||
@saved="onDetailDialogSaved"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.finance-page-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20px;
|
||
padding: 0 4px 32px;
|
||
}
|
||
|
||
/* ================= 头部卡片 ================= */
|
||
.page-header-card {
|
||
background: #ffffff;
|
||
border-radius: 16px;
|
||
padding: 24px 28px;
|
||
box-shadow:
|
||
0 1px 3px rgba(15, 23, 42, 0.04),
|
||
0 4px 12px rgba(15, 23, 42, 0.02);
|
||
border: 1px solid rgba(226, 232, 240, 0.8);
|
||
}
|
||
|
||
.header-main {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.title-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
}
|
||
|
||
.eyebrow-tag {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
letter-spacing: 0.8px;
|
||
color: #7c3aed;
|
||
background: #f5f3ff;
|
||
padding: 3px 10px;
|
||
border-radius: 20px;
|
||
width: fit-content;
|
||
}
|
||
|
||
.page-title {
|
||
margin: 0;
|
||
font-size: 26px;
|
||
font-weight: 800;
|
||
color: #0f172a;
|
||
letter-spacing: -0.5px;
|
||
}
|
||
|
||
.page-subtitle {
|
||
margin: 0;
|
||
font-size: 13.5px;
|
||
color: #64748b;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.toolbar-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
/* ================= 核心指标卡片 ================= */
|
||
.summary-metric-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||
gap: 16px;
|
||
}
|
||
|
||
.metric-card {
|
||
background: #ffffff;
|
||
border-radius: 16px;
|
||
padding: 18px 20px;
|
||
border: 1px solid rgba(226, 232, 240, 0.8);
|
||
box-shadow:
|
||
0 1px 3px rgba(15, 23, 42, 0.04),
|
||
0 4px 12px rgba(15, 23, 42, 0.02);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
transition:
|
||
transform 0.2s ease,
|
||
box-shadow 0.2s ease;
|
||
}
|
||
|
||
.metric-card:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.08);
|
||
}
|
||
|
||
.metric-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 4px;
|
||
}
|
||
|
||
.metric-card--review::before {
|
||
background: linear-gradient(90deg, #f59e0b, #fbbf24);
|
||
}
|
||
|
||
.metric-card--pay::before {
|
||
background: linear-gradient(90deg, #6366f1, #818cf8);
|
||
}
|
||
|
||
.metric-card--paid::before {
|
||
background: linear-gradient(90deg, #10b981, #34d399);
|
||
}
|
||
|
||
.metric-card--total::before {
|
||
background: linear-gradient(90deg, #64748b, #94a3b8);
|
||
}
|
||
|
||
.metric-icon-wrap {
|
||
width: 44px;
|
||
height: 44px;
|
||
border-radius: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 20px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.metric-icon-wrap.amber {
|
||
background: #fffbeb;
|
||
color: #f59e0b;
|
||
}
|
||
|
||
.metric-icon-wrap.indigo {
|
||
background: #eef2ff;
|
||
color: #6366f1;
|
||
}
|
||
|
||
.metric-icon-wrap.emerald {
|
||
background: #ecfdf5;
|
||
color: #10b981;
|
||
}
|
||
|
||
.metric-icon-wrap.slate {
|
||
background: #f1f5f9;
|
||
color: #64748b;
|
||
}
|
||
|
||
.metric-info {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.metric-label {
|
||
font-size: 12.5px;
|
||
font-weight: 500;
|
||
color: #64748b;
|
||
}
|
||
|
||
.metric-val {
|
||
font-size: 22px;
|
||
font-weight: 800;
|
||
color: #0f172a;
|
||
letter-spacing: -0.3px;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.amber-val {
|
||
color: #d97706;
|
||
}
|
||
|
||
.indigo-val {
|
||
color: #4f46e5;
|
||
}
|
||
|
||
.emerald-val {
|
||
color: #059669;
|
||
}
|
||
|
||
.metric-sub-pill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
width: fit-content;
|
||
}
|
||
|
||
.metric-sub-pill.warning {
|
||
background: #fffbeb;
|
||
color: #d97706;
|
||
}
|
||
|
||
.metric-sub-pill.info {
|
||
background: #eef2ff;
|
||
color: #4f46e5;
|
||
}
|
||
|
||
.metric-sub-pill.success {
|
||
background: #f0fdf4;
|
||
color: #16a34a;
|
||
}
|
||
|
||
.metric-sub-text {
|
||
font-size: 11.5px;
|
||
color: #94a3b8;
|
||
}
|
||
|
||
/* ================= 筛选面板 ================= */
|
||
.section-card {
|
||
background: #ffffff;
|
||
border-radius: 16px;
|
||
padding: 22px 24px;
|
||
border: 1px solid rgba(226, 232, 240, 0.8);
|
||
box-shadow:
|
||
0 1px 3px rgba(15, 23, 42, 0.04),
|
||
0 4px 12px rgba(15, 23, 42, 0.02);
|
||
}
|
||
|
||
.filter-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 14px 16px;
|
||
}
|
||
|
||
.filter-grid :deep(.el-form-item) {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.filter-grid :deep(.el-form-item__label) {
|
||
padding-bottom: 4px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: #64748b;
|
||
}
|
||
|
||
.full-control {
|
||
width: 100%;
|
||
}
|
||
|
||
/* ================= 表格卡片 ================= */
|
||
.table-section-card {
|
||
padding-bottom: 16px;
|
||
}
|
||
|
||
.table-card-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.table-header-title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: #1e293b;
|
||
}
|
||
|
||
.header-icon {
|
||
color: #7c3aed;
|
||
font-size: 18px;
|
||
}
|
||
|
||
.count-badge {
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: #7c3aed;
|
||
background: #f5f3ff;
|
||
padding: 3px 10px;
|
||
border-radius: 20px;
|
||
}
|
||
|
||
.table-container {
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
border: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.custom-enterprise-table :deep(.el-table__header-wrapper th) {
|
||
background-color: #f8fafc !important;
|
||
color: #475569;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
padding: 12px 0;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
}
|
||
|
||
.custom-enterprise-table :deep(.el-table__body-wrapper td) {
|
||
padding: 12px 0;
|
||
font-size: 13px;
|
||
color: #334155;
|
||
}
|
||
|
||
.withdraw-no-cell {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
|
||
.mono-code {
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||
font-size: 12.5px;
|
||
font-weight: 600;
|
||
color: #1e293b;
|
||
}
|
||
|
||
.id-tag {
|
||
font-size: 11px;
|
||
color: #94a3b8;
|
||
}
|
||
|
||
.user-cell {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
|
||
.user-name-line {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
font-weight: 600;
|
||
color: #0f172a;
|
||
}
|
||
|
||
.user-icon {
|
||
color: #94a3b8;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.user-sub {
|
||
font-size: 11.5px;
|
||
color: #64748b;
|
||
}
|
||
|
||
.amount-stack {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
gap: 2px;
|
||
}
|
||
|
||
.withdraw-amount {
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
color: #0f172a;
|
||
}
|
||
|
||
.fee-text {
|
||
font-size: 11px;
|
||
color: #94a3b8;
|
||
}
|
||
|
||
.account-cell {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
|
||
.account-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
|
||
.acc-tag {
|
||
font-weight: 600;
|
||
}
|
||
|
||
.acc-name {
|
||
font-size: 12.5px;
|
||
font-weight: 500;
|
||
color: #1e293b;
|
||
}
|
||
|
||
.acc-no {
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||
font-size: 11.5px;
|
||
color: #64748b;
|
||
}
|
||
|
||
.bank-name {
|
||
font-size: 11px;
|
||
color: #94a3b8;
|
||
}
|
||
|
||
.status-pill {
|
||
font-weight: 600;
|
||
}
|
||
|
||
.time-cell {
|
||
font-size: 12px;
|
||
color: #475569;
|
||
}
|
||
|
||
.action-cell {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 4px;
|
||
flex-wrap: wrap;
|
||
}
|
||
</style>
|