refactor(components): 重构积分显示组件布局并添加插槽支持
重构 TotalPointsDisplay 和 PointsCollector 组件结构,添加容器样式和 header/footer 插槽 调整组件样式,移除全屏宽度限制并添加圆角和阴影 优化 Dashboard 页面布局,将步数显示和拍照按钮整合到组件插槽中
Showing
4 changed files
with
178 additions
and
99 deletions
| ... | @@ -19,7 +19,6 @@ declare module 'vue' { | ... | @@ -19,7 +19,6 @@ declare module 'vue' { |
| 19 | NutDialog: typeof import('@nutui/nutui-taro')['Dialog'] | 19 | NutDialog: typeof import('@nutui/nutui-taro')['Dialog'] |
| 20 | NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview'] | 20 | NutImagePreview: typeof import('@nutui/nutui-taro')['ImagePreview'] |
| 21 | NutInput: typeof import('@nutui/nutui-taro')['Input'] | 21 | NutInput: typeof import('@nutui/nutui-taro')['Input'] |
| 22 | - NutLoading: typeof import('@nutui/nutui-taro')['Loading'] | ||
| 23 | NutPicker: typeof import('@nutui/nutui-taro')['Picker'] | 22 | NutPicker: typeof import('@nutui/nutui-taro')['Picker'] |
| 24 | NutPopup: typeof import('@nutui/nutui-taro')['Popup'] | 23 | NutPopup: typeof import('@nutui/nutui-taro')['Popup'] |
| 25 | NutRow: typeof import('@nutui/nutui-taro')['Row'] | 24 | NutRow: typeof import('@nutui/nutui-taro')['Row'] | ... | ... |
| 1 | <template> | 1 | <template> |
| 2 | - <view class="points-collector" :style="{ height: height }"> | 2 | + <view class="points-collector-container"> |
| 3 | - <!-- 积分规则提示 --> | 3 | + <!-- 头部slot --> |
| 4 | - <view class="points-rule-tip" @tap="handleGoToPointsRule"> | 4 | + <view v-if="$slots.header" class="points-collector-header"> |
| 5 | - <view class="tip-icon">💡</view> | 5 | + <slot name="header"></slot> |
| 6 | - <text class="tip-text">积分规则</text> | ||
| 7 | </view> | 6 | </view> |
| 8 | 7 | ||
| 9 | - <!-- 中心圆形显示总积分 --> | 8 | + <!-- 积分收集器主体 --> |
| 10 | - <view class="center-circle"> | 9 | + <view class="points-collector" :style="{ height: height }"> |
| 11 | - <view class="total-points" @tap="handleGoToRewards"> | 10 | + <!-- 积分规则提示 --> |
| 12 | - <text v-if="!isOwner" class="family-points-label">家庭总积分</text> | 11 | + <view class="points-rule-tip" @tap="handleGoToPointsRule"> |
| 13 | - <text class="points-number" :style="{ fontSize: dynamicFontSize }">{{ animatedTotalPoints }}分</text> | 12 | + <view class="tip-icon">💡</view> |
| 14 | - <text v-if="isOwner" class="points-label">去兑换</text> | 13 | + <text class="tip-text">积分规则</text> |
| 14 | + </view> | ||
| 15 | + | ||
| 16 | + <!-- 中心圆形显示总积分 --> | ||
| 17 | + <view class="center-circle"> | ||
| 18 | + <view class="total-points" @tap="handleGoToRewards"> | ||
| 19 | + <text v-if="!isOwner" class="family-points-label">家庭总积分</text> | ||
| 20 | + <text class="points-number" :style="{ fontSize: dynamicFontSize }">{{ animatedTotalPoints }}分</text> | ||
| 21 | + <text v-if="isOwner" class="points-label">去兑换</text> | ||
| 22 | + </view> | ||
| 15 | </view> | 23 | </view> |
| 16 | - </view> | ||
| 17 | 24 | ||
| 18 | - <!-- 周围漂浮的小圆圈 --> | 25 | + <!-- 周围漂浮的小圆圈 --> |
| 19 | - <view | 26 | + <view |
| 20 | - v-for="(item) in floatingItems" | 27 | + v-for="(item) in floatingItems" |
| 21 | - :key="item.id" | 28 | + :key="item.id" |
| 22 | - class="floating-item" | 29 | + class="floating-item" |
| 23 | - :style="getItemStyle(item)" | 30 | + :style="getItemStyle(item)" |
| 24 | - @tap="collectItem(item)" | 31 | + @tap="collectItem(item)" |
| 25 | - > | 32 | + > |
| 26 | - <view class="item-content"> | 33 | + <view class="item-content"> |
| 27 | - <text class="item-value">{{ item.sourceLabel }}</text> | 34 | + <text class="item-value">{{ item.sourceLabel }}</text> |
| 28 | - <text class="item-type">{{ item.points }}分</text> | 35 | + <text class="item-type">{{ item.points }}分</text> |
| 36 | + </view> | ||
| 29 | </view> | 37 | </view> |
| 38 | + | ||
| 39 | + <!-- 一键收取按钮 --> | ||
| 40 | + <!-- <view | ||
| 41 | + v-if="floatingItems.length > 0" | ||
| 42 | + class="collect-all-btn" | ||
| 43 | + @tap="collectAll" | ||
| 44 | + > | ||
| 45 | + <text>一键收取</text> | ||
| 46 | + </view> --> | ||
| 30 | </view> | 47 | </view> |
| 31 | 48 | ||
| 32 | - <!-- 一键收取按钮 --> | 49 | + <!-- 底部slot --> |
| 33 | - <!-- <view | 50 | + <view v-if="$slots.footer" class="points-collector-footer"> |
| 34 | - v-if="floatingItems.length > 0" | 51 | + <slot name="footer"></slot> |
| 35 | - class="collect-all-btn" | 52 | + </view> |
| 36 | - @tap="collectAll" | ||
| 37 | - > | ||
| 38 | - <text>一键收取</text> | ||
| 39 | - </view> --> | ||
| 40 | </view> | 53 | </view> |
| 41 | </template> | 54 | </template> |
| 42 | 55 | ||
| ... | @@ -435,9 +448,22 @@ const handleGoToPointsRule = () => { | ... | @@ -435,9 +448,22 @@ const handleGoToPointsRule = () => { |
| 435 | </script> | 448 | </script> |
| 436 | 449 | ||
| 437 | <style lang="less"> | 450 | <style lang="less"> |
| 451 | +.points-collector-container { | ||
| 452 | + background: white; | ||
| 453 | + border-radius: 24rpx; | ||
| 454 | + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08); | ||
| 455 | + margin: 32rpx; | ||
| 456 | + overflow: hidden; | ||
| 457 | +} | ||
| 458 | + | ||
| 459 | +.points-collector-header, | ||
| 460 | +.points-collector-footer { | ||
| 461 | + padding: 40rpx; | ||
| 462 | +} | ||
| 463 | + | ||
| 438 | .points-collector { | 464 | .points-collector { |
| 439 | position: relative; | 465 | position: relative; |
| 440 | - width: 100vw; | 466 | + width: 100%; |
| 441 | height: 100vh; | 467 | height: 100vh; |
| 442 | // background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | 468 | // background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| 443 | overflow: hidden; | 469 | overflow: hidden; |
| ... | @@ -447,7 +473,7 @@ const handleGoToPointsRule = () => { | ... | @@ -447,7 +473,7 @@ const handleGoToPointsRule = () => { |
| 447 | position: absolute; | 473 | position: absolute; |
| 448 | // background-color: white; | 474 | // background-color: white; |
| 449 | top: 0rpx; | 475 | top: 0rpx; |
| 450 | - right: 0rpx; | 476 | + right: 15rpx; |
| 451 | display: flex; | 477 | display: flex; |
| 452 | flex-direction: column; | 478 | flex-direction: column; |
| 453 | align-items: center; | 479 | align-items: center; | ... | ... |
| 1 | <template> | 1 | <template> |
| 2 | - <view class="total-points-display" :style="{ height: height }"> | 2 | + <view class="total-points-display-container"> |
| 3 | - <!-- 积分规则提示 --> | 3 | + <!-- 头部slot --> |
| 4 | - <view class="points-rule-tip" @tap="handleGoToPointsRule"> | 4 | + <view v-if="$slots.header" class="total-points-display-header"> |
| 5 | - <view class="tip-icon">💡</view> | 5 | + <slot name="header"></slot> |
| 6 | - <text class="tip-text">积分规则</text> | ||
| 7 | </view> | 6 | </view> |
| 8 | 7 | ||
| 9 | - <!-- 中心圆形显示总积分 --> | 8 | + <!-- 积分显示器主体 --> |
| 10 | - <view class="center-circle1"> | 9 | + <view class="total-points-display" :style="{ height: height }"> |
| 11 | - <view class="total-points" @tap="handleGoToRewards"> | 10 | + <!-- 积分规则提示 --> |
| 12 | - <text v-if="!isOwner" class="family-points-label">家庭总积分</text> | 11 | + <view class="points-rule-tip" @tap="handleGoToPointsRule"> |
| 13 | - <text class="points-number" :style="{ fontSize: dynamicFontSize }">{{ animatedTotalPoints }}分</text> | 12 | + <view class="tip-icon">💡</view> |
| 14 | - <text v-if="isOwner" class="points-label">去兑换</text> | 13 | + <text class="tip-text">积分规则</text> |
| 14 | + </view> | ||
| 15 | + | ||
| 16 | + <!-- 中心圆形显示总积分 --> | ||
| 17 | + <view class="center-circle1"> | ||
| 18 | + <view class="total-points" @tap="handleGoToRewards"> | ||
| 19 | + <text v-if="!isOwner" class="family-points-label">家庭总积分</text> | ||
| 20 | + <text class="points-number" :style="{ fontSize: dynamicFontSize }">{{ animatedTotalPoints }}分</text> | ||
| 21 | + <text v-if="isOwner" class="points-label">去兑换</text> | ||
| 22 | + </view> | ||
| 15 | </view> | 23 | </view> |
| 16 | </view> | 24 | </view> |
| 25 | + | ||
| 26 | + <!-- 底部slot --> | ||
| 27 | + <view v-if="$slots.footer" class="total-points-display-footer"> | ||
| 28 | + <slot name="footer"></slot> | ||
| 29 | + </view> | ||
| 17 | </view> | 30 | </view> |
| 18 | </template> | 31 | </template> |
| 19 | 32 | ||
| ... | @@ -121,9 +134,22 @@ const handleGoToPointsRule = () => { | ... | @@ -121,9 +134,22 @@ const handleGoToPointsRule = () => { |
| 121 | </script> | 134 | </script> |
| 122 | 135 | ||
| 123 | <style lang="less"> | 136 | <style lang="less"> |
| 137 | +.total-points-display-container { | ||
| 138 | + background: white; | ||
| 139 | + border-radius: 24rpx; | ||
| 140 | + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08); | ||
| 141 | + margin: 32rpx; | ||
| 142 | + overflow: hidden; | ||
| 143 | +} | ||
| 144 | + | ||
| 145 | +.total-points-display-header, | ||
| 146 | +.total-points-display-footer { | ||
| 147 | + padding: 40rpx; | ||
| 148 | +} | ||
| 149 | + | ||
| 124 | .total-points-display { | 150 | .total-points-display { |
| 125 | position: relative; | 151 | position: relative; |
| 126 | - width: 100vw; | 152 | + width: 100%; |
| 127 | overflow: hidden; | 153 | overflow: hidden; |
| 128 | display: flex; | 154 | display: flex; |
| 129 | justify-content: center; | 155 | justify-content: center; |
| ... | @@ -134,7 +160,7 @@ const handleGoToPointsRule = () => { | ... | @@ -134,7 +160,7 @@ const handleGoToPointsRule = () => { |
| 134 | position: absolute; | 160 | position: absolute; |
| 135 | // background-color: white; | 161 | // background-color: white; |
| 136 | top: 0rpx; | 162 | top: 0rpx; |
| 137 | - right: 0rpx; | 163 | + right: 10rpx; |
| 138 | display: flex; | 164 | display: flex; |
| 139 | flex-direction: column; | 165 | flex-direction: column; |
| 140 | align-items: center; | 166 | align-items: center; | ... | ... |
| 1 | <template> | 1 | <template> |
| 2 | <view class="min-h-screen flex flex-col bg-white pb-16" style="background-color: #F9FAFB;"> | 2 | <view class="min-h-screen flex flex-col bg-white pb-16" style="background-color: #F9FAFB;"> |
| 3 | <!-- Hero section with family name and background image --> | 3 | <!-- Hero section with family name and background image --> |
| 4 | - <view class="relative h-48"> | 4 | + <view class="relative" style="height: 30vh;"> |
| 5 | - <image :src="familyCover" alt="Family background" class="w-full h-full object-cover" /> | 5 | + <image :src="familyCover" mode="aspectFill" alt="Family background" class="w-full h-full object-cover" /> |
| 6 | <view class="absolute inset-0 bg-black bg-opacity-30 flex flex-col justify-end p-5"> | 6 | <view class="absolute inset-0 bg-black bg-opacity-30 flex flex-col justify-end p-5"> |
| 7 | <view v-if="familyOwner" class="absolute top-4 right-4 text-white flex items-center" @click="goToProfile"> | 7 | <view v-if="familyOwner" class="absolute top-4 right-4 text-white flex items-center" @click="goToProfile"> |
| 8 | <Setting size="24" /> | 8 | <Setting size="24" /> |
| ... | @@ -22,57 +22,85 @@ | ... | @@ -22,57 +22,85 @@ |
| 22 | @sync-failed="handleSyncFailed" | 22 | @sync-failed="handleSyncFailed" |
| 23 | > | 23 | > |
| 24 | <template #default> | 24 | <template #default> |
| 25 | - <!-- Today's steps section --> | 25 | + <!-- 合并的大卡片:今日步数 + 积分收集器 + 拍照留念 --> |
| 26 | - <view class="px-5 py-6 bg-white rounded-xl shadow-md mx-4 mt-4"> | 26 | + <template v-if="!showTotalPointsOnly"> |
| 27 | - <view class="flex justify-between items-center mb-1"> | 27 | + <PointsCollector |
| 28 | - <span class="text-gray-500">今日</span> | 28 | + ref="pointsCollectorRef" |
| 29 | - </view> | 29 | + height="30vh" |
| 30 | - <view class="flex justify-between items-center"> | 30 | + :total-points="finalTotalPoints" |
| 31 | - <view class="flex items-baseline"> | 31 | + :pending-points="pendingPoints" |
| 32 | - <span class="text-4xl font-bold"> | 32 | + :family-id="family_id" |
| 33 | - {{ todaySteps?.toLocaleString() }} | 33 | + :is-owner="familyOwner" |
| 34 | - </span> | 34 | + @collection-complete="handleCollectionComplete" |
| 35 | - <span class="ml-1 text-gray-500">步</span> | 35 | + > |
| 36 | - </view> | 36 | + <!-- 头部:今日步数 --> |
| 37 | - <view class="flex gap-2"> | 37 | + <template #header> |
| 38 | - <view class="bg-blue-500 text-white px-4 py-2 rounded-full text-sm" @click="handleCollectAll"> | 38 | + <view class="flex justify-between items-center mb-1"> |
| 39 | - 一键收取 | 39 | + <span class="text-gray-500">今日</span> |
| 40 | </view> | 40 | </view> |
| 41 | - </view> | 41 | + <view class="flex justify-between items-center"> |
| 42 | - </view> | 42 | + <view class="flex items-baseline"> |
| 43 | - </view> | 43 | + <span class="text-4xl font-bold"> |
| 44 | - | 44 | + {{ todaySteps?.toLocaleString() }} |
| 45 | - <!-- Points circles --> | 45 | + </span> |
| 46 | - <view class="flex justify-between px-3 py-3 my-4 bg-white rounded-xl shadow-md mx-4"> | 46 | + <span class="ml-1 text-gray-500">步</span> |
| 47 | - <template v-if="!showTotalPointsOnly"> | 47 | + </view> |
| 48 | - <PointsCollector | 48 | + <view class="flex gap-2"> |
| 49 | - ref="pointsCollectorRef" | 49 | + <view class="bg-blue-500 text-white px-4 py-2 rounded-full text-sm" @click="handleCollectAll"> |
| 50 | - height="30vh" | 50 | + 一键收取 |
| 51 | - :total-points="finalTotalPoints" | 51 | + </view> |
| 52 | - :pending-points="pendingPoints" | 52 | + </view> |
| 53 | - :family-id="family_id" | 53 | + </view> |
| 54 | - :is-owner="familyOwner" | 54 | + </template> |
| 55 | - @collection-complete="handleCollectionComplete" | 55 | + |
| 56 | - /> | 56 | + <!-- 底部:拍照留念 --> |
| 57 | - </template> | 57 | + <template #footer> |
| 58 | - <template v-else> | 58 | + <view @tap="openCamera" class="w-full bg-blue-500 text-white py-3 rounded-lg flex flex-col items-center justify-center"> |
| 59 | - <TotalPointsDisplay | 59 | + <view class="flex items-center justify-center"> |
| 60 | - :total-points="finalTotalPoints" | 60 | + <Photograph size="20" class="mr-2" /> |
| 61 | - :is-owner="familyOwner" | 61 | + 拍照留念,奖励积分 |
| 62 | - height="13vh" | 62 | + </view> |
| 63 | - /> | 63 | + </view> |
| 64 | - </template> | 64 | + </template> |
| 65 | - </view> | 65 | + </PointsCollector> |
| 66 | - | 66 | + </template> |
| 67 | - <!-- Photo button --> | 67 | + <template v-else> |
| 68 | - <view class="px-5 mb-4"> | 68 | + <TotalPointsDisplay |
| 69 | - <view @tap="openCamera" class="w-full bg-blue-500 text-white py-3 rounded-lg flex flex-col items-center justify-center"> | 69 | + :total-points="finalTotalPoints" |
| 70 | - <view class="flex items-center justify-center"> | 70 | + :is-owner="familyOwner" |
| 71 | - <Photograph size="20" class="mr-2" /> | 71 | + height="13vh" |
| 72 | - 拍照留念,奖励积分 | 72 | + > |
| 73 | - </view> | 73 | + <!-- 头部:今日步数 --> |
| 74 | - </view> | 74 | + <template #header> |
| 75 | - </view> | 75 | + <view class="flex justify-between items-center mb-1"> |
| 76 | + <span class="text-gray-500">今日</span> | ||
| 77 | + </view> | ||
| 78 | + <view class="flex justify-between items-center"> | ||
| 79 | + <view class="flex items-baseline"> | ||
| 80 | + <span class="text-4xl font-bold"> | ||
| 81 | + {{ todaySteps?.toLocaleString() }} | ||
| 82 | + </span> | ||
| 83 | + <span class="ml-1 text-gray-500">步</span> | ||
| 84 | + </view> | ||
| 85 | + <view class="flex gap-2"> | ||
| 86 | + <view class="bg-blue-500 text-white px-4 py-2 rounded-full text-sm" @click="handleCollectAll"> | ||
| 87 | + 一键收取 | ||
| 88 | + </view> | ||
| 89 | + </view> | ||
| 90 | + </view> | ||
| 91 | + </template> | ||
| 92 | + | ||
| 93 | + <!-- 底部:拍照留念 --> | ||
| 94 | + <template #footer> | ||
| 95 | + <view @tap="openCamera" class="w-full bg-blue-500 text-white py-3 rounded-lg flex flex-col items-center justify-center"> | ||
| 96 | + <view class="flex items-center justify-center"> | ||
| 97 | + <Photograph size="20" class="mr-2" /> | ||
| 98 | + 拍照留念,奖励积分 | ||
| 99 | + </view> | ||
| 100 | + </view> | ||
| 101 | + </template> | ||
| 102 | + </TotalPointsDisplay> | ||
| 103 | + </template> | ||
| 76 | 104 | ||
| 77 | <!-- Family step ranking --> | 105 | <!-- Family step ranking --> |
| 78 | <view class="p-5 bg-white rounded-xl shadow-md mx-4"> | 106 | <view class="p-5 bg-white rounded-xl shadow-md mx-4"> | ... | ... |
-
Please register or login to post a comment