统一排行榜对齐星雀UI,页面资源后台配置实时刷新

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-06-27 01:26:11 +08:00
parent 1ab2e2080a
commit 21112173f2
292 changed files with 64010 additions and 81 deletions

View File

@@ -0,0 +1,178 @@
// components/fakuan-list/fakuan-pay/fakuan-pay.js
import request from '../../../../utils/request.js'; // 路径保持您原来的样子
Component({
properties: {
visible: { type: Boolean, value: false, observer: 'onVisibleChange' },
fadan: { type: Object, value: null } // { id, fakuanjine }
},
data: {
step: 'method', // method / balance / confirm / loading
balanceOptions: [],
selectedBalance: null,
loadingText: '',
payLoading: false,
dingdanid: '' // 微信支付内部订单ID
},
methods: {
onVisibleChange(val) {
if (val) {
// 每次打开重置为支付方式选择
this.setData({ step: 'method', balanceOptions: [], selectedBalance: null });
}
},
close() {
this.setData({ visible: false });
this.triggerEvent('close');
},
// 选择支付方式
async chooseMethod(e) {
const method = e.currentTarget.dataset.method;
if (method === 'wx') {
this.wxPay();
} else if (method === 'balance') {
this.fetchBalanceOptions();
}
},
// ========== 微信支付流程 ==========
async wxPay() {
this.setData({ step: 'loading', loadingText: '发起支付中...' });
try {
const res = await request({
url: '/yonghu/fkjn',
method: 'POST',
data: { fadan_id: this.properties.fadan.id }
});
if (res.data.code !== 200) throw new Error(res.data.msg || '下单失败');
// 🔥 唯一修改点:后端返回格式 { code:200, payParams:{...}, dingdanid:'...' }
const payParams = res.data.payParams;
const dingdanid = res.data.dingdanid;
this.setData({ dingdanid });
await this.requestWxPayment(payParams);
} catch (e) {
this.setData({ step: 'method' });
wx.showToast({ title: e.message, icon: 'none' });
}
},
async requestWxPayment(payParams) {
return new Promise((resolve, reject) => {
if (!payParams || !payParams.timeStamp || !payParams.paySign) {
wx.showToast({ title: '支付参数错误', icon: 'none' });
reject(new Error('支付参数错误'));
return;
}
wx.requestPayment({
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
package: payParams.package,
signType: payParams.signType || 'MD5',
paySign: payParams.paySign,
success: () => {
this.pollPayStatus();
resolve();
},
fail: (err) => {
this.setData({ step: 'method' });
this.notifyPayFail();
reject(err);
}
});
});
},
async pollPayStatus() {
this.setData({ step: 'loading', loadingText: '确认支付结果...' });
for (let i = 0; i < 10; i++) {
try {
const res = await request({
url: '/yonghu/fkzffc',
method: 'POST',
data: { dingdanid: this.data.dingdanid }
});
if (res.data.code === 200) {
this.triggerEvent('success');
this.close();
wx.showToast({ title: '缴纳成功', icon: 'success' });
return;
}
} catch (e) {}
await new Promise(r => setTimeout(r, 2000));
}
this.setData({ step: 'method' });
wx.showToast({ title: '支付确认超时', icon: 'none' });
},
async notifyPayFail() {
try {
await request({
url: '/yonghu/fkzfsb',
method: 'POST',
data: { dingdanid: this.data.dingdanid }
});
} catch (e) {}
},
// ========== 佣金抵扣流程 ==========
async fetchBalanceOptions() {
this.setData({ step: 'loading', loadingText: '获取可抵扣身份...' });
try {
const res = await request({
url: '/shangpin/czhqdy',
method: 'POST',
data: {
leixing: 4,
fadan_id: this.properties.fadan.id,
jine: this.properties.fadan.fakuanjine
}
});
if (res.data.code !== 200) throw new Error(res.data.msg || '获取失败');
this.setData({ balanceOptions: res.data.data, step: 'balance' });
} catch (e) {
this.setData({ step: 'method' });
wx.showToast({ title: e.message, icon: 'none' });
}
},
selectBalance(e) {
const id = e.currentTarget.dataset.id;
const info = this.data.balanceOptions.find(opt => opt.id === id);
if (!info) return;
this.setData({ selectedBalance: info, step: 'confirm' });
},
backToMethod() {
this.setData({ step: 'method' });
},
async confirmBalance() {
if (!this.data.selectedBalance) return;
this.setData({ step: 'loading', loadingText: '抵扣扣款中...' });
try {
const res = await request({
url: '/shangpin/dsqrgmdh',
method: 'POST',
data: {
leixing: 4,
shenfen_id: this.data.selectedBalance.id,
fadan_id: this.properties.fadan.id
}
});
if (res.data.code !== 200) throw new Error(res.data.msg || '抵扣失败');
this.triggerEvent('success');
this.close();
wx.showToast({ title: '缴纳成功', icon: 'success' });
} catch (e) {
this.setData({ step: 'method' });
wx.showToast({ title: e.message, icon: 'none' });
}
}
}
});

