473 lines
11 KiB
Vue
473 lines
11 KiB
Vue
|
|
<template>
|
||
|
|
<view class="page">
|
||
|
|
<u-navbar title="线下支付审核" :border-bottom="false" :background="bgc" title-color="#2E4975" title-size="36"
|
||
|
|
back-icon-color="#2E4975" height="45"></u-navbar>
|
||
|
|
|
||
|
|
<scroll-view scroll-y class="page-scroll">
|
||
|
|
<view class="scroll-inner" :class="{ 'scroll-inner--with-bar': detail && detail.id && canVerify }">
|
||
|
|
<view v-if="loading" class="loading-wrap">
|
||
|
|
<u-loading mode="circle" size="48" color="#4C97E7"></u-loading>
|
||
|
|
<text class="loading-text">加载中...</text>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<view v-else-if="detail && detail.id" class="content">
|
||
|
|
<view class="amount-card">
|
||
|
|
<view class="amount-label">支付金额</view>
|
||
|
|
<view class="amount-value">{{ displayCurrencySymbol }}{{ formatAmount(detail.amount) }}</view>
|
||
|
|
<view class="amount-status">
|
||
|
|
<text class="status-tag" :class="statusClass">{{ statusLabel }}</text>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<view class="card">
|
||
|
|
<view class="card-title">申请信息</view>
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">申请单号</text>
|
||
|
|
<text class="value">{{ detail.no }}</text>
|
||
|
|
</view>
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">支付单号</text>
|
||
|
|
<text class="value">{{ detail.payNo || '-' }}</text>
|
||
|
|
</view>
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">申请用户</text>
|
||
|
|
<text class="value">{{ detail.userName || '-' }}{{ detail.userPhone ? ' (' + detail.userPhone + ')' : '' }}</text>
|
||
|
|
</view>
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">申请时间</text>
|
||
|
|
<text class="value">{{ detail.createTime || '-' }}</text>
|
||
|
|
</view>
|
||
|
|
<view class="info-row" v-if="detail.reason">
|
||
|
|
<text class="label">支付说明</text>
|
||
|
|
<text class="value">{{ detail.reason }}</text>
|
||
|
|
</view>
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">区域</text>
|
||
|
|
<text class="value">{{ detail.areaName || '-' }}</text>
|
||
|
|
</view>
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">应用</text>
|
||
|
|
<text class="value">{{ detail.appName || '-' }}</text>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<view class="card" v-if="detail.status && !canVerify">
|
||
|
|
<view class="card-title">审核结果</view>
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">审核时间</text>
|
||
|
|
<text class="value">{{ detail.verifyTime || '-' }}</text>
|
||
|
|
</view>
|
||
|
|
<view class="info-row">
|
||
|
|
<text class="label">审核人</text>
|
||
|
|
<text class="value">{{ detail.verifyUserName || '-' }}</text>
|
||
|
|
</view>
|
||
|
|
<view class="info-row" v-if="detail.verifyRemark">
|
||
|
|
<text class="label">审核备注</text>
|
||
|
|
<text class="value">{{ detail.verifyRemark }}</text>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<view class="remark-wrap" v-if="canVerify">
|
||
|
|
<text class="remark-label">审核备注</text>
|
||
|
|
<textarea
|
||
|
|
v-model="form.remark"
|
||
|
|
class="remark-input"
|
||
|
|
placeholder="请输入审核备注(选填)"
|
||
|
|
placeholder-style="color:#C7CDD3"
|
||
|
|
maxlength="500"
|
||
|
|
/>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<view v-else class="empty-wrap">
|
||
|
|
<text class="empty-text">{{ failMsg || '线下支付申请加载失败' }}</text>
|
||
|
|
<view class="empty-btn" hover-class="app-tap-hover" @click="loadDetail">重新加载</view>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
</scroll-view>
|
||
|
|
|
||
|
|
<view class="bottom-actions" v-if="detail && detail.id && canVerify">
|
||
|
|
<view class="btn reject-btn" :class="{ disabled: submitLoading }" hover-class="app-tap-hover" @click="handleSubmit(false)">
|
||
|
|
<text>驳回</text>
|
||
|
|
</view>
|
||
|
|
<view class="btn pass-btn" :class="{ disabled: submitLoading }" hover-class="app-tap-hover" @click="handleSubmit(true)">
|
||
|
|
<text>{{ submitLoading ? '提交中...' : '通过审核' }}</text>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
export default {
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
bgc: { backgroundColor: '#EEF3FA' },
|
||
|
|
applyNo: '',
|
||
|
|
payNo: '',
|
||
|
|
loading: true,
|
||
|
|
failMsg: '',
|
||
|
|
detail: null,
|
||
|
|
form: {
|
||
|
|
remark: ''
|
||
|
|
},
|
||
|
|
submitLoading: false,
|
||
|
|
auditResultEmitted: false,
|
||
|
|
eventChannel: null
|
||
|
|
}
|
||
|
|
},
|
||
|
|
computed: {
|
||
|
|
displayCurrencySymbol() {
|
||
|
|
const d = this.detail || {}
|
||
|
|
return d.currencySymbol != null && d.currencySymbol !== '' ? d.currencySymbol : ''
|
||
|
|
},
|
||
|
|
canVerify() {
|
||
|
|
return this.detail && this.detail.status === 'WAIT_AUDIT'
|
||
|
|
},
|
||
|
|
statusLabel() {
|
||
|
|
if (!this.detail || !this.detail.status) return ''
|
||
|
|
const map = {
|
||
|
|
WAIT_AUDIT: '待审核',
|
||
|
|
PASSED: '已通过',
|
||
|
|
REJECTED: '已驳回',
|
||
|
|
CANCELED: '已取消'
|
||
|
|
}
|
||
|
|
return map[this.detail.status] || this.detail.status
|
||
|
|
},
|
||
|
|
statusClass() {
|
||
|
|
if (!this.detail || !this.detail.status) return ''
|
||
|
|
const map = {
|
||
|
|
WAIT_AUDIT: 'status-wait',
|
||
|
|
PASSED: 'status-passed',
|
||
|
|
REJECTED: 'status-rejected',
|
||
|
|
CANCELED: 'status-canceled'
|
||
|
|
}
|
||
|
|
return map[this.detail.status] || ''
|
||
|
|
}
|
||
|
|
},
|
||
|
|
onLoad(options) {
|
||
|
|
this.applyNo = options.no ? decodeURIComponent(options.no) : ''
|
||
|
|
this.payNo = options.payNo ? decodeURIComponent(options.payNo) : ''
|
||
|
|
if (this.getOpenerEventChannel) {
|
||
|
|
this.eventChannel = this.getOpenerEventChannel()
|
||
|
|
}
|
||
|
|
this.loadDetail()
|
||
|
|
},
|
||
|
|
onUnload() {
|
||
|
|
if (this.auditResultEmitted) return
|
||
|
|
const status = this.detail && this.detail.status
|
||
|
|
if (status === 'REJECTED') {
|
||
|
|
this.emitAuditResult({ pass: false, rejected: true, payNo: this.payNo || '' })
|
||
|
|
} else if (status === 'WAIT_AUDIT') {
|
||
|
|
this.emitAuditResult({ pass: false, canceled: true, payNo: this.payNo || '' })
|
||
|
|
}
|
||
|
|
},
|
||
|
|
methods: {
|
||
|
|
emitAuditResult(payload) {
|
||
|
|
if (this.auditResultEmitted) return
|
||
|
|
this.auditResultEmitted = true
|
||
|
|
const data = payload || {}
|
||
|
|
try {
|
||
|
|
if (this.eventChannel && this.eventChannel.emit) {
|
||
|
|
this.eventChannel.emit('offlineAuditResult', data)
|
||
|
|
}
|
||
|
|
} catch (e) {}
|
||
|
|
uni.setStorageSync('agentOrderOfflineResult', data)
|
||
|
|
},
|
||
|
|
loadDetail() {
|
||
|
|
if (!this.applyNo) {
|
||
|
|
this.failMsg = '缺少线下支付申请单号'
|
||
|
|
this.loading = false
|
||
|
|
return
|
||
|
|
}
|
||
|
|
this.loading = true
|
||
|
|
this.failMsg = ''
|
||
|
|
const query = `no=${encodeURIComponent(this.applyNo)}&pageNum=1&pageSize=1`
|
||
|
|
this.$u.get(`/bst/offlinePay/list?${query}`).then(res => {
|
||
|
|
const first = res.rows && res.rows[0]
|
||
|
|
if (res.code === 200 && first) {
|
||
|
|
this.detail = first
|
||
|
|
} else {
|
||
|
|
this.detail = null
|
||
|
|
this.failMsg = res.msg || '线下支付申请加载失败'
|
||
|
|
}
|
||
|
|
}).catch(err => {
|
||
|
|
this.detail = null
|
||
|
|
this.failMsg = (err && err.msg) || '线下支付申请加载失败'
|
||
|
|
}).finally(() => {
|
||
|
|
this.loading = false
|
||
|
|
})
|
||
|
|
},
|
||
|
|
formatAmount(val) {
|
||
|
|
if (val === null || val === undefined || val === '') return '0.00'
|
||
|
|
const n = Number(val)
|
||
|
|
return isNaN(n) ? '0.00' : n.toFixed(2)
|
||
|
|
},
|
||
|
|
handleSubmit(pass) {
|
||
|
|
if (this.submitLoading || !this.detail) return
|
||
|
|
const tip = pass ? '确定通过此线下支付申请吗?' : '确定驳回此线下支付申请吗?'
|
||
|
|
uni.showModal({
|
||
|
|
title: '确认操作',
|
||
|
|
content: tip,
|
||
|
|
success: (modalRes) => {
|
||
|
|
if (!modalRes.confirm) return
|
||
|
|
this.verifyOfflinePay(pass)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
},
|
||
|
|
verifyOfflinePay(pass) {
|
||
|
|
this.submitLoading = true
|
||
|
|
this.$u.put('/bst/offlinePay/verify', {
|
||
|
|
id: this.detail.id,
|
||
|
|
pass,
|
||
|
|
remark: this.form.remark || undefined
|
||
|
|
}).then(res => {
|
||
|
|
if (res.code === 200) {
|
||
|
|
uni.showToast({ title: '审核成功', icon: 'success', duration: 1200 })
|
||
|
|
const payNo = this.payNo || (this.detail && this.detail.payNo) || ''
|
||
|
|
if (pass) {
|
||
|
|
this.emitAuditResult({ pass: true, payNo })
|
||
|
|
} else {
|
||
|
|
this.emitAuditResult({ pass: false, rejected: true, payNo })
|
||
|
|
}
|
||
|
|
setTimeout(() => { uni.navigateBack() }, 1200)
|
||
|
|
} else {
|
||
|
|
uni.showToast({ title: res.msg || '审核失败', icon: 'none', duration: 2500 })
|
||
|
|
}
|
||
|
|
}).catch(err => {
|
||
|
|
uni.showToast({ title: (err && err.msg) || '审核失败', icon: 'none', duration: 2500 })
|
||
|
|
}).finally(() => {
|
||
|
|
this.submitLoading = false
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style lang="scss" scoped>
|
||
|
|
$primary: #4C97E7;
|
||
|
|
$primary-deep: #3A7EDB;
|
||
|
|
$ink: #2E4975;
|
||
|
|
|
||
|
|
page {
|
||
|
|
background-color: #EEF3FA;
|
||
|
|
}
|
||
|
|
|
||
|
|
.page {
|
||
|
|
height: 100vh;
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
overflow: hidden;
|
||
|
|
background-color: #EEF3FA;
|
||
|
|
box-sizing: border-box;
|
||
|
|
}
|
||
|
|
|
||
|
|
.page-scroll {
|
||
|
|
flex: 1;
|
||
|
|
height: 0;
|
||
|
|
width: 100%;
|
||
|
|
}
|
||
|
|
|
||
|
|
.scroll-inner {
|
||
|
|
padding: 24rpx 32rpx 32rpx;
|
||
|
|
box-sizing: border-box;
|
||
|
|
}
|
||
|
|
|
||
|
|
.scroll-inner--with-bar {
|
||
|
|
padding-bottom: calc(32rpx + 120rpx + constant(safe-area-inset-bottom));
|
||
|
|
padding-bottom: calc(32rpx + 120rpx + env(safe-area-inset-bottom));
|
||
|
|
}
|
||
|
|
|
||
|
|
.loading-wrap {
|
||
|
|
padding: 100rpx 0;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
flex-direction: column;
|
||
|
|
}
|
||
|
|
|
||
|
|
.loading-text {
|
||
|
|
margin-top: 18rpx;
|
||
|
|
font-size: 26rpx;
|
||
|
|
color: #8B98AA;
|
||
|
|
}
|
||
|
|
|
||
|
|
.content {
|
||
|
|
padding: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.amount-card {
|
||
|
|
background: linear-gradient(135deg, $primary, $primary-deep);
|
||
|
|
border-radius: 28rpx;
|
||
|
|
padding: 48rpx 32rpx;
|
||
|
|
margin-bottom: 24rpx;
|
||
|
|
text-align: center;
|
||
|
|
box-shadow: 0 18rpx 40rpx rgba(76, 151, 231, 0.22);
|
||
|
|
}
|
||
|
|
|
||
|
|
.amount-label {
|
||
|
|
font-size: 26rpx;
|
||
|
|
color: rgba(255, 255, 255, 0.78);
|
||
|
|
margin-bottom: 12rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.amount-value {
|
||
|
|
font-size: 60rpx;
|
||
|
|
font-weight: 700;
|
||
|
|
color: #fff;
|
||
|
|
letter-spacing: 2rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.amount-status {
|
||
|
|
margin-top: 20rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.status-tag {
|
||
|
|
display: inline-flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
padding: 8rpx 24rpx;
|
||
|
|
border-radius: 999rpx;
|
||
|
|
font-size: 24rpx;
|
||
|
|
background: rgba(255, 255, 255, 0.2);
|
||
|
|
color: #fff;
|
||
|
|
}
|
||
|
|
|
||
|
|
.status-wait {
|
||
|
|
color: #FFE58F;
|
||
|
|
}
|
||
|
|
|
||
|
|
.status-passed {
|
||
|
|
color: #B7EB8F;
|
||
|
|
}
|
||
|
|
|
||
|
|
.status-rejected {
|
||
|
|
color: #FFA39E;
|
||
|
|
}
|
||
|
|
|
||
|
|
.status-canceled {
|
||
|
|
color: rgba(255, 255, 255, 0.72);
|
||
|
|
}
|
||
|
|
|
||
|
|
.card,
|
||
|
|
.remark-wrap {
|
||
|
|
background: #fff;
|
||
|
|
border-radius: 24rpx;
|
||
|
|
padding: 32rpx;
|
||
|
|
margin-bottom: 24rpx;
|
||
|
|
box-shadow: 0 8rpx 28rpx rgba(46, 73, 117, 0.06);
|
||
|
|
}
|
||
|
|
|
||
|
|
.card-title {
|
||
|
|
font-size: 30rpx;
|
||
|
|
font-weight: 700;
|
||
|
|
color: $ink;
|
||
|
|
margin-bottom: 24rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.info-row {
|
||
|
|
display: flex;
|
||
|
|
align-items: flex-start;
|
||
|
|
margin-bottom: 20rpx;
|
||
|
|
font-size: 28rpx;
|
||
|
|
line-height: 1.5;
|
||
|
|
}
|
||
|
|
|
||
|
|
.info-row:last-child {
|
||
|
|
margin-bottom: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.label {
|
||
|
|
width: 160rpx;
|
||
|
|
color: #8B98AA;
|
||
|
|
flex-shrink: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.value {
|
||
|
|
flex: 1;
|
||
|
|
color: #2F3A4A;
|
||
|
|
word-break: break-all;
|
||
|
|
}
|
||
|
|
|
||
|
|
.remark-label {
|
||
|
|
display: block;
|
||
|
|
font-size: 28rpx;
|
||
|
|
color: #5D6B7D;
|
||
|
|
margin-bottom: 16rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.remark-input {
|
||
|
|
width: 100%;
|
||
|
|
min-height: 180rpx;
|
||
|
|
padding: 20rpx;
|
||
|
|
border: 1rpx solid #E6ECF3;
|
||
|
|
border-radius: 16rpx;
|
||
|
|
font-size: 28rpx;
|
||
|
|
color: #2F3A4A;
|
||
|
|
box-sizing: border-box;
|
||
|
|
background: #F8FAFD;
|
||
|
|
}
|
||
|
|
|
||
|
|
.empty-wrap {
|
||
|
|
padding: 120rpx 40rpx;
|
||
|
|
text-align: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.empty-text {
|
||
|
|
display: block;
|
||
|
|
font-size: 28rpx;
|
||
|
|
color: #8B98AA;
|
||
|
|
margin-bottom: 28rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.empty-btn {
|
||
|
|
display: inline-flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
height: 72rpx;
|
||
|
|
padding: 0 36rpx;
|
||
|
|
border-radius: 999rpx;
|
||
|
|
background: $primary;
|
||
|
|
color: #fff;
|
||
|
|
font-size: 28rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.bottom-actions {
|
||
|
|
flex-shrink: 0;
|
||
|
|
display: flex;
|
||
|
|
gap: 24rpx;
|
||
|
|
padding: 20rpx 32rpx;
|
||
|
|
padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
|
||
|
|
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||
|
|
background: #fff;
|
||
|
|
box-shadow: 0 -8rpx 28rpx rgba(46, 73, 117, 0.08);
|
||
|
|
box-sizing: border-box;
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn {
|
||
|
|
flex: 1;
|
||
|
|
height: 92rpx;
|
||
|
|
border-radius: 999rpx;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
font-size: 32rpx;
|
||
|
|
font-weight: 700;
|
||
|
|
}
|
||
|
|
|
||
|
|
.reject-btn {
|
||
|
|
background: #FFF2F0;
|
||
|
|
color: #F04438;
|
||
|
|
}
|
||
|
|
|
||
|
|
.pass-btn {
|
||
|
|
background: linear-gradient(135deg, $primary, $primary-deep);
|
||
|
|
color: #fff;
|
||
|
|
box-shadow: 0 14rpx 28rpx rgba(76, 151, 231, 0.24);
|
||
|
|
}
|
||
|
|
|
||
|
|
.disabled {
|
||
|
|
opacity: 0.6;
|
||
|
|
pointer-events: none;
|
||
|
|
}
|
||
|
|
</style>
|