index.vue
10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
<!--
* @Description: 积分攻略列表页面
-->
<template>
<view class="points-list-page">
<!-- 搜索框 -->
<view class="search-section">
<view class="search-box">
<input
v-model="searchKeyword"
placeholder="搜索积分攻略"
class="search-input"
@input="handleSearch"
/>
</view>
</view>
<!-- 攻略分类 -->
<view class="category-section">
<view class="category-title">攻略分类</view>
<view class="category-grid">
<view
v-for="category in categories"
:key="category.id"
class="category-item"
@click="filterByCategory(category.id)"
:class="{ active: selectedCategory === category.id }"
>
<text class="category-name">{{ category.name }}</text>
</view>
</view>
</view>
<!-- 攻略列表 -->
<view class="points-list-section">
<scroll-view
class="points-list"
:scroll-y="true"
:style="scrollStyle"
>
<view class="points-items">
<view
v-for="item in filteredPointsItems"
:key="item.id"
class="points-item"
@click="showPointsDetail(item)"
>
<view class="points-item-left">
<view class="points-content">
<text class="points-title">{{ item.title }}</text>
<text class="points-desc">{{ item.description }}</text>
<view class="points-reward">
<text class="reward-text">可获得: {{ item.reward }}积分</text>
</view>
</view>
</view>
<view class="points-item-right">
<text class="arrow-text"><RectRight /></text>
</view>
</view>
</view>
<!-- 空状态 -->
<view
v-if="filteredPointsItems.length === 0"
class="empty-state"
>
<text class="empty-text">暂无相关积分攻略</text>
</view>
</scroll-view>
</view>
<!-- 右侧弹出详情 -->
<nut-popup
v-model:visible="showDetailPopup"
position="right"
:style="{ width: '85%', height: '100%' }"
@close="closeDetail"
>
<view class="detail-popup">
<!-- 详情头部 -->
<view class="detail-header">
<view class="detail-title">{{ currentPointsItem?.title }}</view>
<view class="close-btn1" @click="closeDetail">
<text class="close-text">关闭</text>
</view>
</view>
<!-- 详情内容 -->
<scroll-view class="detail-content" :scroll-y="true">
<view class="detail-body">
<!-- 攻略描述 -->
<view class="detail-section">
<view class="section-title">攻略描述</view>
<text class="section-content">{{ currentPointsItem?.description }}</text>
</view>
<!-- 获取步骤 -->
<view class="detail-section">
<view class="section-title">获取步骤</view>
<view class="solution-content">
<view
v-for="(step, index) in currentPointsItem?.steps"
:key="index"
class="solution-step"
>
<view class="step-number">{{ index + 1 }}</view>
<text class="step-content">{{ step }}</text>
</view>
</view>
</view>
<!-- 积分奖励 -->
<view class="detail-section">
<view class="section-title">积分奖励</view>
<view class="reward-section">
<view class="reward-item">
<text class="reward-label">基础积分:</text>
<text class="reward-value">{{ currentPointsItem?.reward }}分</text>
</view>
<view v-if="currentPointsItem?.bonusReward" class="reward-item">
<text class="reward-label">额外奖励:</text>
<text class="reward-value bonus">{{ currentPointsItem?.bonusReward }}分</text>
</view>
</view>
</view>
<!-- 注意事项 -->
<view v-if="currentPointsItem?.notes?.length" class="detail-section">
<view class="section-title">注意事项</view>
<view class="notes-content">
<view
v-for="(note, index) in currentPointsItem.notes"
:key="index"
class="note-item"
>
<text class="note-text">• {{ note }}</text>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</nut-popup>
<!-- Toast提示 -->
<nut-toast
v-model:visible="toastVisible"
:msg="toastMessage"
:type="toastType"
/>
</view>
</template>
<script setup>
// import '@tarojs/taro/html.css'
import { ref, computed, onMounted } from 'vue'
import Taro from '@tarojs/taro'
import { RectRight } from '@nutui/icons-vue-taro'
import './index.less'
/**
* 积分攻略列表页面组件
* 功能:展示各种积分获取攻略、搜索和分类筛选
*/
// ==================== 响应式数据 ====================
/**
* 搜索关键词
*/
const searchKeyword = ref('')
/**
* 选中的分类
*/
const selectedCategory = ref('all')
/**
* 详情弹窗显示状态
*/
const showDetailPopup = ref(false)
/**
* 当前查看的积分攻略项
*/
const currentPointsItem = ref(null)
/**
* Toast提示
*/
const toastVisible = ref(false)
const toastMessage = ref('')
const toastType = ref('success')
/**
* 攻略分类数据
*/
const categories = ref([
{ id: 'all', name: '全部' },
{ id: 'daily', name: '日常任务' },
{ id: 'activity', name: '活动参与' },
{ id: 'family', name: '家庭互动' },
{ id: 'special', name: '特殊奖励' }
])
/**
* 积分攻略数据
*/
const pointsItems = ref([
// 常规积分
{
id: 'p001',
category: 'daily',
title: '日常步数积分攻略',
description: '通过日常走路获得积分,简单易行的积分获取方式',
reward: '100步=1',
steps: [
'在微信里,我-设置-通用-辅助功能,启用“微信运功”功能',
'每日保持运动,记录步数',
'打开“老来赛”小程序,就可以同步微信步数',
],
notes: [
'家庭成员步数同样按此规则计算'
]
},
{
id: 'p002',
category: 'activity',
title: '参与活动积分攻略',
description: '参加线下活动打卡,获得丰厚积分奖励',
reward: 1000,
steps: [
'关注活动页面的最新活动信息',
'前往活动地点参与活动',
'在任意一个商户卡点完成打卡',
'系统确认后发放1000积分奖励'
],
notes: [
'只要打卡一个商户卡点即可',
'每次参与活动都可获得积分',
'需要实地参与才能获得积分'
]
},
{
id: 'p003',
category: 'activity',
title: '完成活动积分攻略',
description: '完成南京路商圈活动,获得超高积分奖励',
reward: 5000,
steps: [
'参与南京路商圈活动',
'了解活动设置的卡点位置',
'逐一前往各个卡点完成打卡',
'完成所有卡点的打卡任务',
'系统确认后发放5000积分奖励'
],
notes: [
'南京路商圈设置多个卡点',
'完成所有卡点的打卡任务即视为完成活动',
'奖励积分高达5000分'
]
},
// 额外积分
{
id: 'p004',
category: 'family',
title: '家庭成员达标奖励',
description: '家庭成员达到5人,获得奖励积分',
reward: 500,
steps: [
'创建或加入家庭',
'邀请家人加入家庭',
'确保家庭成员达到5人',
'系统自动检测家庭人数',
'达到5人后自动发放500积分奖励'
],
notes: [
'家庭成员达5人获得500分',
'一次性奖励积分',
'鼓励家庭成员共同参与'
]
},
{
id: 'p005',
category: 'family',
title: '家庭陪伴运动攻略',
description: '家庭成员陪伴运动,获得额外积分奖励',
reward: 100,
steps: [
'与家庭成员一起进行运动',
'运动过程中拍摄照片',
'在APP中上传陪伴运动照片',
'系统确认照片有效性',
'获得100积分陪伴运动奖励'
],
notes: [
'只要上传照片即视为有效陪伴',
'每天上传陪伴运动最多可获得100积分',
'鼓励家庭成员互相陪伴'
]
},
{
id: 'p006',
category: 'special',
title: '轮椅陪伴运动攻略',
description: '陪伴轮椅老人运动,获得额外补偿积分',
reward: 100,
steps: [
'陪伴轮椅老人进行运动',
'系统识别轮椅陪伴情况',
'获得轮椅补偿100分'
],
notes: [
'一次性奖励积分',
'额外获得轮椅补偿100分',
'体现对特殊群体的关爱'
]
}
])
/**
* 滚动样式
*/
const scrollStyle = computed(() => {
return {
height: 'calc(100vh - 260rpx)' // 减去header、搜索框、分类的高度
}
})
/**
* 过滤后的积分攻略项
*/
const filteredPointsItems = computed(() => {
let items = pointsItems.value
// 按分类过滤
if (selectedCategory.value !== 'all') {
items = items.filter(item => item.category === selectedCategory.value)
}
// 按搜索关键词过滤
if (searchKeyword.value.trim()) {
const keyword = searchKeyword.value.toLowerCase()
items = items.filter(item =>
item.title.toLowerCase().includes(keyword) ||
item.description.toLowerCase().includes(keyword)
)
}
return items
})
// ==================== 方法 ====================
/**
* 返回上一页
*/
const goBack = () => {
Taro.navigateBack()
}
/**
* 处理搜索
*/
const handleSearch = () => {
// 搜索逻辑已在computed中处理
}
/**
* 按分类过滤
*/
const filterByCategory = (categoryId) => {
selectedCategory.value = categoryId
}
/**
* 显示积分攻略详情
*/
const showPointsDetail = (item) => {
currentPointsItem.value = item
showDetailPopup.value = true
}
/**
* 关闭详情
*/
const closeDetail = () => {
showDetailPopup.value = false
currentPointsItem.value = null
}
// ==================== 生命周期 ====================
onMounted(() => {
// 页面初始化逻辑
})
</script>
<script>
export default {
name: 'PointsListPage'
}
</script>