index.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  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="getImageUrl(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. import config from '@/config.js'
  52. export default {
  53. data() {
  54. return {
  55. packages: [],
  56. keyword: '',
  57. currentCategory: '',
  58. categories: ['学习', '习惯', '健康', '情绪', '其他']
  59. }
  60. },
  61. onShow() {
  62. this.loadPackages()
  63. },
  64. methods: {
  65. async loadPackages() {
  66. try {
  67. const res = await getPublicPackages(this.currentCategory)
  68. this.packages = res.data || []
  69. } catch (e) {
  70. console.error(e)
  71. }
  72. },
  73. selectCategory(cat) {
  74. this.currentCategory = cat
  75. this.loadPackages()
  76. },
  77. search() {
  78. // 简单筛选
  79. if (this.keyword) {
  80. this.packages = this.packages.filter(p => p.name.includes(this.keyword))
  81. } else {
  82. this.loadPackages()
  83. }
  84. },
  85. goToDetail(id) {
  86. uni.navigateTo({
  87. url: `/pages/parent/market/detail?id=${id}`
  88. })
  89. },
  90. getImageUrl: function(path) {
  91. return config.API_BASE_URL + path
  92. }
  93. }
  94. }
  95. </script>
  96. <style scoped>
  97. .container { min-height: 100vh; background: #f5f5f5; }
  98. .filter-bar { background: #fff; padding: 20rpx; margin-bottom: 10rpx; }
  99. .search-box { margin-bottom: 20rpx; }
  100. .search-input { padding: 16rpx 20rpx; background: #f5f5f5; border-radius: 30rpx; font-size: 28rpx; }
  101. .categories { display: flex; gap: 20rpx; overflow-x: auto; }
  102. .cat-item { font-size: 26rpx; color: #666; padding: 10rpx 20rpx; white-space: nowrap; }
  103. .cat-item.active { color: #4CAF50; font-weight: bold; }
  104. .package-list { padding: 20rpx; display: flex; flex-direction: column; gap: 20rpx; }
  105. .package-card { background: #fff; border-radius: 16rpx; overflow: hidden; box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.05); }
  106. .package-cover { width: 100%; height: 300rpx; }
  107. .package-content { padding: 20rpx; }
  108. .package-name { font-size: 32rpx; font-weight: bold; color: #333; display: block; margin-bottom: 10rpx; }
  109. .package-desc { font-size: 26rpx; color: #999; display: block; margin-bottom: 16rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  110. .package-meta { display: flex; align-items: center; gap: 20rpx; }
  111. .meta-item { font-size: 24rpx; color: #666; }
  112. .price { font-size: 32rpx; color: #f44336; font-weight: bold; margin-left: auto; }
  113. .empty { display: flex; justify-content: center; padding: 100rpx; color: #999; }
  114. </style>