DESKTOP-8FESTBI\Administrator 8 mesi fa
parent
commit
fb800c1729

+ 2
- 1
RuoYi-App/.env Vedi File

1
 # 基础配置
1
 # 基础配置
2
 VUE_APP_NAME=哎呀电子小说
2
 VUE_APP_NAME=哎呀电子小说
3
 VUE_APP_VERSION=1.0.0
3
 VUE_APP_VERSION=1.0.0
4
-VUE_APP_BASE_URL=https://xiaoshuo.aiyadianzi.ltd
4
+#VUE_APP_BASE_URL=https://xiaoshuo.aiyadianzi.ltd
5
+VUE_APP_BASE_API=/
5
 # 后端API地址
6
 # 后端API地址
6
 VUE_APP_API_BASE=https://api.aiyadianzi.ltd
7
 VUE_APP_API_BASE=https://api.aiyadianzi.ltd
7
 VUE_APP_PHP_BASE=https://php-backend.aiyadianzi.ltd
8
 VUE_APP_PHP_BASE=https://php-backend.aiyadianzi.ltd

+ 41
- 40
RuoYi-App/App.vue Vedi File

51
   mounted() {
51
   mounted() {
52
     this.isMounted = true;
52
     this.isMounted = true;
53
     this.checkRouteAuth();
53
     this.checkRouteAuth();
54
-    this.initTheme();
54
+    this.initTheme(); // 确保正确调用
55
     this.initStoreState();
55
     this.initStoreState();
56
     this.safeUpdateMenuVisibility();
56
     this.safeUpdateMenuVisibility();
57
   },
57
   },
70
     }
70
     }
71
   },
71
   },
