├── php-api/ # 改造后的PHP接口层 ├── java-ad-service/ # 若依框架微服务(广告+VIP+分账) ├── uniapp-reader/ # UniApp前端项目 │ ├── pages/ # 各端页面 │ └──
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

list.vue 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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. // 确保所有数组初始化为空数组
  69. novelList: [], // 存储小说目录数据
  70. topAds: [],
  71. bottomAds: [],
  72. categories: [],
  73. activeCategory: 0,
  74. hotNovels: [],
  75. novels: [],
  76. currentCategoryName: '全部'
  77. }
  78. },
  79. async onLoad() {
  80. await this.loadAds();
  81. await this.loadNovels();
  82. this.loadCategories();
  83. this.loadHotNovels();
  84. // this.loadNovels();
  85. },
  86. // onLoad() {
  87. // this.loadNovelList();
  88. // },
  89. methods: {
  90. // 从Java后台加载广告
  91. async loadAds() {
  92. const [topRes, bottomRes] = await Promise.all([
  93. this.$http.get('/java-api/ad/position?code=TOP_BANNER'),
  94. this.$http.get('/java-api/ad/position?code=BOTTOM_BANNER')
  95. ]);
  96. this.topAds = topRes.data;
  97. this.bottomAds = bottomRes.data;
  98. },
  99. applyAuthor() {
  100. if (!this.$store.getters.token) {
  101. uni.showToast({ title: '请先登录', icon: 'none' });
  102. uni.navigateTo({ url: '/pages/login' });
  103. return;
  104. }
  105. uni.navigateTo({ url: '/pages/author/apply' });
  106. },
  107. async loadCategories() {
  108. try {
  109. // 从Java后台获取分类
  110. const res = await this.$http.get('/category/tree');
  111. // 确保数据结构正确
  112. if (res.data && Array.isArray(res.data)) {
  113. // 添加"全部"选项
  114. this.categories = [{ id: 0, name: '全部' }];
  115. // 递归处理分类树
  116. const processCategories = (cats) => {
  117. cats.forEach(cat => {
  118. this.categories.push({
  119. id: cat.id,
  120. name: cat.title,
  121. children: cat.children
  122. });
  123. if (cat.children && cat.children.length) {
  124. processCategories(cat.children);
  125. }
  126. });
  127. };
  128. processCategories(res.data);
  129. } else {
  130. console.warn('分类数据格式不正确');
  131. }
  132. } catch (e) {
  133. console.error('加载分类失败', e);
  134. }
  135. },
  136. // 从PHP系统加载小说目录
  137. // async loadNovels() {
  138. // const res = await this.$http.get('/php-api/novel/list');
  139. // this.novelList = res.data;
  140. // },
  141. async loadHotNovels() {
  142. try {
  143. // 获取热门小说
  144. const res = await this.$http.get('/novel/hot');
  145. this.hotNovels = res.data;
  146. } catch (e) {
  147. console.error('加载热门小说失败', e);
  148. }
  149. },
  150. async loadNovels(categoryId = 0) {
  151. try {
  152. const url = categoryId
  153. ? `/novel/list?categoryId=${categoryId}`
  154. : '/novel/list';
  155. const res = await this.$http.get(url);
  156. // 确保数据结构正确
  157. if (res.rows && Array.isArray(res.rows)) {
  158. this.novels = res.rows;
  159. } else {
  160. console.warn('小说列表数据格式不正确');
  161. this.novels = [];
  162. }
  163. } catch (e) {
  164. uni.showToast({ title: '加载小说失败', icon: 'none' });
  165. }
  166. },
  167. changeCategory(categoryId) {
  168. this.activeCategory = categoryId;
  169. this.loadNovels(categoryId);
  170. },
  171. openNovel(novel) {
  172. uni.navigateTo({
  173. url: `/pages/novel/detail?id=${novel.id}`
  174. });
  175. },
  176. applyAuthor() {
  177. uni.navigateTo({
  178. url: '/pages/author/apply'
  179. });
  180. },
  181. // 打开小说详情页
  182. openNovel(novel) {
  183. uni.navigateTo({
  184. url: `/pages/novel/detail?id=${novel.id}&title=${encodeURIComponent(novel.title)}`
  185. });
  186. }
  187. }
  188. }
  189. </script>
  190. <style scoped>
  191. .become-author {
  192. position: fixed;
  193. bottom: 120rpx; /* 在TabBar上方 */
  194. left: 50%;
  195. transform: translateX(-50%);
  196. background-color: #3cc51f;
  197. color: white;
  198. padding: 16rpx 40rpx;
  199. border-radius: 50rpx;
  200. font-size: 28rpx;
  201. box-shadow: 0 4rpx 12rpx rgba(60, 197, 31, 0.3);
  202. z-index: 999;
  203. }
  204. .novel-list-page {
  205. padding: 20rpx;
  206. padding-bottom: 40rpx;
  207. }
  208. .category-nav {
  209. white-space: nowrap;
  210. margin-bottom: 30rpx;
  211. }
  212. .category-item {
  213. display: inline-block;
  214. padding: 10rpx 30rpx;
  215. margin-right: 20rpx;
  216. border-radius: 50rpx;
  217. background-color: #f5f5f5;
  218. font-size: 28rpx;
  219. }
  220. .category-item.active {
  221. background-color: #3cc51f;
  222. color: white;
  223. }
  224. .section {
  225. margin-bottom: 40rpx;
  226. }
  227. .section-header {
  228. display: flex;
  229. justify-content: space-between;
  230. align-items: center;
  231. margin-bottom: 20rpx;
  232. }
  233. .section-title {
  234. font-size: 32rpx;
  235. font-weight: bold;
  236. }
  237. .section-more {
  238. font-size: 24rpx;
  239. color: #888;
  240. }
  241. .hot-list {
  242. white-space: nowrap;
  243. }
  244. .hot-item {
  245. display: inline-block;
  246. width: 200rpx;
  247. margin-right: 20rpx;
  248. }
  249. .hot-cover {
  250. width: 200rpx;
  251. height: 280rpx;
  252. border-radius: 8rpx;
  253. }
  254. .hot-title {
  255. display: block;
  256. font-size: 26rpx;
  257. margin-top: 10rpx;
  258. white-space: nowrap;
  259. overflow: hidden;
  260. text-overflow: ellipsis;
  261. }
  262. .novel-grid {
  263. display: grid;
  264. grid-template-columns: repeat(3, 1fr);
  265. gap: 20rpx;
  266. }
  267. .novel-item {
  268. text-align: center;
  269. }
  270. .novel-cover {
  271. width: 100%;
  272. height: 300rpx;
  273. border-radius: 8rpx;
  274. }
  275. .novel-title {
  276. display: block;
  277. font-size: 26rpx;
  278. margin-top: 10rpx;
  279. overflow: hidden;
  280. text-overflow: ellipsis;
  281. display: -webkit-box;
  282. -webkit-line-clamp: 1;
  283. -webkit-box-orient: vertical;
  284. }
  285. .novel-author {
  286. display: block;
  287. font-size: 22rpx;
  288. color: #888;
  289. }
  290. .become-author {
  291. position: fixed;
  292. bottom: 120rpx; /* 在TabBar上方 */
  293. left: 50%;
  294. transform: translateX(-50%);
  295. background-color: #3cc51f;
  296. color: white;
  297. padding: 16rpx 40rpx;
  298. border-radius: 50rpx;
  299. font-size: 28rpx;
  300. box-shadow: 0 4rpx 12rpx rgba(60, 197, 31, 0.3);
  301. z-index: 999;
  302. }
  303. </style>