Browse Source

'联调一半的功能了'

hfll
hflllll 2 months ago
parent
commit
062e145773
14 changed files with 580 additions and 438 deletions
  1. +86
    -0
      api/modules/book.js
  2. +19
    -1
      api/modules/promotion.js
  3. +7
    -1
      api/request.js
  4. +2
    -2
      config/index.js
  5. +5
    -3
      mixins/list.js
  6. +120
    -94
      pages/index/desk.vue
  7. +130
    -140
      pages/index/home.vue
  8. +24
    -0
      pages/index/member.vue
  9. +2
    -3
      pages/index/user.vue
  10. +60
    -44
      subPages/home/directory.vue
  11. +63
    -12
      subPages/home/search.vue
  12. +23
    -83
      subPages/user/discount.vue
  13. +21
    -53
      subPages/user/promote.vue
  14. +18
    -2
      subPages/user/team.vue

+ 86
- 0
api/modules/book.js View File

@ -1,5 +1,91 @@
import request from "@/api/request";
import http from "@/api/http";
export default {
// 删除书桌
async delStand(data){
return request({
url: "/books/delStand",
method: "POST",
data,
needToken: true,
})
},
// 加入书桌
async addStand(data){
return request({
url: "/books/addStand",
method: "POST",
data,
needToken: true,
})
},
// 查询书桌列表
async stand(data){
return request({
url: "/books/stand",
method: "GET",
data,
needToken: true,
})
},
// 查询书籍列表
async list(data, isDebounce = true){
if(isDebounce){
return http({
url: "/books/list",
method: "GET",
data,
debounce: 200
})
}else {
return request({
url: "/books/list",
method: "GET",
data
})
}
},
// 查询书籍标签
async label(data){
return request({
url: "/books/label",
method: "GET",
data
})
},
// 获取课程列表
async course(data){
return request({
url: "/books/course",
method: "GET",
data
})
},
// 查询书籍分类
async category(data){
return request({
url: "/books/category",
method: "GET",
data
})
},
// 查询书籍详情
async detail(data){
return request({
url: "/books/detail",
method: "GET",
data
})
}
}

+ 19
- 1
api/modules/promotion.js View File

@ -1,5 +1,23 @@
import request from "@/api/request";
export default {
async water(data){
return request({
url: "/promotion/water",
method: "GET",
data,
needToken: true
})
},
// 我的团队
async team(data){
return request({
url: "/promotion/team",
method: "GET",
data,
needToken: true
})
}
}

+ 7
- 1
api/request.js View File