72
   methods: {
72
   methods: {
73
+    // 确保 initTheme 方法正确定义
74
+    initTheme() {
75
+      console.log('主题初始化开始');
76
+      
77
+      // 设置默认主题
78
+      const themeName = uni.getStorageSync('selectedTheme') || 'aydzBlue';
79
+      
80
+      // 应用主题变量
81
+      const themes = {
82
+        aydzBlue: {
83
+          '--primary-color': '#2a5caa',
84
+          '--bg-color': '#e6f7ff',
85
+          '--text-color': '#1a3353',
86
+          '--card-bg': '#d0e8ff',
87
+          '--header-bg': '#2a5caa'
88
+        },
89
+	        default: {
90
+          '--primary-color': '#1890ff',
91
+          '--bg-color': '#f8f9fa',
92
+          '--text-color': '#333',
93
+          '--card-bg': '#ffffff'
94
+        }
95
+      };
96
+            const theme = themes[themeName] || themes.default;
97
+      
98
+      // 应用主题变量
99
+      Object.keys(theme).forEach(key => {
100
+        document.documentElement.style.setProperty(key, theme[key]);
101
+      });
102
+      
103
+      console.log('主题初始化完成');
104
+    },
73
     reload() {
105
     reload() {
74
       this.isRouterAlive = false;
106
       this.isRouterAlive = false;
75
       this.$nextTick(() => {
107
       this.$nextTick(() => {
96
     },
128
     },
97
     
129
     
98
     initStoreState() {
130
     initStoreState() {
99
-      const token = uni.getStorageSync('token') || '';
100
-      const readingProgress = uni.getStorageSync('readingProgress') || 1;
131
+      const token = uni.getStorageSync('token') || ''
132
+      const readingProgress = uni.getStorageSync('readingProgress') || 1
101
       
133
       
102
-      // 确保 store 存在
134
+      // 确保 store 存在并正确提交
103
       if (this.$store) {
135
       if (this.$store) {
104
-        this.$store.commit('SET_TOKEN', token);
105
-        this.$store.commit('SET_READING_PROGRESS', readingProgress);
136
+        this.$store.commit('SET_TOKEN', token)
137
+        this.$store.commit('SET_READING_PROGRESS', readingProgress)
138
+        console.log('Store initialized:', this.$store.state)
139
+      } else {
140
+        console.error('Store is not available!')
106
       }
141
       }
107
-      
108
-      console.log('App launched, store:', this.$store ? this.$store.state : '未初始化');
109
-    },
110
-    
111
-    initTheme() {
112
-      // 主题初始化逻辑
113
-      console.log('主题初始化完成');
114
-      
115
-      // 设置默认主题
116
-      const themeName = uni.getStorageSync('selectedTheme') || 'aydzBlue';
117
-      
118
-      // 应用主题变量
119
-      const themes = {
120
-        aydzBlue: {
121
-          '--primary-color': '#2a5caa',
122
-          '--bg-color': '#e6f7ff',
123
-          '--text-color': '#1a3353',
124
-          '--card-bg': '#d0e8ff',
125
-          '--header-bg': '#2a5caa'
126
-        },
127
-        default: {
128
-          '--primary-color': '#1890ff',
129
-          '--bg-color': '#f8f9fa',
130
-          '--text-color': '#333',
131
-          '--card-bg': '#ffffff'
132
-        }
133
-      };
134
-      
135
-      const theme = themes[themeName] || themes.default;
136
-      
137
-      // 应用主题变量
138
-      Object.keys(theme).forEach(key => {
139
-        document.documentElement.style.setProperty(key, theme[key]);
140
-      });
141
     },
142
     },
142
     
143
     
143
     checkRouteAuth() {
144
     checkRouteAuth() {

+ 2
- 1
RuoYi-App/config.js Vedi File

1
 // 应用全局配置
1
 // 应用全局配置
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
   // 应用信息
6
   // 应用信息
6
   appInfo: {
7
   appInfo: {
7
     // 应用名称
8
     // 应用名称

+ 10
- 6
RuoYi-App/main.js Vedi File

12
 
12
 
13
 Vue.config.productionTip = false
13
 Vue.config.productionTip = false
14
 
14
 
15
-// 正确挂载 $http 方法
15
+// 正确挂载 $http 方法 - 关键修复
16
 Vue.prototype.$http = http
16
 Vue.prototype.$http = http
17
 
17
 
18
 // 初始化应用
18
 // 初始化应用
21
   store,
21
   store,
22
   render: h => h(App),
22
   render: h => h(App),
23
   
23
   
24
-  // 确保应用挂载时路由已准备就绪
25
-  beforeMount() {
24
+  created() {
25
+    // 验证 $http 是否已挂载
26
+    console.log('$http available in root instance:', typeof this.$http.get === 'function')
27
+  },
28
+  
29
+  mounted() {
26
     // 确保路由初始化完成
30
     // 确保路由初始化完成
27
     if (!router.currentRoute) {
31
     if (!router.currentRoute) {
28
-      console.warn('路由未初始化,重定向到默认页面');
29
-      router.push('/pages/novel/list');
32
+      console.warn('路由未初始化,重定向到默认页面')
33
+      router.push('/pages/novel/list')
30
     }
34
     }
31
   }
35
   }
32
-}).$mount('#app')
36
+}).$mount('#app')

+ 76
- 54
RuoYi-App/pages/novel/list.vue Vedi File

40
       
40
       
41
       <!-- 热门推荐 -->
41
       <!-- 热门推荐 -->
42
       <view v-if="hotNovels.length > 0" class="section">
42
       <view v-if="hotNovels.length > 0" class="section">
43
+    <!-- ... -->
44
+  </view>
45
+  <view v-else class="section">
43
         <view class="section-header">
46
         <view class="section-header">
44
-          <text class="section-title">热门推荐</text>
47
+      <text class="section-title">热门推荐(演示数据)</text>
45
           <text class="section-more" @click="goMore">更多 ></text>
48
           <text class="section-more" @click="goMore">更多 ></text>
46
         </view>
49
         </view>
47
         <scroll-view scroll-x class="hot-list">
50
         <scroll-view scroll-x class="hot-list">
48
           <view 
51
           <view 
49
-            v-for="novel in hotNovels" 
52
+        v-for="novel in demoHotNovels" 
50
             :key="novel.id" 
53
             :key="novel.id" 
51
             class="hot-item"
54
             class="hot-item"
52
             @click="openNovel(novel)"
55
             @click="openNovel(novel)"
53
           >
56
           >
54
-            <image :src="getCoverUrl(novel.cover)" class="hot-cover" />
57
+        <image :src="novel.cover" class="hot-cover" />
55
             <text class="hot-title">{{ novel.title }}</text>
58
             <text class="hot-title">{{ novel.title }}</text>
56
           </view>
59
           </view>
57
         </scroll-view>
60
         </scroll-view>
59
       
62
       
60
       <!-- 分类小说列表 -->
63
       <!-- 分类小说列表 -->
61
       <view v-if="novels.length > 0" class="section">
64
       <view v-if="novels.length > 0" class="section">
65
+    <!-- ... -->
66
+  </view>
67
+  <view v-else class="section">
62
         <view class="section-header">
68
         <view class="section-header">
63
-          <text class="section-title">{{ currentCategoryName }}作品</text>
69
+      <text class="section-title">小说列表(演示数据)</text>
64
         </view>
70
         </view>
65
         <view class="novel-grid">
71
         <view class="novel-grid">
66
           <view 
72
           <view 
67
-            v-for="(novel, index) in novels" 
68
-            :key="index" 
73
+        v-for="novel in demoNovels" 
74
+        :key="novel.id" 
69
             class="novel-item"
75
             class="novel-item"
70
             @click="openNovel(novel)"
76
             @click="openNovel(novel)"
71
           >
77
           >
72
-            <image :src="getCoverUrl(novel.cover)" class="novel-cover" />
73
-            <text class="novel-title">{{ novel.title || '未知标题' }}</text>
74
-            <text class="novel-author">{{ novel.author || '未知作者' }}</text>
78
+        <image :src="novel.cover" class="novel-cover" />
79
+        <text class="novel-title">{{ novel.title }}</text>
80
+        <text class="novel-author">{{ novel.author }}</text>
75
           </view>
81
           </view>
76
         </view>
82
         </view>
77
       </view>
83
       </view>
103
       novels: [],
109
       novels: [],
104
       loading: true,
110
       loading: true,
105
       error: null,
111
       error: null,
106
-      currentCategoryName: '全部'
112
+      currentCategoryName: '全部',
113
+      
114
+      // 临时解决方案 - 模拟数据
115
+      mockHotNovels: [
116
+        { id: 1, title: '总裁的替身前妻', cover: '/static/demo-cover1.jpg' },
117
+        { id: 2, title: '神医毒妃', cover: '/static/demo-cover2.jpg' },
118
+        { id: 3, title: '穿越之王妃逆袭', cover: '/static/demo-cover3.jpg' }
119
+      ],
120
+      mockNovels: [
121
+        { id: 4, title: '都市最强赘婿', author: '网络作家', cover: '/static/demo-cover4.jpg' },
122
+        { id: 5, title: '修仙归来', author: '仙侠迷', cover: '/static/demo-cover5.jpg' },
123
+        { id: 6, title: '校园纯爱物语', author: '青春校园', cover: '/static/demo-cover6.jpg' }
124
+      ]
107
     }
125
     }
108
   },
126
   },
109
-  async mounted() {
110
-    await this.initData();
127
+  mounted() {
128
+    console.log('$http available in component:', typeof this.$http.get === 'function');
129
+    
130
+    // 确保所有方法都已绑定
131
+    console.log('loadCategories:', typeof this.loadCategories);
132
+    console.log('loadHotNovels:', typeof this.loadHotNovels);
133
+    console.log('loadNovels:', typeof this.loadNovels);
134
+    console.log('loadAds:', typeof this.loadAds);
135
+    
136
+    this.initData();
111
   },
137
   },
112
   methods: {
138
   methods: {
113
     async initData() {
139
     async initData() {
140
+      // 确保所有方法都已绑定
141
+      if (
142
+        typeof this.loadCategories !== 'function' ||
143
+        typeof this.loadHotNovels !== 'function' ||
144
+        typeof this.loadNovels !== 'function' ||
145
+        typeof this.loadAds !== 'function'
146
+      ) {
147
+        console.error('一个或多个方法未绑定!');
148
+        this.error = '系统初始化失败,请刷新页面';
149
+        this.loading = false;
150
+        return;
151
+      }
152
+      
114
       try {
153
       try {
115
-        // 顺序加载数据,避免并行请求冲突
154
+        // 顺序加载数据
116
         await this.loadCategories();
155
         await this.loadCategories();
117
         await this.loadHotNovels();
156
         await this.loadHotNovels();
118
         await this.loadNovels();
157
         await this.loadNovels();
124
         this.loading = false;
163
         this.loading = false;
125
       }
164
       }
126
     },
165
     },
127
-    
128
-    // 加载广告
129
-    async loadAds() {
130
-      try {
131
-        const [topRes, bottomRes] = await Promise.all([
132
-          this.$http.get('/java-api/ad/position?code=TOP_BANNER'),
133
-          this.$http.get('/java-api/ad/position?code=BOTTOM_BANNER')
134
-        ]);
135
-        
136
-        this.topAds = topRes?.data || topRes || [];
137
-        this.bottomAds = bottomRes?.data || bottomRes || [];
138
-      } catch (e) {
139
-        console.error('加载广告失败', e);
140
-      }
141
-    },
142
-    
143
-    // 加载分类
166
+        // 加载分类
144
     async loadCategories() {
167
     async loadCategories() {
145
       try {
168
       try {
146
-        const res = await this.$http.get('/category/tree');
147
-        // 确保有"全部"选项
169
+        const res = await this.$http.get('/java-api/category/tree');
148
         this.categories = res?.data ? [{ id: 0, name: '全部' }, ...res.data] : [];
170
         this.categories = res?.data ? [{ id: 0, name: '全部' }, ...res.data] : [];
149
       } catch (e) {
171
       } catch (e) {
150
         console.error('加载分类失败', e);
172
         console.error('加载分类失败', e);
151
         this.categories = [{ id: 0, name: '全部' }];
173
         this.categories = [{ id: 0, name: '全部' }];
152
       }
174
       }
153
     },
175
     },
154
-    
155
     // 加载热门小说
176
     // 加载热门小说
156
     async loadHotNovels() {
177
     async loadHotNovels() {
157
       try {
178
       try {
158
-        const res = await this.$http.get('/api/novel/hot');
179
+        const res = await this.$http.get('/novel/hot');
159
         
180
         
160
-        // 处理不同API响应格式
181
+        // 处理响应
161
         if (res?.data?.rows) {
182
         if (res?.data?.rows) {
162
           this.hotNovels = res.data.rows;
183
           this.hotNovels = res.data.rows;
163
         } else if (Array.isArray(res?.data)) {
184
         } else if (Array.isArray(res?.data)) {
164
           this.hotNovels = res.data;
185
           this.hotNovels = res.data;
165
-        } else if (Array.isArray(res)) {
166
-          this.hotNovels = res;
167
         } else {
186
         } else {
168
           console.warn('热门小说API返回格式未知', res);
187
           console.warn('热门小说API返回格式未知', res);
169
-          this.hotNovels = [];
188
+          this.hotNovels = this.mockHotNovels; // 使用模拟数据
170
         }
189
         }
171
       } catch (e) {
190
       } catch (e) {
172
         console.error('加载热门小说失败', e);
191
         console.error('加载热门小说失败', e);
173
-        this.hotNovels = [];
192
+        this.hotNovels = this.mockHotNovels; // 使用模拟数据
174
       }
193
       }
175
     },
194
     },
176
-    
177
     // 加载小说列表
195
     // 加载小说列表
178
     async loadNovels() {
196
     async loadNovels() {
179
       this.loading = true;
197
       this.loading = true;
180
       this.error = null;
198
       this.error = null;
181
       try {
199
       try {
182
-        const res = await this.$http.get('/api/novel/list');
183
-        console.log('小说列表API响应:', res);
200
+        const res = await this.$http.get('/novel/list');
184
         
201
         
185
-        // 处理不同API响应格式
202
+        // 处理响应
186
         if (res?.data?.rows) {
203
         if (res?.data?.rows) {
187
           this.novels = res.data.rows;
204
           this.novels = res.data.rows;
188
         } else if (Array.isArray(res?.data)) {
205
         } else if (Array.isArray(res?.data)) {
189
           this.novels = res.data;
206
           this.novels = res.data;
190
-        } else if (res?.data?.list) {
191
-          this.novels = res.data.list;
192
-        } else if (Array.isArray(res)) {
193
-          this.novels = res;
194
         } else {
207
         } else {
195
           console.warn('小说列表API返回格式未知', res);
208
           console.warn('小说列表API返回格式未知', res);
196
-          this.novels = [];
197
-          this.error = '数据格式错误';
209
+          this.novels = this.mockNovels; // 使用模拟数据
198
         }
210
         }
199
       } catch (e) {
211
       } catch (e) {
200
         console.error('加载小说失败', e);
212
         console.error('加载小说失败', e);
201
         this.error = '加载失败,请重试';
213
         this.error = '加载失败,请重试';
202
-        uni.showToast({
203
-          title: '加载失败,请重试',
204
-          icon: 'none'
205
-        });
206
-        this.novels = [];
214
+        this.novels = this.mockNovels; // 使用模拟数据
207
       } finally {
215
       } finally {
208
         this.loading = false;
216
         this.loading = false;
209
       }
217
       }
210
     },
218
     },
211
     
219
     
220
+    // 加载广告
221
+    async loadAds() {
222
+      try {
223
+        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')
226
+        ]);
227
+        
228
+        this.topAds = topRes?.data || topRes || [];
229
+        this.bottomAds = bottomRes?.data || bottomRes || [];
230
+      } catch (e) {
231
+        console.error('加载广告失败', e);
232
+      }
233
+    },
212
     // 封面URL处理
234
     // 封面URL处理
213
     getCoverUrl(cover) {
235
     getCoverUrl(cover) {
214
       if (!cover) return '/static/default-cover.jpg';
236
       if (!cover) return '/static/default-cover.jpg';

+ 0
- 0
RuoYi-App/static/demo-cover1.jpg Vedi File


+ 0
- 0
RuoYi-App/static/demo-cover2.jpg Vedi File


+ 69
- 51
RuoYi-App/utils/request.js Vedi File

1
 // src/utils/request.js
1
 // src/utils/request.js
2
 import config from '@/config'
2
 import config from '@/config'
3
 const baseUrl = config.baseUrl
3
 const baseUrl = config.baseUrl
4
+// 使用直接导出的函数,避免 this 上下文问题
5
+export function get(url, params = {}, options = {}) {
6
+  return request('GET', url, params, options)
7
+}
8
+
9
+export function post(url, data = {}, options = {}) {
10
+  return request('POST', url, data, options)
11
+}
4
 
12
 
5
-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
-      // 确保URL以斜杠开头
17
-      let finalUrl = url.startsWith('/') ? url : `/${url}`;
18
-      
19
-      // 特殊处理Java API请求
20
-      if (url.startsWith('/java-api')) {
21
-        finalUrl = url;
13
+function request(method, url, data = {}, options = {}) {
14
+  return new Promise((resolve, reject) => {
15
+    // 处理路径
16
+    let finalUrl = url;
17
+    
18
+    // 特殊处理 Java API
19
+    if (url.startsWith('/java-api')) {
20
+        finalUrl = url
22
       }
21
       }
23
-      // 其他API添加前缀
24
-      else if (!url.startsWith('http')) {
25
-        finalUrl = '/api' + finalUrl;
22
+    // 普通 API 处理
23
+    else if (!url.startsWith('http')) {
24
+      // 确保只有一个 /api 前缀
25
+      if (!url.startsWith('/api')) {
26
+        finalUrl = '/api' + (url.startsWith('/') ? url : `/${url}`);
26
       }
27
       }
27
-      
28
-      const header = {
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({
36
+      url: fullUrl,
37
+      method,
38
+      data,
39
+      header: {
29
         'Content-Type': 'application/json',
40
         'Content-Type': 'application/json',
30
         ...(options.headers || {})
41
         ...(options.headers || {})
31
-      }
32
-      
33
-      // 添加认证token
34
-      const token = uni.getStorageSync('token')
35
-      if (token) {
36
-        header['Authorization'] = `Bearer ${token}`
37
-      }
38
-      
39
-      uni.request({
40
-        url: baseUrl + finalUrl,
41
-        method,
42
-        data,
43
-        header,
44
-        success: (res) => {
45
-          // 统一处理成功响应
46
-          if (res.statusCode >= 200 && res.statusCode < 300) {
47
-            resolve(res.data)
48
-          } else {
49
-            // 尝试提取错误消息
50
-            const errorMsg = res.data?.msg || res.data?.message || '请求失败';
51
-            reject(new Error(errorMsg))
52
-          }
53
-        },
54
-        fail: (err) => {
55
-          reject(err)
42
+      },
43
+      success: (res) => {
44
+        console.log('API响应:', res);
45
+        
46
+        // 处理未授权情况
47
+        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)
62
+        } else {
63
+          // 尝试提取错误消息
64
+          const errorMsg = res.data?.msg || res.data?.message || '请求失败';
65
+          reject(new Error(errorMsg))
56
         }
66
         }
57
-      })
67
+      },
68
+      fail: (err) => {
69
+        console.error('请求失败:', err);
70
+        reject(err)
71
+      }
58
     })
72
     })
59
-  }
73
+  })
60
 }
