|
|
@@ -25,59 +25,61 @@
|
|
25
|
25
|
</scroll-view>
|
|
26
|
26
|
|
|
27
|
27
|
<!-- 空状态 -->
|
|
28
|
|
- <view v-if="novels.length === 0 && !error" class="empty-container">
|
|
|
28
|
+ <view v-if="showEmptyState" class="empty-container">
|
|
29
|
29
|
<image src="/static/empty-book.png" class="empty-img" />
|
|
30
|
30
|
<text class="empty-text">暂无小说数据</text>
|
|
31
|
|
- <button class="btn-refresh" @click="reloadData">重新加载</button>
|
|
|
31
|
+ <button class="btn-refresh" @click="reloadData">重新加载</button>
|
|
32
|
32
|
</view>
|
|
33
|
33
|
|
|
34
|
34
|
<!-- 错误状态 -->
|
|
35
|
|
- <view v-if="error" class="error-container">
|
|
|
35
|
+ <view v-if="error && !showNovels" class="error-container">
|
|
36
|
36
|
<uni-icons type="info" size="48" color="var(--error-color)" />
|
|
37
|
37
|
<text class="error-text">{{ error }}</text>
|
|
38
|
38
|
<button class="btn-refresh" @click="loadNovels">重新加载</button>
|
|
39
|
39
|
</view>
|
|
40
|
40
|
|
|
41
|
41
|
<!-- 热门推荐 -->
|
|
42
|
|
- <view v-if="hotNovels.length > 0" class="section">
|
|
43
|
|
- <!-- ... -->
|
|
44
|
|
- </view>
|
|
45
|
|
- <view v-else class="section">
|
|
|
42
|
+ <view v-if="showHotNovels" class="section">
|
|
46
|
43
|
<view class="section-header">
|
|
47
|
|
- <text class="section-title">热门推荐(演示数据)</text>
|
|
|
44
|
+ <text class="section-title">热门推荐</text>
|
|
48
|
45
|
<text class="section-more" @click="goMore">更多 ></text>
|
|
49
|
46
|
</view>
|
|
50
|
47
|
<scroll-view scroll-x class="hot-list">
|
|
51
|
48
|
<view
|
|
52
|
|
- v-for="novel in demoHotNovels"
|
|
|
49
|
+ v-for="novel in displayedHotNovels"
|
|
53
|
50
|
:key="novel.id"
|
|
54
|
51
|
class="hot-item"
|
|
55
|
52
|
@click="openNovel(novel)"
|
|
56
|
53
|
>
|
|
57
|
|
- <image :src="novel.cover" class="hot-cover" />
|
|
|
54
|
+ <image
|
|
|
55
|
+ :src="getCoverUrl(novel.coverImg)"
|
|
|
56
|
+ class="hot-cover"
|
|
|
57
|
+ @error="handleImageError"
|
|
|
58
|
+ />
|
|
58
|
59
|
<text class="hot-title">{{ novel.title }}</text>
|
|
59
|
60
|
</view>
|
|
60
|
61
|
</scroll-view>
|
|
61
|
62
|
</view>
|
|
62
|
63
|
|
|
63
|
|
- <!-- 分类小说列表 -->
|
|
64
|
|
- <view v-if="novels.length > 0" class="section">
|
|
65
|
|
- <!-- ... -->
|
|
66
|
|
- </view>
|
|
67
|
|
- <view v-else class="section">
|
|
|
64
|
+ <!-- 小说列表 -->
|
|
|
65
|
+ <view v-if="showNovels" class="section">
|
|
68
|
66
|
<view class="section-header">
|
|
69
|
|
- <text class="section-title">小说列表(演示数据)</text>
|
|
|
67
|
+ <text class="section-title">小说列表</text>
|
|
70
|
68
|
</view>
|
|
71
|
69
|
<view class="novel-grid">
|
|
72
|
70
|
<view
|
|
73
|
|
- v-for="novel in demoNovels"
|
|
74
|
|
- :key="novel.id"
|
|
|
71
|
+ v-for="novel in displayedNovels"
|
|
|
72
|
+ :key="novel.id"
|
|
75
|
73
|
class="novel-item"
|
|
76
|
74
|
@click="openNovel(novel)"
|
|
77
|
75
|
>
|
|
78
|
|
- <image :src="novel.cover" class="novel-cover" />
|
|
79
|
|
- <text class="novel-title">{{ novel.title }}</text>
|
|
80
|
|
- <text class="novel-author">{{ novel.author }}</text>
|
|
|
76
|
+ <image
|
|
|
77
|
+ :src="getCoverUrl(novel.coverImg)"
|
|
|
78
|
+ class="novel-cover"
|
|
|
79
|
+ @error="handleImageError"
|
|
|
80
|
+ />
|
|
|
81
|
+ <text class="novel-title">{{ novel.title }}</text>
|
|
|
82
|
+ <text class="novel-author">{{ novel.author || '未知作者' }}</text>
|
|
81
|
83
|
</view>
|
|
82
|
84
|
</view>
|
|
83
|
85
|
</view>
|
|
|
@@ -94,9 +96,7 @@
|
|
94
|
96
|
</template>
|
|
95
|
97
|
|
|
96
|
98
|
<script>
|
|
97
|
|
-// 确保路径正确
|
|
98
|
99
|
import AdBanner from '@/components/AdBanner';
|
|
99
|
|
-import urls from '@/api/urls';
|
|
100
|
100
|
|
|
101
|
101
|
export default {
|
|
102
|
102
|
components: { AdBanner },
|
|
|
@@ -114,49 +114,66 @@ export default {
|
|
114
|
114
|
|
|
115
|
115
|
// 临时解决方案 - 模拟数据
|
|
116
|
116
|
mockHotNovels: [
|
|
117
|
|
- { id: 1, title: '总裁的替身前妻', cover: '/static/demo-cover1.jpg' },
|
|
118
|
|
- { id: 2, title: '神医毒妃', cover: '/static/demo-cover2.jpg' },
|
|
119
|
|
- { id: 3, title: '穿越之王妃逆袭', cover: '/static/demo-cover3.jpg' }
|
|
|
117
|
+ { id: 1, title: '总裁的替身前妻', coverImg: '/static/demo-cover1.jpg', author: '言情作家' },
|
|
|
118
|
+ { id: 2, title: '神医毒妃', coverImg: '/static/demo-cover2.jpg', author: '古风大神' },
|
|
|
119
|
+ { id: 3, title: '穿越之王妃逆袭', coverImg: '/static/demo-cover3.jpg', author: '穿越达人' }
|
|
120
|
120
|
],
|
|
121
|
121
|
mockNovels: [
|
|
122
|
|
- { id: 4, title: '都市最强赘婿', author: '网络作家', cover: '/static/demo-cover4.jpg' },
|
|
123
|
|
- { id: 5, title: '修仙归来', author: '仙侠迷', cover: '/static/demo-cover5.jpg' },
|
|
124
|
|
- { id: 6, title: '校园纯爱物语', author: '青春校园', cover: '/static/demo-cover6.jpg' }
|
|
125
|
|
- ]
|
|
|
122
|
+ { id: 4, title: '都市最强赘婿', author: '网络作家', coverImg: '/static/demo-cover4.jpg' },
|
|
|
123
|
+ { id: 5, title: '修仙归来', author: '仙侠迷', coverImg: '/static/demo-cover5.jpg' },
|
|
|
124
|
+ { id: 6, title: '校园纯爱物语', author: '青春校园', coverImg: '/static/demo-cover6.jpg' }
|
|
|
125
|
+ ],
|
|
|
126
|
+ // 添加分页参数
|
|
|
127
|
+ page: 1,
|
|
|
128
|
+ pageSize: 10,
|
|
|
129
|
+ total: 0,
|
|
|
130
|
+ hasMore: true
|
|
|
131
|
+ }
|
|
|
132
|
+ },
|
|
|
133
|
+ computed: {
|
|
|
134
|
+ // 添加计算属性控制显示逻辑
|
|
|
135
|
+ showEmptyState() {
|
|
|
136
|
+ return this.novels.length === 0 && !this.error && !this.loading;
|
|
|
137
|
+ },
|
|
|
138
|
+ showHotNovels() {
|
|
|
139
|
+ return this.hotNovels.length > 0 && !this.error;
|
|
|
140
|
+ },
|
|
|
141
|
+ showNovels() {
|
|
|
142
|
+ return this.novels.length > 0 && !this.error;
|
|
|
143
|
+ },
|
|
|
144
|
+ displayedHotNovels() {
|
|
|
145
|
+ return this.hotNovels.length > 0 ? this.hotNovels : this.mockHotNovels;
|
|
|
146
|
+ },
|
|
|
147
|
+ displayedNovels() {
|
|
|
148
|
+ return this.novels.length > 0 ? this.novels : this.mockNovels;
|
|
126
|
149
|
}
|
|
127
|
150
|
},
|
|
128
|
151
|
mounted() {
|
|
129
|
|
- console.log('$http available in component:', typeof this.$http.get === 'function');
|
|
130
|
|
-
|
|
131
|
|
- // 确保所有方法都已绑定
|
|
132
|
|
- console.log('loadCategories:', typeof this.loadCategories);
|
|
133
|
|
- console.log('loadHotNovels:', typeof this.loadHotNovels);
|
|
134
|
|
- console.log('loadNovels:', typeof this.loadNovels);
|
|
135
|
|
- console.log('loadAds:', typeof this.loadAds);
|
|
136
|
|
-
|
|
137
|
152
|
this.initData();
|
|
138
|
153
|
},
|
|
139
|
154
|
methods: {
|
|
140
|
155
|
async initData() {
|
|
141
|
|
- // 确保所有方法都已绑定
|
|
142
|
|
- if (
|
|
143
|
|
- typeof this.loadCategories !== 'function' ||
|
|
144
|
|
- typeof this.loadHotNovels !== 'function' ||
|
|
145
|
|
- typeof this.loadNovels !== 'function' ||
|
|
146
|
|
- typeof this.loadAds !== 'function'
|
|
147
|
|
- ) {
|
|
148
|
|
- console.error('一个或多个方法未绑定!');
|
|
149
|
|
- this.error = '系统初始化失败,请刷新页面';
|
|
150
|
|
- this.loading = false;
|
|
151
|
|
- return;
|
|
152
|
|
- }
|
|
153
|
|
-
|
|
154
|
156
|
try {
|
|
155
|
|
- // 顺序加载数据
|
|
156
|
|
- await this.loadCategories();
|
|
157
|
|
- await this.loadHotNovels();
|
|
158
|
|
- await this.loadNovels();
|
|
159
|
|
- await this.loadAds();
|
|
|
157
|
+ // 确保所有方法都已绑定
|
|
|
158
|
+ if (
|
|
|
159
|
+ typeof this.loadCategories !== 'function' ||
|
|
|
160
|
+ typeof this.loadHotNovels !== 'function' ||
|
|
|
161
|
+ typeof this.loadNovels !== 'function' ||
|
|
|
162
|
+ typeof this.loadAds !== 'function'
|
|
|
163
|
+ ) {
|
|
|
164
|
+ console.error('一个或多个方法未绑定!');
|
|
|
165
|
+ this.error = '系统初始化失败,请刷新页面';
|
|
|
166
|
+ this.loading = false;
|
|
|
167
|
+ return;
|
|
|
168
|
+ }
|
|
|
169
|
+
|
|
|
170
|
+ // 并行加载数据以提高性能
|
|
|
171
|
+ await Promise.all([
|
|
|
172
|
+ this.loadCategories(),
|
|
|
173
|
+ this.loadHotNovels(),
|
|
|
174
|
+ this.loadNovels(),
|
|
|
175
|
+ this.loadAds()
|
|
|
176
|
+ ]);
|
|
160
|
177
|
} catch (e) {
|
|
161
|
178
|
console.error('初始化失败', e);
|
|
162
|
179
|
this.error = '初始化失败,请检查网络连接';
|
|
|
@@ -164,33 +181,63 @@ export default {
|
|
164
|
181
|
this.loading = false;
|
|
165
|
182
|
}
|
|
166
|
183
|
},
|
|
167
|
|
- // 加载分类
|
|
|
184
|
+
|
|
|
185
|
+ // 修复API请求逻辑
|
|
168
|
186
|
async loadCategories() {
|
|
169
|
187
|
try {
|
|
170
|
|
- // 使用正确的参数格式
|
|
171
|
|
- const res = await this.$http.get('/category/tree', {
|
|
172
|
|
- header: { isToken: false }
|
|
173
|
|
- });
|
|
174
|
|
- this.categories = res && res.data ? [{ id: 0, name: '全部' }, ...res.data] : [];
|
|
|
188
|
+ const res = await this.$http.get('/category/tree', {
|
|
|
189
|
+ header: { isToken: false }
|
|
|
190
|
+ });
|
|
|
191
|
+
|
|
|
192
|
+ console.log('分类API响应:', res);
|
|
|
193
|
+
|
|
|
194
|
+ // 确保正确解析API响应
|
|
|
195
|
+ if (res && res.data && res.data.code === 200 && Array.isArray(res.data.data)) {
|
|
|
196
|
+ // 提取所有子分类(第二级分类)
|
|
|
197
|
+ let subCategories = [];
|
|
|
198
|
+ res.data.data.forEach(parent => {
|
|
|
199
|
+ if (parent.children && Array.isArray(parent.children)) {
|
|
|
200
|
+ subCategories = subCategories.concat(parent.children);
|
|
|
201
|
+ }
|
|
|
202
|
+ });
|
|
|
203
|
+
|
|
|
204
|
+ // 添加"全部"分类
|
|
|
205
|
+ this.categories = [
|
|
|
206
|
+ { id: 0, name: '全部' },
|
|
|
207
|
+ ...subCategories
|
|
|
208
|
+ ];
|
|
|
209
|
+ } else {
|
|
|
210
|
+ console.warn('分类数据结构异常,使用默认分类');
|
|
|
211
|
+ this.categories = [{ id: 0, name: '全部' }];
|
|
|
212
|
+ }
|
|
175
|
213
|
} catch (e) {
|
|
176
|
214
|
console.error('加载分类失败', e);
|
|
177
|
215
|
this.categories = [{ id: 0, name: '全部' }];
|
|
178
|
216
|
}
|
|
179
|
217
|
},
|
|
180
|
|
- // 加载热门小说
|
|
|
218
|
+
|
|
181
|
219
|
async loadHotNovels() {
|
|
182
|
220
|
try {
|
|
183
|
|
- // 公开API,不需要认证
|
|
184
|
|
- const res = await this.$http.get('/novel/hot', {
|
|
185
|
|
- header: { isToken: false }
|
|
186
|
|
- });
|
|
187
|
|
- // 处理响应
|
|
188
|
|
- if (res && res.data && res.data.rows) {
|
|
189
|
|
- this.hotNovels = res.data.rows;
|
|
190
|
|
- } else if (res && res.data && Array.isArray(res.data)) {
|
|
191
|
|
- this.hotNovels = res.data;
|
|
192
|
|
- } else {
|
|
193
|
|
- console.warn('热门小说API返回格式未知', res);
|
|
|
221
|
+ const res = await this.$http.get('/novel/hot', {
|
|
|
222
|
+ header: { isToken: false }
|
|
|
223
|
+ });
|
|
|
224
|
+
|
|
|
225
|
+ console.log('热门小说API响应:', res);
|
|
|
226
|
+
|
|
|
227
|
+ // 处理不同API响应格式
|
|
|
228
|
+ if (res && res.data) {
|
|
|
229
|
+ if (res.data.code === 200 && Array.isArray(res.data.rows)) {
|
|
|
230
|
+ this.hotNovels = res.data.rows;
|
|
|
231
|
+ } else if (Array.isArray(res.data)) {
|
|
|
232
|
+ this.hotNovels = res.data;
|
|
|
233
|
+ } else {
|
|
|
234
|
+ console.error('热门小说数据结构异常');
|
|
|
235
|
+ }
|
|
|
236
|
+ }
|
|
|
237
|
+
|
|
|
238
|
+ // 只在真实数据不可用时使用模拟数据
|
|
|
239
|
+ if (!this.hotNovels || this.hotNovels.length === 0) {
|
|
|
240
|
+ console.warn('使用模拟热门小说数据');
|
|
194
|
241
|
this.hotNovels = this.mockHotNovels;
|
|
195
|
242
|
}
|
|
196
|
243
|
} catch (e) {
|
|
|
@@ -198,66 +245,119 @@ export default {
|
|
198
|
245
|
this.hotNovels = this.mockHotNovels;
|
|
199
|
246
|
}
|
|
200
|
247
|
},
|
|
201
|
|
- // 加载小说列表
|
|
202
|
|
- async loadNovels() {
|
|
|
248
|
+
|
|
|
249
|
+ async loadNovels(reset = false) {
|
|
|
250
|
+ if (reset) {
|
|
|
251
|
+ this.page = 1;
|
|
|
252
|
+ this.hasMore = true;
|
|
|
253
|
+ this.novels = [];
|
|
|
254
|
+ }
|
|
|
255
|
+
|
|
|
256
|
+ if (!this.hasMore) return;
|
|
|
257
|
+
|
|
203
|
258
|
this.loading = true;
|
|
204
|
259
|
this.error = null;
|
|
|
260
|
+
|
|
205
|
261
|
try {
|
|
206
|
|
- // 使用正确的请求方式
|
|
207
|
|
- //const res = await this.$http.get('/novel/list');
|
|
208
|
|
- const res = await this.$http.get('/novel/list', {
|
|
209
|
|
- header: { isToken: false }
|
|
|
262
|
+ const res = await this.$http.get('/novel/list', {
|
|
|
263
|
+ params: {
|
|
|
264
|
+ page: this.page,
|
|
|
265
|
+ pageSize: this.pageSize,
|
|
|
266
|
+ categoryId: this.activeCategory
|
|
|
267
|
+ },
|
|
|
268
|
+ header: { isToken: false }
|
|
210
|
269
|
});
|
|
211
|
270
|
|
|
212
|
|
- // 修复可选链操作符问题
|
|
213
|
|
- if (res && res.data && res.data.rows) {
|
|
214
|
|
- this.novels = res.data.rows;
|
|
215
|
|
- } else if (res && res.data && Array.isArray(res.data)) {
|
|
216
|
|
- this.novels = res.data;
|
|
217
|
|
- } else {
|
|
218
|
|
- console.warn('小说列表API返回格式未知', res);
|
|
219
|
|
- this.novels = this.mockNovels;
|
|
|
271
|
+ console.log('小说列表API响应:', res);
|
|
|
272
|
+
|
|
|
273
|
+ let newNovels = [];
|
|
|
274
|
+ let total = 0;
|
|
|
275
|
+
|
|
|
276
|
+ // 处理不同API响应格式
|
|
|
277
|
+ if (res && res.data) {
|
|
|
278
|
+ if (res.data.code === 200 && Array.isArray(res.data.rows)) {
|
|
|
279
|
+ newNovels = res.data.rows;
|
|
|
280
|
+ total = res.data.total;
|
|
|
281
|
+ }
|
|
|
282
|
+ else if (Array.isArray(res.data)) {
|
|
|
283
|
+ newNovels = res.data;
|
|
|
284
|
+ total = res.data.length;
|
|
|
285
|
+ }
|
|
|
286
|
+ }
|
|
|
287
|
+
|
|
|
288
|
+ if (newNovels.length > 0) {
|
|
|
289
|
+ this.novels = [...this.novels, ...newNovels];
|
|
|
290
|
+ this.hasMore = this.novels.length < total;
|
|
|
291
|
+ this.page++;
|
|
|
292
|
+ } else if (this.page === 1) {
|
|
|
293
|
+ console.warn('小说列表为空');
|
|
|
294
|
+ this.novels = [];
|
|
220
|
295
|
}
|
|
221
|
296
|
} catch (e) {
|
|
222
|
297
|
console.error('加载小说失败', e);
|
|
223
|
298
|
this.error = '加载失败,请重试';
|
|
224
|
|
- this.novels = this.mockNovels;
|
|
|
299
|
+ if (this.novels.length === 0) {
|
|
|
300
|
+ this.novels = this.mockNovels;
|
|
|
301
|
+ }
|
|
225
|
302
|
} finally {
|
|
226
|
303
|
this.loading = false;
|
|
227
|
304
|
}
|
|
228
|
305
|
},
|
|
229
|
|
-
|
|
230
|
|
- // 加载广告
|
|
|
306
|
+
|
|
231
|
307
|
async loadAds() {
|
|
232
|
308
|
try {
|
|
|
309
|
+ // 使用Promise.all并行请求
|
|
233
|
310
|
const [topRes, bottomRes] = await Promise.all([
|
|
234
|
|
- this.$http.get('/ad/position?code=TOP_BANNER', {
|
|
235
|
|
- header: { isToken: false }
|
|
236
|
|
- }),
|
|
237
|
|
- this.$http.get('/ad/position?code=BOTTOM_BANNER', {
|
|
238
|
|
- header: { isToken: false }
|
|
239
|
|
- })
|
|
|
311
|
+ this.$http.get('/ad/position?code=TOP_BANNER', {
|
|
|
312
|
+ header: { isToken: false }
|
|
|
313
|
+ }),
|
|
|
314
|
+ this.$http.get('/ad/position?code=BOTTOM_BANNER', {
|
|
|
315
|
+ header: { isToken: false }
|
|
|
316
|
+ })
|
|
240
|
317
|
]);
|
|
241
|
318
|
|
|
242
|
|
- this.topAds = topRes && topRes.data ? topRes.data : [];
|
|
243
|
|
- this.bottomAds = bottomRes && bottomRes.data ? bottomRes.data : [];
|
|
|
319
|
+ // 处理广告响应
|
|
|
320
|
+ this.topAds = this.parseAdResponse(topRes);
|
|
|
321
|
+ this.bottomAds = this.parseAdResponse(bottomRes);
|
|
244
|
322
|
} catch (e) {
|
|
245
|
323
|
console.error('加载广告失败', e);
|
|
246
|
324
|
}
|
|
247
|
325
|
},
|
|
248
|
|
- // 封面URL处理
|
|
|
326
|
+
|
|
|
327
|
+ // 新增:广告响应解析方法
|
|
|
328
|
+ parseAdResponse(response) {
|
|
|
329
|
+ if (!response || !response.data) return [];
|
|
|
330
|
+
|
|
|
331
|
+ if (response.data.code === 200 && Array.isArray(response.data.data)) {
|
|
|
332
|
+ return response.data.data;
|
|
|
333
|
+ } else if (Array.isArray(response.data)) {
|
|
|
334
|
+ return response.data;
|
|
|
335
|
+ } else if (Array.isArray(response.data.rows)) {
|
|
|
336
|
+ return response.data.rows;
|
|
|
337
|
+ }
|
|
|
338
|
+
|
|
|
339
|
+ return [];
|
|
|
340
|
+ },
|
|
|
341
|
+
|
|
|
342
|
+ // 修复封面URL处理
|
|
249
|
343
|
getCoverUrl(cover) {
|
|
250
|
344
|
if (!cover) return '/static/default-cover.jpg';
|
|
251
|
345
|
if (cover.startsWith('http')) return cover;
|
|
252
|
346
|
if (cover.startsWith('/')) return cover;
|
|
253
|
|
- return `${process.env.VUE_APP_BASE_URL || ''}/uploads/${cover}`;
|
|
|
347
|
+ return `${process.env.VUE_APP_BASE_URL || ''}${cover}`;
|
|
|
348
|
+ },
|
|
|
349
|
+
|
|
|
350
|
+ // 添加图片错误处理
|
|
|
351
|
+ handleImageError(event) {
|
|
|
352
|
+ event.target.src = '/static/default-cover.jpg';
|
|
254
|
353
|
},
|
|
255
|
354
|
|
|
256
|
355
|
// 其他方法...
|
|
257
|
356
|
changeCategory(categoryId) {
|
|
258
|
357
|
this.activeCategory = categoryId;
|
|
259
|
|
- this.currentCategoryName = this.categories.find(c => c.id === categoryId)?.name || '全部';
|
|
260
|
|
- this.loadNovels();
|
|
|
358
|
+ const category = this.categories.find(c => c.id === categoryId);
|
|
|
359
|
+ this.currentCategoryName = category ? category.name : '全部';
|
|
|
360
|
+ this.loadNovels(true); // 重置并加载新分类
|
|
261
|
361
|
},
|
|
262
|
362
|
|
|
263
|
363
|
openNovel(novel) {
|
|
|
@@ -287,9 +387,15 @@ export default {
|
|
287
|
387
|
url: '/pages/novel/more'
|
|
288
|
388
|
});
|
|
289
|
389
|
},
|
|
290
|
|
- // 确保重新加载按钮绑定到正确的方法
|
|
|
390
|
+
|
|
|
391
|
+ // 确保重新加载按钮绑定到正确的方法
|
|
291
|
392
|
reloadData() {
|
|
292
|
393
|
this.initData();
|
|
|
394
|
+ },
|
|
|
395
|
+
|
|
|
396
|
+ // 添加上拉加载事件
|
|
|
397
|
+ onReachBottom() {
|
|
|
398
|
+ this.loadNovels();
|
|
293
|
399
|
}
|
|
294
|
400
|
}
|
|
295
|
401
|
}
|
|
|
@@ -358,16 +464,18 @@ export default {
|
|
358
|
464
|
}
|
|
359
|
465
|
|
|
360
|
466
|
.novel-item {
|
|
361
|
|
- background-color: var(--card-bg);
|
|
|
467
|
+ background-color: white;
|
|
362
|
468
|
border-radius: 12rpx;
|
|
363
|
469
|
overflow: hidden;
|
|
364
|
|
- box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
|
|
470
|
+ box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
|
365
|
471
|
}
|
|
366
|
472
|
|
|
367
|
|
-.novel-cover {
|
|
|
473
|
+/* 确保封面有默认背景和固定比例 */
|
|
|
474
|
+.novel-cover, .hot-cover {
|
|
368
|
475
|
width: 100%;
|
|
369
|
476
|
height: 300rpx;
|
|
370
|
|
- background-color: #f5f5f5; /* 添加默认背景 */
|
|
|
477
|
+ background-color: #f5f5f5;
|
|
|
478
|
+ object-fit: cover;
|
|
371
|
479
|
}
|
|
372
|
480
|
|
|
373
|
481
|
.novel-title {
|
|
|
@@ -385,4 +493,87 @@ export default {
|
|
385
|
493
|
color: #888;
|
|
386
|
494
|
padding: 0 15rpx 15rpx;
|
|
387
|
495
|
}
|
|
|
496
|
+
|
|
|
497
|
+/* 热门列表样式 */
|
|
|
498
|
+.hot-list {
|
|
|
499
|
+ display: flex;
|
|
|
500
|
+ padding: 20rpx;
|
|
|
501
|
+ overflow-x: auto;
|
|
|
502
|
+ white-space: nowrap;
|
|
|
503
|
+}
|
|
|
504
|
+
|
|
|
505
|
+.hot-item {
|
|
|
506
|
+ display: inline-block;
|
|
|
507
|
+ width: 200rpx;
|
|
|
508
|
+ margin-right: 30rpx;
|
|
|
509
|
+}
|
|
|
510
|
+
|
|
|
511
|
+.hot-cover {
|
|
|
512
|
+ width: 200rpx;
|
|
|
513
|
+ height: 280rpx;
|
|
|
514
|
+ border-radius: 8rpx;
|
|
|
515
|
+}
|
|
|
516
|
+
|
|
|
517
|
+.hot-title {
|
|
|
518
|
+ display: block;
|
|
|
519
|
+ margin-top: 10rpx;
|
|
|
520
|
+ font-size: 26rpx;
|
|
|
521
|
+ white-space: nowrap;
|
|
|
522
|
+ overflow: hidden;
|
|
|
523
|
+ text-overflow: ellipsis;
|
|
|
524
|
+}
|
|
|
525
|
+
|
|
|
526
|
+/* 分类导航样式 */
|
|
|
527
|
+.category-nav {
|
|
|
528
|
+ white-space: nowrap;
|
|
|
529
|
+ padding: 20rpx 0;
|
|
|
530
|
+ background-color: #fff;
|
|
|
531
|
+ border-bottom: 1rpx solid #eee;
|
|
|
532
|
+}
|
|
|
533
|
+
|
|
|
534
|
+.category-item {
|
|
|
535
|
+ display: inline-block;
|
|
|
536
|
+ padding: 10rpx 30rpx;
|
|
|
537
|
+ margin: 0 10rpx;
|
|
|
538
|
+ border-radius: 30rpx;
|
|
|
539
|
+ font-size: 28rpx;
|
|
|
540
|
+}
|
|
|
541
|
+
|
|
|
542
|
+.category-item.active {
|
|
|
543
|
+ background-color: var(--primary-color);
|
|
|
544
|
+ color: white;
|
|
|
545
|
+}
|
|
|
546
|
+
|
|
|
547
|
+/* 章节标题样式 */
|
|
|
548
|
+.section {
|
|
|
549
|
+ margin-top: 30rpx;
|
|
|
550
|
+}
|
|
|
551
|
+
|
|
|
552
|
+.section-header {
|
|
|
553
|
+ display: flex;
|
|
|
554
|
+ justify-content: space-between;
|
|
|
555
|
+ align-items: center;
|
|
|
556
|
+ padding: 0 20rpx 20rpx;
|
|
|
557
|
+}
|
|
|
558
|
+
|
|
|
559
|
+.section-title {
|
|
|
560
|
+ font-size: 32rpx;
|
|
|
561
|
+ font-weight: bold;
|
|
|
562
|
+}
|
|
|
563
|
+
|
|
|
564
|
+.section-more {
|
|
|
565
|
+ font-size: 26rpx;
|
|
|
566
|
+ color: var(--text-secondary-color);
|
|
|
567
|
+}
|
|
|
568
|
+
|
|
|
569
|
+/* 成为作家按钮样式 */
|
|
|
570
|
+.become-author {
|
|
|
571
|
+ margin: 40rpx 20rpx;
|
|
|
572
|
+ padding: 20rpx;
|
|
|
573
|
+ text-align: center;
|
|
|
574
|
+ background-color: var(--primary-color);
|
|
|
575
|
+ color: white;
|
|
|
576
|
+ border-radius: 10rpx;
|
|
|
577
|
+ font-size: 30rpx;
|
|
|
578
|
+}
|
|
388
|
579
|
</style>
|