1583 lines
36 KiB
Plaintext
1583 lines
36 KiB
Plaintext
<template>
|
||
<AdminLayout current-page="finance">
|
||
<view class="finance-management">
|
||
<!-- 页面标题 -->
|
||
<view class="page-header">
|
||
<text class="page-title">财务管理</text>
|
||
<text class="page-subtitle">管理系统财务数据和报表</text>
|
||
</view>
|
||
|
||
<!-- Tab 切换栏 -->
|
||
<view class="tab-bar">
|
||
<view
|
||
v-for="tab in tabs"
|
||
:key="tab.key"
|
||
class="tab-item"
|
||
:class="{ 'active': activeTab === tab.key }"
|
||
@click="switchTab(tab.key)"
|
||
>
|
||
<text class="iconfont tab-icon">{{ tab.icon }}</text>
|
||
<text class="tab-title">{{ tab.title }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 财务概览Tab -->
|
||
<view v-if="activeTab === 'finance-overview'">
|
||
<!-- 财务概览 -->
|
||
<view class="finance-overview">
|
||
<view class="overview-cards">
|
||
<view class="overview-card">
|
||
<view class="card-icon">💰</view>
|
||
<view class="card-content">
|
||
<text class="card-value">¥{{ totalRevenue }}</text>
|
||
<text class="card-label">总收入</text>
|
||
</view>
|
||
</view>
|
||
<view class="overview-card">
|
||
<view class="card-icon">📈</view>
|
||
<view class="card-content">
|
||
<text class="card-value">¥{{ monthlyRevenue }}</text>
|
||
<text class="card-label">本月收入</text>
|
||
</view>
|
||
</view>
|
||
<view class="overview-card">
|
||
<view class="card-icon">📊</view>
|
||
<view class="card-content">
|
||
<text class="card-value">{{ orderCount }}</text>
|
||
<text class="card-label">订单数量</text>
|
||
</view>
|
||
</view>
|
||
<view class="overview-card">
|
||
<view class="card-icon">💳</view>
|
||
<view class="card-content">
|
||
<text class="card-value">{{ pendingWithdrawals }}</text>
|
||
<text class="card-label">待提现</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 收入趋势图表 -->
|
||
<view class="chart-section">
|
||
<view class="chart-header">
|
||
<text class="chart-title">收入趋势</text>
|
||
<view class="chart-controls">
|
||
<button
|
||
class="period-btn"
|
||
:class="{ 'active': chartPeriod === 'week' }"
|
||
@click="setChartPeriod('week')"
|
||
>
|
||
本周
|
||
</button>
|
||
<button
|
||
class="period-btn"
|
||
:class="{ 'active': chartPeriod === 'month' }"
|
||
@click="setChartPeriod('month')"
|
||
>
|
||
本月
|
||
</button>
|
||
<button
|
||
class="period-btn"
|
||
:class="{ 'active': chartPeriod === 'year' }"
|
||
@click="setChartPeriod('year')"
|
||
>
|
||
本年
|
||
</button>
|
||
</view>
|
||
</view>
|
||
<view class="chart-placeholder">
|
||
<text class="chart-placeholder-text">📊 图表区域 - 收入趋势可视化</text>
|
||
<text class="chart-placeholder-subtext">集成图表库后可显示实际数据</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 财务操作 -->
|
||
<view class="finance-actions">
|
||
<view class="action-section">
|
||
<text class="section-title">财务操作</text>
|
||
<view class="action-buttons">
|
||
<button class="action-btn primary" @click="showWithdrawalModal = true">
|
||
<text class="btn-icon">💸</text>
|
||
<text class="btn-text">申请提现</text>
|
||
</button>
|
||
<button class="action-btn secondary" @click="showTransferModal = true">
|
||
<text class="btn-icon">🔄</text>
|
||
<text class="btn-text">转账</text>
|
||
</button>
|
||
<button class="action-btn info" @click="showRechargeModal = true">
|
||
<text class="btn-icon">💳</text>
|
||
<text class="btn-text">充值</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="action-section">
|
||
<text class="section-title">财务报表</text>
|
||
<view class="action-buttons">
|
||
<button class="action-btn success" @click="generateReport('daily')">
|
||
<text class="btn-icon">📅</text>
|
||
<text class="btn-text">日报表</text>
|
||
</button>
|
||
<button class="action-btn warning" @click="generateReport('monthly')">
|
||
<text class="btn-icon">📊</text>
|
||
<text class="btn-text">月报表</text>
|
||
</button>
|
||
<button class="action-btn danger" @click="generateReport('yearly')">
|
||
<text class="btn-icon">📈</text>
|
||
<text class="btn-text">年报表</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 交易记录 -->
|
||
<view class="transaction-section">
|
||
<view class="section-header">
|
||
<text class="section-title">交易记录</text>
|
||
<view class="section-controls">
|
||
<input
|
||
v-model="transactionSearch"
|
||
class="search-input"
|
||
placeholder="搜索交易..."
|
||
/>
|
||
<picker
|
||
mode="selector"
|
||
:range="transactionTypes"
|
||
:value="selectedTransactionType"
|
||
@change="handleTransactionTypeChange"
|
||
>
|
||
<view class="type-selector">
|
||
<text>{{ transactionTypes[selectedTransactionType] }}</text>
|
||
<text class="iconfont icon-down"></text>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="transaction-table">
|
||
<view class="table-header">
|
||
<view class="table-row">
|
||
<view class="col-time">时间</view>
|
||
<view class="col-type">类型</view>
|
||
<view class="col-description">描述</view>
|
||
<view class="col-amount">金额</view>
|
||
<view class="col-status">状态</view>
|
||
<view class="col-actions">操作</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="table-body">
|
||
<view
|
||
v-for="transaction in filteredTransactions"
|
||
:key="transaction.id"
|
||
class="table-row"
|
||
>
|
||
<view class="col-time">
|
||
<text class="transaction-time">{{ formatDateTime(transaction.time) }}</text>
|
||
</view>
|
||
<view class="col-type">
|
||
<view class="type-badge" :class="transaction.type">
|
||
{{ transaction.type === 'income' ? '收入' :
|
||
transaction.type === 'expense' ? '支出' :
|
||
transaction.type === 'withdrawal' ? '提现' :
|
||
transaction.type === 'recharge' ? '充值' : '转账' }}
|
||
</view>
|
||
</view>
|
||
<view class="col-description">
|
||
<text class="transaction-desc">{{ transaction.description }}</text>
|
||
<text class="transaction-ref" v-if="transaction.reference">
|
||
{{ transaction.reference }}
|
||
</text>
|
||
</view>
|
||
<view class="col-amount">
|
||
<text
|
||
class="transaction-amount"
|
||
:class="{ 'income': transaction.type === 'income' || transaction.type === 'recharge',
|
||
'expense': transaction.type === 'expense' || transaction.type === 'withdrawal' }"
|
||
>
|
||
{{ transaction.type === 'income' || transaction.type === 'recharge' ? '+' : '-' }}¥{{ transaction.amount }}
|
||
</text>
|
||
</view>
|
||
<view class="col-status">
|
||
<view class="status-badge" :class="transaction.status">
|
||
{{ transaction.status === 'completed' ? '已完成' :
|
||
transaction.status === 'pending' ? '处理中' :
|
||
transaction.status === 'failed' ? '失败' : '已取消' }}
|
||
</view>
|
||
</view>
|
||
<view class="col-actions">
|
||
<button class="action-btn view" @click="viewTransactionDetail(transaction)">
|
||
<text class="iconfont icon-view"></text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 分页 -->
|
||
<view class="pagination">
|
||
<view class="page-info">
|
||
<text>共 {{ totalTransactions }} 条记录,显示 {{ (currentPage - 1) * pageSize + 1 }}-{{ Math.min(currentPage * pageSize, totalTransactions) }} 条</text>
|
||
</view>
|
||
<view class="page-controls">
|
||
<button
|
||
class="page-btn"
|
||
:disabled="currentPage === 1"
|
||
@click="goToPage(currentPage - 1)"
|
||
>
|
||
上一页
|
||
</button>
|
||
|
||
<view class="page-numbers">
|
||
<button
|
||
v-for="page in visiblePages"
|
||
:key="page"
|
||
class="page-number"
|
||
:class="{ 'active': page === currentPage }"
|
||
@click="goToPage(page)"
|
||
>
|
||
{{ page }}
|
||
</button>
|
||
</view>
|
||
|
||
<button
|
||
class="page-btn"
|
||
:disabled="currentPage === totalPages"
|
||
@click="goToPage(currentPage + 1)"
|
||
>
|
||
下一页
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 提现模态框 -->
|
||
<view v-if="showWithdrawalModal" class="modal-overlay" @click="closeModal">
|
||
<view class="modal-content" @click.stop>
|
||
<view class="modal-header">
|
||
<text class="modal-title">申请提现</text>
|
||
<view class="modal-close" @click="closeModal">
|
||
<text class="iconfont icon-close"></text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="modal-body">
|
||
<view class="withdrawal-info">
|
||
<view class="info-item">
|
||
<text class="info-label">可用余额:</text>
|
||
<text class="info-value">¥{{ availableBalance }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="info-label">最低提现金额:</text>
|
||
<text class="info-value">¥100.00</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">提现金额</text>
|
||
<input
|
||
v-model="withdrawalAmount"
|
||
class="form-input"
|
||
placeholder="请输入提现金额"
|
||
type="number"
|
||
/>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">收款账户</text>
|
||
<picker
|
||
mode="selector"
|
||
:range="accountOptions"
|
||
:value="selectedAccount"
|
||
@change="handleAccountChange"
|
||
>
|
||
<view class="form-select">
|
||
<text>{{ accountOptions[selectedAccount] }}</text>
|
||
<text class="iconfont icon-down"></text>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">备注</text>
|
||
<input
|
||
v-model="withdrawalNote"
|
||
class="form-input"
|
||
placeholder="可选"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="modal-footer">
|
||
<button class="btn-secondary" @click="closeModal">取消</button>
|
||
<button class="btn-primary" @click="submitWithdrawal">
|
||
提交申请
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 转账模态框 -->
|
||
<view v-if="showTransferModal" class="modal-overlay" @click="closeModal">
|
||
<view class="modal-content" @click.stop>
|
||
<view class="modal-header">
|
||
<text class="modal-title">转账</text>
|
||
<view class="modal-close" @click="closeModal">
|
||
<text class="iconfont icon-close"></text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="modal-body">
|
||
<view class="form-group">
|
||
<text class="form-label">收款账户</text>
|
||
<input
|
||
v-model="transferToAccount"
|
||
class="form-input"
|
||
placeholder="请输入收款账户"
|
||
/>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">转账金额</text>
|
||
<input
|
||
v-model="transferAmount"
|
||
class="form-input"
|
||
placeholder="请输入转账金额"
|
||
type="number"
|
||
/>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">备注</text>
|
||
<input
|
||
v-model="transferNote"
|
||
class="form-input"
|
||
placeholder="可选"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="modal-footer">
|
||
<button class="btn-secondary" @click="closeModal">取消</button>
|
||
<button class="btn-primary" @click="submitTransfer">
|
||
确认转账
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 充值模态框 -->
|
||
<view v-if="showRechargeModal" class="modal-overlay" @click="closeModal">
|
||
<view class="modal-content" @click.stop>
|
||
<view class="modal-header">
|
||
<text class="modal-title">充值</text>
|
||
<view class="modal-close" @click="closeModal">
|
||
<text class="iconfont icon-close"></text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="modal-body">
|
||
<view class="form-group">
|
||
<text class="form-label">充值金额</text>
|
||
<input
|
||
v-model="rechargeAmount"
|
||
class="form-input"
|
||
placeholder="请输入充值金额"
|
||
type="number"
|
||
/>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">支付方式</text>
|
||
<picker
|
||
mode="selector"
|
||
:range="paymentOptions"
|
||
:value="selectedPayment"
|
||
@change="handlePaymentChange"
|
||
>
|
||
<view class="form-select">
|
||
<text>{{ paymentOptions[selectedPayment] }}</text>
|
||
<text class="iconfont icon-down"></text>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
|
||
<view class="form-group">
|
||
<text class="form-label">备注</text>
|
||
<input
|
||
v-model="rechargeNote"
|
||
class="form-input"
|
||
placeholder="可选"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="modal-footer">
|
||
<button class="btn-secondary" @click="closeModal">取消</button>
|
||
<button class="btn-primary" @click="submitRecharge">
|
||
确认充值
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 提现管理Tab -->
|
||
<view v-if="activeTab === 'withdrawals'" class="withdrawals-section">
|
||
<view class="withdrawals-management">
|
||
<view class="section-header">
|
||
<text class="section-title">提现管理</text>
|
||
<text class="section-desc">管理用户提现申请</text>
|
||
</view>
|
||
|
||
<view class="withdrawals-list">
|
||
<view class="withdrawal-item" v-for="withdrawal in withdrawalRequests" :key="withdrawal.id">
|
||
<view class="withdrawal-info">
|
||
<text class="user-name">{{ withdrawal.userName }}</text>
|
||
<text class="amount">¥{{ withdrawal.amount }}</text>
|
||
<text class="time">{{ formatDate(withdrawal.time) }}</text>
|
||
</view>
|
||
<view class="withdrawal-actions">
|
||
<button class="btn-success" @click="approveWithdrawal(withdrawal)">批准</button>
|
||
<button class="btn-danger" @click="rejectWithdrawal(withdrawal)">拒绝</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</AdminLayout>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { ref, computed } from 'vue'
|
||
import AdminLayout from '@/layouts/admin/index.uvue'
|
||
|
||
// Tab 相关
|
||
const activeTab = ref('finance-overview')
|
||
const tabs = ref([
|
||
{ key: 'finance-overview', title: '财务概览', icon: 'icon-overview' },
|
||
{ key: 'withdrawals', title: '提现管理', icon: 'icon-withdraw' }
|
||
])
|
||
|
||
// 响应式数据
|
||
const totalRevenue = ref(256890)
|
||
const monthlyRevenue = ref(45678)
|
||
const orderCount = ref(1247)
|
||
const pendingWithdrawals = ref(5)
|
||
const availableBalance = ref(125690.50)
|
||
const chartPeriod = ref('month')
|
||
|
||
// 交易记录相关
|
||
const transactionSearch = ref('')
|
||
const selectedTransactionType = ref(0)
|
||
const transactionTypes = ['全部类型', '收入', '支出', '提现', '充值', '转账']
|
||
|
||
const transactions = ref([
|
||
{
|
||
id: 1,
|
||
time: '2024-01-15T14:30:00Z',
|
||
type: 'income',
|
||
description: '商品销售收入',
|
||
reference: '订单 #DD202401150001',
|
||
amount: 2599.00,
|
||
status: 'completed'
|
||
},
|
||
{
|
||
id: 2,
|
||
time: '2024-01-15T10:15:00Z',
|
||
type: 'expense',
|
||
description: '供应商货款',
|
||
reference: '采购单 #CG20240115001',
|
||
amount: 1500.00,
|
||
status: 'completed'
|
||
},
|
||
{
|
||
id: 3,
|
||
time: '2024-01-14T16:45:00Z',
|
||
type: 'withdrawal',
|
||
description: '账户提现',
|
||
reference: '提现单 #TX20240114001',
|
||
amount: 5000.00,
|
||
status: 'pending'
|
||
},
|
||
{
|
||
id: 4,
|
||
time: '2024-01-14T09:20:00Z',
|
||
type: 'recharge',
|
||
description: '账户充值',
|
||
reference: '充值单 #CZ20240114001',
|
||
amount: 10000.00,
|
||
status: 'completed'
|
||
},
|
||
{
|
||
id: 5,
|
||
time: '2024-01-13T11:30:00Z',
|
||
type: 'transfer',
|
||
description: '转账支出',
|
||
reference: '转账单 #ZZ20240113001',
|
||
amount: 2000.00,
|
||
status: 'completed'
|
||
}
|
||
])
|
||
|
||
// 计算属性
|
||
const filteredTransactions = computed(() => {
|
||
let filtered = transactions.value
|
||
|
||
// 搜索过滤
|
||
if (transactionSearch.value) {
|
||
filtered = filtered.filter(t =>
|
||
t.description.toLowerCase().includes(transactionSearch.value.toLowerCase()) ||
|
||
(t.reference && t.reference.toLowerCase().includes(transactionSearch.value.toLowerCase()))
|
||
)
|
||
}
|
||
|
||
// 类型过滤
|
||
if (selectedTransactionType.value > 0) {
|
||
const typeMap = {
|
||
1: 'income',
|
||
2: 'expense',
|
||
3: 'withdrawal',
|
||
4: 'recharge',
|
||
5: 'transfer'
|
||
}
|
||
const targetType = typeMap[selectedTransactionType.value as keyof typeof typeMap]
|
||
filtered = filtered.filter(t => t.type === targetType)
|
||
}
|
||
|
||
return filtered
|
||
})
|
||
|
||
const totalTransactions = computed(() => filteredTransactions.value.length)
|
||
|
||
// 分页相关
|
||
const currentPage = ref(1)
|
||
const pageSize = ref(10)
|
||
const totalPages = computed(() => Math.ceil(totalTransactions.value / pageSize.value))
|
||
const visiblePages = computed(() => {
|
||
const pages = []
|
||
const start = Math.max(1, currentPage.value - 2)
|
||
const end = Math.min(totalPages.value, currentPage.value + 2)
|
||
|
||
for (let i = start; i <= end; i++) {
|
||
pages.push(i)
|
||
}
|
||
|
||
return pages
|
||
})
|
||
|
||
// 模态框相关
|
||
const showWithdrawalModal = ref(false)
|
||
const showTransferModal = ref(false)
|
||
const showRechargeModal = ref(false)
|
||
|
||
// 提现请求数据
|
||
const withdrawalRequests = ref([
|
||
{ id: 1, userName: '张三', amount: 500.00, time: '2024-01-15T10:30:00Z', status: 'pending' },
|
||
{ id: 2, userName: '李四', amount: 1200.00, time: '2024-01-15T09:15:00Z', status: 'pending' },
|
||
{ id: 3, userName: '王五', amount: 800.00, time: '2024-01-14T16:45:00Z', status: 'pending' }
|
||
])
|
||
|
||
// 表单数据
|
||
const withdrawalAmount = ref('')
|
||
const selectedAccount = ref(0)
|
||
const accountOptions = ['支付宝', '银行卡', '微信钱包']
|
||
const withdrawalNote = ref('')
|
||
|
||
const transferToAccount = ref('')
|
||
const transferAmount = ref('')
|
||
const transferNote = ref('')
|
||
|
||
const rechargeAmount = ref('')
|
||
const selectedPayment = ref(0)
|
||
const paymentOptions = ['支付宝', '微信支付', '银行卡']
|
||
const rechargeNote = ref('')
|
||
|
||
// 方法
|
||
const setChartPeriod = (period: string) => {
|
||
chartPeriod.value = period
|
||
// TODO: 重新加载图表数据
|
||
console.log('切换图表周期:', period)
|
||
}
|
||
|
||
const generateReport = (type: string) => {
|
||
uni.showToast({
|
||
title: `${type === 'daily' ? '日报表' : type === 'monthly' ? '月报表' : '年报表'}生成中`,
|
||
icon: 'none'
|
||
})
|
||
// TODO: 实现报表生成功能
|
||
}
|
||
|
||
const handleTransactionTypeChange = (e: any) => {
|
||
selectedTransactionType.value = e.detail.value
|
||
}
|
||
|
||
const handleAccountChange = (e: any) => {
|
||
selectedAccount.value = e.detail.value
|
||
}
|
||
|
||
const handlePaymentChange = (e: any) => {
|
||
selectedPayment.value = e.detail.value
|
||
}
|
||
|
||
const viewTransactionDetail = (transaction: any) => {
|
||
uni.showModal({
|
||
title: '交易详情',
|
||
content: `交易类型: ${transaction.type}\n金额: ¥${transaction.amount}\n状态: ${transaction.status}\n时间: ${formatDateTime(transaction.time)}`,
|
||
showCancel: false
|
||
})
|
||
}
|
||
|
||
const submitWithdrawal = () => {
|
||
const amount = parseFloat(withdrawalAmount.value)
|
||
if (!amount || amount < 100) {
|
||
uni.showToast({
|
||
title: '提现金额不能少于100元',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
if (amount > availableBalance.value) {
|
||
uni.showToast({
|
||
title: '提现金额不能超过可用余额',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
// TODO: 实现提现申请逻辑
|
||
uni.showToast({
|
||
title: '提现申请提交成功',
|
||
icon: 'success'
|
||
})
|
||
|
||
closeModal()
|
||
}
|
||
|
||
const submitTransfer = () => {
|
||
if (!transferToAccount.value || !transferAmount.value) {
|
||
uni.showToast({
|
||
title: '请填写完整信息',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
// TODO: 实现转账逻辑
|
||
uni.showToast({
|
||
title: '转账成功',
|
||
icon: 'success'
|
||
})
|
||
|
||
closeModal()
|
||
}
|
||
|
||
const submitRecharge = () => {
|
||
const amount = parseFloat(rechargeAmount.value)
|
||
if (!amount || amount <= 0) {
|
||
uni.showToast({
|
||
title: '请输入有效的充值金额',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
// TODO: 实现充值逻辑
|
||
uni.showToast({
|
||
title: '充值成功',
|
||
icon: 'success'
|
||
})
|
||
|
||
closeModal()
|
||
}
|
||
|
||
const closeModal = () => {
|
||
showWithdrawalModal.value = false
|
||
showTransferModal.value = false
|
||
showRechargeModal.value = false
|
||
|
||
// 重置表单
|
||
withdrawalAmount.value = ''
|
||
selectedAccount.value = 0
|
||
withdrawalNote.value = ''
|
||
|
||
transferToAccount.value = ''
|
||
transferAmount.value = ''
|
||
transferNote.value = ''
|
||
|
||
rechargeAmount.value = ''
|
||
selectedPayment.value = 0
|
||
rechargeNote.value = ''
|
||
}
|
||
|
||
const goToPage = (page: number) => {
|
||
currentPage.value = page
|
||
// TODO: 实现分页数据加载
|
||
}
|
||
|
||
const formatDateTime = (dateString: string) => {
|
||
const date = new Date(dateString)
|
||
return date.toLocaleDateString('zh-CN') + ' ' + date.toLocaleTimeString('zh-CN', { hour12: false })
|
||
}
|
||
|
||
// Tab 切换方法
|
||
const switchTab = (tabKey: string) => {
|
||
activeTab.value = tabKey
|
||
}
|
||
|
||
// 提现管理方法
|
||
const approveWithdrawal = (withdrawal: any) => {
|
||
uni.showModal({
|
||
title: '确认批准',
|
||
content: `确定要批准用户 ${withdrawal.userName} 的提现申请 ¥${withdrawal.amount} 吗?`,
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
withdrawal.status = 'approved'
|
||
uni.showToast({
|
||
title: '批准成功',
|
||
icon: 'success'
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
const rejectWithdrawal = (withdrawal: any) => {
|
||
uni.showModal({
|
||
title: '确认拒绝',
|
||
content: `确定要拒绝用户 ${withdrawal.userName} 的提现申请吗?`,
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
withdrawal.status = 'rejected'
|
||
uni.showToast({
|
||
title: '拒绝成功',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
const formatDate = (dateString: string) => {
|
||
const date = new Date(dateString)
|
||
return date.toLocaleDateString('zh-CN') + ' ' + date.toLocaleTimeString('zh-CN', { hour12: false })
|
||
}
|
||
|
||
// 页面生命周期
|
||
onLoad((options: any) => {
|
||
// 处理页面参数,切换到对应的tab
|
||
if (options && options.tab) {
|
||
if (options.tab === 'withdrawals') {
|
||
activeTab.value = 'withdrawals'
|
||
} else {
|
||
activeTab.value = 'finance-overview'
|
||
}
|
||
} else {
|
||
activeTab.value = 'finance-overview'
|
||
}
|
||
|
||
console.log('财务管理页面加载,参数:', options)
|
||
})
|
||
|
||
onMounted(() => {
|
||
// 初始化数据
|
||
console.log('财务管理页面初始化')
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
/* Tab 栏样式 */
|
||
.tab-bar {
|
||
display: flex;
|
||
background-color: #ffffff;
|
||
border-radius: 8rpx;
|
||
padding: 8rpx;
|
||
margin-bottom: 24rpx;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.tab-item {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8rpx;
|
||
padding: 16rpx 24rpx;
|
||
border-radius: 6rpx;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
background-color: #f5f5f5;
|
||
color: #666666;
|
||
}
|
||
|
||
.tab-item:hover {
|
||
background-color: #e8e8e8;
|
||
}
|
||
|
||
.tab-item.active {
|
||
background-color: #1890ff;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.tab-icon {
|
||
font-size: 16rpx;
|
||
}
|
||
|
||
.tab-title {
|
||
font-size: 14rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* 提现管理样式 */
|
||
.withdrawals-section {
|
||
background-color: #ffffff;
|
||
border-radius: 8rpx;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.withdrawals-management {
|
||
padding: 32rpx;
|
||
}
|
||
|
||
.section-header {
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.section-title {
|
||
display: block;
|
||
font-size: 18rpx;
|
||
font-weight: 600;
|
||
color: #262626;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.section-desc {
|
||
display: block;
|
||
font-size: 14rpx;
|
||
color: #666666;
|
||
}
|
||
|
||
.withdrawals-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.withdrawal-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20rpx 24rpx;
|
||
background-color: #fafafa;
|
||
border-radius: 6rpx;
|
||
border: 1rpx solid #e8e8e8;
|
||
}
|
||
|
||
.withdrawal-info {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.user-name {
|
||
font-size: 16rpx;
|
||
font-weight: 500;
|
||
color: #262626;
|
||
}
|
||
|
||
.amount {
|
||
font-size: 18rpx;
|
||
font-weight: 600;
|
||
color: #1890ff;
|
||
}
|
||
|
||
.time {
|
||
font-size: 14rpx;
|
||
color: #999999;
|
||
}
|
||
|
||
.withdrawal-actions {
|
||
display: flex;
|
||
gap: 12rpx;
|
||
}
|
||
|
||
.btn-success {
|
||
background-color: #52c41a;
|
||
color: #ffffff;
|
||
border: none;
|
||
border-radius: 6rpx;
|
||
padding: 8rpx 16rpx;
|
||
font-size: 14rpx;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.btn-danger {
|
||
background-color: #ff4d4f;
|
||
color: #ffffff;
|
||
border: none;
|
||
border-radius: 6rpx;
|
||
padding: 8rpx 16rpx;
|
||
font-size: 14rpx;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.finance-management {
|
||
padding: 20rpx;
|
||
}
|
||
|
||
.page-header {
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.page-title {
|
||
display: block;
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.page-subtitle {
|
||
display: block;
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.finance-overview {
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.overview-cards {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-bottom: 40rpx;
|
||
flex-wrap: wrap;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.overview-card {
|
||
flex: 1;
|
||
min-width: 200rpx;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
border-radius: 16rpx;
|
||
padding: 40rpx 30rpx;
|
||
text-align: center;
|
||
color: #fff;
|
||
box-shadow: 0 4rpx 12rpx rgba(102, 126, 234, 0.3);
|
||
}
|
||
|
||
.card-icon {
|
||
font-size: 48rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.card-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.card-value {
|
||
display: block;
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.card-label {
|
||
display: block;
|
||
font-size: 24rpx;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.chart-section {
|
||
background-color: #fff;
|
||
border-radius: 12rpx;
|
||
padding: 30rpx;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.chart-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.chart-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.chart-controls {
|
||
display: flex;
|
||
gap: 15rpx;
|
||
}
|
||
|
||
.period-btn {
|
||
padding: 12rpx 24rpx;
|
||
border: 1rpx solid #ddd;
|
||
border-radius: 6rpx;
|
||
background-color: #fff;
|
||
color: #666;
|
||
cursor: pointer;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.period-btn.active {
|
||
background-color: #1890ff;
|
||
color: #fff;
|
||
border-color: #1890ff;
|
||
}
|
||
|
||
.chart-placeholder {
|
||
height: 300rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background-color: #f8f9fa;
|
||
border-radius: 8rpx;
|
||
border: 2rpx dashed #ddd;
|
||
}
|
||
|
||
.chart-placeholder-text {
|
||
font-size: 32rpx;
|
||
color: #999;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.chart-placeholder-subtext {
|
||
font-size: 24rpx;
|
||
color: #ccc;
|
||
}
|
||
|
||
.finance-actions {
|
||
display: flex;
|
||
gap: 30rpx;
|
||
margin-bottom: 40rpx;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.action-section {
|
||
flex: 1;
|
||
min-width: 300rpx;
|
||
background-color: #fff;
|
||
border-radius: 12rpx;
|
||
padding: 30rpx;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 30rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 20rpx;
|
||
display: block;
|
||
}
|
||
|
||
.action-buttons {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 15rpx;
|
||
}
|
||
|
||
.action-btn {
|
||
flex: 1;
|
||
min-width: 120rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 20rpx 15rpx;
|
||
border-radius: 8rpx;
|
||
border: none;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.action-btn.primary {
|
||
background-color: #1890ff;
|
||
color: #fff;
|
||
}
|
||
|
||
.action-btn.secondary {
|
||
background-color: #52c41a;
|
||
color: #fff;
|
||
}
|
||
|
||
.action-btn.info {
|
||
background-color: #13c2c2;
|
||
color: #fff;
|
||
}
|
||
|
||
.action-btn.success {
|
||
background-color: #52c41a;
|
||
color: #fff;
|
||
}
|
||
|
||
.action-btn.warning {
|
||
background-color: #faad14;
|
||
color: #fff;
|
||
}
|
||
|
||
.action-btn.danger {
|
||
background-color: #ff4d4f;
|
||
color: #fff;
|
||
}
|
||
|
||
.action-btn:hover {
|
||
transform: translateY(-2rpx);
|
||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
.btn-icon {
|
||
font-size: 32rpx;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.btn-text {
|
||
font-size: 24rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.transaction-section {
|
||
background-color: #fff;
|
||
border-radius: 12rpx;
|
||
padding: 30rpx;
|
||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.section-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 30rpx;
|
||
flex-wrap: wrap;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.section-controls {
|
||
display: flex;
|
||
gap: 15rpx;
|
||
align-items: center;
|
||
}
|
||
|
||
.search-input {
|
||
width: 250rpx;
|
||
padding: 12rpx 16rpx;
|
||
border: 1rpx solid #ddd;
|
||
border-radius: 6rpx;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.type-selector {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10rpx;
|
||
padding: 12rpx 16rpx;
|
||
border: 1rpx solid #ddd;
|
||
border-radius: 6rpx;
|
||
background-color: #fff;
|
||
cursor: pointer;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.transaction-table {
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.table-header {
|
||
background-color: #f5f5f5;
|
||
border-bottom: 1rpx solid #eee;
|
||
}
|
||
|
||
.table-row {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 20rpx;
|
||
border-bottom: 1rpx solid #eee;
|
||
}
|
||
|
||
.table-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.col-time {
|
||
flex: 1.2;
|
||
}
|
||
|
||
.col-type,
|
||
.col-status {
|
||
flex: 0.8;
|
||
}
|
||
|
||
.col-description {
|
||
flex: 2;
|
||
}
|
||
|
||
.col-amount {
|
||
flex: 1;
|
||
}
|
||
|
||
.col-actions {
|
||
width: 80rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.transaction-time {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.type-badge {
|
||
display: inline-block;
|
||
padding: 4rpx 8rpx;
|
||
border-radius: 8rpx;
|
||
font-size: 22rpx;
|
||
font-weight: bold;
|
||
text-align: center;
|
||
}
|
||
|
||
.type-badge.income {
|
||
background-color: #52c41a;
|
||
color: #fff;
|
||
}
|
||
|
||
.type-badge.expense {
|
||
background-color: #ff4d4f;
|
||
color: #fff;
|
||
}
|
||
|
||
.type-badge.withdrawal {
|
||
background-color: #faad14;
|
||
color: #fff;
|
||
}
|
||
|
||
.type-badge.recharge {
|
||
background-color: #1890ff;
|
||
color: #fff;
|
||
}
|
||
|
||
.type-badge.transfer {
|
||
background-color: #722ed1;
|
||
color: #fff;
|
||
}
|
||
|
||
.transaction-desc {
|
||
font-size: 28rpx;
|
||
font-weight: 500;
|
||
color: #333;
|
||
margin-bottom: 4rpx;
|
||
display: block;
|
||
}
|
||
|
||
.transaction-ref {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
display: block;
|
||
}
|
||
|
||
.transaction-amount {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.transaction-amount.income {
|
||
color: #52c41a;
|
||
}
|
||
|
||
.transaction-amount.expense {
|
||
color: #ff4d4f;
|
||
}
|
||
|
||
.status-badge {
|
||
display: inline-block;
|
||
padding: 4rpx 8rpx;
|
||
border-radius: 8rpx;
|
||
font-size: 22rpx;
|
||
font-weight: bold;
|
||
text-align: center;
|
||
}
|
||
|
||
.status-badge.completed {
|
||
background-color: #52c41a;
|
||
color: #fff;
|
||
}
|
||
|
||
.status-badge.pending {
|
||
background-color: #faad14;
|
||
color: #fff;
|
||
}
|
||
|
||
.status-badge.failed {
|
||
background-color: #ff4d4f;
|
||
color: #fff;
|
||
}
|
||
|
||
.status-badge.cancelled {
|
||
background-color: #666;
|
||
color: #fff;
|
||
}
|
||
|
||
.action-btn.view {
|
||
width: 50rpx;
|
||
height: 50rpx;
|
||
border-radius: 6rpx;
|
||
background-color: #1890ff;
|
||
color: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: none;
|
||
cursor: pointer;
|
||
font-size: 22rpx;
|
||
}
|
||
|
||
.pagination {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-top: 30rpx;
|
||
flex-wrap: wrap;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.page-info {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.page-controls {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 15rpx;
|
||
}
|
||
|
||
.page-btn {
|
||
padding: 12rpx 20rpx;
|
||
border: 1rpx solid #ddd;
|
||
border-radius: 6rpx;
|
||
background-color: #fff;
|
||
color: #333;
|
||
cursor: pointer;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.page-btn:disabled {
|
||
background-color: #f5f5f5;
|
||
color: #ccc;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.page-numbers {
|
||
display: flex;
|
||
gap: 8rpx;
|
||
margin: 0 20rpx;
|
||
}
|
||
|
||
.page-number {
|
||
width: 50rpx;
|
||
height: 50rpx;
|
||
border: 1rpx solid #ddd;
|
||
border-radius: 6rpx;
|
||
background-color: #fff;
|
||
color: #333;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.page-number.active {
|
||
background-color: #1890ff;
|
||
color: #fff;
|
||
border-color: #1890ff;
|
||
}
|
||
|
||
.modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.modal-content {
|
||
background-color: #fff;
|
||
border-radius: 12rpx;
|
||
width: 90%;
|
||
max-width: 500rpx;
|
||
max-height: 80vh;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 30rpx;
|
||
border-bottom: 1rpx solid #eee;
|
||
}
|
||
|
||
.modal-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.modal-close {
|
||
cursor: pointer;
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
padding: 10rpx;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.withdrawal-info {
|
||
background-color: #f8f9fa;
|
||
border-radius: 8rpx;
|
||
padding: 20rpx;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 15rpx;
|
||
}
|
||
|
||
.info-item:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.info-label {
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.info-value {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.form-label {
|
||
display: block;
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
margin-bottom: 10rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.form-input {
|
||
width: 100%;
|
||
padding: 16rpx 20rpx;
|
||
border: 1rpx solid #ddd;
|
||
border-radius: 8rpx;
|
||
font-size: 28rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.form-select {
|
||
padding: 16rpx 20rpx;
|
||
border: 1rpx solid #ddd;
|
||
border-radius: 8rpx;
|
||
background-color: #fff;
|
||
cursor: pointer;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.modal-footer {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
justify-content: flex-end;
|
||
padding: 30rpx;
|
||
border-top: 1rpx solid #eee;
|
||
}
|
||
|
||
.btn-primary {
|
||
background-color: #1890ff;
|
||
color: #fff;
|
||
border: none;
|
||
border-radius: 8rpx;
|
||
padding: 16rpx 32rpx;
|
||
font-size: 26rpx;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.btn-secondary {
|
||
background-color: #fff;
|
||
color: #666;
|
||
border: 1rpx solid #ddd;
|
||
border-radius: 8rpx;
|
||
padding: 16rpx 32rpx;
|
||
font-size: 26rpx;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.iconfont {
|
||
font-family: 'iconfont';
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media screen and (max-width: 750rpx) {
|
||
.overview-cards {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.finance-actions {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.action-section {
|
||
min-width: auto;
|
||
}
|
||
|
||
.action-buttons {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.action-btn {
|
||
width: 100%;
|
||
}
|
||
|
||
.section-header {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.section-controls {
|
||
justify-content: center;
|
||
}
|
||
|
||
.table-row {
|
||
flex-wrap: wrap;
|
||
gap: 10rpx;
|
||
}
|
||
|
||
.col-description {
|
||
flex: 1 1 100%;
|
||
}
|
||
|
||
.pagination {
|
||
flex-direction: column;
|
||
text-align: center;
|
||
}
|
||
}
|
||
</style> |