74
 }
61
 
75
 
62
-// 确保正确导出
63
-export default http
76
+// 导出对象以兼容旧代码
77
+export default {
78
+  get,
79
+  post,
80
+  request
81
+}

+ 6
- 8
RuoYi-App/vue.config.js Vedi File

1
 module.exports = {
1
 module.exports = {
2
   devServer: {
2
   devServer: {
3
     proxy: {
3
     proxy: {
4
-      // 代理到Java后台
5
-      '/java-api': {
6
-        target: 'http://localhost:8080',
4
+      '/java-api': {  // 确保匹配请求路径前缀
5
+        target: 'http://localhost:8080', // 替换为实际后端地址
7
         changeOrigin: true,
6
         changeOrigin: true,
8
-        pathRewrite: {'^/java-api': ''}
7
+        pathRewrite: { '^/java-api': '' } // 去除路径前缀
9
       },
8
       },
10
-      // 代理到PHP小说系统
11
-      '/php-api': {
12
-        target: 'http://php-novel-site',
9
+      '/api': {
10
+        target: 'http://localhost:8080',
13
         changeOrigin: true,
11
         changeOrigin: true,
14
-        pathRewrite: {'^/php-api': ''}
12
+        pathRewrite: { '^/api': '' }
15
       }
13
       }
16
     }
14
     }
17
   }
15
   }

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

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
 

Loading…
Annulla
Salva