c4ca8a87fa
- sign页面与签约宝精简版对齐:查看合同入口、AI积分区块(合同审查/我的报告/积分充值)
- 新增pages/ai/{points,review,reports}三个页面(与签约宝精简版一致)
- pages.json注册AI页面路由
243 lines
5.6 KiB
Vue
243 lines
5.6 KiB
Vue
<template>
|
||
<view class="page-wrap">
|
||
<uititle title="积分管理"></uititle>
|
||
|
||
<!-- 积分概览 -->
|
||
<view class="points-header">
|
||
<view class="ph-card main">
|
||
<view class="ph-label">可用积分</view>
|
||
<view class="ph-value">{{ pointsBalance }}</view>
|
||
</view>
|
||
<view class="ph-card sub">
|
||
<view class="ph-label">累计获得</view>
|
||
<view class="ph-value">{{ pointsTotalEarned }}</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 充值套餐 -->
|
||
<view class="section-title">💎 积分充值</view>
|
||
<view class="recharge-grid">
|
||
<view v-for="(pkg, i) in rechargePackages" :key="i"
|
||
:class="['recharge-card', selectedRecharge === i && 'selected']"
|
||
@click="selectedRecharge = i">
|
||
<view class="rc-points">{{ pkg.points }}</view>
|
||
<view class="rc-price">{{ pkg.price }}</view>
|
||
<view v-if="pkg.bonus" class="rc-bonus">{{ pkg.bonus }}</view>
|
||
</view>
|
||
</view>
|
||
<view class="p-20">
|
||
<u-button type="primary" shape="circle" @click="doRecharge">
|
||
立即充值 {{ rechargePackages[selectedRecharge].price }}({{ rechargePackages[selectedRecharge].points }} 积分)
|
||
</u-button>
|
||
</view>
|
||
|
||
<!-- 积分明细 -->
|
||
<view class="section-title">📊 积分明细</view>
|
||
<view v-if="pointsLogList.length === 0" class="empty-tip">暂无积分记录</view>
|
||
<view v-for="(item, i) in pointsLogList" :key="i" class="log-item">
|
||
<view class="log-left">
|
||
<view class="log-reason">{{ item.reason }}</view>
|
||
<view class="log-time">{{ item._add_time_str }}</view>
|
||
</view>
|
||
<view :class="['log-amount', item.type === 'earn' ? 'green' : 'red']">
|
||
{{ item.type === 'earn' ? '+' : '-' }}{{ Math.abs(item.amount) }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import uititle from '@/components/common/title'
|
||
import dayjs from 'dayjs'
|
||
|
||
export default {
|
||
components: { uititle },
|
||
data() {
|
||
return {
|
||
pointsBalance: 0,
|
||
pointsTotalEarned: 0,
|
||
pointsLogList: [],
|
||
selectedRecharge: 1,
|
||
rechargePackages: [
|
||
{ points: 10000, price: '20元', bonus: '' },
|
||
{ points: 50000, price: '100元', bonus: '' },
|
||
{ points: 100000, price: '200元', bonus: '' },
|
||
{ points: 450000, price: '900元', bonus: '' },
|
||
{ points: 1000000, price: '2000元', bonus: '' },
|
||
{ points: 2000000, price: '4000元', bonus: '' },
|
||
]
|
||
}
|
||
},
|
||
onShow() {
|
||
this.getPointsBalance()
|
||
this.getPointsLog()
|
||
},
|
||
methods: {
|
||
async getPointsBalance() {
|
||
const res = await vk.callFunction({ url: 'client/points/kh/getBalance' })
|
||
if (res.code === 0 && res.data) {
|
||
this.pointsBalance = res.data.balance || 0
|
||
this.pointsTotalEarned = res.data.total_earned || 0
|
||
}
|
||
},
|
||
async getPointsLog() {
|
||
const res = await vk.callFunction({
|
||
url: 'client/points/kh/getLog',
|
||
data: { pageIndex: 1, pageSize: 50 }
|
||
})
|
||
if (res.code === 0 && res.data) {
|
||
this.pointsLogList = (res.data.rows || []).map(r => ({
|
||
...r,
|
||
_add_time_str: dayjs(r._add_time && r._add_time['$date'] ? r._add_time['$date'] : r._add_time).format('YYYY-MM-DD HH:mm')
|
||
}))
|
||
}
|
||
},
|
||
async doRecharge() {
|
||
const pkg = this.rechargePackages[this.selectedRecharge]
|
||
const priceMap = {
|
||
'20元': 2000, '100元': 10000, '200元': 20000,
|
||
'900元': 90000, '2000元': 200000, '4000元': 400000
|
||
}
|
||
const price = priceMap[pkg.price] || 2000
|
||
const res = await vk.callFunction({
|
||
url: 'client/points/kh/createOrder',
|
||
data: { points: pkg.points, price }
|
||
})
|
||
if (res.code === 0 && res.data) {
|
||
uni.navigateTo({ url: '/pages/shop/pay?id=' + res.data })
|
||
} else {
|
||
uni.showToast({ title: res.msg || '创建订单失败', icon: 'none' })
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.page-wrap {
|
||
min-height: 100vh;
|
||
background: #f5f5f5;
|
||
}
|
||
|
||
.points-header {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.ph-card {
|
||
flex: 1;
|
||
padding: 30rpx;
|
||
border-radius: 16rpx;
|
||
color: #fff;
|
||
text-align: center;
|
||
}
|
||
|
||
.ph-card.main {
|
||
background: linear-gradient(135deg, #a92924, #d4574e);
|
||
}
|
||
|
||
.ph-card.sub {
|
||
background: linear-gradient(135deg, #333, #666);
|
||
}
|
||
|
||
.ph-label {
|
||
font-size: 24rpx;
|
||
opacity: 0.85;
|
||
}
|
||
|
||
.ph-value {
|
||
font-size: 48rpx;
|
||
font-weight: 700;
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
padding: 20rpx 30rpx 10rpx;
|
||
}
|
||
|
||
.recharge-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 12rpx;
|
||
padding: 0 30rpx;
|
||
}
|
||
|
||
.recharge-card {
|
||
flex: 0 0 calc(33.33% - 12rpx);
|
||
padding: 24rpx 0;
|
||
text-align: center;
|
||
border: 2rpx solid #e0e0e0;
|
||
border-radius: 12rpx;
|
||
background: #fff;
|
||
position: relative;
|
||
}
|
||
|
||
.recharge-card.selected {
|
||
border-color: #a92924;
|
||
background: #fdf0f0;
|
||
}
|
||
|
||
.rc-points {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: #333;
|
||
}
|
||
|
||
.rc-price {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
margin-top: 4rpx;
|
||
}
|
||
|
||
.rc-bonus {
|
||
position: absolute;
|
||
top: -2rpx;
|
||
right: -2rpx;
|
||
background: #a92924;
|
||
color: #fff;
|
||
font-size: 18rpx;
|
||
padding: 2rpx 10rpx;
|
||
border-radius: 0 10rpx 0 10rpx;
|
||
}
|
||
|
||
.log-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 24rpx 30rpx;
|
||
background: #fff;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
}
|
||
|
||
.log-left {
|
||
flex: 1;
|
||
}
|
||
|
||
.log-reason {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.log-time {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
margin-top: 4rpx;
|
||
}
|
||
|
||
.log-amount {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.log-amount.green { color: #67c23a; }
|
||
.log-amount.red { color: #f56c6c; }
|
||
|
||
.empty-tip {
|
||
text-align: center;
|
||
padding: 60rpx 0;
|
||
color: #999;
|
||
font-size: 26rpx;
|
||
}
|
||
</style> |