整合api请求、添加购买会员卡页面、登陆页面
This commit is contained in:
@@ -4,24 +4,243 @@
|
||||
v-for="(item, index) in entries"
|
||||
:key="index"
|
||||
class="entry-item"
|
||||
@tap="QEClick(item.path)"
|
||||
@tap="handleEntryClick(item)"
|
||||
>
|
||||
<view :class="['entry-icon', { accent: item.accent }]">
|
||||
<image :src="item.icon" mode="aspectFit" class="icon-img" />
|
||||
<view v-if="item.title === '消息' && hasUnread" class="badge-dot"></view>
|
||||
</view>
|
||||
<text class="entry-title">{{ item.title }}</text>
|
||||
<text class="entry-desc">{{ item.desc }}</text>
|
||||
</view>
|
||||
|
||||
<view v-if="showCheckInMenu" class="checkin-menu-overlay" @tap="showCheckInMenu = false">
|
||||
<view class="checkin-menu" @tap.stop>
|
||||
<view class="menu-item" @tap="handleStoreCheckIn">
|
||||
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/icons/checkIn.png" mode="aspectFit" class="menu-icon" />
|
||||
<text class="menu-text">到店签到</text>
|
||||
</view>
|
||||
<view class="menu-item" @tap="handleGroupCourseCheckIn">
|
||||
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/icons/course.png" mode="aspectFit" class="menu-icon" />
|
||||
<text class="menu-text">团课签到</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { getUnreadMessageCount } from '@/api/main.js'
|
||||
|
||||
const QEClick = path => {
|
||||
uni.navigateTo({
|
||||
url:path
|
||||
})
|
||||
const unreadCount = ref(0)
|
||||
const showCheckInMenu = ref(false)
|
||||
const TEST_MODE = false
|
||||
|
||||
// 是否有未读消息(用于显示红点)
|
||||
const hasUnread = computed(() => unreadCount.value > 0)
|
||||
|
||||
const loadUnreadCount = async () => {
|
||||
if (TEST_MODE) {
|
||||
unreadCount.value = 3
|
||||
return
|
||||
}
|
||||
|
||||
const token = uni.getStorageSync('token')
|
||||
if (!token) {
|
||||
console.log('[QuickEntry] token 不存在,跳过请求')
|
||||
unreadCount.value = 0
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const userId = uni.getStorageSync('userId') || 1
|
||||
console.log('[QuickEntry] 请求未读消息数量, userId:', userId)
|
||||
const res = await getUnreadMessageCount(userId)
|
||||
console.log('[QuickEntry] 未读消息数量响应数据:', res)
|
||||
console.log('[QuickEntry] 响应完整类型:', typeof res, Object.prototype.toString.call(res))
|
||||
|
||||
let count = 0
|
||||
// 兼容多种响应格式
|
||||
if (typeof res === 'number') {
|
||||
count = res
|
||||
console.log('[QuickEntry] 使用直接数字格式')
|
||||
} else if (typeof res === 'string' && res.trim() !== '') {
|
||||
// 处理字符串数字,如 "20"
|
||||
count = parseInt(res, 10)
|
||||
console.log('[QuickEntry] 使用字符串转数字格式:', count)
|
||||
} else if (res !== null && typeof res === 'object') {
|
||||
if (typeof res.count === 'number') {
|
||||
count = res.count
|
||||
console.log('[QuickEntry] 使用 res.count')
|
||||
} else if (res.data !== undefined) {
|
||||
if (typeof res.data === 'number') {
|
||||
count = res.data
|
||||
console.log('[QuickEntry] 使用 res.data')
|
||||
} else if (typeof res.data?.count === 'number') {
|
||||
count = res.data.count
|
||||
console.log('[QuickEntry] 使用 res.data.count')
|
||||
} else if (Array.isArray(res.data?.content)) {
|
||||
count = res.data.content.length
|
||||
console.log('[QuickEntry] 使用 res.data.content.length')
|
||||
} else if (Array.isArray(res.content)) {
|
||||
count = res.content.length
|
||||
console.log('[QuickEntry] 使用 res.content.length')
|
||||
} else if (typeof res.data?.total === 'number') {
|
||||
count = res.data.total
|
||||
console.log('[QuickEntry] 使用 res.data.total')
|
||||
} else if (typeof res.total === 'number') {
|
||||
count = res.total
|
||||
console.log('[QuickEntry] 使用 res.total')
|
||||
}
|
||||
} else if (Array.isArray(res.content)) {
|
||||
count = res.content.length
|
||||
console.log('[QuickEntry] 使用 res.content.length')
|
||||
} else if (typeof res.total === 'number') {
|
||||
count = res.total
|
||||
console.log('[QuickEntry] 使用 res.total')
|
||||
}
|
||||
} else {
|
||||
console.log('[QuickEntry] 无法解析响应格式,res:', res)
|
||||
}
|
||||
|
||||
unreadCount.value = count
|
||||
uni.setStorageSync('unreadMessageCount', count)
|
||||
console.log('[QuickEntry] 最终未读数量:', count, 'hasUnread:', hasUnread.value)
|
||||
} catch (e) {
|
||||
console.error('[QuickEntry] 获取未读消息数失败:', e)
|
||||
try {
|
||||
const count = uni.getStorageSync('unreadMessageCount')
|
||||
unreadCount.value = count || 0
|
||||
} catch (e2) {
|
||||
unreadCount.value = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleEntryClick = (item) => {
|
||||
if (item.title === '消息' || item.title === '健身数据') {
|
||||
if (TEST_MODE) {
|
||||
uni.showLoading({
|
||||
title: '加载中...',
|
||||
mask: true
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
if (item.isTabBar) {
|
||||
uni.switchTab({ url: item.path })
|
||||
} else {
|
||||
uni.navigateTo({ url: item.path })
|
||||
}
|
||||
}, 300)
|
||||
return
|
||||
}
|
||||
|
||||
const token = uni.getStorageSync('token')
|
||||
if (!token) {
|
||||
uni.showModal({
|
||||
title: '请登录',
|
||||
content: '是否跳转到登录页面?',
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
confirmColor: '#FF6B35',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/login/login'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.showLoading({
|
||||
title: '加载中...',
|
||||
mask: true
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
if (item.title === '消息') {
|
||||
uni.navigateTo({
|
||||
url: `${item.path}?unreadCount=${unreadCount.value}`
|
||||
})
|
||||
} else if (item.isTabBar) {
|
||||
uni.switchTab({ url: item.path })
|
||||
} else {
|
||||
uni.navigateTo({ url: item.path })
|
||||
}
|
||||
}, 300)
|
||||
} else if (item.title === '签到') {
|
||||
const token = uni.getStorageSync('token')
|
||||
if (!token) {
|
||||
uni.showModal({
|
||||
title: '请登录',
|
||||
content: '是否跳转到登录页面?',
|
||||
confirmText: '确定',
|
||||
cancelText: '取消',
|
||||
confirmColor: '#FF6B35',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.navigateTo({
|
||||
url: '/pages/login/login'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
showCheckInMenu.value = !showCheckInMenu.value
|
||||
} else if (item.path) {
|
||||
// tabbar 页面使用 switchTab
|
||||
if (item.isTabBar) {
|
||||
uni.switchTab({
|
||||
url: item.path
|
||||
})
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: item.path
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleStoreCheckIn = () => {
|
||||
showCheckInMenu.value = false
|
||||
uni.navigateTo({
|
||||
url: '/pages/checkIn/checkIn'
|
||||
})
|
||||
}
|
||||
|
||||
const handleGroupCourseCheckIn = () => {
|
||||
showCheckInMenu.value = false
|
||||
uni.scanCode({
|
||||
onlyFromCamera: true,
|
||||
success: (res) => {
|
||||
console.log('扫码结果:', res)
|
||||
uni.showToast({
|
||||
title: '扫码成功',
|
||||
icon: 'success'
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('扫码失败:', err)
|
||||
uni.showToast({
|
||||
title: '扫码失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadUnreadCount()
|
||||
})
|
||||
|
||||
uni.$on('pageShow', () => {
|
||||
loadUnreadCount()
|
||||
})
|
||||
|
||||
const entries = [
|
||||
{
|
||||
icon: 'https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/icons/course.png',
|
||||
@@ -34,19 +253,23 @@ const entries = [
|
||||
icon: 'https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/icons/plan.png',
|
||||
title: '训练计划',
|
||||
desc: '个性定制',
|
||||
accent: true
|
||||
accent: true,
|
||||
path: "/pages/train/index",
|
||||
isTabBar: true
|
||||
},
|
||||
{
|
||||
icon: 'https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/icons/data.png',
|
||||
title: '健身数据',
|
||||
desc: '记录分析',
|
||||
accent: false
|
||||
accent: false,
|
||||
path: "/pages/memberInfo/bodyTestTrend"
|
||||
},
|
||||
{
|
||||
icon: 'https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/icons/message.png',
|
||||
title: '消息',
|
||||
desc: '通知消息',
|
||||
accent: true
|
||||
accent: true,
|
||||
path: "/pages/message/index"
|
||||
},
|
||||
{
|
||||
icon: 'https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/icons/checkIn.png',
|
||||
@@ -91,6 +314,20 @@ const entries = [
|
||||
justify-content: center;
|
||||
margin-bottom: 16rpx;
|
||||
box-shadow: 0 6rpx 20rpx rgba(130, 220, 130, 0.35);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.badge-dot {
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
right: 8rpx;
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
background: linear-gradient(135deg, #FF4757, #FF2D55);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2rpx 8rpx rgba(255, 71, 87, 0.5);
|
||||
z-index: 99;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
.icon-img {
|
||||
@@ -113,4 +350,93 @@ const entries = [
|
||||
font-size: 22rpx;
|
||||
color: var(--tabbar-text-inactive);
|
||||
}
|
||||
|
||||
.checkin-menu-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-end;
|
||||
padding-top: 280rpx;
|
||||
padding-right: 60rpx;
|
||||
animation: fadeIn 0.2s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
.checkin-menu {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 140rpx;
|
||||
background: white;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.15);
|
||||
padding: 16rpx;
|
||||
min-width: 320rpx;
|
||||
animation: slideDown 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -24rpx;
|
||||
right: 60rpx;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 24rpx solid transparent;
|
||||
border-right: 24rpx solid transparent;
|
||||
border-bottom: 24rpx solid white;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-40rpx) scale(0.95);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 32rpx;
|
||||
border-radius: 16rpx;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:active {
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
& + .menu-item {
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.menu-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #2D4A5A;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user