forked from gxwebsoft/mp-10550
feat(user): 实现 useUser Hook 并更新相关组件
- 新增 useUser Hook 用于全局用户状态管理 - 更新 UserCard 和 UserCell 组件,集成 useUser 功能 - 添加 UserProfile 组件示例 - 更新 API 引用,统一使用 useUser
This commit is contained in:
277
docs/useUser-hook-guide.md
Normal file
277
docs/useUser-hook-guide.md
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
# useUser Hook 使用指南
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
`useUser` hook 是一个用于管理用户状态的自定义 React Hook,类似于项目中的 `useCart` hook。它提供了用户登录状态管理、用户信息获取和更新、权限检查等功能,方便在整个应用中全局调用。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- ✅ 用户登录状态管理
|
||||||
|
- ✅ 用户信息本地存储和同步
|
||||||
|
- ✅ 从服务器获取最新用户信息
|
||||||
|
- ✅ 用户信息更新
|
||||||
|
- ✅ 权限和角色检查
|
||||||
|
- ✅ 实名认证状态检查
|
||||||
|
- ✅ 用户余额和积分获取
|
||||||
|
- ✅ 自动处理登录过期
|
||||||
|
|
||||||
|
## 基本用法
|
||||||
|
|
||||||
|
### 1. 导入 Hook
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { useUser } from '@/hooks/useUser';
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 在组件中使用
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const MyComponent = () => {
|
||||||
|
const {
|
||||||
|
user, // 用户信息
|
||||||
|
isLoggedIn, // 是否已登录
|
||||||
|
loading, // 加载状态
|
||||||
|
loginUser, // 登录方法
|
||||||
|
logoutUser, // 退出登录方法
|
||||||
|
fetchUserInfo, // 获取用户信息
|
||||||
|
updateUser, // 更新用户信息
|
||||||
|
getDisplayName, // 获取显示名称
|
||||||
|
isCertified, // 是否已实名认证
|
||||||
|
getBalance, // 获取余额
|
||||||
|
getPoints // 获取积分
|
||||||
|
} = useUser();
|
||||||
|
|
||||||
|
// 使用用户信息
|
||||||
|
if (loading) {
|
||||||
|
return <div>加载中...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
return <div>请先登录</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>欢迎,{getDisplayName()}</h1>
|
||||||
|
<p>余额:¥{getBalance()}</p>
|
||||||
|
<p>积分:{getPoints()}</p>
|
||||||
|
{isCertified() && <span>已实名认证</span>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## API 参考
|
||||||
|
|
||||||
|
### 状态属性
|
||||||
|
|
||||||
|
| 属性 | 类型 | 描述 |
|
||||||
|
|------|------|------|
|
||||||
|
| `user` | `User \| null` | 当前用户信息 |
|
||||||
|
| `isLoggedIn` | `boolean` | 用户是否已登录 |
|
||||||
|
| `loading` | `boolean` | 是否正在加载 |
|
||||||
|
|
||||||
|
### 方法
|
||||||
|
|
||||||
|
#### `loginUser(token: string, userInfo: User)`
|
||||||
|
用户登录,保存用户信息和 token 到本地存储。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const handleLogin = async () => {
|
||||||
|
const { access_token, user } = await loginApi(credentials);
|
||||||
|
loginUser(access_token, user);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `logoutUser()`
|
||||||
|
用户退出登录,清除本地存储的用户信息。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const handleLogout = () => {
|
||||||
|
logoutUser();
|
||||||
|
// 跳转到首页或登录页
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `fetchUserInfo()`
|
||||||
|
从服务器获取最新的用户信息。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const refreshUserInfo = async () => {
|
||||||
|
const userInfo = await fetchUserInfo();
|
||||||
|
console.log('最新用户信息:', userInfo);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `updateUser(userData: Partial<User>)`
|
||||||
|
更新用户信息。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const updateProfile = async () => {
|
||||||
|
try {
|
||||||
|
await updateUser({
|
||||||
|
nickname: '新昵称',
|
||||||
|
avatar: 'new-avatar-url'
|
||||||
|
});
|
||||||
|
console.log('更新成功');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 工具方法
|
||||||
|
|
||||||
|
#### `hasPermission(permission: string)`
|
||||||
|
检查用户是否有特定权限。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
if (hasPermission('user:edit')) {
|
||||||
|
// 显示编辑按钮
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `hasRole(roleCode: string)`
|
||||||
|
检查用户是否有特定角色。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
if (hasRole('admin')) {
|
||||||
|
// 显示管理员功能
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `getAvatarUrl()`
|
||||||
|
获取用户头像 URL。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
<Avatar src={getAvatarUrl()} />
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `getDisplayName()`
|
||||||
|
获取用户显示名称(优先级:昵称 > 真实姓名 > 用户名)。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
<span>欢迎,{getDisplayName()}</span>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `isCertified()`
|
||||||
|
检查用户是否已实名认证。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
{isCertified() && <Badge>已认证</Badge>}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `getBalance()`
|
||||||
|
获取用户余额。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
<span>余额:¥{getBalance()}</span>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `getPoints()`
|
||||||
|
获取用户积分。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
<span>积分:{getPoints()}</span>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
### 1. 用户资料页面
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const UserProfile = () => {
|
||||||
|
const { user, updateUser, getDisplayName, getAvatarUrl } = useUser();
|
||||||
|
|
||||||
|
const handleUpdateProfile = async (formData) => {
|
||||||
|
await updateUser(formData);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Avatar src={getAvatarUrl()} />
|
||||||
|
<h2>{getDisplayName()}</h2>
|
||||||
|
<ProfileForm onSubmit={handleUpdateProfile} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 权限控制
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const AdminPanel = () => {
|
||||||
|
const { hasRole, hasPermission } = useUser();
|
||||||
|
|
||||||
|
if (!hasRole('admin')) {
|
||||||
|
return <div>无权限访问</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{hasPermission('user:delete') && (
|
||||||
|
<Button danger>删除用户</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 登录状态检查
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const ProtectedComponent = () => {
|
||||||
|
const { isLoggedIn, loading } = useUser();
|
||||||
|
|
||||||
|
if (loading) return <Loading />;
|
||||||
|
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
return <LoginPrompt />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ProtectedContent />;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 用户余额显示
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const WalletCard = () => {
|
||||||
|
const { getBalance, getPoints, fetchUserInfo } = useUser();
|
||||||
|
|
||||||
|
const refreshBalance = () => {
|
||||||
|
fetchUserInfo(); // 刷新用户信息包括余额
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>余额:¥{getBalance()}</div>
|
||||||
|
<div>积分:{getPoints()}</div>
|
||||||
|
<Button onClick={refreshBalance}>刷新</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **自动登录过期处理**:当 API 返回 401 错误时,hook 会自动清除登录状态。
|
||||||
|
|
||||||
|
2. **本地存储同步**:用户信息会自动同步到本地存储,页面刷新后状态会保持。
|
||||||
|
|
||||||
|
3. **错误处理**:所有异步操作都包含错误处理,失败时会显示相应的提示信息。
|
||||||
|
|
||||||
|
4. **性能优化**:用户信息只在必要时从服务器获取,避免不必要的网络请求。
|
||||||
|
|
||||||
|
## 与 useCart 的对比
|
||||||
|
|
||||||
|
| 特性 | useCart | useUser |
|
||||||
|
|------|---------|---------|
|
||||||
|
| 数据存储 | 购物车商品 | 用户信息 |
|
||||||
|
| 本地持久化 | ✅ | ✅ |
|
||||||
|
| 服务器同步 | ❌ | ✅ |
|
||||||
|
| 状态管理 | ✅ | ✅ |
|
||||||
|
| 全局访问 | ✅ | ✅ |
|
||||||
|
| 权限控制 | ❌ | ✅ |
|
||||||
|
|
||||||
|
这样,用户信息管理就像购物车一样方便了,可以在任何组件中轻松访问和操作用户状态!
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { PageParam } from '@/api';
|
import type { PageParam } from '@/api/index';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 链接
|
* 链接
|
||||||
|
|||||||
99
src/components/UserProfile.tsx
Normal file
99
src/components/UserProfile.tsx
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, Image } from '@tarojs/components';
|
||||||
|
import { Button, Avatar } from '@nutui/nutui-react-taro';
|
||||||
|
import { useUser } from '@/hooks/useUser';
|
||||||
|
import navTo from '@/utils/common';
|
||||||
|
|
||||||
|
// 用户资料组件示例
|
||||||
|
const UserProfile: React.FC = () => {
|
||||||
|
const {
|
||||||
|
user,
|
||||||
|
isLoggedIn,
|
||||||
|
loading,
|
||||||
|
logoutUser,
|
||||||
|
fetchUserInfo,
|
||||||
|
getAvatarUrl,
|
||||||
|
getDisplayName,
|
||||||
|
isCertified,
|
||||||
|
getBalance,
|
||||||
|
getPoints
|
||||||
|
} = useUser();
|
||||||
|
|
||||||
|
// 处理登录跳转
|
||||||
|
const handleLogin = () => {
|
||||||
|
navTo('/pages/login/index');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理退出登录
|
||||||
|
const handleLogout = () => {
|
||||||
|
logoutUser();
|
||||||
|
navTo('/pages/index/index');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 刷新用户信息
|
||||||
|
const handleRefresh = async () => {
|
||||||
|
await fetchUserInfo();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<View className="user-profile loading">
|
||||||
|
<Text>加载中...</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
return (
|
||||||
|
<View className="user-profile not-logged-in">
|
||||||
|
<View className="login-prompt">
|
||||||
|
<Text>请先登录</Text>
|
||||||
|
<Button type="primary" onClick={handleLogin}>
|
||||||
|
立即登录
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="user-profile">
|
||||||
|
<View className="user-header">
|
||||||
|
<Avatar
|
||||||
|
size="large"
|
||||||
|
src={getAvatarUrl()}
|
||||||
|
alt={getDisplayName()}
|
||||||
|
/>
|
||||||
|
<View className="user-info">
|
||||||
|
<Text className="username">{getDisplayName()}</Text>
|
||||||
|
<Text className="user-id">ID: {user?.userId}</Text>
|
||||||
|
{isCertified() && (
|
||||||
|
<Text className="certified">已实名认证</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="user-stats">
|
||||||
|
<View className="stat-item">
|
||||||
|
<Text className="stat-value">¥{getBalance()}</Text>
|
||||||
|
<Text className="stat-label">余额</Text>
|
||||||
|
</View>
|
||||||
|
<View className="stat-item">
|
||||||
|
<Text className="stat-value">{getPoints()}</Text>
|
||||||
|
<Text className="stat-label">积分</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="user-actions">
|
||||||
|
<Button onClick={handleRefresh}>
|
||||||
|
刷新信息
|
||||||
|
</Button>
|
||||||
|
<Button type="danger" onClick={handleLogout}>
|
||||||
|
退出登录
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserProfile;
|
||||||
237
src/hooks/useUser.ts
Normal file
237
src/hooks/useUser.ts
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import Taro from '@tarojs/taro';
|
||||||
|
import { User } from '@/api/system/user/model';
|
||||||
|
import { getUserInfo, updateUserInfo } from '@/api/layout';
|
||||||
|
|
||||||
|
// 用户Hook
|
||||||
|
export const useUser = () => {
|
||||||
|
const [user, setUser] = useState<User | null>(null);
|
||||||
|
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
// 从本地存储加载用户数据
|
||||||
|
const loadUserFromStorage = () => {
|
||||||
|
try {
|
||||||
|
const token = Taro.getStorageSync('access_token');
|
||||||
|
const userData = Taro.getStorageSync('User');
|
||||||
|
const userId = Taro.getStorageSync('UserId');
|
||||||
|
const tenantId = Taro.getStorageSync('TenantId');
|
||||||
|
|
||||||
|
if (token && userData) {
|
||||||
|
const userInfo = typeof userData === 'string' ? JSON.parse(userData) : userData;
|
||||||
|
setUser(userInfo);
|
||||||
|
setIsLoggedIn(true);
|
||||||
|
} else if (token && userId) {
|
||||||
|
// 如果有token和userId但没有完整用户信息,标记为已登录但需要获取用户信息
|
||||||
|
setIsLoggedIn(true);
|
||||||
|
setUser({ userId, tenantId } as User);
|
||||||
|
} else {
|
||||||
|
setUser(null);
|
||||||
|
setIsLoggedIn(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载用户数据失败:', error);
|
||||||
|
setUser(null);
|
||||||
|
setIsLoggedIn(false);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 保存用户数据到本地存储
|
||||||
|
const saveUserToStorage = (token: string, userInfo: User) => {
|
||||||
|
try {
|
||||||
|
Taro.setStorageSync('access_token', token);
|
||||||
|
Taro.setStorageSync('User', userInfo);
|
||||||
|
Taro.setStorageSync('UserId', userInfo.userId);
|
||||||
|
Taro.setStorageSync('TenantId', userInfo.tenantId);
|
||||||
|
Taro.setStorageSync('Phone', userInfo.phone);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存用户数据失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 登录用户
|
||||||
|
const loginUser = (token: string, userInfo: User) => {
|
||||||
|
setUser(userInfo);
|
||||||
|
setIsLoggedIn(true);
|
||||||
|
saveUserToStorage(token, userInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 退出登录
|
||||||
|
const logoutUser = () => {
|
||||||
|
setUser(null);
|
||||||
|
setIsLoggedIn(false);
|
||||||
|
|
||||||
|
// 清除本地存储
|
||||||
|
try {
|
||||||
|
Taro.removeStorageSync('access_token');
|
||||||
|
Taro.removeStorageSync('User');
|
||||||
|
Taro.removeStorageSync('UserId');
|
||||||
|
Taro.removeStorageSync('TenantId');
|
||||||
|
Taro.removeStorageSync('Phone');
|
||||||
|
Taro.removeStorageSync('userInfo');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('清除用户数据失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 从服务器获取最新用户信息
|
||||||
|
const fetchUserInfo = async () => {
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const userInfo = await getUserInfo();
|
||||||
|
setUser(userInfo);
|
||||||
|
|
||||||
|
// 更新本地存储
|
||||||
|
const token = Taro.getStorageSync('access_token');
|
||||||
|
if (token) {
|
||||||
|
saveUserToStorage(token, userInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return userInfo;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取用户信息失败:', error);
|
||||||
|
// 如果获取失败,可能是token过期,清除登录状态
|
||||||
|
if (error.message?.includes('401') || error.message?.includes('未授权')) {
|
||||||
|
logoutUser();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新用户信息
|
||||||
|
const updateUser = async (userData: Partial<User>) => {
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('用户未登录');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const updatedUser = { ...user, ...userData };
|
||||||
|
await updateUserInfo(updatedUser);
|
||||||
|
|
||||||
|
setUser(updatedUser);
|
||||||
|
|
||||||
|
// 更新本地存储
|
||||||
|
const token = Taro.getStorageSync('access_token');
|
||||||
|
if (token) {
|
||||||
|
saveUserToStorage(token, updatedUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
Taro.showToast({
|
||||||
|
title: '更新成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 1500
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedUser;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新用户信息失败:', error);
|
||||||
|
Taro.showToast({
|
||||||
|
title: '更新失败',
|
||||||
|
icon: 'error',
|
||||||
|
duration: 1500
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 检查是否有特定权限
|
||||||
|
const hasPermission = (permission: string) => {
|
||||||
|
if (!user || !user.authorities) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return user.authorities.some(auth => auth.authority === permission);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 检查是否有特定角色
|
||||||
|
const hasRole = (roleCode: string) => {
|
||||||
|
if (!user || !user.roles) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return user.roles.some(role => role.roleCode === roleCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取用户头像URL
|
||||||
|
const getAvatarUrl = () => {
|
||||||
|
return user?.avatar || user?.avatarUrl || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取用户显示名称
|
||||||
|
const getDisplayName = () => {
|
||||||
|
return user?.nickname || user?.realName || user?.username || '未登录';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取用户显示的角色(同步版本)
|
||||||
|
const getRoleName = () => {
|
||||||
|
if(hasRole('superAdmin')){
|
||||||
|
return '超级管理员';
|
||||||
|
}
|
||||||
|
if(hasRole('admin')){
|
||||||
|
return '管理员';
|
||||||
|
}
|
||||||
|
if(hasRole('staff')){
|
||||||
|
return '员工';
|
||||||
|
}
|
||||||
|
if(hasRole('vip')){
|
||||||
|
return 'VIP会员';
|
||||||
|
}
|
||||||
|
return '注册用户';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查用户是否已实名认证
|
||||||
|
const isCertified = () => {
|
||||||
|
return user?.certification === true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 检查用户是否是管理员
|
||||||
|
const isAdmin = () => {
|
||||||
|
return user?.isAdmin === true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取用户余额
|
||||||
|
const getBalance = () => {
|
||||||
|
return user?.balance || 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取用户积分
|
||||||
|
const getPoints = () => {
|
||||||
|
return user?.points || 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化时加载用户数据
|
||||||
|
useEffect(() => {
|
||||||
|
loadUserFromStorage();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
// 状态
|
||||||
|
user,
|
||||||
|
isLoggedIn,
|
||||||
|
loading,
|
||||||
|
|
||||||
|
// 方法
|
||||||
|
loginUser,
|
||||||
|
logoutUser,
|
||||||
|
fetchUserInfo,
|
||||||
|
updateUser,
|
||||||
|
loadUserFromStorage,
|
||||||
|
|
||||||
|
// 工具方法
|
||||||
|
hasPermission,
|
||||||
|
hasRole,
|
||||||
|
getAvatarUrl,
|
||||||
|
getDisplayName,
|
||||||
|
getRoleName,
|
||||||
|
isCertified,
|
||||||
|
isAdmin,
|
||||||
|
getBalance,
|
||||||
|
getPoints
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -8,16 +8,16 @@ import navTo from "@/utils/common";
|
|||||||
import {TenantId} from "@/config/app";
|
import {TenantId} from "@/config/app";
|
||||||
import {getUserCouponCount} from "@/api/user/coupon";
|
import {getUserCouponCount} from "@/api/user/coupon";
|
||||||
import {getUserPointsStats} from "@/api/user/points";
|
import {getUserPointsStats} from "@/api/user/points";
|
||||||
|
import {useUser} from "@/hooks/useUser";
|
||||||
|
|
||||||
function UserCard() {
|
function UserCard() {
|
||||||
|
const {getDisplayName, getRoleName} = useUser();
|
||||||
const [IsLogin, setIsLogin] = useState<boolean>(false)
|
const [IsLogin, setIsLogin] = useState<boolean>(false)
|
||||||
const [userInfo, setUserInfo] = useState<User>()
|
const [userInfo, setUserInfo] = useState<User>()
|
||||||
const [roleName, setRoleName] = useState<string>('注册用户')
|
|
||||||
const [couponCount, setCouponCount] = useState(0)
|
const [couponCount, setCouponCount] = useState(0)
|
||||||
const [pointsCount, setPointsCount] = useState(0)
|
const [pointsCount, setPointsCount] = useState(0)
|
||||||
const [giftCount, setGiftCount] = useState(0)
|
const [giftCount, setGiftCount] = useState(0)
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Taro.getSetting:获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。
|
// Taro.getSetting:获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。
|
||||||
Taro.getSetting({
|
Taro.getSetting({
|
||||||
@@ -89,11 +89,6 @@ function UserCard() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 判断身份
|
|
||||||
const roleName = Taro.getStorageSync('RoleName');
|
|
||||||
if (roleName) {
|
|
||||||
setRoleName(roleName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
console.log('未登录')
|
console.log('未登录')
|
||||||
@@ -206,11 +201,13 @@ function UserCard() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
<div className={'user-info flex flex-col px-2'}>
|
<div className={'user-info flex flex-col px-2'}>
|
||||||
<div className={'py-1 text-black font-bold'}>{IsLogin ? userInfo?.nickname : '请点击头像登录'}</div>
|
<div className={'py-1 text-black font-bold'}>{getDisplayName()}</div>
|
||||||
{IsLogin ? (
|
{IsLogin ? (
|
||||||
<div className={'grade text-xs py-1'}>
|
<div className={'grade text-xs py-1'}>
|
||||||
<Tag type="success" round>
|
<Tag type="success" round>
|
||||||
<div className={'p-1'}>{roleName || '注册用户'}</div>
|
<div className={'p-1'}>
|
||||||
|
{getRoleName()}
|
||||||
|
</div>
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
) : ''}
|
) : ''}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import {Cell} from '@nutui/nutui-react-taro'
|
import {Cell} from '@nutui/nutui-react-taro'
|
||||||
import navTo from "@/utils/common";
|
import navTo from "@/utils/common";
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import {View,Text} from '@tarojs/components'
|
import {View, Text} from '@tarojs/components'
|
||||||
import {ArrowRight, ShieldCheck, LogisticsError, Location, Reward, Tips, Ask} from '@nutui/icons-react-taro'
|
import {ArrowRight, ShieldCheck, LogisticsError, Location, Reward, Tips, Ask, Setting} from '@nutui/icons-react-taro'
|
||||||
|
import {useUser} from '@/hooks/useUser'
|
||||||
|
|
||||||
const UserCell = () => {
|
const UserCell = () => {
|
||||||
|
const {logoutUser, isCertified, hasRole, isAdmin} = useUser();
|
||||||
|
|
||||||
const onLogout = () => {
|
const onLogout = () => {
|
||||||
Taro.showModal({
|
Taro.showModal({
|
||||||
@@ -12,10 +14,8 @@ const UserCell = () => {
|
|||||||
content: '确定要退出登录吗?',
|
content: '确定要退出登录吗?',
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
Taro.removeStorageSync('access_token')
|
// 使用 useUser hook 的 logoutUser 方法
|
||||||
Taro.removeStorageSync('TenantId')
|
logoutUser();
|
||||||
Taro.removeStorageSync('UserId')
|
|
||||||
Taro.removeStorageSync('userInfo')
|
|
||||||
Taro.reLaunch({
|
Taro.reLaunch({
|
||||||
url: '/pages/index/index'
|
url: '/pages/index/index'
|
||||||
})
|
})
|
||||||
@@ -27,6 +27,9 @@ const UserCell = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<View className={'px-4'}>
|
<View className={'px-4'}>
|
||||||
|
|
||||||
|
{/*是否分销商*/}
|
||||||
|
{!hasRole('dealer') && !isAdmin() && (
|
||||||
<Cell
|
<Cell
|
||||||
className="nutui-cell-clickable"
|
className="nutui-cell-clickable"
|
||||||
style={{
|
style={{
|
||||||
@@ -41,6 +44,25 @@ const UserCell = () => {
|
|||||||
}
|
}
|
||||||
extra={<ArrowRight color="#cccccc" size={18}/>}
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/*是否管理员*/}
|
||||||
|
{isAdmin() && (
|
||||||
|
<Cell
|
||||||
|
className="nutui-cell-clickable"
|
||||||
|
style={{
|
||||||
|
backgroundImage: 'linear-gradient(to right bottom, #ff8e0c, #ed680d)',
|
||||||
|
}}
|
||||||
|
title={
|
||||||
|
<View style={{display: 'inline-flex', alignItems: 'center'}} onClick={() => navTo('/admin/article/index', true)}>
|
||||||
|
<Setting className={'text-orange-100 '} size={16}/>
|
||||||
|
<Text style={{fontSize: '16px'}} className={'pl-3 text-orange-100 font-medium'}>管理中心</Text>
|
||||||
|
</View>
|
||||||
|
}
|
||||||
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<Cell.Group divider={true} description={
|
<Cell.Group divider={true} description={
|
||||||
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
<Text style={{marginTop: '12px'}}>我的服务</Text>
|
<Text style={{marginTop: '12px'}}>我的服务</Text>
|
||||||
@@ -81,8 +103,11 @@ const UserCell = () => {
|
|||||||
className="nutui-cell-clickable"
|
className="nutui-cell-clickable"
|
||||||
title={
|
title={
|
||||||
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
<ShieldCheck size={16}/>
|
<ShieldCheck size={16} color={isCertified() ? '#52c41a' : '#666'}/>
|
||||||
<Text className={'pl-3 text-sm'}>实名认证</Text>
|
<Text className={'pl-3 text-sm'}>实名认证</Text>
|
||||||
|
{isCertified() && (
|
||||||
|
<Text className={'pl-2 text-xs text-green-500'}>已认证</Text>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
align="center"
|
align="center"
|
||||||
|
|||||||
Reference in New Issue
Block a user