fzzj 8 meses atrás
pai
commit
07a2d07fe7

+ 20
- 4
RuoYi-App/App.vue Ver arquivo

54
     this.initTheme(); // 确保正确调用
54
     this.initTheme(); // 确保正确调用
55
     this.initStoreState();
55
     this.initStoreState();
56
     this.safeUpdateMenuVisibility();
56
     this.safeUpdateMenuVisibility();
57
+	
57
   },
58
   },
58
   beforeDestroy() {
59
   beforeDestroy() {
59
     this.isMounted = false;
60
     this.isMounted = false;
67
           this.safeUpdateMenuVisibility();
68
           this.safeUpdateMenuVisibility();
68
         }
69
         }
69
       }
70
       }
70
-    }
71
+    },
72
+	  '$store.state.token': {
73
+	    handler(newToken) {
74
+	      if (newToken) {
75
+	        console.log('检测到新token,重新加载数据');
76
+	        this.reloadData();
77
+	      }
78
+	    },
79
+	    immediate: true
80
+	  }
71
   },
81
   },
72
   methods: {
82
   methods: {
73
     // 确保 initTheme 方法正确定义
83
     // 确保 initTheme 方法正确定义
75
       console.log('主题初始化开始');
85
       console.log('主题初始化开始');
76
       
86
       
77
       // 设置默认主题
87
       // 设置默认主题
78
-      const themeName = uni.getStorageSync('selectedTheme') || 'aydzBlue';
79
-      
88
+      //const themeName = uni.getStorageSync('selectedTheme') || 'aydzBlue';
89
+        const themeName = localStorage.getItem('selectedTheme') || 'aydzBlue';
80
       // 应用主题变量
90
       // 应用主题变量
81
       const themes = {
91
       const themes = {
82
         aydzBlue: {
92
         aydzBlue: {
157
           this.$router.push('/pages/login');
167
           this.$router.push('/pages/login');
158
         }
168
         }
159
       }
169
       }
160
-    }
170
+    },
171
+	  reloadData() {
172
+	    // 在需要的地方调用此方法重新加载数据
173
+	    if (this.$route.path === '/pages/novel/list') {
174
+	      this.$refs.novelList?.initData?.();
175
+	    }
176
+	  }
161
   }
177
   }
162
 }
178
 }
163
 </script>
179
 </script>

+ 7
- 0
RuoYi-App/api/urls.js Ver arquivo

1
+// 修正重复的/api路径
2
+export default {
3
+  NOVEL_LIST: '/novel/list', // 从 /api/novel/list 改为 /novel/list
4
+  CATEGORY_TREE: '/category/tree',
5
+  HOT_NOVELS: '/novel/hot',
6
+  ADS: '/ad/position'
7
+}

+ 1
- 1
RuoYi-App/components/AdBanner.vue Ver arquivo

