fzzj 9 месяцев назад
Родитель
Сommit
f274b34047

+ 59
- 8
RuoYi-App/App.vue Просмотреть файл

@@ -10,19 +10,67 @@ import { getToken } from '@/utils/auth'
10 10
 
11 11
 export default {
12 12
   onLaunch() {
13
-    // 检查登录状态
14
-    this.checkLogin()
15
-    
13
+    // 检查阅读进度
14
+    this.checkReadingProgress()  
16 15
     // 初始化主题(临时实现)
17 16
     this.initTheme()
17
+    // 检查登录状态
18
+    //this.checkLogin()
19
+    
20
+
21
+  },
22
+  onShow() {
23
+    // 检查云端阅读进度
24
+    if (this.$store.getters.token) {
25
+      this.syncReadingProgress()
26
+    }
18 27
   },
19 28
   methods: {
20
-    // 检查用户登录状态
21
-    checkLogin() {
22
-      if (!getToken()) {
23
-        this.$tab.reLaunch('/pages/login') 
29
+    // 检查阅读进度
30
+    checkReadingProgress() {
31
+      const chapter = uni.getStorageSync('readingProgress') || 1
32
+      
33
+      // 如果用户阅读超过5章但未登录,提示登录
34
+      if (chapter > 5 && !uni.getStorageSync('token')) {
35
+        uni.showModal({
36
+          title: '登录提示',
37
+          content: `您已阅读到第${chapter}章,登录后继续阅读`,
38
+          confirmText: '立即登录',
39
+          success: (res) => {
40
+            if (res.confirm) {
41
+              uni.navigateTo({
42
+                url: '/pages/login'
43
+              })
44
+            }
45
+          }
46
+        })
47
+		}
48
+	},
49
+  async syncReadingProgress() {
50
+    try {
51
+      const res = await this.$api.getReadingProgress()
52
+      const cloudChapter = res.data.chapter
53
+      const localChapter = uni.getStorageSync('readingProgress') || 1
54
+      
55
+      // 使用最新的阅读进度
56
+      if (cloudChapter > localChapter) {
57
+        uni.setStorageSync('readingProgress', cloudChapter)
58
+      } else if (localChapter > cloudChapter) {
59
+        await this.$api.saveReadingProgress(localChapter)
24 60
       }
25
-    },
61
+    } catch (err) {
62
+      console.error('同步阅读进度失败', err)
63
+    }
64
+  },
65
+    // 添加 checkLogin 方法
66
+    // checkLogin() {
67
+    //   // 这里写登录检查逻辑
68
+    //   // 示例:检查本地存储的登录状态
69
+    //   const isLoggedIn = uni.getStorageSync('isLogin');
70
+    //   if (!isLoggedIn) {
71
+    //     uni.navigateTo({ url: '/pages/login' });
72
+    //   }
73
+    // },
26 74
     
27 75
     // 临时主题初始化
28 76
     initTheme() {
@@ -77,4 +125,7 @@ export default {
77 125
 
78 126
 <style lang="scss">
79 127
 /* 保留原有样式 */
128
+uni-tabbar {
129
+  z-index: 999;
130
+}
80 131
 </style>

+ 51
- 0
RuoYi-App/components/AdBanner.vue Просмотреть файл

@@ -0,0 +1,51 @@
1
+<template>
2
+  <view class="ad-container">
3
+    <block v-for="(ad, index) in ads" :key="index">
4
+      <image 
5
+        :src="ad.imageUrl" 
6
+        mode="widthFix"
7
+        @click="handleAdClick(ad)"
8
+      />
9
+      <text v-if="ad.title" class="ad-title">{{ ad.title }}</text>
10
+    </block>
11
+  </view>
12
+</template>
13
+
14
+<script>
15
+export default {
16
+  props: {
17
+    ads: {
18
+      type: Array,
19
+      default: () => []
20
+    }
21
+  },
22
+  methods: {
23
+    handleAdClick(ad) {
24
+      // 广告点击统计
25
+      this.$http.post('/java-api/ad/click', { adId: ad.id });
26
+      
27
+      // 处理跳转
28
+      if (ad.linkType === 'NOVEL') {
29
+        uni.navigateTo({ url: `/pages/novel/detail?id=${ad.linkTarget}` });
30
+      } else if (ad.linkType === 'URL') {
31
+        uni.navigateTo({ url: `/pages/webview?url=${encodeURIComponent(ad.linkTarget)}` });
32
+      }
33
+    }
34
+  }
35
+}
36
+</script>
37
+
38
+<style>
39
+.ad-container {
40
+  padding: 10px;
41
+  background: #f5f5f5;
42
+  margin: 15px 0;
43
+  border-radius: 8px;
44
+}
45
+.ad-title {
46
+  display: block;
47
+  text-align: center;
48
+  font-size: 14px;
49
+  color: #666;
50
+}
51
+</style>

+ 111
- 0
RuoYi-App/components/login-prompt.vue Просмотреть файл

@@ -0,0 +1,111 @@
1
+<template>
2
+  <view class="login-prompt">
3
+    <view class="prompt-content">
4
+      <text class="title">登录后继续阅读</text>
5
+      <text class="tip">您已阅读{{ freeChaptersRead }}章免费内容</text>
6
+      
7
+      <view class="benefits">
8
+        <view class="benefit-item">
9
+          <uni-icons type="checkmark" size="18" color="#2ecc71" />
10
+          <text>解锁全部精彩章节</text>
11
+        </view>
12
+        <view class="benefit-item">
13
+          <uni-icons type="checkmark" size="18" color="#2ecc71" />
14
+          <text>加入书架保存阅读进度</text>
15
+        </view>
16
+        <view class="benefit-item">
17
+          <uni-icons type="checkmark" size="18" color="#2ecc71" />
18
+          <text>每日领取阅读金币</text>
19
+        </view>
20
+      </view>
21
+      
22
+      <button class="login-btn" @click="$emit('login')">立即登录/注册</button>
23
+      <button class="continue-btn" @click="$emit('continue')">
24
+        继续阅读 (还可阅读{{ maxFreeChapters - freeChaptersRead }}章)
25
+      </button>
26
+    </view>
27
+  </view>
28
+</template>
29
+
30
+<script>
31
+export default {
32
+  props: {
33
+    freeChaptersRead: {
34
+      type: Number,
35
+      default: 0
36
+    },
37
+    maxFreeChapters: {
38
+      type: Number,
39
+      default: 5
40
+    }
41
+  }
42
+}
43
+</script>
44
+
45
+<style lang="scss">
46
+.login-prompt {
47
+  position: fixed;
48
+  top: 0;
49
+  left: 0;
50
+  right: 0;
51
+  bottom: 0;
52
+  background: rgba(0, 0, 0, 0.5);
53
+  display: flex;
54
+  align-items: center;
55
+  justify-content: center;
56
+  z-index: 1000;
57
+}
58
+
59
+.prompt-content {
60
+  width: 80%;
61
+  background: white;
62
+  border-radius: 20rpx;
63
+  padding: 40rpx;
64
+  text-align: center;
65
+  
66
+  .title {
67
+    font-size: 36rpx;
68
+    font-weight: bold;
69
+    display: block;
70
+    margin-bottom: 15rpx;
71
+    color: #e74c3c;
72
+  }
73
+  
74
+  .tip {
75
+    font-size: 28rpx;
76
+    color: #666;
77
+    display: block;
78
+    margin-bottom: 30rpx;
79
+  }
80
+}
81
+
82
+.benefits {
83
+  text-align: left;
84
+  margin-bottom: 40rpx;
85
+  
86
+  .benefit-item {
87
+    display: flex;
88
+    align-items: center;
89
+    margin-bottom: 20rpx;
90
+    font-size: 28rpx;
91
+    color: #333;
92
+    
93
+    text {
94
+      margin-left: 15rpx;
95
+    }
96
+  }
97
+}
98
+
99
+.login-btn {
100
+  background-color: #e74c3c;
101
+  color: white;
102
+  border-radius: 50rpx;
103
+  margin-bottom: 20rpx;
104
+}
105
+
106
+.continue-btn {
107
+  background-color: #3498db;
108
+  color: white;
109
+  border-radius: 50rpx;
110
+}
111
+</style>

+ 3
- 0
RuoYi-App/main.js Просмотреть файл

@@ -4,7 +4,10 @@ import store from './store' // store
4 4
 import plugins from './plugins' // plugins
5 5
 import './permission' // permission
6 6
 import { getDicts } from "@/api/system/dict/data"
7
+import http from '@/utils/request' // 导入HTTP工具
7 8
 
9
+// 注册HTTP工具
10
+Vue.use(http)
8 11
 Vue.use(plugins)
9 12
 
10 13
 Vue.config.productionTip = false

+ 62
- 92
RuoYi-App/pages.json Просмотреть файл

@@ -1,112 +1,82 @@
1 1
 {
2 2
   "pages": [
3
-	     {
4
-	        "path": "pages/index/index",  // 确保这是您的首页文件路径
3
+    {
4
+      "path": "pages/index/index",
5
+      "style": {
6
+        "navigationBarTitleText": "首页",
7
+        "enablePullDownRefresh": false
8
+      }
9
+    },
10
+	      {
11
+	        "path": "pages/novel/list",
5 12
 	        "style": {
6
-	          "navigationBarTitleText": "首页",
7
-	          "enablePullDownRefresh": false
8
-	        }
13
+	                "navigationBarTitleText": "小说",
14
+	                "enablePullDownRefresh": false
15
+	              }
9 16
 	      },
10
-	  
11
-	  
12
-	  {
13
-    "path": "pages/login",
14
-    "style": {
15
-      "navigationBarTitleText": "登录"
16
-    }
17
-  }, {
17
+    {
18
+      "path": "pages/bookshelf/index",
19
+      "style": {
20
+        "navigationBarTitleText": "我的书架"
21
+      }
22
+    },
23
+    {
24
+      "path": "pages/me/index",
25
+      "style": {
26
+        "navigationBarTitleText": "个人中心"
27
+      }
28
+    },
29
+    {
30
+      "path": "pages/login",
31
+      "style": {
32
+        "navigationBarTitleText": "登录"
33
+      }
34
+    }, 
35
+    {
18 36
     "path": "pages/register",
19 37
     "style": {
20 38
       "navigationBarTitleText": "注册"
21
-    }
22
-  }, {
23
-    "path": "pages/index",
24
-    "style": {
25
-      "navigationBarTitleText": "若依移动端框架",
26
-      "navigationStyle": "custom"
27
-    }
28
-  }, {
29
-    "path": "pages/work/index",
30
-    "style": {
31
-      "navigationBarTitleText": "工作台"
32
-    }
33
-  }, {
34
-    "path": "pages/mine/index",
35
-    "style": {
36
-      "navigationBarTitleText": "我的"
37
-    }
38
-  }, {
39
-    "path": "pages/mine/avatar/index",
40
-    "style": {
41
-      "navigationBarTitleText": "修改头像"
42
-    }
43
-  }, {
44
-    "path": "pages/mine/info/index",
45
-    "style": {
46
-      "navigationBarTitleText": "个人信息"
47
-    }
48
-  }, {
49
-    "path": "pages/mine/info/edit",
50
-    "style": {
51
-      "navigationBarTitleText": "编辑资料"
52
-    }
53
-  }, {
54
-    "path": "pages/mine/pwd/index",
55
-    "style": {
56
-      "navigationBarTitleText": "修改密码"
57
-    }
58
-  }, {
59
-    "path": "pages/mine/setting/index",
60
-    "style": {
61
-      "navigationBarTitleText": "应用设置"
62
-    }
63
-  }, {
64
-    "path": "pages/mine/help/index",
65
-    "style": {
66
-      "navigationBarTitleText": "常见问题"
67
-    }
68
-  }, {
69
-    "path": "pages/mine/about/index",
70
-    "style": {
71
-      "navigationBarTitleText": "关于我们"
72
-    }
73
-  }, {
74
-    "path": "pages/common/webview/index",
75
-    "style": {
76
-      "navigationBarTitleText": "浏览网页"
77
-    }
78
-  }, {
79
-    "path": "pages/common/textview/index",
80
-    "style": {
81
-      "navigationBarTitleText": "浏览文本"
82
-    }
83
-  }],
39
+		}
40
+	}
41
+  ],
84 42
   "tabBar": {
85 43
     "color": "#7A7E83",
86
-    "selectedColor": "#3cc51f",
87
-    "borderStyle": "black",
44
+    "selectedColor": "#2a5caa", // 使用主色调
45
+    "borderStyle": "white",
88 46
     "backgroundColor": "#ffffff",
89
-    "list": [{
90
-        "pagePath": "pages/index/index",  // 首页tab",
91
-        "iconPath": "static/tabbar/home.png",
92
-        "selectedIconPath": "static/tabbar/home_active.png",
47
+    "height": "50px",
48
+    "list": [
49
+      {
50
+        "pagePath": "pages/index/index",
51
+        "iconPath": "static/images/tabbar/home.png",
52
+        "selectedIconPath": "static/tabbar/home_.png",
93 53
         "text": "首页"
94
-      },       {
95
-        "pagePath": "pages/bookshelf/index",  // 书架页
96
-        "iconPath": "static/tabbar/bookshelf.png",
97
-        "selectedIconPath": "static/tabbar/bookshelf_active.png",
54
+      },
55
+	        {
56
+	          "pagePath": "pages/novel/list",
57
+	          "text": "小说"
58
+	        },
59
+      {
60
+        "pagePath": "pages/bookshelf/index",
61
+        "iconPath": "static/images/tabbar/work.png",
62
+        "selectedIconPath": "static/images/tabbar/work_.png",
98 63
         "text": "书架"
99
-      },       {
100
-        "pagePath": "pages/me/index",  // 我的页面
101
-        "iconPath": "static/tabbar/me.png",
102
-        "selectedIconPath": "static/tabbar/me_active.png",
64
+      },
65
+      {
66
+        "pagePath": "pages/me/index",
67
+        "iconPath": "static/images/tabbar/mine.png",
68
+        "selectedIconPath": "static/images/tabbar/mine_.png",
103 69
         "text": "我的"
104 70
       }
105 71
     ]
106 72
   },
107 73
   "globalStyle": {
108 74
     "navigationBarTextStyle": "black",
109
-    "navigationBarTitleText": "RuoYi",
110
-    "navigationBarBackgroundColor": "#FFFFFF"
75
+    "navigationBarTitleText": "哎呀免费小说",
76
+    "navigationBarBackgroundColor": "#FFFFFF",
77
+    "backgroundColor": "#F5F5F5",
78
+    "app-plus": {
79
+      "titleNView": false // 禁用原生导航栏
80
+    }
111 81
   }
112 82
 }

+ 180
- 0
RuoYi-App/pages/bookshelf/index.vue Просмотреть файл

@@ -0,0 +1,180 @@
1
+<template>
2
+  <view class="bookshelf">
3
+    <!-- 顶部导航 -->
4
+    <view class="header">
5
+      <text class="title">我的书架</text>
6
+      <view class="actions">
7
+        <uni-icons type="plus" size="24" color="#333" @click="addBook"></uni-icons>
8
+        <uni-icons type="search" size="24" color="#333" @click="searchBooks"></uni-icons>
9
+      </view>
10
+    </view>
11
+    
12
+    <!-- 书架内容 -->
13
+    <view v-if="books.length > 0" class="book-grid">
14
+      <view v-for="book in books" :key="book.id" class="book-item" @click="readBook(book)">
15
+        <image :src="book.cover" class="cover" />
16
+        <text class="book-title">{{ book.title }}</text>
17
+        <text class="progress">阅读到: {{ book.progress }}%</text>
18
+      </view>
19
+    </view>
20
+    
21
+    <!-- 空书架提示 -->
22
+    <view v-else class="empty">
23
+      <image src="/static/empty-bookshelf.png" class="empty-img" />
24
+      <text class="empty-text">书架空空如也</text>
25
+      <button class="btn-find" @click="goToHome">去发现好书</button>
26
+    </view>
27
+  </view>
28
+</template>
29
+
30
+<script>
31
+export default {
32
+  data() {
33
+    return {
34
+      books: [
35
+        // 从后端获取实际数据
36
+        {
37
+          id: 101,
38
+          title: "万古神帝",
39
+          cover: "/static/book1.jpg",
40
+          progress: 65,
41
+          lastChapter: 125
42
+        }
43
+      ]
44
+    }
45
+  },
46
+  onLoad() {
47
+    this.loadBookshelf()
48
+  },
49
+  methods: {
50
+    async loadBookshelf() {
51
+      try {
52
+        // 调用后端API获取书架数据
53
+        const res = await this.$http.get('/bookshelf', {
54
+          headers: {
55
+            'Authorization': `Bearer ${uni.getStorageSync('token')}`
56
+          }
57
+        })
58
+        
59
+        if (res.data.code === 0) {
60
+          this.books = res.data.data
61
+        }
62
+      } catch (error) {
63
+        uni.showToast({
64
+          title: '加载书架失败',
65
+          icon: 'none'
66
+        })
67
+      }
68
+    },
69
+    
70
+    readBook(book) {
71
+      uni.navigateTo({
72
+        url: `/pages/novel/reader?novelId=${book.id}&chapterId=${book.lastChapter}`
73
+      })
74
+    },
75
+    
76
+    goToHome() {
77
+      uni.switchTab({
78
+        url: '/pages/index/index'
79
+      })
80
+    },
81
+    
82
+    addBook() {
83
+      uni.navigateTo({
84
+        url: '/pages/search/index'
85
+      })
86
+    },
87
+    
88
+    searchBooks() {
89
+      uni.navigateTo({
90
+        url: '/pages/search/index'
91
+      })
92
+    }
93
+  }
94
+}
95
+</script>
96
+
97
+<style scoped>
98
+.bookshelf {
99
+  padding: 20rpx;
100
+}
101
+
102
+.header {
103
+  display: flex;
104
+  justify-content: space-between;
105
+  align-items: center;
106
+  padding: 20rpx 0;
107
+  border-bottom: 1px solid #eee;
108
+}
109
+
110
+.title {
111
+  font-size: 36rpx;
112
+  font-weight: bold;
113
+}
114
+
115
+.actions {
116
+  display: flex;
117
+  gap: 30rpx;
118
+}
119
+
120
+.book-grid {
121
+  display: grid;
122
+  grid-template-columns: repeat(3, 1fr);
123
+  gap: 30rpx;
124
+  margin-top: 30rpx;
125
+}
126
+
127
+.book-item {
128
+  display: flex;
129
+  flex-direction: column;
130
+}
131
+
132
+.cover {
133
+  width: 200rpx;
134
+  height: 280rpx;
135
+  border-radius: 8rpx;
136
+  box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1);
137
+}
138
+
139
+.book-title {
140
+  font-size: 28rpx;
141
+  margin-top: 10rpx;
142
+  overflow: hidden;
143
+  text-overflow: ellipsis;
144
+  display: -webkit-box;
145
+  -webkit-line-clamp: 1;
146
+  -webkit-box-orient: vertical;
147
+}
148
+
149
+.progress {
150
+  font-size: 24rpx;
151
+  color: #666;
152
+}
153
+
154
+.empty {
155
+  display: flex;
156
+  flex-direction: column;
157
+  align-items: center;
158
+  justify-content: center;
159
+  height: 60vh;
160
+}
161
+
162
+.empty-img {
163
+  width: 300rpx;
164
+  height: 300rpx;
165
+  opacity: 0.6;
166
+}
167
+
168
+.empty-text {
169
+  font-size: 32rpx;
170
+  color: #999;
171
+  margin: 30rpx 0;
172
+}
173
+
174
+.btn-find {
175
+  background-color: #2a5caa;
176
+  color: white;
177
+  width: 60%;
178
+  border-radius: 50rpx;
179
+}
180
+</style>

+ 511
- 22
RuoYi-App/pages/index/index.vue Просмотреть файл

@@ -1,44 +1,533 @@
1 1
 <template>
2
-  <view class="container">
3
-    <h1>欢迎使用哎呀小说</h1>
4
-    <p>基础框架已加载!</p>
5
-    <p>当前时间: {{ new Date().toLocaleString() }}</p>
2
+  <view class="novel-home">
3
+    <!-- 顶部导航栏 -->
4
+    <view class="header">
5
+      <image src="/static/logo.png" class="logo" />
6
+      <view class="search-box" @click="goToSearch">
7
+        <uni-icons type="search" size="18" color="#999" />
8
+        <text class="placeholder">搜索书名或作者</text>
9
+      </view>
10
+      <view class="user-icon" @click="goToUserCenter">
11
+        <uni-icons type="person" size="24" color="#333" />
12
+      </view>
13
+    </view>
14
+    
15
+    <!-- 轮播图 -->
16
+    <swiper class="banner" :autoplay="true" :interval="3000" circular>
17
+      <swiper-item v-for="(item, index) in banners" :key="index">
18
+        <image :src="item.image" mode="aspectFill" @click="readNovel(item.novelId)" />
19
+      </swiper-item>
20
+    </swiper>
21
+    
22
+    <!-- 分类导航 -->
23
+    <view class="category-nav">
24
+      <view v-for="category in categories" :key="category.id" class="nav-item">
25
+        <image :src="category.icon" class="nav-icon" />
26
+        <text>{{ category.name }}</text>
27
+      </view>
28
+    </view>
29
+    
30
+    <!-- 推荐书单 -->
31
+    <view class="section">
32
+      <view class="section-header">
33
+        <text class="section-title">编辑推荐</text>
34
+        <text class="more" @click="goToBookList">更多 ></text>
35
+      </view>
36
+      <scroll-view scroll-x class="book-list">
37
+        <view v-for="book in recommendedBooks" :key="book.id" class="book-item" @click="readNovel(book.id, 1)">
38
+          <image :src="book.cover" class="book-cover" />
39
+          <text class="book-title">{{ book.title }}</text>
40
+          <text class="book-author">{{ book.author }}</text>
41
+        </view>
42
+      </scroll-view>
43
+    </view>
44
+    
45
+    <!-- 热门连载 -->
46
+    <view class="section">
47
+      <view class="section-header">
48
+        <text class="section-title">热门连载</text>
49
+      </view>
50
+      <view v-for="book in serialBooks" :key="book.id" class="book-row" @click="readNovel(book.id, 1)">
51
+        <image :src="book.cover" class="row-cover" />
52
+        <view class="book-info">
53
+          <text class="row-title">{{ book.title }}</text>
54
+          <text class="row-author">{{ book.author }}</text>
55
+          <text class="row-desc">{{ book.description }}</text>
56
+          <view class="row-tags">
57
+            <text v-for="tag in book.tags" :key="tag" class="tag">{{ tag }}</text>
58
+          </view>
59
+        </view>
60
+      </view>
61
+    </view>
62
+    
63
+    <!-- 阅读登录提示组件 -->
64
+    <login-prompt v-if="showLoginPrompt" @login="goToLogin" @continue="continueReading" />
6 65
   </view>
7 66
 </template>
8 67
 <script>
68
+	import novelService from '@/services/novelService'
69
+	
9 70
 export default {
71
+  data() {
72
+    return {
73
+      banners: [],
74
+      categories: [],
75
+      recommendedBooks: [],
76
+      serialBooks: [],
77
+      currentReading: null,
78
+      showLoginPrompt: false,
79
+      maxFreeChapters: 5,
80
+      freeChaptersRead: 0,
81
+      currentChapter: 1, // 当前阅读章节
82
+      maxFreeChapters: 5, // 最大免费章节数
83
+	      readingTime: 0, // 阅读时长(秒)
84
+	      timer: null
85
+    }
86
+  },
87
+    async onLoad() {
88
+      await this.loadHomeData()
89
+    },
10 90
   mounted() {
91
+    // 开始阅读计时
92
+    this.timer = setInterval(() => {
93
+      this.readingTime++
94
+    }, 1000)
95
+  },
96
+  beforeDestroy() {
97
+    // 清除计时器
98
+    clearInterval(this.timer)
99
+  },
100
+  computed: {
101
+    // 计算显示哪些章节
102
+    visibleChapters() {
103
+      return this.chapters.filter(chap => chap.id <= this.currentChapter)
104
+    },
105
+    // 是否显示登录提示
106
+    showLoginPrompt() {
107
+      return this.currentChapter > this.maxFreeChapters && !this.$store.getters.token
108
+    },
109
+    // 剩余免费章节数
110
+    remainingFreeChapters() {
111
+      return this.maxFreeChapters - this.currentChapter
112
+    },
113
+    // 总章节数
114
+    totalChapters() {
115
+      return this.chapters.length
116
+    }
117
+  },
118
+  onLoad() {
119
+    // 从本地存储获取阅读进度
120
+    const progress = uni.getStorageSync('readingProgress') || {}
121
+    this.currentReading = progress.currentReading || null
122
+    this.freeChaptersRead = progress.freeChaptersRead || 0
123
+    
124
+    // 如果用户正在阅读小说,显示继续阅读提示
125
+    if (this.currentReading) {
126
+      setTimeout(() => {
127
+        uni.showToast({
128
+          title: `继续阅读《${this.currentReading.title}》`,
129
+          icon: 'none',
130
+          duration: 3000
131
+        })
132
+      }, 1000)
133
+    }
134
+  },
135
+  methods: {
136
+    async loadHomeData() {
137
+      uni.showLoading({ title: '加载中...' })
138
+      
139
+      try {
140
+        const homeData = await novelService.getHomeRecommend()
141
+        
142
+        this.banners = homeData.banners || []
143
+        this.categories = homeData.categories || []
144
+        this.recommendedBooks = homeData.recommended || []
145
+        this.serialBooks = homeData.serializing || []
146
+        
147
+      } catch (error) {
148
+        uni.showToast({
149
+          title: '加载失败',
150
+          icon: 'none'
151
+        })
152
+      } finally {
153
+        uni.hideLoading()
154
+      }
155
+    },
156
+    // 开始/继续阅读小说
157
+    readNovel(novelId, chapterId = 1) {
158
+      // 查找小说信息
159
+      const novel = this.findNovelById(novelId)
160
+      
161
+      // 检查是否需要登录提示
162
+      if (this.freeChaptersRead >= this.maxFreeChapters && !this.$store.getters.token) {
163
+        this.showLoginPrompt = true
164
+        this.currentReading = {
165
+          id: novelId,
166
+          title: novel.title,
167
+          chapterId
168
+        }
169
+        return
170
+      }
171
+      
172
+      // 记录阅读进度
173
+      this.recordReadingProgress(novelId, chapterId)
174
+      
175
+      // 跳转到阅读页
176
+      uni.navigateTo({
177
+        url: `/pages/novel/reader?novelId=${novelId}&chapterId=${chapterId}`
178
+      })
179
+    },
180
+    
181
+    // 继续阅读(临时允许)
182
+    continueReading() {
183
+      if (this.currentReading) {
184
+        // 增加已读章节计数
185
+        this.freeChaptersRead += 1
186
+        
187
+        // 保存进度
188
+        this.recordReadingProgress(this.currentReading.id, this.currentReading.chapterId)
189
+        
190
+        // 跳转到阅读页
191
+        uni.navigateTo({
192
+          url: `/pages/novel/reader?novelId=${this.currentReading.id}&chapterId=${this.currentReading.chapterId}`
193
+        })
194
+        
195
+        this.showLoginPrompt = false
196
+      }
197
+    },
198
+    
199
+    // 记录阅读进度
200
+    recordReadingProgress(novelId, chapterId) {
201
+      const novel = this.findNovelById(novelId)
202
+      
203
+      // 更新当前阅读
204
+      this.currentReading = {
205
+        id: novelId,
206
+        title: novel.title,
207
+        chapterId
208
+      }
209
+      
210
+      // 更新已读免费章节数
211
+      if (!this.$store.getters.token) {
212
+        this.freeChaptersRead += 1
213
+      }
214
+      
215
+      // 保存到本地存储
216
+      uni.setStorageSync('readingProgress', {
217
+        currentReading: this.currentReading,
218
+        freeChaptersRead: this.freeChaptersRead
219
+      })
220
+    },
221
+    
222
+    // 根据ID查找小说
223
+    findNovelById(id) {
224
+      // 在实际应用中,这里应该调用API获取小说详情
225
+      // 这里简化为在所有书籍中查找
226
+      const allBooks = [...this.recommendedBooks, ...this.serialBooks]
227
+      return allBooks.find(book => book.id === id) || { title: '未知小说' }
228
+    },
229
+    
230
+    goToSearch() {
231
+      uni.navigateTo({ url: '/pages/search/index' })
232
+    },
233
+    
234
+    goToUserCenter() {
235
+      if (this.$store.getters.token) {
236
+        uni.navigateTo({ url: '/pages/user/index' })
237
+      } else {
238
+        uni.navigateTo({ url: '/pages/login' })
239
+      }
240
+    },
241
+    
242
+    goToBookList() {
243
+      uni.navigateTo({ url: '/pages/book/list' })
244
+    },
245
+    
246
+    goToLogin() {
247
+      uni.navigateTo({ url: '/pages/login' })
248
+    },
249
+    // 下一章
250
+    nextChapter() {
251
+      if (this.currentChapter < this.totalChapters) {
252
+        this.currentChapter++
253
+        
254
+        // 阅读到第五章时提示
255
+        if (this.currentChapter === this.maxFreeChapters) {
256
+          uni.showToast({
257
+            title: '免费章节已读完,登录后继续',
258
+            icon: 'none',
259
+            duration: 3000
260
+          })
261
+        }
262
+      }
263
+    },
264
+      // 根据阅读时长奖励金币
265
+      rewardReadingTime() {
266
+        const minutes = Math.floor(this.readingTime / 60)
267
+        if (minutes > 0 && minutes % 5 === 0) {
268
+          const coins = minutes / 5
269
+          this.$store.commit('addCoins', coins)
270
+          uni.showToast({ title: `阅读奖励: ${coins}金币` })
271
+        }
272
+      },
273
+    // 跳转登录页
274
+    goToLogin() {
275
+      uni.navigateTo({
276
+        url: '/pages/login'
277
+      })
278
+    },
279
+    
280
+    // 继续阅读(临时允许阅读剩余免费章节)
281
+    continueReading() {
282
+      if (this.remainingFreeChapters > 0) {
283
+        this.nextChapter()
284
+      }
285
+    }
286
+  },
287
+
288
+  onUnload() {
289
+    // 保存阅读进度
290
+    uni.setStorageSync('readingProgress', this.currentChapter)
11 291
     console.log('首页已加载')
292
+  },
293
+  unlockChapter() {
294
+    if (this.currentChapter > this.maxFreeChapters) {
295
+      if (this.$store.getters.vipLevel > 0) {
296
+        // VIP用户直接解锁
297
+        return true
298
+      } else if (this.$store.getters.coins > 10) {
299
+        // 消耗金币解锁
300
+        this.$store.commit('deductCoins', 10)
301
+        return true
302
+      } else {
303
+        // 提示获取金币方式
304
+        uni.showModal({
305
+          title: '解锁章节',
306
+          content: '观看广告可获取金币解锁本章节',
307
+          confirmText: '观看广告',
308
+          success: () => {
309
+            this.watchAdToUnlock()
310
+          }
311
+        })
312
+        return false
313
+      }
314
+    }
315
+    return true
316
+  },
317
+  
318
+  watchAdToUnlock() {
319
+    // 实现广告观看逻辑
320
+    // 观看成功后增加金币
321
+    this.$store.commit('addCoins', 5)
322
+    uni.showToast({ title: '获得5金币' })
12 323
   }
13 324
 }
14 325
 </script>
15
-<style scoped>
16
-	  page {
17
-	    height: 100%;
18
-	    background-color: #fff;
19
-	  }
20
-  .content {
326
+
327
+</script>
328
+
329
+<style lang="scss">
330
+.novel-home {
331
+  padding: 20rpx;
332
+  background-color: #f5f5f5;
333
+  min-height: 100vh;
334
+  padding-bottom: 100rpx;
335
+}
336
+
337
+.header {
338
+  display: flex;
339
+  align-items: center;
340
+  padding: 20rpx;
341
+  background: white;
342
+  
343
+  .logo {
344
+    width: 120rpx;
345
+    height: 60rpx;
346
+    margin-right: 20rpx;
347
+  }
348
+  
349
+  .search-box {
350
+    flex: 1;
351
+    background: #f0f0f0;
352
+    border-radius: 30rpx;
353
+    padding: 15rpx 25rpx;
354
+    display: flex;
355
+    align-items: center;
356
+    
357
+    .placeholder {
358
+      color: #999;
359
+      font-size: 28rpx;
360
+      margin-left: 10rpx;
361
+    }
362
+  }
363
+  
364
+  .user-icon {
365
+    width: 60rpx;
366
+    height: 60rpx;
21 367
     display: flex;
22
-    flex-direction: column;
23 368
     align-items: center;
24 369
     justify-content: center;
25
-    height: 100vh; /* 确保全屏高度 */
370
+    margin-left: 20rpx;
26 371
   }
372
+}
27 373
 
28
-  .logo {
29
-    height: 200rpx;
30
-    width: 200rpx;
31
-    margin-bottom: 50rpx;
374
+.banner {
375
+  height: 300rpx;
376
+  margin: 20rpx 0;
377
+  border-radius: 16rpx;
378
+  overflow: hidden;
379
+  
380
+  image {
381
+    width: 100%;
382
+    height: 100%;
32 383
   }
384
+}
33 385
 
34
-  .text-area {
386
+.category-nav {
387
+  display: flex;
388
+  justify-content: space-around;
389
+  background: white;
390
+  border-radius: 16rpx;
391
+  padding: 30rpx 0;
392
+  margin-bottom: 30rpx;
393
+  
394
+  .nav-item {
35 395
     display: flex;
36
-    justify-content: center;
396
+    flex-direction: column;
397
+    align-items: center;
398
+    
399
+    .nav-icon {
400
+      width: 80rpx;
401
+      height: 80rpx;
402
+      margin-bottom: 15rpx;
403
+    }
404
+    
405
+    text {
406
+      font-size: 24rpx;
407
+      color: #666;
408
+    }
37 409
   }
410
+}
411
+
412
+.section {
413
+  background: white;
414
+  border-radius: 16rpx;
415
+  padding: 25rpx;
416
+  margin-bottom: 30rpx;
417
+  
418
+  .section-header {
419
+    display: flex;
420
+    justify-content: space-between;
421
+    align-items: center;
422
+    margin-bottom: 25rpx;
423
+    
424
+    .section-title {
425
+      font-size: 32rpx;
426
+      font-weight: bold;
427
+      color: #333;
428
+    }
429
+    
430
+    .more {
431
+      font-size: 26rpx;
432
+      color: #999;
433
+    }
434
+  }
435
+}
436
+
437
+.book-list {
438
+  white-space: nowrap;
439
+  
440
+  .book-item {
441
+    display: inline-block;
442
+    width: 180rpx;
443
+    margin-right: 25rpx;
444
+    vertical-align: top;
445
+    
446
+    .book-cover {
447
+      width: 180rpx;
448
+      height: 240rpx;
449
+      border-radius: 8rpx;
450
+    }
451
+    
452
+    .book-title {
453
+      display: block;
454
+      font-size: 26rpx;
455
+      font-weight: bold;
456
+      margin-top: 15rpx;
457
+      white-space: nowrap;
458
+      overflow: hidden;
459
+      text-overflow: ellipsis;
460
+    }
461
+    
462
+    .book-author {
463
+      display: block;
464
+      font-size: 24rpx;
465
+      color: #999;
466
+      white-space: nowrap;
467
+      overflow: hidden;
468
+      text-overflow: ellipsis;
469
+    }
470
+  }
471
+}
38 472
 
39
-  .title {
40
-    font-size: 36rpx;
41
-    color: #333; /* 修改为深色确保可见 */
42
-    font-weight: bold;
473
+.book-row {
474
+  display: flex;
475
+  padding: 25rpx 0;
476
+  border-bottom: 1rpx solid #eee;
477
+  
478
+  &:last-child {
479
+    border-bottom: none;
43 480
   }
481
+  
482
+  .row-cover {
483
+    width: 160rpx;
484
+    height: 210rpx;
485
+    border-radius: 8rpx;
486
+    margin-right: 25rpx;
487
+  }
488
+  
489
+  .book-info {
490
+    flex: 1;
491
+    display: flex;
492
+    flex-direction: column;
493
+    
494
+    .row-title {
495
+      font-size: 30rpx;
496
+      font-weight: bold;
497
+      margin-bottom: 10rpx;
498
+    }
499
+    
500
+    .row-author {
501
+      font-size: 26rpx;
502
+      color: #666;
503
+      margin-bottom: 15rpx;
504
+    }
505
+    
506
+    .row-desc {
507
+      font-size: 26rpx;
508
+      color: #666;
509
+      display: -webkit-box;
510
+      -webkit-box-orient: vertical;
511
+      -webkit-line-clamp: 2;
512
+      overflow: hidden;
513
+      margin-bottom: 15rpx;
514
+    }
515
+  }
516
+  
517
+  .row-tags {
518
+    display: flex;
519
+    flex-wrap: wrap;
520
+    
521
+    .tag {
522
+      font-size: 22rpx;
523
+      color: #e74c3c;
524
+      border: 1rpx solid #e74c3c;
525
+      border-radius: 20rpx;
526
+      padding: 5rpx 15rpx;
527
+      margin-right: 15rpx;
528
+      margin-bottom: 10rpx;
529
+    }
530
+  }
531
+}
44 532
 </style>
533
+

+ 28
- 2
RuoYi-App/pages/login.vue Просмотреть файл

@@ -2,7 +2,7 @@
2 2
   <view class="normal-login-container">
3 3
     <view class="logo-content align-center justify-center flex">
4 4
 <!--      <image :src="globalConfig.appInfo.logo" class="logo" /> -->
5
-      <text class="title">若依移动端登录</text>
5
+      <text class="title">哎呀免费小说登录</text>
6 6
     </view>
7 7
     <view class="login-form-content">
8 8
       <view class="input-item flex align-center">
@@ -33,7 +33,14 @@
33 33
         <text @click="handlePrivacy" class="text-blue">《隐私协议》</text>
34 34
       </view>
35 35
     </view>
36
-     
36
+    <view class="reading-tips">
37
+      <text class="tip-text">登录后即可:</text>
38
+      <view class="benefits">
39
+        <text>✓ 继续阅读精彩章节</text>
40
+        <text>✓ 加入书架保存进度</text>
41
+        <text>✓ 获得每日阅读奖励</text>
42
+      </view>
43
+    </view>
37 44
   </view>
38 45
 </template>
39 46
 
@@ -135,7 +142,26 @@
135 142
   page {
136 143
     background-color: #ffffff;
137 144
   }
145
+.reading-tips {
146
+  margin-top: 40rpx;
147
+  padding: 20rpx;
148
+  background-color: #f9f9f9;
149
+  border-radius: 10rpx;
150
+}
151
+
152
+.tip-text {
153
+  font-size: 32rpx;
154
+  font-weight: bold;
155
+  display: block;
156
+  margin-bottom: 20rpx;
157
+}
138 158
 
159
+.benefits text {
160
+  display: block;
161
+  font-size: 28rpx;
162
+  margin: 10rpx 0;
163
+  color: #2c3e50;
164
+}
139 165
   .normal-login-container {
140 166
     width: 100%;
141 167
 

RuoYi-App/pages/mine/about/index.vue → RuoYi-App/pages/me/about/index.vue Просмотреть файл


RuoYi-App/pages/mine/avatar/index.vue → RuoYi-App/pages/me/avatar/index.vue Просмотреть файл


RuoYi-App/pages/mine/help/index.vue → RuoYi-App/pages/me/help/index.vue Просмотреть файл


+ 249
- 0
RuoYi-App/pages/me/index.vue Просмотреть файл

@@ -0,0 +1,249 @@
1
+<template>
2
+  <view class="user-center">
3
+    <!-- 用户信息 -->
4
+    <view class="user-info">
5
+      <image :src="user.avatar || '/static/default-avatar.png'" class="avatar" />
6
+      <view class="info">
7
+        <text class="name">{{ user.nickname || '未登录用户' }}</text>
8
+        <text v-if="user.vipLevel" class="vip">VIP{{ user.vipLevel }}会员</text>
9
+        <text v-else class="vip">普通用户</text>
10
+      </view>
11
+      <button v-if="!isLogin" class="login-btn" @click="goToLogin">登录/注册</button>
12
+      <uni-icons v-else type="gear" size="28" color="#333" @click="goToSettings"></uni-icons>
13
+    </view>
14
+    
15
+    <!-- VIP信息 -->
16
+    <view v-if="isLogin" class="vip-card">
17
+      <view class="vip-info">
18
+        <text class="vip-title">VIP会员</text>
19
+        <text v-if="user.vipExpire" class="vip-expire">有效期至: {{ user.vipExpire }}</text>
20
+        <text v-else class="vip-expire">立即开通会员</text>
21
+      </view>
22
+      <button class="btn-upgrade">立即升级</button>
23
+    </view>
24
+    
25
+    <!-- 功能列表 -->
26
+    <view class="menu-list">
27
+      <view class="menu-item" @click="goToMyBooks">
28
+        <uni-icons type="star" size="24" color="#2a5caa" />
29
+        <text>我的收藏</text>
30
+        <uni-icons type="right" size="18" color="#999" />
31
+      </view>
32
+      <view class="menu-item" @click="goToHistory">
33
+        <uni-icons type="time" size="24" color="#2a5caa" />
34
+        <text>阅读历史</text>
35
+        <uni-icons type="right" size="18" color="#999" />
36
+      </view>
37
+      <view class="menu-item" @click="goToWallet">
38
+        <uni-icons type="wallet" size="24" color="#2a5caa" />
39
+        <text>我的钱包</text>
40
+        <uni-icons type="right" size="18" color="#999" />
41
+      </view>
42
+      <view class="menu-item" @click="goToSettings">
43
+        <uni-icons type="gear" size="24" color="#2a5caa" />
44
+        <text>设置</text>
45
+        <uni-icons type="right" size="18" color="#999" />
46
+      </view>
47
+    </view>
48
+    
49
+    <!-- 退出登录 -->
50
+    <button v-if="isLogin" class="btn-logout" @click="logout">退出登录</button>
51
+  </view>
52
+</template>
53
+
54
+<script>
55
+export default {
56
+  data() {
57
+    return {
58
+      user: {
59
+        avatar: '',
60
+        nickname: '',
61
+        vipLevel: 0,
62
+        vipExpire: ''
63
+      },
64
+      isLogin: false
65
+    }
66
+  },
67
+  onShow() {
68
+    this.checkLoginStatus()
69
+    if (this.isLogin) {
70
+      this.loadUserInfo()
71
+    }
72
+  },
73
+  methods: {
74
+    checkLoginStatus() {
75
+      this.isLogin = !!uni.getStorageSync('token')
76
+    },
77
+    
78
+    async loadUserInfo() {
79
+      try {
80
+        const res = await this.$http.get('/user/info', {
81
+          headers: {
82
+            'Authorization': `Bearer ${uni.getStorageSync('token')}`
83
+          }
84
+        })
85
+        
86
+        if (res.data.code === 0) {
87
+          this.user = res.data.data
88
+        }
89
+      } catch (error) {
90
+        console.error('获取用户信息失败', error)
91
+      }
92
+    },
93
+    
94
+    goToLogin() {
95
+      uni.navigateTo({
96
+        url: '/pages/login'
97
+      })
98
+    },
99
+    
100
+    goToSettings() {
101
+      uni.navigateTo({
102
+        url: '/pages/settings/index'
103
+      })
104
+    },
105
+    
106
+    goToMyBooks() {
107
+      uni.navigateTo({
108
+        url: '/pages/collection/index'
109
+      })
110
+    },
111
+    
112
+    goToHistory() {
113
+      uni.navigateTo({
114
+        url: '/pages/history/index'
115
+      })
116
+    },
117
+    
118
+    goToWallet() {
119
+      uni.navigateTo({
120
+        url: '/pages/wallet/index'
121
+      })
122
+    },
123
+    
124
+    logout() {
125
+      uni.removeStorageSync('token')
126
+      uni.removeStorageSync('userInfo')
127
+      this.isLogin = false
128
+      this.user = {}
129
+      uni.showToast({
130
+        title: '已退出登录',
131
+        icon: 'success'
132
+      })
133
+    }
134
+  }
135
+}
136
+</script>
137
+
138
+<style scoped>
139
+.user-center {
140
+  padding: 20rpx;
141
+  background-color: #f5f5f5;
142
+  min-height: 100vh;
143
+}
144
+
145
+.user-info {
146
+  display: flex;
147
+  align-items: center;
148
+  padding: 30rpx;
149
+  background: white;
150
+  border-radius: 16rpx;
151
+  margin-bottom: 30rpx;
152
+}
153
+
154
+.avatar {
155
+  width: 120rpx;
156
+  height: 120rpx;
157
+  border-radius: 50%;
158
+  margin-right: 30rpx;
159
+}
160
+
161
+.info {
162
+  flex: 1;
163
+}
164
+
165
+.name {
166
+  font-size: 36rpx;
167
+  font-weight: bold;
168
+  display: block;
169
+  margin-bottom: 10rpx;
170
+}
171
+
172
+.vip {
173
+  font-size: 28rpx;
174
+  color: #e67e22;
175
+  background: #fef9e7;
176
+  padding: 5rpx 15rpx;
177
+  border-radius: 20rpx;
178
+}
179
+
180
+.login-btn {
181
+  background: #2a5caa;
182
+  color: white;
183
+  font-size: 28rpx;
184
+  padding: 10rpx 30rpx;
185
+  border-radius: 40rpx;
186
+}
187
+
188
+.vip-card {
189
+  background: linear-gradient(to right, #f6d365, #fda085);
190
+  border-radius: 16rpx;
191
+  padding: 30rpx;
192
+  display: flex;
193
+  justify-content: space-between;
194
+  align-items: center;
195
+  margin-bottom: 30rpx;
196
+  color: white;
197
+}
198
+
199
+.vip-title {
200
+  font-size: 36rpx;
201
+  font-weight: bold;
202
+  display: block;
203
+}
204
+
205
+.vip-expire {
206
+  font-size: 28rpx;
207
+  opacity: 0.9;
208
+}
209
+
210
+.btn-upgrade {
211
+  background: rgba(255,255,255,0.3);
212
+  color: white;
213
+  border: 1rpx solid white;
214
+  border-radius: 40rpx;
215
+  font-size: 28rpx;
216
+  padding: 10rpx 30rpx;
217
+}
218
+
219
+.menu-list {
220
+  background: white;
221
+  border-radius: 16rpx;
222
+  overflow: hidden;
223
+}
224
+
225
+.menu-item {
226
+  display: flex;
227
+  align-items: center;
228
+  padding: 30rpx;
229
+  border-bottom: 1rpx solid #f0f0f0;
230
+  
231
+  text {
232
+    flex: 1;
233
+    margin-left: 20rpx;
234
+    font-size: 32rpx;
235
+  }
236
+}
237
+
238
+.menu-item:last-child {
239
+  border-bottom: none;
240
+}
241
+
242
+.btn-logout {
243
+  margin-top: 50rpx;
244
+  background: white;
245
+  color: #e74c3c;
246
+  border: 1rpx solid #e74c3c;
247
+  border-radius: 40rpx;
248
+}
249
+</style>

RuoYi-App/pages/mine/info/edit.vue → RuoYi-App/pages/me/info/edit.vue Просмотреть файл


RuoYi-App/pages/mine/info/index.vue → RuoYi-App/pages/me/info/index.vue Просмотреть файл


RuoYi-App/pages/mine/pwd/index.vue → RuoYi-App/pages/me/pwd/index.vue Просмотреть файл


RuoYi-App/pages/mine/setting/index.vue → RuoYi-App/pages/me/setting/index.vue Просмотреть файл


+ 0
- 188
RuoYi-App/pages/mine/index.vue Просмотреть файл

@@ -1,188 +0,0 @@
1
-<template>
2
-  <view class="mine-container" :style="{height: `${windowHeight}px`}">
3
-    <!--顶部个人信息栏-->
4
-    <view class="header-section">
5
-      <view class="flex padding justify-between">
6
-        <view class="flex align-center">
7
-          <view v-if="!avatar" class="cu-avatar xl round bg-white">
8
-            <view class="iconfont icon-people text-gray icon"></view>
9
-          </view>
10
-          <image v-if="avatar" @click="handleToAvatar" :src="avatar" class="cu-avatar xl round" mode="widthFix">
11
-          </image>
12
-          <view v-if="!name" @click="handleToLogin" class="login-tip">
13
-            点击登录
14
-          </view>
15
-          <view v-if="name" @click="handleToInfo" class="user-info">
16
-            <view class="u_title">
17
-              用户名:{{ name }}
18
-            </view>
19
-          </view>
20
-        </view>
21
-        <view @click="handleToInfo" class="flex align-center">
22
-          <text>个人信息</text>
23
-          <view class="iconfont icon-right"></view>
24
-        </view>
25
-      </view>
26
-    </view>
27
-
28
-    <view class="content-section">
29
-      <view class="mine-actions grid col-4 text-center">
30
-        <view class="action-item" @click="handleJiaoLiuQun">
31
-          <view class="iconfont icon-friendfill text-pink icon"></view>
32
-          <text class="text">交流群</text>
33
-        </view>
34
-        <view class="action-item" @click="handleBuilding">
35
-          <view class="iconfont icon-service text-blue icon"></view>
36
-          <text class="text">在线客服</text>
37
-        </view>
38
-        <view class="action-item" @click="handleBuilding">
39
-          <view class="iconfont icon-community text-mauve icon"></view>
40
-          <text class="text">反馈社区</text>
41
-        </view>
42
-        <view class="action-item" @click="handleBuilding">
43
-          <view class="iconfont icon-dianzan text-green icon"></view>
44
-          <text class="text">点赞我们</text>
45
-        </view>
46
-      </view>
47
-
48
-      <view class="menu-list">
49
-        <view class="list-cell list-cell-arrow" @click="handleToEditInfo">
50
-          <view class="menu-item-box">
51
-            <view class="iconfont icon-user menu-icon"></view>
52
-            <view>编辑资料</view>
53
-          </view>
54
-        </view>
55
-        <view class="list-cell list-cell-arrow" @click="handleHelp">
56
-          <view class="menu-item-box">
57
-            <view class="iconfont icon-help menu-icon"></view>
58
-            <view>常见问题</view>
59
-          </view>
60
-        </view>
61
-        <view class="list-cell list-cell-arrow" @click="handleAbout">
62
-          <view class="menu-item-box">
63
-            <view class="iconfont icon-aixin menu-icon"></view>
64
-            <view>关于我们</view>
65
-          </view>
66
-        </view>
67
-        <view class="list-cell list-cell-arrow" @click="handleToSetting">
68
-          <view class="menu-item-box">
69
-            <view class="iconfont icon-setting menu-icon"></view>
70
-            <view>应用设置</view>
71
-          </view>
72
-        </view>
73
-      </view>
74
-
75
-    </view>
76
-  </view>
77
-</template>
78
-
79
-<script>
80
-  export default {
81
-    data() {
82
-      return {
83
-        name: this.$store.state.user.name
84
-      }
85
-    },
86
-    computed: {
87
-      avatar() {
88
-        return this.$store.state.user.avatar
89
-      },
90
-      windowHeight() {
91
-        return uni.getSystemInfoSync().windowHeight - 50
92
-      }
93
-    },
94
-    methods: {
95
-      handleToInfo() {
96
-        this.$tab.navigateTo('/pages/mine/info/index')
97
-      },
98
-      handleToEditInfo() {
99
-        this.$tab.navigateTo('/pages/mine/info/edit')
100
-      },
101
-      handleToSetting() {
102
-        this.$tab.navigateTo('/pages/mine/setting/index')
103
-      },
104
-      handleToLogin() {
105
-        this.$tab.reLaunch('/pages/login')
106
-      },
107
-      handleToAvatar() {
108
-        this.$tab.navigateTo('/pages/mine/avatar/index')
109
-      },
110
-      handleHelp() {
111
-        this.$tab.navigateTo('/pages/mine/help/index')
112
-      },
113
-      handleAbout() {
114
-        this.$tab.navigateTo('/pages/mine/about/index')
115
-      },
116
-      handleJiaoLiuQun() {
117
-        this.$modal.showToast('QQ群:①133713780(满)、②146013835(满)、③189091635')
118
-      },
119
-      handleBuilding() {
120
-        this.$modal.showToast('模块建设中~')
121
-      }
122
-    }
123
-  }
124
-</script>
125
-
126
-<style lang="scss" scoped>
127
-  page {
128
-    background-color: #f5f6f7;
129
-  }
130
-
131
-  .mine-container {
132
-    width: 100%;
133
-    height: 100%;
134
-
135
-
136
-    .header-section {
137
-      padding: 15px 15px 45px 15px;
138
-      background-color: #3c96f3;
139
-      color: white;
140
-
141
-      .login-tip {
142
-        font-size: 18px;
143
-        margin-left: 10px;
144
-      }
145
-
146
-      .cu-avatar {
147
-        border: 2px solid #eaeaea;
148
-
149
-        .icon {
150
-          font-size: 40px;
151
-        }
152
-      }
153
-
154
-      .user-info {
155
-        margin-left: 15px;
156
-
157
-        .u_title {
158
-          font-size: 18px;
159
-          line-height: 30px;
160
-        }
161
-      }
162
-    }
163
-
164
-    .content-section {
165
-      position: relative;
166
-      top: -50px;
167
-
168
-      .mine-actions {
169
-        margin: 15px 15px;
170
-        padding: 20px 0px;
171
-        border-radius: 8px;
172
-        background-color: white;
173
-
174
-        .action-item {
175
-          .icon {
176
-            font-size: 28px;
177
-          }
178
-
179
-          .text {
180
-            display: block;
181
-            font-size: 13px;
182
-            margin: 8px 0px;
183
-          }
184
-        }
185
-      }
186
-    }
187
-  }
188
-</style>

+ 102
- 0
RuoYi-App/pages/novel/detail.vue Просмотреть файл

@@ -0,0 +1,102 @@
1
+<template>
2
+  <view>
3
+    <!-- 章节间广告 -->
4
+    <ad-banner v-if="showMidAd" :ads="midAds" />
5
+    
6
+    <scroll-view scroll-y>
7
+      <!-- 章节内容 -->
8
+      <view v-for="chapter in chapters" :key="chapter.id">
9
+        <text>{{ chapter.title }}</text>
10
+        <rich-text :nodes="chapter.content" />
11
+      </view>
12
+      
13
+      <!-- 章节底部广告 -->
14
+      <ad-banner :ads="chapterAds" />
15
+    </scroll-view>
16
+    
17
+    <!-- 底部操作栏 -->
18
+    <view class="action-bar">
19
+      <button @click="prevChapter">上一章</button>
20
+      <button @click="showCatalog">目录</button>
21
+      <button @click="nextChapter">下一章</button>
22
+    </view>
23
+  </view>
24
+</template>
25
+
26
+<script>
27
+import AdBanner from '@/components/AdBanner';
28
+
29
+export default {
30
+  components: { AdBanner },
31
+  data() {
32
+    return {
33
+      novelId: null,
34
+      chapters: [],
35
+      currentChapter: 0,
36
+      midAds: [],
37
+      chapterAds: [],
38
+      showMidAd: false
39
+    };
40
+  },
41
+  onLoad(options) {
42
+    this.novelId = options.id;
43
+    this.loadNovelData();
44
+    this.scheduleMidAd();
45
+  },
46
+  methods: {
47
+    async loadNovelData() {
48
+      // 加载章节列表
49
+      const chapterRes = await this.$http.get(`/php-api/novel/chapters?id=${this.novelId}`);
50
+      this.chapters = chapterRes.data;
51
+      
52
+      // 加载当前章节内容
53
+      await this.loadChapterContent(0);
54
+      
55
+      // 加载章节广告
56
+      const adRes = await this.$http.get('/java-api/ad/position?code=CHAPTER_FOOTER');
57
+      this.chapterAds = adRes.data;
58
+    },
59
+    
60
+    async loadChapterContent(index) {
61
+      if (index < 0 || index >= this.chapters.length) return;
62
+      
63
+      const chapterId = this.chapters[index].id;
64
+      const res = await this.$http.get(`/php-api/novel/chapter?id=${chapterId}`);
65
+      
66
+      // 更新章节内容
67
+      this.$set(this.chapters, index, {
68
+        ...this.chapters[index],
69
+        content: res.data.content
70
+      });
71
+      
72
+      this.currentChapter = index;
73
+    },
74
+    
75
+    // 定时显示中间广告
76
+    scheduleMidAd() {
77
+      setTimeout(async () => {
78
+        const res = await this.$http.get('/java-api/ad/position?code=MID_CHAPTER');
79
+        this.midAds = res.data;
80
+        this.showMidAd = true;
81
+        
82
+        // 10秒后隐藏
83
+        setTimeout(() => this.showMidAd = false, 10000);
84
+      }, 30000); // 30秒后显示
85
+    },
86
+    
87
+    prevChapter() {
88
+      this.loadChapterContent(this.currentChapter - 1);
89
+    },
90
+    
91
+    nextChapter() {
92
+      this.loadChapterContent(this.currentChapter + 1);
93
+    },
94
+    
95
+    showCatalog() {
96
+      uni.navigateTo({
97
+        url: `/pages/novel/catalog?id=${this.novelId}`
98
+      });
99
+    }
100
+  }
101
+}
102
+</script>

+ 63
- 0
RuoYi-App/pages/novel/list.vue Просмотреть файл

@@ -0,0 +1,63 @@
1
+<template>
2
+  <view>
3
+    <!-- 顶部广告(来自Java后台) -->
4
+    <ad-banner :ads="topAds" />
5
+    
6
+    <!-- 小说列表 -->
7
+    <view class="novel-list">
8
+      <view v-for="novel in novelList" :key="novel.id" @click="openNovel(novel)">
9
+        <image :src="novel.cover" mode="aspectFill" />
10
+        <text>{{ novel.title }}</text>
11
+      </view>
12
+    </view>
13
+    
14
+    <!-- 底部广告 -->
15
+    <ad-banner :ads="bottomAds" />
16
+  </view>
17
+</template>
18
+<script>
19
+import AdBanner from '@/components/AdBanner';
20
+
21
+export default {
22
+	components: { AdBanner },
23
+  data() {
24
+    return {
25
+      novelList: [], // 存储小说目录数据
26
+	        topAds: [],
27
+	        bottomAds: []
28
+    }
29
+  },
30
+  async onLoad() {
31
+    await this.loadAds();
32
+    await this.loadNovels();
33
+  },
34
+  // onLoad() {
35
+  //   this.loadNovelList();
36
+  // },
37
+  methods: {
38
+    // 从Java后台加载广告
39
+    async loadAds() {
40
+      const [topRes, bottomRes] = await Promise.all([
41
+        this.$http.get('/java-api/ad/position?code=TOP_BANNER'),
42
+        this.$http.get('/java-api/ad/position?code=BOTTOM_BANNER')
43
+      ]);
44
+      
45
+      this.topAds = topRes.data;
46
+      this.bottomAds = bottomRes.data;
47
+    },
48
+    
49
+    // 从PHP系统加载小说目录
50
+    async loadNovels() {
51
+      const res = await this.$http.get('/php-api/novel/list');
52
+      this.novelList = res.data;
53
+    },
54
+    
55
+    // 打开小说详情页
56
+    openNovel(novel) {
57
+      uni.navigateTo({
58
+        url: `/pages/novel/detail?id=${novel.id}&title=${encodeURIComponent(novel.title)}`
59
+      });
60
+    }
61
+  }
62
+}
63
+</script>

+ 314
- 0
RuoYi-App/pages/novel/reader.vue Просмотреть файл

@@ -0,0 +1,314 @@
1
+<template>
2
+  <view class="reader-container" :style="readerStyles">
3
+    <!-- 顶部导航 -->
4
+    <view class="reader-header">
5
+      <uni-icons type="arrowleft" size="28" color="#fff" @click="goBack"></uni-icons>
6
+      <text class="novel-title">{{ novelTitle }}</text>
7
+      <view class="header-actions">
8
+        <uni-icons type="more" size="28" color="#fff"></uni-icons>
9
+      </view>
10
+    </view>
11
+    
12
+    <!-- 阅读区域 -->
13
+    <scroll-view scroll-y class="reader-content" :scroll-top="scrollTop" @scroll="onScroll">
14
+      <view class="chapter-title">{{ chapterDetail.title }}</view>
15
+      <rich-text :nodes="chapterContent" class="content-text"></rich-text>
16
+    </scroll-view>
17
+    
18
+    <!-- 底部操作栏 -->
19
+    <view class="reader-footer">
20
+      <view class="progress">
21
+        <text>{{ chapterDetail.chapterNumber }}/{{ totalChapters }}</text>
22
+        <text>{{ progress }}%</text>
23
+      </view>
24
+      <view class="actions">
25
+        <button class="action-btn" @click="prevChapter">
26
+          <uni-icons type="arrow-up" size="24"></uni-icons>
27
+          <text>上一章</text>
28
+        </button>
29
+        <button class="action-btn" @click="toggleMenu">
30
+          <uni-icons type="list" size="24"></uni-icons>
31
+          <text>目录</text>
32
+        </button>
33
+        <button class="action-btn" @click="toggleSettings">
34
+          <uni-icons type="gear" size="24"></uni-icons>
35
+          <text>设置</text>
36
+        </button>
37
+        <button class="action-btn" @click="nextChapter">
38
+          <uni-icons type="arrow-down" size="24"></uni-icons>
39
+          <text>下一章</text>
40
+        </button>
41
+      </view>
42
+    </view>
43
+    
44
+    <!-- 目录抽屉 -->
45
+    <uni-drawer ref="drawer" mode="right" :width="300">
46
+      <view class="drawer-content">
47
+        <text class="drawer-title">目录</text>
48
+        <scroll-view scroll-y class="chapter-list">
49
+          <view 
50
+            v-for="chapter in chapters" 
51
+            :key="chapter.id" 
52
+            class="chapter-item"
53
+            :class="{ active: chapter.id === chapterDetail.id }"
54
+            @click="selectChapter(chapter)"
55
+          >
56
+            <text>{{ chapter.chapterNumber }}. {{ chapter.title }}</text>
57
+          </view>
58
+        </scroll-view>
59
+      </view>
60
+    </uni-drawer>
61
+  </view>
62
+</template>
63
+
64
+<script>
65
+import novelService from '@/services/novelService'
66
+
67
+export default {
68
+  data() {
69
+    return {
70
+      novelId: null,
71
+      chapterId: null,
72
+      novelTitle: '',
73
+      chapterDetail: {},
74
+      chapterContent: '',
75
+      chapters: [],
76
+      totalChapters: 0,
77
+      progress: 0,
78
+      scrollTop: 0,
79
+      readerStyles: {
80
+        fontSize: '32rpx',
81
+        lineHeight: '1.8',
82
+        backgroundColor: '#f8f2e0',
83
+        color: '#333'
84
+      }
85
+    }
86
+  },
87
+  async onLoad(options) {
88
+    this.novelId = options.novelId
89
+    this.chapterId = options.chapterId || 1
90
+    
91
+    await this.loadNovelData()
92
+    await this.loadChapter()
93
+  },
94
+  methods: {
95
+    async loadNovelData() {
96
+      // 获取小说基本信息
97
+      const novel = await novelService.getNovelDetail(this.novelId)
98
+      this.novelTitle = novel.title
99
+      
100
+      // 获取章节列表
101
+      this.chapters = await novelService.getChapters(this.novelId)
102
+      this.totalChapters = this.chapters.length
103
+    },
104
+    
105
+    async loadChapter() {
106
+      uni.showLoading({ title: '加载中...' })
107
+      
108
+      try {
109
+        const chapter = await novelService.getChapterContent(this.novelId, this.chapterId)
110
+        this.chapterDetail = chapter
111
+        
112
+        // 处理章节内容
113
+        this.chapterContent = this.formatContent(chapter.content)
114
+        
115
+        // 计算阅读进度
116
+        this.progress = Math.round((this.chapterId / this.totalChapters) * 100)
117
+        
118
+        // 保存阅读进度
119
+        this.saveReadingProgress()
120
+        
121
+      } catch (error) {
122
+        uni.showToast({
123
+          title: '加载章节失败',
124
+          icon: 'none'
125
+        })
126
+      } finally {
127
+        uni.hideLoading()
128
+      }
129
+    },
130
+    
131
+    formatContent(content) {
132
+      // 将文本内容转换为带格式的HTML
133
+      const paragraphs = content.split('\n\n')
134
+      return paragraphs.map(p => `<p>${p}</p>`).join('')
135
+    },
136
+    
137
+    saveReadingProgress() {
138
+      // 保存到本地
139
+      uni.setStorageSync('readingProgress', {
140
+        novelId: this.novelId,
141
+        chapterId: this.chapterId
142
+      })
143
+      
144
+      // 如果已登录,同步到服务器
145
+      if (uni.getStorageSync('token')) {
146
+        this.$http.post('/reading/progress', {
147
+          novelId: this.novelId,
148
+          chapterId: this.chapterId
149
+        })
150
+      }
151
+    },
152
+    
153
+    prevChapter() {
154
+      if (this.chapterId > 1) {
155
+        this.chapterId--
156
+        this.loadChapter()
157
+      } else {
158
+        uni.showToast({
159
+          title: '已经是第一章',
160
+          icon: 'none'
161
+        })
162
+      }
163
+    },
164
+    
165
+    nextChapter() {
166
+      if (this.chapterId < this.totalChapters) {
167
+        this.chapterId++
168
+        this.loadChapter()
169
+      } else {
170
+        uni.showToast({
171
+          title: '已是最新章节',
172
+          icon: 'none'
173
+        })
174
+      }
175
+    },
176
+    
177
+    selectChapter(chapter) {
178
+      this.chapterId = chapter.id
179
+      this.$refs.drawer.close()
180
+      this.loadChapter()
181
+    },
182
+    
183
+    toggleMenu() {
184
+      this.$refs.drawer.open()
185
+    },
186
+    
187
+    toggleSettings() {
188
+      uni.navigateTo({
189
+        url: '/pages/reader/settings'
190
+      })
191
+    },
192
+    
193
+    goBack() {
194
+      uni.navigateBack()
195
+    },
196
+    
197
+    onScroll(e) {
198
+      // 记录滚动位置
199
+      this.scrollTop = e.detail.scrollTop
200
+    }
201
+  }
202
+}
203
+</script>
204
+
205
+<style scoped>
206
+.reader-container {
207
+  position: relative;
208
+  height: 100vh;
209
+  padding: 20rpx;
210
+  box-sizing: border-box;
211
+}
212
+
213
+.reader-header {
214
+  position: absolute;
215
+  top: 0;
216
+  left: 0;
217
+  right: 0;
218
+  display: flex;
219
+  justify-content: space-between;
220
+  align-items: center;
221
+  padding: 20rpx 30rpx;
222
+  background: rgba(0, 0, 0, 0.7);
223
+  color: white;
224
+  z-index: 100;
225
+}
226
+
227
+.novel-title {
228
+  font-size: 32rpx;
229
+  max-width: 60%;
230
+  overflow: hidden;
231
+  text-overflow: ellipsis;
232
+  white-space: nowrap;
233
+}
234
+
235
+.reader-content {
236
+  height: calc(100vh - 200rpx);
237
+  padding-top: 80rpx;
238
+  padding-bottom: 120rpx;
239
+}
240
+
241
+.chapter-title {
242
+  font-size: 40rpx;
243
+  font-weight: bold;
244
+  text-align: center;
245
+  margin-bottom: 40rpx;
246
+  color: #2a5caa;
247
+}
248
+
249
+.content-text {
250
+  font-size: 32rpx;
251
+  line-height: 1.8;
252
+}
253
+
254
+.reader-footer {
255
+  position: absolute;
256
+  bottom: 0;
257
+  left: 0;
258
+  right: 0;
259
+  background: rgba(255, 255, 255, 0.9);
260
+  padding: 20rpx;
261
+  border-top: 1rpx solid #eee;
262
+}
263
+
264
+.progress {
265
+  display: flex;
266
+  justify-content: space-between;
267
+  font-size: 28rpx;
268
+  color: #666;
269
+  margin-bottom: 20rpx;
270
+}
271
+
272
+.actions {
273
+  display: flex;
274
+  justify-content: space-between;
275
+}
276
+
277
+.action-btn {
278
+  display: flex;
279
+  flex-direction: column;
280
+  align-items: center;
281
+  background: none;
282
+  border: none;
283
+  font-size: 24rpx;
284
+  padding: 10rpx;
285
+}
286
+
287
+.drawer-content {
288
+  padding: 30rpx;
289
+}
290
+
291
+.drawer-title {
292
+  font-size: 36rpx;
293
+  font-weight: bold;
294
+  display: block;
295
+  margin-bottom: 30rpx;
296
+  border-bottom: 1rpx solid #eee;
297
+  padding-bottom: 20rpx;
298
+}
299
+
300
+.chapter-list {
301
+  height: calc(100vh - 150rpx);
302
+}
303
+
304
+.chapter-item {
305
+  padding: 20rpx 10rpx;
306
+  border-bottom: 1rpx solid #f0f0f0;
307
+  font-size: 28rpx;
308
+}
309
+
310
+.chapter-item.active {
311
+  color: #2a5caa;
312
+  font-weight: bold;
313
+}
314
+</style>

+ 13
- 13
RuoYi-App/permission.js Просмотреть файл

@@ -1,12 +1,11 @@
1
+// src/permission.js
1 2
 import { getToken } from '@/utils/auth'
2 3
 
3
-// 登录页面
4
-const loginPage = "/pages/login"
5
-  
6 4
 // 页面白名单
7 5
 const whiteList = [
8
-    '/pages/index/index', // 确保首页在名单中
9
-	'/pages/login', '/pages/register', '/pages/common/webview/index'
6
+  '/pages/index/index', // 首页无需登录
7
+  '/pages/login',
8
+  '/pages/register'
10 9
 ]
11 10
 
12 11
 // 检查地址白名单
@@ -20,16 +19,17 @@ let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"]
20 19
 list.forEach(item => {
21 20
   uni.addInterceptor(item, {
22 21
     invoke(to) {
22
+      // 始终允许访问白名单页面
23
+      if (checkWhite(to.url)) {
24
+        return true
25
+      }
26
+      
27
+      // 检查登录状态
23 28
       if (getToken()) {
24
-        if (to.url === loginPage) {
25
-          uni.reLaunch({ url: "/" })
26
-        }
27 29
         return true
28 30
       } else {
29
-        if (checkWhite(to.url)) {
30
-          return true
31
-        }
32
-        uni.reLaunch({ url: loginPage })
31
+        // 非白名单页面重定向到登录
32
+        uni.reLaunch({ url: "/pages/login" })
33 33
         return false
34 34
       }
35 35
     },
@@ -37,4 +37,4 @@ list.forEach(item => {
37 37
       console.log(err)
38 38
     }
39 39
   })
40
-})
40
+})

+ 56
- 0
RuoYi-App/services/novelService.js Просмотреть файл

@@ -0,0 +1,56 @@
1
+export default {
2
+  // 获取首页推荐小说列表
3
+  async getHomeRecommend() {
4
+    try {
5
+      const res = await this.$http.get('/novel/home')
6
+      return res.data
7
+    } catch (error) {
8
+      console.error('获取首页推荐失败', error)
9
+      return []
10
+    }
11
+  },
12
+  
13
+  // 获取小说详情
14
+  async getNovelDetail(novelId) {
15
+    try {
16
+      const res = await this.$http.get(`/novel/detail/${novelId}`)
17
+      return res.data
18
+    } catch (error) {
19
+      console.error('获取小说详情失败', error)
20
+      return null
21
+    }
22
+  },
23
+  
24
+  // 获取小说章节列表
25
+  async getChapters(novelId) {
26
+    try {
27
+      const res = await this.$http.get(`/novel/chapters/${novelId}`)
28
+      return res.data
29
+    } catch (error) {
30
+      console.error('获取章节列表失败', error)
31
+      return []
32
+    }
33
+  },
34
+  
35
+  // 获取章节内容
36
+  async getChapterContent(novelId, chapterId) {
37
+    try {
38
+      const res = await this.$http.get(`/novel/chapter/${novelId}/${chapterId}`)
39
+      return res.data
40
+    } catch (error) {
41
+      console.error('获取章节内容失败', error)
42
+      return null
43
+    }
44
+  },
45
+  
46
+  // 添加到书架
47
+  async addToBookshelf(novelId) {
48
+    try {
49
+      const res = await this.$http.post('/bookshelf/add', { novelId })
50
+      return res.code === 0
51
+    } catch (error) {
52
+      console.error('添加到书架失败', error)
53
+      return false
54
+    }
55
+  }
56
+}

+ 44
- 65
RuoYi-App/utils/request.js Просмотреть файл

@@ -1,73 +1,52 @@
1
-import store from '@/store'
2 1
 import config from '@/config'
3
-import { getToken } from '@/utils/auth'
4
-import errorCode from '@/utils/errorCode'
5
-import { toast, showConfirm, tansParams } from '@/utils/common'
6 2
 
7
-let timeout = 10000
8 3
 const baseUrl = config.baseUrl
9 4
 
10
-const request = config => {
11
-  // 是否需要设置 token
12
-  const isToken = (config.headers || {}).isToken === false
13
-  config.header = config.header || {}
14
-  if (getToken() && !isToken) {
15
-    config.header['Authorization'] = 'Bearer ' + getToken()
16
-  }
17
-  // get请求映射params参数
18
-  if (config.params) {
19
-    let url = config.url + '?' + tansParams(config.params)
20
-    url = url.slice(0, -1)
21
-    config.url = url
22
-  }
23
-  return new Promise((resolve, reject) => {
24
-    uni.request({
25
-        method: config.method || 'get',
26
-        timeout: config.timeout ||  timeout,
27
-        url: config.baseUrl || baseUrl + config.url,
28
-        data: config.data,
29
-        header: config.header,
30
-        dataType: 'json'
31
-      }).then(response => {
32
-        let [error, res] = response
33
-        if (error) {
34
-          toast('后端接口连接异常')
35
-          reject('后端接口连接异常')
36
-          return
5
+export const http = {
6
+  get(url, params = {}, options = {}) {
7
+    return this.request('GET', url, params, options)
8
+  },
9
+  
10
+  post(url, data = {}, options = {}) {
11
+    return this.request('POST', url, data, options)
12
+  },
13
+  
14
+  request(method, url, data = {}, options = {}) {
15
+    return new Promise((resolve, reject) => {
16
+      const header = {
17
+        'Content-Type': 'application/json',
18
+        ...(options.headers || {})
19
+      }
20
+      
21
+      // 添加认证token
22
+      const token = uni.getStorageSync('token')
23
+      if (token) {
24
+        header['Authorization'] = `Bearer ${token}`
25
+      }
26
+      
27
+      uni.request({
28
+        url: baseUrl + url,
29
+        method,
30
+        data,
31
+        header,
32
+        success: (res) => {
33
+          if (res.statusCode >= 200 && res.statusCode < 300) {
34
+            resolve(res.data)
35
+          } else {
36
+            reject(res.data)
37
+          }
38
+        },
39
+        fail: (err) => {
40
+          reject(err)
37 41
         }
38
-        const code = res.data.code || 200
39
-        const msg = errorCode[code] || res.data.msg || errorCode['default']
40
-        if (code === 401) {
41
-          showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => {
42
-            if (res.confirm) {
43
-              store.dispatch('LogOut').then(res => {
44
-                uni.reLaunch({ url: '/pages/login' })
45
-              })
46
-            }
47
-          })
48
-          reject('无效的会话,或者会话已过期,请重新登录。')
49
-        } else if (code === 500) {
50
-          toast(msg)
51
-          reject('500')
52
-        } else if (code !== 200) {
53
-          toast(msg)
54
-          reject(code)
55
-        }
56
-        resolve(res.data)
57 42
       })
58
-      .catch(error => {
59
-        let { message } = error
60
-        if (message === 'Network Error') {
61
-          message = '后端接口连接异常'
62
-        } else if (message.includes('timeout')) {
63
-          message = '系统接口请求超时'
64
-        } else if (message.includes('Request failed with status code')) {
65
-          message = '系统接口' + message.substr(message.length - 3) + '异常'
66
-        }
67
-        toast(message)
68
-        reject(error)
69
-      })
70
-  })
43
+    })
44
+  }
71 45
 }
72 46
 
73
-export default request
47
+// 挂载到Vue原型
48
+export default {
49
+  install(Vue) {
50
+    Vue.prototype.$http = http
51
+  }
52
+}

+ 18
- 0
RuoYi-App/vue.config.js Просмотреть файл

@@ -0,0 +1,18 @@
1
+module.exports = {
2
+  devServer: {
3
+    proxy: {
4
+      // 代理到Java后台
5
+      '/java-api': {
6
+        target: 'http://localhost:8080',
7
+        changeOrigin: true,
8
+        pathRewrite: {'^/java-api': ''}
9
+      },
10
+      // 代理到PHP小说系统
11
+      '/php-api': {
12
+        target: 'http://php-novel-site',
13
+        changeOrigin: true,
14
+        pathRewrite: {'^/php-api': ''}
15
+      }
16
+    }
17
+  }
18
+}

Загрузка…
Отмена
Сохранить