Преглед изворни кода

新增小说相关API接口,优化书籍列表加载逻辑,更新分类页面和个人中心界面,完善用户交互体验,修复部分样式问题。

yangwenlan пре 1 година
родитељ
комит
8f6c80c53e

+ 27 - 0
api/api.js

@@ -23,3 +23,30 @@ export const getBannerList = (channel) => {
 export const getCardNovels = (channel) => {
     return requestAll('/novel/cardNovels', {channel:channel}, 'GET', addTokenToHeader())
 }
+
+// /novel/cardNovelsMore
+export const getCardNovelsMore = (id) => {
+    return requestAll('/novel/cardNovelsMore', {id}, 'GET', addTokenToHeader())
+}
+
+// /category/list
+export const getCategoryList = (category) => {
+    console.log('category',category)
+    return requestAll('/category/list', {category}, 'GET', addTokenToHeader())
+}
+
+// /novel/detail
+export const getNovelDetail = (id) => {
+    return requestAll('/novel/detail', {id}, 'GET', addTokenToHeader())
+}
+
+// /novel/search
+export const getNovelSearch = ({
+    keyword,
+    page=1,
+    size=10,
+    category,
+    isOver=0
+}) => {
+    return requestAll('/novel/search', {keyword,page,size,category,isOver}, 'GET', addTokenToHeader())
+}

BIN
assets/images/avatar-placeholder.jpg


+ 8 - 1
components/charge-dialog/index.js

@@ -1,5 +1,5 @@
 const novelPlugin = requirePlugin('novel-plugin')
-
+import { getNovelDetail } from '../../api/api'
 Component({
   properties: {
     novelManagerId: {
@@ -24,6 +24,13 @@ Component({
     },
   },
 
+  onLoad(options) {
+    console.log('options',options)
+    getNovelDetail(options.id).then(res => {
+      console.log('res',res)
+    })
+  },
+
   methods: {
     unlock() {
       // 取出对应的阅读器实例

+ 10 - 46
pages/book/list.js

@@ -1,51 +1,37 @@
+import { getCardNovelsMore } from '../../api/api'
+
 Page({
   data: {
     pageTitle: '',
     bookList: [],
     loading: false,
-    hasMore: true,
-    pageNum: 1,
-    pageSize: 10,
-    type: ''  // 列表类型:editor(主编推荐)、hot(全网热搜)、recommend(强力推荐)
+    id: ''
   },
 
   onLoad(options) {
-    // 设置页面标题
-    const titles = {
-      'editor': '主编推荐',
-      'hot': '全网热搜',
-      'recommend': '强力推荐'
-    }
-    const type = options.type || 'editor'
-    const pageTitle = titles[type] || '书籍列表'
+    const pageTitle = options.name || '书籍列表'
     
     this.setData({
       pageTitle,
-      type
+      id: options.id
     })
 
-    // 加载初始数据
+    // 加载数据
     this.loadBookList()
   },
 
   // 加载书籍列表
   async loadBookList() {
-    if (this.data.loading || !this.data.hasMore) return
+    if (this.data.loading) return
 
     this.setData({ loading: true })
 
     try {
-      // 模拟API请求
-      const mockData = await this.getMockData()
-      
-      const { bookList } = this.data
-      const newList = bookList.concat(mockData)
+      const result = await getCardNovelsMore(this.data.id)
       
       this.setData({
-        bookList: newList,
-        loading: false,
-        hasMore: mockData.length === this.data.pageSize,
-        pageNum: this.data.pageNum + 1
+        bookList: result || [],
+        loading: false
       })
     } catch (error) {
       console.error('加载书籍列表失败:', error)
@@ -57,28 +43,6 @@ Page({
     }
   },
 
-  // 模拟获取数据
-  getMockData() {
-    return new Promise((resolve) => {
-      setTimeout(() => {
-        const mockBooks = Array(this.data.pageSize).fill(0).map((_, index) => ({
-          id: this.data.bookList.length + index + 1,
-          title: '书籍标题 ' + (this.data.bookList.length + index + 1),
-          cover: '/images/book-cover.png',
-          description: '这是一段书籍描述,介绍书籍的主要内容和特点。这是一段书籍描述,介绍书籍的主要内容和特点。',
-          readCount: Math.floor(Math.random() * 10000),
-          tag: Math.random() > 0.5 ? '推荐' : '热门'
-        }))
-        resolve(mockBooks)
-      }, 500)
-    })
-  },
-
-  // 触底加载更多
-  onReachBottom() {
-    this.loadBookList()
-  },
-
   // 点击书籍项
   onBookTap(e) {
     const { id } = e.currentTarget.dataset

+ 6 - 8
pages/book/list.wxml

@@ -11,20 +11,21 @@
   </view>
 
   <!-- 书籍列表 -->
-  <scroll-view class="book-list" scroll-y bindscrolltolower="onReachBottom">
-    <block wx:for="{{bookList}}" wx:key="id">
+  <scroll-view class="book-list" scroll-y>
+    <block wx:for="{{bookList.novelList}}" wx:key="id">
       <view class="book-item" bindtap="onBookTap" data-id="{{item.id}}">
         <image class="book-cover" src="{{item.cover}}" mode="aspectFill" />
         <view class="book-info">
           <view>
             <text class="book-title">{{item.title}}</text>
-            <text class="book-desc">{{item.description}}</text>
+            <text class="book-author">{{item.author}}</text>
+            <text class="book-desc">{{item.brief}}</text>
           </view>
           <view class="book-stats">
-            <text>{{item.readCount}}人在读</text>
+            <text>{{item.readingNumber}}人在读</text>
+            <text class="book-category" wx:if="{{item.categoryNameLevel2}}">{{item.categoryNameLevel2}}</text>
           </view>
         </view>
-        <text class="book-tag">{{item.tag}}</text>
       </view>
     </block>
 
@@ -32,8 +33,5 @@
     <view class="loading" wx:if="{{loading}}">
       <text>加载中...</text>
     </view>
-    <view class="no-more" wx:if="{{!loading && !hasMore}}">
-      <text>没有更多了</text>
-    </view>
   </scroll-view>
 </view> 

+ 99 - 39
pages/category/category.js

@@ -1,27 +1,16 @@
+import { getCategoryList, getNovelSearch } from '../../api/api'
+
 Page({
   data: {
     gender: 'male', // 当前性别选择
-    currentCategory: 'qingchun', // 当前选中的分类
-    bookList: [
-      {
-        id: 1,
-        title: '狂龙圣医',
-        description: '大夏国谪仙少年苏浩,意外封印三魂六魄,一朝觉醒,如若龙升天。一手保命天医术,...',
-        coverUrl: '/images/book1.jpg'
-      },
-      {
-        id: 2,
-        title: '被逼后,她成了豪门宠',
-        description: '某诡异豪门家族的葬礼中,她从漆黑带回一个女子,听说那女子手段不凡,穿...',
-        coverUrl: '/images/book2.jpg'
-      },
-      {
-        id: 3,
-        title: '恢复记忆后,她回归豪门找前夫',
-        description: '江浅从人一朝落难,天之骄女跌落泥泞,传言预知生死,她以倾城之姿...',
-        coverUrl: '/images/book3.jpg'
-      }
-    ]
+    currentCategory: '', // 当前选中的分类
+    categories: [],
+    bookList: [],
+    loading: false,
+    keyword: '',
+    page: 1,
+    size: 10,
+    hasMore: true // 是否还有更多数据
   },
 
   onLoad: function(options) {
@@ -40,45 +29,116 @@ Page({
   },
 
   // 同步性别状态
-  syncGenderState: function() {
+  syncGenderState: async function() {
     const gender = wx.getStorageSync('gender') || 'male';
     if (gender !== this.data.gender) {
       this.setData({ gender });
+      await this.loadCategories();
     }
   },
 
   // 切换性别
-  switchGender: function(e) {
+  switchGender: async function(e) {
     const { gender } = e.detail;
     this.setData({
       gender: gender,
-      currentCategory: 'qingchun' // 切换性别时重置分类
+      currentCategory: '',
+      bookList: [],
+      page: 1,
+      hasMore: true
     });
-    // 重新加载分类和书籍列表
-    this.loadBookList('qingchun');
+    await this.loadCategories();
+  },
+
+  // 加载分类列表
+  async loadCategories() {
+    try {
+      const channel = this.data.gender === 'male' ? "man" : "woman";
+      const result = await getCategoryList(channel);
+      
+      if (result && Array.isArray(result)) {
+        this.setData({
+          categories: result,
+          currentCategory: result[0]?.code || '',
+          page: 1,
+          hasMore: true
+        });
+        // 加载第一个分类的书籍
+        if (result[0]?.code) {
+          this.loadBookList(result[0].code);
+        }
+      }
+    } catch (error) {
+      console.error('加载分类失败:', error);
+      wx.showToast({
+        title: '加载分类失败',
+        icon: 'none'
+      });
+    }
   },
 
   // 切换分类
   switchCategory: function(e) {
     const category = e.currentTarget.dataset.category;
     this.setData({
-      currentCategory: category
+      currentCategory: category,
+      bookList: [],
+      page: 1,
+      hasMore: true
     });
-    // 加载对应分类的书籍列表
     this.loadBookList(category);
   },
 
   // 加载书籍列表
-  loadBookList: function(category) {
-    wx.showLoading({
-      title: '加载中...'
-    });
-    
-    // 模拟接口调用
-    setTimeout(() => {
-      wx.hideLoading();
-      // 实际开发中这里应该调用真实的接口,需要传入 gender 和 category 参数
-      // this.requestBookList(this.data.gender, category);
-    }, 500);
+  async loadBookList(category) {
+    if (this.data.loading) return;  // 只检查loading状态,移除hasMore检查
+
+    this.setData({ loading: true });
+
+    try {
+      const params = {
+        keyword: this.data.keyword,
+        page: this.data.page,
+        size: this.data.size,
+        category: category,
+        isOver: 0
+      };
+
+      const result = await getNovelSearch(params);
+      
+      if (result && result.records) {
+        const newList = this.data.page === 1 ? result.records : [...this.data.bookList, ...result.records];
+        const hasMore = !result.last;
+        
+        this.setData({
+          bookList: newList,
+          loading: false,
+          hasMore: hasMore,
+          page: hasMore ? this.data.page + 1 : this.data.page
+        });
+      } else {
+        this.setData({
+          loading: false,
+          hasMore: false
+        });
+      }
+    } catch (error) {
+      console.error('加载书籍列表失败:', error);
+      this.setData({ 
+        loading: false,
+        hasMore: false
+      });
+      wx.showToast({
+        title: '加载失败,请重试',
+        icon: 'none'
+      });
+    }
+  },
+
+  // 监听滚动到底部
+  onScrollToLower: function() {
+    if (this.data.currentCategory && this.data.hasMore) {
+      this.loadBookList(this.data.currentCategory);
+    }
   }
 })

+ 31 - 26
pages/category/category.wxml

@@ -12,41 +12,46 @@
   <view class="content">
     <!-- 左侧分类列表 -->
     <scroll-view scroll-y class="category-list">
-      <view class="category-item {{currentCategory === 'qingchun' ? 'active' : ''}}" data-category="qingchun" bindtap="switchCategory">
-        <text>青春校园</text>
-      </view>
-      <view class="category-item {{currentCategory === 'zongcai' ? 'active' : ''}}" data-category="zongcai" bindtap="switchCategory">
-        <text>总裁豪门</text>
-      </view>
-      <view class="category-item {{currentCategory === 'dushi' ? 'active' : ''}}" data-category="dushi" bindtap="switchCategory">
-        <text>都市言情</text>
-      </view>
-      <view class="category-item {{currentCategory === 'chongsheng' ? 'active' : ''}}" data-category="chongsheng" bindtap="switchCategory">
-        <text>重生年代</text>
-      </view>
-      <view class="category-item {{currentCategory === 'xuanhuan' ? 'active' : ''}}" data-category="xuanhuan" bindtap="switchCategory">
-        <text>玄幻言情</text>
-      </view>
-      <view class="category-item {{currentCategory === 'xiandai' ? 'active' : ''}}" data-category="xiandai" bindtap="switchCategory">
-        <text>现代言情</text>
-      </view>
-      <view class="category-item {{currentCategory === 'gudai' ? 'active' : ''}}" data-category="gudai" bindtap="switchCategory">
-        <text>古代言情</text>
-      </view>
-      <view class="category-item {{currentCategory === 'wuxia' ? 'active' : ''}}" data-category="wuxia" bindtap="switchCategory">
-        <text>武侠架空</text>
+      <view 
+        wx:for="{{categories}}" 
+        wx:key="code"
+        class="category-item {{currentCategory === item.code ? 'active' : ''}}" 
+        data-category="{{item.code}}" 
+        bindtap="switchCategory"
+      >
+        <text>{{item.name}}</text>
       </view>
     </scroll-view>
 
     <!-- 右侧书籍列表 -->
-    <scroll-view scroll-y class="book-list">
+    <scroll-view 
+      scroll-y 
+      class="book-list"
+      bindscrolltolower="onScrollToLower"
+      lower-threshold="50"
+    >
       <view class="book-item" wx:for="{{bookList}}" wx:key="id">
-        <image class="book-cover" src="{{item.coverUrl}}" mode="aspectFill"/>
+        <image class="book-cover" src="{{item.cover}}" mode="aspectFill"/>
         <view class="book-info">
           <text class="book-title">{{item.title}}</text>
-          <text class="book-desc">{{item.description}}</text>
+          <text class="book-desc">{{item.brief}}</text>
         </view>
       </view>
+      
+      <!-- 加载状态 -->
+      <view class="loading" wx:if="{{loading}}">
+        <text>加载中...</text>
+      </view>
+      
+      <!-- 没有更多数据 -->
+      <view class="no-more" wx:if="{{!hasMore && bookList.length > 0}}">
+        <text>没有更多了</text>
+      </view>
+      
+      <!-- 暂无数据 -->
+      <view class="empty" wx:if="{{!loading && bookList.length === 0}}">
+        <text>暂无数据</text>
+      </view>
     </scroll-view>
   </view>
 </view>

+ 21 - 2
pages/category/category.wxss

@@ -7,6 +7,7 @@ page {
   height: 100vh;
   display: flex;
   flex-direction: column;
+  padding-bottom:0;
 }
 
 /* 顶部区域 */
@@ -48,6 +49,8 @@ page {
   flex: 1;
   display: flex;
   overflow: hidden;
+  width: 100vw;
+  padding-bottom: 20rpx;
 }
 
 /* 左侧分类列表 */
@@ -110,14 +113,13 @@ page {
   flex: 1;
   display: flex;
   flex-direction: column;
-  justify-content: space-between;
 }
 
 .book-title {
   font-size: 30rpx;
   font-weight: bold;
   color: #333;
-  margin-bottom: 10rpx;
+  margin-bottom: 15rpx;
 }
 
 .book-desc {
@@ -129,3 +131,20 @@ page {
   -webkit-box-orient: vertical;
   overflow: hidden;
 }
+
+.loading, .no-more, .empty {
+  width: 100%;
+  height: 80rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.loading text, .no-more text, .empty text {
+  font-size: 24rpx;
+  color: #999;
+}
+
+.empty {
+  height: 300rpx;
+}

+ 5 - 5
pages/index/index.js

@@ -13,6 +13,7 @@ Page({
     autoplay: true,
     interval: 3000,
     duration: 500,
+    cardNovels:[],
     bannerList: [], // 添加banner列表数据
     
     // 性别选项
@@ -184,6 +185,7 @@ Page({
   // 跳转到功能页面
   goToFeature: function(e) {
     const feature = e.currentTarget.dataset.feature;
+    
     switch(feature) {
       case 'recent':
         wx.navigateTo({
@@ -200,11 +202,6 @@ Page({
           url: '/pages/category/hot'
         });
         break;
-      case 'editor':
-        wx.navigateTo({
-          url: '/pages/book/list?type=editor'
-        });
-        break;
       case 'hot':
         wx.navigateTo({
           url: '/pages/book/list?type=hot'
@@ -273,6 +270,9 @@ Page({
   async getCardNovels() {
     try {
       const cardNovels = await getCardNovels(this.data.gender === 'male' ? 1 : 2);
+      this.setData({
+        cardNovels:cardNovels
+      })
       // 默认使用第一组数据作为主编推荐
       if (cardNovels[0]) {
         const [firstBook, ...restBooks] = cardNovels[0].novelList;

+ 7 - 8
pages/index/index.wxml

@@ -29,14 +29,13 @@
     <!-- 主Banner区域 -->
     <view wx:if="{{bannerList.length > 0}}" class="main-banner">
       <swiper 
-        indicator-dots="{{indicatorDots}}"
         autoplay="{{autoplay}}"
         interval="{{interval}}"
         duration="{{duration}}"
         circular="true"
         class="banner-swiper">
         <swiper-item wx:for="{{bannerList}}" wx:key="id">
-          <image src="{{item.imageUrl}}" mode="aspectFill" bindtap="onBannerTap" data-item="{{item}}"/>
+          <image src="{{item.cover}}" mode="aspectFill" bindtap="onBannerTap" data-item="{{item}}"/>
         </swiper-item>
       </swiper>
     </view>
@@ -62,8 +61,8 @@
   <!-- 主编推荐 -->
   <view class="recommend-section">
     <view class="section-header">
-      <text class="title">{{editorTitle || '编辑推荐'}}</text>
-      <text class="more" bindtap="goToFeature" data-feature="editor">更多 ></text>
+      <text class="title">{{cardNovels[0].name}}</text>
+      <navigator url="/pages/book/list?type=editor&id={{cardNovels[0].settingId}}&name={{cardNovels[0].name}}" class="more">更多 ></navigator>
     </view>
 
     <view class="recommend-main">
@@ -88,8 +87,8 @@
   <!-- 全网热推 -->
   <view class="hot-section">
     <view class="section-header">
-      <text class="title">{{hotTitle || '热门推荐'}}</text>
-      <text class="more" bindtap="goToFeature" data-feature="hot">更多 ></text>
+      <text class="title">{{cardNovels[1].name}}</text>
+      <navigator url="/pages/book/list?type=editor&id={{cardNovels[1].settingId}}&name={{cardNovels[1].name}}" class="more">更多 ></navigator>
     </view>
 
     <view class="hot-books">
@@ -107,8 +106,8 @@
   <!-- 强力推荐 -->
   <view class="strong-recommend" wx:if="{{strongBooks.length>0}}">
     <view class="section-header">
-      <text class="title">强力推荐</text>
-      <text class="more" bindtap="goToFeature" data-feature="recommend">更多 ></text>
+      <text class="title">{{cardNovels[2].name}}</text>
+      <navigator url="/pages/book/list?type=editor&id={{cardNovels[2].settingId}}&name={{cardNovels[2].name}}" class="more">更多 ></navigator>
     </view>
 
     <view class="strong-books">

+ 3 - 2
pages/index/index.wxss

@@ -132,14 +132,15 @@ page {
 /* 主Banner */
 .main-banner {
   width: 100%;
-  height: 260rpx;
+  height: 240rpx;
   padding: 20rpx 30rpx;
   box-sizing: border-box;
+  margin-bottom: 50rpx;
 }
 
 .main-banner image {
   width: 100%;
-  height: 100%;
+  height: 240rpx;
   border-radius: 20rpx;
 }
 

+ 5 - 0
pages/mine/mine.js

@@ -5,5 +5,10 @@ Page({
   },
   onLoad: function(options) {
     
+  },
+  goToReadingHistory() {
+    wx.navigateTo({
+      url: '/pages/readingHistory/readingHistory'
+    })
   }
 })

+ 22 - 4
pages/mine/mine.wxml

@@ -1,9 +1,27 @@
 <!--pages/mine/mine.wxml-->
 <view class="container">
-  <view class="header">
-    <text class="title">个人中心</text>
+  <view class="user-section">
+    <image class="user-avatar" src="/assets/images/avatar-placeholder.jpg"></image>
+    <view class="user-info">
+      <text class="nickname">游客</text>
+      <text class="user-id">用户ID:243518916</text>
+    </view>
   </view>
-  <view class="content">
-    <text>我的页面内容</text>
+
+  <view class="menu-list">
+    <view class="menu-item" bindtap="goToReadingHistory">
+      <image class="menu-icon" src="/assets/images/tabs/tab-bookshelf_active.png"></image>
+      <text class="menu-text">阅读记录</text>
+      <view class="arrow"></view>
+    </view>
+  </view>
+
+  <view class="bottom-info">
+    <view class="terms">
+      <text class="terms-text">用户协议</text>
+      <text class="divider">|</text>
+      <text class="terms-text">隐私政策</text>
+    </view>
+    <text class="version-text">V 3.7.25</text>
   </view>
 </view>

Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
pages/mine/mine.wxss


+ 55 - 29
utils/request.js

@@ -117,37 +117,63 @@ export const requestAll = (requestUrl, body, method = 'POST', customHeaders = {}
     }
   }
 
-  return new Promise((resolve, reject) => {
-    wx.request({
-      url: url,
-      data: body,
-      method: method,
-      dataType: 'json',
-      header: {
-        ...customHeaders,
-        token: customHeaders.token || app.token || '',
-      },
-      success: function (res) {
-        if (res.data.code == 200) {
-          // 是否需要解密
-          console.log(url, '<------>', res.data.data);
-          if (typeof res.data.data === 'number') {
-            resolve(res.data.data);
-          } else {
-            resolve(res.data.data || []);
+  // 封装请求函数
+  const doRequest = () => {
+    return new Promise((resolve, reject) => {
+      wx.request({
+        url: url,
+        data: body,
+        method: method,
+        dataType: 'json',
+        header: {
+          ...customHeaders,
+          token: customHeaders.token || app.token || '',
+        },
+        success: function (res) {
+          if (res.data.code == 200) {
+            // 是否需要解密
+            console.log(url, '<------>', res.data.data);
+            if (typeof res.data.data === 'number') {
+              resolve(res.data.data);
+            } else {
+              resolve(res.data.data || []);
+            }
+          } 
+          else if(res.data.code==20001){
+            //令牌过期,重新登录
+            reject({code: 20001, msg: '令牌过期'});
           }
-        } else {
-          console.log('error:', res.data, url);
-          reject(res.data);
-        }
-      },
-      fail: function (res) {
-        wx.showToast({
-          title: '网络出错,请重试...',
-          icon: 'none',
-        });
-      },
+          else {
+            console.log('error:', res.data, url);
+            reject(res.data);
+          }
+        },
+        fail: function (res) {
+          wx.showToast({
+            title: '网络出错,请重试...',
+            icon: 'none',
+          });
+          reject(res);
+        },
+      });
     });
+  };
+
+  // 执行请求,如果token过期则重新登录后重试
+  return doRequest().catch(async error => {
+    if (error.code === 20001) {
+      // 重新登录
+      const app = getApp();
+      try {
+        await app.login();
+        // 重新执行请求
+        return doRequest();
+      } catch (loginError) {
+        console.error('重新登录失败:', loginError);
+        throw loginError;
+      }
+    }
+    throw error;
   });
 };
 

Неке датотеке нису приказане због велике количине промена