This commit is contained in:
2026-10-08 18:19:39 +08:00
parent 25b8d0104f
commit 75527ae81d
425 changed files with 53925 additions and 2645 deletions
+574
View File
@@ -0,0 +1,574 @@
<template>
<view class="page-body">
<view class="overview-card">
<view class="overview-header">
<view class="summary-main">
<view class="summary-label">总余额</view>
<view class="summary-amount">¥{{ formatMoney(totalBalance) }}</view>
<view class="summary-time" v-if="balanceInfo.updateTime">最近更新时间:{{ balanceInfo.updateTime }}</view>
</view>
<el-button type="text" class="toggle-button" @click="toggleMerchantList">
{{ merchantExpanded ? "收起明细" : "展开明细" }}
</el-button>
</view>
<view class="merchant-list" v-show="merchantExpanded">
<view class="merchant-row" v-for="item in balanceInfoList" :key="item.accNo">
<view class="merchant-info">
<view class="merchant-name">{{ item.merchantName }}</view>
<view class="merchant-meta">{{ item.accNo }}</view>
<view class="merchant-meta" v-if="item.updateTime">更新时间:{{ item.updateTime }}</view>
</view>
<view class="merchant-side">
<view class="merchant-balance">¥{{ formatMoney(item.balance) }}</view>
<view class="summary-actions">
<el-button size="small" :loading="balanceQuery[item.accNo]" @click="queryBalance(item)">查询余额</el-button>
<el-button type="warning" size="small" icon="el-icon-wallet"
:disabled="Number(item.balance || 0) <= 0"
@click="openWithdrawDialog(item)">提现</el-button>
</view>
</view>
</view>
</view>
</view>
<vk-data-table-query v-model="queryForm1.formData" :columns="queryForm1.columns"
@search="search"></vk-data-table-query>
<vk-data-table ref="table1" :action="table1.action" :columns="table1.columns" :query-form-param="queryForm1"
:pagination="true"
@current-change="currentChange"
@selection-change="selectionChange">
<template v-slot:action="{ row }">
<el-button v-if="row.srcReqId" type="primary" size="mini" @click="queryActionLog(row)">查询状态</el-button>
</template>
</vk-data-table>
<el-dialog title="提现" :visible.sync="withdrawDialog.show" width="520px" :close-on-click-modal="false">
<el-form label-width="90px">
<el-form-item label="提现企业">
<view class="withdraw-company">{{ withdrawDialog.form.merchantName || "-" }}</view>
</el-form-item>
<el-form-item label="提现账号">
<view class="withdraw-company">{{ withdrawDialog.form.merchantId || "-" }}</view>
</el-form-item>
<el-form-item label="当前余额">
<view class="withdraw-balance">¥{{ formatMoney(withdrawDialog.form.balance) }}</view>
</el-form-item>
<el-form-item label="提现金额" required>
<el-input-number v-model="withdrawDialog.form.amount" :min="0" :precision="2" :step="100"
controls-position="right" style="width: 100%;"></el-input-number>
</el-form-item>
<el-form-item label="说明">
<el-input v-model="withdrawDialog.form.description" type="textarea" :rows="4" maxlength="100"
show-word-limit placeholder="请输入提现说明"></el-input>
</el-form-item>
</el-form>
<template v-slot:footer>
<el-button @click="withdrawDialog.show = false">取消</el-button>
<el-button type="primary" :loading="withdrawDialog.loading" @click="submitWithdraw">确认提现</el-button>
</template>
</el-dialog>
</view>
</template>
<script>
var that;
var vk = uni.vk;
const typeData = [
{ value: "income", label: "收入" },
{ value: "expense", label: "支出" }
];
const merchantOptions = [
{ value: "zg000002466577", accNo: "zg000002466577", label: "广西金久信电子商务有限公司" },
{ value: "zg000002460927", accNo: "zg000002460927", label: "南宁亿利通钢管有限公司" },
{ value: "zg000002494918", accNo: "zg000002494918", label: "广西冀旺贸易有限公司" },
{ value: "zg000002459804", accNo: "zg000002459804", label: "广西金久信电子商务有限公司" }
];
export default {
computed: {
totalBalance() {
return this.balanceInfoList.reduce((total, item) => {
return total + Number(item.balance || 0);
}, 0);
}
},
data() {
return {
loading: false,
data: {},
merchantOptions,
balanceInfo: {
balance: 0,
updateTime: ""
},
merchantExpanded: true,
balanceInfoList: merchantOptions.map(item => ({
accNo: item.accNo,
merchantName: item.label,
balance: 0,
updateTime: ""
})),
table1: {
action: "admin/finance/balance/sys/getList",
columns: [
{
key: "description",
title: "说明",
type: "text",
align: "left",
minWidth: 240
},
{
key: "type",
title: "类型",
type: "select",
width: 120,
data: typeData
},
{
key: "amount",
title: "金额",
type: "text",
width: 140,
formatter: (val, row) => {
const sign = row.type === "income" ? "+" : "-";
return `${sign}¥${that.formatMoney(val)}`;
}
},
{
key: "logTime",
title: "时间",
type: "time",
width: 180,
sortable: "custom"
},
{
key: "_add_time",
title: "创建时间",
type: "time",
width: 180,
sortable: "custom"
},
{
key: "status",
title: "状态",
type: "text",
width: 120,
formatter: (val) => {
return that.formatStatus(val);
}
},
{
key: "action",
title: "操作",
type: "html",
width: 140
}
],
multipleSelection: [],
selectItem: ""
},
queryForm1: {
formData: {},
columns: [
{
key: "type",
title: "类型",
type: "select",
mode: "=",
data: typeData
},
{
key: "logTime",
title: "时间",
type: "datetimerange",
width: 380,
mode: "[]"
}
]
},
withdrawDialog: {
show: false,
loading: false,
form: {
merchantId: "",
merchantName: "",
balance: 0,
amount: 0,
description: ""
}
},
balanceQuery: merchantOptions.reduce((map, item) => {
map[item.accNo] = false;
return map;
}, {})
};
},
onLoad(options = {}) {
that = this;
vk = that.vk;
that.options = options;
that.init(options);
},
methods: {
init() {
that.getBalanceSummary();
},
search() {
that.$refs.table1.search();
that.getBalanceSummary();
},
refresh() {
that.$refs.table1.refresh();
that.getBalanceSummary();
},
toggleMerchantList() {
that.merchantExpanded = !that.merchantExpanded;
},
currentChange(val) {
that.table1.selectItem = val;
},
selectionChange(list) {
that.table1.multipleSelection = list;
},
async getBalanceSummary() {
const { data = {} } = await vk.callFunction({
url: "admin/finance/balance/sys/getSummary",
data: {}
});
that.balanceInfo = {
balance: 0,
updateTime: data.updateTime || ""
};
const serverList = Array.isArray(data.balanceInfoList) ? data.balanceInfoList : [];
const serverMap = serverList.reduce((map, item) => {
map[item.accNo] = item;
return map;
}, {});
that.balanceInfoList = merchantOptions.map(item => {
const current = serverMap[item.accNo] || {};
return {
accNo: item.accNo,
merchantName: current.merchantName || item.label,
balance: Number(current.balance || 0),
updateTime: current.updateTime || ""
};
});
},
async queryBalance(item) {
const accNo = item && item.accNo;
if (!accNo) {
vk.toast("未配置提现账号");
return;
}
that.balanceQuery[accNo] = true;
try {
await vk.callFunction({
url: "admin/finance/balance/sys/getInfo",
data: {
accNo
}
});
await that.getBalanceSummary();
vk.toast(`${item.merchantName}余额已更新`);
} finally {
that.balanceQuery[accNo] = false;
}
},
openWithdrawDialog(item) {
if (Number(item.balance || 0) <= 0) {
vk.toast("当前企业余额小于等于0,无法提现");
return;
}
that.withdrawDialog.form = {
merchantId: item.accNo,
merchantName: item.merchantName,
balance: Number(item.balance || 0),
amount: 0,
description: ""
};
that.withdrawDialog.show = true;
},
async queryActionLog(row) {
const accNo = String(row && (row.merchantId || row.accNo) || "").trim();
const reqJournalNo = String(row && row.srcReqId || "").trim();
const reqDate = that.formatReqDate(row && row._add_time);
if (!accNo) {
vk.toast("账号不能为空");
return;
}
if (!reqJournalNo) {
vk.toast("srcReqId不能为空");
return;
}
if (!reqDate) {
vk.toast("日期格式不正确");
return;
}
try {
const res = await vk.request({
method: "POST",
url: "http://jjx-union-pay.gxwebsoft.com/api/union-pay-jjx/getActionLog",
header: {
"content-type": "application/json; charset=utf-8"
},
data: {
accNo,
reqDate,
reqJournalNo
},
timeout: 60000
});
const code = Number(res && res.code);
const success = res && (
code === 0 ||
code === 200 ||
res.success === true ||
(typeof res.code === "undefined" && !res.msg && !res.message)
);
if (!success) {
throw new Error(
(res && (res.msg || res.message)) ||
(res && res.content && (res.content.msg || res.content.message)) ||
"查询状态失败"
);
}
const status = that.getActionStatusValue(res);
const statusText = that.formatStatus(status);
if (status !== "" && row && row._id) {
row.status = status;
}
vk.toast(statusText || "未识别到状态");
console.log("getActionLog", res);
} catch (err) {
vk.toast((err && err.message) || "查询状态失败");
}
},
async submitWithdraw() {
const { amount, description, merchantId, balance } = that.withdrawDialog.form;
if (!merchantId) {
vk.toast("提现账号不能为空");
return;
}
if (!amount || Number(amount) <= 0) {
vk.toast("提现金额必须大于0");
return;
}
if (Number(balance || 0) <= 0) {
vk.toast("当前企业余额小于等于0,无法提现");
return;
}
if (Number(amount) > Number(balance || 0)) {
vk.toast("提现金额不能大于当前企业余额");
return;
}
that.withdrawDialog.loading = true;
try {
await vk.callFunction({
url: "admin/finance/balance/sys/withdraw",
data: {
merchantId,
amount: Number(amount),
description
}
});
that.withdrawDialog.show = false;
that.refresh();
vk.toast("提现成功");
} finally {
that.withdrawDialog.loading = false;
}
},
formatMoney(value) {
const amount = Number(value || 0);
return amount.toFixed(2);
},
formatReqDate(value) {
const raw = value || "";
const date = typeof raw === "number"
? new Date(raw)
: new Date(String(raw).replace(/-/g, "/"));
if (Number.isNaN(date.getTime())) {
return "";
}
return vk.pubfn.timeFormat(date, "yyyyMMdd");
},
getActionStatusValue(res) {
const orderSet = this.pickOrderSet(res);
if (!orderSet) {
return "";
}
let orderList = [];
try {
orderList = JSON.parse(orderSet);
} catch (err) {
console.log("orderSet parse error", err, orderSet);
return "";
}
if (!Array.isArray(orderList) || !orderList.length) {
return "";
}
const firstOrder = orderList[0] || {};
const status = Number(firstOrder.status);
return Number.isFinite(status) ? status : "";
},
formatStatus(status) {
const statusMap = {
1: "处理中",
2: "成功",
3: "处理失败",
5: "退票",
6: "退票重提"
};
return statusMap[Number(status)] || "";
},
pickOrderSet(res) {
const candidates = [
res && res.orderSet,
res && res.data && res.data.orderSet,
res && res.info && res.info.orderSet,
res && res.content && res.content.orderSet,
res && res.data && Array.isArray(res.data) && res.data[0] && res.data[0].orderSet,
res && res.list && Array.isArray(res.list) && res.list[0] && res.list[0].orderSet,
res && res.rows && Array.isArray(res.rows) && res.rows[0] && res.rows[0].orderSet,
res && res.data && res.data.list && Array.isArray(res.data.list) && res.data.list[0] && res.data.list[0].orderSet,
res && res.data && res.data.rows && Array.isArray(res.data.rows) && res.data.rows[0] && res.data.rows[0].orderSet
];
for (let i = 0; i < candidates.length; i++) {
if (typeof candidates[i] === "string" && candidates[i]) {
return candidates[i];
}
}
return "";
}
}
};
</script>
<style lang="scss" scoped>
.page-body {
.overview-card {
display: flex;
flex-direction: column;
padding: 24rpx 32rpx;
margin-bottom: 24rpx;
border-radius: 16rpx;
background: linear-gradient(135deg, #fff7e6 0%, #fff1d6 100%);
border: 1px solid #f7d8a8;
}
.overview-header {
width: 100%;
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 24rpx;
padding-bottom: 24rpx;
margin-bottom: 24rpx;
border-bottom: 1px solid rgba(194, 106, 0, 0.12);
}
.toggle-button {
flex-shrink: 0;
padding: 0;
}
.merchant-list {
display: flex;
flex-direction: column;
gap: 20rpx;
width: 100%;
}
.merchant-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24rpx;
padding: 20rpx 0;
border-bottom: 1px solid rgba(194, 106, 0, 0.12);
}
.merchant-row:last-child {
padding-bottom: 0;
border-bottom: none;
}
.merchant-info {
min-width: 0;
flex: 1;
}
.merchant-side {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 12rpx;
flex-shrink: 0;
}
.summary-main {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.summary-actions {
display: flex;
align-items: center;
gap: 16rpx;
flex-shrink: 0;
}
.summary-label {
font-size: 26rpx;
color: #8c6b2f;
}
.merchant-name {
font-size: 28rpx;
font-weight: 600;
color: #8c6b2f;
}
.merchant-meta {
font-size: 24rpx;
color: #9f8252;
word-break: break-all;
}
.summary-amount {
font-size: 52rpx;
line-height: 1.1;
font-weight: 700;
color: #c26a00;
}
.merchant-balance {
font-size: 34rpx;
line-height: 1.1;
font-weight: 700;
color: #c26a00;
}
.summary-time {
font-size: 24rpx;
color: #9f8252;
}
.withdraw-balance {
font-size: 32rpx;
font-weight: 600;
color: #c26a00;
}
.withdraw-company {
color: #8c6b2f;
word-break: break-all;
}
}
</style>