v4.2 冲刺版+每日推送+支付修复+全量代码评审
## 新增功能 - 冲刺版 ¥49.9/月:完整支付→激活→权益扣减链路 - 每日一题定时推送(@nestjs/schedule,早8点微信订阅消息) - miniprogram-ci 编译上传脚本(scripts/upload-mp.js) ## Bug修复 - 套餐值统一:vip→growth/sprint(interview轮次限制、analyze次数检查) - member/pay 移除开发绕过:改为订单校验后激活 - progress→report 参数名不匹配:id→interviewId - result.vue resume.create() 参数传错(对象→独立参数) - resume.vue analyze请求缺少Authorization header - bank.vue contribution请求缺少Authorization header - member.vue startPay() 缺少try/catch导致网络错误崩溃 - login.vue 调试面板 v-if="true" 生产泄漏 ## 配置 - 微信支付生产证书就位(商户号1113760598) - .env 清理冗余文件(删除.example/.production) - WX_NOTIFY_URL 更新为 zhiyinwx.yzrcloud.cn ## 文档 - PROJECT-STATUS.md v4.1→v4.2,状态全面更新 - DEPLOYMENT.md 新增小程序编译上传章节、清理检查清单
This commit is contained in:
@@ -48,6 +48,38 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 技能缺口分析 -->
|
||||
<view class="section" v-if="skillsGap">
|
||||
<view class="section-header">
|
||||
<text class="section-title">技能缺口</text>
|
||||
<text class="section-desc">{{ skillsGap.assessment }}</text>
|
||||
</view>
|
||||
<view class="gap-card card">
|
||||
<view class="gap-item" v-for="g in skillsGap.gaps" :key="g.dimension">
|
||||
<view class="gap-header">
|
||||
<text class="gap-name">{{ g.dimension }}</text>
|
||||
<text class="gap-badge" :class="g.level === '严重不足' ? 'badge-danger' : g.level === '需提升' ? 'badge-warn' : 'badge-ok'">{{ g.level }}</text>
|
||||
</view>
|
||||
<view class="gap-bar-bg">
|
||||
<view class="gap-bar-fill" :style="{ width: (g.currentScore / g.targetScore * 100) + '%' }"></view>
|
||||
<view class="gap-target" :style="{ left: '100%' }">▎</view>
|
||||
</view>
|
||||
<view class="gap-info">
|
||||
<text class="gap-score">当前 {{ g.currentScore }}分</text>
|
||||
<text class="gap-target-text">目标 {{ g.targetScore }}分</text>
|
||||
<text class="gap-diff" v-if="g.gap > 0">差 {{ g.gap }}分</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="gap-suggestions" v-if="skillsGap.suggestions.length">
|
||||
<text class="gap-suggest-title">提升建议</text>
|
||||
<view class="suggest-item" v-for="s in skillsGap.suggestions" :key="s.dimension">
|
||||
<text class="suggest-dim">{{ s.dimension }}:</text>
|
||||
<text class="suggest-tip">{{ s.tip }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 打卡日历 -->
|
||||
<view class="section">
|
||||
<view class="section-header">
|
||||
@@ -106,6 +138,7 @@ import { api } from '../../config'
|
||||
|
||||
const stats = ref({ completedInterviews: 0, avgScore: 0, streak: 0 })
|
||||
const progress = ref({ dimensions: {}, interviews: [], recentScores: [] })
|
||||
const skillsGap = ref(null)
|
||||
const dimensions = ref([
|
||||
{ key: 'logic', label: '逻辑思维', value: 0, color: 'linear-gradient(90deg, #6366F1, #818CF8)' },
|
||||
{ key: 'expression', label: '表达能力', value: 0, color: 'linear-gradient(90deg, #10B981, #34D399)' },
|
||||
@@ -138,6 +171,15 @@ onMounted(async () => {
|
||||
}
|
||||
} catch (e) { console.error(e) }
|
||||
|
||||
try {
|
||||
const gapRes = await uni.request({
|
||||
url: api('/analyze/skills-gap'), method: 'POST',
|
||||
header: { 'Authorization': `Bearer ${t}`, 'Content-Type': 'application/json' },
|
||||
data: {},
|
||||
})
|
||||
if (gapRes.statusCode === 200) skillsGap.value = gapRes.data
|
||||
} catch (e) { /* skills gap not available */ }
|
||||
|
||||
try {
|
||||
// Load stats
|
||||
const sres = await uni.request({
|
||||
@@ -173,7 +215,7 @@ const formatDate = (d) => {
|
||||
return `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${String(date.getMinutes()).padStart(2, '0')}`
|
||||
}
|
||||
const scoreClass = (s) => s >= 80 ? 'score-high' : s >= 60 ? 'score-mid' : 'score-low'
|
||||
const viewReport = (id) => uni.navigateTo({ url: `/pages/report/report?id=${id}` })
|
||||
const viewReport = (id) => uni.navigateTo({ url: `/pages/report/report?interviewId=${id}` })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -208,6 +250,24 @@ const viewReport = (id) => uni.navigateTo({ url: `/pages/report/report?id=${id}`
|
||||
.dim-bar-bg { height: 16rpx; background: #F3F4F6; border-radius: 8rpx; overflow: hidden; }
|
||||
.dim-bar-fill { height: 100%; border-radius: 8rpx; transition: width 0.8s cubic-bezier(0.4, 0, 0.2, 1); }
|
||||
|
||||
.gap-card { padding: 32rpx; border-radius: var(--radius-xl); }
|
||||
.gap-item { margin-bottom: 24rpx; }
|
||||
.gap-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8rpx; }
|
||||
.gap-name { font-size: 24rpx; font-weight: 600; color: var(--color-text); }
|
||||
.gap-badge { font-size: 20rpx; font-weight: 600; padding: 2rpx 12rpx; border-radius: 20rpx; }
|
||||
.badge-danger { background: #FEE2E2; color: #DC2626; }
|
||||
.badge-warn { background: #FEF3C7; color: #D97706; }
|
||||
.badge-ok { background: #D1FAE5; color: #059669; }
|
||||
.gap-bar-bg { height: 20rpx; background: #F3F4F6; border-radius: 10rpx; position: relative; overflow: visible; }
|
||||
.gap-bar-fill { height: 100%; border-radius: 10rpx; background: linear-gradient(90deg, #EF4444, #F59E0B, #10B981); transition: width 0.8s ease; }
|
||||
.gap-target { position: absolute; top: -4rpx; font-size: 28rpx; color: #6366F1; }
|
||||
.gap-info { display: flex; gap: 16rpx; margin-top: 6rpx; font-size: 20rpx; color: var(--color-text-tertiary); }
|
||||
.gap-diff { color: #EF4444; font-weight: 600; }
|
||||
.gap-suggestions { margin-top: 24rpx; padding-top: 20rpx; border-top: 1rpx solid var(--color-border); }
|
||||
.gap-suggest-title { font-size: 24rpx; font-weight: 700; color: var(--color-text); margin-bottom: 12rpx; display: block; }
|
||||
.suggest-item { font-size: 22rpx; color: var(--color-text-secondary); line-height: 1.6; margin-bottom: 8rpx; }
|
||||
.suggest-dim { font-weight: 600; color: var(--color-primary); }
|
||||
|
||||
.streak-card { padding: 24rpx; border-radius: var(--radius-xl); }
|
||||
.streak-grid { display: flex; justify-content: space-around; }
|
||||
.streak-day { display: flex; flex-direction: column; align-items: center; gap: 6rpx; }
|
||||
|
||||
Reference in New Issue
Block a user