feat: 同步签约宝手机端功能到签约宝页面

- sign页面与签约宝精简版对齐:查看合同入口、AI积分区块(合同审查/我的报告/积分充值)
- 新增pages/ai/{points,review,reports}三个页面(与签约宝精简版一致)
- pages.json注册AI页面路由
This commit is contained in:
2026-10-23 19:57:27 +08:00
parent e134209d16
commit c4ca8a87fa
1301 changed files with 314434 additions and 207 deletions
+243
View File
@@ -0,0 +1,243 @@
<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>