feat: 添加权限控制显示兑换相关功能
根据用户角色控制显示兑换按钮和积分信息 在多个组件中添加isOwner/isCreator判断 修改头像显示模式为aspectFill
Showing
7 changed files
with
84 additions
and
17 deletions
| ... | @@ -3,8 +3,9 @@ | ... | @@ -3,8 +3,9 @@ |
| 3 | <!-- 中心圆形显示总积分 --> | 3 | <!-- 中心圆形显示总积分 --> |
| 4 | <view class="center-circle"> | 4 | <view class="center-circle"> |
| 5 | <view class="total-points" @tap="handleGoToRewards"> | 5 | <view class="total-points" @tap="handleGoToRewards"> |
| 6 | + <text v-if="!isOwner" class="family-points-label">家庭总积分</text> | ||
| 6 | <text class="points-number">{{ animatedTotalPoints }}分</text> | 7 | <text class="points-number">{{ animatedTotalPoints }}分</text> |
| 7 | - <text class="points-label">去兑换</text> | 8 | + <text v-if="isOwner" class="points-label">去兑换</text> |
| 8 | </view> | 9 | </view> |
| 9 | </view> | 10 | </view> |
| 10 | 11 | ||
| ... | @@ -55,7 +56,11 @@ const props = defineProps({ | ... | @@ -55,7 +56,11 @@ const props = defineProps({ |
| 55 | familyId: { | 56 | familyId: { |
| 56 | type: [String, Number], | 57 | type: [String, Number], |
| 57 | required: true | 58 | required: true |
| 58 | - } | 59 | + }, |
| 60 | + isOwner: { | ||
| 61 | + type: Boolean, | ||
| 62 | + default: false | ||
| 63 | + }, | ||
| 59 | }) | 64 | }) |
| 60 | 65 | ||
| 61 | // 响应式数据 | 66 | // 响应式数据 |
| ... | @@ -80,7 +85,7 @@ const generatePointsData = () => { | ... | @@ -80,7 +85,7 @@ const generatePointsData = () => { |
| 80 | if (!props.pendingPoints || props.pendingPoints.length === 0) { | 85 | if (!props.pendingPoints || props.pendingPoints.length === 0) { |
| 81 | return []; | 86 | return []; |
| 82 | } | 87 | } |
| 83 | - | 88 | + |
| 84 | const pointsItems = props.pendingPoints.map(item => ({ | 89 | const pointsItems = props.pendingPoints.map(item => ({ |
| 85 | id: item.id, | 90 | id: item.id, |
| 86 | points: parseInt(item.points), | 91 | points: parseInt(item.points), |
| ... | @@ -335,6 +340,9 @@ useDidShow(() => { | ... | @@ -335,6 +340,9 @@ useDidShow(() => { |
| 335 | * 处理去兑换点击事件 | 340 | * 处理去兑换点击事件 |
| 336 | */ | 341 | */ |
| 337 | const handleGoToRewards = () => { | 342 | const handleGoToRewards = () => { |
| 343 | + if (!props.isOwner) { | ||
| 344 | + return | ||
| 345 | + } | ||
| 338 | Taro.navigateTo({ | 346 | Taro.navigateTo({ |
| 339 | url: '/pages/RewardCategories/index', | 347 | url: '/pages/RewardCategories/index', |
| 340 | }) | 348 | }) |
| ... | @@ -382,7 +390,14 @@ const handleGoToRewards = () => { | ... | @@ -382,7 +390,14 @@ const handleGoToRewards = () => { |
| 382 | .points-label { | 390 | .points-label { |
| 383 | display: block; | 391 | display: block; |
| 384 | font-size: 24rpx; | 392 | font-size: 24rpx; |
| 385 | - margin-top: 8rpx; | 393 | + margin-top: 10rpx; |
| 394 | + opacity: 0.9; | ||
| 395 | +} | ||
| 396 | + | ||
| 397 | +.family-points-label { | ||
| 398 | + display: block; | ||
| 399 | + font-size: 24rpx; | ||
| 400 | + margin-bottom: 10rpx; | ||
| 386 | opacity: 0.9; | 401 | opacity: 0.9; |
| 387 | } | 402 | } |
| 388 | 403 | ... | ... |
| ... | @@ -3,8 +3,9 @@ | ... | @@ -3,8 +3,9 @@ |
| 3 | <!-- 中心圆形显示总积分 --> | 3 | <!-- 中心圆形显示总积分 --> |
| 4 | <view class="center-circle1"> | 4 | <view class="center-circle1"> |
| 5 | <view class="total-points" @tap="handleGoToRewards"> | 5 | <view class="total-points" @tap="handleGoToRewards"> |
| 6 | + <text v-if="!isOwner" class="family-points-label">家庭总积分</text> | ||
| 6 | <text class="points-number">{{ animatedTotalPoints }}分</text> | 7 | <text class="points-number">{{ animatedTotalPoints }}分</text> |
| 7 | - <text class="points-label">去兑换</text> | 8 | + <text v-if="isOwner" class="points-label">去兑换</text> |
| 8 | </view> | 9 | </view> |
| 9 | </view> | 10 | </view> |
| 10 | </view> | 11 | </view> |
| ... | @@ -24,6 +25,10 @@ const props = defineProps({ | ... | @@ -24,6 +25,10 @@ const props = defineProps({ |
| 24 | height: { | 25 | height: { |
| 25 | type: String, | 26 | type: String, |
| 26 | default: '30vh' | 27 | default: '30vh' |
| 28 | + }, | ||
| 29 | + isOwner: { | ||
| 30 | + type: Boolean, | ||
| 31 | + default: false | ||
| 27 | } | 32 | } |
| 28 | }) | 33 | }) |
| 29 | 34 | ||
| ... | @@ -72,6 +77,9 @@ onMounted(() => { | ... | @@ -72,6 +77,9 @@ onMounted(() => { |
| 72 | * 处理去兑换点击事件 | 77 | * 处理去兑换点击事件 |
| 73 | */ | 78 | */ |
| 74 | const handleGoToRewards = () => { | 79 | const handleGoToRewards = () => { |
| 80 | + if (!props.isOwner) { | ||
| 81 | + return | ||
| 82 | + } | ||
| 75 | Taro.navigateTo({ | 83 | Taro.navigateTo({ |
| 76 | url: '/pages/RewardCategories/index', | 84 | url: '/pages/RewardCategories/index', |
| 77 | }) | 85 | }) |
| ... | @@ -116,10 +124,17 @@ const handleGoToRewards = () => { | ... | @@ -116,10 +124,17 @@ const handleGoToRewards = () => { |
| 116 | line-height: 1; | 124 | line-height: 1; |
| 117 | } | 125 | } |
| 118 | 126 | ||
| 127 | +.family-points-label { | ||
| 128 | + display: block; | ||
| 129 | + font-size: 24rpx; | ||
| 130 | + margin-bottom: 10rpx; | ||
| 131 | + opacity: 0.9; | ||
| 132 | +} | ||
| 133 | + | ||
| 119 | .points-label { | 134 | .points-label { |
| 120 | display: block; | 135 | display: block; |
| 121 | font-size: 24rpx; | 136 | font-size: 24rpx; |
| 122 | - margin-top: 8rpx; | 137 | + margin-top: 10rpx; |
| 123 | opacity: 0.9; | 138 | opacity: 0.9; |
| 124 | } | 139 | } |
| 125 | 140 | ... | ... |
| ... | @@ -55,12 +55,14 @@ | ... | @@ -55,12 +55,14 @@ |
| 55 | :total-points="finalTotalPoints" | 55 | :total-points="finalTotalPoints" |
| 56 | :pending-points="pendingPoints" | 56 | :pending-points="pendingPoints" |
| 57 | :family-id="family_id" | 57 | :family-id="family_id" |
| 58 | + :is-owner="familyOwner" | ||
| 58 | @collection-complete="handleCollectionComplete" | 59 | @collection-complete="handleCollectionComplete" |
| 59 | /> | 60 | /> |
| 60 | </template> | 61 | </template> |
| 61 | <template v-else> | 62 | <template v-else> |
| 62 | <TotalPointsDisplay | 63 | <TotalPointsDisplay |
| 63 | :total-points="finalTotalPoints" | 64 | :total-points="finalTotalPoints" |
| 65 | + :is-owner="familyOwner" | ||
| 64 | height="13vh" | 66 | height="13vh" |
| 65 | /> | 67 | /> |
| 66 | </template> | 68 | </template> |
| ... | @@ -86,7 +88,7 @@ | ... | @@ -86,7 +88,7 @@ |
| 86 | </view> | 88 | </view> |
| 87 | <view class="grid grid-cols-4 gap-2"> | 89 | <view class="grid grid-cols-4 gap-2"> |
| 88 | <view v-for="member in familyMembers" :key="member.user_id" class="flex flex-col items-center"> | 90 | <view v-for="member in familyMembers" :key="member.user_id" class="flex flex-col items-center"> |
| 89 | - <image :src="member.avatar_url || defaultAvatar" :alt="member.role" class="w-16 h-16 rounded-full mb-1" /> | 91 | + <image :src="member.avatar_url || defaultAvatar" mode="aspectFill" :alt="member.role" class="w-16 h-16 rounded-full mb-1" /> |
| 90 | <span class="text-sm text-gray-700"> | 92 | <span class="text-sm text-gray-700"> |
| 91 | {{ member?.today_step?.toLocaleString() }}步 | 93 | {{ member?.today_step?.toLocaleString() }}步 |
| 92 | </span> | 94 | </span> | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-08-27 17:47:46 | 2 | * @Date: 2025-08-27 17:47:46 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-09-03 20:02:11 | 4 | + * @LastEditTime: 2025-09-03 21:30:51 |
| 5 | * @FilePath: /lls_program/src/pages/Profile/index.vue | 5 | * @FilePath: /lls_program/src/pages/Profile/index.vue |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | --> | 7 | --> |
| ... | @@ -14,7 +14,7 @@ | ... | @@ -14,7 +14,7 @@ |
| 14 | <!-- User profile section --> | 14 | <!-- User profile section --> |
| 15 | <view class="px-4 pt-8 pb-6 flex items-center justify-between"> | 15 | <view class="px-4 pt-8 pb-6 flex items-center justify-between"> |
| 16 | <view class="flex items-center"> | 16 | <view class="flex items-center"> |
| 17 | - <image :src="userInfo.avatarUrl || defaultAvatar" alt="User avatar" class="w-16 h-16 rounded-full border-2 border-white" /> | 17 | + <image :src="userInfo.avatarUrl || defaultAvatar" alt="User avatar" mode="aspectFill" class="w-16 h-16 rounded-full border-2 border-white" /> |
| 18 | <view class="ml-4"> | 18 | <view class="ml-4"> |
| 19 | <h1 class="text-xl font-bold text-white">{{ userInfo.nickName }}</h1> | 19 | <h1 class="text-xl font-bold text-white">{{ userInfo.nickName }}</h1> |
| 20 | </view> | 20 | </view> | ... | ... |
| ... | @@ -54,7 +54,7 @@ | ... | @@ -54,7 +54,7 @@ |
| 54 | </view> | 54 | </view> |
| 55 | 55 | ||
| 56 | <!-- Bottom Button --> | 56 | <!-- Bottom Button --> |
| 57 | - <view class="fixed bottom-0 left-0 right-0 p-4 bg-white border-t border-gray-100"> | 57 | + <view v-if="isCreator" class="fixed bottom-0 left-0 right-0 p-4 bg-white border-t border-gray-100"> |
| 58 | <nut-button type="primary" size="large" block color="#3B82F6" @click="handleRedeem"> | 58 | <nut-button type="primary" size="large" block color="#3B82F6" @click="handleRedeem"> |
| 59 | 立即兑换 | 59 | 立即兑换 |
| 60 | </nut-button> | 60 | </nut-button> |
| ... | @@ -64,8 +64,11 @@ | ... | @@ -64,8 +64,11 @@ |
| 64 | 64 | ||
| 65 | <script setup> | 65 | <script setup> |
| 66 | import { ref } from 'vue'; | 66 | import { ref } from 'vue'; |
| 67 | -import Taro from '@tarojs/taro'; | 67 | +import Taro, { useDidShow } from '@tarojs/taro'; |
| 68 | import AppHeader from '../../components/AppHeader.vue'; | 68 | import AppHeader from '../../components/AppHeader.vue'; |
| 69 | +import { getUserProfileAPI } from '@/api/user'; | ||
| 70 | + | ||
| 71 | +const isCreator = ref(false); | ||
| 69 | 72 | ||
| 70 | // Mock reward data based on the image | 73 | // Mock reward data based on the image |
| 71 | const reward = ref({ | 74 | const reward = ref({ |
| ... | @@ -120,4 +123,21 @@ const handleRedeem = () => { | ... | @@ -120,4 +123,21 @@ const handleRedeem = () => { |
| 120 | } | 123 | } |
| 121 | }); | 124 | }); |
| 122 | }; | 125 | }; |
| 126 | + | ||
| 127 | +const initData = async () => { | ||
| 128 | + // 获取用户信息,判断是否为创建者 | ||
| 129 | + try { | ||
| 130 | + const { code, data } = await getUserProfileAPI(); | ||
| 131 | + if (code) { | ||
| 132 | + isCreator.value = data?.user?.is_creator || false; | ||
| 133 | + } | ||
| 134 | + } catch (error) { | ||
| 135 | + console.error('获取用户信息失败:', error); | ||
| 136 | + isCreator.value = false; | ||
| 137 | + } | ||
| 138 | +}; | ||
| 139 | + | ||
| 140 | +useDidShow(() => { | ||
| 141 | + initData(); | ||
| 142 | +}); | ||
| 123 | </script> | 143 | </script> | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-08-27 17:47:26 | 2 | * @Date: 2025-08-27 17:47:26 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-08-29 20:32:08 | 4 | + * @LastEditTime: 2025-09-03 21:43:41 |
| 5 | * @FilePath: /lls_program/src/pages/Rewards/index.vue | 5 | * @FilePath: /lls_program/src/pages/Rewards/index.vue |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | --> | 7 | --> |
| 8 | <template> | 8 | <template> |
| 9 | <view class="min-h-screen flex flex-col bg-white"> | 9 | <view class="min-h-screen flex flex-col bg-white"> |
| 10 | <!-- Blue header background --> | 10 | <!-- Blue header background --> |
| 11 | - <view class="bg-blue-500 h-48 absolute w-full top-0 left-0 z-0"></view> | 11 | + <view v-if="isCreator" class="bg-blue-500 h-48 absolute w-full top-0 left-0 z-0"></view> |
| 12 | <!-- <AppHeader title="兑换中心" :showBack="false" /> --> | 12 | <!-- <AppHeader title="兑换中心" :showBack="false" /> --> |
| 13 | <!-- Content --> | 13 | <!-- Content --> |
| 14 | <view class="relative z-10 flex-1 pb-20"> | 14 | <view class="relative z-10 flex-1 pb-20"> |
| 15 | <!-- Points display --> | 15 | <!-- Points display --> |
| 16 | - <view class="pt-8 pb-8 flex flex-col items-center"> | 16 | + <view v-if="isCreator" class="pt-8 pb-8 flex flex-col items-center"> |
| 17 | <h2 class="text-4xl font-bold text-white mb-1">{{ totalPoints }}分</h2> | 17 | <h2 class="text-4xl font-bold text-white mb-1">{{ totalPoints }}分</h2> |
| 18 | <p class="text-white text-opacity-80">我的积分</p> | 18 | <p class="text-white text-opacity-80">我的积分</p> |
| 19 | </view> | 19 | </view> |
| ... | @@ -60,7 +60,7 @@ | ... | @@ -60,7 +60,7 @@ |
| 60 | </view> | 60 | </view> |
| 61 | <view class="ml-4 px-4 py-2 bg-blue-500 text-white rounded-lg text-sm flex-shrink-0" | 61 | <view class="ml-4 px-4 py-2 bg-blue-500 text-white rounded-lg text-sm flex-shrink-0" |
| 62 | @click="goToRewardDetail(reward)"> | 62 | @click="goToRewardDetail(reward)"> |
| 63 | - {{ reward.points }}分兑换 | 63 | + {{ isCreator ? reward.points + '分兑换' : '查看' }} |
| 64 | </view> | 64 | </view> |
| 65 | </view> | 65 | </view> |
| 66 | </view> | 66 | </view> |
| ... | @@ -74,12 +74,14 @@ | ... | @@ -74,12 +74,14 @@ |
| 74 | import { ref, computed, onMounted } from 'vue'; | 74 | import { ref, computed, onMounted } from 'vue'; |
| 75 | import Taro, { useDidShow } from '@tarojs/taro'; | 75 | import Taro, { useDidShow } from '@tarojs/taro'; |
| 76 | import { ScreenLittle, Search2 } from '@nutui/icons-vue-taro'; | 76 | import { ScreenLittle, Search2 } from '@nutui/icons-vue-taro'; |
| 77 | +import { getUserProfileAPI } from '@/api/user'; | ||
| 77 | 78 | ||
| 78 | const searchQuery = ref(''); | 79 | const searchQuery = ref(''); |
| 79 | const selectedPoints = ref(null); | 80 | const selectedPoints = ref(null); |
| 80 | const sortOrder = ref('desc'); // 'asc' or 'desc' | 81 | const sortOrder = ref('desc'); // 'asc' or 'desc' |
| 81 | 82 | ||
| 82 | const totalPoints = ref(0); | 83 | const totalPoints = ref(0); |
| 84 | +const isCreator = ref(false); | ||
| 83 | 85 | ||
| 84 | const sortedRewardItems = computed(() => { | 86 | const sortedRewardItems = computed(() => { |
| 85 | let items = [...rewardItems.value]; | 87 | let items = [...rewardItems.value]; |
| ... | @@ -156,7 +158,20 @@ const goToRewardDetail = (reward) => { | ... | @@ -156,7 +158,20 @@ const goToRewardDetail = (reward) => { |
| 156 | }; | 158 | }; |
| 157 | 159 | ||
| 158 | const initData = async () => { | 160 | const initData = async () => { |
| 159 | - totalPoints.value = '9856'; | 161 | + // 获取用户信息,判断是否为创建者 |
| 162 | + try { | ||
| 163 | + const { code, data } = await getUserProfileAPI(); | ||
| 164 | + if (code) { | ||
| 165 | + isCreator.value = data?.user?.is_creator || false; | ||
| 166 | + // 只有创建者才显示积分 | ||
| 167 | + if (isCreator.value) { | ||
| 168 | + totalPoints.value = '9856'; | ||
| 169 | + } | ||
| 170 | + } | ||
| 171 | + } catch (error) { | ||
| 172 | + console.error('获取用户信息失败:', error); | ||
| 173 | + isCreator.value = false; | ||
| 174 | + } | ||
| 160 | console.warn('初始化数据') | 175 | console.warn('初始化数据') |
| 161 | } | 176 | } |
| 162 | 177 | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2022-09-19 14:11:06 | 2 | * @Date: 2022-09-19 14:11:06 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-09-03 17:02:11 | 4 | + * @LastEditTime: 2025-09-03 21:05:25 |
| 5 | * @FilePath: /lls_program/src/pages/auth/index.vue | 5 | * @FilePath: /lls_program/src/pages/auth/index.vue |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | --> | 7 | --> | ... | ... |
-
Please register or login to post a comment