View File

@@ -0,0 +1,3 @@
{
"component": true
}

View File

@@ -0,0 +1,61 @@
<!-- 支付组件模板 -->
<view wx:if="{{ visible }}" class="pay-mask" catchtouchmove="prevent">
<!-- 支付方式选择 -->
<view wx:if="{{ step === 'method' }}" class="pay-panel">
<view class="pay-head">
<text class="pay-title">选择支付方式</text>
<view class="pay-close" bindtap="close">✕</view>
</view>
<view class="pay-body">
<view class="method-item" bindtap="chooseMethod" data-method="wx">
<text class="method-icon">💳</text>
<text class="method-name">微信支付</text>
</view>
<view class="method-item" bindtap="chooseMethod" data-method="balance">
<text class="method-icon">💰</text>
<text class="method-name">佣金抵扣</text>
</view>
</view>
</view>
<!-- 余额抵扣身份选择 -->
<view wx:if="{{ step === 'balance' }}" class="pay-panel">
<view class="pay-head">
<text class="pay-title">选择抵扣身份</text>
<view class="pay-close" bindtap="close">✕</view>
</view>
<scroll-view scroll-y class="pay-body">
<view wx:for="{{ balanceOptions }}" wx:key="id" class="balance-item" bindtap="selectBalance" data-id="{{ item.id }}">
<text class="balance-name">{{ item.jieshao }}</text>
<text class="balance-amount">需¥{{ item.xuyao }}</text>
</view>
</scroll-view>
</view>
<!-- 余额抵扣确认 -->
<view wx:if="{{ step === 'confirm' }}" class="pay-panel">
<view class="pay-head">
<text class="pay-title">确认抵扣</text>
<view class="pay-close" bindtap="close">✕</view>
</view>
<view class="pay-body">
<view class="confirm-row">
<text>抵扣身份:{{ selectedBalance.jieshao }}</text>
</view>
<view class="confirm-row">
<text>需扣除:¥{{ selectedBalance.xuyao }}</text>
</view>
<text class="confirm-tip">确认后将从您的佣金/分红中扣除</text>
</view>
<view class="pay-foot">
<view class="btn default" bindtap="backToMethod">取消</view>
<view class="btn primary" bindtap="confirmBalance">确认</view>
</view>
</view>
<!-- 支付加载动画 -->
<view wx:if="{{ step === 'loading' }}" class="pay-mask loading-box">
<view class="spinner"></view>
<text class="loading-text">{{ loadingText }}</text>
</view>
</view>

View File

@@ -0,0 +1,59 @@
/* 支付组件样式,与罚款列表统一 */
.pay-mask {
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.5); z-index: 2000;
display: flex; align-items: flex-end; justify-content: center;
}
.pay-panel {
width: 100%; max-height: 85vh; background: #fff;
border-radius: 24rpx 24rpx 0 0; display: flex; flex-direction: column; overflow: hidden;
}
.pay-head {
display: flex; justify-content: space-between; align-items: center;
padding: 28rpx 30rpx 16rpx; border-bottom: 1px solid #eee;
}
.pay-title { font-size: 32rpx; font-weight: 600; }
.pay-close {
width: 44rpx; height: 44rpx; background: #f5f5f5;
border-radius: 50%; display: flex; align-items: center; justify-content: center;
font-size: 26rpx; color: #888;
}
.pay-body { padding: 20rpx 30rpx; flex: 1; overflow-y: auto; }
.pay-foot {
display: flex; padding: 16rpx 30rpx 30rpx; gap: 20rpx; border-top: 1px solid #eee;
}
.method-item {
display: flex; flex-direction: column; align-items: center;
padding: 40rpx 30rpx; background: #f9f9f9; border-radius: 20rpx; margin-bottom: 20rpx;
}
.method-icon { font-size: 48rpx; margin-bottom: 12rpx; }
.method-name { font-size: 28rpx; color: #333; }
.balance-item {
display: flex; justify-content: space-between; align-items: center;
padding: 24rpx 0; border-bottom: 1rpx solid #f0f0f0;
}
.balance-name { font-size: 28rpx; color: #333; }
.balance-amount { font-size: 28rpx; color: #E53935; }
.confirm-row { font-size: 28rpx; margin-bottom: 16rpx; }
.confirm-tip { font-size: 24rpx; color: #888; display: block; margin-top: 20rpx; }
.loading-box {
display: flex; flex-direction: column; align-items: center; justify-content: center;
background: rgba(0,0,0,0.6);
}
.spinner {
width: 60rpx; height: 60rpx;
border: 4rpx solid rgba(255,255,255,0.3);
border-top-color: #fff; border-radius: 50%;
animation: spin 1s linear infinite; margin-bottom: 20rpx;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text { color: #fff; font-size: 26rpx; }
.btn { flex: 1; text-align: center; padding: 22rpx 0; border-radius: 16rpx; font-size: 28rpx; font-weight: 600; }
.primary { background: #2e7d32; color: #fff; }
.default { background: #f0f0f0; color: #555; }
.btn:active { opacity: 0.8; }