This commit is contained in:
2026-10-08 18:18:21 +08:00
parent 9cc1ff546c
commit 1e3095c05e
1911 changed files with 349551 additions and 0 deletions
+351
View File
@@ -0,0 +1,351 @@
<template>
<el-card shadow="never" class="balance-log">
<div slot="header" class="flex justify-between items-center">
<span>余额记录</span>
<div class="flex justify-end items-center">
<div class="query-date mr-20">
<span class="query-date-label">查询日期:</span>
<el-date-picker
v-model="queryDate"
type="date"
size="small"
value-format="yyyyMMdd"
format="yyyy-MM-dd"
:clearable="false"
:editable="false"
placeholder="选择日期"
@change="onQueryDateChange">
</el-date-picker>
</div>
<div class="balance-text mr-20">当前余额:<span class="amount">{{ balanceLoading ? '加载中...' : formatBalance(balance) }}</span></div>
<el-button type="primary" size="small" :loading="withdrawLoading" @click="openWithdrawDialog">提现</el-button>
</div>
</div>
<el-table
v-loading="loading"
:data="list"
stripe
style="width: 100%">
<el-table-column prop="reqJournalNo" label="订单号" min-width="220">
<template slot-scope="{ row }">
{{ row.reqJournalNo || '-' }}
</template>
</el-table-column>
<el-table-column label="订单金额" min-width="140">
<template slot-scope="{ row }">
<span class="price">¥{{ formatFen(row.orderAmt) }}</span>
</template>
</el-table-column>
<el-table-column label="订单状态" min-width="140">
<template slot-scope="{ row }">
{{ orderStatusText(row.orderStatus) }}
</template>
</el-table-column>
<el-table-column prop="errMsg" label="失败原因" min-width="220">
<template slot-scope="{ row }">
{{ row.errMsg || '-' }}
</template>
</el-table-column>
<el-table-column label="付款方" min-width="180">
<template slot-scope="{ row }">
{{ row.payerName || row.payerAcctNo || '-' }}
</template>
</el-table-column>
</el-table>
<div class="mt-20 text-center" v-if="totalPages > 1">
<el-pagination
background
layout="prev, pager, next"
:current-page="pageIndex"
:page-size="pageSize"
:total="totalPages"
@current-change="getList">
</el-pagination>
</div>
<el-dialog
title="余额提现"
:visible.sync="dialogVisible"
width="420px"
append-to-body>
<el-form label-width="90px">
<el-form-item label="企业用户号">
<span>{{ merNo || '无企业用户号' }}</span>
</el-form-item>
<el-form-item label="提现金额">
<el-input v-model.trim="withdrawForm.amount" placeholder="请输入提现金额">
<template slot="append">元</template>
</el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="withdrawLoading" @click="submitWithdraw">确定</el-button>
</div>
</el-dialog>
</el-card>
</template>
<script>
export default {
name: "balance-log",
data() {
return {
loading: false,
list: [],
pageIndex: 1,
pageSize: 10,
// 查询日期默认当天且不允许清空。
queryDate: '',
// 后端返回的是记录总数,供 el-pagination 使用。
totalPages: 0,
balance: null,
balanceLoading: false,
dialogVisible: false,
withdrawLoading: false,
withdrawForm: {
amount: ''
}
}
},
computed: {
// 企业用户号用于查询实时余额及银联流水状态。
merNo() {
return vk.getVuex('$user.userInfo.merNo')
}
},
mounted() {
this.queryDate = this.getToday()
this.requestMerchantBalance()
this.getList()
},
methods: {
/**
* 查询当前商户实时余额。
* 客户端云函数内部调用 balanceUtil.requestMerchantBalance,并将金额统一转换为元。
*/
async requestMerchantBalance() {
if (!this.merNo) {
this.balance = null
return
}
this.balanceLoading = true
try {
const res = await vk.callFunction({
url: 'client/userBalanceLog/kh/getMerchantBalance',
data: {
accNo: this.merNo
}
})
const balance = this.pickFirstDefined([
this.pickResponseValue(res, 'balance'),
this.pickResponseValue(res, 'merchantBalance'),
this.pickResponseValue(res, 'canPayAmt')
])
if (balance === undefined) {
throw new Error((res && (res.msg || res.message)) || '未获取到余额')
}
// getInfo 已将 canPayAmt 从分转换为元;仅在直接返回 canPayAmt 时做分转元。
const canPayAmt = this.pickResponseValue(res, 'canPayAmt')
this.balance = canPayAmt !== undefined ? Number(canPayAmt || 0) / 100 : Number(balance || 0)
} catch (err) {
this.balance = null
this.$message.error((err && err.message) || '余额获取失败')
} finally {
this.balanceLoading = false
}
},
async getList(pageIndex = 1) {
this.loading = true
try {
// 由服务端从当前登录用户身份取得 sellerUid,客户端只传分页和日期。
const queryDate = this.queryDate || this.getToday()
const res = await vk.callFunction({
url: 'client/userBalanceLog/kh/getList',
data: {
queryDate,
pageIndex,
pageSize: this.pageSize
}
})
const code = Number(res && res.code)
if (code !== 0 && code !== 200 && res && (res.msg || res.message)) {
throw new Error(res.msg || res.message)
}
const rows = this.pickResponseValue(res, 'rows')
this.list = Array.isArray(rows) ? rows : []
this.pageIndex = Number(this.pickResponseValue(res, 'pageIndex') || pageIndex) || pageIndex
this.totalPages = Number(this.pickResponseValue(res, 'total') || 0)
} catch (err) {
this.list = []
this.totalPages = 0
this.$message.error((err && err.message) || '余额记录获取失败')
} finally {
this.loading = false
}
},
onQueryDateChange() {
// 日期切换后从第一页开始查询,日期选择器不可清空;异常情况下恢复当天。
if (!this.queryDate) {
this.queryDate = this.getToday()
}
this.getList(1)
},
pickFirstDefined(values) {
return values.find(value => value !== undefined && value !== null && value !== '')
},
pickResponseValue(res, key) {
if (!res || typeof res !== 'object') return undefined
return this.pickFirstDefined([
res[key],
res.data && typeof res.data === 'object' && !Array.isArray(res.data) ? res.data[key] : undefined,
res.info && typeof res.info === 'object' && !Array.isArray(res.info) ? res.info[key] : undefined,
res.content && typeof res.content === 'object' && !Array.isArray(res.content) ? res.content[key] : undefined
])
},
getToday() {
const date = new Date()
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}${month}${day}`
},
formatFen(value) {
const num = Number(value)
if (!Number.isFinite(num)) return '0.00'
return (num / 100).toFixed(2)
},
orderStatusText(status) {
const statusMap = {
0: '处理中',
1: '处理成功',
2: '处理失败',
3: '退票',
4: '对账'
}
return statusMap[Number(status)] || status || '-'
},
formatPrice(price) {
const num = Number(price)
if (Number.isNaN(num)) return price || '0.00'
return num.toFixed(2)
},
formatBalance(balance) {
return balance === null || balance === undefined ? '--' : `¥${this.formatPrice(balance)}`
},
openWithdrawDialog() {
if (!this.merNo) {
this.$message.error('无商户号,无法提现')
return
}
this.withdrawForm.amount = ''
this.dialogVisible = true
},
async submitWithdraw() {
if (!this.merNo) {
this.$message.error('无商户号,无法提现')
return
}
if (!this.withdrawForm.amount) {
this.$message.error('请输入提现金额')
return
}
if (!/^\d+(\.\d{1,2})?$/.test(this.withdrawForm.amount)) {
this.$message.error('提现金额最多保留两位小数')
return
}
const amount = Number(this.withdrawForm.amount)
if (Number.isNaN(amount) || amount <= 0) {
this.$message.error('提现金额必须大于0')
return
}
if (this.balance === null || amount > Number(this.balance || 0)) {
this.$message.error('提现金额不能大于当前余额')
return
}
this.withdrawLoading = true
try {
const res = await vk.request({
method: "POST",
url: 'http://jjx-union-pay.gxwebsoft.com/api/union-pay-jjx/transByAmount',
header: {
"content-type": "application/json; charset=utf-8",
},
data: {
accNo: this.merNo,
amount
}
})
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)) ||
'提现失败'
)
}
this.$message.success('提现申请已提交')
this.dialogVisible = false
this.withdrawForm.amount = ''
await this.requestMerchantBalance()
await this.getList(this.pageIndex)
} catch (err) {
this.$message.error((err && err.message) || '提现失败')
} finally {
this.withdrawLoading = false
}
},
refreshUserInfo() {
return new Promise((resolve) => {
vk.userCenter.getCurrentUserInfo({
success: () => {
resolve()
},
fail: () => {
resolve()
}
})
})
}
}
}
</script>
<style lang="scss" scoped>
.balance-log {
.query-date {
display: flex;
align-items: center;
}
.query-date-label {
color: #666;
white-space: nowrap;
}
.balance-text {
color: #666;
}
.amount,
.price {
color: #bf2b28;
font-weight: 600;
}
}
</style>
+199
View File
@@ -0,0 +1,199 @@
<template>
<el-card>
<div slot="header" class="clearfix">
<div class="flex justify-between items-center">
<div class="text-main text-40">金久信公正银联资料</div>
<el-tag :type="statusTagType">{{ statusText }}</el-tag>
</div>
</div>
<div class="text-25 text-main leading-8 mb-20">
<p>银联提供资料明细</p>
<p class="my-20">根据银联交易要求,为防止网络犯罪,金久信公正交易您需向银联提供以下资料:</p>
<p class="my-20">1、营业执照(原件手机拍照或复印件加盖公章);</p>
<p class="my-20">2、法人身份证正反面(原件手机拍照或复印件加盖公章);</p>
<p class="my-20">3、结算账户信息(结算账户信息需要开户许可证或银行回单等盖有银行印章的证明文件);</p>
<p class="my-20">4.店铺/办公地址照片(门头+门牌+经营场所的照片,收银台照片);</p>
<p class="my-20">5.法人手机号(用于电子签約)</p>
<p class="my-20">如由授权代理人负责,需要加收代理人身份证+代理人签约授权书,授权书我司提供模板;</p>
<p class="my-20">6、金久信平台入驻协议、授权书(请联系平台工作人员签约)</p>
<p class="my-20">7、审核时间2-3天,建议注册提前开通。</p>
<p>8、如果您完成审核请联系平台客服开通。</p>
</div>
<div class="mb-30">
<div class="flex justify-start items-center flex-wrap">
<el-button v-if="extraInfo.agreementUrl" type="primary" plain @click="openLink(extraInfo.agreementUrl)">金久信平台入驻协议下载</el-button>
<el-button v-if="extraInfo.authorizationUrl" type="primary" plain class="ml-20" @click="openLink(extraInfo.authorizationUrl)">授权书下载</el-button>
</div>
<div class="mt-30 flex justify-start items-start">
<div v-if="extraInfo.unionWechat" class="mr-35">
<div class="mb-10">银联工作人员微信</div>
<el-image :src="extraInfo.unionWechat" style="width: 160px;height: 160px" fit="cover" :preview-src-list="[extraInfo.unionWechat]"/>
</div>
<div v-if="extraInfo.platformWechat">
<div class="mb-10">金久信工作人员微信</div>
<el-image :src="extraInfo.platformWechat" style="width: 160px;height: 160px" fit="cover" :preview-src-list="[extraInfo.platformWechat]"/>
</div>
</div>
</div>
<el-form ref="form" :model="form" label-width="160px">
<el-form-item label="营业执照" required>
<vk-data-upload :limit="1" v-model="form.businessLicenseFile" :disabled="isLocked"></vk-data-upload>
</el-form-item>
<el-form-item label="法人身份证正面" required>
<vk-data-upload :limit="1" v-model="form.legalIdCardFront" :disabled="isLocked"></vk-data-upload>
</el-form-item>
<el-form-item label="法人身份证反面" required>
<vk-data-upload :limit="1" v-model="form.legalIdCardBack" :disabled="isLocked"></vk-data-upload>
</el-form-item>
<el-form-item label="结算账户证明" required>
<vk-data-upload :limit="10" v-model="form.settlementAccountFiles" :disabled="isLocked"></vk-data-upload>
</el-form-item>
<el-form-item label="店铺/办公照片" required>
<vk-data-upload :limit="10" v-model="form.shopPhotos" :disabled="isLocked"></vk-data-upload>
</el-form-item>
<el-form-item label="法人手机号" required>
<el-input v-model="form.legalMobile" placeholder="请输入法人手机号" :disabled="isLocked"/>
</el-form-item>
<el-form-item label="是否有代理人">
<el-switch v-model="form.hasAgent" :disabled="isLocked"/>
</el-form-item>
<template v-if="form.hasAgent">
<el-form-item label="代理人身份证正面" required>
<vk-data-upload :limit="1" v-model="form.agentIdCardFront" :disabled="isLocked"></vk-data-upload>
</el-form-item>
<el-form-item label="代理人身份证反面" required>
<vk-data-upload :limit="1" v-model="form.agentIdCardBack" :disabled="isLocked"></vk-data-upload>
</el-form-item>
<el-form-item label="代理人授权书" required>
<vk-data-upload :limit="10" v-model="form.agentAuthorizationFiles" :disabled="isLocked"></vk-data-upload>
</el-form-item>
</template>
<el-form-item label="入驻协议/授权书" required>
<vk-data-upload :limit="10" v-model="form.platformAgreementFiles" :disabled="isLocked"></vk-data-upload>
</el-form-item>
<el-form-item label="平台授权书" required>
<vk-data-upload :limit="10" v-model="form.platformAuthorizationFiles" :disabled="isLocked"></vk-data-upload>
</el-form-item>
</el-form>
<div class="mt-30 flex justify-center items-center">
<el-button type="primary" @click="submit" :disabled="isLocked">提交资料</el-button>
<el-button class="ml-20" @click="refresh">刷新</el-button>
</div>
</el-card>
</template>
<script>
const PROFILE_KEY = 'notaryUnionPayProfile'
export default {
name: "notary-union-pay",
data() {
return {
userInfo: null,
form: {
businessLicenseFile: '',
legalIdCardFront: '',
legalIdCardBack: '',
settlementAccountFiles: [],
shopPhotos: [],
legalMobile: '',
hasAgent: false,
agentIdCardFront: '',
agentIdCardBack: '',
agentAuthorizationFiles: [],
platformAgreementFiles: [],
platformAuthorizationFiles: []
},
extraInfo: {
agreementUrl: '',
authorizationUrl: '',
unionWechat: '',
platformWechat: ''
}
}
},
computed: {
profile() {
return (this.userInfo && this.userInfo[PROFILE_KEY]) || {}
},
statusText() {
if (this.profile.status === 1) return '已通过'
if (this.profile.status === 2) return '已拒绝'
if (this.profile.status === 0) return '审核中'
return '未提交'
},
statusTagType() {
if (this.profile.status === 1) return 'success'
if (this.profile.status === 2) return 'danger'
if (this.profile.status === 0) return 'warning'
return 'info'
},
isLocked() {
return [0, 1].includes(this.profile.status)
}
},
methods: {
async refresh() {
const {data} = await vk.callFunction({
url: 'client/user/kh/getNotaryUnionPayProfile',
})
this.userInfo = {
...(vk.getVuex('$user.userInfo') || {}),
[PROFILE_KEY]: data
}
this.fillForm()
if (!this.extraInfo.agreementUrl && !this.extraInfo.authorizationUrl && !this.extraInfo.unionWechat && !this.extraInfo.platformWechat) {
this.getExtraInfo()
}
},
async getExtraInfo() {
const dictIds = {
agreementUrl: 'd6a300ae6a2f6d98009a50e36a4f0766',
authorizationUrl: 'ed693ef16a2f6db00036fcd95c9dabf8',
unionWechat: 'ed693ef16a2f6dcf0037006c1e6aec14',
platformWechat: 'd6a300ae6a2f6dda009a81bc6b8b1fab'
}
const entries = await Promise.all(Object.entries(dictIds).map(async ([key, id]) => {
const res = await vk.callFunction({
url: 'client/dict/pub/getDetail',
data: {id}
})
return [key, res.content || '']
}))
this.extraInfo = Object.fromEntries(entries)
},
fillForm() {
const profile = this.profile
this.form = {
businessLicenseFile: profile.businessLicenseFile || '',
legalIdCardFront: profile.legalIdCardFront || '',
legalIdCardBack: profile.legalIdCardBack || '',
settlementAccountFiles: profile.settlementAccountFiles || [],
shopPhotos: profile.shopPhotos || [],
legalMobile: profile.legalMobile || '',
hasAgent: !!profile.hasAgent,
agentIdCardFront: profile.agentIdCardFront || '',
agentIdCardBack: profile.agentIdCardBack || '',
agentAuthorizationFiles: profile.agentAuthorizationFiles || [],
platformAgreementFiles: profile.platformAgreementFiles || [],
platformAuthorizationFiles: profile.platformAuthorizationFiles || []
}
},
async submit() {
await vk.callFunction({
url: 'client/user/kh/saveNotaryUnionPayProfile',
title: '提交中...',
data: this.form
})
this.$message.success('提交成功')
await this.refresh()
},
openLink(url) {
window.open(url)
}
},
mounted() {
this.refresh()
}
}
</script>