fix(scan-checkin): 完善扫码打卡列表分页与交互细节
- ESLint comma-dangle 改为 always-multiline,允许多行尾随逗号 - ScanCheckinList 接入分页加载,增加加载中/空状态/加载更多提示 - ScanCheckinDetail 打卡成功后等用户确认再跳转列表页 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Showing
4 changed files
with
108 additions
and
10 deletions
| ... | @@ -15,7 +15,7 @@ module.exports = { | ... | @@ -15,7 +15,7 @@ module.exports = { |
| 15 | indent: ['error', 2, { SwitchCase: 1 }], // 缩进 | 15 | indent: ['error', 2, { SwitchCase: 1 }], // 缩进 |
| 16 | quotes: ['error', 'single', { avoidEscape: true }], // 单引号 | 16 | quotes: ['error', 'single', { avoidEscape: true }], // 单引号 |
| 17 | semi: ['error', 'never'], // 不使用分号 | 17 | semi: ['error', 'never'], // 不使用分号 |
| 18 | - 'comma-dangle': ['error', 'never'], // 不使用尾随逗号 | 18 | + 'comma-dangle': ['error', 'always-multiline'], // 多行时必须尾随逗号,单行不允许 |
| 19 | 'space-before-function-paren': [ | 19 | 'space-before-function-paren': [ |
| 20 | 'error', | 20 | 'error', |
| 21 | { | 21 | { | ... | ... |
| ... | @@ -214,21 +214,23 @@ const handleScanCheckin = async () => { | ... | @@ -214,21 +214,23 @@ const handleScanCheckin = async () => { |
| 214 | detail.isChecked = true | 214 | detail.isChecked = true |
| 215 | detail.lastScanCode = scannedCode | 215 | detail.lastScanCode = scannedCode |
| 216 | 216 | ||
| 217 | - await Taro.showModal({ | 217 | + const modalResult = await Taro.showModal({ |
| 218 | title: '打卡成功', | 218 | title: '打卡成功', |
| 219 | content: '您已完成当前扫码打卡,可前往列表查看状态。', | 219 | content: '您已完成当前扫码打卡,可前往列表查看状态。', |
| 220 | showCancel: false, | 220 | showCancel: false, |
| 221 | confirmText: '查看列表', | 221 | confirmText: '查看列表', |
| 222 | }) | 222 | }) |
| 223 | 223 | ||
| 224 | + if (modalResult.confirm) { | ||
| 224 | const params = new URLSearchParams({ | 225 | const params = new URLSearchParams({ |
| 225 | activityId: detail.activityId, | 226 | activityId: detail.activityId, |
| 226 | }) | 227 | }) |
| 227 | 228 | ||
| 228 | - // 扫码打卡完成后回列表页,方便用户继续处理同一活动下的其他关卡。 | 229 | + // 用户确认“查看列表”后回到列表页,方便继续处理同一活动下的其他关卡。 |
| 229 | Taro.redirectTo({ | 230 | Taro.redirectTo({ |
| 230 | url: `/pages/ScanCheckinList/index?${params.toString()}`, | 231 | url: `/pages/ScanCheckinList/index?${params.toString()}`, |
| 231 | }) | 232 | }) |
| 233 | + } | ||
| 232 | return | 234 | return |
| 233 | } | 235 | } |
| 234 | 236 | ... | ... |
| 1 | .scan-checkin-list-page { | 1 | .scan-checkin-list-page { |
| 2 | min-height: 100vh; | 2 | min-height: 100vh; |
| 3 | - padding: 32rpx 24rpx 40rpx; | 3 | + padding: 32rpx 24rpx 220rpx; |
| 4 | background: linear-gradient(180deg, #f6f8fb 0%, #eef2f5 100%); | 4 | background: linear-gradient(180deg, #f6f8fb 0%, #eef2f5 100%); |
| 5 | box-sizing: border-box; | 5 | box-sizing: border-box; |
| 6 | position: relative; | 6 | position: relative; |
| ... | @@ -36,6 +36,13 @@ | ... | @@ -36,6 +36,13 @@ |
| 36 | box-shadow: 0 12rpx 40rpx rgba(15, 23, 42, 0.08); | 36 | box-shadow: 0 12rpx 40rpx rgba(15, 23, 42, 0.08); |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | +.scan-checkin-list-status { | ||
| 40 | + padding: 88rpx 0; | ||
| 41 | + text-align: center; | ||
| 42 | + font-size: 28rpx; | ||
| 43 | + color: #8b98a7; | ||
| 44 | +} | ||
| 45 | + | ||
| 39 | .scan-checkin-list-leading { | 46 | .scan-checkin-list-leading { |
| 40 | width: 68rpx; | 47 | width: 68rpx; |
| 41 | height: 68rpx; | 48 | height: 68rpx; |
| ... | @@ -80,6 +87,21 @@ | ... | @@ -80,6 +87,21 @@ |
| 80 | flex-shrink: 0; | 87 | flex-shrink: 0; |
| 81 | } | 88 | } |
| 82 | 89 | ||
| 90 | +.scan-checkin-list-load-more, | ||
| 91 | +.scan-checkin-list-no-more { | ||
| 92 | + padding: 24rpx 0 8rpx; | ||
| 93 | + text-align: center; | ||
| 94 | + font-size: 26rpx; | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +.scan-checkin-list-load-more { | ||
| 98 | + color: #36b5bb; | ||
| 99 | +} | ||
| 100 | + | ||
| 101 | +.scan-checkin-list-no-more { | ||
| 102 | + color: #98a3af; | ||
| 103 | +} | ||
| 104 | + | ||
| 83 | .scan-checkin-list-floating-button { | 105 | .scan-checkin-list-floating-button { |
| 84 | position: fixed; | 106 | position: fixed; |
| 85 | right: 24rpx; | 107 | right: 24rpx; | ... | ... |
| ... | @@ -11,6 +11,11 @@ | ... | @@ -11,6 +11,11 @@ |
| 11 | <text class="scan-checkin-list-subtitle">请选择一个打卡点,进入详情后完成扫码打卡</text> | 11 | <text class="scan-checkin-list-subtitle">请选择一个打卡点,进入详情后完成扫码打卡</text> |
| 12 | </view> | 12 | </view> |
| 13 | 13 | ||
| 14 | + <view v-if="loading && pointList.length === 0" class="scan-checkin-list-status"> | ||
| 15 | + 加载中... | ||
| 16 | + </view> | ||
| 17 | + | ||
| 18 | + <template v-else> | ||
| 14 | <view v-for="point in pointList" :key="point.id" class="scan-checkin-list-card"> | 19 | <view v-for="point in pointList" :key="point.id" class="scan-checkin-list-card"> |
| 15 | <view class="scan-checkin-list-leading"> | 20 | <view class="scan-checkin-list-leading"> |
| 16 | <IconFont size="30" name="https://cdn.ipadbiz.cn/lls_prog/icon/check_list_logo.png" /> | 21 | <IconFont size="30" name="https://cdn.ipadbiz.cn/lls_prog/icon/check_list_logo.png" /> |
| ... | @@ -25,6 +30,23 @@ | ... | @@ -25,6 +30,23 @@ |
| 25 | </view> | 30 | </view> |
| 26 | </view> | 31 | </view> |
| 27 | 32 | ||
| 33 | + <view v-if="!loading && pointList.length === 0" class="scan-checkin-list-status"> | ||
| 34 | + 暂无扫码打卡点 | ||
| 35 | + </view> | ||
| 36 | + | ||
| 37 | + <view | ||
| 38 | + v-if="hasMore && pointList.length > 0" | ||
| 39 | + class="scan-checkin-list-load-more" | ||
| 40 | + @click="loadMore" | ||
| 41 | + > | ||
| 42 | + {{ loadingMore ? '加载中...' : '加载更多' }} | ||
| 43 | + </view> | ||
| 44 | + | ||
| 45 | + <view v-if="!hasMore && pointList.length > 0" class="scan-checkin-list-no-more"> | ||
| 46 | + 没有更多数据了 | ||
| 47 | + </view> | ||
| 48 | + </template> | ||
| 49 | + | ||
| 28 | <view class="scan-checkin-list-floating-button" @click="handleShowBoothMap"> | 50 | <view class="scan-checkin-list-floating-button" @click="handleShowBoothMap"> |
| 29 | <IconFont | 51 | <IconFont |
| 30 | class="scan-checkin-list-floating-icon" | 52 | class="scan-checkin-list-floating-icon" |
| ... | @@ -45,6 +67,18 @@ import { getScanStageListAPI } from '@/api/map' | ... | @@ -45,6 +67,18 @@ import { getScanStageListAPI } from '@/api/map' |
| 45 | 67 | ||
| 46 | const pointList = ref([]) | 68 | const pointList = ref([]) |
| 47 | const activityId = ref('') | 69 | const activityId = ref('') |
| 70 | +const loading = ref(false) | ||
| 71 | +const loadingMore = ref(false) | ||
| 72 | +const hasMore = ref(true) | ||
| 73 | +const currentPage = ref(0) | ||
| 74 | +const pageSize = ref(10) | ||
| 75 | + | ||
| 76 | +const mapStageList = stageList => | ||
| 77 | + stageList.map(stage => ({ | ||
| 78 | + id: stage.id, | ||
| 79 | + title: stage.title, | ||
| 80 | + isChecked: stage.is_checked, | ||
| 81 | + })) | ||
| 48 | 82 | ||
| 49 | const goToDetail = point => { | 83 | const goToDetail = point => { |
| 50 | const params = new URLSearchParams({ | 84 | const params = new URLSearchParams({ |
| ... | @@ -74,9 +108,35 @@ useLoad(options => { | ... | @@ -74,9 +108,35 @@ useLoad(options => { |
| 74 | loadStageList() | 108 | loadStageList() |
| 75 | }) | 109 | }) |
| 76 | 110 | ||
| 77 | -const loadStageList = async () => { | 111 | +const loadStageList = async (isLoadMore = false) => { |
| 112 | + if (!activityId.value) { | ||
| 113 | + pointList.value = [] | ||
| 114 | + hasMore.value = false | ||
| 115 | + return | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + if (isLoadMore) { | ||
| 119 | + if (loading.value || loadingMore.value || !hasMore.value) { | ||
| 120 | + return | ||
| 121 | + } | ||
| 122 | + loadingMore.value = true | ||
| 123 | + } else { | ||
| 124 | + if (loading.value) { | ||
| 125 | + return | ||
| 126 | + } | ||
| 127 | + loading.value = true | ||
| 128 | + currentPage.value = 0 | ||
| 129 | + hasMore.value = true | ||
| 130 | + pointList.value = [] | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + const page = currentPage.value | ||
| 134 | + | ||
| 135 | + try { | ||
| 78 | const result = await getScanStageListAPI({ | 136 | const result = await getScanStageListAPI({ |
| 79 | activity_id: activityId.value, | 137 | activity_id: activityId.value, |
| 138 | + page, | ||
| 139 | + limit: pageSize.value, | ||
| 80 | }) | 140 | }) |
| 81 | 141 | ||
| 82 | if (result?.code !== 1) { | 142 | if (result?.code !== 1) { |
| ... | @@ -87,11 +147,25 @@ const loadStageList = async () => { | ... | @@ -87,11 +147,25 @@ const loadStageList = async () => { |
| 87 | return | 147 | return |
| 88 | } | 148 | } |
| 89 | 149 | ||
| 90 | - pointList.value = (result?.data?.stages || []).map(stage => ({ | 150 | + const stageList = mapStageList(result?.data?.stages || []) |
| 91 | - id: stage.id, | 151 | + |
| 92 | - title: stage.title, | 152 | + pointList.value = isLoadMore ? [...pointList.value, ...stageList] : stageList |
| 93 | - isChecked: stage.is_checked, | 153 | + hasMore.value = stageList.length === pageSize.value |
| 94 | - })) | 154 | + currentPage.value = page + 1 |
| 155 | + } catch (error) { | ||
| 156 | + console.error('获取扫码打卡关卡列表失败:', error) | ||
| 157 | + Taro.showToast({ | ||
| 158 | + title: '获取关卡列表失败', | ||
| 159 | + icon: 'none', | ||
| 160 | + }) | ||
| 161 | + } finally { | ||
| 162 | + loading.value = false | ||
| 163 | + loadingMore.value = false | ||
| 164 | + } | ||
| 165 | +} | ||
| 166 | + | ||
| 167 | +const loadMore = () => { | ||
| 168 | + loadStageList(true) | ||
| 95 | } | 169 | } |
| 96 | </script> | 170 | </script> |
| 97 | 171 | ... | ... |
-
Please register or login to post a comment