hookehuyr

feat(积分): 新增积分攻略列表页面及相关功能

添加积分攻略列表页面,包含以下功能:
- 积分攻略分类展示和筛选
- 搜索功能
- 攻略详情弹窗展示
- 从积分详情页跳转到列表页
- 添加NutToast组件支持
...@@ -17,6 +17,7 @@ declare module 'vue' { ...@@ -17,6 +17,7 @@ declare module 'vue' {
17 NutInput: typeof import('@nutui/nutui-taro')['Input'] 17 NutInput: typeof import('@nutui/nutui-taro')['Input']
18 NutPicker: typeof import('@nutui/nutui-taro')['Picker'] 18 NutPicker: typeof import('@nutui/nutui-taro')['Picker']
19 NutPopup: typeof import('@nutui/nutui-taro')['Popup'] 19 NutPopup: typeof import('@nutui/nutui-taro')['Popup']
20 + NutToast: typeof import('@nutui/nutui-taro')['Toast']
20 Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] 21 Picker: typeof import('./src/components/time-picker-data/picker.vue')['default']
21 PointsCollector: typeof import('./src/components/PointsCollector.vue')['default'] 22 PointsCollector: typeof import('./src/components/PointsCollector.vue')['default']
22 PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default'] 23 PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default']
......
...@@ -28,6 +28,7 @@ export default { ...@@ -28,6 +28,7 @@ export default {
28 'pages/EditFamily/index', 28 'pages/EditFamily/index',
29 'pages/AlbumList/index', 29 'pages/AlbumList/index',
30 'pages/ActivitiesCover/index', 30 'pages/ActivitiesCover/index',
31 + 'pages/PointsList/index',
31 ], 32 ],
32 window: { 33 window: {
33 backgroundTextStyle: 'light', 34 backgroundTextStyle: 'light',
......
...@@ -14,9 +14,9 @@ ...@@ -14,9 +14,9 @@
14 <view class="bg-white rounded-t-3xl px-4 pt-5"> 14 <view class="bg-white rounded-t-3xl px-4 pt-5">
15 <view class="flex justify-between items-center mb-4"> 15 <view class="flex justify-between items-center mb-4">
16 <h3 class="text-lg font-medium">积分攻略</h3> 16 <h3 class="text-lg font-medium">积分攻略</h3>
17 - <view class="text-blue-500 text-sm flex items-center"> 17 + <view @tap="handleViewAll" class="text-blue-500 text-sm flex items-center">
18 查看全部 18 查看全部
19 - <Right size="16" /> 19 + <!-- <Right size="16" /> -->
20 </view> 20 </view>
21 </view> 21 </view>
22 <!-- Strategy cards --> 22 <!-- Strategy cards -->
...@@ -90,6 +90,7 @@ ...@@ -90,6 +90,7 @@
90 90
91 <script setup> 91 <script setup>
92 import { ref, computed } from 'vue'; 92 import { ref, computed } from 'vue';
93 +import Taro from '@tarojs/taro';
93 import AppHeader from '../../components/AppHeader.vue'; 94 import AppHeader from '../../components/AppHeader.vue';
94 import BottomNav from '../../components/BottomNav.vue'; 95 import BottomNav from '../../components/BottomNav.vue';
95 import { Right, My } from '@nutui/icons-vue-taro'; 96 import { Right, My } from '@nutui/icons-vue-taro';
...@@ -140,4 +141,10 @@ const filteredPoints = computed(() => { ...@@ -140,4 +141,10 @@ const filteredPoints = computed(() => {
140 } 141 }
141 return pointsHistory.value.filter(p => p.type === activeTab.value); 142 return pointsHistory.value.filter(p => p.type === activeTab.value);
142 }); 143 });
144 +
145 +const handleViewAll = () => {
146 + Taro.navigateTo({
147 + url: '/pages/PointsList/index'
148 + })
149 +}
143 </script> 150 </script>
......
1 +export default {
2 + navigationBarTitleText: '积分攻略',
3 + navigationBarBackgroundColor: '#ffffff',
4 + navigationBarTextStyle: 'black',
5 + backgroundColor: '#f5f5f5',
6 + enablePullDownRefresh: false
7 +}
...\ No newline at end of file ...\ No newline at end of file
1 +.points-list-page {
2 + width: 100%;
3 + min-height: 100vh;
4 + background-color: #f5f5f5;
5 + display: flex;
6 + flex-direction: column;
7 +}
8 +
9 +// 搜索区域
10 +.search-section {
11 + padding: 32rpx;
12 + background-color: white;
13 + border-bottom: 1rpx solid #f0f0f0;
14 +}
15 +
16 +.search-box {
17 + position: relative;
18 + background-color: #f8f9fa;
19 + border-radius: 48rpx;
20 + padding: 0 32rpx;
21 + height: 80rpx;
22 + display: flex;
23 + align-items: center;
24 +}
25 +
26 +.search-input {
27 + flex: 1;
28 + height: 100%;
29 + font-size: 28rpx;
30 + color: #333;
31 + background: transparent;
32 + border: none;
33 + outline: none;
34 +
35 + &::placeholder {
36 + color: #999;
37 + }
38 +}
39 +
40 +// 分类区域
41 +.category-section {
42 + padding: 32rpx;
43 + background-color: white;
44 + border-bottom: 1rpx solid #f0f0f0;
45 +}
46 +
47 +.category-title {
48 + font-size: 32rpx;
49 + font-weight: bold;
50 + color: #333;
51 + margin-bottom: 24rpx;
52 +}
53 +
54 +.category-grid {
55 + display: flex;
56 + flex-wrap: wrap;
57 + gap: 16rpx;
58 +}
59 +
60 +.category-item {
61 + padding: 16rpx 32rpx;
62 + background-color: #f8f9fa;
63 + border-radius: 32rpx;
64 + border: 2rpx solid transparent;
65 + transition: all 0.3s ease;
66 +
67 + &.active {
68 + background-color: #e6f7ff;
69 + border-color: #1890ff;
70 +
71 + .category-name {
72 + color: #1890ff;
73 + font-weight: bold;
74 + }
75 + }
76 +}
77 +
78 +.category-name {
79 + font-size: 26rpx;
80 + color: #666;
81 + transition: color 0.3s ease;
82 +}
83 +
84 +// 列表区域
85 +.points-list-section {
86 + flex: 1;
87 + background-color: white;
88 +}
89 +
90 +.points-list {
91 + width: 100%;
92 +}
93 +
94 +.points-items {
95 + padding: 0 32rpx;
96 +}
97 +
98 +.points-item {
99 + display: flex;
100 + align-items: center;
101 + padding: 32rpx 0;
102 + border-bottom: 1rpx solid #f0f0f0;
103 + transition: background-color 0.3s ease;
104 +
105 + &:active {
106 + background-color: #f8f9fa;
107 + }
108 +
109 + &:last-child {
110 + border-bottom: none;
111 + }
112 +}
113 +
114 +.points-item-left {
115 + flex: 1;
116 + margin-right: 24rpx;
117 +}
118 +
119 +.points-content {
120 + display: flex;
121 + flex-direction: column;
122 + gap: 12rpx;
123 +}
124 +
125 +.points-title {
126 + font-size: 32rpx;
127 + font-weight: bold;
128 + color: #333;
129 + line-height: 1.4;
130 +}
131 +
132 +.points-desc {
133 + font-size: 26rpx;
134 + color: #666;
135 + line-height: 1.5;
136 +}
137 +
138 +.points-reward {
139 + display: flex;
140 + align-items: center;
141 +}
142 +
143 +.reward-text {
144 + font-size: 24rpx;
145 + color: #52c41a;
146 + background-color: #f6ffed;
147 + padding: 8rpx 16rpx;
148 + border-radius: 16rpx;
149 + border: 1rpx solid #b7eb8f;
150 +}
151 +
152 +.points-item-right {
153 + display: flex;
154 + align-items: center;
155 +}
156 +
157 +.arrow-text {
158 + font-size: 32rpx;
159 + color: #ccc;
160 +}
161 +
162 +// 空状态
163 +.empty-state {
164 + display: flex;
165 + flex-direction: column;
166 + align-items: center;
167 + justify-content: center;
168 + padding: 120rpx 32rpx;
169 +}
170 +
171 +.empty-text {
172 + font-size: 28rpx;
173 + color: #999;
174 + margin-top: 24rpx;
175 +}
176 +
177 +// 详情弹窗
178 +.detail-popup {
179 + width: 100%;
180 + height: 100%;
181 + background-color: white;
182 + display: flex;
183 + flex-direction: column;
184 +}
185 +
186 +.detail-header {
187 + display: flex;
188 + align-items: center;
189 + justify-content: space-between;
190 + padding: 32rpx;
191 + border-bottom: 1rpx solid #f0f0f0;
192 + background-color: white;
193 + position: sticky;
194 + top: 0;
195 + z-index: 10;
196 +}
197 +
198 +.detail-title {
199 + font-size: 36rpx;
200 + font-weight: bold;
201 + color: #333;
202 + flex: 1;
203 + margin-right: 24rpx;
204 +}
205 +
206 +.close-btn1 {
207 + padding: 16rpx 24rpx;
208 + background-color: #f0f0f0;
209 + border-radius: 24rpx;
210 + transition: background-color 0.3s ease;
211 +
212 + &:active {
213 + background-color: #e0e0e0;
214 + }
215 +}
216 +
217 +.close-text {
218 + font-size: 26rpx;
219 + color: #666;
220 +}
221 +
222 +.detail-content {
223 + flex: 1;
224 +}
225 +
226 +.detail-body {
227 + padding: 32rpx;
228 +}
229 +
230 +.detail-section {
231 + margin-bottom: 48rpx;
232 +
233 + &:last-child {
234 + margin-bottom: 0;
235 + }
236 +}
237 +
238 +.section-title {
239 + font-size: 32rpx;
240 + font-weight: bold;
241 + color: #333;
242 + margin-bottom: 24rpx;
243 + position: relative;
244 +
245 + &::before {
246 + content: '';
247 + position: absolute;
248 + left: -16rpx;
249 + top: 50%;
250 + transform: translateY(-50%);
251 + width: 6rpx;
252 + height: 24rpx;
253 + background-color: #1890ff;
254 + border-radius: 3rpx;
255 + }
256 +}
257 +
258 +.section-content {
259 + font-size: 28rpx;
260 + line-height: 1.6;
261 + color: #666;
262 +}
263 +
264 +.solution-content {
265 + display: flex;
266 + flex-direction: column;
267 + gap: 24rpx;
268 +}
269 +
270 +.solution-step {
271 + display: flex;
272 + align-items: flex-start;
273 + gap: 16rpx;
274 +}
275 +
276 +.step-number {
277 + width: 48rpx;
278 + height: 48rpx;
279 + background-color: #1890ff;
280 + color: white;
281 + border-radius: 50%;
282 + display: flex;
283 + align-items: center;
284 + justify-content: center;
285 + font-size: 24rpx;
286 + font-weight: bold;
287 + flex-shrink: 0;
288 +}
289 +
290 +.step-content {
291 + flex: 1;
292 + font-size: 28rpx;
293 + line-height: 1.5;
294 + color: #666;
295 + padding-top: 8rpx;
296 +}
297 +
298 +// 积分奖励区域
299 +.reward-section {
300 + display: flex;
301 + flex-direction: column;
302 + gap: 16rpx;
303 +}
304 +
305 +.reward-item {
306 + display: flex;
307 + align-items: center;
308 + justify-content: space-between;
309 + padding: 24rpx;
310 + background-color: #f8f9fa;
311 + border-radius: 12rpx;
312 + border-left: 6rpx solid #52c41a;
313 +}
314 +
315 +.reward-label {
316 + font-size: 28rpx;
317 + color: #333;
318 + font-weight: 500;
319 +}
320 +
321 +.reward-value {
322 + font-size: 32rpx;
323 + color: #52c41a;
324 + font-weight: bold;
325 +
326 + &.bonus {
327 + color: #fa8c16;
328 + }
329 +}
330 +
331 +// 注意事项
332 +.notes-content {
333 + display: flex;
334 + flex-direction: column;
335 + gap: 16rpx;
336 +}
337 +
338 +.note-item {
339 + padding: 16rpx 20rpx;
340 + background-color: #fff7e6;
341 + border-radius: 8rpx;
342 + border-left: 4rpx solid #ffd591;
343 +}
344 +
345 +.note-text {
346 + font-size: 26rpx;
347 + line-height: 1.5;
348 + color: #d46b08;
349 +}
350 +
351 +// 响应式适配
352 +@media (max-width: 750rpx) {
353 + .category-grid {
354 + gap: 12rpx;
355 + }
356 +
357 + .category-item {
358 + padding: 12rpx 24rpx;
359 + }
360 +
361 + .category-name {
362 + font-size: 24rpx;
363 + }
364 +
365 + .points-title {
366 + font-size: 30rpx;
367 + }
368 +
369 + .points-desc {
370 + font-size: 24rpx;
371 + }
372 +}
...\ No newline at end of file ...\ No newline at end of file
1 +<!--
2 + * @Description: 积分攻略列表页面
3 +-->
4 +<template>
5 + <view class="points-list-page">
6 + <!-- 搜索框 -->
7 + <view class="search-section">
8 + <view class="search-box">
9 + <input
10 + v-model="searchKeyword"
11 + placeholder="搜索积分攻略"
12 + class="search-input"
13 + @input="handleSearch"
14 + />
15 + </view>
16 + </view>
17 +
18 + <!-- 攻略分类 -->
19 + <view class="category-section">
20 + <view class="category-title">攻略分类</view>
21 + <view class="category-grid">
22 + <view
23 + v-for="category in categories"
24 + :key="category.id"
25 + class="category-item"
26 + @click="filterByCategory(category.id)"
27 + :class="{ active: selectedCategory === category.id }"
28 + >
29 + <text class="category-name">{{ category.name }}</text>
30 + </view>
31 + </view>
32 + </view>
33 +
34 + <!-- 攻略列表 -->
35 + <view class="points-list-section">
36 + <scroll-view
37 + class="points-list"
38 + :scroll-y="true"
39 + :style="scrollStyle"
40 + >
41 + <view class="points-items">
42 + <view
43 + v-for="item in filteredPointsItems"
44 + :key="item.id"
45 + class="points-item"
46 + @click="showPointsDetail(item)"
47 + >
48 + <view class="points-item-left">
49 + <view class="points-content">
50 + <text class="points-title">{{ item.title }}</text>
51 + <text class="points-desc">{{ item.description }}</text>
52 + <view class="points-reward">
53 + <text class="reward-text">可获得: {{ item.reward }}积分</text>
54 + </view>
55 + </view>
56 + </view>
57 + <view class="points-item-right">
58 + <text class="arrow-text"><RectRight /></text>
59 + </view>
60 + </view>
61 + </view>
62 +
63 + <!-- 空状态 -->
64 + <view
65 + v-if="filteredPointsItems.length === 0"
66 + class="empty-state"
67 + >
68 + <text class="empty-text">暂无相关积分攻略</text>
69 + </view>
70 + </scroll-view>
71 + </view>
72 +
73 + <!-- 右侧弹出详情 -->
74 + <nut-popup
75 + v-model:visible="showDetailPopup"
76 + position="right"
77 + :style="{ width: '85%', height: '100%' }"
78 + @close="closeDetail"
79 + >
80 + <view class="detail-popup">
81 + <!-- 详情头部 -->
82 + <view class="detail-header">
83 + <view class="detail-title">{{ currentPointsItem?.title }}</view>
84 + <view class="close-btn1" @click="closeDetail">
85 + <text class="close-text">关闭</text>
86 + </view>
87 + </view>
88 +
89 + <!-- 详情内容 -->
90 + <scroll-view class="detail-content" :scroll-y="true">
91 + <view class="detail-body">
92 + <!-- 攻略描述 -->
93 + <view class="detail-section">
94 + <view class="section-title">攻略描述</view>
95 + <text class="section-content">{{ currentPointsItem?.description }}</text>
96 + </view>
97 +
98 + <!-- 获取步骤 -->
99 + <view class="detail-section">
100 + <view class="section-title">获取步骤</view>
101 + <view class="solution-content">
102 + <view
103 + v-for="(step, index) in currentPointsItem?.steps"
104 + :key="index"
105 + class="solution-step"
106 + >
107 + <view class="step-number">{{ index + 1 }}</view>
108 + <text class="step-content">{{ step }}</text>
109 + </view>
110 + </view>
111 + </view>
112 +
113 + <!-- 积分奖励 -->
114 + <view class="detail-section">
115 + <view class="section-title">积分奖励</view>
116 + <view class="reward-section">
117 + <view class="reward-item">
118 + <text class="reward-label">基础积分:</text>
119 + <text class="reward-value">{{ currentPointsItem?.reward }}分</text>
120 + </view>
121 + <view v-if="currentPointsItem?.bonusReward" class="reward-item">
122 + <text class="reward-label">额外奖励:</text>
123 + <text class="reward-value bonus">{{ currentPointsItem?.bonusReward }}分</text>
124 + </view>
125 + </view>
126 + </view>
127 +
128 + <!-- 注意事项 -->
129 + <view v-if="currentPointsItem?.notes?.length" class="detail-section">
130 + <view class="section-title">注意事项</view>
131 + <view class="notes-content">
132 + <view
133 + v-for="(note, index) in currentPointsItem.notes"
134 + :key="index"
135 + class="note-item"
136 + >
137 + <text class="note-text">• {{ note }}</text>
138 + </view>
139 + </view>
140 + </view>
141 + </view>
142 + </scroll-view>
143 + </view>
144 + </nut-popup>
145 +
146 + <!-- Toast提示 -->
147 + <nut-toast
148 + v-model:visible="toastVisible"
149 + :msg="toastMessage"
150 + :type="toastType"
151 + />
152 + </view>
153 +</template>
154 +
155 +<script setup>
156 +// import '@tarojs/taro/html.css'
157 +import { ref, computed, onMounted } from 'vue'
158 +import Taro from '@tarojs/taro'
159 +import { RectRight } from '@nutui/icons-vue-taro'
160 +import './index.less'
161 +
162 +/**
163 + * 积分攻略列表页面组件
164 + * 功能:展示各种积分获取攻略、搜索和分类筛选
165 + */
166 +
167 +// ==================== 响应式数据 ====================
168 +/**
169 + * 搜索关键词
170 + */
171 +const searchKeyword = ref('')
172 +
173 +/**
174 + * 选中的分类
175 + */
176 +const selectedCategory = ref('all')
177 +
178 +/**
179 + * 详情弹窗显示状态
180 + */
181 +const showDetailPopup = ref(false)
182 +
183 +/**
184 + * 当前查看的积分攻略项
185 + */
186 +const currentPointsItem = ref(null)
187 +
188 +/**
189 + * Toast提示
190 + */
191 +const toastVisible = ref(false)
192 +const toastMessage = ref('')
193 +const toastType = ref('success')
194 +
195 +/**
196 + * 攻略分类数据
197 + */
198 +const categories = ref([
199 + { id: 'all', name: '全部' },
200 + { id: 'daily', name: '日常任务' },
201 + { id: 'activity', name: '活动参与' },
202 + { id: 'family', name: '家庭互动' },
203 + { id: 'special', name: '特殊奖励' }
204 +])
205 +
206 +/**
207 + * 积分攻略数据
208 + */
209 +const pointsItems = ref([
210 + {
211 + id: 'p001',
212 + category: 'daily',
213 + title: '每日步数积分攻略',
214 + description: '通过日常走路获得积分,简单易行的积分获取方式',
215 + reward: 50,
216 + bonusReward: 20,
217 + steps: [
218 + '打开微信运动,确保步数同步正常',
219 + '每日保持至少3000步的运动量',
220 + '在老来赛APP中同步当日步数',
221 + '系统自动计算并发放积分奖励',
222 + '连续7天可获得额外奖励积分'
223 + ],
224 + notes: [
225 + '每日步数积分上限为50分',
226 + '步数需在当日23:59前同步',
227 + '连续签到可获得额外奖励'
228 + ]
229 + },
230 + {
231 + id: 'p002',
232 + category: 'family',
233 + title: '家庭成员邀请攻略',
234 + description: '邀请家庭成员加入,共同获得积分奖励',
235 + reward: 200,
236 + bonusReward: 100,
237 + steps: [
238 + '进入家庭页面,点击邀请成员',
239 + '分享家庭邀请码给家人',
240 + '家人通过邀请码成功加入家庭',
241 + '双方都可获得邀请奖励积分',
242 + '家庭成员达到5人可获得额外奖励'
243 + ],
244 + notes: [
245 + '每邀请一位成员可获得200积分',
246 + '被邀请者也可获得100积分',
247 + '家庭成员上限为10人'
248 + ]
249 + },
250 + {
251 + id: 'p003',
252 + category: 'activity',
253 + title: '参与社区活动攻略',
254 + description: '参加线下社区活动,获得丰厚积分奖励',
255 + reward: 500,
256 + bonusReward: 300,
257 + steps: [
258 + '关注活动页面的最新活动信息',
259 + '报名参加感兴趣的社区活动',
260 + '按时到达活动地点参与活动',
261 + '完成活动打卡和照片上传',
262 + '活动结束后获得积分奖励'
263 + ],
264 + notes: [
265 + '不同活动积分奖励不同',
266 + '需要实地参与才能获得积分',
267 + '上传照片可获得额外积分'
268 + ]
269 + },
270 + {
271 + id: 'p004',
272 + category: 'daily',
273 + title: '每日签到积分攻略',
274 + description: '每天登录APP签到,轻松获得积分',
275 + reward: 10,
276 + bonusReward: 50,
277 + steps: [
278 + '每日打开老来赛APP',
279 + '点击首页的签到按钮',
280 + '完成当日签到获得基础积分',
281 + '连续签到可获得递增奖励',
282 + '连续签到7天获得额外奖励'
283 + ],
284 + notes: [
285 + '每日签到基础积分为10分',
286 + '连续签到奖励递增',
287 + '中断签到需重新开始计算'
288 + ]
289 + },
290 + {
291 + id: 'p005',
292 + category: 'family',
293 + title: '家庭步数排行攻略',
294 + description: '参与家庭步数排行,获得排名奖励',
295 + reward: 100,
296 + bonusReward: 200,
297 + steps: [
298 + '确保家庭成员都已加入',
299 + '每日同步个人步数数据',
300 + '查看家庭步数排行榜',
301 + '努力提升个人排名',
302 + '周末结算排名奖励积分'
303 + ],
304 + notes: [
305 + '排名前三可获得额外奖励',
306 + '家庭总步数也有奖励',
307 + '鼓励家庭成员互相督促'
308 + ]
309 + },
310 + {
311 + id: 'p006',
312 + category: 'special',
313 + title: '轮椅陪伴运动攻略',
314 + description: '陪伴轮椅老人运动,获得三倍积分奖励',
315 + reward: 150,
316 + bonusReward: 300,
317 + steps: [
318 + '在个人资料中标注陪伴轮椅老人',
319 + '与轮椅老人一起进行运动',
320 + '记录陪伴运动的时间和距离',
321 + '上传陪伴运动的照片或视频',
322 + '系统验证后发放三倍积分奖励'
323 + ],
324 + notes: [
325 + '需要提供陪伴证明',
326 + '积分为普通运动的三倍',
327 + '体现社会关爱精神'
328 + ]
329 + },
330 + {
331 + id: 'p007',
332 + category: 'activity',
333 + title: '完成城市漫步攻略',
334 + description: '参与城市漫步活动,探索城市获得积分',
335 + reward: 300,
336 + bonusReward: 200,
337 + steps: [
338 + '选择感兴趣的城市漫步路线',
339 + '按照路线进行实地探索',
340 + '在指定地点完成打卡',
341 + '上传风景照片和感想',
342 + '完成全部打卡点获得奖励'
343 + ],
344 + notes: [
345 + '需要完成所有打卡点',
346 + '照片需要包含地标建筑',
347 + '可以邀请家人一起参与'
348 + ]
349 + },
350 + {
351 + id: 'p008',
352 + category: 'special',
353 + title: '节日特殊活动攻略',
354 + description: '参与节日特殊活动,获得限时积分奖励',
355 + reward: 888,
356 + steps: [
357 + '关注节日活动公告',
358 + '参与节日主题活动',
359 + '完成节日特殊任务',
360 + '分享节日祝福内容',
361 + '获得节日限定积分奖励'
362 + ],
363 + notes: [
364 + '节日活动时间有限',
365 + '积分奖励通常较高',
366 + '增加节日氛围和参与感'
367 + ]
368 + }
369 +])
370 +
371 +/**
372 + * 滚动样式
373 + */
374 +const scrollStyle = computed(() => {
375 + return {
376 + height: 'calc(100vh - 260rpx)' // 减去header、搜索框、分类的高度
377 + }
378 +})
379 +
380 +/**
381 + * 过滤后的积分攻略项
382 + */
383 +const filteredPointsItems = computed(() => {
384 + let items = pointsItems.value
385 +
386 + // 按分类过滤
387 + if (selectedCategory.value !== 'all') {
388 + items = items.filter(item => item.category === selectedCategory.value)
389 + }
390 +
391 + // 按搜索关键词过滤
392 + if (searchKeyword.value.trim()) {
393 + const keyword = searchKeyword.value.toLowerCase()
394 + items = items.filter(item =>
395 + item.title.toLowerCase().includes(keyword) ||
396 + item.description.toLowerCase().includes(keyword)
397 + )
398 + }
399 +
400 + return items
401 +})
402 +
403 +// ==================== 方法 ====================
404 +/**
405 + * 返回上一页
406 + */
407 +const goBack = () => {
408 + Taro.navigateBack()
409 +}
410 +
411 +/**
412 + * 处理搜索
413 + */
414 +const handleSearch = () => {
415 + // 搜索逻辑已在computed中处理
416 +}
417 +
418 +/**
419 + * 按分类过滤
420 + */
421 +const filterByCategory = (categoryId) => {
422 + selectedCategory.value = categoryId
423 +}
424 +
425 +/**
426 + * 显示积分攻略详情
427 + */
428 +const showPointsDetail = (item) => {
429 + currentPointsItem.value = item
430 + showDetailPopup.value = true
431 +}
432 +
433 +/**
434 + * 关闭详情
435 + */
436 +const closeDetail = () => {
437 + showDetailPopup.value = false
438 + currentPointsItem.value = null
439 +}
440 +
441 +// ==================== 生命周期 ====================
442 +onMounted(() => {
443 + // 页面初始化逻辑
444 +})
445 +</script>
446 +
447 +<script>
448 +export default {
449 + name: 'PointsListPage'
450 +}
451 +</script>