|
@@ -9,6 +9,40 @@
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
|
|
+ <view class="section-card">
|
|
|
|
|
+ <text class="section-title">🎓 完课证书</text>
|
|
|
|
|
+ <view v-if="certLoading" class="empty-text">加载中...</view>
|
|
|
|
|
+ <view v-else-if="cert.eligible && cert.certNo" class="cert-ok">
|
|
|
|
|
+ <text class="cert-no">{{ cert.certNo }}</text>
|
|
|
|
|
+ <text class="cert-tip">恭喜完成离场验收全部 6 项!</text>
|
|
|
|
|
+ <button class="mini-btn" @click="copyCertNo">复制证书号</button>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view v-else-if="cert.items && cert.items.length">
|
|
|
|
|
+ <text class="cert-progress">已完成 {{ certDoneCount }}/6 项</text>
|
|
|
|
|
+ <view class="cert-items">
|
|
|
|
|
+ <view class="cert-item" v-for="(it, idx) in cert.items" :key="idx">
|
|
|
|
|
+ <text class="cert-icon">{{ it.ok ? '✅' : '⬜' }}</text>
|
|
|
|
|
+ <text class="cert-label">{{ it.label }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view v-else class="empty-text">暂无证书信息,完成 6 项离场验收后可领取</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="section-card">
|
|
|
|
|
+ <text class="section-title">🎟️ 卡券核销</text>
|
|
|
|
|
+ <view v-if="coupons.length === 0" class="empty-text">暂无卡券</view>
|
|
|
|
|
+ <view class="coupon-list">
|
|
|
|
|
+ <view class="coupon-item" v-for="(c, idx) in coupons" :key="idx">
|
|
|
|
|
+ <view class="coupon-info">
|
|
|
|
|
+ <text class="coupon-code">{{ c.code }}</text>
|
|
|
|
|
+ <text class="coupon-status" :class="c.status">{{ couponStatusText(c) }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text v-if="c.status === 'unused'" class="redeem-btn" @click="redeem(c)">核销</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
<view class="menu-card">
|
|
<view class="menu-card">
|
|
|
<view class="menu-item" @click="goTo('/pages/enroll/list')">
|
|
<view class="menu-item" @click="goTo('/pages/enroll/list')">
|
|
|
<text class="menu-icon">🎫</text>
|
|
<text class="menu-icon">🎫</text>
|
|
@@ -36,8 +70,8 @@
|
|
|
<text class="menu-arrow">›</text>
|
|
<text class="menu-arrow">›</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="menu-item" @click="goTo('/pages/survey/index')">
|
|
<view class="menu-item" @click="goTo('/pages/survey/index')">
|
|
|
- <text class="menu-icon">🎟️</text>
|
|
|
|
|
- <text class="menu-text">我的卡券</text>
|
|
|
|
|
|
|
+ <text class="menu-icon">📝</text>
|
|
|
|
|
+ <text class="menu-text">测评调研</text>
|
|
|
<text class="menu-arrow">›</text>
|
|
<text class="menu-arrow">›</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="menu-item" @click="goTo('/pages/plan/index')">
|
|
<view class="menu-item" @click="goTo('/pages/plan/index')">
|
|
@@ -57,7 +91,17 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
|
|
+import { getMyCert, getMyCoupons, redeemCoupon } from '@/utils/api.js'
|
|
|
|
|
+
|
|
|
export default {
|
|
export default {
|
|
|
|
|
+ data() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ certLoading: true,
|
|
|
|
|
+ cert: {},
|
|
|
|
|
+ coupons: [],
|
|
|
|
|
+ redeemingId: 0
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
computed: {
|
|
computed: {
|
|
|
name() {
|
|
name() {
|
|
|
var stored = uni.getStorageSync('userInfo')
|
|
var stored = uni.getStorageSync('userInfo')
|
|
@@ -79,12 +123,74 @@ export default {
|
|
|
verifyClass() {
|
|
verifyClass() {
|
|
|
var v = this.$store.state.alumniVerify
|
|
var v = this.$store.state.alumniVerify
|
|
|
return v === 'accepted' ? 'verify-pass' : (v === 'pending' ? 'verify-pending' : 'verify-fail')
|
|
return v === 'accepted' ? 'verify-pass' : (v === 'pending' ? 'verify-pending' : 'verify-fail')
|
|
|
|
|
+ },
|
|
|
|
|
+ certDoneCount() {
|
|
|
|
|
+ var items = this.cert.items || []
|
|
|
|
|
+ var done = 0
|
|
|
|
|
+ for (var i = 0; i < items.length; i++) {
|
|
|
|
|
+ if (items[i].ok) done++
|
|
|
|
|
+ }
|
|
|
|
|
+ return done
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
+ onShow() {
|
|
|
|
|
+ this.loadCert()
|
|
|
|
|
+ this.loadCoupons()
|
|
|
|
|
+ },
|
|
|
methods: {
|
|
methods: {
|
|
|
goTo(url) {
|
|
goTo(url) {
|
|
|
uni.navigateTo({ url: url })
|
|
uni.navigateTo({ url: url })
|
|
|
},
|
|
},
|
|
|
|
|
+ loadCert() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ self.certLoading = true
|
|
|
|
|
+ getMyCert().then(function(resp) {
|
|
|
|
|
+ self.cert = resp.data || {}
|
|
|
|
|
+ }).catch(function() {
|
|
|
|
|
+ self.cert = {}
|
|
|
|
|
+ }).finally(function() {
|
|
|
|
|
+ self.certLoading = false
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ loadCoupons() {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ getMyCoupons().then(function(resp) {
|
|
|
|
|
+ self.coupons = resp.data || []
|
|
|
|
|
+ }).catch(function() {})
|
|
|
|
|
+ },
|
|
|
|
|
+ copyCertNo() {
|
|
|
|
|
+ var certNo = this.cert.certNo || ''
|
|
|
|
|
+ uni.setClipboardData({
|
|
|
|
|
+ data: certNo,
|
|
|
|
|
+ success: function() {
|
|
|
|
|
+ uni.showToast({ title: '已复制', icon: 'success' })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ couponStatusText(c) {
|
|
|
|
|
+ var map = { unused: '未使用', used: '已使用', expired: '已过期' }
|
|
|
|
|
+ return map[c.status] || c.status
|
|
|
|
|
+ },
|
|
|
|
|
+ redeem(c) {
|
|
|
|
|
+ var self = this
|
|
|
|
|
+ if (self.redeemingId === c.id) return
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '核销卡券',
|
|
|
|
|
+ content: '确定核销卡券 ' + (c.code || '') + ' 吗?',
|
|
|
|
|
+ success: function(res) {
|
|
|
|
|
+ if (!res.confirm) return
|
|
|
|
|
+ self.redeemingId = c.id
|
|
|
|
|
+ redeemCoupon({ id: c.id }).then(function() {
|
|
|
|
|
+ uni.showToast({ title: '核销成功', icon: 'success' })
|
|
|
|
|
+ self.loadCoupons()
|
|
|
|
|
+ }).catch(function(err) {
|
|
|
|
|
+ uni.showToast({ title: (err && err.message) || '核销失败', icon: 'none' })
|
|
|
|
|
+ }).finally(function() {
|
|
|
|
|
+ self.redeemingId = 0
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
handleLogout() {
|
|
handleLogout() {
|
|
|
var self = this
|
|
var self = this
|
|
|
uni.showModal({
|
|
uni.showModal({
|
|
@@ -109,10 +215,33 @@ export default {
|
|
|
.profile-info { flex: 1; }
|
|
.profile-info { flex: 1; }
|
|
|
.profile-name { display: block; font-size: 36rpx; font-weight: 700; color: #FFF; margin-bottom: 8rpx; }
|
|
.profile-name { display: block; font-size: 36rpx; font-weight: 700; color: #FFF; margin-bottom: 8rpx; }
|
|
|
.profile-role { display: block; font-size: 26rpx; color: rgba(255,255,255,0.85); margin-bottom: 4rpx; }
|
|
.profile-role { display: block; font-size: 26rpx; color: rgba(255,255,255,0.85); margin-bottom: 4rpx; }
|
|
|
-.profile-verify { display: block; font-size: 24rpx; padding: 4rpx 16rpx; border-radius: 6rpx; display: inline-block; margin-top: 8rpx; }
|
|
|
|
|
|
|
+.profile-verify { display: block; font-size: 24rpx; padding: 4rpx 16rpx; border-radius: 6rpx; margin-top: 8rpx; }
|
|
|
.verify-pass { background: rgba(34,197,94,0.2); color: #86EFAC; }
|
|
.verify-pass { background: rgba(34,197,94,0.2); color: #86EFAC; }
|
|
|
.verify-pending { background: rgba(255,183,77,0.2); color: #FFD54F; }
|
|
.verify-pending { background: rgba(255,183,77,0.2); color: #FFD54F; }
|
|
|
.verify-fail { background: rgba(239,83,80,0.2); color: #E57373; }
|
|
.verify-fail { background: rgba(239,83,80,0.2); color: #E57373; }
|
|
|
|
|
+.section-card { background: #FFF; border-radius: 16rpx; padding: 28rpx 32rpx; margin-bottom: 24rpx; }
|
|
|
|
|
+.section-title { display: block; font-size: 30rpx; font-weight: 700; color: #1E293B; margin-bottom: 16rpx; }
|
|
|
|
|
+.empty-text { font-size: 26rpx; color: #94A3B8; text-align: center; padding: 24rpx 0; }
|
|
|
|
|
+.cert-ok { display: flex; flex-direction: column; align-items: center; padding: 16rpx 0; }
|
|
|
|
|
+.cert-no { font-size: 32rpx; font-weight: 700; color: #F97316; font-family: monospace; margin-bottom: 12rpx; }
|
|
|
|
|
+.cert-tip { font-size: 26rpx; color: #22C55E; margin-bottom: 20rpx; }
|
|
|
|
|
+.mini-btn { width: 240rpx; height: 64rpx; line-height: 64rpx; background: #F97316; color: #FFF; font-size: 26rpx; border-radius: 32rpx; border: none; padding: 0; text-align: center; }
|
|
|
|
|
+.mini-btn:active { opacity: 0.85; }
|
|
|
|
|
+.cert-progress { display: block; font-size: 26rpx; color: #64748B; margin-bottom: 16rpx; }
|
|
|
|
|
+.cert-items { }
|
|
|
|
|
+.cert-item { display: flex; align-items: center; padding: 10rpx 0; }
|
|
|
|
|
+.cert-icon { font-size: 28rpx; margin-right: 12rpx; }
|
|
|
|
|
+.cert-label { font-size: 26rpx; color: #1E293B; }
|
|
|
|
|
+.coupon-list { }
|
|
|
|
|
+.coupon-item { display: flex; justify-content: space-between; align-items: center; padding: 20rpx 0; border-bottom: 1rpx solid #F1F5F9; }
|
|
|
|
|
+.coupon-info { display: flex; align-items: center; flex-wrap: wrap; flex: 1; margin-right: 16rpx; }
|
|
|
|
|
+.coupon-code { font-size: 24rpx; color: #64748B; font-family: monospace; margin-right: 16rpx; }
|
|
|
|
|
+.coupon-status { font-size: 22rpx; padding: 4rpx 12rpx; border-radius: 6rpx; }
|
|
|
|
|
+.coupon-status.unused { background: #F0FDF4; color: #22C55E; }
|
|
|
|
|
+.coupon-status.used { background: #F1F5F9; color: #94A3B8; }
|
|
|
|
|
+.coupon-status.expired { background: #FEF2F2; color: #EF4444; }
|
|
|
|
|
+.redeem-btn { font-size: 26rpx; color: #F97316; font-weight: 600; padding: 8rpx 24rpx; border: 2rpx solid #FED7AA; border-radius: 28rpx; }
|
|
|
|
|
+.redeem-btn:active { background: #FFF7ED; }
|
|
|
.menu-card { background: #FFF; border-radius: 16rpx; overflow: hidden; margin-bottom: 32rpx; }
|
|
.menu-card { background: #FFF; border-radius: 16rpx; overflow: hidden; margin-bottom: 32rpx; }
|
|
|
.menu-item { display: flex; align-items: center; padding: 28rpx 32rpx; border-bottom: 1rpx solid #F1F5F9; }
|
|
.menu-item { display: flex; align-items: center; padding: 28rpx 32rpx; border-bottom: 1rpx solid #F1F5F9; }
|
|
|
.menu-item:active { background: #F8FAFC; }
|
|
.menu-item:active { background: #F8FAFC; }
|