category.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. <template>
  2. <view class="container">
  3. <!-- 顶部导航栏 -->
  4. <view class="header">
  5. <view class="back-btn" @click="goBack">
  6. <text class="back-icon">←</text>
  7. </view>
  8. <text class="header-title">分类</text>
  9. <view class="placeholder"></view>
  10. </view>
  11. <!-- 分隔线 -->
  12. <view class="divider"></view>
  13. <!-- 分类列表 -->
  14. <scroll-view class="scroll-content" scroll-y>
  15. <!-- 加载中 -->
  16. <view class="loading-container" v-if="isLoading">
  17. <text class="loading-text">加载中...</text>
  18. </view>
  19. <!-- 分类列表 -->
  20. <view class="category-grid" v-else-if="categoryList.length > 0">
  21. <view
  22. class="category-card"
  23. v-for="(category, index) in categoryList"
  24. :key="category.id"
  25. @click="goToCategoryBooks(category)"
  26. :class="{ 'card-pressed': pressedIndex === index }"
  27. @touchstart="handleTouchStart(index)"
  28. @touchend="handleTouchEnd"
  29. >
  30. <image
  31. class="category-cover"
  32. :src="category.cover"
  33. mode="aspectFill"
  34. :lazy-load="true"
  35. @error="handleImageError(index)"
  36. ></image>
  37. <view class="category-info">
  38. <text class="category-title">{{ category.name }}</text>
  39. <text class="category-count">{{ category.count }}本书</text>
  40. </view>
  41. </view>
  42. </view>
  43. <!-- 空状态 -->
  44. <view class="empty-container" v-else>
  45. <text class="empty-text">暂无分类数据</text>
  46. </view>
  47. </scroll-view>
  48. </view>
  49. </template>
  50. <script>
  51. import { getAllCategories } from '../../utils/api.js'
  52. export default {
  53. data() {
  54. return {
  55. pressedIndex: -1,
  56. categoryList: [],
  57. isLoading: false
  58. }
  59. },
  60. onLoad() {
  61. // 页面加载时获取分类数据
  62. this.loadCategories();
  63. },
  64. methods: {
  65. goBack() {
  66. uni.navigateBack({
  67. delta: 1
  68. });
  69. },
  70. handleTouchStart(index) {
  71. this.pressedIndex = index;
  72. },
  73. handleTouchEnd() {
  74. setTimeout(() => {
  75. this.pressedIndex = -1;
  76. }, 150);
  77. },
  78. goToCategoryBooks(category) {
  79. // 根据分类名称判断跳转类型
  80. const categoryName = category.name;
  81. // 特殊分类:跳转到对应的特殊页面
  82. if (categoryName === '全部分类') {
  83. uni.navigateTo({
  84. url: '/pages/more-books/more-books?type=all'
  85. });
  86. } else if (categoryName === '排行榜') {
  87. uni.navigateTo({
  88. url: '/pages/more-books/more-books?type=ranking'
  89. });
  90. } else if (categoryName === '热门书籍') {
  91. uni.navigateTo({
  92. url: '/pages/more-books/more-books?type=popular'
  93. });
  94. } else if (categoryName === '新书榜') {
  95. uni.navigateTo({
  96. url: '/pages/more-books/more-books?type=new'
  97. });
  98. } else if (categoryName === 'VIP书籍') {
  99. uni.navigateTo({
  100. url: '/pages/more-books/more-books?type=vip'
  101. });
  102. } else {
  103. // 普通分类:跳转到该分类的书籍列表页面
  104. uni.navigateTo({
  105. url: `/pages/more-books/more-books?type=category&categoryId=${category.id}&categoryName=${encodeURIComponent(category.name)}`
  106. });
  107. }
  108. },
  109. handleImageError(index) {
  110. // 图片加载失败时使用备用图片
  111. if (this.categoryList[index]) {
  112. this.categoryList[index].cover = `https://picsum.photos/seed/fallback${index}/200/300`;
  113. }
  114. },
  115. loadCategories() {
  116. console.log('开始加载分类列表...');
  117. this.isLoading = true;
  118. getAllCategories()
  119. .then((res) => {
  120. console.log('分类列表API响应:', res);
  121. this.isLoading = false;
  122. if (res && res.code === 200) {
  123. if (res.data && Array.isArray(res.data)) {
  124. // 处理数据格式
  125. this.categoryList = res.data.map((category) => {
  126. return {
  127. id: category.id,
  128. name: category.name || '未知分类',
  129. count: category.bookCount || 0,
  130. cover: category.cover || `https://picsum.photos/seed/category${category.id}/200/300`,
  131. icon: category.icon,
  132. color: category.color
  133. };
  134. });
  135. console.log('分类列表加载成功,共', this.categoryList.length, '个分类');
  136. } else {
  137. console.warn('分类列表数据为空或格式不正确:', res.data);
  138. this.categoryList = [];
  139. uni.showToast({
  140. title: '暂无分类数据',
  141. icon: 'none',
  142. duration: 2000
  143. });
  144. }
  145. } else {
  146. console.warn('分类列表API返回错误:', res);
  147. this.categoryList = [];
  148. uni.showToast({
  149. title: res.message || '获取分类列表失败',
  150. icon: 'none',
  151. duration: 2000
  152. });
  153. }
  154. })
  155. .catch((err) => {
  156. this.isLoading = false;
  157. console.error('获取分类列表失败:', err);
  158. this.categoryList = [];
  159. uni.showToast({
  160. title: err.message || '网络请求失败,请检查后端服务',
  161. icon: 'none',
  162. duration: 3000
  163. });
  164. });
  165. }
  166. }
  167. }
  168. </script>
  169. <style scoped>
  170. .container {
  171. width: 100%;
  172. height: 100vh;
  173. background-color: #FFFFFF;
  174. display: flex;
  175. flex-direction: column;
  176. }
  177. /* 顶部导航栏 */
  178. .header {
  179. display: flex;
  180. align-items: center;
  181. justify-content: space-between;
  182. padding: 20rpx 30rpx;
  183. background-color: #FFFFFF;
  184. position: relative;
  185. }
  186. .back-btn {
  187. width: 60rpx;
  188. height: 60rpx;
  189. display: flex;
  190. align-items: center;
  191. justify-content: center;
  192. }
  193. .back-icon {
  194. font-size: 40rpx;
  195. color: #000000;
  196. font-weight: bold;
  197. }
  198. .header-title {
  199. font-size: 36rpx;
  200. font-weight: bold;
  201. color: #000000;
  202. position: absolute;
  203. left: 50%;
  204. transform: translateX(-50%);
  205. }
  206. .placeholder {
  207. width: 60rpx;
  208. }
  209. /* 分隔线 */
  210. .divider {
  211. width: 100%;
  212. height: 1rpx;
  213. background-color: #E5E5E5;
  214. }
  215. /* 滚动内容 */
  216. .scroll-content {
  217. flex: 1;
  218. width: 100%;
  219. height: 0;
  220. overflow: hidden;
  221. padding: 20rpx 30rpx;
  222. box-sizing: border-box;
  223. }
  224. /* 分类网格 */
  225. .category-grid {
  226. display: flex;
  227. flex-wrap: wrap;
  228. justify-content: space-between;
  229. }
  230. /* 分类卡片 */
  231. .category-card {
  232. width: calc(50% - 10rpx);
  233. height: 200rpx;
  234. background-color: #F5F5F5;
  235. border-radius: 16rpx;
  236. margin-bottom: 20rpx;
  237. display: flex;
  238. align-items: center;
  239. padding: 20rpx;
  240. box-sizing: border-box;
  241. transition: all 0.15s ease;
  242. overflow: hidden;
  243. position: relative;
  244. }
  245. .category-card:active,
  246. .category-card.card-pressed {
  247. transform: scale(0.98);
  248. background-color: #EEEEEE;
  249. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
  250. }
  251. /* 分类封面 */
  252. .category-cover {
  253. width: 100rpx;
  254. height: 140rpx;
  255. border-radius: 8rpx;
  256. margin-right: 20rpx;
  257. flex-shrink: 0;
  258. background-color: #E0E0E0;
  259. }
  260. /* 分类信息 */
  261. .category-info {
  262. flex: 1;
  263. display: flex;
  264. flex-direction: column;
  265. justify-content: center;
  266. min-width: 0;
  267. }
  268. .category-title {
  269. font-size: 32rpx;
  270. font-weight: bold;
  271. color: #000000;
  272. margin-bottom: 12rpx;
  273. overflow: hidden;
  274. text-overflow: ellipsis;
  275. white-space: nowrap;
  276. }
  277. .category-count {
  278. font-size: 24rpx;
  279. color: #999999;
  280. }
  281. /* 加载中 */
  282. .loading-container {
  283. width: 100%;
  284. height: 400rpx;
  285. display: flex;
  286. align-items: center;
  287. justify-content: center;
  288. }
  289. .loading-text {
  290. font-size: 28rpx;
  291. color: #999999;
  292. }
  293. /* 空状态 */
  294. .empty-container {
  295. width: 100%;
  296. height: 400rpx;
  297. display: flex;
  298. align-items: center;
  299. justify-content: center;
  300. }
  301. .empty-text {
  302. font-size: 28rpx;
  303. color: #999999;
  304. }
  305. </style>