Commit 124f05e2 by zhuzhequan

feat:需求功能完善

parent 6b13a992
import axios from '../axios'
/**
* 线下充值 - 请求方法(SAAS 端)
* 接口文档:@e:\zzq\saas-manage\图\jomalls-mange端账户相关接口.md
* 基础路径:/api/offline-recharge(需 token 校验)
*/
const BASE = '/offline-recharge'
// ==================== 1. 左边栏状态统计 ====================
// POST /api/offline-recharge/status
// 响应:[ { code, remark, num } ]
export function getOfflineRechargeStatus() {
return axios.post(`${BASE}/status`)
}
// ==================== 2. 运营初审 ====================
// POST /api/offline-recharge/firstAudit
// 请求体 OfflineRechargeRecordParam:{ id, audit(boolean), rejectReason? }
export function firstAudit(data) {
return axios.post(`${BASE}/firstAudit`, data)
}
// ==================== 3. 财务复审 ====================
// POST /api/offline-recharge/secondAudit
// 请求体 OfflineRechargeRecordParam:{ id, audit(boolean), rejectReason?, actualAmount?, serviceFee?, financeBillUrl?, financeRemark? }
export function secondAudit(data) {
return axios.post(`${BASE}/secondAudit`, data)
}
// 状态码 -> 文案 映射
export const OFFLINE_RECHARGE_STATUS_MAP = {
OPER_APPROVED: '运营初审',
FIN_APPROVED: '财务复审',
SETTLED_AMOUNT: '充值成功',
WAIT_UPDATE: '充值失败'
}
import axios from '../axios'
/**
* 付款账户管理 - 请求方法
* 说明:后端接口暂未联调,当前数据使用本地 mock;
* 联调时只需将函数体内的 mock 逻辑替换为真实 axios 请求即可(URL 已注释)。
*/
// ==================== mock 数据 ====================
let paymentAccountList = [
{
id: 1,
accountName: '九猫科技团队CNY账户',
accountNo: 'CNY260804001',
balance: 12800.5,
totalRecharge: 20000,
arrears: 0,
currency: 'CNY',
creator: '张三',
createTime: '2026-08-04 10:20:33',
hasDetail: true
},
{
id: 2,
accountName: '一衣蔓蔓团队CNY账户',
accountNo: 'CNY260804002',
balance: 5632,
totalRecharge: 8000,
arrears: 0,
currency: 'CNY',
creator: '张三',
createTime: '2026-08-04 10:25:11',
hasDetail: false
},
{
id: 3,
accountName: '一本万利团队CNY账户',
accountNo: 'CNY260804003',
balance: 0,
totalRecharge: 5000,
arrears: 300,
currency: 'CNY',
creator: '李四',
createTime: '2026-08-04 10:30:45',
hasDetail: true
},
{
id: 4,
accountName: '九猫海外业务USD账户',
accountNo: 'USD260804001',
balance: 2200,
totalRecharge: 5000,
arrears: 0,
currency: 'USD',
creator: '李四',
createTime: '2026-08-05 09:12:08',
hasDetail: false
}
]
let rechargeRecordList = [
{
id: 1,
accountId: 1,
rechargeNo: 'CZ20260805001',
status: '充值成功',
payType: '支付宝扫码充值',
currency: 'CNY',
amount: 10000,
serviceFee: 0,
actualAmount: 10000,
remark: '线下充值',
createTime: '2026-08-05 14:00:00'
},
{
id: 2,
accountId: 1,
rechargeNo: 'CZ20260804002',
status: '充值中',
payType: '线下充值',
currency: 'CNY',
amount: 5000,
serviceFee: 0,
actualAmount: 5000,
remark: '待审核',
createTime: '2026-08-04 18:30:00'
},
{
id: 3,
accountId: 4,
rechargeNo: 'CZ20260805002',
status: '充值失败',
payType: '线下充值',
currency: 'USD',
amount: 3000,
serviceFee: 0,
actualAmount: 3000,
remark: '水单不清晰',
createTime: '2026-08-05 16:00:00'
}
]
let operationLogList = [
{
id: 1,
accountId: 1,
createTime: '2026-08-05 14:22:10',
description:
'XXX审核充值单通过,充值单号为:CZ20260805001,入账金额为:10000¥'
},
{
id: 2,
accountId: 1,
createTime: '2026-08-04 15:40:55',
description:
'XXX对账户九猫科技团队CNY账户进行手动调整,调整金额为:5000$,备注:测试调整;'
},
{
id: 3,
accountId: 1,
createTime: '2026-08-04 10:20:33',
description:
'demo客户XXX添加了一个付款账户,账户名为:九猫科技团队CNY账户,账户币种为:CNY;'
}
]
// ==================== 工具 ====================
const sleep = (ms = 300) => new Promise((resolve) => setTimeout(resolve, ms))
const ok = (data, message = '操作成功') => ({ code: 200, data, message })
// 生成账号:币种 + 年月日 + 三位数,如 CNY260805001
export function genAccountNo(currency) {
const now = new Date()
const ymd =
`${now.getFullYear()}` +
`${String(now.getMonth() + 1).padStart(2, '0')}` +
`${String(now.getDate()).padStart(2, '0')}`
const suffix = String(Math.floor(Math.random() * 900) + 100)
return `${currency}${ymd}${suffix}`
}
// ==================== 请求方法 ====================
// 分页查询付款账户列表
export async function getPaymentAccountPage(params) {
await sleep()
let list = [...paymentAccountList]
if (params.accountName) {
list = list.filter((item) => item.accountName.includes(params.accountName))
}
if (params.accountNo) {
list = list.filter((item) => item.accountNo.includes(params.accountNo))
}
if (params.currency) {
list = list.filter((item) => item.currency === params.currency)
}
const { currentPage = 1, pageSize = 10 } = params
const start = (currentPage - 1) * pageSize
const records = list.slice(start, start + pageSize)
// 真实接口: axios.post('/customer/paymentAccount/pageList', params)
return ok({ records, total: list.length })
}
// 新增付款账户
export async function addPaymentAccount(params) {
await sleep()
paymentAccountList.unshift({
id: Date.now(),
...params,
balance: 0,
totalRecharge: 0,
arrears: 0,
creator: '当前用户',
createTime: '2026-08-06 09:00:00',
hasDetail: false
})
operationLogList.unshift({
id: Date.now() + 1,
accountId: paymentAccountList[0].id,
createTime: '2026-08-06 09:00:00',
description: `demo客户XXX添加了一个付款账户,账户名为:${params.accountName},账户币种为:${params.currency};`
})
// 真实接口: axios.post('/customer/paymentAccount/add', params)
return ok(null, '新增成功')
}
// 编辑付款账户
export async function updatePaymentAccount(params) {
await sleep()
const target = paymentAccountList.find((item) => item.id === params.id)
if (target) {
target.accountName = params.accountName
}
// 真实接口: axios.post('/customer/paymentAccount/update', params)
return ok(null, '编辑成功')
}
// 手动调整账户余额(amount 支持正负数)
export async function adjustPaymentAccount(params) {
await sleep()
const target = paymentAccountList.find((item) => item.id === params.id)
if (target) {
target.balance = Number((target.balance + params.amount).toFixed(2))
if (target.balance < 0) {
target.arrears = Number(Math.abs(target.balance).toFixed(2))
} else {
target.arrears = 0
}
}
operationLogList.unshift({
id: Date.now(),
accountId: params.id,
createTime: '2026-08-06 09:30:00',
description: `XXX对账户${params.accountName}进行手动调整,调整金额为:${params.amount}$,备注:${params.remark || '无'};`
})
// 真实接口: axios.post('/customer/paymentAccount/adjust', params)
return ok(null, '调整成功')
}
// 删除付款账户
export async function deletePaymentAccount(params) {
await sleep()
const target = paymentAccountList.find((item) => item.id === params.id)
if (target && target.hasDetail) {
return {
code: 500,
message: `您要删除的账户${target.accountName}(${target.accountNo})存在消费或充值明细,无法进行删除操作!`,
data: null
}
}
paymentAccountList = paymentAccountList.filter((item) => item.id !== params.id)
// 真实接口: axios.post('/customer/paymentAccount/delete', params)
return ok(null, '删除成功')
}
// 分页查询充值记录
export async function getRechargeRecordPage(params) {
await sleep()
let list = rechargeRecordList.filter((item) => item.accountId === params.accountId)
const { currentPage = 1, pageSize = 10 } = params
const start = (currentPage - 1) * pageSize
const records = list.slice(start, start + pageSize)
// 真实接口: axios.post('/customer/paymentAccount/rechargeRecord/pageList', params)
return ok({ records, total: list.length })
}
// 分页查询操作日志
export async function getOperationLogPage(params) {
await sleep()
let list = operationLogList
.filter((item) => item.accountId === params.accountId)
.sort((a, b) => (a.createTime < b.createTime ? 1 : -1))
const { currentPage = 1, pageSize = 10 } = params
const start = (currentPage - 1) * pageSize
const records = list.slice(start, start + pageSize)
// 真实接口: axios.post('/customer/paymentAccount/operationLog/pageList', params)
return ok({ records, total: list.length })
}
......@@ -562,8 +562,8 @@ export default {
highlight-hover-row
show-header={this.showHeader}
onCell-click={this.cellClick}
show-header-overflow
show-overflow
show-header-overflow="title"
show-overflow="title"
height="100%"
scroll-x={{ gt: 30 }}
scroll-y={{ gt: 30, enabled: this.virtualScroll }}
......
......@@ -92,6 +92,10 @@
:commandTitle="commandTitle"
:erpStatus="erpStatus"
ref="customerRef" />
<paymentAccountManageDialog
:visible.sync="paymentAccountDialog"
:customer-id="paymentAccountRow.id"
:customer-name="paymentAccountRow.companyName || ''" />
<el-dialog
title="操作日志"
:close-on-click-modal="false"
......@@ -160,6 +164,7 @@
import tableView from '@/common/components/base/tableView.vue'
import pagination from '../../mixins/pagination'
import editCustmerDialog from './editCustmerDialog.vue'
import paymentAccountManageDialog from './paymentAccountManageDialog.vue'
import { get, post, remove } from '@/common/api/axios'
import { mapState } from 'vuex'
import { addGiftOrders } from '@/common/api/manage/index'
......@@ -168,7 +173,8 @@ export default {
mixins: [pagination],
components: {
tableView,
editCustmerDialog
editCustmerDialog,
paymentAccountManageDialog
},
data() {
return {
......@@ -186,6 +192,8 @@ export default {
logVisible: false,
giftOrderVisible: false,
logList: [],
paymentAccountDialog: false,
paymentAccountRow: {},
searchForm: {
companyName: '',
contactUserName: '',
......@@ -383,13 +391,22 @@ export default {
},
{
label: '订单账户',
key: 'createTime',
width: 130
key: 'orderAccount',
width: 130,
align: 'center',
render: (row) => (
<div class="order-account-cell">
<span class="balance">{Number(row.orderBalance || 0)}</span>
<span class="separator"> / </span>
<span class="recharge">{Number(row.orderAccumulatedRecharge || 0)}</span>
</div>
)
},
{
label: '付款账户数量',
key: 'createTime',
width: 130
key: 'paymentAccountCount',
width: 120,
align: 'center'
},
{
label: '操作',
......@@ -481,6 +498,13 @@ export default {
</el-button>
</el-dropdown-item>
)}
<el-dropdown-item command="paymentAccount">
<el-button
type="text"
style={{ color: '#2776ce', fontWeight: 'bold' }}>
管理付款账户
</el-button>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
......@@ -502,7 +526,7 @@ export default {
})
},
getStatusList() {
get('customer/info/serviceStatusList')
get('customer/info/service-status-list')
.then((res) => {
if (res.code !== 200) return
this.serviceStatusLists = res.data || []
......@@ -533,6 +557,10 @@ export default {
case 'log':
this.logInfo(row)
break
case 'paymentAccount':
this.paymentAccountRow = row
this.paymentAccountDialog = true
break
}
},
sizeChange(pageSize) {
......@@ -546,7 +574,7 @@ export default {
async getTableList() {
this.loading = true
const { pageSize, currentPage } = this.centerPageOptions
post('customer/info/pageList', {
post('customer/info/page-list', {
pageSize,
currentPage,
...this.searchForm
......@@ -619,8 +647,8 @@ export default {
openErp: () => {
url =
this.erpStatus === '未开通'
? 'customer/info/openService'
: 'customer/info/reOpenService'
? 'customer/info/open-service'
: 'customer/info/re-open-service'
params.serviceType = 1
params.customerId = editForm.id
params.domain = fullDomain
......@@ -750,7 +778,7 @@ export default {
},
async logInfo(v) {
try {
const res = await get(`customer/info/operationLog/${v.id}`)
const res = await get(`customer/info/operation-log/${v.id}`)
if (res.code !== 200) return
this.logList = res.data || []
this.logVisible = true
......@@ -767,6 +795,18 @@ export default {
display: flex;
overflow: hidden;
}
.order-account-cell {
font-size: 12px;
color: #303133;
.balance,
.recharge {
color: #303133;
font-weight: 600;
}
.separator {
color: #303133;
}
}
.page_right {
flex: 1;
display: flex;
......
<template>
<el-dialog
:visible.sync="dialogVisible"
:title="`管理付款账户 - ${customerName}`"
width="1200px"
:close-on-click-modal="false"
append-to-body
@closed="handleClosed">
<!-- 筛选区 -->
<el-form
:inline="true"
size="mini"
:model="searchForm"
ref="searchFormRef"
class="search_form">
<el-form-item label="账户名称">
<el-input
v-model="searchForm.accountName"
style="width: 160px"
clearable
placeholder="请输入账户名称" />
</el-form-item>
<el-form-item label="账号">
<el-input
v-model="searchForm.accountNumber"
style="width: 160px"
clearable
placeholder="请输入账号" />
</el-form-item>
<el-form-item label="账户币种">
<el-select
v-model="searchForm.currencyCode"
style="width: 130px"
clearable
placeholder="全部">
<el-option label="CNY" value="CNY" />
<el-option label="USD" value="USD" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
</el-form-item>
<el-form-item>
<el-button type="success" @click="handleAdd">新增账户</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<div class="table-wrap" v-loading="loading">
<table-view
:tableColumns="tableColumns"
:sourceData="sourceData" />
</div>
<div class="pagination">
<el-pagination
layout="total, prev, pager, next, jumper"
background
:total="pageOptions.total"
:page-size="pageOptions.pageSize"
:current-page="pageOptions.currentPage"
@size-change="sizeChange"
@current-change="currentChange" />
</div>
<!-- 新增/编辑账户 -->
<el-dialog
:visible.sync="addEditDialog.visible"
:title="addEditDialog.title"
width="450px"
:close-on-click-modal="false"
append-to-body>
<el-form
:model="addEditForm"
size="small"
ref="addEditFormRef"
label-width="100px">
<el-form-item label="账户名称" prop="accountName"
:rules="{ required: true, message: '请输入账户名称', trigger: 'blur' }">
<el-input
v-model="addEditForm.accountName"
style="width: 250px"
placeholder="请输入账户名称" />
</el-form-item>
<el-form-item label="账户币种" prop="currency"
:rules="{ required: true, message: '请选择账户币种', trigger: 'change' }">
<el-select
v-model="addEditForm.currency"
style="width: 250px"
:disabled="addEditDialog.type === 'edit'"
placeholder="请选择账户币种">
<el-option label="CNY" value="CNY" />
<el-option label="USD" value="USD" />
</el-select>
</el-form-item>
<el-form-item label="账号" v-if="addEditDialog.type === 'edit'">
<el-input
:value="addEditForm.accountNumber"
style="width: 250px"
disabled />
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="addEditDialog.visible = false" size="small">取消</el-button>
<el-button
type="primary"
size="small"
:loading="addEditDialog.loading"
@click="submitAddEdit">
确定
</el-button>
</span>
</el-dialog>
<!-- 手动调整余额 -->
<el-dialog
:visible.sync="adjustDialog.visible"
:title="`手动调整${adjustForm.currency}账户余额`"
width="400px"
:close-on-click-modal="false"
append-to-body>
<el-form
:model="adjustForm"
size="small"
ref="adjustFormRef"
label-width="90px">
<el-form-item label="账户余额" prop="amount"
:rules="[
{ required: true, message: '请输入余额', trigger: 'blur' },
{
validator: validateAmount,
trigger: 'blur'
}
]">
<el-input
v-model="adjustForm.amount"
placeholder="正数加款、负数扣款,如 100 或 -50"
@input="handleAmountInput" />
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="adjustForm.remark"
placeholder="请输入备注" />
</el-form-item>
</el-form>
<span slot="footer" class="adjust-footer">
<el-button @click="adjustDialog.visible = false" size="small">取消</el-button>
<el-button
type="primary"
size="small"
:loading="adjustDialog.loading"
@click="submitAdjust">
确定
</el-button>
</span>
</el-dialog>
<!-- 充值记录 -->
<el-dialog
:visible.sync="rechargeDialog.visible"
:title="`${rechargeDialog.accountName}充值记录 `"
width="1100px"
:close-on-click-modal="false"
append-to-body>
<div class="table-wrap" v-loading="rechargeDialog.loading">
<table-view
:tableColumns="rechargeColumns"
:sourceData="rechargeDialog.list" />
</div>
<div class="pagination">
<el-pagination
layout="total, prev, pager, next, jumper"
background
:total="rechargeDialog.total"
:page-size="rechargeDialog.pageSize"
:current-page="rechargeDialog.currentPage"
@current-change="(p) => loadRechargeRecord(rechargeDialog.accountId, p)" />
</div>
</el-dialog>
<!-- 删除确认 -->
<el-dialog
:visible.sync="deleteDialog.visible"
title="删除付款账户"
width="450px"
:close-on-click-modal="false"
append-to-body>
<div class="delete-tip">
<span>{{ deleteDialog.tip }}</span>
</div>
<span slot="footer">
<el-button @click="deleteDialog.visible = false" size="small">取消</el-button>
<el-button
type="primary"
size="small"
:loading="deleteDialog.loading"
:disabled="deleteDialog.hasDetail"
@click="submitDelete">
确定删除
</el-button>
</span>
</el-dialog>
<!-- 操作日志 -->
<el-dialog
:visible.sync="logDialog.visible"
:title="`操作日志 - ${logDialog.accountName}`"
width="650px"
:close-on-click-modal="false"
append-to-body>
<ul class="log-list" v-loading="logDialog.loading">
<li v-for="item in logDialog.list" :key="item.id">
<span class="log-time">{{ item.createTime }}</span>
<span class="log-desc">{{ item.description }}</span>
</li>
<li v-if="!logDialog.list.length" class="empty">暂无数据</li>
</ul>
</el-dialog>
</el-dialog>
</template>
<script>
import tableView from '@/common/components/base/tableView.vue'
import { get, post, remove } from '@/common/api/axios'
export default {
name: 'paymentAccountManageDialog',
components: { tableView },
props: {
visible: { type: Boolean, default: false },
customerId: { type: [Number, String], default: null },
customerName: { type: String, default: '' }
},
data() {
return {
dialogVisible: false,
loading: false,
searchForm: {
accountName: '',
accountNumber: '',
currencyCode: ''
},
sourceData: [],
pageOptions: {
pageSize: 10,
currentPage: 1,
total: 0
},
// 新增/编辑账户
addEditDialog: {
visible: false,
type: 'add', // add | edit
title: '新增账户',
loading: false
},
addEditForm: {
id: null,
accountName: '',
accountNumber: '',
currency: 'CNY'
},
// 手动调整
adjustDialog: {
visible: false,
loading: false
},
adjustForm: {
id: null,
accountName: '',
currentBalance: 0,
currency: 'CNY',
amount: '',
remark: ''
},
// 充值记录
rechargeDialog: {
visible: false,
accountId: null,
accountName: '',
list: [],
total: 0,
pageSize: 10,
currentPage: 1,
loading: false
},
// 删除确认
deleteDialog: {
visible: false,
hasDetail: false,
tip: '',
id: null,
accountName: '',
accountNumber: '',
loading: false
},
// 操作日志
logDialog: {
visible: false,
accountId: null,
accountName: '',
list: [],
loading: false
}
}
},
computed: {
tableColumns() {
return [
{ label: '账户名称', key: 'accountName', minWidth: 160, align: 'center' },
{ label: '账号', key: 'accountNumber', width: 150, align: 'center' },
{
label: '账户余额',
key: 'amount',
width: 120,
align: 'center',
render: (row) => (
<span style={{ color: row.amount < 0 ? '#F56C6C' : '#67C23A' }}>
{row.amount}
</span>
)
},
{
label: '累计充值金额',
key: 'accumulatedRecharge',
width: 130,
align: 'center'
},
{
label: '欠费金额',
key: 'arrears',
width: 110,
align: 'center',
render: (row) => (
<span style={{ color: row.arrears > 0 ? '#F56C6C' : '#909399' }}>
{row.arrears}
</span>
)
},
{
label: '账户币种',
key: 'currencyCode',
width: 140,
align: 'center',
render: (row) => {
const colorMap = { CNY: '#E6A23C', USD: '#409EFF' }
return (
<el-tag size="small" style={{ color: colorMap[row.currencyCode] }}>
{row.currencyCode}
{row.currencyName ? ` ${row.currencyName}` : ''}
</el-tag>
)
}
},
{ label: '创建人', key: 'creater', width: 100, align: 'center' },
{ label: '创建时间', key: 'createTime', width: 165, align: 'center' },
{ label: '修改时间', key: 'updateTime', width: 165, align: 'center' },
{
label: '操作',
fixed: 'right',
align: 'center',
width: 160,
render: (row) => (
<div class="op-icons">
<el-tooltip content="编辑" placement="top">
<span class="op-item" onClick={() => this.handleEdit(row)}>
<img src={require('@/assets/images/edit_yellow.png')} />
</span>
</el-tooltip>
<el-tooltip content="手动调整" placement="top">
<span class="op-item" onClick={() => this.handleAdjust(row)}>
<img src={require('@/assets/images/minus.png')} />
</span>
</el-tooltip>
<el-tooltip content="充值记录" placement="top">
<span class="op-item" onClick={() => this.handleRechargeRecord(row)}>
<img src={require('@/assets/images/recharge_record.png')} />
</span>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<span
class="op-item danger"
onClick={() => this.handleDelete(row)}>
<img src={require('@/assets/images/delete.png')} />
</span>
</el-tooltip>
<el-tooltip content="操作日志" placement="top">
<span class="op-item" onClick={() => this.handleOperationLog(row)}>
<img src={require('@/assets/images/operation_log.png')} />
</span>
</el-tooltip>
</div>
)
}
]
},
rechargeColumns() {
return [
{ label: '充值单号', key: 'rechargeOrderNo', width: 160, align: 'center' },
{
label: '充值状态',
key: 'statusName',
width: 100,
align: 'center'
},
{
label: '充值方式',
key: 'transactionTypeName',
width: 110,
align: 'center'
},
{ label: '充值币种', key: 'currency', width: 90, align: 'center' },
{ label: '充值金额', key: 'rechargeAmount', width: 110, align: 'center' },
{ label: '手续费', key: 'fee', width: 90, align: 'center' },
{ label: '入账金额', key: 'creditedAmount', width: 110, align: 'center' },
{ label: '变动前余额', key: 'balanceBefore', width: 110, align: 'center' },
{ label: '变动后余额', key: 'balanceAfter', width: 110, align: 'center' },
{
label: '收款备注',
key: 'remark',
minWidth: 120,
align: 'center'
},
{ label: '创建人', key: 'createName', width: 90, align: 'center' },
{ label: '创建时间', key: 'createTime', width: 165, align: 'center' }
]
}
},
watch: {
visible(v) {
this.dialogVisible = v
if (v) {
this.searchForm = { accountName: '', accountNumber: '', currencyCode: '' }
this.pageOptions.currentPage = 1
this.loadList()
}
},
dialogVisible(v) {
// 弹框被关闭时(点 X / 遮罩 / ESC),同步回父组件,保证再次打开可正常触发
if (!v) {
this.$emit('update:visible', false)
}
}
},
methods: {
// 列表
loadList() {
this.loading = true
post('customer/account/page-list', {
customerId: this.customerId,
...this.searchForm,
currentPage: this.pageOptions.currentPage,
pageSize: this.pageOptions.pageSize
})
.then((res) => {
if (res.code === 200) {
this.sourceData = res.data.records
this.pageOptions.total = res.data.total
}
})
.finally(() => {
this.loading = false
})
},
handleSearch() {
this.pageOptions.currentPage = 1
this.loadList()
},
sizeChange(size) {
this.pageOptions.pageSize = size
this.pageOptions.currentPage = 1
this.loadList()
},
currentChange(page) {
this.pageOptions.currentPage = page
this.loadList()
},
// 新增/编辑
handleAdd() {
this.addEditForm = {
id: null,
accountName: '',
accountNumber: '',
currency: 'CNY'
}
this.addEditDialog.type = 'add'
this.addEditDialog.title = '新增账户'
this.addEditDialog.visible = true
this.$nextTick(() => {
this.$refs.addEditFormRef?.clearValidate()
})
},
// 快速新增(基于当前行币种预填)
handleQuickAdd(row) {
const currency = row?.currencyCode || 'CNY'
this.addEditForm = {
id: null,
accountName: '',
accountNumber: '',
currency
}
this.addEditDialog.type = 'add'
this.addEditDialog.title = '新增账户'
this.addEditDialog.visible = true
this.$nextTick(() => {
this.$refs.addEditFormRef?.clearValidate()
})
},
handleEdit(row) {
this.addEditForm = {
id: row.id,
accountName: row.accountName,
accountNumber: row.accountNumber,
currency: row.currencyCode
}
this.addEditDialog.type = 'edit'
this.addEditDialog.title = '编辑账户'
this.addEditDialog.visible = true
this.$nextTick(() => {
this.$refs.addEditFormRef?.clearValidate()
})
},
submitAddEdit() {
this.$refs.addEditFormRef.validate((valid) => {
if (!valid) return
this.addEditDialog.loading = true
const params =
this.addEditDialog.type === 'add'
? {
customerId: this.customerId,
accountName: this.addEditForm.accountName,
currencyCode: this.addEditForm.currency
}
: {
id: this.addEditForm.id,
accountName: this.addEditForm.accountName,
currencyCode: this.addEditForm.currency
}
const request =
this.addEditDialog.type === 'add'
? post('customer/account/add', params)
: post('customer/account/update', params)
request
.then((res) => {
if (res.code === 200) {
this.$message.success(res.message || '操作成功')
this.addEditDialog.visible = false
this.loadList()
}
})
.finally(() => {
this.addEditDialog.loading = false
})
})
},
// 手动调整
handleAdjust(row) {
this.adjustForm = {
id: row.id,
accountName: row.accountName,
currentBalance: row.amount,
currency: row.currencyCode,
amount: '',
remark: ''
}
this.adjustDialog.visible = true
this.$nextTick(() => {
this.$refs.adjustFormRef?.clearValidate()
})
},
// 金额输入过滤:仅允许数字、小数点和开头负号,最多两位小数
handleAmountInput(value) {
let v = String(value).replace(/[^\d.-]/g, '')
// 负号仅允许一个且必须在开头
if (v.indexOf('-') === 0) {
v = '-' + v.slice(1).replace(/-/g, '')
} else if (v.indexOf('-') > 0) {
v = v.replace(/-/g, '')
}
// 小数点仅保留一个
const firstDot = v.indexOf('.')
if (firstDot > -1) {
v = v.slice(0, firstDot + 1) + v.slice(firstDot + 1).replace(/\./g, '')
}
// 最多两位小数
const matched = v.match(/^-?\d*(\.\d{0,2})?/)
this.adjustForm.amount = matched ? matched[0] : ''
},
validateAmount(_rule, value, callback) {
if (value === '' || value === null || value === undefined) {
callback()
return
}
const num = Number(value)
if (Number.isNaN(num)) {
callback(new Error('请输入数字'))
return
}
if (num === 0) {
callback(new Error('调整金额不能为 0'))
return
}
callback()
},
submitAdjust() {
this.$refs.adjustFormRef.validate((valid) => {
if (!valid) return
this.adjustDialog.loading = true
post('customer/account/manual-adjust', {
accountId: this.adjustForm.id,
amount: Number(this.adjustForm.amount),
remark: this.adjustForm.remark
})
.then((res) => {
if (res.code === 200) {
this.$message.success(res.message || '调整成功')
this.adjustDialog.visible = false
this.loadList()
}
})
.finally(() => {
this.adjustDialog.loading = false
})
})
},
// 充值记录
handleRechargeRecord(row) {
this.rechargeDialog.accountId = row.id
this.rechargeDialog.accountName = row.accountName
this.rechargeDialog.currentPage = 1
this.loadRechargeRecord(row.id, 1)
this.rechargeDialog.visible = true
},
loadRechargeRecord(accountId, page = 1) {
this.rechargeDialog.loading = true
post('customer/account/transaction-list', {
accountId,
currentPage: page,
pageSize: this.rechargeDialog.pageSize
})
.then((res) => {
if (res.code === 200) {
this.rechargeDialog.list = res.data.records
this.rechargeDialog.total = res.data.total
this.rechargeDialog.currentPage = page
}
})
.finally(() => {
this.rechargeDialog.loading = false
})
},
// 删除
handleDelete(row) {
this.deleteDialog = {
visible: true,
hasDetail: row.hasDetail,
tip: row.hasDetail
? `您要删除的账户${row.accountName}(${row.accountNumber})存在消费或充值明细,无法进行删除操作!`
: '删除后无法恢复,确定删除当前选择的账户?',
id: row.id,
accountName: row.accountName,
accountNumber: row.accountNumber,
loading: false
}
},
submitDelete() {
if (this.deleteDialog.hasDetail) {
this.$message.warning('该账户存在账户明细,无法删除')
return
}
this.deleteDialog.loading = true
remove(`customer/account/delete/${this.deleteDialog.id}`)
.then((res) => {
if (res.code === 200) {
this.$message.success(res.message || '删除成功')
this.loadList()
}
})
.finally(() => {
this.deleteDialog.loading = false
this.deleteDialog.visible = false
})
},
// 操作日志
handleOperationLog(row) {
this.logDialog = {
visible: true,
accountId: row.id,
accountName: row.accountName,
list: [],
loading: true
}
get(`customer/account/logs/${row.id}`)
.then((res) => {
if (res.code === 200) {
this.logDialog.list = res.data || []
}
})
.finally(() => {
this.logDialog.loading = false
})
},
handleClosed() {
this.sourceData = []
this.pageOptions = { pageSize: 10, currentPage: 1, total: 0 }
// 关闭主弹框时强制关闭所有内嵌子弹框,避免残留遮罩影响再次打开
this.addEditDialog.visible = false
this.adjustDialog.visible = false
this.rechargeDialog.visible = false
this.deleteDialog.visible = false
this.logDialog.visible = false
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .el-dialog__body {
padding: 15px 20px 0;
}
.search_form {
margin-bottom: 5px;
::v-deep .el-form-item {
margin-bottom: 10px;
}
}
.table-wrap {
background: #fff;
padding: 5px;
border: 1px solid #ebeef5;
border-radius: 4px;
min-height: 500px;
}
.pagination {
text-align: right;
margin-top: 10px;
}
.op-icons {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 4px;
.op-item {
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
width: 24px;
height: 24px;
img {
width: 22px;
height: 22px;
vertical-align: middle;
}
&.danger {
img {
filter: hue-rotate(0deg);
}
}
&:hover {
opacity: 0.85;
transform: scale(1.05);
transition: all 0.2s;
}
}
}
.adjust-footer {
display: flex;
justify-content: center;
align-items: center;
padding-top: 10px;
::v-deep .el-button {
min-width: 90px;
}
}
.delete-tip {
display: flex;
align-items: center;
font-size: 14px;
color: #303133;
padding: 10px 0;
i {
color: #f56c6c;
font-size: 24px;
margin-right: 10px;
}
}
.log-list {
max-height: 500px;
overflow-y: auto;
padding: 0 5px;
li {
line-height: 28px;
color: #303133;
font-size: 13px;
padding: 6px 0;
display: flex;
align-items: center;
.log-type {
flex-shrink: 0;
margin-right: 8px;
}
.log-name {
color: #409eff;
width: 80px;
flex-shrink: 0;
margin-right: 8px;
}
.log-desc {
flex: 1;
color: #606266;
}
.log-time {
color: #909399;
width: 160px;
flex-shrink: 0;
}
&.empty {
justify-content: center;
color: #c0c4cc;
}
}
}
</style>
......@@ -220,7 +220,7 @@
</div>
</div>
</el-tab-pane>
<el-tab-pane label="线下充值记录" name="offline">
<el-tab-pane label="线下充值管理" name="offline">
<div class="tab-content">
<div class="search">
<el-form :model="offlineSearchForm" size="small" :inline="true">
......@@ -255,10 +255,10 @@
:value="item.code"></el-option>
</el-select>
</el-form-item>
<el-form-item label="账户类型">
<el-form-item label="余额账户">
<el-select
v-model="offlineSearchForm.accountType"
placeholder="请选择账户类型"
placeholder="请选择余额账户"
clearable
@change="onOfflineFilterChange">
<el-option
......@@ -358,7 +358,7 @@
:show-overflow-tooltip="true" />
<el-table-column
label="账户类型"
label="余额账户"
prop="accountTypeName"
header-align="center"
align="center"
......@@ -644,7 +644,7 @@ export default {
this.aliPayLoading = false
}
},
// 线下充值记录
// 线下充值管理
async loadOfflineData() {
const data = {
...this.offlineSearchForm,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment