From 823d62644019b3b73b47b7d33b2fb09aa4f699e5 Mon Sep 17 00:00:00 2001 From: future <1360317836@qq.com> Date: Fri, 5 Jun 2026 21:26:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=90=9C=E7=B4=A2=E8=AF=BE?= =?UTF-8?q?=E7=A8=8B=E5=92=8C=E5=8A=A0=E8=BD=BD=E7=BB=84=E4=BB=B6=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=EF=BC=8C=E7=AD=BE=E5=88=B0=E9=A1=B5=E9=9D=A2=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E9=81=AE=E7=BD=A9=E9=98=B2=E9=87=8D=E5=A4=8D=E6=89=AB?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=20request=20=E4=BE=BF?= =?UTF-8?q?=E6=8D=B7=E6=96=B9=E6=B3=95=EF=BC=88get/post/put/delete?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gym-manage-uniapp/App.vue | 104 +- gym-manage-uniapp/api/main.js | 50 + gym-manage-uniapp/common/constants/routes.js | 2 +- gym-manage-uniapp/components/TabBar.vue | 138 +- .../components/index/QuickEntry.vue | 7 +- .../components/index/RecommendCourses.vue | 302 ++- gym-manage-uniapp/pages.json | 60 +- .../pages/LoadingOverlay/LoadingOverlay.vue | 74 + gym-manage-uniapp/pages/checkIn/checkIn.vue | 95 +- gym-manage-uniapp/pages/course/index.vue | 143 +- gym-manage-uniapp/pages/index/index.vue | 7 +- .../pages/searchCourse/searchCourse.vue | 1621 +++++++++++++++++ gym-manage-uniapp/static/tabBar/home.png | Bin 0 -> 3532 bytes gym-manage-uniapp/utils/cache.js | 89 + gym-manage-uniapp/utils/request.js | 19 +- gym-manage-uniapp/vite.config.js | 13 +- 16 files changed, 2585 insertions(+), 139 deletions(-) create mode 100644 gym-manage-uniapp/api/main.js create mode 100644 gym-manage-uniapp/pages/LoadingOverlay/LoadingOverlay.vue create mode 100644 gym-manage-uniapp/pages/searchCourse/searchCourse.vue create mode 100644 gym-manage-uniapp/static/tabBar/home.png create mode 100644 gym-manage-uniapp/utils/cache.js diff --git a/gym-manage-uniapp/App.vue b/gym-manage-uniapp/App.vue index dabb9d5..7e8caf1 100644 --- a/gym-manage-uniapp/App.vue +++ b/gym-manage-uniapp/App.vue @@ -1,28 +1,82 @@ + - + \ No newline at end of file diff --git a/gym-manage-uniapp/api/main.js b/gym-manage-uniapp/api/main.js new file mode 100644 index 0000000..eb59c09 --- /dev/null +++ b/gym-manage-uniapp/api/main.js @@ -0,0 +1,50 @@ +import request from "@/utils/request.js" + +export function login(params) { + return request.post('/member/auth/miniapp/login', params) +} + +export function logout() { + return request.post('/member/auth/logout') +} + +export function getQRCode(options = { cache: true, cacheTime: 5 * 60 * 1000 }) { + return request.get('/checkIn/qrcode', {}, options) +} + +export function checkIn(params) { + return request.post('/checkIn/scan', params) +} + +export function getUserInfo(options = { cache: true, cacheTime: 30 * 60 * 1000 }) { + return request.get('/member/info', {}, options) +} + +export function updateUserInfo(params) { + return request.put('/member/info', params) +} + +export function getRecommendCourses(options = { cache: true, cacheTime: 10 * 60 * 1000 }) { + return request.get('/course/recommend', {}, options) +} + +export function getCourseDetail(id, options = { cache: true, cacheTime: 15 * 60 * 1000 }) { + return request.get(`/course/${id}`, {}, options) +} + +export function getGroupCoursePage(params = {}, options = { cache: true, cacheTime: 5 * 60 * 1000 }) { + const { page = 0, size = 10, sort = 'id', order = 'asc', keyword } = params + return request.post('/groupCourse/page', { page, size, sort, order, keyword }, options) +} + +export default { + login, + logout, + getQRCode, + checkIn, + getUserInfo, + updateUserInfo, + getRecommendCourses, + getCourseDetail, + getGroupCoursePage +} diff --git a/gym-manage-uniapp/common/constants/routes.js b/gym-manage-uniapp/common/constants/routes.js index e824abc..dada154 100644 --- a/gym-manage-uniapp/common/constants/routes.js +++ b/gym-manage-uniapp/common/constants/routes.js @@ -1,7 +1,7 @@ /** 与 pages.json 保持一致 */ export const PAGE = { INDEX: '/pages/index/index', - COURSE: '/pages/groupCourse/list', + COURSE: '/pages/course/index', TRAIN: '/pages/train/index', DISCOVER: '/pages/discover/index', MEMBER: '/pages/memberInfo/memberInfo', diff --git a/gym-manage-uniapp/components/TabBar.vue b/gym-manage-uniapp/components/TabBar.vue index 8ad3aee..4922c27 100644 --- a/gym-manage-uniapp/components/TabBar.vue +++ b/gym-manage-uniapp/components/TabBar.vue @@ -1,14 +1,15 @@ + + \ No newline at end of file diff --git a/gym-manage-uniapp/pages.json b/gym-manage-uniapp/pages.json index da2f516..b22206c 100644 --- a/gym-manage-uniapp/pages.json +++ b/gym-manage-uniapp/pages.json @@ -3,32 +3,52 @@ { "path": "pages/index/index", "style": { - "navigationBarTitleText": "健身房" + "navigationBarTitleText": "健身房", + "app-plus": { + "animationType": "fade-in", + "animationDuration": 200 + } } }, { "path": "pages/course/index", "style": { - "navigationBarTitleText": "课程" + "navigationBarTitleText": "课程", + "app-plus": { + "animationType": "fade-in", + "animationDuration": 200 + } } }, { "path": "pages/train/index", "style": { - "navigationBarTitleText": "训练" + "navigationBarTitleText": "训练", + "app-plus": { + "animationType": "fade-in", + "animationDuration": 200 + } } }, { "path": "pages/discover/index", "style": { - "navigationBarTitleText": "发现" + "navigationBarTitleText": "发现", + "app-plus": { + "animationType": "fade-in", + "animationDuration": 200 + } } }, { "path": "pages/memberInfo/memberInfo", "style": { "navigationStyle": "custom", - "navigationBarTitleText": "我的" + "navigationBarTitleText": "我的", + "app-plus": { + "animationType": "fade-in", + "animationDuration": 200 + } } }, { @@ -231,13 +251,39 @@ "navigationBarTitleText": "课程详情", "navigationStyle": "custom" } + }, + { + "path": "pages/LoadingOverlay/LoadingOverlay", + "style": { + "navigationBarTitleText": "" + } + }, + { + "path": "pages/searchCourse/searchCourse", + "style": { + "navigationBarTitleText": "搜索课程" + } } ], "globalStyle": { "navigationBarTextStyle": "black", "navigationBarTitleText": "健身房", "navigationBarBackgroundColor": "#F8F8F8", - "backgroundColor": "#F8F8F8" + "backgroundColor": "#F8F8F8", + "app-plus": { + "animationType": "pop-in", + "animationDuration": 200 + } }, - "uniIdRouter": {} + "uniIdRouter": {}, + "tabBar": { + "custom": true, // 启用自定义 tabBar + "list": [ + { "pagePath": "pages/index/index", "text": "首页" }, + { "pagePath": "pages/course/index", "text": "课程" }, + { "pagePath": "pages/train/index", "text": "训练" }, + { "pagePath": "pages/discover/index", "text": "发现" }, + { "pagePath": "pages/memberInfo/memberInfo", "text": "我的" } + ] + } } \ No newline at end of file diff --git a/gym-manage-uniapp/pages/LoadingOverlay/LoadingOverlay.vue b/gym-manage-uniapp/pages/LoadingOverlay/LoadingOverlay.vue new file mode 100644 index 0000000..045d1b1 --- /dev/null +++ b/gym-manage-uniapp/pages/LoadingOverlay/LoadingOverlay.vue @@ -0,0 +1,74 @@ + + + + + + \ No newline at end of file diff --git a/gym-manage-uniapp/pages/checkIn/checkIn.vue b/gym-manage-uniapp/pages/checkIn/checkIn.vue index 2fbbb26..09ec852 100644 --- a/gym-manage-uniapp/pages/checkIn/checkIn.vue +++ b/gym-manage-uniapp/pages/checkIn/checkIn.vue @@ -104,7 +104,7 @@ import { onLoad, onUnload } from '@dcloudio/uni-app' // 引入状态组件(路径与你保持一致) import QrStatus from '@/components/QRCode/StatusCard.vue' // 引入API封装 -import { getQRCode, checkIn as apiCheckIn } from '@/request_api/main.js' +import { getQRCode, checkIn as apiCheckIn } from '@/api/main.js' let image = ref("") let width = ref(0) @@ -115,6 +115,7 @@ import { getQRCode, checkIn as apiCheckIn } from '@/request_api/main.js' const QRStatus = ref("生成中...") const STQRC = ref(false)//是否扫码 const isCheckIn = ref(false) + const webSoketURL = "ws://localhost:8084/webSocket/checkIn" const qrcode = ref("") @@ -209,22 +210,31 @@ import { getQRCode, checkIn as apiCheckIn } from '@/request_api/main.js' } /** - * 清除所有QR_开头的缓存(用于测试阶段) + * 清除所有与签到相关的缓存(用于测试阶段) */ const clearQRCache = () => { try { const keys = uni.getStorageInfoSync().keys || [] let clearedCount = 0 for (const key of keys) { + // 清除 QR_ 开头的缓存(页面内部缓存) if (key.startsWith(CACHE_PREFIX)) { uni.removeStorageSync(key) clearedCount++ } + // 清除 API_CACHE_ 开头的缓存(通过 utils/cache.js 缓存的接口数据) + if (key.startsWith('API_CACHE_')) { + // 只清除与签到相关的 API 缓存 + if (key.includes('checkIn') || key.includes('qrcode')) { + uni.removeStorageSync(key) + clearedCount++ + } + } } - console.log(`已清除 ${clearedCount} 个QR_开头的缓存`) + console.log(`已清除 ${clearedCount} 个签到相关缓存`) return clearedCount } catch (e) { - console.error('清除QR缓存失败:', e) + console.error('清除 QR 缓存失败:', e) return 0 } } @@ -256,10 +266,8 @@ import { getQRCode, checkIn as apiCheckIn } from '@/request_api/main.js' duration: 2000 }) - // 重新请求二维码 - setTimeout(() => { - getStorage(null) - }, 500) + // 重置页面状态后不再自动请求二维码 + uni.hideLoading() } } }) @@ -319,7 +327,7 @@ import { getQRCode, checkIn as apiCheckIn } from '@/request_api/main.js' errorText.value = '' // 重置错误文本 image.value = "" - getQRCode(true).then(res => { + getQRCode({ cache: true, cacheTime: 5 * 60 * 1000 }).then(res => { console.log(res) // 保存到本地缓存(用于签到状态判断) setCacheData("QRInfo", res) @@ -403,7 +411,7 @@ import { getQRCode, checkIn as apiCheckIn } from '@/request_api/main.js' // 手动签到接口 const checkIn = (qrContent) => { console.log(qrContent) - apiCheckIn(qrContent).then(res => { + apiCheckIn({ qrContent }).then(res => { closeWebSocket() console.log(res) status.value = 'scanned' @@ -422,17 +430,20 @@ import { getQRCode, checkIn as apiCheckIn } from '@/request_api/main.js' console.error('签到请求失败:', err) status.value = 'error' errorText.value = err.message || '签到失败,请重试' // 对应错误文案 + uni.showToast({ + title: err.message, + icon: 'none' + }) }) } // 建立WebSocket连接 const connectWebSocket = (qrContent) => { - const wsUrl = `ws://192.168.43.89:8084/webSocket/checkIn` - console.log('WebSocket 连接地址:', wsUrl) + console.log('WebSocket 连接地址:', webSoketURL) socketTask = uni.connectSocket({ - url: wsUrl, + url: webSoketURL, success: () => { console.log('WebSocket 连接中...') }, @@ -477,17 +488,27 @@ import { getQRCode, checkIn as apiCheckIn } from '@/request_api/main.js' socketTask.onMessage((res) => { console.log('收到 WebSocket 消息:', res.data) const message = res.data - + if (message === '正在进行签到') { + // 显示遮罩,防止用户重复扫码 QRStatus.value = "正在进行签到..." STQRC.value = true - // status.value = 'scanned' - // errorText.value = '' // 成功重置错误文本 - // uni.showToast({ - // title: '签到成功!', - // icon: 'success', - // duration: 2000 - // }) + } else if (message === '签到成功' || message.includes('签到成功')) { + // 签到成功,更新状态 + status.value = 'scanned' + errorText.value = '' + isCheckIn.value = true + QRStatus.value = "签到成功" + // 缓存签到状态 + setCacheData("isCheckIn", true) + setCacheData("checkInTime", "签到成功") + uni.showToast({ + title: '签到成功!', + icon: 'success', + duration: 2000 + }) + // 关闭WebSocket连接 + closeWebSocket() } else if (message.startsWith('二维码无效')) { uni.showToast({ title: message, @@ -495,18 +516,32 @@ import { getQRCode, checkIn as apiCheckIn } from '@/request_api/main.js' duration: 2000 }) status.value = 'error' - errorText.value = '二维码无效,请刷新' // 对应错误文案 + errorText.value = '二维码无效,请刷新' + // 隐藏遮罩,允许用户重新操作 + STQRC.value = false + QRStatus.value = "生成中..." setTimeout(() => { closeWebSocket() }, 3000) } else if (message === '消息格式错误') { - uni.showToast({ - title: '消息格式错误', - icon: 'none' - }) status.value = 'error' - errorText.value = '消息格式错误' // 对应错误文案 - } else { + errorText.value = '消息格式错误' + // 隐藏遮罩,允许用户重新操作 + STQRC.value = false + QRStatus.value = "生成中..." + } else if (message.includes('失败') || message.includes('错误')) { + // 其他失败情况 + uni.showToast({ + title: message, + icon: 'none', + duration: 2000 + }) + status.value = 'error' + errorText.value = message + // 隐藏遮罩,允许用户重新操作 + STQRC.value = false + QRStatus.value = "生成中..." + } else { console.log('未知消息:', message) } }) @@ -523,10 +558,6 @@ import { getQRCode, checkIn as apiCheckIn } from '@/request_api/main.js' console.error('WebSocket 错误:', err) status.value = 'error' errorText.value = '连接失败,请重试' // 对应错误文案 - uni.showToast({ - title: '连接失败', - icon: 'none' - }) }) } diff --git a/gym-manage-uniapp/pages/course/index.vue b/gym-manage-uniapp/pages/course/index.vue index dc11910..82113d6 100644 --- a/gym-manage-uniapp/pages/course/index.vue +++ b/gym-manage-uniapp/pages/course/index.vue @@ -1,3 +1,4 @@ + + + \ No newline at end of file diff --git a/gym-manage-uniapp/static/tabBar/home.png b/gym-manage-uniapp/static/tabBar/home.png new file mode 100644 index 0000000000000000000000000000000000000000..c4220ccf4cc1a164d2991cb431d1a55909c13a8d GIT binary patch literal 3532 zcmeHKe@qis9EYJ)VM-ANqe5}hk?Cq#G@y*Mq978S-Nvwg)KQ7EnjuuepauIwK?1B( z%v=Vp3yoxsD8VjaT4$jLSXPbHikWu!gM%V%LBU?H$X&Vi*217g%VZ||V~a0oo7|<} z_kEvV@4bqTb|x zvnpkg`21-_a$+Kz&EA#n99-8J?f5swm#)$bhHDl!d)v0eV+zUX0PPMogPYIg(zUKB zchp`UEKS%w(iVB4D3i!&jxMjySgJ~=yr~zRq}cDWcmsDWNFDzg6N|yuM>+_|d7158 zUmtrky?WGtGoQ=dDC|)4JMKTRfPoPBM64XBi`ZQ|M($~iuVXS#$l!NLmKeDOBtW3a z`O=u7I3^6(coXK5`Et=)!Qj;u(BnBN&|e~?vmi|(r~%#dGN!7`>%A7$svMLa7!-qlx${uopwmKtaz zJy-4f$fB#$~!(yebqwWSE% zn$8+YQ>!(@_0dl?a3;1%YE9rkT}?!mg-jdm+9GX--&QI<#Cq0~XM)pQT2YlsXKN2UktmNda#UUa-UulvEhcBj@ z>W=iq%RI1_nWvR&_>BaokgTCJjp#j<=x0~Ov4dx{dNkM*%;o$$4Eyg;ZsXXRj>9nD zO+qdaPw*t6koq;vc#3KwQzf|13~L(QiV>#a>SD&=#G5RX;G4()boWJ;1Vnr%%laX0 z^xkS~T1#$?u7Rx9q2C4-N|-2prZoZKda!p&JVF(hTh`~)qq<464zHM@Z_#oU(8>_p zp@)-V6|%ObSZx_(92et$m}Q!wSX{a2EXDSdwCGU9u>1%CZYp5fvR(Sp%TbPbHE7A> zkge%0C7$ByPbxzglR!9JviIchxQoES*405W=O^=K!+-b59@l9N}MaW&9S3kTT{$v z2w<-uAqLr1d=JakK*O2UI)hLzr$K%omOs4*yNV;y6O`y_0HqXLuaacni! zIi1n{B%2rPZ6@!k7WYiL0!t=NO5Od?ZI!51z^n-_)%#;1S zRNc<#f59DIp?J%SJH`Hx%-dA0UX18&*=^)kg3JS*+4er84!poL+uo-Ip<8lZVVa`* zCA}XY;sI04z67S(;_f{FXHT)W-Y52rzft8Y^zMDHcrDN8_Nf1}JmG`%NH~@EaUy?_ z`9RHe^WvW6yiR^~PjCGFkZahYjK#syIq~0n#GGfk;*#gj5-6(nt2Bt@fb)n^1XoQS zF;JA|mr;X1bYLMglZJJ4{FEXF28`gQVv=68cK!TrV+a+|08-bRfmG#C80YqeW-1c9C$;)=iU^dr@q5hqF#p^;fh8W-93=S^ey!kM@k+pXOY=!d M+L?G^%bwyt0dnPRApigX literal 0 HcmV?d00001 diff --git a/gym-manage-uniapp/utils/cache.js b/gym-manage-uniapp/utils/cache.js new file mode 100644 index 0000000..5c196ab --- /dev/null +++ b/gym-manage-uniapp/utils/cache.js @@ -0,0 +1,89 @@ +// 缓存相关常量 +const CACHE_PREFIX = 'API_CACHE_' +const CACHE_EXPIRE_TIME = 5 * 60 * 1000 // 默认缓存时间5分钟 + +/** + * 获取缓存数据 + * @param {string} key - 缓存键名 + * @returns {any} 缓存数据(过期返回null) + */ +export const getCache = (key) => { + try { + const cacheData = uni.getStorageSync(CACHE_PREFIX + key) + if (cacheData && cacheData.expireTime && Date.now() < cacheData.expireTime) { + return cacheData.data + } + // 缓存过期,清除 + uni.removeStorageSync(CACHE_PREFIX + key) + return null + } catch (e) { + console.error('获取缓存失败:', e) + return null + } +} + +/** + * 设置缓存数据 + * @param {string} key - 缓存键名 + * @param {any} data - 要缓存的数据 + * @param {number} expireTime - 过期时间(毫秒),默认5分钟 + */ +export const setCache = (key, data, expireTime = CACHE_EXPIRE_TIME) => { + try { + const cacheData = { + data: data, + expireTime: Date.now() + expireTime + } + uni.setStorageSync(CACHE_PREFIX + key, cacheData) + } catch (e) { + console.error('设置缓存失败:', e) + } +} + +/** + * 清除指定缓存 + * @param {string} key - 缓存键名 + */ +export const clearCache = (key) => { + try { + uni.removeStorageSync(CACHE_PREFIX + key) + } catch (e) { + console.error('清除缓存失败:', e) + } +} + +/** + * 清除所有API缓存 + */ +export const clearAllCache = () => { + try { + const keys = uni.getStorageInfoSync().keys || [] + for (const key of keys) { + if (key.startsWith(CACHE_PREFIX)) { + uni.removeStorageSync(key) + } + } + } catch (e) { + console.error('清除所有缓存失败:', e) + } +} + +/** + * 生成请求缓存键名 + * @param {string} url - 请求URL + * @param {object} data - 请求参数 + * @param {string} method - 请求方法 + * @returns {string} 缓存键名 + */ +export const generateCacheKey = (url, data, method) => { + const params = JSON.stringify(data || {}) + return `${method}_${url}_${params}` +} + +export default { + getCache, + setCache, + clearCache, + clearAllCache, + generateCacheKey +} \ No newline at end of file diff --git a/gym-manage-uniapp/utils/request.js b/gym-manage-uniapp/utils/request.js index e7a1898..ccffb07 100644 --- a/gym-manage-uniapp/utils/request.js +++ b/gym-manage-uniapp/utils/request.js @@ -1,4 +1,4 @@ -const BASE_URL = 'http://192.168.5.15:8084/api' +const BASE_URL = '/api' // 缓存相关常量 const CACHE_PREFIX = 'API_CACHE_' @@ -212,4 +212,21 @@ export const requestUtils = { clearAllCache } +// 添加便捷方法 +request.get = (url, data = {}, options = {}) => { + return request({ url, method: 'GET', data, ...options }) +} + +request.post = (url, data = {}, options = {}) => { + return request({ url, method: 'POST', data, ...options }) +} + +request.put = (url, data = {}, options = {}) => { + return request({ url, method: 'PUT', data, ...options }) +} + +request.delete = (url, data = {}, options = {}) => { + return request({ url, method: 'DELETE', data, ...options }) +} + export default request diff --git a/gym-manage-uniapp/vite.config.js b/gym-manage-uniapp/vite.config.js index 879e01c..400a2ca 100644 --- a/gym-manage-uniapp/vite.config.js +++ b/gym-manage-uniapp/vite.config.js @@ -7,17 +7,22 @@ export default defineConfig({ plugins: [uni()], resolve: { alias: { - '@': path.resolve(__dirname, 'src') + '@': path.resolve(__dirname, '.') }, }, server: { proxy: { - // 匹配所有 /api 开头的请求 + // 匹配所有 /api/ 开头的请求(排除静态文件) '/api': { target: 'http://192.168.5.15:8084', // 你的后端SpringBoot地址 changeOrigin: true, // 开启跨域伪装 - // rewrite: (path) => path.replace(/^\/areyouok/, '') - // 举例:前端请求 /api/login → 代理成 http://localhost:8088/login + // 只代理真正的后端API请求,排除 .js .vue 等静态文件 + bypass: function(req, res, proxyOptions) { + if (req.url.indexOf('.js') !== -1 || req.url.indexOf('.vue') !== -1 || + req.url.indexOf('.css') !== -1 || req.url.indexOf('.json') !== -1) { + return req.url + } + } } } }