22
   methods: {
22
   methods: {
23
     handleAdClick(ad) {
23
     handleAdClick(ad) {
24
       // 广告点击统计
24
       // 广告点击统计
25
-      this.$http.post('/java-api/ad/click', { adId: ad.id });
25
+      this.$http.post('/ad/click', { adId: ad.id });
26
       
26
       
27
       // 处理跳转
27
       // 处理跳转
28
       if (ad.linkType === 'NOVEL') {
28
       if (ad.linkType === 'NOVEL') {

+ 1
- 1
RuoYi-App/config.js Ver arquivo

2
 module.exports = {
2
 module.exports = {
3
   //baseUrl: 'https://vue.ruoyi.vip/prod-api',
3
   //baseUrl: 'https://vue.ruoyi.vip/prod-api',
4
    //baseUrl: 'http://localhost:8080',
4
    //baseUrl: 'http://localhost:8080',
5
-   baseUrl: process.env.VUE_APP_BASE_API || '/',
5
+   baseUrl: process.env.VUE_APP_BASE_API || 'http://localhost:8080',
6
   // 应用信息
6
   // 应用信息
7
   appInfo: {
7
   appInfo: {
8
     // 应用名称
8
     // 应用名称

+ 2
- 2
RuoYi-App/pages/login.vue Ver arquivo

45
 </template>
45
 </template>
46
 
46
 
47
 <script>
47
 <script>
48
-  import { getCodeImg } from '@/api/login'
49
-  import { getToken } from '@/utils/auth'
48
+  //import { getCodeImg } from '@/api/login'
49
+  //import { getToken } from '@/utils/auth'
50
 
50
 
51
   export default {
51
   export default {
52
     data() {
52
     data() {

+ 2
- 2
RuoYi-App/pages/novel/detail.vue Ver arquivo

64
       }
64
       }
65
       
65
       
66
       // 加载章节广告
66
       // 加载章节广告
67
-      const adRes = await this.$http.get('/java-api/ad/position?code=CHAPTER_FOOTER');
67
+      const adRes = await this.$http.get('/ad/position?code=CHAPTER_FOOTER');
68
       this.chapterAds = adRes.data;
68
       this.chapterAds = adRes.data;
69
     },
69
     },
70
     
70
     
87
     // 定时显示中间广告
87
     // 定时显示中间广告
88
     scheduleMidAd() {
88
     scheduleMidAd() {
89
       setTimeout(async () => {
89
       setTimeout(async () => {
90
-        const res = await this.$http.get('/java-api/ad/position?code=MID_CHAPTER');
90
+        const res = await this.$http.get('/ad/position?code=MID_CHAPTER');
91
         this.midAds = res.data;
91
         this.midAds = res.data;
92
         this.showMidAd = true;
92
         this.showMidAd = true;
93
         
93
         

+ 22
- 12
RuoYi-App/pages/novel/list.vue Ver arquivo

28
       <view v-if="novels.length === 0 && !error" class="empty-container">
28
       <view v-if="novels.length === 0 && !error" class="empty-container">
29
         <image src="/static/empty-book.png" class="empty-img" />
29
         <image src="/static/empty-book.png" class="empty-img" />
30
         <text class="empty-text">暂无小说数据</text>
30
         <text class="empty-text">暂无小说数据</text>
31
-        <button class="btn-refresh" @click="loadNovels">重新加载</button>
31
+		<button class="btn-refresh" @click="reloadData">重新加载</button>
32
       </view>
32
       </view>
33
       
33
       
34
       <!-- 错误状态 -->
34
       <!-- 错误状态 -->
96
 <script>
96
 <script>
97
 // 确保路径正确
97
 // 确保路径正确
98
 import AdBanner from '@/components/AdBanner';
98
 import AdBanner from '@/components/AdBanner';
99
+import urls from '@/api/urls';
99
 
100
 
100
 export default {
101
 export default {
101
   components: { AdBanner },
102
   components: { AdBanner },
166
         // 加载分类
167
         // 加载分类
167
     async loadCategories() {
168
     async loadCategories() {
168
       try {
169
       try {
169
-        const res = await this.$http.get('/java-api/category/tree');
170
+        // 公开API,不需要认证
171
+              const res = await this.$http.get('/category/tree', {}, {
172
+                header: { isToken: false } // 明确不携带token
173
+              });
170
         this.categories = res?.data ? [{ id: 0, name: '全部' }, ...res.data] : [];
174
         this.categories = res?.data ? [{ id: 0, name: '全部' }, ...res.data] : [];
171
       } catch (e) {
175
       } catch (e) {
172
         console.error('加载分类失败', e);
176
         console.error('加载分类失败', e);
176
     // 加载热门小说
180
     // 加载热门小说
177
     async loadHotNovels() {
181
     async loadHotNovels() {
178
       try {
182
       try {
179
-        const res = await this.$http.get('/novel/hot');
180
-        
183
+      // 公开API,不需要认证
184
+      const res = await this.$http.get('/novel/hot', {}, {
185
+        header: { isToken: false } // 明确不携带token
186
+      });
181
         // 处理响应
187
         // 处理响应
182
         if (res?.data?.rows) {
188
         if (res?.data?.rows) {
183
           this.hotNovels = res.data.rows;
189
           this.hotNovels = res.data.rows;
197
       this.loading = true;
203
       this.loading = true;
198
       this.error = null;
204
       this.error = null;
199
       try {
205
       try {
200
-        const res = await this.$http.get('/novel/list');
201
-        
206
+        // 使用正确的请求方式
207
+        //const res = await this.$http.get('/novel/list');
208
+         const res = await this.$http.get('/novel/list', {}, { header: { isToken: false } });
202
         // 处理响应
209
         // 处理响应
203
         if (res?.data?.rows) {
210
         if (res?.data?.rows) {
204
           this.novels = res.data.rows;
211
           this.novels = res.data.rows;
206
           this.novels = res.data;
213
           this.novels = res.data;
207
         } else {
214
         } else {
208
           console.warn('小说列表API返回格式未知', res);
215
           console.warn('小说列表API返回格式未知', res);
209
-          this.novels = this.mockNovels; // 使用模拟数据
216
+          this.novels = this.mockNovels;
210
         }
217
         }
211
       } catch (e) {
218
       } catch (e) {
212
         console.error('加载小说失败', e);
219
         console.error('加载小说失败', e);
213
         this.error = '加载失败,请重试';
220
         this.error = '加载失败,请重试';
214
-        this.novels = this.mockNovels; // 使用模拟数据
221
+        this.novels = this.mockNovels;
215
       } finally {
222
       } finally {
216
         this.loading = false;
223
         this.loading = false;
217
       }
224
       }
221
     async loadAds() {
228
     async loadAds() {
222
       try {
229
       try {
223
         const [topRes, bottomRes] = await Promise.all([
230
         const [topRes, bottomRes] = await Promise.all([
224
-          this.$http.get('/java-api/ad/position?code=TOP_BANNER'),
225
-          this.$http.get('/java-api/ad/position?code=BOTTOM_BANNER')
231
+          this.$http.get('/ad/position?code=TOP_BANNER', {}, { header: { isToken: false } }),
232
+          this.$http.get('/ad/position?code=BOTTOM_BANNER', {}, { header: { isToken: false } })
226
         ]);
233
         ]);
227
-        
228
         this.topAds = topRes?.data || topRes || [];
234
         this.topAds = topRes?.data || topRes || [];
229
         this.bottomAds = bottomRes?.data || bottomRes || [];
235
         this.bottomAds = bottomRes?.data || bottomRes || [];
230
       } catch (e) {
236
       } catch (e) {
267
       }
273
       }
268
       uni.navigateTo({ url: '/pages/author/apply' });
274
       uni.navigateTo({ url: '/pages/author/apply' });
269
     },
275
     },
270
-    
276
+
271
     goMore() {
277
     goMore() {
272
       uni.navigateTo({
278
       uni.navigateTo({
273
         url: '/pages/novel/more'
279
         url: '/pages/novel/more'
274
       });
280
       });
281
+    },
282
+        // 确保重新加载按钮绑定到正确的方法
283
+    reloadData() {
284
+      this.initData();
275
     }
285
     }
276
   }
286
   }
277
 }
287
 }

+ 2
- 2
RuoYi-App/router/index.js Ver arquivo

7
 const routes = [
7
 const routes = [
8
   {
8
   {
9
     path: '/',
9
     path: '/',
10
-    redirect: '/pages/novel/list'
10
+    redirect: '/pages/index/index'
11
   },
11
   },
12
   {
12
   {
13
     path: '/pages/novel/list',
13
     path: '/pages/novel/list',
74
   console.log('Router is ready');
74
   console.log('Router is ready');
75
 })
75
 })
76
 
76
 
77
-export default router
77
+export default router

+ 5
- 5
RuoYi-App/services/api.js Ver arquivo

2
 // Java 后台接口
2
 // Java 后台接口
3
 const javaApi = {
3
 const javaApi = {
4
   // 小说相关接口
4
   // 小说相关接口
5
-  getNovelList: (params) => request({ url: '/java-api/novel/list', params }),
6
-  getChapterContent: (chapterId) => request({ url: `/java-api/novel/chapter/${chapterId}` }),
5
+  getNovelList: (params) => request({ url: '/novel/list', params }),
6
+  getChapterContent: (chapterId) => request({ url: `/novel/chapter/${chapterId}` }),
7
   
7
   
8
   // 作家申请
8
   // 作家申请
9
-  applyAuthor: (data) => request({ url: '/java-api/author/apply', method: 'post', data })
9
+  applyAuthor: (data) => request({ url: '/author/apply', method: 'post', data })
10
 }
10
 }
11
 
11
 
12
 // PHP 后台接口 (预留)
12
 // PHP 后台接口 (预留)
84
   // 保存阅读进度
84
   // 保存阅读进度
85
   saveReadingProgress(chapter) {
85
   saveReadingProgress(chapter) {
86
     return request({
86
     return request({
87
-      url: '/java-api/reading/progress',
87
+      url: '/reading/progress',
88
       method: 'post',
88
       method: 'post',
89
       data: { chapter }
89
       data: { chapter }
90
     })
90
     })
93
   // 获取阅读进度
93
   // 获取阅读进度
94
   getReadingProgress() {
94
   getReadingProgress() {
95
     return request({
95
     return request({
96
-      url: '/java-api/reading/progress',
96
+      url: '/reading/progress',
97
       method: 'get'
97
       method: 'get'
98
     })
98
     })
99
   }
99
   }

+ 7
- 2
RuoYi-App/utils/auth.js Ver arquivo

1
 const TokenKey = 'App-Token'
1
 const TokenKey = 'App-Token'
2
 
2
 
3
 export function getToken() {
3
 export function getToken() {
4
-  return uni.getStorageSync(TokenKey)
4
+  //return uni.getStorageSync(TokenKey)
5
+    // 确保使用正确的存储方式
6
+    return uni.getStorageSync('token') || localStorage.getItem('token') || '';
5
 }
7
 }
6
 
8
 
7
 export function setToken(token) {
9
 export function setToken(token) {
8
-  return uni.setStorageSync(TokenKey, token)
10
+	  // 同时设置到uni和localStorage确保兼容性
11
+	  uni.setStorageSync('token', token);
12
+	  localStorage.setItem('token', token);
13
+  return uni.setStorageSync('token', token)
9
 }
14
 }
10
 
15
 
11
 export function removeToken() {
16
 export function removeToken() {

+ 74
- 72
RuoYi-App/utils/request.js Ver arquivo

1
 // src/utils/request.js
1
 // src/utils/request.js
2
 import config from '@/config'
2
 import config from '@/config'
3
+import { getToken } from '@/utils/auth'
4
+import { toast, showConfirm, tansParams } from '@/utils/common'
5
+
3
 const baseUrl = config.baseUrl
6
 const baseUrl = config.baseUrl
4
-// 使用直接导出的函数,避免 this 上下文问题
5
-export function get(url, params = {}, options = {}) {
6
-  return request('GET', url, params, options)
7
-}
7
+let timeout = 10000
8
 
8
 
9
-export function post(url, data = {}, options = {}) {
10
-  return request('POST', url, data, options)
11
-}
9
+const request = (configObj) => {  // 重命名参数为 configObj
10
+  // 确保传入的配置对象有效
11
+  configObj = configObj || {}
12
+  configObj.header = configObj.header || {}
13
+  
14
+  // 处理 token
15
+  const token = getToken()
16
+  // 只有存在token且未明确禁止携带token时才添加
17
+  if (token && configObj.header.isToken !== false) {
18
+    configObj.header['Authorization'] = 'Bearer ' + token
19
+  } else if (!token) {
20
+    console.warn('请求未携带Token:', configObj.url)
21
+  }
12
 
22
 
13
-function request(method, url, data = {}, options = {}) {
23
+  
24
+  // GET 请求参数处理
25
+  if (configObj.params) {
26
+    let url = configObj.url + '?' + tansParams(configObj.params)
27
+    url = url.slice(0, -1)
28
+    configObj.url = url
29
+  }
30
+  
14
   return new Promise((resolve, reject) => {
31
   return new Promise((resolve, reject) => {
15
-    // 处理路径
16
-    let finalUrl = url;
17
-    
18
-    // 特殊处理 Java API
19
-    if (url.startsWith('/java-api')) {
20
-        finalUrl = url
21
-      }
22
-    // 普通 API 处理
23
-    else if (!url.startsWith('http')) {
24
-      // 确保只有一个 /api 前缀
25
-      if (!url.startsWith('/api')) {
26
-        finalUrl = '/api' + (url.startsWith('/') ? url : `/${url}`);
27
-      }
28
-    }
29
-    
30
-    const baseUrl = process.env.VUE_APP_BASE_API || '/';
31
-    const fullUrl = baseUrl + finalUrl;
32
-    
33
-    console.log('请求URL:', fullUrl, '方法:', method);
34
-    
35
     uni.request({
32
     uni.request({
36
-      url: fullUrl,
37
-      method,
38
-      data,
39
-      header: {
40
-        'Content-Type': 'application/json',
41
-        ...(options.headers || {})
42
-      },
33
+      method: configObj.method || 'get',
34
+      timeout: configObj.timeout || timeout,
35
+      url: (configObj.baseUrl || baseUrl) + configObj.url,
36
+      data: configObj.data,
37
+      header: configObj.header,
38
+      dataType: 'json',
43
       success: (res) => {
39
       success: (res) => {
44
-        console.log('API响应:', res);
45
-        
46
-        // 处理未授权情况
40
+        // 处理401认证失败
47
         if (res.statusCode === 401) {
41
         if (res.statusCode === 401) {
48
-          uni.showToast({
49
-            title: '请先登录',
50
-            icon: 'none'
51
-          });
52
-          uni.navigateTo({
53
-            url: '/pages/login'
54
-          });
55
-          reject(new Error('未授权访问'));
56
-          return;
57
-        }
58
-        
59
-        // 统一处理成功响应
60
-        if (res.statusCode >= 200 && res.statusCode < 300) {
61
-          resolve(res.data)
42
+          // 更友好的错误处理
43
+          const errorMsg = res.data?.msg || '认证失败,请重新登录';
44
+          console.warn('认证失败:', configObj.url, errorMsg);
45
+          
46
+          // 显示提示但不强制跳转
47
+          toast(errorMsg);
48
+          
49
+          // 清除无效token
50
+          if (token) {
51
+            uni.removeStorageSync('token');
52
+            localStorage.removeItem('token');
53
+          }
54
+          
55
+          reject(new Error(errorMsg));
56
+        } else if (res.statusCode === 200) {
57
+          resolve(res.data);
62
         } else {
58
         } else {
63
-          // 尝试提取错误消息
64
-          const errorMsg = res.data?.msg || res.data?.message || '请求失败';
65
-          reject(new Error(errorMsg))
59
+          reject(res.data);
66
         }
60
         }
67
       },
61
       },
68
       fail: (err) => {
62
       fail: (err) => {
69
-        console.error('请求失败:', err);
63
+        let message = '后端接口连接异常'
64
+        if (err.errMsg.includes('timeout')) {
65
+          message = '请求超时'
66
+        }
67
+        toast(message)
70
         reject(err)
68
         reject(err)
71
       }
69
       }
72
     })
70
     })
73
   })
71
   })
74
 }
72
 }
75
-service.interceptors.request.use(config => {
76
-  // 从Vuex或localStorage获取token
77
-  const token = store.state.token || localStorage.getItem('token');
78
-  
79
-  if (token) {
80
-    config.headers['Authorization'] = `Bearer ${token}`;
81
-  }
82
-  return config;
83
-}, error => {
84
-  return Promise.reject(error);
85
-});
86
-// 导出对象以兼容旧代码
87
-export default {
88
-  get,
89
-  post,
90
-  request
73
+
74
+// 添加便捷方法
75
+request.get = (url, params, config = {}) => {
76
+  return request({
77
+    url,
78
+    params,
79
+    method: 'get',
80
+    ...config
81
+  })
91
 }
82
 }
83
+
84
+request.post = (url, data, config = {}) => {
85
+  return request({
86
+    url,
87
+    data,
88
+    method: 'post',
89
+    ...config
90
+  })
91
+}
92
+
93
+export default request

+ 7
- 10
RuoYi-App/vue.config.js Ver arquivo

1
 // vue.config.js
1
 // vue.config.js
2
+const path = require('path');
3
+
2
 module.exports = {
4
 module.exports = {
3
   devServer: {
5
   devServer: {
4
     proxy: {
6
     proxy: {
5
-      '/java-api': {
7
+      // 统一代理配置
8
+      '/': {
6
         target: 'http://localhost:8080',
9
         target: 'http://localhost:8080',
7
         changeOrigin: true,
10
         changeOrigin: true,
8
-        pathRewrite: { 
9
-          '^/java-api': '/java-api' // 保持原始路径
10
-        }
11
-      },
12
-      '/api': {
13
-        target: 'http://localhost:8080',
14
-        changeOrigin: true,
15
-        pathRewrite: { 
16
-          '^/api': '/api' // 修正路径重写规则
11
+        pathRewrite: {
12
+          '^/api': '', // 移除多余的/api前缀
13
+          '^/': '' 
17
         }
14
         }
18
       }
15
       }
19
     }
16
     }

+ 7
- 3
RuoYi-Vue/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java Ver arquivo

129
     }
129
     }
130
     // SecurityConfig.java
130
     // SecurityConfig.java
131
 
131
 
132
+
132
     protected void configure(HttpSecurity http) throws Exception {
133
     protected void configure(HttpSecurity http) throws Exception {
133
         http
134
         http
134
                 .authorizeRequests()
135
                 .authorizeRequests()
135
-                .antMatchers("/api/novel/list").permitAll() // 放行小说列表
136
-                .antMatchers("/java-api/category/tree").permitAll() // 放行分类
137
-                .antMatchers("/api/novel/hot").permitAll() // 放行热门小说
136
+                // 放行公共API
137
+                .antMatchers("/novel/list").permitAll()
138
+                .antMatchers("/category/tree").permitAll()
139
+                .antMatchers("/novel/hot").permitAll()
140
+                .antMatchers("/ad/position").permitAll()
141
+                // 其他请求需要认证
138
                 .anyRequest().authenticated()
142
                 .anyRequest().authenticated()
139
                 .and()
143
                 .and()
140
                 .csrf().disable();
144
                 .csrf().disable();

+ 1
- 1
RuoYi-Vue/ruoyi-system/src/main/java/com/ruoyi/novel/controller/NovelController.java Ver arquivo

29
 
29
 
30
 // NovelController.java
30
 // NovelController.java
31
 @RestController
31
 @RestController
32
-@RequestMapping("/api/novel")
32
+@RequestMapping("/novel")
33
 public class NovelController extends BaseController {
33
 public class NovelController extends BaseController {
34
 
34
 
35
 
35
 

Carregando…
Cancelar
Salvar