index.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. <template>
  2. <view class="container">
  3. <!-- 搜索栏 -->
  4. <view class="search-bar">
  5. <image class="search-icon" src="/static/search-icon.png"></image>
  6. <input class="search-input" placeholder="搜索课程" v-model="searchText"></input>
  7. </view>
  8. <!-- 动态分类导航 -->
  9. <view class="category-nav">
  10. <view class="category-item"
  11. :class="{active: currentCategory === item.name}"
  12. v-for="item in categories"
  13. :key="item.id"
  14. @click="changeCategory(item.name)">
  15. {{item.name}}
  16. </view>
  17. </view>
  18. <!-- 课程列表 -->
  19. <view class="course-list">
  20. <view class="course-item" v-for="(course, index) in filteredCourses" :key="index" @click="goToCourseDetail(course)">
  21. <image class="course-image" :src="course.imageUrl"></image>
  22. <text class="course-title">{{course.title}}</text>
  23. <text class="course-info">{{course.description}}</text>
  24. <text class="course-price">¥{{course.price}}</text>
  25. </view>
  26. </view>
  27. <!-- 加载状态 -->
  28. <view class="loading" v-if="loading">
  29. <text>加载中...</text>
  30. </view>
  31. </view>
  32. </template>
  33. <script>
  34. export default {
  35. data() {
  36. return {
  37. time: '18:31',
  38. speed: '70.0',
  39. battery: '36',
  40. courses: [],
  41. categories: [],
  42. currentCategory: '', // 初始为空,等待数据加载
  43. searchText: '',
  44. loading: true
  45. };
  46. },
  47. onLoad() {
  48. this.fetchData();
  49. },
  50. computed: {
  51. filteredCourses() {
  52. if (!Array.isArray(this.courses)) {
  53. return [];
  54. }
  55. // 如果当前分类为空,显示全部课程
  56. if (!this.currentCategory) {
  57. return this.courses;
  58. }
  59. return this.courses
  60. .filter(course => {
  61. return course.category === this.currentCategory;
  62. })
  63. .filter(course => {
  64. return course.title.toLowerCase().includes(this.searchText.toLowerCase());
  65. });
  66. }
  67. },
  68. methods: {
  69. async fetchData() {
  70. try {
  71. // 并行请求分类和课程数据
  72. const [categoriesRes, coursesRes] = await Promise.all([
  73. uni.request({ url: 'http://localhost:8080/categories', method: 'GET' }),
  74. uni.request({ url: 'http://localhost:8080/courses', method: 'GET' })
  75. ]);
  76. if (categoriesRes.statusCode === 200 && coursesRes.statusCode === 200) {
  77. this.categories = categoriesRes.data || [];
  78. this.courses = coursesRes.data || [];
  79. // 如果有分类数据,设置第一个分类为当前选中
  80. if (this.categories.length > 0) {
  81. this.currentCategory = this.categories[0].name;
  82. }
  83. }
  84. } catch (error) {
  85. console.error('获取数据失败:', error);
  86. uni.showToast({
  87. title: '加载失败,请重试',
  88. icon: 'none'
  89. });
  90. } finally {
  91. this.loading = false;
  92. }
  93. },
  94. changeCategory(category) {
  95. this.currentCategory = category;
  96. },
  97. goToCourseDetail(course) {
  98. uni.navigateTo({
  99. url: `/pages/course-detail/index?id=${course.id}`
  100. });
  101. },
  102. goToPage(page) {
  103. uni.navigateTo({
  104. url: `/${page}`
  105. });
  106. }
  107. }
  108. };
  109. </script>
  110. <style>
  111. .loading {
  112. display: flex;
  113. justify-content: center;
  114. align-items: center;
  115. height: 100rpx;
  116. font-size: 32rpx;
  117. color: #999;
  118. }
  119. .search-bar {
  120. display: flex;
  121. align-items: center;
  122. padding: 20rpx;
  123. background-color: #ebf4ff;
  124. }
  125. .search-icon {
  126. width: 30rpx; /* 原尺寸为40rpx,现减小为30rpx */
  127. height: 30rpx; /* 原尺寸为40rpx,现减小为30rpx */
  128. margin-right: 10rpx;
  129. }
  130. .search-input {
  131. flex: 1;
  132. height: 60rpx;
  133. border: none;
  134. border-radius: 30rpx;
  135. padding: 0 20rpx;
  136. font-size: 30rpx;
  137. }
  138. </style>