feat(user): 在用户页面新增角色展示功能

- 新增 state 用于存储用户角色列表
- 登录后调用 listUserRole 接口获取用户角色数据
- 在用户昵称下方展示用户角色,样式为蓝色圆角徽章
- 支持多角色展示,采用 flex-wrap 实现多行换行
- 下拉刷新时同步刷新用户角色信息
- 处理登录、退出及接口异常等边界情况,确保展示稳定
This commit is contained in:
2026-07-03 17:59:56 +08:00
parent f202d44b5b
commit 2deaaffe13
2 changed files with 36 additions and 2 deletions

View File

@@ -0,0 +1,8 @@
# 2026-07-03
## 用户页面新增角色展示
-`src/pages/user/user.tsx` 用户信息卡片中新增当前登录用户的系统角色展示
- 通过 `listUserRole` API (`GET /system/user-role?userId=xxx`) 获取用户角色列表
- 角色以蓝色圆角徽章形式展示在昵称下方,与会员等级徽章并排
- 支持多角色展示flex-wrap支持下拉刷新同步更新
- 登录/退出/API失败等边界情况均已处理

View File

@@ -6,6 +6,8 @@ import { useScrollHeight } from '@/hooks/useScrollHeight'
import { useRequest } from '@/hooks/useRequest' import { useRequest } from '@/hooks/useRequest'
import { getUserCardStats, getUserOrderStats, type UserCardStats, type UserOrderStats } from '@/api/shop/shopUserCard' import { getUserCardStats, getUserOrderStats, type UserCardStats, type UserOrderStats } from '@/api/shop/shopUserCard'
import { getMyClerk } from '@/api/shop/shopStoreUser' import { getMyClerk } from '@/api/shop/shopStoreUser'
import { listUserRole } from '@/api/system/userRole'
import type { UserRole } from '@/api/system/userRole/model'
import MemberBadge from '@/components/business/MemberBadge' import MemberBadge from '@/components/business/MemberBadge'
const UserPage: React.FC = () => { const UserPage: React.FC = () => {
@@ -15,6 +17,8 @@ const UserPage: React.FC = () => {
// 查询门店关联信息 // 查询门店关联信息
const [storeInfo, setStoreInfo] = useState<any>(null) const [storeInfo, setStoreInfo] = useState<any>(null)
// 用户角色列表
const [userRoles, setUserRoles] = useState<UserRole[]>([])
// 获取用户卡片统计(余额/积分/优惠券/礼品卡) // 获取用户卡片统计(余额/积分/优惠券/礼品卡)
// 使用 cacheKey 缓存 30s页面返回时先显示缓存数据再静默刷新 // 使用 cacheKey 缓存 30s页面返回时先显示缓存数据再静默刷新
@@ -38,8 +42,11 @@ const UserPage: React.FC = () => {
runOrderStats() runOrderStats()
// 查询门店关联 // 查询门店关联
getMyClerk().then(data => setStoreInfo(data)).catch(() => setStoreInfo(null)) getMyClerk().then(data => setStoreInfo(data)).catch(() => setStoreInfo(null))
// 查询用户角色
listUserRole({ userId: user?.userId }).then(data => setUserRoles(data || [])).catch(() => setUserRoles([]))
} else { } else {
setStoreInfo(null) setStoreInfo(null)
setUserRoles([])
} }
}, [isLoggedIn]) }, [isLoggedIn])
@@ -53,6 +60,11 @@ const UserPage: React.FC = () => {
runOrderStats() runOrderStats()
// 刷新门店关联 // 刷新门店关联
getMyClerk().then(data => setStoreInfo(data)).catch(() => setStoreInfo(null)) getMyClerk().then(data => setStoreInfo(data)).catch(() => setStoreInfo(null))
// 刷新用户角色
const userId = Taro.getStorageSync('UserId')
if (userId) {
listUserRole({ userId }).then(data => setUserRoles(data || [])).catch(() => {})
}
} }
}) })
@@ -88,12 +100,17 @@ const UserPage: React.FC = () => {
// 下拉刷新状态 // 下拉刷新状态
const [refreshing, setRefreshing] = useState(false) const [refreshing, setRefreshing] = useState(false)
// 下拉刷新:同时刷新用户信息、卡片统计、订单统计 // 下拉刷新:同时刷新用户信息、卡片统计、订单统计、角色
const onRefresh = async () => { const onRefresh = async () => {
if (!isLoggedIn) { setRefreshing(false); return } if (!isLoggedIn) { setRefreshing(false); return }
setRefreshing(true) setRefreshing(true)
try { try {
await Promise.all([refreshUser(), runCardStats(), runOrderStats()]) await Promise.all([refreshUser(), runCardStats(), runOrderStats()])
// 刷新角色
const uid = user?.userId
if (uid) {
listUserRole({ userId: uid }).then(data => setUserRoles(data || [])).catch(() => {})
}
} finally { } finally {
setRefreshing(false) setRefreshing(false)
} }
@@ -116,8 +133,17 @@ const UserPage: React.FC = () => {
<Text className='text-lg font-medium text-gray-800 block'> <Text className='text-lg font-medium text-gray-800 block'>
{isLoggedIn ? (user?.nickname || user?.phone || '用户') : '点击登录'} {isLoggedIn ? (user?.nickname || user?.phone || '用户') : '点击登录'}
</Text> </Text>
<View className='flex items-center gap-2 mt-1'> <View className='flex items-center gap-2 mt-1 flex-wrap'>
{isLoggedIn && <MemberBadge levelName={(user as any)?.memberLevelName} />} {isLoggedIn && <MemberBadge levelName={(user as any)?.memberLevelName} />}
{userRoles.map(role => (
<View
key={role.roleId}
className='inline-flex items-center rounded-full text-xs px-2 py-0.5'
style={{ background: '#eff6ff', color: '#1d4ed8', border: '1px solid #bfdbfe' }}
>
<Text>{role.roleName}</Text>
</View>
))}
</View> </View>
</View> </View>
{isLoggedIn && <Text className='text-gray-300 text-sm'>{'>'}</Text>} {isLoggedIn && <Text className='text-gray-300 text-sm'>{'>'}</Text>}