Files
RentWeAppFront/pages-biz/unpaid/unpaidMargin.vue
2026-06-03 16:02:11 +08:00

437 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="pending-bill-page">
<!-- 自定义导航栏 -->
<custom-navbar title="保证金待付" />
<DateFilter :start="startDate" :end="endDate" @update:start="startDate = $event"
@update:end="endDate = $event" @change="onDateFilterChange" />
<scroll-view
scroll-y
class="scroll-content"
:refresher-enabled="true"
:refresher-triggered="isRefreshing"
@refresherrefresh="refresh"
@scrolltolower="loadMore"
>
<view v-if="flowList.length > 0">
<view class="bill-item" v-for="item in flowList" :key="item.billNo">
<!-- 左侧勾选 + 信息 -->
<view class="bill-left">
<u-checkbox v-model="item.checked" @change="toggleCheck(item)" size="32"
active-color="#EA414A" />
<view class="bill-info">
<text class="bill-name">{{ $ellipsis(item.billName + item.billStartDate+'-'+item.billEndDate,27) }}) </text>
<text class="bill-date">缴费截止<text class="date">{{ item.billPayEndDate }}</text></text>
</view>
</view>
<!-- 右侧金额 + 支付按钮 -->
<view class="bill-right">
<text class="amount">{{ formatMoney(item.billAmount) }}</text>
<button class="pay-btn" @click="goPay([item])" :disabled="isPaying">
去支付
</button>
</view>
</view>
</view>
<view v-else class="empty">
<u-empty mode="list" text="暂无待支付账单" />
</view>
<u-loadmore :status="loadStatus" />
</scroll-view>
<!-- 底部批量支付栏 -->
<view v-if="selectedBills.length > 0" class="batch-pay-bar">
<view class="batch-pay-bar-left"><text>已选 {{ selectedBills.length }} </text></view>
<view class="batch-pay-bar-center">
合计<text class="sumAmount">{{ formatMoney(sumAmount) }}</text>
</view>
<view class="batch-pay-bar-right">
<button class="bottomPayBtn" type="primary" size="small"
@click="goPay(selectedBills)" :disabled="isPaying">
批量支付
</button>
</view>
</view>
</view>
</template>
<script>
import DateFilter from '../../components/DatePicker/DateFilter.vue';
export default {
components: { DateFilter },
data() {
return {
pageNo: 1,
pageSize: 10,
flowList: [],
isRefreshing: false,
loadStatus: 'more', // ✅ 修复为正确状态
sumAmount: 0,
selectedBills: [],
startDate: "",
endDate: "",
minDate: "2000-01-01",
maxDate: "2199-12-31",
isPaying: false
};
},
onLoad(options) {
// loadBills 移到 onShow 中,等 token 校验通过后再加载
},
async onShow() {
try {
await this.$checkToken(this.$getToken())
this.checkOaAuth()
this.loadBills()
} catch (e) {
return
}
},
// ❌ 已删除冲突的 onReachBottom
methods: {
checkOaAuth() {
const token = this.$getToken()
if (!token) return
const userInfo = uni.getStorageSync('userInfo')
if (!userInfo || !userInfo.oaAuth) {
uni.showModal({
title: '提示',
content: '您还未实名认证,请先完成实名认证',
showCancel: false,
confirmText: '去认证',
success: () => {
uni.redirectTo({ url: '/pages-biz/profile/profile' })
}
})
}
},
loadBills() {
// ✅ 正确判断状态
if (this.loadStatus !== 'more') return;
this.loadStatus = 'loading';
const token = this.$getToken()
let url = '/bill/pageQueryUnpaidMargin'
this.$u.post(url, {
pageNo: this.pageNo,
pageSize: this.pageSize,
startDate: this.startDate,
endDate: this.endDate,
billStatus: '待缴费'
}, {
WT: token
}).then(res => {
const rows = res.data.result || [];
rows.forEach(item => {
item.checked = false;
});
if (this.pageNo === 1) this.flowList = [];
this.flowList = this.flowList.concat(rows);
if (rows.length < this.pageSize) {
this.loadStatus = 'nomore';
} else {
this.pageNo++;
this.loadStatus = 'more'; // ✅ 恢复可加载
}
}).catch(err => {
console.log("获取待付保证金账单记录失败:", err)
this.loadStatus = 'more'; // ✅ 异常恢复
})
},
onDateFilterChange({ start, end }) {
this.loadStatus = 'more'
this.refresh();
},
refresh() {
this.isRefreshing = true;
this.pageNo = 1;
this.selectedBills = [];
this.sumAmount = 0;
setTimeout(() => {
this.loadBills();
this.isRefreshing = false;
}, 500);
},
loadMore() {
this.loadBills();
},
formatMoney(val) {
if (val === null || val === undefined || isNaN(val)) return '—';
val = Number(val);
if (val >= 10000) {
return '¥' + (val / 10000).toFixed(2) + '万';
}
return '¥' + val.toFixed(2);
},
toggleCheck(item) {
item.checked = !item.checked;
if (item.checked) {
if (!this.selectedBills.some(b => b.billNo === item.billNo)) {
this.selectedBills.push(item);
}
} else {
this.selectedBills = this.selectedBills.filter(b => b.billNo !== item.billNo);
}
this.calcTotalAmount();
},
calcTotalAmount() {
let sum = 0;
this.selectedBills.forEach(b => {
sum += Number(b.billAmount || 0);
});
this.sumAmount = sum;
},
updateSelected() {
this.selectedBills = this.flowList.filter(b => b.checked);
this.calcTotalAmount();
},
goPayTest(billList) {
if (!billList || billList.length === 0) return;
const order = {
timeStamp: String(Date.now()),
nonceStr: Math.random().toString(36).substr(2, 15),
package: 'prepay_id=TEST1234567890',
signType: 'MD5',
paySign: 'TEST_SIGN',
};
uni.requestPayment({
...order,
success: () => {
uni.showToast({ title: '支付成功(测试)', icon: 'success' });
billList.forEach(b => b.checked = false);
this.updateSelected();
this.refresh();
},
fail: () => {
uni.showToast({ title: '支付失败(测试)', icon: 'none' });
}
});
},
updateBillStatus(billIds) {
const token = this.$getToken()
this.$u.post('/bill/updateRentBillStatus', { billIds }, { WT: token })
},
async goPay(billList) {
if (this.isPaying) return;
if (!billList || billList.length === 0) return;
this.isPaying = true;
const token = this.$getToken()
try {
let unpaidBillVos = []
let tempSumAmount = 0
billList.forEach(bill => {
unpaidBillVos.push({
billNo: bill.billNo,
bizType: 'margin',
amount: bill.billAmount
})
tempSumAmount += Number(bill.billAmount)
})
const res = await this.$u.post('/bill/pay', {
amount: tempSumAmount,
billVoList: unpaidBillVos
}, { WT: token });
if (res.code !== 200) {
uni.showToast({ title: res.msg, icon: 'none' });
this.isPaying = false;
return;
}
const order = res.data;
let orderId = order.mainOrderId
uni.requestPayment({
timeStamp: order.miniPayRequest.timeStamp,
nonceStr: order.miniPayRequest.nonceStr,
package: order.miniPayRequest.packageStr,
signType: order.miniPayRequest.signType,
paySign: order.miniPayRequest.paySign,
success: () => {
billList.forEach(b => b.checked = false);
this.updateSelected();
this.refresh();
this.isPaying = false;
},
fail: (err) => {
this.payFailCallback(err, orderId);
this.isPaying = false;
}
});
} catch (error) {
console.error('支付请求异常', error);
uni.showToast({ title: '支付请求失败', icon: 'none' });
this.isPaying = false;
}
},
payFailCallback(err, orderId) {
console.log("触发支付失败回调")
let reason = "支付失败";
if (err.errMsg?.includes("cancel")) {
reason = "您已取消支付";
} else if (err.errMsg?.includes("fail")) {
reason = "支付失败,请重试";
}
this.$u.get(`/bill/paycallback`, {
orderId: orderId,
payResult: "FAIL"
}, { WT: this.$getToken() }).then(res => {
console.log("回调成功", res)
}).catch(err => {
console.log("回调失败", err)
})
uni.showToast({ title: reason, icon: 'none' });
}
}
}
</script>
<style lang="scss" scoped>
.pending-bill-page {
background: #f7f8fa;
min-height: 100vh;
padding-top: 175rpx;
box-sizing: border-box;
}
/* ✅ 核心修复:给滚动区域固定高度 */
.scroll-content {
height: calc(100vh - 175rpx - 110rpx);
padding: 20rpx;
box-sizing: border-box;
}
.bill-item {
display: flex;
justify-content: space-between;
background: #fff;
margin-bottom: 20rpx;
border-radius: 12rpx;
padding: 20rpx;
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05);
}
.bill-left {
display: flex;
align-items: center;
.bill-info {
display: flex;
flex-direction: column;
margin-left: 12rpx;
.bill-name {
font-size: 30rpx;
color: #2D2B2C;
font-weight: 500;
}
.bill-date {
font-size: 26rpx;
color: #86868C;
margin-top: 6rpx;
.date {
font-size: 24rpx;
color: #2D2B2C;
}
}
}
}
.bill-right {
flex-direction: column;
align-items: flex-end;
width: 30%;
.amount {
text-align: center;
font-size: 30rpx;
height: 28rpx;
line-height: 28rpx;
color: #EF8849;
}
.pay-btn {
margin-top: 12rpx;
padding: 0 40rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 6rpx;
font-size: 30rpx;
color: #fff;
background-color: #F34038;
text-align: center;
}
}
.batch-pay-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 110rpx;
padding: 0 30rpx;
background-color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 -4rpx 10rpx rgba(0, 0, 0, 0.08);
z-index: 999;
.batch-pay-bar-left {
font-size: 28rpx;
color: #86868C;
}
.batch-pay-bar-center {
font-size: 28rpx;
color: #222222;
.sumAmount {
font-size: 28rpx;
color: #ff7f00;
}
}
.batch-pay-bar-right {
flex-direction: column;
align-items: flex-end;
.bottomPayBtn {
height: 60rpx;
line-height: 60rpx;
padding: 0 40rpx;
border-radius: 6rpx;
font-size: 30rpx;
font-weight: bold;
text-align: center;
background: #F34038;
}
}
}
button[disabled] {
opacity: 0.5 !important;
background-color: #ccc !important;
color: #fff !important;
}
.empty {
margin-top: 200rpx;
text-align: center;
}
</style>