Files
jurong_circle_frontdesk/src/views/Transfers.vue
2025-07-26 15:35:53 +08:00

1433 lines
33 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="transfers-page">
<!-- 导航栏 -->
<nav class="navbar">
<div class="nav-center">
<h1 class="nav-title">转账管理</h1>
</div>
<div class="nav-right">
<!-- 移除发起转账按钮 -->
</div>
</nav>
<!-- 账户信息 -->
<div class="account-info">
<el-card>
<div class="account-balance">
<div class="balance-item">
<span class="label">账户余额</span>
<span class="amount">¥{{ accountInfo.balance || '0.00' }}</span>
</div>
<div class="balance-item">
<span class="label">账户类型</span>
<span class="type">{{ accountInfo.account_type === 'public' ? '公户' : '用户账户' }}</span>
</div>
</div>
</el-card>
</div>
<!-- 待转账 -->
<div class="pending-transfers" v-if="pendingAllocations.length > 0 || pendingTransfers.length > 0">
<el-card>
<template #header>
<div class="card-header">
<span>待转账</span>
<el-badge :value="pendingAllocations.length + pendingTransfers.length" class="badge" />
</div>
</template>
<div class="transfer-list">
<!-- 我要转给别人的分配 -->
<div
v-for="allocation in pendingAllocations"
:key="'allocation-' + allocation.id"
class="transfer-item pending"
:class="{ 'timeout-item': !allocation.can_transfer }"
>
<div class="transfer-info">
<div class="transfer-header">
<span class="from-user"></span>
<el-icon class="arrow"><Right /></el-icon>
<span class="to-user">{{ allocation.to_user_name }}</span>
<span class="amount">¥{{ allocation.amount }}</span>
<el-tag
:type="getOrderStatusType(allocation.order_status)"
size="small"
>
{{ getOrderStatusText(allocation.order_status) }}
</el-tag>
</div>
<div class="transfer-details">
<p class="description">匹配订单 {{ allocation.matching_order_id }} - {{ allocation.cycle_number }}</p>
<p class="time">创建时间{{ formatTime(allocation.created_at) }}</p>
<div class="deadline-info">
<span class="deadline-label">转账截止</span>
<span class="deadline-time" :class="allocation.time_status">
{{ formatTime(allocation.deadline) }}
</span>
<span class="time-left" :class="allocation.time_status">
({{ allocation.time_left }})
</span>
</div>
<!-- 超时警告 -->
<div v-if="!allocation.can_transfer" class="timeout-warning">
<el-icon><Warning /></el-icon>
<span class="warning-text">{{ allocation.timeout_reason }}</span>
</div>
</div>
</div>
</div>
<!-- 别人转给我的进行中转账 -->
<div
v-for="transfer in pendingTransfers"
:key="'transfer-' + transfer.id"
class="transfer-item pending"
>
<div class="transfer-info">
<div class="transfer-header">
<span class="from-user">{{ transfer.from_real_name || transfer.from_username }}</span>
<el-icon class="arrow"><Right /></el-icon>
<span class="to-user"></span>
<span class="amount">¥{{ transfer.amount }}</span>
<el-tag type="warning" size="small">
进行中
</el-tag>
</div>
<div class="transfer-details">
<p class="description">{{ transfer.description || '转账' }}</p>
<p class="time">创建时间{{ formatTime(transfer.created_at) }}</p>
<p class="type">类型{{ getTransferTypeText(transfer.transfer_type) }}</p>
</div>
</div>
</div>
</div>
</el-card>
</div>
<!-- 转账记录 -->
<div class="transfer-history">
<el-card>
<template #header>
<div class="card-header">
<span>转账记录</span>
<el-select v-model="statusFilter" placeholder="状态筛选" size="small" style="width: 120px">
<el-option label="全部" value="" />
<el-option label="待确认" value="pending" />
<el-option label="已确认" value="confirmed" />
<el-option label="已拒绝" value="rejected" />
</el-select>
</div>
</template>
<div class="transfer-list" v-loading="loading">
<div
v-for="transfer in transferHistory"
:key="transfer.id"
class="transfer-item"
:class="transfer.status"
>
<div class="transfer-info">
<div class="transfer-header">
<span class="from-user">
{{ transfer.from_user_id === userStore.user.id ? '我' : (transfer.from_real_name || transfer.from_username) }}
</span>
<el-icon class="arrow"><Right /></el-icon>
<span class="to-user">
{{ transfer.to_user_id === userStore.user.id ? '我' : (transfer.to_real_name || transfer.to_username) }}
</span>
<span class="amount">¥{{ transfer.amount }}</span>
<el-tag
:type="getStatusType(transfer.status)"
size="small"
>
{{ getStatusText(transfer.status) }}
</el-tag>
</div>
<div class="transfer-details">
<p class="description">{{ transfer.description }}</p>
<p class="time">{{ formatTime(transfer.created_at) }}</p>
<p class="type">类型{{ getTransferTypeText(transfer.transfer_type) }}</p>
<div v-if="transfer.voucher_url" class="voucher">
<el-image
:src="formatImageUrl(transfer.voucher_url)"
:preview-src-list="[formatImageUrl(transfer.voucher_url)]"
class="voucher-image"
fit="cover"
>
<template #error>
<div class="image-slot">
<el-icon><Picture /></el-icon>
</div>
</template>
</el-image>
</div>
<!-- 确认收款操作按钮 -->
<div v-if="transfer.status === 'confirmed' && transfer.to_user_id === userStore.user.id" class="transfer-actions">
<el-button
type="success"
size="small"
@click="confirmReceived(transfer.id)"
:loading="confirmLoading"
>
确认收款
</el-button>
<el-button
type="warning"
size="small"
@click="confirmNotReceived(transfer.id)"
:loading="confirmLoading"
>
未收到款
</el-button>
</div>
</div>
</div>
</div>
<div v-if="transferHistory.length === 0" class="empty-state">
<el-empty description="暂无转账记录" />
</div>
</div>
<!-- 分页 -->
<div class="pagination" v-if="pagination.total > 0">
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.limit"
:page-sizes="[10, 20, 50]"
:total="pagination.total"
:layout="paginationLayout"
:small="isMobile"
@size-change="loadTransferHistory"
@current-change="loadTransferHistory"
/>
</div>
</el-card>
</div>
<!-- 发起转账对话框 -->
<el-dialog
v-model="showCreateTransfer"
title="发起转账"
width="90%"
:style="{ maxWidth: '500px' }"
@close="resetTransferForm"
>
<el-form
ref="transferFormRef"
:model="transferForm"
:rules="transferRules"
label-width="100px"
>
<el-form-item label="收款用户" prop="to_user_id">
<el-select
v-model="transferForm.to_user_id"
placeholder="请选择收款用户"
filterable
style="width: 100%"
>
<el-option
v-for="user in userList"
:key="user.id"
:label="`${user.real_name || user.username} (${user.username})`"
:value="user.id"
/>
</el-select>
</el-form-item>
<el-form-item label="转账金额" prop="amount">
<el-input
v-model="transferForm.amount"
placeholder="请输入转账金额"
type="number"
step="0.01"
>
<template #prepend>¥</template>
</el-input>
</el-form-item>
<el-form-item label="转账类型" prop="transfer_type">
<el-select v-model="transferForm.transfer_type" placeholder="请选择转账类型">
<el-option label="普通转账" value="user_to_user" />
<el-option label="初始转账" value="initial" />
<el-option label="返还转账" value="return" />
</el-select>
</el-form-item>
<el-form-item label="转账说明" prop="description">
<el-input
v-model="transferForm.description"
type="textarea"
placeholder="请输入转账说明"
:rows="3"
/>
</el-form-item>
<el-form-item label="转账凭证">
<el-upload
ref="uploadRef"
:action="uploadUrl"
:headers="uploadHeaders"
:before-upload="beforeUpload"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:show-file-list="false"
accept="image/*"
>
<el-button size="small" type="primary">上传凭证</el-button>
</el-upload>
<div v-if="transferForm.voucher" class="upload-preview">
<img :src="formatImageUrl(transferForm.voucher)" alt="转账凭证" />
</div>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showCreateTransfer = false">取消</el-button>
<el-button
type="primary"
@click="submitTransfer"
:loading="submitLoading"
>
提交转账
</el-button>
</span>
</template>
</el-dialog>
<!-- 转账凭证查看对话框 -->
<el-dialog
v-model="showVoucherDialog"
title="转账凭证"
width="90%"
:style="{ maxWidth: '600px' }"
>
<div class="voucher-dialog-content">
<div class="transfer-info">
<h4>转账信息</h4>
<div class="info-row">
<span class="label">转账金额</span>
<span class="value amount">¥{{ currentTransfer.amount }}</span>
</div>
<div class="info-row">
<span class="label">转账说明</span>
<span class="value">{{ currentTransfer.description }}</span>
</div>
<div class="info-row">
<span class="label">转账时间</span>
<span class="value">{{ formatTime(currentTransfer.created_at) }}</span>
</div>
<div class="info-row">
<span class="label">转账类型</span>
<span class="value">{{ getTransferTypeText(currentTransfer.transfer_type) }}</span>
</div>
</div>
<div class="voucher-section" v-if="currentTransfer.voucher_url">
<h4>转账凭证</h4>
<div class="voucher-image-container">
<el-image
:src="formatImageUrl(currentTransfer.voucher_url)"
:preview-src-list="[formatImageUrl(currentTransfer.voucher_url)]"
class="voucher-preview"
fit="contain"
>
<template #error>
<div class="image-slot">
<el-icon><Picture /></el-icon>
<p>凭证加载失败</p>
</div>
</template>
</el-image>
</div>
</div>
<div v-else class="no-voucher">
<el-icon><Picture /></el-icon>
<p>暂无转账凭证</p>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showVoucherDialog = false">取消</el-button>
<el-button
type="success"
@click="doConfirmReceived"
:loading="confirmLoading"
>
确认收款
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted, watch, computed } from 'vue'
import { useRouter } from 'vue-router'
import { useUserStore } from '@/stores/user'
import { ElMessage, ElMessageBox } from 'element-plus'
import { ArrowLeft, Plus, Right, Picture, Warning } from '@element-plus/icons-vue'
import api, { transferAPI } from '@/utils/api'
import { uploadURL, getImageUrl, getUploadConfig } from '@/config'
const router = useRouter()
const userStore = useUserStore()
// 响应式数据
const loading = ref(false)
const submitLoading = ref(false)
const confirmLoading = ref(false)
const showCreateTransfer = ref(false)
const showVoucherDialog = ref(false)
const statusFilter = ref('')
const currentTransfer = ref({})
const accountInfo = ref({})
const pendingTransfers = ref([])
const pendingAllocations = ref([])
const transferHistory = ref([])
const userList = ref([])
const pagination = reactive({
page: 1,
limit: 10,
total: 0
})
const transferForm = reactive({
to_user_id: '',
amount: '',
transfer_type: 'user_to_user',
description: '',
voucher: ''
})
const transferFormRef = ref()
const uploadRef = ref()
// 移动端检测和分页布局
const windowWidth = ref(window.innerWidth)
const isMobile = computed(() => {
return windowWidth.value <= 768
})
const paginationLayout = computed(() => {
if (windowWidth.value <= 480) {
return 'prev, pager, next'
} else if (windowWidth.value <= 768) {
return 'total, prev, pager, next'
} else {
return 'total, sizes, prev, pager, next, jumper'
}
})
// 窗口大小变化监听
const handleResize = () => {
windowWidth.value = window.innerWidth
}
// 上传配置
const uploadUrl = ref(uploadURL)
const uploadHeaders = computed(() => getUploadConfig().headers)
// 表单验证规则
const transferRules = {
to_user_id: [
{ required: true, message: '请选择收款用户', trigger: 'change' }
],
amount: [
{ required: true, message: '请输入转账金额', trigger: 'blur' },
{ pattern: /^\d+(\.\d{1,2})?$/, message: '请输入有效的金额', trigger: 'blur' }
],
transfer_type: [
{ required: true, message: '请选择转账类型', trigger: 'change' }
]
}
// 生命周期
onMounted(() => {
loadAccountInfo()
loadPendingTransfers()
loadPendingAllocations()
loadTransferHistory()
loadUserList()
// 添加窗口大小变化监听
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
// 移除窗口大小变化监听
window.removeEventListener('resize', handleResize)
})
// 监听状态筛选
watch(statusFilter, () => {
pagination.page = 1
loadTransferHistory()
})
// 方法
const loadAccountInfo = async () => {
try {
const response = await api.get(`/transfers/account/${userStore.user.id}`)
if (response.data.success) {
accountInfo.value = response.data.data
}
} catch (error) {
console.error('加载账户信息失败:', error)
}
}
const loadPendingTransfers = async () => {
try {
const response = await api.get('/transfers/pending')
if (response.data.success) {
pendingTransfers.value = response.data.data
}
} catch (error) {
console.error('加载待确认转账失败:', error)
}
}
/**
* 加载用户待处理的分配
*/
const loadPendingAllocations = async () => {
try {
const response = await api.get('/matching/pending-allocations')
if (response.data.success) {
pendingAllocations.value = response.data.data || []
}
} catch (error) {
console.error('加载待处理分配失败:', error)
ElMessage.error('加载待处理分配失败')
}
}
const loadTransferHistory = async () => {
try {
loading.value = true
const params = {
page: pagination.page,
limit: pagination.limit
}
if (statusFilter.value) {
params.status = statusFilter.value
}
const response = await api.get(`/transfers/user/${userStore.user.id}`, { params })
if (response.data.success) {
transferHistory.value = response.data.data.transfers
Object.assign(pagination, response.data.data.pagination)
}
} catch (error) {
console.error('加载转账记录失败:', error)
ElMessage.error('加载转账记录失败')
} finally {
loading.value = false
}
}
const loadUserList = async () => {
try {
const response = await api.get('/users/for-transfer')
if (response.data.success) {
userList.value = response.data.data
}
} catch (error) {
console.error('加载用户列表失败:', error)
ElMessage.error('加载用户列表失败')
}
}
/**
* 获取订单状态对应的标签类型
*/
const getOrderStatusType = (status) => {
const statusMap = {
'matching': 'warning',
'completed': 'success',
'failed': 'danger',
'cancelled': 'info'
}
return statusMap[status] || 'info'
}
/**
* 获取订单状态文本
*/
const getOrderStatusText = (status) => {
const statusMap = {
'matching': '进行中',
'completed': '已完成',
'failed': '已失败',
'cancelled': '已取消'
}
return statusMap[status] || '未知状态'
}
/**
* 获取转账类型文本
*/
const getTransferTypeText = (type) => {
const typeMap = {
'user_to_user': '用户转账',
'initial': '初始转账',
'return': '返还转账',
'system_to_user': '系统转账',
'user_to_system': '系统回收'
}
return typeMap[type] || '未知类型'
}
const confirmTransfer = async (transferId, action) => {
try {
const actionText = action === 'confirm' ? '确认' : '拒绝'
await ElMessageBox.confirm(
`确定要${actionText}这笔转账吗?`,
'确认操作',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
confirmLoading.value = true
const response = await api.post(`/transfers/confirm/${transferId}`, { action })
if (response.data.success) {
ElMessage.success(response.data.message)
loadPendingTransfers()
loadTransferHistory()
loadAccountInfo()
}
} catch (error) {
if (error !== 'cancel') {
console.error('确认转账失败:', error)
ElMessage.error(error.response?.data?.message || '操作失败')
}
} finally {
confirmLoading.value = false
}
}
const submitTransfer = async () => {
try {
const valid = await transferFormRef.value.validate()
if (!valid) return
submitLoading.value = true
const formData = new FormData()
formData.append('to_user_id', transferForm.to_user_id)
formData.append('amount', transferForm.amount)
formData.append('transfer_type', transferForm.transfer_type)
formData.append('description', transferForm.description)
if (transferForm.voucher) {
// 如果有上传的凭证,需要处理文件
const fileInput = uploadRef.value?.$el.querySelector('input[type="file"]')
if (fileInput?.files[0]) {
formData.append('voucher', fileInput.files[0])
}
}
const response = await api.post('/transfers/create', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
if (response.data.success) {
ElMessage.success(response.data.message)
showCreateTransfer.value = false
resetTransferForm()
loadTransferHistory()
}
} catch (error) {
console.error('提交转账失败:', error)
ElMessage.error(error.response?.data?.message || '提交失败')
} finally {
submitLoading.value = false
}
}
const resetTransferForm = () => {
Object.assign(transferForm, {
to_user_id: '',
amount: '',
transfer_type: 'user_to_user',
description: '',
voucher: ''
})
transferFormRef.value?.resetFields()
}
// 文件上传处理
const beforeUpload = (file) => {
const isImage = file.type.startsWith('image/')
const isLt5M = file.size / 1024 / 1024 < 5
if (!isImage) {
ElMessage.error('只能上传图片文件!')
return false
}
if (!isLt5M) {
ElMessage.error('图片大小不能超过 5MB!')
return false
}
return true
}
/**
* 处理文件上传成功回调
* @param {Object} response - 上传接口返回的响应数据
*/
const handleUploadSuccess = (response) => {
if (response.success) {
// 使用工具函数将相对路径转换为完整的服务器地址
transferForm.voucher = formatImageUrl(response.url)
ElMessage.success('上传成功')
} else {
ElMessage.error(response.message || '上传失败')
}
}
const handleUploadError = (error) => {
console.error('上传错误:', error)
ElMessage.error('上传失败,请重试')
}
// 工具方法
/**
* 格式化时间显示
* @param {string} time - 时间字符串
* @returns {string} 格式化后的时间
*/
const formatTime = (time) => {
return new Date(time).toLocaleString('zh-CN')
}
/**
* 格式化图片URL
* @param {string} url - 相对路径或完整URL
* @returns {string} 处理后的图片URL
*/
const formatImageUrl = (url) => {
return getImageUrl(url)
}
const getStatusType = (status) => {
const types = {
pending: 'warning',
confirmed: 'success',
rejected: 'danger',
received: 'success',
not_received: 'danger'
}
return types[status] || 'info'
}
const getStatusText = (status) => {
const texts = {
pending: '待确认',
confirmed: '已确认',
rejected: '已拒绝',
received: '已收款',
not_received: '未收到款'
}
return texts[status] || '未知'
}
/**
* 显示转账凭证并确认收款
* @param {number} transferId - 转账ID
*/
const confirmReceived = async (transferId) => {
try {
// 找到对应的转账记录
const transfer = transferHistory.value.find(t => t.id === transferId)
if (!transfer) {
ElMessage.error('转账记录不存在')
return
}
// 设置当前转账信息并显示凭证对话框
currentTransfer.value = transfer
showVoucherDialog.value = true
} catch (error) {
console.error('显示转账凭证失败:', error)
ElMessage.error('操作失败')
}
}
/**
* 执行确认收款操作
*/
const doConfirmReceived = async () => {
try {
await ElMessageBox.confirm(
'确定已收到款项吗?',
'确认收款',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
confirmLoading.value = true
const response = await transferAPI.confirmReceived(currentTransfer.value.id)
if (response.data.success) {
ElMessage.success(response.data.message)
showVoucherDialog.value = false
loadTransferHistory()
loadAccountInfo()
}
} catch (error) {
if (error !== 'cancel') {
console.error('确认收款失败:', error)
ElMessage.error(error.response?.data?.message || '操作失败')
}
} finally {
confirmLoading.value = false
}
}
const confirmNotReceived = async (transferId) => {
try {
await ElMessageBox.confirm(
'确定未收到款项吗?',
'确认未收款',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
confirmLoading.value = true
const response = await transferAPI.confirmNotReceived(transferId)
if (response.data.success) {
ElMessage.success(response.data.message)
loadTransferHistory()
loadAccountInfo()
}
} catch (error) {
if (error !== 'cancel') {
console.error('确认未收款失败:', error)
ElMessage.error(error.response?.data?.message || '操作失败')
}
} finally {
confirmLoading.value = false
}
}
</script>
<style lang="scss" scoped>
.transfers-page {
min-height: 100vh;
background: #f5f7fa;
}
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
height: 60px;
background: white;
border-bottom: 1px solid #ebeef5;
position: sticky;
top: 0;
z-index: 100;
}
.nav-title {
margin: 0;
font-size: 18px;
font-weight: 500;
}
.back-btn {
color: #409eff;
}
.account-info {
margin: 20px;
}
.account-balance {
display: flex;
gap: 30px;
}
.balance-item {
display: flex;
align-items: center;
gap: 8px;
}
.balance-item .label {
color: #666;
}
.balance-item .amount {
font-size: 24px;
font-weight: bold;
color: #409eff;
}
.balance-item .type {
color: #909399;
}
.pending-transfers,
.transfer-history {
margin: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.badge {
margin-left: 10px;
}
.transfer-list {
max-height: 400px;
overflow-y: auto;
}
.transfer-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
border: 1px solid #ebeef5;
border-radius: 8px;
margin-bottom: 10px;
background: white;
overflow: hidden;
word-wrap: break-word;
}
.transfer-item.pending {
border-color: #e6a23c;
background: #fdf6ec;
}
.transfer-item.confirmed {
border-color: #67c23a;
background: #f0f9ff;
}
.transfer-item.rejected {
border-color: #f56c6c;
background: #fef0f0;
}
.transfer-info {
flex: 1;
}
.transfer-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 8px;
}
.from-user,
.to-user {
font-weight: 500;
}
.arrow {
color: #909399;
}
.amount {
font-size: 18px;
font-weight: bold;
color: #409eff;
margin-left: auto;
}
.transfer-details {
color: #666;
font-size: 14px;
}
.transfer-details p {
margin: 4px 0;
}
.voucher {
margin-top: 10px;
}
.voucher-image {
width: 100px;
height: 100px;
border-radius: 4px;
}
.image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: #f5f7fa;
color: #909399;
}
.transfer-actions {
display: flex;
gap: 10px;
}
/* 待转账相关样式 */
.timeout-item {
border-left: 3px solid #f56c6c;
background-color: #fef0f0;
}
.deadline-info {
display: flex;
align-items: center;
gap: 8px;
margin: 8px 0;
font-size: 13px;
}
.deadline-label {
color: #606266;
font-weight: 500;
}
.deadline-time {
color: #303133;
font-weight: 500;
}
.deadline-time.urgent {
color: #e6a23c;
}
.deadline-time.expired {
color: #f56c6c;
}
.time-left {
font-size: 12px;
}
.time-left.normal {
color: #67c23a;
}
.time-left.urgent {
color: #e6a23c;
font-weight: 500;
}
.time-left.expired {
color: #f56c6c;
font-weight: 500;
}
.timeout-warning {
display: flex;
align-items: center;
gap: 8px;
margin-top: 10px;
padding: 8px 12px;
background-color: #fef0f0;
border: 1px solid #fbc4c4;
border-radius: 4px;
color: #f56c6c;
}
.timeout-warning .el-icon {
font-size: 16px;
color: #f56c6c;
}
.warning-text {
font-size: 12px;
font-weight: 500;
}
/* 订单状态标签样式优化 */
.transfer-header .el-tag {
margin-left: 8px;
font-size: 11px;
height: 20px;
line-height: 18px;
padding: 0 6px;
}
.empty-state {
text-align: center;
padding: 40px;
}
.pagination {
margin-top: 20px;
text-align: center;
}
.upload-preview {
margin-top: 10px;
}
.upload-preview img {
width: 100px;
height: 100px;
object-fit: cover;
border-radius: 4px;
border: 1px solid #dcdfe6;
}
.dialog-footer {
text-align: right;
}
/* 转账凭证对话框样式 */
.voucher-dialog-content {
.transfer-info {
margin-bottom: 20px;
padding: 16px;
background-color: #f8f9fa;
border-radius: 8px;
h4 {
margin: 0 0 12px 0;
color: #303133;
font-size: 16px;
font-weight: 600;
}
.info-row {
display: flex;
margin-bottom: 8px;
.label {
min-width: 80px;
color: #606266;
font-weight: 500;
}
.value {
color: #303133;
flex: 1;
&.amount {
color: #e6a23c;
font-weight: 600;
font-size: 16px;
}
}
}
}
.voucher-section {
h4 {
margin: 0 0 12px 0;
color: #303133;
font-size: 16px;
font-weight: 600;
}
.voucher-image-container {
display: flex;
justify-content: center;
padding: 20px;
background-color: #f8f9fa;
border-radius: 8px;
border: 2px dashed #dcdfe6;
.voucher-preview {
max-width: 100%;
max-height: 400px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
}
}
.no-voucher {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 20px;
background-color: #f8f9fa;
border-radius: 8px;
border: 2px dashed #dcdfe6;
color: #909399;
.el-icon {
font-size: 48px;
margin-bottom: 12px;
}
p {
margin: 0;
font-size: 14px;
}
}
.image-slot {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 200px;
color: #909399;
.el-icon {
font-size: 48px;
margin-bottom: 8px;
}
p {
margin: 0;
font-size: 14px;
}
}
}
/* 移动端适配增强 */
@media (max-width: 768px) {
.deadline-info {
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
.timeout-warning {
padding: 6px 10px;
}
.warning-text {
font-size: 11px;
}
.transfer-header .el-tag {
margin-left: 0;
margin-top: 4px;
}
.navbar {
padding: 0 15px;
}
.account-info,
.pending-transfers,
.transfer-history {
margin: 15px;
}
.account-balance {
flex-direction: column;
gap: 15px;
}
.balance-item .amount {
font-size: 20px;
}
.transfer-item {
flex-direction: column;
align-items: flex-start;
gap: 12px;
padding: 12px;
}
.transfer-info {
width: 100%;
}
.transfer-header {
flex-wrap: wrap;
gap: 8px;
}
.amount {
font-size: 16px;
margin-left: 0;
margin-top: 8px;
}
.transfer-actions {
width: 100%;
justify-content: flex-end;
flex-wrap: wrap;
}
.transfer-details p {
font-size: 13px;
}
.voucher-image {
width: 80px;
height: 80px;
}
/* 分页组件移动端适配 */
.pagination {
margin: 15px 0;
overflow-x: auto;
padding: 0 5px;
}
.pagination :deep(.el-pagination) {
justify-content: center;
flex-wrap: wrap;
gap: 8px;
}
.pagination :deep(.el-pagination__total),
.pagination :deep(.el-pagination__sizes),
.pagination :deep(.el-pagination__jump) {
margin: 4px 0;
}
.pagination :deep(.el-pager) {
flex-wrap: wrap;
justify-content: center;
}
.pagination :deep(.el-pager .number),
.pagination :deep(.btn-prev),
.pagination :deep(.btn-next) {
min-width: 32px;
height: 32px;
font-size: 14px;
margin: 2px;
}
}
@media (max-width: 480px) {
.deadline-info {
font-size: 12px;
}
.timeout-warning {
padding: 4px 8px;
}
.warning-text {
font-size: 10px;
}
.navbar {
padding: 0 10px;
}
.account-info,
.pending-transfers,
.transfer-history {
margin: 10px;
}
.transfer-item {
padding: 10px;
}
.balance-item .amount {
font-size: 18px;
}
.transfer-actions {
gap: 8px;
}
.transfer-actions .el-button {
font-size: 12px;
padding: 6px 12px;
}
.voucher-image {
width: 60px;
height: 60px;
}
/* 小屏幕分页优化 */
.pagination {
margin: 10px 0;
padding: 0;
}
.pagination :deep(.el-pagination) {
gap: 4px;
}
.pagination :deep(.el-pagination__total) {
font-size: 12px;
order: 1;
width: 100%;
text-align: center;
margin-bottom: 8px;
}
.pagination :deep(.el-pagination__sizes) {
order: 2;
margin: 4px;
}
.pagination :deep(.el-pagination__sizes .el-select) {
width: 80px;
}
.pagination :deep(.el-pagination__jump) {
order: 4;
margin: 4px;
font-size: 12px;
}
.pagination :deep(.el-pagination__jump .el-input) {
width: 50px;
}
.pagination :deep(.el-pager) {
order: 3;
margin: 4px 0;
}
.pagination :deep(.el-pager .number),
.pagination :deep(.btn-prev),
.pagination :deep(.btn-next) {
min-width: 28px;
height: 28px;
font-size: 12px;
margin: 1px;
}
/* 隐藏跳转功能以节省空间 */
.pagination :deep(.el-pagination__jump) {
display: none;
}
}
</style>