├── php-api/ # 改造后的PHP接口层 ├── java-ad-service/ # 若依框架微服务(广告+VIP+分账) ├── uniapp-reader/ # UniApp前端项目 │ ├── pages/ # 各端页面 │ └──
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. <template>
  2. <view class="novel-list-page">
  3. <!-- 顶部广告(来自Java后台) -->
  4. <ad-banner :ads="topAds" />
  5. <!-- 小说列表 -->
  6. <!-- 顶部分类导航 -->
  7. <scroll-view scroll-x class="category-nav">
  8. <view
  9. v-for="category in categories"
  10. :key="category.id"
  11. class="category-item"
  12. :class="{ active: activeCategory === category.id }"
  13. @click="changeCategory(category.id)"
  14. >
  15. {{ category.name }}
  16. </view>
  17. </scroll-view>
  18. <!-- 热门推荐 -->
  19. <view class="section">
  20. <view class="section-header">
  21. <text class="section-title">热门推荐</text>
  22. <text class="section-more">更多 ></text>
  23. </view>
  24. <scroll-view scroll-x class="hot-list">
  25. <view
  26. v-for="novel in hotNovels"
  27. :key="novel.id"
  28. class="hot-item"
  29. @click="openNovel(novel)"
  30. >
  31. <image :src="novel.cover" class="hot-cover" />
  32. <text class="hot-title">{{ novel.title }}</text>
  33. </view>
  34. </scroll-view>
  35. </view>
  36. <!-- 分类小说列表 -->
  37. <view class="section">
  38. <view class="section-header">
  39. <text class="section-title">{{ currentCategoryName }}作品</text>
  40. </view>
  41. <view class="novel-grid">
  42. <view
  43. v-for="novel in novels"
  44. :key="novel.id"
  45. class="novel-item"
  46. @click="openNovel(novel)"
  47. >
  48. <image :src="novel.cover" class="novel-cover" />
  49. <text class="novel-title">{{ novel.title }}</text>
  50. <text class="novel-author">{{ novel.author }}</text>
  51. </view>
  52. </view>
  53. </view>
  54. <!-- 成为签约作家按钮 -->
  55. <view class="become-author" @click="applyAuthor">
  56. <text>成为签约作家,发布你的作品</text>
  57. </view>
  58. <!-- 底部广告 -->
  59. <ad-banner :ads="bottomAds" />
  60. </view>
  61. </template>
  62. <script>
  63. import AdBanner from '@/components/AdBanner';
  64. export default {
  65. components: { AdBanner },
  66. data() {
  67. return {
  68. novelList: [], // 存储小说目录数据
  69. topAds: [],
  70. bottomAds: [],
  71. categories: [],
  72. activeCategory: 0,
  73. hotNovels: [],
  74. novels: [],
  75. currentCategoryName: '全部'
  76. }
  77. },
  78. async onLoad() {
  79. await this.loadAds();
  80. await this.loadNovels();
  81. this.loadCategories();
  82. this.loadHotNovels();
  83. // this.loadNovels();
  84. },
  85. // onLoad() {
  86. // this.loadNovelList();
  87. // },
  88. methods: {
  89. // 从Java后台加载广告
  90. async loadAds() {
  91. const [topRes, bottomRes] = await Promise.all([
  92. this.$http.get('/java-api/ad/position?code=TOP_BANNER'),
  93. this.$http.get('/java-api/ad/position?code=BOTTOM_BANNER')
  94. ]);
  95. this.topAds = topRes.data;
  96. this.bottomAds = bottomRes.data;
  97. },
  98. applyAuthor() {
  99. if (!this.$store.getters.token) {
  100. uni.showToast({ title: '请先登录', icon: 'none' });
  101. uni.navigateTo({ url: '/pages/login' });
  102. return;
  103. }
  104. uni.navigateTo({ url: '/pages/author/apply' });
  105. },
  106. async loadCategories() {
  107. try {
  108. // 从Java后台获取分类
  109. const res = await this.$http.get('/novel/category/list');
  110. this.categories = [{ id: 0, name: '全部' }, ...res.data];
  111. } catch (e) {
  112. console.error('加载分类失败', e);
  113. }
  114. },
  115. // 从PHP系统加载小说目录
  116. // async loadNovels() {
  117. // const res = await this.$http.get('/php-api/novel/list');
  118. // this.novelList = res.data;
  119. // },
  120. async loadHotNovels() {
  121. try {
  122. // 获取热门小说
  123. const res = await this.$http.get('/novel/hot');
  124. this.hotNovels = res.data;
  125. } catch (e) {
  126. console.error('加载热门小说失败', e);
  127. }
  128. },
  129. async loadNovels(categoryId = 0) {
  130. try {
  131. const url = categoryId
  132. ? `/novel/list?categoryId=${categoryId}`
  133. : '/novel/list';
  134. const res = await this.$http.get(url);
  135. this.novels = res.data;
  136. // 更新当前分类名称
  137. if (categoryId === 0) {
  138. this.currentCategoryName = '全部';
  139. } else {
  140. const category = this.categories.find(c => c.id === categoryId);
  141. this.currentCategoryName = category ? category.name : '';
  142. }
  143. } catch (e) {
  144. uni.showToast({ title: '加载小说失败', icon: 'none' });
  145. }
  146. },
  147. changeCategory(categoryId) {
  148. this.activeCategory = categoryId;
  149. this.loadNovels(categoryId);
  150. },
  151. openNovel(novel) {
  152. uni.navigateTo({
  153. url: `/pages/novel/detail?id=${novel.id}`
  154. });
  155. },
  156. applyAuthor() {
  157. uni.navigateTo({
  158. url: '/pages/author/apply'
  159. });
  160. },
  161. // 打开小说详情页
  162. openNovel(novel) {
  163. uni.navigateTo({
  164. url: `/pages/novel/detail?id=${novel.id}&title=${encodeURIComponent(novel.title)}`
  165. });
  166. }
  167. }
  168. }
  169. </script>
  170. <style scoped>
  171. .become-author {
  172. position: fixed;
  173. bottom: 120rpx; /* 在TabBar上方 */
  174. left: 50%;
  175. transform: translateX(-50%);
  176. background-color: #3cc51f;
  177. color: white;
  178. padding: 16rpx 40rpx;
  179. border-radius: 50rpx;
  180. font-size: 28rpx;
  181. box-shadow: 0 4rpx 12rpx rgba(60, 197, 31, 0.3);
  182. z-index: 999;
  183. }
  184. .novel-list-page {
  185. padding: 20rpx;
  186. padding-bottom: 40rpx;
  187. }
  188. .category-nav {
  189. white-space: nowrap;
  190. margin-bottom: 30rpx;
  191. }
  192. .category-item {
  193. display: inline-block;
  194. padding: 10rpx 30rpx;
  195. margin-right: 20rpx;
  196. border-radius: 50rpx;
  197. background-color: #f5f5f5;
  198. font-size: 28rpx;
  199. }
  200. .category-item.active {
  201. background-color: #3cc51f;
  202. color: white;
  203. }
  204. .section {
  205. margin-bottom: 40rpx;
  206. }
  207. .section-header {
  208. display: flex;
  209. justify-content: space-between;
  210. align-items: center;
  211. margin-bottom: 20rpx;
  212. }
  213. .section-title {
  214. font-size: 32rpx;
  215. font-weight: bold;
  216. }
  217. .section-more {
  218. font-size: 24rpx;
  219. color: #888;
  220. }
  221. .hot-list {
  222. white-space: nowrap;
  223. }
  224. .hot-item {
  225. display: inline-block;
  226. width: 200rpx;
  227. margin-right: 20rpx;
  228. }
  229. .hot-cover {
  230. width: 200rpx;
  231. height: 280rpx;
  232. border-radius: 8rpx;
  233. }
  234. .hot-title {
  235. display: block;
  236. font-size: 26rpx;
  237. margin-top: 10rpx;
  238. white-space: nowrap;
  239. overflow: hidden;
  240. text-overflow: ellipsis;
  241. }
  242. .novel-grid {
  243. display: grid;
  244. grid-template-columns: repeat(3, 1fr);
  245. gap: 20rpx;
  246. }
  247. .novel-item {
  248. text-align: center;
  249. }
  250. .novel-cover {
  251. width: 100%;
  252. height: 300rpx;
  253. border-radius: 8rpx;
  254. }
  255. .novel-title {
  256. display: block;
  257. font-size: 26rpx;
  258. margin-top: 10rpx;
  259. overflow: hidden;
  260. text-overflow: ellipsis;
  261. display: -webkit-box;
  262. -webkit-line-clamp: 1;
  263. -webkit-box-orient: vertical;
  264. }
  265. .novel-author {
  266. display: block;
  267. font-size: 22rpx;
  268. color: #888;
  269. }
  270. .become-author {
  271. position: fixed;
  272. bottom: 120rpx; /* 在TabBar上方 */
  273. left: 50%;
  274. transform: translateX(-50%);
  275. background-color: #3cc51f;
  276. color: white;
  277. padding: 16rpx 40rpx;
  278. border-radius: 50rpx;
  279. font-size: 28rpx;
  280. box-shadow: 0 4rpx 12rpx rgba(60, 197, 31, 0.3);
  281. z-index: 999;
  282. }
  283. </style>