diff --git a/api/index.js b/api/index.js index 42ce6d6..7afadea 100644 --- a/api/index.js +++ b/api/index.js @@ -1,18 +1,19 @@ -import user from '@/api/modules/user' +// import user from '@/api/modules/user' import config from '@/api/modules/config' import login from '@/api/modules/login' import home from '@/api/modules/home' import member from '@/api/modules/member' import book from '@/api/modules/book' import promotion from '@/api/modules/promotion' - +import music from '@/api/modules/music' export { - user, + // user, config, login, home, member, book, - promotion + promotion, + music } diff --git a/api/modules/book.js b/api/modules/book.js index 87b657b..231eae3 100644 --- a/api/modules/book.js +++ b/api/modules/book.js @@ -87,5 +87,23 @@ export default { method: "GET", data }) - } + }, + + // 获取课程页面详情 + async coursesPageDetail(data){ + return request({ + url: "/books/coursesPageDetail", + method: "GET", + data + }) + }, + + // 获取课程页面列表 + async coursePage(data){ + return request({ + url: "/books/coursePage", + method: "GET", + data + }) + }, } \ No newline at end of file diff --git a/api/modules/member.js b/api/modules/member.js index c78e1af..6ab3c62 100644 --- a/api/modules/member.js +++ b/api/modules/member.js @@ -28,5 +28,25 @@ export default { needToken: true, data }) - } + }, + + // 开通会员 + async openMember(data) { + return request({ + url: '/member/open', + method: 'POST', + data, + needToken: true, + showLoading: true + }) + }, + + // 获取当前用户会员信息 + async getUserMemberInfo() { + return request({ + url: '/member/userMemberInfo', + method: 'GET', + needToken: true + }) + }, } \ No newline at end of file diff --git a/api/modules/music.js b/api/modules/music.js new file mode 100644 index 0000000..0247391 --- /dev/null +++ b/api/modules/music.js @@ -0,0 +1,14 @@ +import request from '@/api/request' + +export default{ + + // 查询音色列表 + async list(){ + return request({ + url: "/tts/list", + method: "GET", + needToken: true + }) + } + +} diff --git a/api/modules/promotion.js b/api/modules/promotion.js index 513851f..4fd7848 100644 --- a/api/modules/promotion.js +++ b/api/modules/promotion.js @@ -19,5 +19,36 @@ export default { data, needToken: true }) + }, + + + // 提现 + async withdraw(data){ + return request({ + url: "/promotion/withdraw", + method: "POST", + data, + needToken: true, + showLoading: true, + }) + }, + +// 获取推广统计 + async statistics(data){ + return request({ + url: "/promotion/statistics", + method: "GET", + data, + needToken: true + }) + }, + + async qrCode(data){ + return request({ + url: "/promotion/qrCode", + method: "GET", + data, + needToken: true + }) } } \ No newline at end of file diff --git a/api/modules/user.js b/api/modules/user.js deleted file mode 100644 index 874db2b..0000000 --- a/api/modules/user.js +++ /dev/null @@ -1,24 +0,0 @@ -// import request from "@/api/request"; -import http from "@/api/http"; - -export default { - - // 我的资料- 获取个人信息 - async queryUser() { - return http({ - url: '/userInfo/queryUser', - method: 'GET', - needToken: true - }) - }, - - // 我的资料- 修改个人信息 - async updateUser(data) { - return http({ - url: '/userInfo/updateUser', - method: 'POST', - data, - needToken: true - }) - }, -} \ No newline at end of file diff --git a/pages.json b/pages.json index 9cfa060..4d0f81b 100644 --- a/pages.json +++ b/pages.json @@ -125,6 +125,18 @@ "style": { "navigationStyle": "custom" } + }, + { + "path": "home/music", + "style": { + "navigationBarTitleText": "音乐切换" + } + }, + { + "path": "user/share", + "style": { + "navigationBarTitleText": "分享" + } } ] } diff --git a/pages/index/member.vue b/pages/index/member.vue index 09c25d9..a1cfadd 100644 --- a/pages/index/member.vue +++ b/pages/index/member.vue @@ -18,62 +18,64 @@ class="zuanshi-img" /> - - 共19项会员特权 | 3 项年VIP专属特权 - - - - - {{userInfo.name}} + + + 共19项会员特权 | 3 项年VIP专属特权 + + + + + {{userInfo.name}} + + - + + + + + + + {{userInfo.name}} + {{memberInfo[0].endTime.split(' ')[0]}} + + + {{ memberInfo[0].memberTitle }} + {{ '预计剩余学习' + $utils.calculateDateDifference(memberInfo[0].endTime.split(' ')[0]) + '天'}} + - - - - - - + 会员权益 @@ -115,7 +117,7 @@ - + 学习计划 @@ -123,10 +125,11 @@ v-for="(book, index) in studyPlanBooks" :key="index" class="plan-book-item" + @click="goBookDetail(book.book.id)" :class="{ 'active-book': index === 1 }" > - + @@ -134,12 +137,12 @@ - {{ book.title }} + {{ book.book.booksName }} - {{ book.grade }}/ + {{ book.book.categoryName }}/ - {{ book.duration }} + {{ book.book.duration }} @@ -147,8 +150,8 @@ - - + + 学习推荐 更多 @@ -160,15 +163,16 @@ - + - {{ book.title }} + {{ book.booksName }} - {{ book.grade }}/ + {{ book.categoryName }}/ {{ book.duration }} @@ -184,6 +188,8 @@ export default{ data() { return { + isLogin: uni.getStorageSync('token') ? true : false, + memberInfo: [], userInfo: { name: '战斗世界', avatar: '/static/默认头像.png' @@ -191,76 +197,96 @@ export default{ // 学习计划书籍数据 studyPlanBooks: [ - { - cover: '/static/默认图片.png', - title: '精讲短文', - grade: '四级', - duration: '03:24' - }, - { - cover: '/static/默认图片.png', - title: '精讲短文', - grade: '四级', - duration: '03:24' - }, - { - cover: '/static/默认图片.png', - title: '精讲短文', - grade: '四级', - duration: '03:24' - } + ], // 学习推荐书籍数据 recommendBooks: [ - { - cover: '/static/默认图片.png', - title: '小王子', - grade: '四级', - duration: '03:24' - }, - { - cover: '/static/默认图片.png', - title: '自私的巨人', - grade: '四级', - duration: '03:24' - }, - { - cover: '/static/默认图片.png', - title: '百万英镑', - grade: '四级', - duration: '03:24' - }, - { - cover: '/static/默认图片.png', - title: 'MATILDA', - grade: '四级', - duration: '03:24' - }, - { - cover: '/static/默认图片.png', - title: 'Pride and Prejudice', - grade: '四级', - duration: '03:24' - }, - { - cover: '/static/默认图片.png', - title: '温德尔·范·德拉南', - grade: '四级', - duration: '03:24' - } + ] } }, + computed: { + isMember() { + return this.memberInfo.length > 0 + } + }, methods: { + // 跳转学习推荐 + goRecommend() { + uni.navigateTo({ + url: '/subPages/home/search' + }) + }, + // 跳转书籍详情 + goBookDetail(bookId) { + uni.navigateTo({ + url: `/subPages/home/directory?id=${bookId}` + }) + }, goRecharge() { + if (!this.isLogin) { + uni.navigateTo({ + url: '/subPages/login/login' + }) + return + } uni.navigateTo({ url: '/subPages/member/recharge' }) }, - + // 获取会员信息 + async getUserMemberInfo() { + const memberRes = await this.$api.member.getUserMemberInfo() + if (memberRes.code === 200) { + this.memberInfo = [...memberRes.result] + } + }, + // 获取用户信息 + async getUserInfo() { + const userRes = await this.$api.login.getUserInfo() + if (userRes.code === 200) { + this.userInfo = userRes.result + } + }, + // 获取3个学习计划书籍 + async getStudyPlanBook() { + const bookRes = await this.$api.book.stand({ + pageNo: 1, + pageSize: 3 + }) + if (bookRes.code === 200) { + const oneBook = bookRes.result.records[1] + const twoBook = bookRes.result.records[0] + const threeBook = bookRes.result.records[2] + this.studyPlanBooks = [oneBook, twoBook, threeBook] + + } + }, + async getRecommendBook() { + const bookRes = await this.$api.book.list({ + pageNo: 1, + pageSize: 6, + member: 1 + }, false) + if (bookRes.code === 200) { + this.recommendBooks = bookRes.result.records + } + }, }, - + async onShow() { + // 如果登录了就查询会员情况 如果没有登录就不查询 + if (uni.getStorageSync('token')) { + this.isLogin = true + Promise.all([this.getUserMemberInfo(), this.getUserInfo(), this.getStudyPlanBook(), this.getRecommendBook()]) + }else { + this.isLogin = false + this.userInfo = { + name: '登录后查看会员情况', + avatar: '/static/默认头像.png' + } + } + } } @@ -296,7 +322,7 @@ export default{ .header-content{ margin: 0 18rpx; margin-top: -150rpx; - height: 256rpx; + // height: 256rpx; border-radius: 32rpx; border-width: 2rpx; padding: 40rpx; @@ -306,6 +332,32 @@ export default{ flex-direction: column; gap: 28rpx; position: relative; + .vip-container{ + padding: 20rpx 0; + display: flex; + flex-direction: column; + gap: 18rpx; + .project{ + font-size: 36rpx; + color: #191919; + line-height: 1.4; + font-weight: 500; + } + .res-time{ + color: $primary-color; + + font-size: 24rpx; + line-height: 36rpx; + } + } + .noVip-container{ + display: flex; + flex-direction: column; + // align-items: center; + // justify-content: center; + gap: 28rpx; + + } .zuanshi{ position: absolute; width: 190rpx; @@ -343,25 +395,30 @@ export default{ display: flex; justify-content: space-between; align-items: center; - .avatar-box{ - display: flex; - align-items: center; - gap: 16rpx; - .name{ - font-weight: 600; - font-size: 36rpx; - line-height: 44rpx; - letter-spacing: 0%; - vertical-align: middle; - color: #252545; - } - .avatar{ - width: 60rpx; - height: 60rpx; - border-radius: 50%; - } + } + .avatar-box{ + display: flex; + align-items: center; + gap: 16rpx; + .name{ + font-weight: 600; + font-size: 36rpx; + line-height: 44rpx; + letter-spacing: 0%; + vertical-align: middle; + color: #252545; + } + .avatar{ + width: 60rpx; + height: 60rpx; + border-radius: 50%; } + .time{ + font-size: 30rpx; + color: #8B8B8B; + line-height: 36rpx; } + } } } /* 立即开通会员按钮样式 */ diff --git a/pages/index/user.vue b/pages/index/user.vue index 8821c1a..9dbf66b 100644 --- a/pages/index/user.vue +++ b/pages/index/user.vue @@ -43,7 +43,7 @@ - + 推广中心 diff --git a/static/知识收获图标.png b/static/知识收获图标.png new file mode 100644 index 0000000..f8bcdd2 Binary files /dev/null and b/static/知识收获图标.png differ diff --git a/static/视频封面.png b/static/视频封面.png new file mode 100644 index 0000000..146df08 Binary files /dev/null and b/static/视频封面.png differ diff --git a/stores/index.js b/stores/index.js index 1177b22..0c5a0b3 100644 --- a/stores/index.js +++ b/stores/index.js @@ -9,7 +9,8 @@ const store = new Vuex.Store({ // 存放状态 configList: [], departmentList: [], - categoryList: [] + categoryList: [], + }, mutations: { // 构造用于uv-picker的树状结构数组 diff --git a/subPages/home/book.vue b/subPages/home/book.vue index f2ae515..f27b703 100644 --- a/subPages/home/book.vue +++ b/subPages/home/book.vue @@ -8,10 +8,8 @@ - {{ bookTitle }} - - - + {{ currentPageTitle }} + @@ -20,7 +18,6 @@ class="content-swiper" :current="currentPage - 1" @change="onSwiperChange" - > - - - - 划线重点 + + + + + 划线重点 + + + {{ item.englishText }} + {{ item.chineseText }} + + + + + {{ item.content }} + + + + + - - {{ page.englishText }} - {{ page.chineseText }} - - - - - {{ page.content }} - - - - + + + + + + + + {{ item.title }} + + {{ item.buttonText }} + + + - - - - - 课程 - - - - 音色切换 + + + + {{ formatTime(currentTime) }} + + + + + + {{ formatTime(totalTime) }} - - - - - {{ index + 1 }} + + + + 循环 + + + + 上一页 + + + + + + + + 下一页 + + + + {{ playSpeed }}x + + + + + + + + + + + 课程 + + + + 音色切换 + + + + + + + + {{ index + 1 }} + + - + + + + + + + + + 课程 + + 倒序 + + + + + {{ String(course.index).padStart(2, '0') }} + + {{ course.english }} + {{ course.chinese }} + + + + + + + + + + + + 关闭 + + 释义 + + + + + + + + + {{ currentWordMeaning.word }} + + + + {{ currentWordMeaning.phonetic }} + + + {{ currentWordMeaning.partOfSpeech }} + {{ currentWordMeaning.meaning }} + + + + + + + 知识收获 + + {{ currentWordMeaning.knowledgeGain }} + + + + @@ -91,52 +224,206 @@ export default { data() { return { + courseId: '', showNavbar: true, currentPage: 1, - bookTitle: '看,乔治!', + currentCourse: 1, // 当前课程索引 + currentWordMeaning: null, // 当前显示的单词释义 + isReversed: false, // 是否倒序显示 + // 音频控制相关数据 + isPlaying: false, + currentTime: 0, + totalTime: 317, // 5:17 = 317秒 + isLoop: false, + playSpeed: 1.0, + speedOptions: [1.0, 1.25, 1.5, 2.0], + courseIdList: [], + bookTitle: '', + courseList: [ + + ], + + // 二维数组 代表每个页面 bookPages: [ - { - type: 'card', - englishText: 'I ought to have judged by deeds and not by words.', - chineseText: '要对一个人下定论,不应听其言,而应观其行。', - image: '/static/画重点图片.png' - }, - { - type: 'video', - video: 'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4' - }, - { - type: 'text', - content: '索菲达以南几英里处,萨利和斯坦对着近旁河岸的斜坡流淌,水深且绿意盎然。水也很温暖,因为它在阳光下闪烁着,清澈黄色的沙子,才到达深深的池塘。河的一侧,金色的山丘斜坡向上蜿蜒,通往坚固多岩石的加比亚山脉,但在山谷一侧,水边排列着树木——柳树在春天新鲜而翠绿,它们的低垂接触处充满着各天的冰水残留物;还有枫树,它们现实的,白色的横枝条和树枝;吉他也是绿色的地塘,在河下游的河岸上,叶子能发出很大的啪啪声。傍晚时分,兔子们从灌木中出现,坐在沙子上,湖泊的平地上覆盖着沉重的夜晚迹,还有来自农场的狗的散布的垫子,以及前来暗黑中饮水的鹿的分裂形足迹。' - }, - { - type: 'text', - content: '有一条穿过柳树和榆树之间的路,这条路从农场下来的男孩们踏得又硬又深;他们来深池游泳,也被那些上游运送货物公路上下来的流浪汉使得又硬又深。他们在水边停歇,在一棵巨大的榆树低垂的水平树枝前,有一个由许多火堆形成的灰烬堆,核桃被坐在上面的人留下光滑。' - } - ] + + ], + // 存储每个页面的标题 + pageTitles: [], + } + }, + computed: { + displayCourseList() { + return this.isReversed ? [...this.courseList].reverse() : this.courseList; + }, + // 判断当前页面是否为文字类型 + isTextPage() { + const currentPageData = this.bookPages[this.currentPage - 1]; + // currentPageData是一个数组 其中的一个元素的type是text就会返回true + return currentPageData && currentPageData.some(item => item.type === 'text'); + }, + // 计算音频播放进度百分比 + progressPercent() { + return this.totalTime > 0 ? (this.currentTime / this.totalTime) * 100 : 0; + }, + // 动态页面标题 + currentPageTitle() { + return this.pageTitles[this.currentPage - 1] || this.bookTitle; } }, methods: { toggleNavbar() { this.showNavbar = !this.showNavbar - }, goBack() { uni.navigateBack() }, - showMenu() { - console.log('显示菜单') + toggleCoursePopup() { + if (this.$refs.coursePopup) { + this.$refs.coursePopup.open() + } + // console.log('123123123'); + + }, + toggleSort() { + this.isReversed = !this.isReversed + }, + selectCourse(courseId) { + this.currentCourse = courseId + if (this.$refs.coursePopup) { + this.$refs.coursePopup.close() + } + // 这里可以添加切换课程的逻辑 + // console.log('选择课程:', courseId) + this.getCourseList(courseId) + }, + showWordMeaning(wordMeaning) { + if (wordMeaning) { + this.currentWordMeaning = wordMeaning + if (this.$refs.meaningPopup) { + this.$refs.meaningPopup.open() + } + } + }, + closeMeaningPopup() { + if (this.$refs.meaningPopup) { + this.$refs.meaningPopup.close() + } + this.currentWordMeaning = null + }, + // 音频控制方法 + togglePlay() { + this.isPlaying = !this.isPlaying; + // 这里可以添加实际的音频播放/暂停逻辑 + }, + toggleLoop() { + this.isLoop = !this.isLoop; + }, + toggleSpeed() { + const currentIndex = this.speedOptions.indexOf(this.playSpeed); + const nextIndex = (currentIndex + 1) % this.speedOptions.length; + this.playSpeed = this.speedOptions[nextIndex]; + }, + async previousPage() { + if (this.currentPage > 1) { + this.currentPage--; + // 获取对应页面的数据(如果还没有获取过) + if (this.courseIdList[this.currentPage - 1] && this.bookPages[this.currentPage - 1].length === 0) { + await this.getBookPages(this.courseIdList[this.currentPage - 1]); + } + } + }, + async nextPage() { + if (this.currentPage < this.bookPages.length) { + this.currentPage++; + // 获取对应页面的数据(如果还没有获取过) + if (this.courseIdList[this.currentPage - 1] && this.bookPages[this.currentPage - 1].length === 0) { + await this.getBookPages(this.courseIdList[this.currentPage - 1]); + } + } + }, + formatTime(seconds) { + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; }, toggleSound() { console.log('音色切换') + uni.navigateTo({ + url: '/subPages/home/music' + }) }, - goToPage(page) { + unlockBook() { + console.log('解锁全书') + // 这里可以跳转到会员页面或者调用解锁接口 + uni.navigateTo({ + url: '/pages/index/member' + }) + }, + async goToPage(page) { this.currentPage = page console.log('跳转到页面:', page) + // 获取对应页面的数据(如果还没有获取过) + if (this.courseIdList[this.currentPage - 1] && this.bookPages[this.currentPage - 1].length === 0) { + await this.getBookPages(this.courseIdList[this.currentPage - 1]); + } }, - onSwiperChange(e) { + async onSwiperChange(e) { this.currentPage = e.detail.current + 1 + // 获取对应页面的数据(如果还没有获取过) + if (this.courseIdList[this.currentPage - 1] && this.bookPages[this.currentPage - 1].length === 0) { + await this.getBookPages(this.courseIdList[this.currentPage - 1]); + } + }, + async getCourseList(id) { + const res = await this.$api.book.coursePage({ + id: id + }) + if (res.code === 200) { + this.courseIdList = res.result.map(item => item.id) + // 初始化二维数组 换一种方式 + this.bookPages = this.courseIdList.map(() => []) + // 初始化标题数组 + this.pageTitles = this.courseIdList.map(() => '') + // 初始化第一页 + if (this.courseIdList.length > 0) { + await this.getBookPages(this.courseIdList[0]) + } + } + }, + async getBookPages(id) { + const res = await this.$api.book.coursesPageDetail({ + id: id + }) + if (res.code === 200) { + // 使用$set确保响应式更新 + // 确保当前页面存在 + if (this.currentPage - 1 < this.bookPages.length) { + this.$set(this.bookPages, this.currentPage - 1, JSON.parse(res.result.content)) + // 保存页面标题 + this.$set(this.pageTitles, this.currentPage - 1, res.result.title || '') + } + } + }, + // 获取课程列表 + async getCoursePageList (bookId) { + const res = await this.$api.book.course({ + id: bookId + }) + if (res.code === 200) { + this.courseList = res.result.records + // 打上序列号 + this.courseList = this.courseList.map((item, index) => ({ + ...item, + index, + })) + } } + }, + async onLoad(args) { + this.courseId = args.courseId + // 先获取点进来的课程的页面列表 + await Promise.all([this.getCourseList(this.courseId), this.getCoursePageList(args.bookId)]) + } } @@ -181,10 +468,13 @@ export default { } .navbar-right { + justify-content: flex-end; + flex: 1; } .navbar-title { + transform: translateX(-50rpx); flex: 1; text-align: center; font-family: PingFang SC; @@ -196,8 +486,8 @@ export default { .content-swiper { flex: 1; - height: calc(100vh - 200rpx); - margin-top: 100rpx; + height: calc(100vh - 100rpx); + // margin-top: 100rpx; margin-bottom: 100rpx; } @@ -208,6 +498,7 @@ export default { .content-area { flex: 1; padding: 0 40rpx; + padding-top: 100rpx; // background: linear-gradient(180deg, #DEFFFF 0%, #FBFEFF 22.65%, #F0FBFF 100%); height: 100%; box-sizing: border-box; @@ -254,36 +545,80 @@ export default { // margin-bottom: 20rpx; } .english-text { - display: block; + display: block; + font-family: PingFang SC; + font-weight: 600; + font-size: 32rpx; + line-height: 48rpx; + color: #3B3D3D; + // margin-bottom: 16rpx; + } + + .chinese-text { + display: block; + font-family: PingFang SC; + font-weight: 400; + font-size: 28rpx; + line-height: 48rpx; + color: #4F4F4F; + } + } + +/* 会员限制页面样式 */ +.member-content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 90%; + background-color: #F8F8F8; + padding: 40rpx; + margin: -40rpx; + box-sizing: border-box; +} + +.member-title { font-family: PingFang SC; - font-weight: 600; - font-size: 32rpx; - line-height: 48rpx; - color: #3B3D3D; - // margin-bottom: 16rpx; + font-weight: 500; + font-size: 40rpx; + line-height: 1; + color: #6f6f6f; + text-align: center; + margin-bottom: 48rpx; +} + +.member-button { + width: 670rpx; + height: 72rpx; + background: #06DADC; + border-radius: 200rpx; + display: flex; + align-items: center; + justify-content: center; } -.chinese-text { - display: block; +.member-button-text { font-family: PingFang SC; - // font-weight: 400; - font-size: 28rpx; - line-height: 48rpx; - color: #4F4F4F; + font-weight: 400; + font-size: 30rpx; + color: #FFFFFF; } - } .video-content { width: 100vw; margin: 200rpx -40rpx 0; height: 500rpx; background-color: #FFFFFF; - padding: 40rpx; + // padding: 40rpx; border-radius: 24rpx; display: flex; align-items: center; justify-content: center; - + .video-player{ + width: 670rpx; + margin: 0 auto; + height: 376rpx; + } } @@ -313,7 +648,7 @@ export default { bottom: 0; left: 0; right: 0; - background-color: #fff; + // background-color: #fff; border-top: 1rpx solid #EEEEEE; z-index: 1000; transition: transform 0.3s ease; @@ -328,6 +663,9 @@ export default { align-items: center; justify-content: space-between; padding: 24rpx 62rpx; + // z-index: 100; + // position: relative; + // background-color: #fff; // padding-bottom: calc(24rpx + env(safe-area-inset-bottom)); height: 88rpx; } @@ -401,4 +739,297 @@ export default { color: $primary-color; } } + +/* 课程弹出窗样式 */ +.course-popup { + padding: 0 32rpx; + max-height: 80vh; +} + +.popup-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20rpx 0; + border-bottom: 2rpx solid #EEEEEE + // margin-bottom: 40rpx; +} + +.popup-title { + font-family: PingFang SC; + font-weight: 500; + font-size: 34rpx; + color: #181818; +} + + +.course-list { + max-height: 60vh; + overflow-y: auto; +} + +.course-item { + display: flex; + align-items: center; + gap: 24rpx; + padding-top: 24rpx; + padding-right: 8rpx; + padding-bottom: 24rpx; + padding-left: 8rpx; + + border-bottom: 1px solid #EEEEEE; + cursor: pointer; + + &:last-child { + border-bottom: none; + } +} + +.course-number { + width: 80rpx; + font-family: PingFang SC; + // font-weight: 400; + font-size: 36rpx; + color: #999; + &.highlight { + color: $primary-color; + } + // margin-right: 24rpx; +} + +.course-content { + flex: 1; +} + +.course-english { + font-family: PingFang SC; + font-weight: 600; + font-size: 36rpx; + line-height: 44rpx; + color: #252545; + margin-bottom: 8rpx; + + &.highlight { + color: $primary-color; + } +} + +.course-chinese { + font-size: 28rpx; + line-height: 48rpx; + color: #3B3D3D; + &.highlight { + color: $primary-color; + } +} + +/* 释义弹窗样式 */ +.meaning-popup { + // width: 670rpx; + background-color: #FFFFFF; + // border-radius: 32rpx; + overflow: hidden; +} + +.meaning-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 32rpx; + border-bottom: 2rpx solid #EEEEEE; +} + +.close-btn { + width: 80rpx; +} + +.close-text { + font-family: PingFang SC; + font-size: 32rpx; + color: #8b8b8b; +} + +.meaning-title { + font-family: PingFang SC; + font-weight: 500; + font-size: 34rpx; + color: #181818; +} + +.meaning-content { + padding: 32rpx; +} + +.meaning-image { + width: 670rpx; + height: 268rpx; + border-radius: 24rpx; + margin-bottom: 32rpx; +} + +.word-info { + margin-bottom: 32rpx; +} + +.word-main { + display: flex; + align-items: center; + gap: 16rpx; + margin-bottom: 8rpx; +} + +.word-text { + font-family: PingFang SC; + font-weight: 500; + font-size: 40rpx; + color: #181818; +} + +.phonetic-container{ + display: flex; + gap: 24rpx; + .phonetic-text { + font-family: PingFang SC; + font-size: 28rpx; + color: #262626; + margin-bottom: 16rpx; + } +} + +.word-meaning { + display: flex; + gap: 16rpx; +} + +.part-of-speech { + font-family: PingFang SC; + font-size: 28rpx; + color: #262626; +} + +.meaning-text { + font-family: PingFang SC; + font-size: 28rpx; + color: #181818; + flex: 1; +} + +.knowledge-gain { + background: linear-gradient(180deg, #DEFFFF 0%, #FBFEFF 22.65%, #F0FBFF 100%); + border-radius: 24rpx; + padding: 32rpx 40rpx; +} + +.knowledge-header { + display: flex; + align-items: center; + gap: 16rpx; + margin-bottom: 20rpx; +} + +.knowledge-icon { + width: 48rpx; + height: 48rpx; +} + +.knowledge-title { + font-family: PingFang SC; + font-weight: 600; + font-size: 30rpx; + color: #3B3D3D; +} + +.knowledge-content { + font-family: PingFang SC; + font-size: 28rpx; + color: #4f4f4f; + line-height: 48rpx; +} + +/* 音频控制栏样式 */ +.audio-controls { + background: #fff; + padding: 20rpx 40rpx; + border-bottom: 1rpx solid #eee; + transition: transform 0.3s ease; + position: relative; + z-index: 10; + &.audio-hidden { + transform: translateY(100%); + + } +} + +.audio-time { + display: flex; + align-items: center; + margin-bottom: 20rpx; +} + +.time-text { + font-size: 28rpx; + color: #999; + min-width: 80rpx; +} + +.progress-container { + flex: 1; + margin: 0 20rpx; +} + +.progress-bar { + position: relative; + height: 6rpx; + background: #f0f0f0; + border-radius: 3rpx; +} + +.progress-fill { + position: absolute; + left: 0; + top: 0; + height: 100%; + background: #06DADC; + border-radius: 3rpx; + transition: width 0.1s ease; +} + +.progress-thumb { + position: absolute; + top: 50%; + width: 16rpx; + height: 16rpx; + background: #06DADC; + border-radius: 50%; + transform: translate(-50%, -50%); + transition: left 0.1s ease; +} + +.audio-controls-row { + display: flex; + align-items: center; + justify-content: space-between; +} + +.control-btn { + display: flex; + // flex-direction: column; + align-items: center; + + padding: 10rpx; + gap: 8rpx; +} + +.control-text { + font-size: 28rpx; + color: #4A4A4A; + // margin-top: 8rpx; +} + +.play-btn { + display: flex; + align-items: center; + justify-content: center; + padding: 10rpx; +} \ No newline at end of file diff --git a/subPages/home/directory.vue b/subPages/home/directory.vue index aa0a404..89f171b 100644 --- a/subPages/home/directory.vue +++ b/subPages/home/directory.vue @@ -41,8 +41,8 @@ @@ -98,7 +98,7 @@ 内容朗读 - + + + + + + + + + + + {{ voice.name }} + {{ voice.info }} + + + 已选择 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/subPages/member/recharge.vue b/subPages/member/recharge.vue index 5f2c2f0..a5cbe79 100644 --- a/subPages/member/recharge.vue +++ b/subPages/member/recharge.vue @@ -74,12 +74,39 @@ 选择优惠券 - 请选择 - + + 请选择 + + + + + {{ selectedCoupon.name }} + -¥{{ selectedCoupon.money }} + + + + + + + + 会员权益 @@ -134,28 +161,10 @@ selectedPackage: 0, // 默认选择第一个套餐 selectedMember: 0, // 默认选择第一个会员 defaultPackageList: [ - { - title: '当前会员没有返回套餐', - discountedprice: '36.00', - originalprice: '128', - gift: null, - content: '赠送66天' - }, - { - title: '当前会员没有返回套餐', - discountedprice: '88.00', - originalprice: '384', - gift: '180', - content: '赠送666天' - }, - { - title: '当前会员没有返回套餐', - discountedprice: '128.00', - originalprice: '384', - gift: null, - content: '赠送9999天' - } - ] + + ], + couponId: '', + selectedCoupon: null // 选中的优惠券信息 } }, computed:{ @@ -163,7 +172,45 @@ return this.list[this.selectedMember].setmeals.length ? this.list[this.selectedMember].setmeals : this.defaultPackageList } }, + + methods: { + handleCouponSelected(coupon) { + + // 处理选中的优惠券 + this.selectedCoupon = coupon + this.couponId = coupon.id + + uni.showToast({ + title: `已选择优惠券:¥${coupon.money}`, + icon: 'success' + }) + }, + async handleRecharge(){ + // console.log('选中的会员id是:', this.packageList[this.selectedPackage]); + + const object = { + memberId: this.list[this.selectedMember].id, + setmealId: this.packageList[this.selectedPackage].id + } + if (this.couponId) { + object.couponId = this.couponId + } + const res = await this.$api.member.openMember({...object}) + if (res.code === 200){ + if (res.result === 0){ + // 零元购 + uni.showToast({ + title: '充值成功', + icon: 'success' + }) + this.goBack() + }else { + // 调起微信支付 + this.$utils.wxPay(res.result) + } + } + }, goBack(){ uni.navigateBack() }, @@ -188,9 +235,8 @@ this.selectedPackage = index }, selectCoupon() { - uni.showToast({ - title: '功能开发中', - icon: 'none' + uni.navigateTo({ + url: '/subPages/user/discount?from=recharge' }) }, async getMemberList(){ @@ -205,8 +251,16 @@ } }, onShow(){ + // 监听优惠券选择事件 + uni.$on('couponSelected', this.handleCouponSelected) this.getMemberList() - } + }, + onUnload() { + // 移除事件监听 + uni.$off('couponSelected', this.handleCouponSelected) + console.log('接触监听'); + + }, } @@ -264,6 +318,11 @@ } +/* 立即开通会员按钮样式 */ +.member-button-section { + margin: 0 50rpx 40rpx; +} + // 会员套餐选择容器 .membership-container { background: linear-gradient(180deg, #DEFFFF 0%, #FBFEFF 22.65%, #FFFFFF 100%); @@ -370,9 +429,41 @@ align-items: center; border-bottom: 2rpx solid #f0f0f0; - .coupon-text { - font-size: 32rpx; - color: #C6C6C6; + .coupon-placeholder { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + + .coupon-text { + font-size: 32rpx; + color: #C6C6C6; + } + } + + .coupon-selected { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + + .coupon-info { + display: flex; + // flex-direction: column; + align-items: center; + justify-content: space-between; + .coupon-name { + font-size: 28rpx; + color: #181818; + margin-bottom: 4rpx; + } + + .coupon-amount { + font-size: 28rpx; + color: red; + font-weight: 500; + } + } } } } diff --git a/subPages/user/cash.vue b/subPages/user/cash.vue index 1df2bc7..e43b70f 100644 --- a/subPages/user/cash.vue +++ b/subPages/user/cash.vue @@ -25,7 +25,7 @@ @@ -73,7 +73,7 @@ export default { } }, methods: { - handleWithdraw() { + async handleWithdraw() { if (!this.realName) { uni.showToast({ title: '请输入真实姓名', @@ -89,6 +89,26 @@ export default { return } + const subRes = await this.$api.promotion.withdraw({ + name: this.realName, + money: this.amount + }) + if (subRes.code === 200) { + uni.showToast({ + title: '提现申请已提交', + icon: 'success' + }) + uni.navigateBack({ + delta: 1, + duration: 1000 + }) + } else { + uni.showToast({ + title: subRes.msg, + icon: 'none' + }) + } + // 提现逻辑 console.log('提现信息:', { realName: this.realName, diff --git a/subPages/user/discount.vue b/subPages/user/discount.vue index 41640fb..3f3272f 100644 --- a/subPages/user/discount.vue +++ b/subPages/user/discount.vue @@ -88,6 +88,7 @@ export default { return { mixinListApi: 'member.getCouponList', currentTab: 0, + fromPage: '', // 来源页面标识 tabList: [ { name: '待使用' }, { name: '已使用' }, @@ -122,6 +123,13 @@ export default { } }, + onLoad(options) { + // 接收来源页面参数 + if (options.from) { + this.fromPage = options.from + } + }, + computed: { }, @@ -140,10 +148,25 @@ export default { }, useCoupon(coupon) { - uni.showToast({ - title: '跳转到使用页面', - icon: 'none' - }) + // 如果是从充值页面跳转过来的,选择优惠券后返回 + if (this.fromPage === 'recharge') { + console.log('被选中了'); + // 通过事件总线传递选中的优惠券数据 + uni.$emit('couponSelected', { + id: coupon.id, + name: coupon.name, + money: coupon.money, + endTime: coupon.endTime + }) + + uni.navigateBack() + } else { + // 其他情况的处理逻辑 + uni.showToast({ + title: '跳转到使用页面', + icon: 'none' + }) + } } } } diff --git a/subPages/user/promote.vue b/subPages/user/promote.vue index 4d83f18..ec9ecc5 100644 --- a/subPages/user/promote.vue +++ b/subPages/user/promote.vue @@ -32,18 +32,18 @@ - + - 战斗世界 - ID: 5625354 + {{ userInfo.name }} + - 888 + {{ num }} 推广人数 - 341 + {{ userInfo.price }} 总佣金 @@ -55,7 +55,7 @@ 我的团队 - + 我的二维码 @@ -96,13 +96,14 @@ 分享 + }" type="primary" @click="goQrcode">分享 @@ -115,6 +116,8 @@ export default { data() { return { mixinListApi: 'promotion.water', + num: 0, + userInfo: {}, } }, methods: { @@ -127,10 +130,11 @@ export default { }) }, goCash() { - uni.navigateTo({ - url: '/subPages/user/cash' + uni.requestMerchantTransfer({ + }) }, + getText(widhdraw) { // 如果已经领取了 if (widhdraw.withdrawStatus !== '0') { @@ -140,9 +144,34 @@ export default { }else if(widhdraw.status === '2'){ return '审核失败!' } - } - } - + }, + // 获取推广统计 + async getStatistics() { + const res = await this.$api.promotion.statistics() + if (res.code === 200) { + this.num = res.result.num + } + }, + // 获取用户信息 + async getUserInfo() { + const res = await this.$api.login.getUserInfo() + if (res.code === 200) { + this.userInfo = res.result + } + }, + // 去二维码 + goQrcode() { + uni.navigateTo({ + url: '/subPages/user/share' + }) + }, + }, + async onShow() { + Promise.all([ + this.getUserInfo(), + this.getStatistics() + ]) + }, } @@ -208,7 +237,7 @@ background: linear-gradient(180deg, #DEFFFF 0%, #FBFEFF 22.65%, #F0FBFF 100%); // padding-top: 32rpx; padding-right: 40rpx; // padding-bottom: 32rpx; - padding-left: 40rpx; + // padding-left: 40rpx; height: 200rpx; .profile-avatar { width: 128rpx; @@ -239,7 +268,7 @@ background: linear-gradient(180deg, #DEFFFF 0%, #FBFEFF 22.65%, #F0FBFF 100%); .stat-item { text-align: center; - margin-left: 60rpx; + margin-left: 100rpx; .stat-number { display: block; diff --git a/subPages/user/share.vue b/subPages/user/share.vue new file mode 100644 index 0000000..9bf2070 --- /dev/null +++ b/subPages/user/share.vue @@ -0,0 +1,106 @@ + + + + + \ No newline at end of file diff --git a/utils/common.js b/utils/common.js index e33c974..5d375cd 100644 --- a/utils/common.js +++ b/utils/common.js @@ -54,6 +54,8 @@ const checkPhone = (phone) => { } // 转换时间戳为yyyy-mm-dd +// params: 时间戳 +// return yyyy-mmm-dd const formatTime = (time) => { if (!time) { return '时间格式错误,需要传入时间戳' @@ -65,9 +67,84 @@ const formatTime = (time) => { return `${year}-${month}-${day}` } +// 计算yyyy-mm-dd与当前时间的差值 +// params: yyyy-mm-dd格式的字符串 +// return: 差值(天) +const calculateDateDifference = (dateString) => { + if (!dateString) { + return '时间格式错误,需要传入yyyy-mm-dd格式的字符串' + } + // 传入值为yyyy-mm-dd格式的字符串 + const inputDate = new Date(dateString) + // 化为时间戳 + // const inputTime = inputDate.getTime() + const currentDate = new Date() + const timeDifference = inputDate - currentDate + + if (!(currentDate.setHours(0, 0, 0, 0) - inputDate.setHours(0, 0, 0, 0))) { + return 0 + } + // 如果为负 返回-1 + if (timeDifference < 0) { + return -1 + } + // 计算天数 + const dayDifference = Math.ceil(timeDifference / (1000 * 60 * 60 * 24)) + + return dayDifference +} + +// 微信支付方法 +// 传参1: 支付数据 +// 传参2: 成功回调 +// 传参3: 失败回调 +// 传三个参数 支付数据 成功回调 失败回调 +const wxPay = (paymentData, successCallback, failCallback) => { + uni.requestPayment({ + provider: 'wxpay', + timeStamp: paymentData.timeStamp, + nonceStr: paymentData.nonceStr, + package: paymentData.packageValue, + signType: paymentData.signType, + paySign: paymentData.paySign, + success: (res) => { + if (successCallback) { + successCallback(res) + } + console.log('支付成功:', res) + uni.showToast({ + title: '支付成功', + icon: 'success' + }) + // 支付成功后返回上一页 + setTimeout(() => { + this.goBack() + }, 1500) + }, + fail: (err) => { + console.log('支付失败:', err) + if (failCallback) { + failCallback(err) + } + if (err.errMsg === 'requestPayment:fail cancel') { + uni.showToast({ + title: '支付已取消', + icon: 'none' + }) + } else { + uni.showToast({ + title: '支付失败', + icon: 'none' + }) + } + } + }) +} export { authorize, checkPhone, - formatTime + formatTime, + calculateDateDifference, + wxPay } \ No newline at end of file diff --git a/utils/index.js b/utils/index.js index 3925288..a515809 100644 --- a/utils/index.js +++ b/utils/index.js @@ -1,7 +1,9 @@ import { authorize, checkPhone, - formatTime + formatTime, + calculateDateDifference, + wxPay } from '@/utils/common' import { @@ -14,6 +16,8 @@ export default { authorize, checkPhone, formatTime, + calculateDateDifference, + wxPay, uploadImage, chooseAndUpload, getFileExtension