1204 lines
28 KiB
Vue
1204 lines
28 KiB
Vue
<template>
|
||
<div class="agent-withdrawals">
|
||
<!-- 顶部导航 -->
|
||
<div class="page-header">
|
||
<button class="btn-back" @click="goBack">
|
||
<i class="icon-arrow-left"></i>
|
||
返回仪表盘
|
||
</button>
|
||
<h2>佣金提现</h2>
|
||
</div>
|
||
|
||
<!-- 顶部统计卡片 -->
|
||
<div class="stats-cards">
|
||
<div class="stat-card">
|
||
<div class="stat-icon">
|
||
<i class="icon-wallet"></i>
|
||
</div>
|
||
<div class="stat-content">
|
||
<div class="stat-value">¥{{ commissionStats.total_commission || '0.00' }}</div>
|
||
<div class="stat-label">总佣金</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon">
|
||
<i class="icon-money"></i>
|
||
</div>
|
||
<div class="stat-content">
|
||
<div class="stat-value">¥{{ commissionStats.available_amount || '0.00' }}</div>
|
||
<div class="stat-label">可提现金额</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon">
|
||
<i class="icon-pending"></i>
|
||
</div>
|
||
<div class="stat-content">
|
||
<div class="stat-value">¥{{ commissionStats.pending_withdrawal || '0.00' }}</div>
|
||
<div class="stat-label">提现中</div>
|
||
</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon">
|
||
<i class="icon-success"></i>
|
||
</div>
|
||
<div class="stat-content">
|
||
<div class="stat-value">¥{{ commissionStats.withdrawn_amount || '0.00' }}</div>
|
||
<div class="stat-label">已提现</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 收款方式设置 -->
|
||
<div class="payment-info-section">
|
||
<div class="section-header">
|
||
<h3>收款方式</h3>
|
||
<button class="btn-edit" @click="showPaymentForm = !showPaymentForm">
|
||
{{ showPaymentForm ? '取消' : '编辑' }}
|
||
</button>
|
||
</div>
|
||
|
||
<div v-if="!showPaymentForm" class="payment-info-display">
|
||
<div class="info-item">
|
||
<span class="label">收款方式:</span>
|
||
<span class="value">{{ getPaymentTypeText(paymentInfo.payment_type) || '未设置' }}</span>
|
||
</div>
|
||
<div class="info-item" v-if="paymentInfo.payment_type === 'bank'">
|
||
<span class="label">银行名称:</span>
|
||
<span class="value">{{ paymentInfo.bank_name || '未设置' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">{{ getAccountLabel(paymentInfo.payment_type) }}:</span>
|
||
<span class="value">{{ maskAccount(paymentInfo.account_number) || '未设置' }}</span>
|
||
</div>
|
||
<div class="info-item">
|
||
<span class="label">{{ getHolderLabel(paymentInfo.payment_type) }}:</span>
|
||
<span class="value">{{ paymentInfo.account_holder || '未设置' }}</span>
|
||
</div>
|
||
<div v-if="paymentInfo.payment_type !== 'bank' && paymentInfo.qr_code_url" class="info-item qr-code-preview">
|
||
<span class="label">收款码:</span>
|
||
<div class="qr-code-image">
|
||
<img :src="paymentInfo.qr_code_url" alt="收款码" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<form v-if="showPaymentForm" @submit.prevent="updatePaymentInfo" class="payment-form">
|
||
<div class="form-group">
|
||
<label>收款方式</label>
|
||
<select v-model="paymentForm.payment_type" @change="onPaymentTypeChange" required>
|
||
<option value="">请选择收款方式</option>
|
||
<option value="bank">银行卡</option>
|
||
<option value="wechat">微信收款码</option>
|
||
<option value="alipay">支付宝收款码</option>
|
||
<option value="unionpay">云闪付收款码</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div v-if="paymentForm.payment_type === 'bank'" class="form-group">
|
||
<label>银行名称</label>
|
||
<input
|
||
v-model="paymentForm.bank_name"
|
||
type="text"
|
||
placeholder="请输入银行名称"
|
||
required
|
||
>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>{{ getAccountLabel(paymentForm.payment_type) }}</label>
|
||
<input
|
||
v-model="paymentForm.account_number"
|
||
type="text"
|
||
:placeholder="getAccountPlaceholder(paymentForm.payment_type)"
|
||
required
|
||
>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>{{ getHolderLabel(paymentForm.payment_type) }}</label>
|
||
<input
|
||
v-model="paymentForm.account_holder"
|
||
type="text"
|
||
:placeholder="getHolderPlaceholder(paymentForm.payment_type)"
|
||
required
|
||
>
|
||
</div>
|
||
|
||
<div v-if="paymentForm.payment_type && paymentForm.payment_type !== 'bank'" class="form-group">
|
||
<label>收款码图片</label>
|
||
<div class="qr-code-upload">
|
||
<input
|
||
type="file"
|
||
ref="qrCodeInput"
|
||
accept="image/*"
|
||
@change="handleQrCodeUpload"
|
||
style="display: none;"
|
||
>
|
||
<div
|
||
class="upload-area"
|
||
:class="{ 'has-image': paymentForm.qr_code_url }"
|
||
@click="$refs.qrCodeInput.click()"
|
||
>
|
||
<div v-if="!paymentForm.qr_code_url" class="upload-placeholder">
|
||
<div class="upload-icon">📷</div>
|
||
<div class="upload-text">点击上传收款码</div>
|
||
<div class="upload-hint">支持 JPG、PNG 格式</div>
|
||
</div>
|
||
<div v-else class="uploaded-image">
|
||
<img :src="paymentForm.qr_code_url" alt="收款码" />
|
||
<div class="image-overlay">
|
||
<span>点击重新上传</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div v-if="uploadingQrCode" class="upload-progress">
|
||
上传中...
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<button type="submit" class="btn-primary" :disabled="updating">保存</button>
|
||
<button type="button" class="btn-secondary" @click="showPaymentForm = false">取消</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- 提现申请 -->
|
||
<div class="withdrawal-section">
|
||
<div class="section-header">
|
||
<h3>申请提现</h3>
|
||
</div>
|
||
|
||
<form @submit.prevent="submitWithdrawal" class="withdrawal-form">
|
||
<div class="form-group">
|
||
<label>提现金额</label>
|
||
<div class="amount-input">
|
||
<input
|
||
v-model="withdrawalForm.amount"
|
||
type="number"
|
||
step="0.01"
|
||
min="1"
|
||
:max="commissionStats.available_amount"
|
||
placeholder="请输入提现金额"
|
||
required
|
||
>
|
||
<span class="currency">元</span>
|
||
</div>
|
||
<div class="amount-tips">
|
||
可提现金额:¥{{ commissionStats.available_amount || '0.00' }}
|
||
</div>
|
||
</div>
|
||
<div class="form-actions">
|
||
<button
|
||
type="submit"
|
||
class="btn-primary"
|
||
:disabled="submitting || !canWithdraw"
|
||
>
|
||
{{ submitting ? '提交中...' : '申请提现' }}
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- 提现记录 -->
|
||
<div class="records-section">
|
||
<div class="section-header">
|
||
<h3>提现记录</h3>
|
||
<div class="filter-controls">
|
||
<select v-model="recordsFilter" @change="loadWithdrawalRecords">
|
||
<option value="">全部状态</option>
|
||
<option value="pending">待审核</option>
|
||
<option value="approved">已通过</option>
|
||
<option value="completed">已完成</option>
|
||
<option value="rejected">已拒绝</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="records-list">
|
||
<div v-if="loading" class="loading">加载中...</div>
|
||
<div v-else-if="withdrawalRecords.length === 0" class="empty">
|
||
暂无提现记录
|
||
</div>
|
||
<div v-else>
|
||
<div
|
||
v-for="record in withdrawalRecords"
|
||
:key="record.id"
|
||
class="record-item"
|
||
>
|
||
<div class="record-header">
|
||
<span class="amount">¥{{ record.amount }}</span>
|
||
<span class="status" :class="`status-${record.status}`">
|
||
{{ getStatusText(record.status) }}
|
||
</span>
|
||
</div>
|
||
<div class="record-details">
|
||
<div class="detail-item">
|
||
<span class="label">申请时间:</span>
|
||
<span class="value">{{ formatDate(record.created_at) }}</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="label">收款信息:</span>
|
||
<span class="value">
|
||
{{ getPaymentTypeText(record.payment_type || 'bank') }}
|
||
<template v-if="record.payment_type === 'bank' || !record.payment_type">
|
||
- {{ record.bank_name }} {{ maskAccount(record.account_number || record.bank_account) }}
|
||
</template>
|
||
<template v-else>
|
||
- {{ record.account_holder }} ({{ maskAccount(record.account_number) }})
|
||
</template>
|
||
</span>
|
||
</div>
|
||
<div v-if="record.admin_note" class="detail-item">
|
||
<span class="label">备注:</span>
|
||
<span class="value">{{ record.admin_note }}</span>
|
||
</div>
|
||
<div v-if="record.completed_at" class="detail-item">
|
||
<span class="label">完成时间:</span>
|
||
<span class="value">{{ formatDate(record.completed_at) }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { ref, reactive, computed, onMounted } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import { useUserStore } from '@/stores/user'
|
||
import api from '@/utils/api'
|
||
|
||
export default {
|
||
name: 'AgentWithdrawals',
|
||
setup() {
|
||
const router = useRouter()
|
||
const userStore = useUserStore()
|
||
|
||
// 响应式数据
|
||
const loading = ref(false)
|
||
const updating = ref(false)
|
||
const submitting = ref(false)
|
||
const showPaymentForm = ref(false)
|
||
const recordsFilter = ref('')
|
||
|
||
const commissionStats = reactive({
|
||
total_commission: '0.00',
|
||
available_amount: '0.00',
|
||
pending_withdrawal: '0.00',
|
||
withdrawn_amount: '0.00'
|
||
})
|
||
|
||
const paymentInfo = reactive({
|
||
payment_type: '',
|
||
bank_name: '',
|
||
account_number: '',
|
||
account_holder: '',
|
||
qr_code_url: ''
|
||
})
|
||
|
||
const paymentForm = reactive({
|
||
payment_type: '',
|
||
bank_name: '',
|
||
account_number: '',
|
||
account_holder: '',
|
||
qr_code_url: ''
|
||
})
|
||
|
||
const withdrawalForm = reactive({
|
||
amount: ''
|
||
})
|
||
|
||
const withdrawalRecords = ref([])
|
||
const uploadingQrCode = ref(false)
|
||
|
||
// 计算属性
|
||
const canWithdraw = computed(() => {
|
||
const amount = parseFloat(withdrawalForm.amount)
|
||
const available = parseFloat(commissionStats.available_amount)
|
||
const hasBasicInfo = amount > 0 && amount <= available && paymentInfo.account_number && paymentInfo.payment_type
|
||
|
||
// 银行卡只需要基本信息
|
||
if (paymentInfo.payment_type === 'bank') {
|
||
return hasBasicInfo
|
||
}
|
||
|
||
// 收款码需要额外验证图片
|
||
return hasBasicInfo && paymentInfo.qr_code_url
|
||
})
|
||
|
||
// 方法
|
||
/**
|
||
* 加载佣金统计数据
|
||
*/
|
||
const loadCommissionStats = async () => {
|
||
try {
|
||
const response = await api.get('/agent-withdrawals/stats')
|
||
if (response.data.success) {
|
||
// 更新佣金统计数据
|
||
Object.assign(commissionStats, {
|
||
total_commission: response.data.data.total_commission || '0.00',
|
||
available_amount: response.data.data.available_amount || '0.00',
|
||
pending_withdrawal: response.data.data.pending_withdrawal || '0.00',
|
||
withdrawn_amount: response.data.data.withdrawn_amount || '0.00'
|
||
})
|
||
|
||
// 处理收款方式信息
|
||
if (response.data.data.paymentInfo) {
|
||
Object.assign(paymentInfo, response.data.data.paymentInfo)
|
||
Object.assign(paymentForm, response.data.data.paymentInfo)
|
||
} else if (response.data.data.bank_account) {
|
||
// 兼容旧的银行信息字段
|
||
const bankInfo = {
|
||
payment_type: 'bank',
|
||
bank_name: response.data.data.bank_name || '',
|
||
account_number: response.data.data.bank_account || '',
|
||
account_holder: response.data.data.account_holder || '',
|
||
qr_code_url: ''
|
||
}
|
||
Object.assign(paymentInfo, bankInfo)
|
||
Object.assign(paymentForm, bankInfo)
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('加载佣金统计失败:', error)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新收款方式信息
|
||
*/
|
||
const updatePaymentInfo = async () => {
|
||
if (updating.value) return
|
||
|
||
updating.value = true
|
||
try {
|
||
const response = await api.put('/agent-withdrawals/payment-info', paymentForm)
|
||
if (response.data.success) {
|
||
Object.assign(paymentInfo, paymentForm)
|
||
showPaymentForm.value = false
|
||
alert('收款方式更新成功')
|
||
} else {
|
||
alert(response.data.message || '更新失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('更新收款方式失败:', error)
|
||
alert('更新失败,请重试')
|
||
} finally {
|
||
updating.value = false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 提交提现申请
|
||
*/
|
||
const submitWithdrawal = async () => {
|
||
if (submitting.value || !canWithdraw.value) return
|
||
|
||
if (!paymentInfo.account_number || !paymentInfo.payment_type) {
|
||
alert('请先设置收款方式')
|
||
return
|
||
}
|
||
|
||
// 收款码类型需要验证图片
|
||
if (paymentInfo.payment_type !== 'bank' && !paymentInfo.qr_code_url) {
|
||
alert('请上传收款码图片')
|
||
return
|
||
}
|
||
|
||
submitting.value = true
|
||
try {
|
||
const response = await api.post('/agent-withdrawals/apply', {
|
||
amount: parseFloat(withdrawalForm.amount)
|
||
})
|
||
|
||
if (response.data.success) {
|
||
alert('提现申请提交成功,请等待审核')
|
||
withdrawalForm.amount = ''
|
||
await loadCommissionStats()
|
||
await loadWithdrawalRecords()
|
||
} else {
|
||
alert(response.data.message || '申请失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('提现申请失败:', error)
|
||
alert('申请失败,请重试')
|
||
} finally {
|
||
submitting.value = false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 加载提现记录
|
||
*/
|
||
const loadWithdrawalRecords = async () => {
|
||
loading.value = true
|
||
try {
|
||
const params = {}
|
||
if (recordsFilter.value) {
|
||
params.status = recordsFilter.value
|
||
}
|
||
|
||
const response = await api.get('/agent-withdrawals/records', { params })
|
||
if (response.data.success) {
|
||
withdrawalRecords.value = response.data.data.records || []
|
||
}
|
||
} catch (error) {
|
||
console.error('加载提现记录失败:', error)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 格式化日期
|
||
*/
|
||
const formatDate = (dateString) => {
|
||
if (!dateString) return ''
|
||
const date = new Date(dateString)
|
||
return date.toLocaleString('zh-CN')
|
||
}
|
||
|
||
/**
|
||
* 获取状态文本
|
||
*/
|
||
const getStatusText = (status) => {
|
||
const statusMap = {
|
||
pending: '待审核',
|
||
approved: '已通过',
|
||
completed: '已完成',
|
||
rejected: '已拒绝'
|
||
}
|
||
return statusMap[status] || status
|
||
}
|
||
|
||
/**
|
||
* 掩码显示账号信息
|
||
*/
|
||
const maskAccount = (account) => {
|
||
if (!account) return ''
|
||
if (account.length <= 8) return account
|
||
return account.slice(0, 4) + '****' + account.slice(-4)
|
||
}
|
||
|
||
/**
|
||
* 处理收款码图片上传
|
||
*/
|
||
const handleQrCodeUpload = async (event) => {
|
||
const file = event.target.files[0]
|
||
if (!file) return
|
||
|
||
// 验证文件类型
|
||
if (!file.type.startsWith('image/')) {
|
||
alert('请选择图片文件')
|
||
return
|
||
}
|
||
|
||
// 验证文件大小(限制为5MB)
|
||
if (file.size > 5 * 1024 * 1024) {
|
||
alert('图片大小不能超过5MB')
|
||
return
|
||
}
|
||
|
||
uploadingQrCode.value = true
|
||
|
||
try {
|
||
const formData = new FormData()
|
||
formData.append('qrCode', file)
|
||
|
||
const response = await api.post('/agent-withdrawals/upload-qr-code', formData, {
|
||
headers: {
|
||
'Content-Type': 'multipart/form-data'
|
||
}
|
||
})
|
||
|
||
if (response.data.success) {
|
||
paymentForm.qr_code_url = response.data.data.url
|
||
} else {
|
||
alert(response.data.message || '上传失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('上传收款码失败:', error)
|
||
alert('上传失败,请重试')
|
||
} finally {
|
||
uploadingQrCode.value = false
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* 获取收款方式显示文本
|
||
*/
|
||
const getPaymentTypeText = (type) => {
|
||
const types = {
|
||
'bank': '银行卡',
|
||
'wechat': '微信收款码',
|
||
'alipay': '支付宝收款码',
|
||
'unionpay': '云闪付收款码'
|
||
}
|
||
return types[type] || ''
|
||
}
|
||
|
||
/**
|
||
* 获取账号标签
|
||
*/
|
||
const getAccountLabel = (type) => {
|
||
const labels = {
|
||
'bank': '银行账号',
|
||
'wechat': '微信号',
|
||
'alipay': '支付宝账号',
|
||
'unionpay': '云闪付账号'
|
||
}
|
||
return labels[type] || '账号'
|
||
}
|
||
|
||
/**
|
||
* 获取持有人标签
|
||
*/
|
||
const getHolderLabel = (type) => {
|
||
const labels = {
|
||
'bank': '开户人',
|
||
'wechat': '微信昵称',
|
||
'alipay': '支付宝昵称',
|
||
'unionpay': '云闪付昵称'
|
||
}
|
||
return labels[type] || '持有人'
|
||
}
|
||
|
||
/**
|
||
* 获取账号输入占位符
|
||
*/
|
||
const getAccountPlaceholder = (type) => {
|
||
const placeholders = {
|
||
'bank': '请输入银行账号',
|
||
'wechat': '请输入微信号',
|
||
'alipay': '请输入支付宝账号',
|
||
'unionpay': '请输入云闪付账号'
|
||
}
|
||
return placeholders[type] || '请输入账号'
|
||
}
|
||
|
||
/**
|
||
* 获取持有人输入占位符
|
||
*/
|
||
const getHolderPlaceholder = (type) => {
|
||
const placeholders = {
|
||
'bank': '请输入开户人姓名',
|
||
'wechat': '请输入微信昵称',
|
||
'alipay': '请输入支付宝昵称',
|
||
'unionpay': '请输入云闪付昵称'
|
||
}
|
||
return placeholders[type] || '请输入持有人姓名'
|
||
}
|
||
|
||
/**
|
||
* 收款方式类型改变时的处理
|
||
*/
|
||
const onPaymentTypeChange = () => {
|
||
// 清空非通用字段
|
||
if (paymentForm.payment_type !== 'bank') {
|
||
paymentForm.bank_name = ''
|
||
} else {
|
||
paymentForm.qr_code_url = ''
|
||
}
|
||
}
|
||
|
||
// 监听收款方式信息变化
|
||
const initPaymentForm = () => {
|
||
Object.assign(paymentForm, paymentInfo)
|
||
}
|
||
|
||
/**
|
||
* 返回代理仪表盘
|
||
*/
|
||
const goBack = () => {
|
||
router.push('/agent/dashboard')
|
||
}
|
||
|
||
// 生命周期
|
||
onMounted(async () => {
|
||
await loadCommissionStats()
|
||
await loadWithdrawalRecords()
|
||
initPaymentForm()
|
||
})
|
||
|
||
return {
|
||
loading,
|
||
updating,
|
||
submitting,
|
||
showPaymentForm,
|
||
recordsFilter,
|
||
commissionStats,
|
||
paymentInfo,
|
||
paymentForm,
|
||
withdrawalForm,
|
||
withdrawalRecords,
|
||
canWithdraw,
|
||
loadCommissionStats,
|
||
updatePaymentInfo,
|
||
submitWithdrawal,
|
||
loadWithdrawalRecords,
|
||
formatDate,
|
||
getStatusText,
|
||
maskAccount,
|
||
handleQrCodeUpload,
|
||
uploadingQrCode,
|
||
getPaymentTypeText,
|
||
getAccountLabel,
|
||
getHolderLabel,
|
||
getAccountPlaceholder,
|
||
getHolderPlaceholder,
|
||
onPaymentTypeChange,
|
||
initPaymentForm,
|
||
goBack
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.agent-withdrawals {
|
||
padding: 20px;
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
/* 页面头部 */
|
||
.page-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
margin-bottom: 24px;
|
||
padding-bottom: 16px;
|
||
border-bottom: 1px solid #eee;
|
||
}
|
||
|
||
.page-header h2 {
|
||
margin: 0;
|
||
font-size: 24px;
|
||
color: #333;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* 返回按钮 */
|
||
.btn-back {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 10px 16px;
|
||
background: #f8f9fa;
|
||
border: 1px solid #dee2e6;
|
||
border-radius: 8px;
|
||
color: #495057;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.btn-back:hover {
|
||
background: #e9ecef;
|
||
border-color: #adb5bd;
|
||
color: #212529;
|
||
}
|
||
|
||
.btn-back .icon-arrow-left::before {
|
||
content: '←';
|
||
font-size: 16px;
|
||
}
|
||
|
||
/* 统计卡片 */
|
||
.stats-cards {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||
gap: 20px;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.stat-card {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
}
|
||
|
||
.stat-icon {
|
||
width: 48px;
|
||
height: 48px;
|
||
border-radius: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 24px;
|
||
color: white;
|
||
}
|
||
|
||
.stat-card:nth-child(1) .stat-icon {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
}
|
||
|
||
.stat-card:nth-child(2) .stat-icon {
|
||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||
}
|
||
|
||
.stat-card:nth-child(3) .stat-icon {
|
||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||
}
|
||
|
||
.stat-card:nth-child(4) .stat-icon {
|
||
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
||
}
|
||
|
||
.stat-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.stat-value {
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 14px;
|
||
color: #666;
|
||
}
|
||
|
||
/* 通用区块样式 */
|
||
.payment-info-section,
|
||
.withdrawal-section,
|
||
.records-section {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
margin-bottom: 20px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.section-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
padding-bottom: 12px;
|
||
border-bottom: 1px solid #eee;
|
||
}
|
||
|
||
.section-header h3 {
|
||
margin: 0;
|
||
font-size: 18px;
|
||
color: #333;
|
||
}
|
||
|
||
/* 收款方式信息显示 */
|
||
.payment-info-display {
|
||
display: grid;
|
||
gap: 12px;
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.info-item .label {
|
||
width: 100px;
|
||
color: #666;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.info-item .value {
|
||
color: #333;
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 收款码预览 */
|
||
.qr-code-preview {
|
||
grid-template-columns: auto 1fr;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.qr-code-image {
|
||
max-width: 150px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.qr-code-image img {
|
||
width: 100%;
|
||
height: auto;
|
||
display: block;
|
||
}
|
||
|
||
/* 表单样式 */
|
||
.payment-form,
|
||
.withdrawal-form {
|
||
display: grid;
|
||
gap: 20px;
|
||
}
|
||
|
||
/* 收款方式选择器 */
|
||
.payment-type-selector {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||
gap: 12px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.payment-type-option {
|
||
padding: 12px 16px;
|
||
border: 2px solid #e9ecef;
|
||
border-radius: 8px;
|
||
background: white;
|
||
cursor: pointer;
|
||
text-align: center;
|
||
transition: all 0.3s ease;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.payment-type-option:hover {
|
||
border-color: #007bff;
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
.payment-type-option.active {
|
||
border-color: #007bff;
|
||
background: #007bff;
|
||
color: white;
|
||
}
|
||
|
||
.payment-type-option.active:hover {
|
||
background: #0056b3;
|
||
}
|
||
|
||
/* 收款码上传 */
|
||
.qr-code-upload {
|
||
display: grid;
|
||
gap: 12px;
|
||
}
|
||
|
||
.upload-area {
|
||
border: 2px dashed #ddd;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
min-height: 120px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.upload-area:hover {
|
||
border-color: #007bff;
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
.upload-area.has-image {
|
||
padding: 0;
|
||
border: 1px solid #ddd;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.upload-placeholder {
|
||
display: grid;
|
||
gap: 8px;
|
||
color: #666;
|
||
}
|
||
|
||
.upload-icon {
|
||
font-size: 32px;
|
||
}
|
||
|
||
.upload-text {
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.upload-hint {
|
||
font-size: 12px;
|
||
color: #999;
|
||
}
|
||
|
||
.uploaded-image {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 200px;
|
||
}
|
||
|
||
.uploaded-image img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: contain;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.image-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.7);
|
||
color: white;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
opacity: 0;
|
||
transition: opacity 0.3s ease;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.uploaded-image:hover .image-overlay {
|
||
opacity: 1;
|
||
}
|
||
|
||
.upload-progress {
|
||
text-align: center;
|
||
color: #007bff;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.form-group {
|
||
display: grid;
|
||
gap: 8px;
|
||
}
|
||
|
||
.form-group label {
|
||
font-size: 14px;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.form-group input,
|
||
.form-group select {
|
||
padding: 12px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
transition: border-color 0.3s;
|
||
}
|
||
|
||
.form-group input:focus,
|
||
.form-group select:focus {
|
||
outline: none;
|
||
border-color: #007bff;
|
||
}
|
||
|
||
.amount-input {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.amount-input input {
|
||
flex: 1;
|
||
padding-right: 40px;
|
||
}
|
||
|
||
.currency {
|
||
position: absolute;
|
||
right: 12px;
|
||
color: #666;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.amount-tips {
|
||
font-size: 12px;
|
||
color: #666;
|
||
}
|
||
|
||
.form-actions {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
/* 按钮样式 */
|
||
.btn-primary,
|
||
.btn-secondary,
|
||
.btn-edit {
|
||
padding: 10px 20px;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: #007bff;
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary:hover:not(:disabled) {
|
||
background: #0056b3;
|
||
}
|
||
|
||
.btn-primary:disabled {
|
||
background: #ccc;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: #6c757d;
|
||
color: white;
|
||
}
|
||
|
||
.btn-secondary:hover {
|
||
background: #545b62;
|
||
}
|
||
|
||
.btn-edit {
|
||
background: #28a745;
|
||
color: white;
|
||
padding: 8px 16px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.btn-edit:hover {
|
||
background: #1e7e34;
|
||
}
|
||
|
||
/* 筛选控件 */
|
||
.filter-controls select {
|
||
padding: 8px 12px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 记录列表 */
|
||
.records-list {
|
||
display: grid;
|
||
gap: 16px;
|
||
}
|
||
|
||
.loading,
|
||
.empty {
|
||
text-align: center;
|
||
padding: 40px;
|
||
color: #666;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.record-item {
|
||
border: 1px solid #eee;
|
||
border-radius: 8px;
|
||
padding: 16px;
|
||
transition: box-shadow 0.3s;
|
||
}
|
||
|
||
.record-item:hover {
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.record-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.record-header .amount {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.status {
|
||
padding: 4px 12px;
|
||
border-radius: 20px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.status-pending {
|
||
background: #fff3cd;
|
||
color: #856404;
|
||
}
|
||
|
||
.status-approved {
|
||
background: #d1ecf1;
|
||
color: #0c5460;
|
||
}
|
||
|
||
.status-completed {
|
||
background: #d4edda;
|
||
color: #155724;
|
||
}
|
||
|
||
.status-rejected {
|
||
background: #f8d7da;
|
||
color: #721c24;
|
||
}
|
||
|
||
.record-details {
|
||
display: grid;
|
||
gap: 8px;
|
||
}
|
||
|
||
.detail-item {
|
||
display: flex;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.detail-item .label {
|
||
width: 80px;
|
||
color: #666;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.detail-item .value {
|
||
color: #333;
|
||
flex: 1;
|
||
}
|
||
|
||
/* 图标 */
|
||
.icon-wallet::before { content: '💰'; }
|
||
.icon-money::before { content: '💵'; }
|
||
.icon-pending::before { content: '⏳'; }
|
||
.icon-success::before { content: '✅'; }
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 768px) {
|
||
.agent-withdrawals {
|
||
padding: 16px;
|
||
}
|
||
|
||
.stats-cards {
|
||
grid-template-columns: 1fr;
|
||
gap: 16px;
|
||
}
|
||
|
||
.stat-card {
|
||
padding: 20px;
|
||
}
|
||
|
||
.section-header {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
}
|
||
|
||
.form-actions {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.record-header {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 8px;
|
||
}
|
||
}
|
||
</style> |