diff --git a/gym-manage-uniapp/common/style/base.css b/gym-manage-uniapp/common/style/base.css index b7ab5ca..730565e 100644 --- a/gym-manage-uniapp/common/style/base.css +++ b/gym-manage-uniapp/common/style/base.css @@ -8,11 +8,12 @@ */ :root { - /* ========== 主品牌色(清新浅蓝色系)========== */ - --primary-dark: #0B2B4B; /* 深蓝主色 - 用于重要文字、品牌标识,体现专业信赖感 */ - --primary-deep: #1A4A6F; /* 中深蓝色 - 用于hover状态、次级按钮、图标点缀,增加层次感 */ + /* ========== 主品牌色(首页主题蓝绿色系)========== */ + --primary-dark: #2D4A5A; /* 深蓝绿主色 - 用于重要文字、品牌标识,体现专业信赖感 */ + --primary-deep: #7AB5CC; /* 蓝绿主色 - 用于按钮、图标、标签背景,首页核心主题色 */ + --primary-light: #9CCFDF; /* 浅蓝绿 - 用于hover状态、渐变辅助,增加层次感 */ - /* 主页主题浅蓝渐变色系 */ + /* 主页主题蓝绿渐变色系 */ --primary-sky-100: #D6EEF8; /* 最浅蓝 - 渐变起始色,清新自然 */ --primary-sky-200: #E4F2FA; /* 浅蓝 - 渐变第二层 */ --primary-sky-300: #EEF6FB; /* 淡蓝 - 渐变第三层 */ @@ -24,10 +25,13 @@ --glow-blue-2: rgba(180, 220, 240, 0.3); /* 浅蓝色光晕 */ --glow-blue-3: rgba(170, 215, 238, 0.25); /* 浅蓝绿色光晕 */ - /* ========== 强调/行动色(活力橙)========== */ - --accent-orange: #FF6B35; /* 活力橙 - 主要CTA按钮、会员标识、高亮徽章、关键数据,刺激行动力 */ - --accent-orange-light: #FF8C5A; /* 浅橙色 - hover轻量背景、渐变辅助,带来温暖运动感 */ - --accent-orange-dark: #E55A2B; /* 深橙色 - 按压状态或重要警告,保持色彩体系完整 */ + /* ========== 强调/行动色(活力绿)========== */ + --accent-green: rgba(130, 220, 130, 0.9); /* 活力绿 - 主要CTA按钮、图标背景,首页核心行动色 */ + --accent-green-light: rgba(150, 230, 150, 0.8); /* 浅绿 - hover轻量背景 */ + --accent-green-dark: rgba(100, 200, 100, 1); /* 深绿 - 按压状态 */ + --accent-orange: #FF6B35; /* 活力橙 - 辅助CTA按钮、会员标识、高亮徽章、关键数据 */ + --accent-orange-light: #FF8C5A; /* 浅橙色 - hover轻量背景、渐变辅助 */ + --accent-orange-dark: #E55A2B; /* 深橙色 - 按压状态或重要警告 */ /* ========== 背景色系(主页主题)========== */ --bg-gradient-primary: linear-gradient(180deg, #D6EEF8 0%, #E4F2FA 15%, #EEF6FB 30%, #F5FAFD 50%, #FAFCFE 70%, #FFFFFF 100%); /* 主页主渐变背景 */ @@ -52,19 +56,20 @@ --info-blue: #3498DB; /* 信息蓝 - 提示气泡、帮助文字 */ /* ========== 渐变色 (提升活力感) ========== */ - --gradient-orange: linear-gradient(135deg, #FF6B35 0%, #FF8C5A 100%); /* 橙色渐变 - 会员按钮、重要徽章 */ - --gradient-blue: linear-gradient(135deg, #0B2B4B 0%, #1A4A6F 100%); /* 深蓝渐变 - 头部banner或特别卡片 */ + --gradient-green: linear-gradient(135deg, rgba(130, 220, 130, 0.9) 0%, rgba(150, 230, 150, 0.8) 100%); /* 绿色渐变 - 主要CTA按钮、图标背景 */ + --gradient-orange: linear-gradient(135deg, #FF6B35 0%, #FF8C5A 100%); /* 橙色渐变 - 辅助按钮、重要徽章 */ + --gradient-blue: linear-gradient(135deg, #7AB5CC 0%, #9CCFDF 100%); /* 蓝绿渐变 - 头部banner或特别卡片,首页核心渐变 */ --gradient-sky: linear-gradient(180deg, #D6EEF8 0%, #E4F2FA 15%, #EEF6FB 30%, #F5FAFD 50%, #FAFCFE 70%, #FFFFFF 100%); /* 主页天空渐变 - 全局背景 */ --gradient-subtle: linear-gradient(120deg, #F9FAFE 0%, #FFFFFF 100%); /* 微弱渐变 - 增加细节精致度 */ - /* ========== TabBar 配色(清新蓝调风格)========== */ + /* ========== TabBar 配色(清新蓝绿风格)========== */ /* 引用位置:components/TabBar.vue */ --tabbar-bg: rgba(200, 225, 238, 0.8); /* TabBar背景色 - 半透明浅蓝色毛玻璃效果 */ --tabbar-shadow: rgba(120, 185, 215, 0.2); /* TabBar阴影色 - 蓝色系柔和阴影 */ --tabbar-icon-inactive: gray; /* 未选中图标颜色 - 灰色 */ - --tabbar-icon-active: #5A98B0; /* 选中图标颜色 - 蓝绿色 */ + --tabbar-icon-active: #7AB5CC; /* 选中图标颜色 - 蓝绿色(首页主题色) */ --tabbar-text-inactive: #8AABBB; /* 未选中文字颜色 - 浅灰蓝 */ - --tabbar-text-active: #5A98B0; /* 选中文字颜色 - 蓝绿色(与图标一致) */ + --tabbar-text-active: #7AB5CC; /* 选中文字颜色 - 蓝绿色(与图标一致,首页主题色) */ /* ========== 通用蓝色系阴影(用于卡片、按钮等)========== */ /* 引用位置:components/index/RecommendCourses.vue, QuickEntry.vue, TodayRecommend.vue */ diff --git a/gym-manage-uniapp/components/index/TodayRecommend.vue b/gym-manage-uniapp/components/index/TodayRecommend.vue index b41f3d6..286c387 100644 --- a/gym-manage-uniapp/components/index/TodayRecommend.vue +++ b/gym-manage-uniapp/components/index/TodayRecommend.vue @@ -60,7 +60,7 @@ const recommends = [ participants: '2784' }, { - image: 'https://images.unsplash.com/photo-1581009146145-b5ef050c149a?w=300&q=80', + image: 'https://images.unsplash.com/photo-1517836357463-d25dfeac3438?w=300&q=80', title: '全身力量塑形', tags: ['50分钟', '中级'], desc: '全身综合训练,塑造完美线条', diff --git a/gym-manage-uniapp/manifest.json b/gym-manage-uniapp/manifest.json index 70f4370..ecc7130 100644 --- a/gym-manage-uniapp/manifest.json +++ b/gym-manage-uniapp/manifest.json @@ -45,7 +45,39 @@ "dSYMs" : false }, /* SDK配置 */ - "sdkConfigs" : {} + "sdkConfigs" : {}, + "icons" : { + "android" : { + "hdpi" : "unpackage/res/icons/72x72.png", + "xhdpi" : "unpackage/res/icons/96x96.png", + "xxhdpi" : "unpackage/res/icons/144x144.png", + "xxxhdpi" : "unpackage/res/icons/192x192.png" + }, + "ios" : { + "appstore" : "unpackage/res/icons/1024x1024.png", + "ipad" : { + "app" : "unpackage/res/icons/76x76.png", + "app@2x" : "unpackage/res/icons/152x152.png", + "notification" : "unpackage/res/icons/20x20.png", + "notification@2x" : "unpackage/res/icons/40x40.png", + "proapp@2x" : "unpackage/res/icons/167x167.png", + "settings" : "unpackage/res/icons/29x29.png", + "settings@2x" : "unpackage/res/icons/58x58.png", + "spotlight" : "unpackage/res/icons/40x40.png", + "spotlight@2x" : "unpackage/res/icons/80x80.png" + }, + "iphone" : { + "app@2x" : "unpackage/res/icons/120x120.png", + "app@3x" : "unpackage/res/icons/180x180.png", + "notification@2x" : "unpackage/res/icons/40x40.png", + "notification@3x" : "unpackage/res/icons/60x60.png", + "settings@2x" : "unpackage/res/icons/58x58.png", + "settings@3x" : "unpackage/res/icons/87x87.png", + "spotlight@2x" : "unpackage/res/icons/80x80.png", + "spotlight@3x" : "unpackage/res/icons/120x120.png" + } + } + } } }, /* 快应用特有相关 */ diff --git a/gym-manage-uniapp/pages.json b/gym-manage-uniapp/pages.json index 45c07e7..378467d 100644 --- a/gym-manage-uniapp/pages.json +++ b/gym-manage-uniapp/pages.json @@ -237,6 +237,7 @@ { "path": "pages/checkIn/checkIn", "style": { + "navigationBarTitleText": "会员签到" } }, diff --git a/gym-manage-uniapp/pages/checkIn/checkIn.vue b/gym-manage-uniapp/pages/checkIn/checkIn.vue index 09ec852..58d8569 100644 --- a/gym-manage-uniapp/pages/checkIn/checkIn.vue +++ b/gym-manage-uniapp/pages/checkIn/checkIn.vue @@ -81,19 +81,12 @@ - + - - - - @@ -103,9 +96,10 @@ import { ref } from 'vue'; import { onLoad, onUnload } from '@dcloudio/uni-app' // 引入状态组件(路径与你保持一致) import QrStatus from '@/components/QRCode/StatusCard.vue' -// 引入API封装 -import { getQRCode, checkIn as apiCheckIn } from '@/api/main.js' + // 测试模式配置 + const TEST_MODE = true // 开启测试模式 + let image = ref("") let width = ref(0) let height = ref(0) @@ -279,6 +273,13 @@ import { getQRCode, checkIn as apiCheckIn } from '@/api/main.js' mask: true }) + // 测试模式:直接生成假二维码,内容为"欢迎来到活氧舱" + if (TEST_MODE) { + console.log("测试模式:生成假二维码") + generateTestQRCode() + return + } + // 读取签到状态缓存(自动检查过期) // isCheckIn 代表签到状态,从缓存读取 const cachedIsCheckIn = getCacheData("isCheckIn") @@ -313,6 +314,49 @@ import { getQRCode, checkIn as apiCheckIn } from '@/api/main.js' getStorage(null) }) + // 测试模式:生成假二维码(内容为"欢迎来到活氧舱") + const generateTestQRCode = () => { + // 使用 canvas 生成简单的二维码图案(模拟) + // 由于无法直接生成真正的二维码图片,使用一个静态图片或占位图 + // 在实际测试环境中,可以使用第三方库生成二维码 + + // 模拟网络延迟 + setTimeout(() => { + // 使用一个在线二维码生成服务生成内容为"欢迎来到活氧舱"的二维码 + const qrContent = "欢迎来到活氧舱" + qrcode.value = qrContent + + // 使用在线API生成二维码图片 + // 注意:实际使用时应该考虑离线方案 + const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${encodeURIComponent(qrContent)}` + + // 将在线图片下载为base64(在uniapp中) + uni.request({ + url: qrUrl, + method: 'GET', + responseType: 'arraybuffer', + success: (res) => { + const base64 = uni.arrayBufferToBase64(res.data) + image.value = `data:image/png;base64,${base64}` + width.value = 500 + height.value = 500 + status.value = 'waiting' + QRStatus.value = '请出示二维码签到' + uni.hideLoading() + }, + fail: () => { + // 如果无法获取在线二维码,使用默认占位图 + image.value = '' + width.value = 500 + height.value = 500 + status.value = 'waiting' + QRStatus.value = '测试模式 - 欢迎来到活氧舱' + uni.hideLoading() + } + }) + }, 1000) + } + // 页面卸载时关闭WebSocket连接(不清除缓存,让缓存自然过期) onUnload(() => { closeWebSocket() @@ -590,13 +634,13 @@ import { getQRCode, checkIn as apiCheckIn } from '@/api/main.js' left: 0; right: 0; z-index: 99998; /* 低于底部按钮,确保底部按钮永远最上层 */ - background: linear-gradient(135deg, #0B2B4B 0%, #1A4A6F 100%); + background: linear-gradient(135deg, #7AB5CC 0%, #9CCFDF 100%); padding: 64rpx 48rpx 48rpx; text-align: center; color: #FFFFFF; border-bottom-left-radius: 56rpx; border-bottom-right-radius: 56rpx; - box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1); + box-shadow: 0 4rpx 20rpx rgba(120, 185, 215, 0.3); .header-title { font-size: 45rpx; @@ -632,7 +676,7 @@ import { getQRCode, checkIn as apiCheckIn } from '@/api/main.js' height: 120rpx; border-radius: 999px; overflow: hidden; - border: 4rpx solid #FF6B35; + border: 4rpx solid #7AB5CC; flex-shrink: 0; image { @@ -664,7 +708,7 @@ import { getQRCode, checkIn as apiCheckIn } from '@/api/main.js' flex-wrap: wrap; .level-badge { - background: linear-gradient(135deg, #FF6B35 0%, #FF8C5A 100%); + background: linear-gradient(135deg, #7AB5CC 0%, #9CCFDF 100%); color: white; padding: 4rpx 16rpx; border-radius: 24rpx; @@ -691,7 +735,7 @@ import { getQRCode, checkIn as apiCheckIn } from '@/api/main.js' .points-value { font-size: 38rpx; font-weight: 700; - color: #FF6B35; + color: #7AB5CC; } .points-label { @@ -763,7 +807,7 @@ import { getQRCode, checkIn as apiCheckIn } from '@/api/main.js' width: 80rpx; height: 80rpx; border: 6rpx solid #E9EDF2; - border-top-color: #FF6B35; + border-top-color: #7AB5CC; border-radius: 50%; animation: spin 1s linear infinite; } @@ -788,7 +832,7 @@ import { getQRCode, checkIn as apiCheckIn } from '@/api/main.js' position: absolute; width: 48rpx; height: 48rpx; - border: 6rpx solid #FF6B35; + border: 6rpx solid #7AB5CC; &.top-left { top: 0; @@ -855,7 +899,7 @@ import { getQRCode, checkIn as apiCheckIn } from '@/api/main.js' .tip-dot { width: 12rpx; height: 12rpx; - background: #FF6B35; + background: #7AB5CC; border-radius: 50%; margin-top: 12rpx; flex-shrink: 0; @@ -934,7 +978,7 @@ import { getQRCode, checkIn as apiCheckIn } from '@/api/main.js' } .header { - background: linear-gradient(135deg, #123A5E 0%, #1A4A6F 100%); + background: linear-gradient(135deg, #5A98B0 0%, #7AB5CC 100%); box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.3); } diff --git a/gym-manage-uniapp/pages/searchCourse/searchCourse.vue b/gym-manage-uniapp/pages/searchCourse/searchCourse.vue index 4c594da..70ffa51 100644 --- a/gym-manage-uniapp/pages/searchCourse/searchCourse.vue +++ b/gym-manage-uniapp/pages/searchCourse/searchCourse.vue @@ -172,27 +172,40 @@ - + - + - + - + - + + + + - - + + + + + + + + 加载更多... + + + + @@ -202,21 +215,19 @@ - + 已经到底啦~ - @@ -1057,7 +774,6 @@ onMounted(() => { .fixed-header { flex-shrink: 0; background: #ffffff; - /* 阻止触摸滚动穿透 */ touch-action: none; } @@ -1122,7 +838,7 @@ onMounted(() => { } } -/* 下拉刷新提示(列表顶部) */ +/* 下拉刷新提示 */ .refresh-hint { display: flex; justify-content: center; @@ -1130,7 +846,6 @@ onMounted(() => { padding: 30rpx 0; } -/* 下拉刷新内容 */ .refresh-content { display: flex; flex-direction: column; @@ -1138,7 +853,6 @@ onMounted(() => { gap: 12rpx; } -/* 刷新旋转动画 */ .refresh-spinner { width: 40rpx; height: 40rpx; @@ -1153,7 +867,6 @@ onMounted(() => { } } -/* 刷新文字 */ .refresh-text { font-size: 24rpx; color: #94a3b8; @@ -1238,7 +951,6 @@ onMounted(() => { opacity: 1; } - /* 筛选组 */ .filter-group { padding: 0 24rpx; margin-top: 20rpx; @@ -1277,14 +989,13 @@ onMounted(() => { } } - /* 筛选操作按钮 */ .filter-actions { display: flex; gap: 24rpx; padding: 0 24rpx; margin-top: 24rpx; - .btn-reset, .btn-confirm { + .btn-reset { flex: 1; padding: 20rpx; border-radius: 24rpx; @@ -1292,9 +1003,6 @@ onMounted(() => { font-size: 28rpx; font-weight: 600; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); - } - - .btn-reset { background: #f8fafc; color: #64748b; @@ -1302,15 +1010,6 @@ onMounted(() => { background: #f1f5f9; } } - - .btn-confirm { - background: linear-gradient(135deg, #f97316 0%, #ea580c 100%); - color: #ffffff; - - &:active { - transform: scale(0.98); - } - } } } @@ -1318,125 +1017,25 @@ onMounted(() => { .course-scroll { flex: 1; height: 0; - /* 确保可以滚动 */ touch-action: pan-y; -webkit-overflow-scrolling: touch; } -/* 课程列表 */ +/* 课程列表 - Grid布局 */ .course-list { - display: flex; - flex-wrap: wrap; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24rpx; padding: 24rpx; - margin: 0; - width: 100%; box-sizing: border-box; - min-height: 120%; /* 确保骨架屏显示时可以滚动 */ -} - -/* 骨架屏课程项 - 与真实卡片样式一致 */ -.skeleton-course-item { - width: calc(50% - 13rpx); - margin-right: 24rpx; - margin-bottom: 24rpx; - background: #ffffff; - border-radius: 24rpx; - overflow: hidden; - box-sizing: border-box; - box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08); - - &:nth-child(2n) { - margin-right: 0; - } - - .skeleton-card-image-wrapper { - position: relative; - width: 100%; - padding-top: 70%; - overflow: hidden; - - .skeleton-card-image { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%); - background-size: 200% 100%; - animation: shimmer 1.5s infinite; - } - } - - .skeleton-card-content { - padding: 20rpx; - - .skeleton-card-tag { - display: inline-block; - padding: 6rpx 16rpx; - background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%); - background-size: 200% 100%; - animation: shimmer 1.5s infinite; - border-radius: 8rpx; - margin-bottom: 16rpx; - } - - .skeleton-card-title { - height: 36rpx; - width: 90%; - background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%); - background-size: 200% 100%; - animation: shimmer 1.5s infinite; - border-radius: 8rpx; - margin-bottom: 16rpx; - } - - .skeleton-card-meta { - display: flex; - align-items: center; - gap: 24rpx; - - .skeleton-meta-item { - height: 28rpx; - background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%); - background-size: 200% 100%; - animation: shimmer 1.5s infinite; - border-radius: 8rpx; - - &.duration { - width: 100rpx; - } - - &.level { - width: 80rpx; - } - - &.participants { - width: 120rpx; - } - } - } - } -} - -@keyframes shimmer { - 0% { background-position: 200% 0; } - 100% { background-position: -200% 0; } } /* 课程卡片 */ .course-card { - width: calc(50% - 13rpx); - margin-right: 24rpx; - margin-bottom: 24rpx; + width: 100%; background: #ffffff; border-radius: 24rpx; overflow: hidden; - box-sizing: border-box; - - /* 偶数个卡片移除右边距 */ - &:nth-child(2n) { - margin-right: 0; - } box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08); transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); @@ -1518,8 +1117,9 @@ onMounted(() => { .course-meta { display: flex; - gap: 16rpx; + gap: 12rpx; margin-bottom: 16rpx; + flex-wrap: wrap; .meta-item { display: flex; @@ -1571,6 +1171,79 @@ onMounted(() => { } } +/* 骨架屏样式 */ +.skeleton-course { + .card-image-wrapper { + .skeleton-card-image { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + } + } + + .card-content { + .skeleton-card-tag { + display: inline-block; + width: 80rpx; + height: 32rpx; + background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + border-radius: 8rpx; + margin-bottom: 16rpx; + } + + .skeleton-card-title { + height: 36rpx; + width: 90%; + background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + border-radius: 8rpx; + margin-bottom: 16rpx; + } + + .skeleton-meta-item { + height: 32rpx; + background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + border-radius: 8rpx; + + &.duration { + width: 80rpx; + } + + &.level { + width: 60rpx; + } + + &.course-type { + width: 60rpx; + } + } + + .skeleton-footer { + height: 28rpx; + width: 120rpx; + background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + border-radius: 8rpx; + } + } +} + +@keyframes shimmer { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } +} + /* 空状态 */ .empty-state { display: flex; @@ -1617,61 +1290,24 @@ onMounted(() => { } } -/* 加载状态 */ -.loading-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 40rpx; - - .loading-spinner { - width: 48rpx; - height: 48rpx; - - .spinner { - width: 100%; - height: 100%; - border: 4rpx solid #f3f3f3; - border-top: 4rpx solid #f97316; - border-radius: 50%; - animation: spin 1s linear infinite; - } - } - - .loading-text { - font-size: 26rpx; - color: #94a3b8; - margin-top: 16rpx; - } -} - -/* 加载更多 */ -.load-more { - padding: 32rpx; - text-align: center; - font-size: 26rpx; - color: #94a3b8; -} - -/* 加载更多中 */ +/* 加载更多状态 */ .loading-more-state { display: flex; flex-direction: column; align-items: center; justify-content: center; - padding: 24rpx; + padding: 32rpx; .loading-spinner { &.small { - width: 32rpx; - height: 32rpx; + width: 40rpx; + height: 40rpx; .spinner { width: 100%; height: 100%; - border: 3rpx solid #f3f3f3; - border-top: 3rpx solid #f97316; + border: 4rpx solid #f3f3f3; + border-top: 4rpx solid #f97316; border-radius: 50%; animation: spin 1s linear infinite; } @@ -1691,10 +1327,8 @@ onMounted(() => { text-align: center; font-size: 24rpx; color: #cbd5e1; - margin: auto; } -/* 旋转动画 */ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); }