index.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. <template>
  2. <view class="container">
  3. <!-- 搜索/分类 -->
  4. <view class="filter-bar">
  5. <view class="search-box">
  6. <input class="search-input" v-model="keyword" placeholder="搜索套餐" @confirm="search" />
  7. </view>
  8. <view class="categories">
  9. <text
  10. class="cat-item"
  11. :class="{ active: !currentCategory }"
  12. @click="selectCategory('')"
  13. >全部</text>
  14. <text
  15. class="cat-item"
  16. v-for="cat in categories"
  17. :key="cat"
  18. :class="{ active: currentCategory === cat }"
  19. @click="selectCategory(cat)"
  20. >{{ cat }}</text>
  21. </view>
  22. </view>
  23. <!-- 套餐列表 -->
  24. <view class="package-list">
  25. <view
  26. class="package-card"
  27. v-for="pkg in packages"
  28. :key="pkg.id"
  29. @click="goToDetail(pkg.id)"
  30. >
  31. <image class="package-cover" :src="pkg.coverImage || '/static/default-cover.png'" mode="aspectFill" />
  32. <view class="package-content">
  33. <text class="package-name">{{ pkg.name }}</text>
  34. <text class="package-desc">{{ pkg.description }}</text>
  35. <view class="package-meta">
  36. <text class="meta-item">{{ pkg.cycleDays }}天</text>
  37. <text class="meta-item">{{ pkg.category }}</text>
  38. <text class="price">{{ formatPriceWithSymbol(pkg.price) }}</text>
  39. </view>
  40. </view>
  41. </view>
  42. </view>
  43. <!-- 空状态 -->
  44. <view class="empty" v-if="packages.length === 0">
  45. <text>暂无套餐</text>
  46. </view>
  47. </view>
  48. </template>
  49. <script>
  50. import { getPublicPackages } from '@/utils/api.js'
  51. export default {
  52. data() {
  53. return {
  54. packages: [],
  55. keyword: '',
  56. currentCategory: '',
  57. categories: ['学习', '习惯', '健康', '情绪', '其他']
  58. }
  59. },
  60. onShow() {
  61. this.loadPackages()
  62. },
  63. methods: {
  64. async loadPackages() {
  65. try {
  66. const res = await getPublicPackages(this.currentCategory)
  67. this.packages = res.data || []
  68. } catch (e) {
  69. console.error(e)
  70. }
  71. },
  72. selectCategory(cat) {
  73. this.currentCategory = cat
  74. this.loadPackages()
  75. },
  76. search() {
  77. // 简单筛选
  78. if (this.keyword) {
  79. this.packages = this.packages.filter(p => p.name.includes(this.keyword))
  80. } else {
  81. this.loadPackages()
  82. }
  83. },
  84. goToDetail(id) {
  85. uni.navigateTo({
  86. url: `/pages/parent/market/detail?id=${id}`
  87. })
  88. }
  89. }
  90. }
  91. </script>
  92. <style scoped>
  93. .container { min-height: 100vh; background: #f5f5f5; }
  94. .filter-bar { background: #fff; padding: 20rpx; margin-bottom: 10rpx; }
  95. .search-box { margin-bottom: 20rpx; }
  96. .search-input { padding: 16rpx 20rpx; background: #f5f5f5; border-radius: 30rpx; font-size: 28rpx; }
  97. .categories { display: flex; gap: 20rpx; overflow-x: auto; }
  98. .cat-item { font-size: 26rpx; color: #666; padding: 10rpx 20rpx; white-space: nowrap; }
  99. .cat-item.active { color: #4CAF50; font-weight: bold; }
  100. .package-list { padding: 20rpx; display: flex; flex-direction: column; gap: 20rpx; }
  101. .package-card { background: #fff; border-radius: 16rpx; overflow: hidden; box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.05); }
  102. .package-cover { width: 100%; height: 300rpx; }
  103. .package-content { padding: 20rpx; }
  104. .package-name { font-size: 32rpx; font-weight: bold; color: #333; display: block; margin-bottom: 10rpx; }
  105. .package-desc { font-size: 26rpx; color: #999; display: block; margin-bottom: 16rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  106. .package-meta { display: flex; align-items: center; gap: 20rpx; }
  107. .meta-item { font-size: 24rpx; color: #666; }
  108. .price { font-size: 32rpx; color: #f44336; font-weight: bold; margin-left: auto; }
  109. .empty { display: flex; justify-content: center; padding: 100rpx; color: #999; }
  110. </style>