feat: 结算管理3个页面的样式

This commit is contained in:
lzhizhao 2025-11-02 00:46:59 +08:00
parent 446dcf9579
commit 60ecd8015d
4 changed files with 2193 additions and 0 deletions

View File

@ -464,6 +464,59 @@ export default {
} }
] ]
}, },
{
menuId: getUUID(),
parentId: 0,
parentName: null,
name: '结算管理',
url: 'settle',
perms: 'settle',
type: 0,
elIcon: 'el-icon-office-building',
orderNum: 0,
open: null,
list: [
{
menuId: getUUID(),
parentId: 0,
parentName: null,
name: '待结算管理',
url: 'settle/unsettle',
perms: 'settle:unsettle',
type: 1,
elIcon: 'el-icon-document-checked',
orderNum: 0,
open: null,
list: []
},
{
menuId: getUUID(),
parentId: 0,
parentName: null,
name: '金额管理',
url: 'settle/money',
perms: 'settle:money',
type: 1,
elIcon: 'el-icon-coin',
orderNum: 0,
open: null,
list: []
},
{
menuId: getUUID(),
parentId: 0,
parentName: null,
name: '钱包管理',
url: 'settle/wallet',
perms: 'settle:wallet',
type: 1,
elIcon: 'el-icon-wallet',
orderNum: 0,
open: null,
list: []
}
]
},
{ {
menuId: getUUID(), menuId: getUUID(),
parentId: 0, parentId: 0,

View File

@ -0,0 +1,808 @@
<template>
<div class="page-container">
<!-- 统计数据区域 -->
<el-row :gutter="16" class="stats-row">
<el-col :span="8">
<el-card class="stat-card" shadow="hover">
<div class="stat-content">
<div class="stat-label">冻结金额</div>
<div class="stat-value stat-frozen">
¥ {{ statsInfo.frozenAmount.toFixed(2) }}
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card class="stat-card" shadow="hover">
<div class="stat-content">
<div class="stat-label">可用金额</div>
<div class="stat-value stat-available">
¥ {{ statsInfo.availableAmount.toFixed(2) }}
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card class="stat-card" shadow="hover">
<div class="stat-content">
<div class="stat-label">钱包余额</div>
<div class="stat-value stat-balance">
¥ {{ statsInfo.walletBalance.toFixed(2) }}
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 标签页区域 -->
<el-card class="tabs-card" shadow="never">
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<!-- 收入Tab -->
<el-tab-pane label="收入" name="income">
<!-- 搜索区域 -->
<div class="search-section">
<el-form
:model="incomeSearchForm"
:inline="true"
ref="incomeSearchForm"
class="search-form"
size="small"
>
<el-form-item label="订单ID" prop="orderId">
<el-input
v-model="incomeSearchForm.orderId"
placeholder="请输入订单ID"
clearable
@clear="handleIncomeSearchClear"
style="width: 200px"
/>
</el-form-item>
<el-form-item label="订单时间" prop="dateRange">
<el-date-picker
v-model="incomeSearchForm.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
style="width: 340px"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="handleIncomeSearch"
icon="el-icon-search"
size="small"
>
搜索
</el-button>
<el-button
@click="handleIncomeReset"
icon="el-icon-refresh"
size="small"
>
重置
</el-button>
</el-form-item>
</el-form>
</div>
<!-- 表格区域 -->
<el-table
ref="incomeTable"
:data="incomeDataList"
border
style="width: 100%"
v-loading="incomeLoading"
>
<el-table-column
type="index"
width="60"
align="center"
label="序号"
/>
<el-table-column
prop="orderId"
label="订单ID"
align="center"
min-width="150"
/>
<el-table-column
prop="orderAmount"
label="订单金额"
align="center"
width="130"
>
<template slot-scope="scope">
<span style="font-weight: 600">
¥{{ scope.row.orderAmount.toFixed(2) }}
</span>
</template>
</el-table-column>
<el-table-column
prop="orderTime"
label="订单时间"
align="center"
min-width="160"
/>
<el-table-column
prop="incomeAmount"
label="收入金额"
align="center"
width="130"
>
<template slot-scope="scope">
<span style="color: #52c41a; font-weight: 600">
+ ¥{{ scope.row.incomeAmount.toFixed(2) }}
</span>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
@size-change="handleIncomeSizeChange"
@current-change="handleIncomeCurrentChange"
:current-page="incomePageInfo.pageNumber"
:page-sizes="[10, 20, 50, 100]"
:page-size="incomePageInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="incomePageInfo.total"
class="pagination-container"
/>
</el-tab-pane>
<!-- 支出Tab -->
<el-tab-pane label="支出" name="expense">
<!-- 搜索区域 -->
<div class="search-section">
<el-form
:model="expenseSearchForm"
:inline="true"
ref="expenseSearchForm"
class="search-form"
size="small"
>
<el-form-item label="结算明细ID" prop="settlementId">
<el-input
v-model="expenseSearchForm.settlementId"
placeholder="请输入结算明细ID"
clearable
@clear="handleExpenseSearchClear"
style="width: 180px"
/>
</el-form-item>
<el-form-item label="清算明细ID" prop="clearingId">
<el-input
v-model="expenseSearchForm.clearingId"
placeholder="请输入清算明细ID"
clearable
@clear="handleExpenseSearchClear"
style="width: 180px"
/>
</el-form-item>
<el-form-item label="支出对象ID" prop="targetId">
<el-input
v-model="expenseSearchForm.targetId"
placeholder="请输入支出对象ID"
clearable
@clear="handleExpenseSearchClear"
style="width: 180px"
/>
</el-form-item>
<el-form-item label="支出时间" prop="dateRange">
<el-date-picker
v-model="expenseSearchForm.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
style="width: 340px"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="handleExpenseSearch"
icon="el-icon-search"
size="small"
>
搜索
</el-button>
<el-button
@click="handleExpenseReset"
icon="el-icon-refresh"
size="small"
>
重置
</el-button>
</el-form-item>
</el-form>
</div>
<!-- 表格区域 -->
<el-table
ref="expenseTable"
:data="expenseDataList"
border
style="width: 100%"
v-loading="expenseLoading"
>
<el-table-column
type="index"
width="60"
align="center"
label="序号"
/>
<el-table-column
prop="settlementId"
label="结算明细ID"
align="center"
min-width="140"
/>
<el-table-column
prop="clearingId"
label="清算明细ID"
align="center"
min-width="140"
/>
<el-table-column
prop="settlementAmount"
label="结算金额"
align="center"
width="130"
>
<template slot-scope="scope">
<span style="color: #ff4d4f; font-weight: 600">
- ¥{{ scope.row.settlementAmount.toFixed(2) }}
</span>
</template>
</el-table-column>
<el-table-column
prop="settlementTime"
label="结算时间"
align="center"
min-width="160"
/>
<el-table-column
prop="targetId"
label="支出对象ID"
align="center"
min-width="120"
/>
<el-table-column
prop="targetRole"
label="支出对象角色"
align="center"
width="120"
/>
<el-table-column
prop="expenseTime"
label="支出时间"
align="center"
min-width="160"
/>
</el-table>
<!-- 分页 -->
<el-pagination
@size-change="handleExpenseSizeChange"
@current-change="handleExpenseCurrentChange"
:current-page="expensePageInfo.pageNumber"
:page-sizes="[10, 20, 50, 100]"
:page-size="expensePageInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="expensePageInfo.total"
class="pagination-container"
/>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</template>
<script>
export default {
name: "MoneyManagement",
data() {
return {
//
activeTab: "income",
//
statsInfo: {
frozenAmount: 5280.0,
availableAmount: 23450.5,
walletBalance: 28730.5
},
//
incomeSearchForm: {
orderId: "",
dateRange: null
},
//
incomeDataList: [],
//
incomePageInfo: {
pageNumber: 1,
pageSize: 10,
total: 0
},
//
incomeLoading: false,
//
expenseSearchForm: {
settlementId: "",
clearingId: "",
targetId: "",
dateRange: null
},
//
expenseDataList: [],
//
expensePageInfo: {
pageNumber: 1,
pageSize: 10,
total: 0
},
//
expenseLoading: false
};
},
created() {
this.fetchStatsInfo();
this.fetchIncomeData();
},
methods: {
//
async fetchStatsInfo() {
try {
// TODO: API
// const res = await this.$api.mer_admin.getMoneyStats();
// this.statsInfo = res.data.data;
//
this.statsInfo = {
frozenAmount: 5280.0,
availableAmount: 23450.5,
walletBalance: 28730.5
};
} catch (error) {
console.error("获取统计信息失败:", error);
this.$message.error("获取统计信息失败");
}
},
//
handleTabClick(tab) {
if (tab.name === "income") {
this.fetchIncomeData();
} else if (tab.name === "expense") {
this.fetchExpenseData();
}
},
// ==================== ====================
//
fetchIncomeData() {
this.incomeLoading = true;
const marketId = this.marketId;
if (!marketId) {
this.$message.error("获取市场ID失败请重新登录");
this.$router.push("/login");
return;
}
const params = {
pageNumber: this.incomePageInfo.pageNumber,
pageSize: this.incomePageInfo.pageSize,
marketId,
...this.buildIncomeSearchParams()
};
// TODO: API
// this.$api.mer_admin
// .getIncomeList(params)
// .then(res => {
// if (res.data && res.data.data) {
// this.incomeDataList = res.data.data.data || [];
// this.incomePageInfo.total = Number(res.data.data.total) || 0;
// }
// })
// .catch(err => {
// console.error(":", err);
// this.$message.error("");
// })
// .finally(() => {
// this.incomeLoading = false;
// });
//
setTimeout(() => {
const mockData = [
{
orderId: "ORD202501310001",
orderAmount: 1280.0,
orderTime: "2025-01-31 10:30:25",
incomeAmount: 1280.0
},
{
orderId: "ORD202501310002",
orderAmount: 850.5,
orderTime: "2025-01-31 11:15:42",
incomeAmount: 850.5
},
{
orderId: "ORD202501310003",
orderAmount: 2100.0,
orderTime: "2025-01-31 14:20:18",
incomeAmount: 2100.0
},
{
orderId: "ORD202501300001",
orderAmount: 560.0,
orderTime: "2025-01-30 09:45:33",
incomeAmount: 560.0
},
{
orderId: "ORD202501300002",
orderAmount: 1450.0,
orderTime: "2025-01-30 16:28:56",
incomeAmount: 1450.0
}
];
//
let filteredData = mockData;
const searchParams = this.buildIncomeSearchParams();
if (searchParams.orderId) {
filteredData = filteredData.filter(item =>
item.orderId.includes(searchParams.orderId)
);
}
this.incomeDataList = filteredData;
this.incomePageInfo.total = 127; //
this.incomeLoading = false;
}, 500);
},
//
buildIncomeSearchParams() {
const params = {};
if (this.incomeSearchForm.orderId) {
params.orderId = this.incomeSearchForm.orderId;
}
if (
this.incomeSearchForm.dateRange &&
this.incomeSearchForm.dateRange.length === 2
) {
params.startDate = this.incomeSearchForm.dateRange[0];
params.endDate = this.incomeSearchForm.dateRange[1];
}
return params;
},
//
handleIncomeSearch() {
this.incomePageInfo.pageNumber = 1;
this.fetchIncomeData();
},
//
handleIncomeSearchClear() {
this.handleIncomeSearch();
},
//
handleIncomeReset() {
this.incomeSearchForm = {
orderId: "",
dateRange: null
};
this.incomePageInfo.pageNumber = 1;
this.fetchIncomeData();
},
//
handleIncomeSizeChange(val) {
this.incomePageInfo.pageSize = val;
this.incomePageInfo.pageNumber = 1;
this.fetchIncomeData();
},
//
handleIncomeCurrentChange(val) {
this.incomePageInfo.pageNumber = val;
this.fetchIncomeData();
},
// ==================== ====================
//
fetchExpenseData() {
this.expenseLoading = true;
const marketId = this.marketId;
if (!marketId) {
this.$message.error("获取市场ID失败请重新登录");
this.$router.push("/login");
return;
}
const params = {
pageNumber: this.expensePageInfo.pageNumber,
pageSize: this.expensePageInfo.pageSize,
marketId,
...this.buildExpenseSearchParams()
};
// TODO: API
// this.$api.mer_admin
// .getExpenseList(params)
// .then(res => {
// if (res.data && res.data.data) {
// this.expenseDataList = res.data.data.data || [];
// this.expensePageInfo.total = Number(res.data.data.total) || 0;
// }
// })
// .catch(err => {
// console.error(":", err);
// this.$message.error("");
// })
// .finally(() => {
// this.expenseLoading = false;
// });
//
setTimeout(() => {
const mockData = [
{
settlementId: "SET202501310001",
clearingId: "CLR202501310001",
settlementAmount: 320.0,
settlementTime: "2025-01-31 15:30:20",
targetId: "SUP100256",
targetRole: "供应商",
expenseTime: "2025-01-31 15:30:25"
},
{
settlementId: "SET202501310002",
clearingId: "CLR202501310002",
settlementAmount: 580.5,
settlementTime: "2025-01-31 16:15:35",
targetId: "DRV100128",
targetRole: "配送员",
expenseTime: "2025-01-31 16:15:40"
},
{
settlementId: "SET202501310003",
clearingId: "CLR202501310003",
settlementAmount: 1200.0,
settlementTime: "2025-01-31 17:20:18",
targetId: "SUP100328",
targetRole: "供应商",
expenseTime: "2025-01-31 17:20:22"
},
{
settlementId: "SET202501300001",
clearingId: "CLR202501300001",
settlementAmount: 450.0,
settlementTime: "2025-01-30 10:45:33",
targetId: "PLT100045",
targetRole: "平台",
expenseTime: "2025-01-30 10:45:38"
},
{
settlementId: "SET202501300002",
clearingId: "CLR202501300002",
settlementAmount: 280.0,
settlementTime: "2025-01-30 14:28:56",
targetId: "DRV100089",
targetRole: "配送员",
expenseTime: "2025-01-30 14:29:00"
}
];
//
let filteredData = mockData;
const searchParams = this.buildExpenseSearchParams();
if (searchParams.settlementId) {
filteredData = filteredData.filter(item =>
item.settlementId.includes(searchParams.settlementId)
);
}
if (searchParams.clearingId) {
filteredData = filteredData.filter(item =>
item.clearingId.includes(searchParams.clearingId)
);
}
if (searchParams.targetId) {
filteredData = filteredData.filter(item =>
item.targetId.includes(searchParams.targetId)
);
}
this.expenseDataList = filteredData;
this.expensePageInfo.total = 85; //
this.expenseLoading = false;
}, 500);
},
//
buildExpenseSearchParams() {
const params = {};
if (this.expenseSearchForm.settlementId) {
params.settlementId = this.expenseSearchForm.settlementId;
}
if (this.expenseSearchForm.clearingId) {
params.clearingId = this.expenseSearchForm.clearingId;
}
if (this.expenseSearchForm.targetId) {
params.targetId = this.expenseSearchForm.targetId;
}
if (
this.expenseSearchForm.dateRange &&
this.expenseSearchForm.dateRange.length === 2
) {
params.startDate = this.expenseSearchForm.dateRange[0];
params.endDate = this.expenseSearchForm.dateRange[1];
}
return params;
},
//
handleExpenseSearch() {
this.expensePageInfo.pageNumber = 1;
this.fetchExpenseData();
},
//
handleExpenseSearchClear() {
this.handleExpenseSearch();
},
//
handleExpenseReset() {
this.expenseSearchForm = {
settlementId: "",
clearingId: "",
targetId: "",
dateRange: null
};
this.expensePageInfo.pageNumber = 1;
this.fetchExpenseData();
},
//
handleExpenseSizeChange(val) {
this.expensePageInfo.pageSize = val;
this.expensePageInfo.pageNumber = 1;
this.fetchExpenseData();
},
//
handleExpenseCurrentChange(val) {
this.expensePageInfo.pageNumber = val;
this.fetchExpenseData();
}
},
computed: {
// ID
marketId() {
return this.$store.state.userData.marketId;
}
}
};
</script>
<style lang="scss" scoped>
.page-container {
padding: 0;
}
//
.stats-row {
margin-bottom: 16px;
}
.stat-card {
border-radius: 8px;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
}
::v-deep .el-card__body {
padding: 24px;
}
}
.stat-content {
text-align: center;
}
.stat-label {
font-size: 14px;
color: #666;
margin-bottom: 12px;
}
.stat-value {
font-size: 28px;
font-weight: 600;
&.stat-frozen {
color: #fa8c16;
}
&.stat-available {
color: #52c41a;
}
&.stat-balance {
color: #1890ff;
}
}
//
.tabs-card {
border-radius: 8px;
::v-deep .el-card__body {
padding: 0;
}
::v-deep .el-tabs__header {
margin: 0;
padding: 0 24px;
background: #fafafa;
}
::v-deep .el-tabs__nav-wrap::after {
height: 1px;
}
::v-deep .el-tabs__item {
height: 48px;
line-height: 48px;
font-size: 15px;
}
::v-deep .el-tabs__content {
padding: 0;
}
}
//
.search-section {
padding: 24px;
border-bottom: 1px solid #f0f0f0;
}
.search-form .el-form-item {
margin-bottom: 0;
}
//
::v-deep .el-table {
margin: 0;
}
//
.pagination-container {
padding: 20px 24px;
text-align: right;
border-top: 1px solid #f0f0f0;
}
</style>
<style>
.search-form .el-form-item__label {
padding-bottom: 0px !important;
font-size: 14px;
line-height: 32px;
}
</style>

View File

@ -0,0 +1,594 @@
<template>
<div class="page-container">
<!-- 资金概览卡片 -->
<el-row :gutter="16" class="info-cards-row">
<el-col :span="8">
<el-card class="info-card info-card-purple" shadow="hover">
<div class="info-card-content">
<div class="info-card-label">当前冻结金额</div>
<div class="info-card-value">
<span>{{ accountInfo.totalAmount.toFixed(2) }}</span>
<span class="info-card-unit"></span>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card class="info-card info-card-pink" shadow="hover">
<div class="info-card-content">
<div class="info-card-label">当前可用金额</div>
<div class="info-card-value">
<span>{{ accountInfo.availableAmount.toFixed(2) }}</span>
<span class="info-card-unit"></span>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card class="info-card info-card-blue" shadow="hover">
<div class="info-card-content">
<div class="info-card-label">当前钱包余额</div>
<div class="info-card-value">
<span>{{ accountInfo.walletBalance.toFixed(2) }}</span>
<span class="info-card-unit"></span>
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 搜索区域 -->
<el-card class="search-card" shadow="never">
<el-form
:model="searchForm"
:inline="true"
ref="searchForm"
class="search-form"
size="small"
>
<el-form-item label="待结算明细ID" prop="settlementId">
<el-input
v-model="searchForm.settlementId"
placeholder="请输入待结算明细ID"
clearable
@clear="handleSearchClear"
style="width: 200px"
/>
</el-form-item>
<el-form-item label="关联清算明细ID" prop="clearingId">
<el-input
v-model="searchForm.clearingId"
placeholder="请输入关联清算明细ID"
clearable
@clear="handleSearchClear"
style="width: 200px"
/>
</el-form-item>
<el-form-item label="结算目标对象ID" prop="targetId">
<el-input
v-model="searchForm.targetId"
placeholder="请输入结算目标对象ID"
clearable
@clear="handleSearchClear"
style="width: 200px"
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="handleSearch"
icon="el-icon-search"
size="small"
>
查询
</el-button>
<el-button @click="handleReset" icon="el-icon-refresh" size="small">
重置
</el-button>
<el-button
@click="handleRefreshAccount"
icon="el-icon-refresh-right"
size="small"
:loading="refreshLoading"
>
刷新账户信息
</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 表格区域 -->
<div class="table-container">
<el-table
ref="multipleTable"
:data="dataList"
border
style="width: 100%"
v-loading="loading"
>
<el-table-column type="index" width="60" align="center" label="序号" />
<el-table-column
prop="id"
label="待结算明细ID"
align="center"
min-width="140"
/>
<el-table-column
prop="clearingId"
label="关联清算明细ID"
align="center"
min-width="150"
/>
<el-table-column
prop="targetId"
label="结算目标对象ID"
align="center"
min-width="140"
/>
<el-table-column
prop="targetRole"
label="结算目标角色"
align="center"
width="120"
/>
<el-table-column
prop="amount"
label="结算金额(元)"
align="center"
width="130"
>
<template slot-scope="scope">
<span style="color: #f56c6c; font-weight: 600">
¥{{ scope.row.amount.toFixed(2) }}
</span>
</template>
</el-table-column>
<el-table-column
prop="clearingTime"
label="清算时间"
align="center"
min-width="160"
/>
<el-table-column label="结算状态" align="center" width="100">
<template slot-scope="scope">
<el-tag
v-if="scope.row.status === 'pending'"
type="warning"
size="small"
>
未结算
</el-tag>
<el-tag
v-else-if="scope.row.status === 'processing'"
type="primary"
size="small"
>
执行中
</el-tag>
<el-tag v-else type="success" size="small">已完成</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" align="center" width="150">
<template slot-scope="scope">
<el-button
v-if="scope.row.status === 'pending'"
size="mini"
type="primary"
@click="handleSettlement(scope.row, false)"
>
开始结算
</el-button>
<el-button
v-else-if="scope.row.status === 'processing'"
size="mini"
type="warning"
@click="handleSettlement(scope.row, true)"
>
重新执行
</el-button>
<span v-else style="color: #909399">-</span>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageInfo.pageNumber"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="pageInfo.total"
class="pagination-container"
/>
</div>
</div>
</template>
<script>
export default {
name: "UnsettleManagement",
data() {
return {
//
accountInfo: {
totalAmount: 0,
availableAmount: 0,
walletBalance: 0
},
//
searchForm: {
settlementId: "",
clearingId: "",
targetId: ""
},
//
dataList: [],
//
pageInfo: {
pageNumber: 1,
pageSize: 10,
total: 0
},
//
loading: false,
refreshLoading: false
};
},
created() {
this.fetchAccountInfo();
this.fetchData();
},
methods: {
//
async fetchAccountInfo() {
try {
// TODO: API
// const res = await this.$api.mer_admin.getAccountInfo();
// this.accountInfo = res.data.data;
//
this.accountInfo = {
totalAmount: 8770.5,
availableAmount: 6420.25,
walletBalance: 15230.75
};
} catch (error) {
console.error("获取账户信息失败:", error);
this.$message.error("获取账户信息失败");
}
},
//
async handleRefreshAccount() {
this.refreshLoading = true;
try {
await this.fetchAccountInfo();
this.$message.success("刷新成功");
} catch (error) {
console.error("刷新失败:", error);
} finally {
this.refreshLoading = false;
}
},
//
fetchData() {
this.loading = true;
const marketId = this.marketId;
if (!marketId) {
this.$message.error("获取市场ID失败请重新登录");
this.$router.push("/login");
return;
}
const params = {
pageNumber: this.pageInfo.pageNumber,
pageSize: this.pageInfo.pageSize,
marketId,
...this.buildSearchParams()
};
// TODO: API
// this.$api.mer_admin
// .getUnsettleList(params)
// .then(res => {
// if (res.data && res.data.data) {
// this.dataList = res.data.data.data || [];
// this.pageInfo.total = Number(res.data.data.total) || 0;
// }
// })
// .catch(err => {
// console.error(":", err);
// this.$message.error("");
// })
// .finally(() => {
// this.loading = false;
// });
//
setTimeout(() => {
const mockData = [
{
id: "SET2024010001",
clearingId: "CLR2024010001",
targetId: "TGT001",
targetRole: "商家",
amount: 1580.5,
clearingTime: "2024-01-15 14:30:25",
status: "pending"
},
{
id: "SET2024010002",
clearingId: "CLR2024010002",
targetId: "TGT002",
targetRole: "供应商",
amount: 2350.0,
clearingTime: "2024-01-15 15:20:10",
status: "processing"
},
{
id: "SET2024010003",
clearingId: "CLR2024010003",
targetId: "TGT003",
targetRole: "平台",
amount: 890.75,
clearingTime: "2024-01-15 16:45:30",
status: "completed"
},
{
id: "SET2024010004",
clearingId: "CLR2024010004",
targetId: "TGT004",
targetRole: "商家",
amount: 3200.0,
clearingTime: "2024-01-16 09:15:40",
status: "pending"
},
{
id: "SET2024010005",
clearingId: "CLR2024010005",
targetId: "TGT005",
targetRole: "供应商",
amount: 1750.25,
clearingTime: "2024-01-16 10:30:55",
status: "processing"
}
];
//
let filteredData = mockData;
const searchParams = this.buildSearchParams();
if (searchParams.settlementId) {
filteredData = filteredData.filter(item =>
item.id.includes(searchParams.settlementId)
);
}
if (searchParams.clearingId) {
filteredData = filteredData.filter(item =>
item.clearingId.includes(searchParams.clearingId)
);
}
if (searchParams.targetId) {
filteredData = filteredData.filter(item =>
item.targetId.includes(searchParams.targetId)
);
}
this.dataList = filteredData;
this.pageInfo.total = filteredData.length;
this.loading = false;
}, 500);
},
//
buildSearchParams() {
const params = {};
if (this.searchForm.settlementId) {
params.settlementId = this.searchForm.settlementId;
}
if (this.searchForm.clearingId) {
params.clearingId = this.searchForm.clearingId;
}
if (this.searchForm.targetId) {
params.targetId = this.searchForm.targetId;
}
return params;
},
//
handleSearch() {
this.pageInfo.pageNumber = 1;
this.fetchData();
},
//
handleSearchClear() {
this.handleSearch();
},
//
handleReset() {
this.searchForm = {
settlementId: "",
clearingId: "",
targetId: ""
};
this.pageInfo.pageNumber = 1;
this.fetchData();
},
//
handleSizeChange(val) {
this.pageInfo.pageSize = val;
this.pageInfo.pageNumber = 1;
this.fetchData();
},
//
handleCurrentChange(val) {
this.pageInfo.pageNumber = val;
this.fetchData();
},
//
handleSettlement(row, isRetry) {
const action = isRetry ? "重新执行" : "开始结算";
const message = `确认${action}该笔待结算明细吗?\n待结算明细ID: ${
row.id
}\n结算金额: ¥${row.amount.toFixed(2)}`;
this.$confirm(message, "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
this.executeSettlement(row, isRetry);
})
.catch(() => {
this.$message.info("已取消操作");
});
},
//
async executeSettlement(row, isRetry) {
try {
this.loading = true;
// TODO: API
// const params = {
// settlementId: row.id,
// isRetry: isRetry
// };
// await this.$api.mer_admin.executeSettlement(params);
// API
await new Promise(resolve => setTimeout(resolve, 1000));
//
row.status = "processing";
this.$message.success("结算任务已提交");
//
setTimeout(() => {
row.status = "completed";
this.$message.success("结算完成");
this.fetchAccountInfo(); //
}, 2000);
} catch (error) {
console.error("结算失败:", error);
this.$message.error("结算失败,请重试");
} finally {
this.loading = false;
}
}
},
computed: {
// ID
marketId() {
return this.$store.state.userData.marketId;
}
}
};
</script>
<style lang="scss" scoped>
.page-container {
padding: 0;
}
//
.info-cards-row {
margin-bottom: 16px;
}
.info-card {
border-radius: 8px;
overflow: hidden;
position: relative;
&::before {
content: "";
position: absolute;
top: -50%;
right: -20%;
width: 200px;
height: 200px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
}
&.info-card-purple {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
&.info-card-pink {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
&.info-card-blue {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
::v-deep .el-card__body {
padding: 20px;
}
}
.info-card-content {
color: white;
position: relative;
z-index: 1;
}
.info-card-label {
font-size: 14px;
opacity: 0.9;
margin-bottom: 8px;
}
.info-card-value {
font-size: 28px;
font-weight: 700;
line-height: 1.2;
span {
display: inline-block;
}
}
.info-card-unit {
font-size: 16px;
margin-left: 4px;
}
//
.search-card {
margin-bottom: 16px;
}
.search-form .el-form-item {
margin-bottom: 0;
}
//
.table-container {
background: white;
padding: 20px;
border-radius: 8px;
}
.pagination-container {
margin-top: 20px;
text-align: right;
}
</style>
<style>
.search-form .el-form-item__label {
padding-bottom: 0px !important;
font-size: 14px;
line-height: 32px;
}
</style>

View File

@ -0,0 +1,738 @@
<template>
<div class="page-container">
<!-- 统计数据区域 -->
<el-row :gutter="16" class="stats-row">
<el-col :span="12">
<el-card class="stat-card stat-card-available" shadow="hover">
<div class="stat-content">
<div class="stat-label">可用金额</div>
<div class="stat-value">
¥ {{ walletInfo.availableAmount.toFixed(2) }}
</div>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="stat-card stat-card-balance" shadow="hover">
<div class="stat-content">
<div class="stat-label">钱包余额</div>
<div class="stat-value">
¥ {{ walletInfo.walletBalance.toFixed(2) }}
</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 操作按钮区域 -->
<el-card class="action-card" shadow="never">
<div class="button-group">
<el-button type="primary" size="medium" @click="openDepositDialog">
提取可用金额到钱包余额
</el-button>
<el-button size="medium" @click="openWithdrawDialog">
提现钱包金额到银行卡
</el-button>
</div>
</el-card>
<!-- 标签页区域 -->
<el-card class="tabs-card" shadow="never">
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<!-- 提取到钱包Tab -->
<el-tab-pane label="提取到钱包" name="deposit">
<el-table
ref="depositTable"
:data="depositDataList"
border
style="width: 100%"
v-loading="depositLoading"
>
<el-table-column
type="index"
width="60"
align="center"
label="序号"
/>
<el-table-column
prop="amount"
label="操作金额"
align="center"
width="150"
>
<template slot-scope="scope">
<span style="color: #52c41a; font-weight: 600">
¥{{ scope.row.amount.toFixed(2) }}
</span>
</template>
</el-table-column>
<el-table-column
prop="afterAvailableAmount"
label="提取后可用金额余额"
align="center"
width="180"
>
<template slot-scope="scope">
<span style="font-weight: 600">
¥{{ scope.row.afterAvailableAmount.toFixed(2) }}
</span>
</template>
</el-table-column>
<el-table-column
prop="afterWalletBalance"
label="提取后钱包余额"
align="center"
width="160"
>
<template slot-scope="scope">
<span style="font-weight: 600">
¥{{ scope.row.afterWalletBalance.toFixed(2) }}
</span>
</template>
</el-table-column>
<el-table-column
prop="time"
label="操作时间"
align="center"
min-width="160"
/>
</el-table>
<!-- 分页 -->
<el-pagination
@size-change="handleDepositSizeChange"
@current-change="handleDepositCurrentChange"
:current-page="depositPageInfo.pageNumber"
:page-sizes="[10, 20, 50, 100]"
:page-size="depositPageInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="depositPageInfo.total"
class="pagination-container"
/>
</el-tab-pane>
<!-- 提现到银行卡Tab -->
<el-tab-pane label="提现到银行卡" name="withdraw">
<el-table
ref="withdrawTable"
:data="withdrawDataList"
border
style="width: 100%"
v-loading="withdrawLoading"
>
<el-table-column
type="index"
width="60"
align="center"
label="序号"
/>
<el-table-column
prop="amount"
label="操作金额"
align="center"
width="150"
>
<template slot-scope="scope">
<span style="color: #ff4d4f; font-weight: 600">
¥{{ scope.row.amount.toFixed(2) }}
</span>
</template>
</el-table-column>
<el-table-column
prop="afterWalletBalance"
label="提现后钱包余额"
align="center"
width="160"
>
<template slot-scope="scope">
<span style="font-weight: 600">
¥{{ scope.row.afterWalletBalance.toFixed(2) }}
</span>
</template>
</el-table-column>
<el-table-column
prop="time"
label="操作时间"
align="center"
min-width="160"
/>
</el-table>
<!-- 分页 -->
<el-pagination
@size-change="handleWithdrawSizeChange"
@current-change="handleWithdrawCurrentChange"
:current-page="withdrawPageInfo.pageNumber"
:page-sizes="[10, 20, 50, 100]"
:page-size="withdrawPageInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="withdrawPageInfo.total"
class="pagination-container"
/>
</el-tab-pane>
</el-tabs>
</el-card>
<!-- 提取到钱包弹窗 -->
<el-dialog
title="提取可用金额到钱包余额"
:visible.sync="depositDialogVisible"
width="500px"
:close-on-click-modal="false"
>
<el-form
ref="depositForm"
:model="depositForm"
:rules="depositRules"
label-width="100px"
>
<el-form-item label="提取金额" prop="amount">
<el-input-number
v-model="depositForm.amount"
:min="0.01"
:max="walletInfo.availableAmount"
:precision="2"
:step="0.01"
controls-position="right"
style="width: 100%"
placeholder="请输入提取金额"
/>
<div style="margin-top: 8px; font-size: 12px; color: #999">
当前可用金额¥{{ walletInfo.availableAmount.toFixed(2) }}
</div>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="depositDialogVisible = false">取消</el-button>
<el-button
type="primary"
@click="handleDepositSubmit"
:loading="depositSubmitLoading"
>
确定
</el-button>
</div>
</el-dialog>
<!-- 提现到银行卡弹窗 -->
<el-dialog
title="提现钱包金额到银行卡"
:visible.sync="withdrawDialogVisible"
width="500px"
:close-on-click-modal="false"
>
<el-form
ref="withdrawForm"
:model="withdrawForm"
:rules="withdrawRules"
label-width="100px"
>
<el-form-item label="提现金额" prop="amount">
<el-input-number
v-model="withdrawForm.amount"
:min="0.01"
:max="walletInfo.walletBalance"
:precision="2"
:step="0.01"
controls-position="right"
style="width: 100%"
placeholder="请输入提现金额"
/>
<div style="margin-top: 8px; font-size: 12px; color: #999">
当前钱包余额¥{{ walletInfo.walletBalance.toFixed(2) }}
</div>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="withdrawDialogVisible = false">取消</el-button>
<el-button
type="primary"
@click="handleWithdrawSubmit"
:loading="withdrawSubmitLoading"
>
确定
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: "WalletManagement",
data() {
//
const validateDepositAmount = (rule, value, callback) => {
if (!value || value <= 0) {
callback(new Error("请输入有效的金额"));
} else if (value > this.walletInfo.availableAmount) {
callback(new Error("可用金额不足"));
} else {
callback();
}
};
const validateWithdrawAmount = (rule, value, callback) => {
if (!value || value <= 0) {
callback(new Error("请输入有效的金额"));
} else if (value > this.walletInfo.walletBalance) {
callback(new Error("钱包余额不足"));
} else {
callback();
}
};
return {
//
activeTab: "deposit",
//
walletInfo: {
availableAmount: 0,
walletBalance: 0
},
//
depositDialogVisible: false,
depositSubmitLoading: false,
depositForm: {
amount: null
},
depositRules: {
amount: [
{ required: true, message: "请输入提取金额", trigger: "blur" },
{ validator: validateDepositAmount, trigger: "blur" }
]
},
//
withdrawDialogVisible: false,
withdrawSubmitLoading: false,
withdrawForm: {
amount: null
},
withdrawRules: {
amount: [
{ required: true, message: "请输入提现金额", trigger: "blur" },
{ validator: validateWithdrawAmount, trigger: "blur" }
]
},
//
depositDataList: [],
//
depositPageInfo: {
pageNumber: 1,
pageSize: 10,
total: 0
},
//
depositLoading: false,
//
withdrawDataList: [],
//
withdrawPageInfo: {
pageNumber: 1,
pageSize: 10,
total: 0
},
//
withdrawLoading: false
};
},
created() {
this.fetchWalletInfo();
this.fetchDepositData();
},
methods: {
//
async fetchWalletInfo() {
try {
// TODO: API
// const res = await this.$api.mer_admin.getWalletInfo();
// this.walletInfo = res.data.data;
//
this.walletInfo = {
availableAmount: 5000.0,
walletBalance: 2000.0
};
} catch (error) {
console.error("获取钱包信息失败:", error);
this.$message.error("获取钱包信息失败");
}
},
//
handleTabClick(tab) {
if (tab.name === "deposit") {
this.fetchDepositData();
} else if (tab.name === "withdraw") {
this.fetchWithdrawData();
}
},
// ==================== ====================
//
openDepositDialog() {
this.depositForm.amount = null;
this.depositDialogVisible = true;
this.$nextTick(() => {
if (this.$refs.depositForm) {
this.$refs.depositForm.clearValidate();
}
});
},
//
handleDepositSubmit() {
this.$refs.depositForm.validate(valid => {
if (valid) {
this.executeDeposit();
}
});
},
//
async executeDeposit() {
this.depositSubmitLoading = true;
try {
const amount = this.depositForm.amount;
// TODO: API
// const params = {
// amount: amount
// };
// await this.$api.mer_admin.depositToWallet(params);
// API
await new Promise(resolve => setTimeout(resolve, 1000));
//
this.walletInfo.availableAmount -= amount;
this.walletInfo.walletBalance += amount;
this.$message.success("提取成功");
this.depositDialogVisible = false;
//
this.fetchWalletInfo();
this.fetchDepositData();
} catch (error) {
console.error("提取失败:", error);
this.$message.error("提取失败,请重试");
} finally {
this.depositSubmitLoading = false;
}
},
//
fetchDepositData() {
this.depositLoading = true;
const marketId = this.marketId;
if (!marketId) {
this.$message.error("获取市场ID失败请重新登录");
this.$router.push("/login");
return;
}
const params = {
pageNumber: this.depositPageInfo.pageNumber,
pageSize: this.depositPageInfo.pageSize,
marketId
};
// TODO: API
// this.$api.mer_admin
// .getDepositList(params)
// .then(res => {
// if (res.data && res.data.data) {
// this.depositDataList = res.data.data.data || [];
// this.depositPageInfo.total = Number(res.data.data.total) || 0;
// }
// })
// .catch(err => {
// console.error(":", err);
// this.$message.error("");
// })
// .finally(() => {
// this.depositLoading = false;
// });
//
setTimeout(() => {
const mockData = [
{
amount: 1000.0,
time: "2025-10-30 10:15:42",
afterAvailableAmount: 4000.0,
afterWalletBalance: 2000.0
},
{
amount: 1500.0,
time: "2025-10-29 09:45:33",
afterAvailableAmount: 3500.0,
afterWalletBalance: 3500.0
},
{
amount: 2000.0,
time: "2025-10-28 14:22:18",
afterAvailableAmount: 3000.0,
afterWalletBalance: 2000.0
}
];
this.depositDataList = mockData;
this.depositPageInfo.total = mockData.length;
this.depositLoading = false;
}, 500);
},
//
handleDepositSizeChange(val) {
this.depositPageInfo.pageSize = val;
this.depositPageInfo.pageNumber = 1;
this.fetchDepositData();
},
//
handleDepositCurrentChange(val) {
this.depositPageInfo.pageNumber = val;
this.fetchDepositData();
},
// ==================== ====================
//
openWithdrawDialog() {
this.withdrawForm.amount = null;
this.withdrawDialogVisible = true;
this.$nextTick(() => {
if (this.$refs.withdrawForm) {
this.$refs.withdrawForm.clearValidate();
}
});
},
//
handleWithdrawSubmit() {
this.$refs.withdrawForm.validate(valid => {
if (valid) {
this.executeWithdraw();
}
});
},
//
async executeWithdraw() {
this.withdrawSubmitLoading = true;
try {
const amount = this.withdrawForm.amount;
// TODO: API
// const params = {
// amount: amount
// };
// await this.$api.mer_admin.withdrawToBank(params);
// API
await new Promise(resolve => setTimeout(resolve, 1000));
//
this.walletInfo.walletBalance -= amount;
this.$message.success("提现成功");
this.withdrawDialogVisible = false;
//
this.fetchWalletInfo();
this.fetchWithdrawData();
} catch (error) {
console.error("提现失败:", error);
this.$message.error("提现失败,请重试");
} finally {
this.withdrawSubmitLoading = false;
}
},
//
fetchWithdrawData() {
this.withdrawLoading = true;
const marketId = this.marketId;
if (!marketId) {
this.$message.error("获取市场ID失败请重新登录");
this.$router.push("/login");
return;
}
const params = {
pageNumber: this.withdrawPageInfo.pageNumber,
pageSize: this.withdrawPageInfo.pageSize,
marketId
};
// TODO: API
// this.$api.mer_admin
// .getWithdrawList(params)
// .then(res => {
// if (res.data && res.data.data) {
// this.withdrawDataList = res.data.data.data || [];
// this.withdrawPageInfo.total = Number(res.data.data.total) || 0;
// }
// })
// .catch(err => {
// console.error(":", err);
// this.$message.error("");
// })
// .finally(() => {
// this.withdrawLoading = false;
// });
//
setTimeout(() => {
const mockData = [
{
amount: 500.0,
time: "2025-10-30 15:30:25",
afterWalletBalance: 1500.0
},
{
amount: 800.0,
time: "2025-10-29 16:20:10",
afterWalletBalance: 1200.0
}
];
this.withdrawDataList = mockData;
this.withdrawPageInfo.total = mockData.length;
this.withdrawLoading = false;
}, 500);
},
//
handleWithdrawSizeChange(val) {
this.withdrawPageInfo.pageSize = val;
this.withdrawPageInfo.pageNumber = 1;
this.fetchWithdrawData();
},
//
handleWithdrawCurrentChange(val) {
this.withdrawPageInfo.pageNumber = val;
this.fetchWithdrawData();
}
},
computed: {
// ID
marketId() {
return this.$store.state.userData.marketId;
}
}
};
</script>
<style lang="scss" scoped>
.page-container {
padding: 0;
}
//
.stats-row {
margin-bottom: 16px;
}
.stat-card {
border-radius: 8px;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
}
::v-deep .el-card__body {
padding: 24px;
}
&.stat-card-available {
border-left: 4px solid #52c41a;
}
&.stat-card-balance {
border-left: 4px solid #1890ff;
}
}
.stat-content {
text-align: center;
}
.stat-label {
font-size: 14px;
color: #666;
margin-bottom: 12px;
}
.stat-value {
font-size: 32px;
font-weight: 600;
color: #1890ff;
}
//
.action-card {
margin-bottom: 16px;
border-radius: 8px;
::v-deep .el-card__body {
padding: 20px 24px;
}
}
.button-group {
display: flex;
gap: 12px;
}
//
.tabs-card {
border-radius: 8px;
::v-deep .el-card__body {
padding: 0;
}
::v-deep .el-tabs__header {
margin: 0;
padding: 0 24px;
background: #fafafa;
}
::v-deep .el-tabs__nav-wrap::after {
height: 1px;
}
::v-deep .el-tabs__item {
height: 48px;
line-height: 48px;
font-size: 15px;
}
::v-deep .el-tabs__content {
padding: 0;
}
}
//
::v-deep .el-table {
margin: 0;
}
//
.pagination-container {
padding: 20px 24px;
text-align: right;
border-top: 1px solid #f0f0f0;
}
//
.dialog-footer {
text-align: right;
}
</style>