@ -24,7 +24,10 @@ export default function request({
icon: 'none'
})
uni.reLaunch({ url: '/subPages/login/login' })
return
return Promise.reject({
code: 401,
message: '请先登录'
})
}
}
return new Promise((resolve, reject) => {
@ -79,6 +82,9 @@ export default function request({
switch (res.statusCode) {
case 401:
uni.reLaunch({ url: '/subPages/login/login' })
error.message = '请先登录'
break;
case 403:
uni.removeStorageSync('token')
uni.reLaunch({ url: '/subPages/login/login' })


+ 2
- 2
config/index.js View File

@ -12,14 +12,14 @@ const envParam = {
prod: 'production',
}
const env = envParam['test']
const env = envParam['dev']
// 全局配置
const config = {
// 网络全局配置
netConfig: {
development: {
baseURL: 'http://h5.xzaiyp.top/englishread-admin/doc.html',
baseURL: 'http://h5.xzaiyp.top/englishread-admin/appletApi',
},
testing: {
baseURL: 'https://www.multipleculture.com/englishread-admin/appletApi',


+ 5
- 3
mixins/list.js View File

@ -23,7 +23,9 @@ export default {
// 列表是否需要上拉加载
isReachBottomLoad: true,
// 额外返回出去的数据的路径
extraDataPath: ''
extraDataPath: '' ,
// 自定义onShow
customOnShow: false,
}
}
},
@ -113,11 +115,11 @@ export default {
},
},
async onShow() {
// if (!this.list.length) {
if (!this.mixinListConfig.customOnShow) {
if (this.mixinFnBeforePageShow) this.mixinFnBeforePageShow()
this.initPage()
await this.getList(true)
// }
}
},
async onPullDownRefresh() {
// 在下拉还没结束前 不做任何操作


+ 120
- 94
pages/index/desk.vue View File

@ -14,8 +14,12 @@
color="#C6C6C6"
height="32"
margin="0"
v-model="searchKeyword"
searchIconColor="#8B8B8B"
placeholderColor="#c6c6c6"
@search="handleSearch"
@clear="handleSearch"
@custom="handleSearch"
></uv-search>
</view>
</view>
@ -24,122 +28,125 @@
<view class="book-grid-container">
<view class="book-grid">
<view
v-for="(book, index) in bookList"
:key="index"
class="book-grid-item"
v-for="(book, index) in list"
:key="index"
class="book-grid-item"
@click="goDetail(book)"
@longpress="showDeleteConfirm(book, index)"
>
<view class="book-grid-cover">
<image :src="book.cover" mode="aspectFill"></image>
<image :src="book.book.booksImg" mode="aspectFill"></image>
<!-- 删除按钮 -->
<view class="delete-btn" @click.stop="showDeleteConfirm(book, index)">
<uv-icon name="close" size="16" color="#fff"></uv-icon>
</view>
</view>
<view class="book-grid-info">
<text class="book-grid-title">{{ book.title }}</text>
<text class="book-grid-title">{{ book.book.booksName }}</text>
<view class="book-grid-meta">
<text class="book-grid-grade">{{ book.grade }}/</text>
<text class="book-grid-grade">{{ book.book.categoryName }}/</text>
<image src="/static/播放图标.png" class="book-grid-duration-icon" />
<text class="book-grid-duration">{{ book.duration }}</text>
<text class="book-grid-duration">{{ book.book.duration }}</text>
</view>
</view>
</view>
</view>
<uv-loading-icon text="加载中" textSize="30rpx" v-if="isLoading"></uv-loading-icon>
<uv-empty v-else-if="!list.length" text="暂无数据"></uv-empty>
</view>
</view>
</template>
<script>
import MixinList from '@/mixins/list'
export default {
mixins: [MixinList],
data() {
return {
//
bookList: [
{
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: '精讲短文',
grade: '六级',
duration: '03:24'
},
{
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: '精讲短文',
grade: '考研',
duration: '03:24'
},
{
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: '精讲短文',
grade: '六级',
duration: '03:24'
},
{
cover: '/static/默认图片.png',
title: '精讲短文',
grade: '考研',
duration: '03:24'
},
{
cover: '/static/默认图片.png',
title: '精讲短文',
grade: '考研',
duration: '03:24'
}
]
mixinListApi: 'book.stand',
mixinListConfig: {
customOnShow: true
},
searchKeyword: '',
}
},
methods: {
mixinSetParams(){
const params = {}
if(this.searchKeyword){
params.title = this.searchKeyword
}
return params
},
goDetail(book){
uni.navigateTo({
url: '/subPages/home/directory?id=' + book.bookId
})
},
handleSearch(){
this.list = []
this.initPage()
this.getList(true)
},
//
showDeleteConfirm(book, index) {
uni.showModal({
title: '确认删除',
content: `确定要从书桌中移除《${book.book.booksName}》吗?`,
confirmText: '删除',
confirmColor: '#06DADC',
success: (res) => {
if (res.confirm) {
this.deleteBook(book, index)
}
}
})
},
//
async deleteBook(book, index) {
try {
uni.showLoading({
title: '删除中...'
})
const res = await this.$api.book.delStand({
id: book.id
})
if (res.code === 200) {
//
this.list.splice(index, 1)
uni.showToast({
title: '删除成功',
icon: 'success'
})
} else {
uni.showToast({
title: res.message || '删除失败',
icon: 'error'
})
}
} catch (error) {
uni.hideLoading()
console.error('删除书本失败:', error)
uni.showToast({
title: '删除失败',
icon: 'error'
})
}
}
},
onShow() {
//
if (uni.getStorageSync('token')) {
this.list = []
this.initPage()
this.getList(true)
}
}
}
</script>
@ -189,11 +196,30 @@ export default {
border-radius: 16rpx;
overflow: hidden;
margin-bottom: 16rpx;
position: relative;
image {
width: 100%;
height: 100%;
}
.delete-btn {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 32rpx;
height: 32rpx;
background: rgba(0, 0, 0, 0.6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
&:active {
background: rgba(0, 0, 0, 0.8);
}
}
}
.book-grid-info {


+ 130
- 140
pages/index/home.vue View File

@ -31,7 +31,7 @@
:class="{ active: activeTab === index }"
@click="switchTab(index)"
>
{{ tab }}
{{ tab.title }}
</view>
</view>
</scroll-view>
@ -53,93 +53,90 @@
></uv-swiper>
</view>
<!-- 今日更新 -->
<view class="section">
<view class="section-header">
<text class="section-title">今日更新</text>
<!-- 根据labelBooksData动态渲染书籍区块 -->
<view
v-for="(labelData, labelIndex) in labelBooksData"
:key="labelIndex"
class="section"
>
<view class="section-header" @click="goLabel(labelData.labelInfo)">
<text class="section-title">{{ labelData.labelInfo.title }}</text>
<view class="section-more">
<text>更多</text>
<uv-icon name="arrow-right" size="14" color="#888"></uv-icon>
</view>
</view>
<scroll-view show-scrollbar="false" class="content-scroll" scroll-x="true" >
<!-- 第一个label今日更新样式 -->
<scroll-view
v-if="labelIndex === 0"
show-scrollbar="false"
class="content-scroll"
scroll-x="true"
>
<view class="content-list">
<view
v-for="(item, index) in todayUpdates"
v-for="(item, index) in labelData.books"
:key="index"
class="content-item"
>
<view class="item-cover">
<image :src="item.cover" mode="aspectFill"></image>
<image :src="item.booksImg || '/static/默认图片.png'" mode="aspectFill"></image>
</view>
<view class="item-info">
<text class="item-title">{{ item.title }}</text>
<text class="item-author">{{ item.author }}</text>
<view class="item-duration"><image src="/static/播放图标.png" class="item-icon" /><text>{{ item.duration }}</text></view>
<text class="item-title">{{ item.booksName }}</text>
<text class="item-author">{{ item.booksAuthor }}</text>
<view class="item-duration">
<image src="/static/播放图标.png" class="item-icon" />
<text>{{ item.duration }}</text>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 推荐书籍 -->
<view class="section">
<view class="section-header">
<text class="section-title">推荐书籍</text>
<view class="section-more">
<text>更多</text>
<uv-icon name="arrow-right" size="14" color="#999"></uv-icon>
</view>
</view>
<scroll-view show-scrollbar="false" class="content-scroll" scroll-x="true" >
<!-- 第二个label推荐书籍样式 -->
<scroll-view
v-else-if="labelIndex === 1"
show-scrollbar="false"
class="content-scroll"
scroll-x="true"
>
<view class="book-list">
<view
v-for="(book, index) in recommendBooks"
v-for="(book, index) in labelData.books"
:key="index"
class="book-item"
@click="goBook"
@click="goBook(book)"
>
<view class="book-cover">
<image :src="book.cover" mode="aspectFill"></image>
<!-- <view class="book-duration">
<uv-icon name="time" size="10" color="#fff"></uv-icon>
<text>{{ book.duration }}</text>
</view> -->
<image :src="book.booksImg || '/static/默认图片.png'" mode="aspectFill"></image>
<view class="book-overlay">
<view class="book-duration">
<image src="/static/闹钟图标.png" class="book-duration-icon" />
<text class="book-duration-text">{{ book.duration }}</text>
</view>
<view class="book-title">{{ book.title }}</view>
<view class="book-title">{{ book.booksName }}</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 四级精讲短文合集书 -->
<view class="section">
<view class="section-header">
<text class="section-title">四级精讲短文合集书</text>
<view class="section-more">
<text>更多</text>
<uv-icon name="arrow-right" size="14" color="#999"></uv-icon>
</view>
</view>
<view class="book-grid">
<!-- 第三个及以后的label网格样式 -->
<view v-else class="book-grid">
<view
v-for="(book, index) in bookGridList"
v-for="(book, index) in labelData.books"
:key="index"
class="book-grid-item"
>
<view class="book-grid-cover">
<image :src="book.cover" mode="aspectFill"></image>
<image :src="book.booksImg || '/static/默认图片.png'" mode="aspectFill"></image>
</view>
<view class="book-grid-info">
<text class="book-grid-title">{{ book.title }}</text>
<text class="book-grid-title">{{ book.booksName }}</text>
<view class="book-grid-meta">
<text class="book-grid-grade">{{ book.grade }}/</text>
<text class="book-grid-grade">{{ book.categoryName }}/</text>
<image src="/static/播放图标.png" class="book-grid-duration-icon" />
<text class="book-grid-duration">{{ book.duration }}</text>
</view>
@ -169,7 +166,7 @@ export default {
data() {
return {
// Tab
tabs: ['为你推荐', '精选', '短文', '专栏', '初中', '四六级'],
tabs: [ ],
activeTab: 0,
//
@ -177,97 +174,15 @@ export default {
],
//
todayUpdates: [
{
cover: '/static/默认图片.png',
title: '全脑孩子:12项革命性策略...',
author: 'Daniel J. Siegel / Tina Payne Bryso...',
duration: '03:24'
},
{
cover: '/static/默认图片.png',
title: '全脑孩子:12项革命性策略...',
author: 'Daniel J. Siegel / Tina Payne Bryso...',
duration: '03:24'
},
{
cover: '/static/默认图片.png',
title: '全脑孩子:12项革命性策略...',
author: 'Daniel J. Siegel / Tina Payne Bryso...',
duration: '03:24'
},
{
cover: '/static/默认图片.png',
title: '全脑孩子:12项革命性策略...',
author: 'Daniel J. Siegel / Tina Payne Bryso...',
duration: '03:24'
},
//
labels: [
],
//
recommendBooks: [
{
cover: '/static/默认图片.png',
title: 'The Power of Now 擦拭才:The Power of Now :The Power of Now :',
duration: '03:24'
},
{
cover: '/static/默认图片.png',
title: 'Dealing in Desire 擦擦:The Power of Now :The Power of Now :',
duration: '03:24'
},
{
cover: '/static/默认图片.png',
title: 'A New Earth擦超2 :The Power of Now :The Power of Now :',
// label
labelBooksData: [],
duration: '03:24'
}
],
//
bookGridList: [
{
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: '精讲短文',
grade: '四级',
duration: '03:24'
},
{
cover: '/static/默认图片.png',
title: '精讲短文',
grade: '四级',
duration: '03:24'
},
{
cover: '/static/默认图片.png',
title: '精讲短文',
grade: '四级',
duration: '03:24'
}
],
//
recommendList: [
@ -278,8 +193,9 @@ export default {
methods: {
// Tab
switchTab(index) {
async switchTab(index) {
this.activeTab = index
await this.getBooksByLabels()
},
//
@ -298,9 +214,9 @@ export default {
url: '/subPages/home/search'
})
},
goBook() {
goBook(book) {
uni.navigateTo({
url: '/subPages/home/directory'
url: '/subPages/home/directory?id=' + book.id
})
},
async getBanner() {
@ -321,11 +237,85 @@ export default {
type: item.type
}))
}
},
//
async getCategory() {
const categoryRes = await this.$api.book.category()
if (categoryRes.code === 200){
this.tabs = categoryRes.result.map(item => ({
title:item.title,
id: item.id
}))
}
},
//
async getLabel() {
const labelRes = await this.$api.book.label()
if (labelRes.code === 200){
this.labels = labelRes.result.map(item => ({
title:item.lable,
id: item.id
}))
}
},
// label
async getBooksByLabels() {
if (!this.labels || this.labels.length === 0) {
console.log('labels数据为空,无法获取书籍')
return
}
try {
// label
const requests = this.labels.map(label =>
this.$api.book.list({
label: label.id,
pageNo: 1,
pageSize: 6,
category: this.tabs[this.activeTab].id
}, false)
)
//
const responses = await Promise.all(requests)
//
this.labelBooksData = responses.map((response, index) => {
if (response.code === 200) {
return {
labelInfo: this.labels[index],
books: response.result.records || []
}
} else {
console.error(`获取label ${this.labels[index].title} 的书籍失败:`, response)
return {
labelInfo: this.labels[index],
books: []
}
}
})
console.log('根据label获取的书籍数据:', this.labelBooksData)
} catch (error) {
console.error('获取书籍数据失败:', error)
this.labelBooksData = []
}
},
goLabel(label){
uni.navigateTo({
url: '/subPages/home/search?label=' + label.id
})
}
},
async onShow() {
Promise.all([this.getBanner(), this.getSignup()])
//
await Promise.all([this.getBanner(), this.getSignup(), this.getCategory(), this.getLabel()])
// label
await this.getBooksByLabels()
}
}
</script>


+ 24
- 0
pages/index/member.vue View File

@ -53,6 +53,25 @@
</view>
</view>
<!-- 立即开通会员按钮 -->
<view class="member-button-section">
<uv-button
type="primary"
text="立即开通会员"
:custom-style="{
width: '100%',
height: '82rpx',
borderRadius: '44rpx',
backgroundColor: '#06DADC',
fontSize: '36rpx',
fontWeight: '500',
border: '1px solid #06DADC'
}"
@click="goRecharge"
></uv-button>
</view>
<!-- 会员权益 -->
<view class="benefits-section">
<view class="benefits-title">会员权益</view>
@ -345,6 +364,11 @@ export default{
}
}
}
/* 立即开通会员按钮样式 */
.member-button-section {
margin: 40rpx 50rpx;
}
/* 会员权益样式 */
.benefits-section {
margin-top: 40rpx;


+ 2
- 3
pages/index/user.vue View File

@ -12,7 +12,7 @@
<view class="user-name">{{ isLogin ? userInfo.name : displayInfo.name }}</view>
<view class="user-phone">手机号{{ isLogin ? userInfo.phone || displayInfo.phone : displayInfo.phone }}</view>
<view class="user-desc">{{ displayInfo.description }}</view>
<!-- <view class="user-desc">{{ displayInfo.description }}</view> -->
</view>
</view>
</view>
@ -151,8 +151,7 @@ export default {
displayInfo: {
name: '点击登录',
phone: 'xxxxxxxxx',
avatar: '/static/默认头像.png',
description: '世界这么美,我想去看看~'
avatar: '/static/默认头像.png'
}
}
},


+ 60
- 44
subPages/home/directory.vue View File

@ -3,13 +3,13 @@
<view class="book-container">
<view class="book-info">
<view class="book-cover">
<image :src="bookInfo.cover" mode="aspectFill" :style="{width: '100%', height: '100%'}"></image>
<image :src="bookInfo.booksImg" mode="aspectFill" :style="{width: '100%', height: '100%'}"></image>
</view>
<view class="book-details">
<view class="book-title">{{ bookInfo.title }}</view>
<view class="book-subtitle">{{ bookInfo.subtitle }}</view>
<view class="book-author">{{ bookInfo.author }}</view>
<view class="book-level" :class="classMap[bookInfo.level]">{{ bookInfo.level }}</view>
<view class="book-title">{{ bookInfo.translate }}</view>
<view class="book-subtitle">{{ bookInfo.booksName }}</view>
<view class="book-author">{{ bookInfo.booksAuthor }}</view>
<view class="book-level" :class="classMap[bookInfo.vipInfo.title]">{{ bookInfo.vipInfo.title }}</view>
</view>
</view>
<view class="book-knowledge">
@ -41,19 +41,19 @@
</view>
<view class="course-list">
<view
v-for="(course, index) in courseList"
v-for="(course, index) in courseList.records"
:key="index"
class="course-item"
>
<view class="course-number">{{ String(index + 1).padStart(2, '0') }}</view>
<view class="course-content">
<view class="course-name">{{ course.name }}</view>
<view class="course-subtitle">{{ course.subtitle }}</view>
<view class="course-name">{{ course.english }}</view>
<view class="course-subtitle">{{ course.chinese }}</view>
</view>
</view>
</view>
<view class="course-footer">
<view class="course-total">全部课程 · {{ courseList.length }}</view>
<view class="course-total">全部课程 · {{ courseList.total }}</view>
<uv-icon name="arrow-right" size="24rpx" color="#999"></uv-icon>
</view>
</view>
@ -62,7 +62,7 @@
<view class="intro-section">
<view class="intro-title">简介</view>
<view class="intro-content">
{{ bookInfo.introduction }}
{{ bookInfo.booksIntro }}
</view>
</view>
@ -71,15 +71,15 @@
<view class="author-title">作者</view>
<view class="author-info">
<view class="author-avatar">
<image src="/static/默认头像.png" mode="aspectFill"></image>
<image :src="bookInfo.aouthorImg" mode="aspectFill"></image>
<view>
<view class="author-name">John Steinbeck</view>
<view class="author-subtitle">约翰·斯坦贝克</view>
<view class="author-name">{{ bookInfo.enAuthor }}</view>
<view class="author-subtitle">{{ bookInfo.booksAuthor }}</view>
</view>
</view>
<view class="author-details">
<view class="author-description">
约翰·斯坦贝克1902 1968是美国现代小说家1962 年荣获诺贝尔文学奖斯坦贝克一生创作出了 27 部作品它们大多着眼于美国底层人民的生活对贫穷而善良的劳苦大众展现出了强烈的同情心不仅为被压迫者申辩而且还歌颂了他们在困境中生存的勇气和斗志斯坦贝克最为优秀的几部作品都发表于 20世纪30年代之后例如荣获美国国家图书奖普利策奖的长篇小说愤怒的葡萄具有浓厚乡土气息的短篇集小红马和中篇小说人鼠之间以反法西斯战争为题材的中篇小说月亮下去了斯坦贝克既是地位崇高的文学大师也是深受美国民众爱戴的畅销作家他的作品不仅深刻影响了美国文学的发展也启发了人们对社会人性生死等话题的思考
{{ bookInfo.aouthorIntro }}
</view>
</view>
</view>
@ -89,7 +89,7 @@
<!-- 底部固定操作栏 -->
<view class="bottom-action-bar">
<view class="bottom-action-container">
<view class="action-button secondary">
<view class="action-button secondary" @click="joinCourse">
<image src="/static/课程图标.png" class="button-icon" mode="aspectFill"></image>
<text>加入课程</text>
</view>
@ -122,43 +122,59 @@ export default {
'盛放会员': 'book-level-3',
},
bookInfo: {
cover: '/static/默认图片.png',
title: 'The Little Prince',
subtitle: '小王子',
author: '圣-埃克苏佩里',
level: '朵蕾会员',
vocabularyRange: '1.6K~2.5K',
//
knowledgePoints: '<div>1. 41天读完法国作家圣埃克苏佩里文学作品</div><div>2. 掌握400+单词英文文本,收获对于法语的</div><div>3. 感受英文文学作品的魅力,在阅读中提升英语语感和文学素养</div><div>4. 通过英文原著,在生活中,对语言使用的文学性</div><div>5. 名作阅读</div>',
introduction: '在阿尔卑斯山上住着一个可爱的女孩小海蒂。她自幼失去父母,同性格倔强但心地善良的爷爷一起生活。后来,因为姨妈介绍的一个机会,海蒂去到了法兰克福,和一个叫克拉拉的女孩儿作伴。克拉拉自小患病无法行走,虽然两个孩子感情很好,但海蒂却因为想家患上了梦游症。最终海蒂回到了阿尔卑斯山,同山里的亲友们找回了幸福的生活。随后,克拉拉也如约前来拜访,在海蒂和爷爷的悉心照料下,奇迹发生了...'
},
id: '',
courseList: [
{
name: 'Look, George!',
subtitle: '你瞧,乔治'
},
{
name: 'A Live Mouse?',
subtitle: '活老鼠?'
},
{
name: 'Beans with Ketchup',
subtitle: '豆子配番茄酱'
},
{
name: 'Aunt Clara',
subtitle: '卡莉拉姑姑'
},
{
name: 'No Ketchup',
subtitle: '无番茄酱'
}
]
}
},
methods: {
goBack() {
uni.navigateBack()
},
//
async joinCourse() {
const joinRes = await this.$api.book.addStand({
id: this.id
})
if (joinRes.code === 200){
uni.showToast({
title: '加入成功',
icon: 'success',
duration: 2000
})
}
},
//
async getDetail() {
const detailRes = await this.$api.book.detail({
id: this.id
})
if (detailRes.code === 200){
this.bookInfo = detailRes.result
}
},
//
async getCourse() {
const courseRes = await this.$api.book.course({
id: this.id,
pageNo: 1,
pageSize: 5
})
if (courseRes.code === 200){
this.courseList = courseRes.result
}
},
},
onLoad(options) {
if (options.id){
this.id = options.id
Promise.all([
this.getDetail(),
this.getCourse()
])
}
}
}


+ 63
- 12
subPages/home/search.vue View File

@ -25,6 +25,7 @@
}"
@search="handleSearch"
@custom="handleSearch"
@clear="handleSearch"
></uv-search>
</view>
@ -39,7 +40,7 @@
:class="{ active: currentTab === index }"
@click="switchTab(index)"
>
{{ tab }}
{{ tab.title }}
</view>
</view>
</scroll-view>
@ -48,42 +49,53 @@
<!-- 搜索结果列表 -->
<view class="search-results">
<view
v-for="(book, index) in bookList"
v-for="(book, index) in list"
:key="index"
class="book-item"
@click="goToDetail(book)"
>
<view class="book-cover">
<image :src="book.cover" mode="aspectFill"></image>
<image :src="book.booksImg" mode="aspectFill"></image>
</view>
<view class="book-info">
<view class="book-title">{{ book.title }}</view>
<view class="book-author">{{ book.author }}</view>
<view class="book-title">{{ book.booksName }}</view>
<view class="book-author">{{ book.booksAuthor }}</view>
<view class="book-meta">
<view class="book-duration">
<image src="/static/播放图标.png" mode="aspectFill" class="book-icon"></image>
<text>{{ book.duration }}</text>
</view>
<view class="book-membership" :class="classMap[book.membershipType]">
{{ book.membership }}
<view class="book-membership" :class="classMap[book.vipInfo.title]">
{{ book.vipInfo.title }}
</view>
</view>
</view>
</view>
<uv-loading-icon text="加载中" textSize="30rpx" v-if="isLoading" ></uv-loading-icon>
<uv-empty v-else-if="list.length === 0" ></uv-empty>
</view>
</view>
</template>
<script>
import MixinList from '@/mixins/list.js'
export default {
mixins: [MixinList],
data() {
return {
searchKeyword: '短文',
mixinListApi: 'book.list',
// onShow
mixinListConfig: {
customOnShow: true,
},
searchKeyword: '',
label : '',
currentTab: 0,
categoryTabs: ['为你推荐', '精选', '短文', '专栏', '初中', '四六级'],
categoryTabs: [ ],
//
classMap: {
premium: 'book-membership-premium',
'盛放会员': 'book-membership-premium',
vip: 'book-membership-vip',
basic: 'book-membership-basic',
},
@ -156,19 +168,58 @@ export default {
}
},
methods: {
mixinSetParams(){
const params = {
category: this.categoryTabs[this.currentTab].id,
}
if(this.label){
params.label = this.label
}
if(this.searchKeyword){
params.title = this.searchKeyword
}
return params
},
handleSearch() {
console.log('搜索:', this.searchKeyword)
this.list = []
this.initPage()
this.getList(true)
//
},
switchTab(index) {
this.currentTab = index
console.log('切换分类:', this.categoryTabs[index])
this.list = []
this.initPage()
this.getList(true)
//
},
goToDetail(book) {
console.log('查看详情:', book)
//
uni.navigateTo({
url: '/subPages/home/directory?id=' + book.id
})
},
//
async getCategory() {
const categoryRes = await this.$api.book.category()
if (categoryRes.code === 200){
this.categoryTabs = categoryRes.result.map(item => ({
title:item.title,
id: item.id
}))
}
},
},
onLoad(options) {
if (options.label){
this.label = options.label
}
},
async onShow() {
await this.getCategory()
this.getList()
}
}
</script>


+ 23
- 83
subPages/user/discount.vue View File

@ -17,36 +17,36 @@
<!-- 优惠券列表 -->
<view class="coupon-list">
<view
v-for="(coupon, index) in currentCoupons"
v-for="(coupon, index) in list"
:key="index"
class="coupon-item"
:class="{
'used-coupon': coupon.status === 'used',
'expired-coupon': coupon.status === 'expired'
'used-coupon': currentTab === 1,
'expired-coupon': currentTab === 2
}"
>
<!-- 左侧金额区域 -->
<view class="coupon-left">
<text class="coupon-symbol">¥</text>
<text class="coupon-amount">{{ coupon.amount }}</text>
<text class="coupon-amount">{{ coupon.money }}</text>
<!-- 状态盖章 -->
<image
v-if="coupon.status !== 'available'"
v-if="currentTab !== 0"
class="status-stamp"
:src="coupon.status === 'used' ? '/static/已使用盖章.png' : '/static/已过期盖章.png'"
:src="currentTab === 1 ? '/static/已使用盖章.png' : '/static/已过期盖章.png'"
mode="aspectFit"
></image>
</view>
<!-- 右侧信息区域 -->
<view class="coupon-right">
<view class="coupon-title">{{ coupon.title }}</view>
<view class="coupon-expire">有效期至 {{ coupon.expireDate }}</view>
<view class="coupon-title">{{ coupon.name }}</view>
<view class="coupon-expire">有效期至 {{ coupon.endTime }}</view>
<!-- 使用按钮或状态 -->
<view class="coupon-action">
<uv-button
v-if="coupon.status === 'available'"
v-if="currentTab === 0"
:customStyle="buttonStyle"
text="去使用"
@click="useCoupon(coupon)"
@ -55,11 +55,11 @@
v-else
class="coupon-status"
:class="{
'used-status': coupon.status === 'used',
'expired-status': coupon.status === 'expired'
'used-status': currentTab === 1,
'expired-status': currentTab === 2
}"
>
{{ coupon.status === 'used' ? '已使用' : '已过期' }}
{{ currentTab === 1 ? '已使用' : '已过期' }}
</view>
</view>
@ -67,9 +67,10 @@
</view>
</view>
<uv-loading-icon text="加载中" textSize="30rpx" v-if="isLoading"></uv-loading-icon>
<!-- 空状态 -->
<uv-empty
v-if="currentCoupons.length === 0"
v-else-if="list === 0"
text="暂无优惠券"
:textColor="emptyStyle.textColor"
:textSize="emptyStyle.textSize"
@ -117,86 +118,25 @@ export default {
textColor: '#999',
textSize: '28rpx',
marginTop: '200rpx'
},
//
coupons: {
available: [
{
id: 1,
title: '专属福利】20元红包',
amount: 20,
expireDate: '2026-04-28',
status: 'available'
},
{
id: 2,
title: '专属福利】400元红包',
amount: 400,
expireDate: '2026-04-28',
status: 'available'
},
{
id: 3,
title: '专属福利】400元红包',
amount: 400,
expireDate: '2026-04-28',
status: 'available'
},
{
id: 1,
title: '专属福利】20元红包',
amount: 20,
expireDate: '2026-04-28',
status: 'available'
},
{
id: 2,
title: '专属福利】400元红包',
amount: 400,
expireDate: '2026-04-28',
status: 'available'
},
{
id: 3,
title: '专属福利】400元红包',
amount: 400,
expireDate: '2026-04-28',
status: 'available'
},
],
used: [
{
id: 4,
title: '专属福利】20元红包',
amount: 20,
expireDate: '2026-04-28',
status: 'used'
}
],
expired: [
{
id: 5,
title: '专属福利】20元红包',
amount: 20,
expireDate: '2026-04-28',
status: 'expired'
}
]
}
}
},
computed: {
currentCoupons() {
const statusMap = ['available', 'used', 'expired']
return this.coupons[statusMap[this.currentTab]] || []
}
},
methods: {
mixinSetParams() {
return {
status: String(this.currentTab)
}
},
onTabChange(index) {
this.currentTab = index
this.list = []
this.initPage()
this.getList(true)
},
useCoupon(coupon) {


+ 21
- 53
subPages/user/promote.vue View File

@ -75,18 +75,18 @@
<!-- 转账记录容器 -->
<view class="record-container">
<view class="record-list">
<view class="record-item" v-for="(item, index) in recordList" :key="index">
<view class="record-item" v-for="(item, index) in list" :key="index">
<view class="record-avatar">
<image class="avatar" :src="item.avatar" mode="aspectFill"></image>
<text class="avatar-name">{{ item.avatarName }}</text>
<image class="avatar" :src="item.userInfo.avatar" mode="aspectFill"></image>
<text class="avatar-name">{{ item.user_dictText }}</text>
</view>
<view class="record-info">
<text class="record-title">{{ item.title }}</text>
<text class="record-date">{{ item.date }}</text>
<text class="record-date">{{ item.createTime }}</text>
</view>
<view class="record-amount">
<text class="amount">{{ item.amount }}</text>
<text class="status" :class="item.statusClass">{{ item.status }}</text>
<text class="amount">+{{ item.money }}</text>
<text class="status" :class="item.withdrawal.status === '1' && withdrawStatus === '0' ? 'status-available' : 'status-received'" v-if="item.withdrawal">{{ getText(item.withdrawal) }}</text>
</view>
</view>
</view>
@ -109,56 +109,12 @@
</template>
<script>
import MixinList from '@/mixins/list'
export default {
mixins: [MixinList],
data() {
return {
recordList: [
{
avatar: '/static/待上传头像.png',
title: '分享提成获得奖励',
date: '2025-03-18',
amount: '+5',
status: '已领取,微信打款',
statusClass: 'status-received',
avatarName: '战斗世界'
},
{
avatar: '/static/待上传头像.png',
title: '分享提成获得奖励',
date: '2025-03-18',
amount: '+5',
status: '领取',
statusClass: 'status-available',
avatarName: '战斗世界'
},
{
avatar: '/static/待上传头像.png',
title: '分享提成获得奖励',
date: '2025-03-18',
amount: '+5',
status: '已领取,线下打款',
statusClass: 'status-received',
avatarName: '战斗世界'
},
{
avatar: '/static/待上传头像.png',
title: '分享提成获得奖励',
date: '2025-03-18',
amount: '+5',
status: '领取',
statusClass: 'status-available',
avatarName: '战斗世界'
},
{
avatar: '/static/待上传头像.png',
title: '分享提成获得奖励',
date: '2025-03-18',
amount: '+5',
status: '领取',
statusClass: 'status-available',
avatarName: '战斗世界'
}
]
mixinListApi: 'promotion.water',
}
},
methods: {
@ -174,8 +130,20 @@ export default {
uni.navigateTo({
url: '/subPages/user/cash'
})
},
getText(widhdraw) {
//
if (widhdraw.withdrawStatus !== '0') {
return '已领取,' + (widhdraw.method === '0' ? '微信打款' : '线下付款')
}else if (widhdraw.status === '0') {
return '提现待审核中'
}else if(widhdraw.status === '2'){
return '审核失败!'
}
}
}
}
</script>


+ 18
- 2
subPages/user/team.vue View File

@ -12,7 +12,7 @@
bg-color="#f5f5f5"
@search="handleSearch"
@custom="handleSearch"
@clear="handleCancel"
:action-style="{color: '#fff', backgroundColor: '#06DADC', borderRadius:'8rpx', width:'100rpx', height: '64rpx', lineHeight: '64rpx', borderRadius: '198rpx', text:'white', fontSize:'26rpx'}"
action
></uv-search>
@ -25,7 +25,7 @@
<view class="member-list">
<view
class="member-item"
v-for="(member, index) in filteredMembers"
v-for="(member, index) in list"
:key="index"
@click="handleMemberClick(member)"
>
@ -37,14 +37,19 @@
<text class="member-name">{{ member.name }}</text>
</view>
</view>
<uv-loading-icon text="加载中" textSize="30rpx" v-if="isLoading"></uv-loading-icon>
<uv-empty v-else-if="list.length === 0 " text="暂无数据"></uv-empty>
</view>
</view>
</template>
<script>
import MixinList from '@/mixins/list'
export default {
mixins: [MixinList],
data() {
return {
mixinListApi: 'promotion.team',
searchKeyword: '',
members: [
{
@ -121,9 +126,20 @@ export default {
}
},
methods: {
mixinSetParams(){
return {
name: this.searchKeyword
}
},
handleCancel(){
this.initPage()
this.getList(true)
},
handleSearch() {
// computed
console.log('搜索关键词:', this.searchKeyword)
this.initPage()
this.getList(true)
},
handleMemberClick(member) {
console.log('点击成员:', member)


Loading…
Cancel
Save