From 2357dcfc6768fe098fcc5d08dd9a7e7e59ba97f1 Mon Sep 17 00:00:00 2001 From: liwentao Date: Thu, 4 Jun 2026 13:05:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=9B=A2=E8=AF=BE=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E9=A1=B5=E9=9D=A2=E5=B8=83=E5=B1=80=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E4=BA=A4=E4=BA=92=EF=BC=8C=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gym-manage-uniapp/api/envConfig.js | 13 + gym-manage-uniapp/api/groupCourse.mock.js | 407 ++++++++++ .../common/style/font/iconfont_courseCard.ttf | Bin 0 -> 2168 bytes .../style/font/iconfont_time_select.ttf | Bin 0 -> 2344 bytes .../common/style/iconfont_courseCard.css | 25 + .../common/style/iconfont_time_select.css | 29 + .../components/groupCourse/CourseCard.vue | 487 ++++++++++++ .../components/groupCourse/FilterSection.vue | 202 +++++ .../components/groupCourse/SearchBar.vue | 165 ++++ .../groupCourse/TimePeriodSelector.vue | 191 +++++ .../groupCourse/TimeRangePicker.vue | 728 ++++++++++++++++++ gym-manage-uniapp/docs/api-documentation.md | 234 ++++++ gym-manage-uniapp/pages/groupCourse/list.vue | 172 +++++ 13 files changed, 2653 insertions(+) create mode 100644 gym-manage-uniapp/api/envConfig.js create mode 100644 gym-manage-uniapp/api/groupCourse.mock.js create mode 100644 gym-manage-uniapp/common/style/font/iconfont_courseCard.ttf create mode 100644 gym-manage-uniapp/common/style/font/iconfont_time_select.ttf create mode 100644 gym-manage-uniapp/common/style/iconfont_courseCard.css create mode 100644 gym-manage-uniapp/common/style/iconfont_time_select.css create mode 100644 gym-manage-uniapp/components/groupCourse/CourseCard.vue create mode 100644 gym-manage-uniapp/components/groupCourse/FilterSection.vue create mode 100644 gym-manage-uniapp/components/groupCourse/SearchBar.vue create mode 100644 gym-manage-uniapp/components/groupCourse/TimePeriodSelector.vue create mode 100644 gym-manage-uniapp/components/groupCourse/TimeRangePicker.vue create mode 100644 gym-manage-uniapp/docs/api-documentation.md create mode 100644 gym-manage-uniapp/pages/groupCourse/list.vue diff --git a/gym-manage-uniapp/api/envConfig.js b/gym-manage-uniapp/api/envConfig.js new file mode 100644 index 0000000..5fb6881 --- /dev/null +++ b/gym-manage-uniapp/api/envConfig.js @@ -0,0 +1,13 @@ +/** + * 环境配置文件 + * 当前仅使用模拟数据(开发模式) + */ + +import { groupCourseMockApi } from './groupCourse.mock.js' + +/** + * 团课服务(仅使用模拟数据) + */ +export const groupCourseService = groupCourseMockApi + +export default groupCourseService diff --git a/gym-manage-uniapp/api/groupCourse.mock.js b/gym-manage-uniapp/api/groupCourse.mock.js new file mode 100644 index 0000000..1f0aa29 --- /dev/null +++ b/gym-manage-uniapp/api/groupCourse.mock.js @@ -0,0 +1,407 @@ +/** + * 团课模拟数据(测试环境使用) + * 数据格式与后端返回格式保持一致 + */ + +// 模拟团课列表数据(与后端返回格式一致) +const mockCourseList = [ + { + "id": "1", + "createBy": "admin", + "updateBy": null, + "createdAt": "2026-06-01T11:00:00", + "updatedAt": "2026-06-01T11:00:00", + "deletedAt": null, + "courseName": "极速燃脂单车", + "coachId": "104", + "courseType": "2", + "startTime": "2026-06-02T16:45:00", + "endTime": "2026-06-15T20:20:00", + "maxMembers": 25, + "currentMembers": 0, + "status": "0", + "location": "单车房", + "coverImage": "https://picsum.photos/seed/spinning/640/360", + "description": "跟随音乐节奏变换阻力和速度,体验爬坡与冲刺的快感,一节课消耗800大卡。支持次数卡(1次)或储值卡(50元)支付。", + "pointCardAmount": 0, + "storedValueAmount": 0 + }, + { + "id": "3", + "createBy": "coach_zhang", + "updateBy": null, + "createdAt": "2026-06-01T14:30:00", + "updatedAt": "2026-06-01T14:30:00", + "deletedAt": null, + "courseName": "燃脂搏击", + "coachId": "102", + "courseType": "2", + "startTime": "2026-06-10T18:30:00", + "endTime": "2026-06-10T19:30:00", + "maxMembers": 20, + "currentMembers": 20, + "status": "0", + "location": "综合训练区", + "coverImage": "https://picsum.photos/seed/kickboxing/640/360", + "description": "高强度间歇训练,配合音乐快速燃脂,释放压力。名额已满,无法预约。支持次数卡(1次)或储值卡(60元)支付。", + "pointCardAmount": 1, + "storedValueAmount": 60 + }, + { + "id": "4", + "createBy": "coach_li", + "updateBy": null, + "createdAt": "2026-06-01T08:00:00", + "updatedAt": "2026-06-01T08:00:00", + "deletedAt": null, + "courseName": "哈他瑜伽", + "coachId": "101", + "courseType": "1", + "startTime": "2026-06-01T15:20:00", + "endTime": "2026-06-01T16:50:00", + "maxMembers": 12, + "currentMembers": 3, + "status": "0", + "location": "瑜伽教室B", + "coverImage": "https://picsum.photos/seed/yoga/640/360", + "description": "基础哈他瑜伽,适合所有级别。距开始不足30分钟,已停止预约。支持次数卡(1次)或储值卡(40元)支付。", + "pointCardAmount": 1, + "storedValueAmount": 40 + }, + { + "id": "5", + "createBy": "coach_wang", + "updateBy": null, + "createdAt": "2026-05-28T08:00:00", + "updatedAt": "2026-05-28T08:00:00", + "deletedAt": null, + "courseName": "周末冥想修复", + "coachId": "101", + "courseType": "1", + "startTime": "2026-06-20T15:00:00", + "endTime": "2026-06-20T16:00:00", + "maxMembers": 12, + "currentMembers": 3, + "status": "1", + "location": "冥想室", + "coverImage": "https://picsum.photos/seed/meditation/640/360", + "description": "通过呼吸和正念冥想,深度放松身心。该课程已被取消。支持次数卡(1次)或储值卡(30元)支付。", + "pointCardAmount": 1, + "storedValueAmount": 30 + }, + { + "id": "6", + "createBy": "coach_li", + "updateBy": null, + "createdAt": "2026-05-20T09:15:00", + "updatedAt": "2026-05-20T09:15:00", + "deletedAt": null, + "courseName": "蜜桃臀塑造", + "coachId": "103", + "courseType": "3", + "startTime": "2026-05-30T19:00:00", + "endTime": "2026-05-30T20:00:00", + "maxMembers": 10, + "currentMembers": 8, + "status": "2", + "location": "私教专区", + "coverImage": "https://picsum.photos/seed/glute/640/360", + "description": "针对性训练臀部肌肉群,课程已于5月30日结束,无法预约。支持次数卡(1次)或储值卡(80元)支付。", + "pointCardAmount": 1, + "storedValueAmount": 80 + }, + { + "id": "7", + "createBy": "admin", + "updateBy": null, + "createdAt": "2026-05-25T09:00:00", + "updatedAt": "2026-05-25T09:00:00", + "deletedAt": null, + "courseName": "午间冥想放松", + "coachId": "101", + "courseType": "1", + "startTime": "2026-05-31T12:00:00", + "endTime": "2026-05-31T13:00:00", + "maxMembers": 15, + "currentMembers": 6, + "status": "2", + "location": "冥想室", + "coverImage": "https://picsum.photos/seed/noonmeditation/640/360", + "description": "午间冥想课程,已于5月31日结束。支持次数卡(1次)或储值卡(25元)支付。", + "pointCardAmount": 1, + "storedValueAmount": 25 + }, + { + "id": "8", + "createBy": "admin", + "updateBy": null, + "createdAt": "2026-06-01T10:00:00", + "updatedAt": "2026-06-01T10:00:00", + "deletedAt": null, + "courseName": "燃脂搏击_次数卡课程", + "coachId": "102", + "courseType": "2", + "startTime": "2026-06-10T19:30:00", + "endTime": "2026-06-10T20:30:00", + "maxMembers": 20, + "currentMembers": 0, + "status": "0", + "location": "综合训练区", + "coverImage": null, + "description": "高强度间歇训练,配合音乐快速燃脂,消耗1次", + "pointCardAmount": 1, + "storedValueAmount": 0 + }, + { + "id": "9", + "createBy": "admin", + "updateBy": null, + "createdAt": "2026-06-01T10:00:00", + "updatedAt": "2026-06-01T10:00:00", + "deletedAt": null, + "courseName": "高端普拉提_储值卡课程", + "coachId": "103", + "courseType": "1", + "startTime": "2026-06-11T19:00:00", + "endTime": "2026-06-11T20:00:00", + "maxMembers": 15, + "currentMembers": 0, + "status": "0", + "location": "普拉提教室", + "coverImage": null, + "description": "精准训练核心肌群,消耗储值50元", + "pointCardAmount": 0, + "storedValueAmount": 20 + }, + { + "id": "11", + "createBy": "admin", + "updateBy": null, + "createdAt": "2026-06-02T10:00:00", + "updatedAt": "2026-06-02T10:00:00", + "deletedAt": null, + "courseName": "时间冲突测试_A_13点-15点", + "coachId": "102", + "courseType": "2", + "startTime": "2026-06-15T13:00:00", + "endTime": "2026-06-15T15:00:00", + "maxMembers": 20, + "currentMembers": 0, + "status": "0", + "location": "综合训练区", + "coverImage": null, + "description": "测试用团课A,用于验证时间冲突检测。支持次数卡(1次)或储值卡(50元)支付。", + "pointCardAmount": 1, + "storedValueAmount": 50 + }, + { + "id": "12", + "createBy": "admin", + "updateBy": null, + "createdAt": "2026-06-02T10:00:00", + "updatedAt": "2026-06-02T10:00:00", + "deletedAt": null, + "courseName": "时间冲突测试_B_14点-16点", + "coachId": "103", + "courseType": "1", + "startTime": "2026-06-15T14:00:00", + "endTime": "2026-06-15T16:00:00", + "maxMembers": 15, + "currentMembers": 0, + "status": "0", + "location": "普拉提教室", + "coverImage": null, + "description": "测试用团课B,与团课A时间重叠(14:00-15:00)。支持次数卡(1次)或储值卡(50元)支付。", + "pointCardAmount": 1, + "storedValueAmount": 50 + }, + { + "id": "13", + "createBy": "admin", + "updateBy": null, + "createdAt": "2026-06-02T10:00:00", + "updatedAt": "2026-06-02T10:00:00", + "deletedAt": null, + "courseName": "时间冲突测试_C_10点-12点", + "coachId": "101", + "courseType": "1", + "startTime": "2026-06-15T10:00:00", + "endTime": "2026-06-15T12:00:00", + "maxMembers": 15, + "currentMembers": 0, + "status": "0", + "location": "瑜伽教室", + "coverImage": null, + "description": "测试用团课C,与团课A/B不冲突。支持次数卡(1次)或储值卡(50元)支付。", + "pointCardAmount": 1, + "storedValueAmount": 50 + }, + { + "id": "14", + "createBy": "system", + "updateBy": "system", + "createdAt": "2026-06-02T17:32:50.532336", + "updatedAt": "2026-06-02T17:32:50.532336", + "deletedAt": null, + "courseName": "动感单车aaa", + "coachId": "2", + "courseType": "2", + "startTime": "2026-06-05T18:00:00", + "endTime": "2026-06-05T19:00:00", + "maxMembers": 25, + "currentMembers": 0, + "status": "0", + "location": "健身房B区", + "coverImage": "https://example.com/spinning.jpg", + "description": "高强度有氧运动课程", + "pointCardAmount": 1, + "storedValueAmount": 50 + }, + { + "id": "2", + "createBy": "admin", + "updateBy": null, + "createdAt": "2026-06-01T10:00:00", + "updatedAt": "2026-06-02T17:35:35.155616", + "deletedAt": null, + "courseName": "清晨流瑜伽", + "coachId": "101", + "courseType": "1", + "startTime": "2026-06-12T09:00:00", + "endTime": "2026-06-12T10:30:00", + "maxMembers": 15, + "currentMembers": 6, + "status": "0", + "location": "A座3楼瑜伽教室", + "coverImage": "https://picsum.photos/seed/yogaflow/640/360", + "description": "适合有一定基础的学员,通过流畅的体式连接呼吸,唤醒身体能量。支持次数卡(1次)或储值卡(45元)支付。", + "pointCardAmount": 1, + "storedValueAmount": 45 + }, + { + "id": "15", + "createBy": "system", + "updateBy": "system", + "createdAt": "2026-06-02T17:57:27.483488", + "updatedAt": "2026-06-02T17:57:27.483488", + "deletedAt": null, + "courseName": "动感单车", + "coachId": "2", + "courseType": "2", + "startTime": "2026-06-05T18:00:00", + "endTime": "2026-06-05T19:00:00", + "maxMembers": 25, + "currentMembers": 0, + "status": "0", + "location": "健身房B区", + "coverImage": "https://example.com/spinning.jpg", + "description": "高强度有氧运动课程", + "pointCardAmount": 1, + "storedValueAmount": 50 + } +] + +/** + * 模拟团课API(测试环境) + * 接口签名与真实API保持一致 + */ +export const groupCourseMockApi = { + /** + * 获取团课列表(支持分页) + * @param {Object} params - 查询参数 + * @param {number} params.pageNum - 页码(从1开始) + * @param {number} params.pageSize - 每页数量 + * @returns {Promise} - 分页团课列表数据 + */ + getList: (params = {}) => { + return new Promise((resolve) => { + setTimeout(() => { + const pageNum = params.pageNum || 1 + const pageSize = params.pageSize || 10 + + const total = mockCourseList.length + const startIndex = (pageNum - 1) * pageSize + const endIndex = startIndex + pageSize + const list = mockCourseList.slice(startIndex, endIndex) + + console.log('[groupCourse.mock.js] 模拟获取团课列表(分页):', { + pageNum, + pageSize, + total, + listCount: list.length + }) + + resolve({ + code: 0, + message: 'success', + data: { + list, + total, + pageNum, + pageSize, + totalPages: Math.ceil(total / pageSize) + } + }) + }, 500) + }) + }, + + /** + * 获取团课详情 + * @param {string} id - 课程ID + * @returns {Promise} - 团课详情数据 + */ + getDetail: (id) => { + return new Promise((resolve, reject) => { + setTimeout(() => { + console.log('[groupCourse.mock.js] 模拟获取团课详情:', id) + const course = mockCourseList.find(item => item.id === id) + if (course) { + resolve(course) + } else { + reject({ code: -1, message: '课程不存在' }) + } + }, 300) + }) + }, + + /** + * 预约团课 + * @param {Object} data - 预约数据 + * @param {string} data.courseId - 课程ID + * @param {string} data.memberId - 会员ID + * @returns {Promise} - 预约结果 + */ + book: (data) => { + return new Promise((resolve) => { + setTimeout(() => { + console.log('[groupCourse.mock.js] 模拟预约团课:', data) + resolve({ + code: 0, + message: '预约成功', + data: { bookingId: `BK${Date.now()}` } + }) + }, 400) + }) + }, + + /** + * 取消预约 + * @param {string} id - 预约记录ID + * @returns {Promise} - 取消结果 + */ + cancelBooking: (id) => { + return new Promise((resolve) => { + setTimeout(() => { + console.log('[groupCourse.mock.js] 模拟取消预约:', id) + resolve({ + code: 0, + message: '取消成功', + data: null + }) + }, 300) + }) + } +} + +export default groupCourseMockApi diff --git a/gym-manage-uniapp/common/style/font/iconfont_courseCard.ttf b/gym-manage-uniapp/common/style/font/iconfont_courseCard.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3e233cbcaaadade29cc21c3e138631c433669f43 GIT binary patch literal 2168 zcmd^AOK%%h6h3#xkHmQp)s7pC5?OBJB(-sp@i-upSShLP#zF$xI&q|^;Mn6wTf;9BvB#l3_w?CuX?h~@FytM`&u7Z%+LyaS*CGFd z{$9ScnfvaOZ(~H_M?}K$f|AaTf0lHDdk1~00EP9o_$u%>=*J6Xb)ydewtrygD^)XT zwnaB!|1M-(IlWP1N9b9s4?uQT(q*OZ!|5-G%s;|^zE*9hw?22iPt-Ai^%x;`al5rm z%O;3K7w{EQfvQY-qyXn$9 zuW@rw@BAG0=Jjs8g(bRy`=A|K>!SD)-q3F9r%5`6x_Ww31A;;sIsqG3{a25w8%ExghbSeup5CKjHT$Qhe|aheC-^h$Vx;Wa_8X zf)t#UQq%E{0oOoBd^#n~?(bMEZ(s9`#pgerkDc&c6Lx)xMQ*v6@ck{rG$EhNr4}Y- zd2%6@OI*rdUOsQ@?zWv@zMQ|bZ}059$tL5;)A2DMe4I|k_kZSDwr+~M;z>G&n?x+a zLdXW0^*fwLdl`}l9*8$6j|A9=C&1(glZKe1mkC!wk?8uH>(NMPX~#P{8wxHi{j#(e z49$*ucbalamQw{juvO26x#)Tnzqt#Z!L4L!rNC*NUsy>cw}J){Y<@^MiPrbx*J79) zG)7@wZC+BXq7pBlR%WYKMh9Q<@Um;6+C#F}V;N))=M$*B>^&yXMm`&!nF(j}f40F! zK9!VYS>lB@z-xAw-80bavD#R+e^e1`t9fc zFm?kAWTi(8EJE)wunG8tfz9|DNCvh*KVx7U`S5S3`|N_p8mh!ho!G}quNat7A6+-F zK)v*pfko(lFt7>uR|A{LNqYviK>x3SZM4984BSOtmYJ(7X;sO(S2o?nOtq4$R@C-< zM#-<0()G63o)?sQqgbuDCj%30Ra~hk^|qVFdR|i1oI6*qmfbPBRDj79U zP}N#^e4OvY^?^*aOmkExh0>&A%#xc{XcJ?RGE}7s<+M?~Pd)>7p4O-Y%ldt*hpiXD zu2Ta%?$eE{9>B#o%!-3m(U^y$Y0x@$lMthdh}`IPcq#+MjQA8pQUZ440QuS~{A9om zP(ib*;dLL!)6S>O4`_Lo`A0}^{9E{-W?}*pnTeUnk}YP7=}Ko^sl2fEC`N0eP+WzE GoBaoxxl`Q$ literal 0 HcmV?d00001 diff --git a/gym-manage-uniapp/common/style/font/iconfont_time_select.ttf b/gym-manage-uniapp/common/style/font/iconfont_time_select.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c1d24b12f6859ee5f65c7f5ea94ce97856396e79 GIT binary patch literal 2344 zcmd^B-%ne06hG(Q_TEA(wxcV8tZQj$S=(LMrL<|vyri3GI8@#E-hPsS;{tM z#z>qdX3M}BUwq&}UuNQi55}n315eJFCBCUKx)~o>)cInv2V-bG-+OPf$@U+3+VA)K zIX}PWobR{y77zfeunIi%j7^<*-gC9{A^<)|(&I;`y5)g)XTK%>8u72D^GfNnPXCVp z9I<#KlUtr2TR-_G0RJh;Ze~?wuKVNRJSl&n5zCT5yu-gm_$Cd1Heb2mBtX9RX>#U@ zX$4PlUF3g*cw1h%P(mlXO!{Yt_Y{=8YX5NH69Az^@iV33V&(pXa2mk!1L=b6^>mp^BQ`dZsG0Y6BrMJtUdSR^wXrNM|5 z+(;jQ8e@r=z7cvL3Ear&i^riUr&J2i#*)y8=^w?ITgWIBU-v}PF)fk=K`^8y6W}`A zk$aoFO|mt8#Xmqs^5nqGlk^ERIiH$?&8?MMP3s5nKfx-Qu^b)}$2QzKNW>~+aU;4K zvxeU+Nm+yltyDS5O%NmAqIYR)W$W&}D>drJLquA=CT7Dn<5&b__5I(;rZr&DXagO? z)`~vZk!;=72fJtYuIPh0hjiQ6(9Z{8628GEJf)o$@c(dGXazsrK|r5`!9Xz3Aqrv> zioOoP>FV{$v1kD07B{wdlNgVo_o0~Iy3R$Vp2X0#p+t`q<*ql_Yz_QvnyZe<)W{US z{Q*BUlER^?+hVg>@K>7AlDZ`Kw(r@~-YZ{9*&H_dVm!2O?Zk<-eW7Ynm#_lqM%6a? zO@0uZupbV>2>n(8S#pB2#ns}H^ z$P#DOh@Exh<6wC~?N227)rG(6;QiR>_{R8X&P|j{9o{&c(qQ%HCQrM=FZ&(8wfTI# zK3^NY-sJE z^hYB7+r3;@Xm9oG-VhFO-))CB{K?KPlRP;yDVw6z0rJ4~{$b%yad@S=><)+BUnN-Y z=?2vv@W**8xS^Mhg7%P*TASopBQ+Rnk=51`h1hM<%(vaQX*?-m=kBTmrgWol?I(}$zzs2RU za}Ahy?VXM5#s15E;n4UCFAbiXk>yJA$XbCV2)p18-D;hhY0)uz5;hx{0}-AzFi-Lh z1Dgnk3~Z*imTX{w)WouN2svX&sLd6np~ z(oLUwJ#~9YtCRkMus1d%_RfA1nUO;tO=zaKlXy`nrn726%&3LT+)~kUVL>^+l+G&V LW%azG-P(Tv+O2EK literal 0 HcmV?d00001 diff --git a/gym-manage-uniapp/common/style/iconfont_courseCard.css b/gym-manage-uniapp/common/style/iconfont_courseCard.css new file mode 100644 index 0000000..98fb44c --- /dev/null +++ b/gym-manage-uniapp/common/style/iconfont_courseCard.css @@ -0,0 +1,25 @@ +@font-face { + font-family: "iconfont_courseCard"; /* Project id */ + src: url('./font/iconfont_courseCard.ttf?t=1780537357472') format('truetype'); +} + +.iconfont_courseCard { + font-family: "iconfont_courseCard" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-didian:before { + content: "\e61a"; +} + +.icon-renwu-ren:before { + content: "\e749"; +} + +.icon-shijian:before { + content: "\e61d"; +} + diff --git a/gym-manage-uniapp/common/style/iconfont_time_select.css b/gym-manage-uniapp/common/style/iconfont_time_select.css new file mode 100644 index 0000000..1efde39 --- /dev/null +++ b/gym-manage-uniapp/common/style/iconfont_time_select.css @@ -0,0 +1,29 @@ +@font-face { + font-family: "iconfont_time_select"; /* Project id */ + src: url('./font/iconfont_time_select.ttf?t=1780535096813') format('truetype'); +} + +.iconfont_time_select { + font-family: "iconfont_time_select" !important; + font-size: 25px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-zaochen:before { + content: "\e784"; +} + +.icon-gengduo:before { + content: "\e6df"; +} + +.icon-xiawucha:before { + content: "\100ff"; +} + +.icon-yewan:before { + content: "\e67e"; +} + diff --git a/gym-manage-uniapp/components/groupCourse/CourseCard.vue b/gym-manage-uniapp/components/groupCourse/CourseCard.vue new file mode 100644 index 0000000..2ff731e --- /dev/null +++ b/gym-manage-uniapp/components/groupCourse/CourseCard.vue @@ -0,0 +1,487 @@ + + + + + \ No newline at end of file diff --git a/gym-manage-uniapp/components/groupCourse/FilterSection.vue b/gym-manage-uniapp/components/groupCourse/FilterSection.vue new file mode 100644 index 0000000..d97595d --- /dev/null +++ b/gym-manage-uniapp/components/groupCourse/FilterSection.vue @@ -0,0 +1,202 @@ + + + + + \ No newline at end of file diff --git a/gym-manage-uniapp/components/groupCourse/SearchBar.vue b/gym-manage-uniapp/components/groupCourse/SearchBar.vue new file mode 100644 index 0000000..cec1e7b --- /dev/null +++ b/gym-manage-uniapp/components/groupCourse/SearchBar.vue @@ -0,0 +1,165 @@ + + + + + \ No newline at end of file diff --git a/gym-manage-uniapp/components/groupCourse/TimePeriodSelector.vue b/gym-manage-uniapp/components/groupCourse/TimePeriodSelector.vue new file mode 100644 index 0000000..ef71591 --- /dev/null +++ b/gym-manage-uniapp/components/groupCourse/TimePeriodSelector.vue @@ -0,0 +1,191 @@ + + + + + \ No newline at end of file diff --git a/gym-manage-uniapp/components/groupCourse/TimeRangePicker.vue b/gym-manage-uniapp/components/groupCourse/TimeRangePicker.vue new file mode 100644 index 0000000..bb70cdc --- /dev/null +++ b/gym-manage-uniapp/components/groupCourse/TimeRangePicker.vue @@ -0,0 +1,728 @@ + + + + + \ No newline at end of file diff --git a/gym-manage-uniapp/docs/api-documentation.md b/gym-manage-uniapp/docs/api-documentation.md new file mode 100644 index 0000000..2f508e9 --- /dev/null +++ b/gym-manage-uniapp/docs/api-documentation.md @@ -0,0 +1,234 @@ +# 团课管理系统 API 文档 + +## 1. 项目概述 + +本项目是一个基于 UniApp 的健身房团课管理系统,包含完整的 API 层设计,支持开发/生产环境的快速切换。 + +### 1.1 项目结构 + +``` +gym-manage-uniapp/ +├── api/ # API 层目录 +│ ├── requestBase.js # 基础请求封装 +│ ├── groupCourse.js # 团课真实API +│ ├── groupCourse.mock.js # 团课模拟数据API +│ └── envConfig.js # 环境配置与服务导出 +├── pages/ +│ └── groupCourse/ +│ └── list.vue # 团课列表页面 +└── components/ # 组件目录 +``` + +--- + +## 2. API 层设计 + +### 2.1 架构设计 + +| 层级 | 文件 | 职责 | +|------|------|------| +| 基础层 | `requestBase.js` | 封装 uni.request,统一处理加载状态、错误提示 | +| 接口层 | `groupCourse.js` | 定义真实后端API接口 | +| 模拟层 | `groupCourse.mock.js` | 提供模拟数据,支持开发测试 | +| 配置层 | `envConfig.js` | 环境配置,统一服务导出入口 | + +### 2.2 环境切换机制 + +通过修改 `envConfig.js` 中的 `ENV_MODE` 变量实现环境切换: + +```javascript +// envConfig.js +export const ENV_MODE = 'development' // 'production' | 'development' +``` + +- **`production`**:生产环境,使用真实网络请求 +- **`development`**:开发环境,使用模拟数据 + +--- + +## 3. 文件详细说明 + +### 3.1 requestBase.js - 基础请求封装 + +**功能定位**:封装 `uni.request`,提供统一的请求处理逻辑。 + +**核心特性**: +- 自动显示/隐藏加载提示 +- 统一的响应状态码处理 +- 统一的错误提示机制 + +**接口定义**: + +| 方法 | 说明 | 参数 | 返回值 | +|------|------|------|--------| +| `request(options)` | 发起网络请求 | `options` 对象 | `Promise` | + +**options 参数**: + +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `url` | string | 是 | 请求路径 | +| `method` | string | 否 | 请求方法,默认 `GET` | +| `data` | object | 否 | 请求数据 | +| `header` | object | 否 | 请求头 | + +**响应数据结构**: + +```javascript +// 成功响应 +{ + code: 0, // 状态码,0表示成功 + message: 'success', // 提示信息 + data: {} // 业务数据 +} +``` + +### 3.2 groupCourse.js - 团课真实 API + +**功能定位**:定义团课相关的真实后端接口。 + +**接口列表**: + +| 方法 | 说明 | 参数 | HTTP方法 | 路径 | +|------|------|------|----------|------| +| `getList()` | 获取团课列表 | 无 | GET | `/api/groupCourse/list` | +| `getDetail(id)` | 获取团课详情 | `id`: 课程ID | GET | `/api/groupCourse/detail/{id}` | +| `book(data)` | 预约团课 | `data`: 预约数据 | POST | `/api/groupCourse/book` | +| `cancelBooking(id)` | 取消预约 | `id`: 预约记录ID | POST | `/api/groupCourse/cancel/{id}` | + +**book 方法参数结构**: + +```javascript +{ + courseId: string, // 课程ID + memberId: string // 会员ID +} +``` + +### 3.3 groupCourse.mock.js - 团课模拟数据 API + +**功能定位**:提供模拟数据,支持开发测试,无需后端服务。 + +**特性**: +- 模拟网络延迟(300-500ms) +- 接口签名与真实API完全一致 +- 包含6条完整的模拟团课数据 + +**模拟数据结构**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `id` | string | 课程唯一标识 | +| `courseName` | string | 课程名称 | +| `coachName` | string | 教练姓名 | +| `coachAvatar` | string | 教练头像 | +| `startTime` | string | 开始时间(ISO格式) | +| `endTime` | string | 结束时间(ISO格式) | +| `duration` | number | 课程时长(分钟) | +| `location` | string | 上课地点 | +| `maxMembers` | number | 最大人数 | +| `currentMembers` | number | 当前人数 | +| `storedValueAmount` | number | 储值卡价格(元) | +| `pointCardAmount` | number | 次卡次数 | +| `courseType` | string | 课程类型 | +| `level` | string | 难度级别 | +| `description` | string | 课程描述 | +| `tags` | array | 标签列表 | +| `status` | string | 状态(available/closed) | + +### 3.4 envConfig.js - 环境配置 + +**功能定位**:统一服务导出入口,根据环境模式自动选择 API 实现。 + +**导出内容**: + +| 导出项 | 类型 | 说明 | +|--------|------|------| +| `ENV_MODE` | string | 当前环境模式 | +| `groupCourseService` | object | 团课服务实例 | + +--- + +## 4. 使用示例 + +### 4.1 在页面中使用 + +```javascript +// pages/groupCourse/list.vue +import { groupCourseService } from '@/api/envConfig.js' + +// 获取团课列表 +const fetchCourseList = async () => { + try { + const data = await groupCourseService.getList() + courseList.value = data + console.log('团课列表获取成功:', data) + } catch (error) { + console.error('获取团课列表异常:', error) + } +} +``` + +### 4.2 切换环境 + +```javascript +// api/envConfig.js +// 开发环境 - 使用模拟数据 +export const ENV_MODE = 'development' + +// 生产环境 - 使用真实网络请求 +export const ENV_MODE = 'production' +``` + +--- + +## 5. 数据流转图 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 页面层 (View) │ +│ pages/groupCourse/list.vue │ +└───────────────────────────┬─────────────────────────────────┘ + │ import & call + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 环境配置层 (Config) │ +│ api/envConfig.js │ +│ 根据 ENV_MODE 选择对应的服务实现 │ +└───────────────────────────┬─────────────────────────────────┘ + │ + ┌─────────────────┴─────────────────┐ + ▼ ▼ +┌─────────────────────────┐ ┌─────────────────────────────┐ +│ groupCourse.js │ │ groupCourse.mock.js │ +│ (production 环境) │ │ (development 环境) │ +└───────────┬─────────────┘ └─────────────┬───────────────┘ + │ │ + ▼ ▼ +┌─────────────────────────┐ ┌─────────────────────┐ +│ requestBase.js │ │ 本地模拟数据 │ +│ (真实网络请求) │ │ (无需后端) │ +└───────────┬─────────────┘ └─────────────────────┘ + │ + ▼ +┌─────────────────────────┐ +│ 后端服务器 API │ +└─────────────────────────┘ +``` + +--- + +## 6. 注意事项 + +1. **环境变量配置**:部署前务必确认 `ENV_MODE` 设置正确 +2. **数据一致性**:模拟数据结构应与真实API保持一致 +3. **错误处理**:所有API调用都应包含 try-catch 错误处理 +4. **日志记录**:建议在关键节点添加日志,便于调试 + +--- + +## 7. 版本历史 + +| 版本 | 日期 | 更新内容 | +|------|------|----------| +| v1.0 | 2026-06-04 | 初始版本,完成基础API层设计 | diff --git a/gym-manage-uniapp/pages/groupCourse/list.vue b/gym-manage-uniapp/pages/groupCourse/list.vue new file mode 100644 index 0000000..4870609 --- /dev/null +++ b/gym-manage-uniapp/pages/groupCourse/list.vue @@ -0,0 +1,172 @@ + + + + + \ No newline at end of file