Files
template-10584/src/hooks/useUser.ts
赵忠林 ab61aa9ee0 fix(user): 修复用户页面组件状态刷新和水票余额显示问题
- 在订单确认页面添加水票模板购买数量限制逻辑
- 为用户页面添加dealerViewKey状态确保子组件正确刷新
- 交换用户卡片中水票和余额的位置显示正确数据
- 移除自动跳转邀请注册页面逻辑改用显式跳转
- 添加预期失败场景的日志过滤避免不必要的错误输出
2026-02-28 20:28:18 +08:00

338 lines
9.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from 'react';
import Taro from '@tarojs/taro';
import { User } from '@/api/system/user/model';
import { getUserInfo, updateUserInfo, loginByOpenId } from '@/api/layout';
import { TenantId } from '@/config/app';
import { handleInviteRelation } from '@/utils/invite';
// 用户Hook
export const useUser = () => {
const [user, setUser] = useState<User | null>(null);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [loading, setLoading] = useState(true);
// 自动登录通过OpenID
const autoLoginByOpenId = async () => {
try {
const res = await new Promise<any>((resolve, reject) => {
Taro.login({
success: (loginRes) => {
loginByOpenId({
code: loginRes.code,
tenantId: TenantId
}).then(async (data) => {
if (data) {
// 保存登录信息
saveUserToStorage(data.access_token, data.user);
setUser(data.user);
setIsLoggedIn(true);
// 处理邀请关系
if (data.user?.userId) {
try {
const inviteSuccess = await handleInviteRelation(data.user.userId);
if (inviteSuccess) {
console.log('自动登录时邀请关系建立成功');
}
} catch (error) {
console.error('自动登录时处理邀请关系失败:', error);
}
}
resolve(data.user);
} else {
reject(new Error('自动登录失败'));
}
}).catch(_ => {
// 登录失败(通常是新用户尚未注册/未绑定手机号等)。
// 这里不做任何“自动跳转”,避免用户点击「我的」时被强制带到分销/申请页,体验割裂。
// 需要登录的页面请使用 utils/auth 的 ensureLoggedIn / goToRegister 做显式跳转。
reject(new Error('autoLoginByOpenId failed'));
});
},
fail: reject
});
});
return res;
} catch (error) {
const msg = error instanceof Error ? error.message : String(error);
// 新用户首次进入、未绑定手机号等场景属于“预期失败”,避免刷屏报错。
if (msg !== 'autoLoginByOpenId failed') {
console.error('自动登录失败:', error);
}
return null;
}
};
// 从本地存储加载用户数据
const loadUserFromStorage = async () => {
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 {
// 没有本地登录信息,尝试自动登录
console.log('没有本地登录信息,尝试自动登录...');
const autoLoginResult = await autoLoginByOpenId();
if (!autoLoginResult) {
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);
// 确保关键字段不为空时才保存,避免覆盖现有数据
if (userInfo.userId) {
Taro.setStorageSync('UserId', userInfo.userId);
}
if (userInfo.tenantId) {
Taro.setStorageSync('TenantId', userInfo.tenantId);
}
if (userInfo.phone) {
Taro.setStorageSync('Phone', userInfo.phone);
}
// 保存头像和昵称信息
if (userInfo.avatar) {
Taro.setStorageSync('Avatar', userInfo.avatar);
}
if (userInfo.nickname) {
Taro.setStorageSync('Nickname', userInfo.nickname);
}
} 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过期清除登录状态
const errorMessage = error instanceof Error ? error.message : String(error);
if (errorMessage?.includes('401') || errorMessage?.includes('未授权')) {
logoutUser();
}
return null;
} finally {
setLoading(false);
}
};
// 更新用户信息
const updateUser = async (userData: Partial<User>) => {
if (!user) {
throw new Error('用户未登录');
}
try {
// 先获取最新的用户信息,确保我们有完整的数据
const latestUserInfo = await getUserInfo();
// 合并最新的用户信息和要更新的数据
const updatedUser = { ...latestUserInfo, ...userData };
// 调用API更新用户信息
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 getUserId = () => {
return user?.userId;
};
// 获取用户显示名称
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 = () => {
// Some backends use `1/0` (or `1/2`) instead of boolean.
const v: any = (user as any)?.isAdmin;
return v === true || v === 1 || v === '1';
};
const isSuperAdmin = () => {
const v: any = (user as any)?.isSuperAdmin;
return v === true || v === 1 || v === '1';
};
// 获取用户余额
const getBalance = () => {
return user?.balance || 0;
};
// 获取用户积分
const getPoints = () => {
return user?.points || 0;
};
// 初始化时加载用户数据
useEffect(() => {
loadUserFromStorage().catch(error => {
console.error('初始化用户数据失败:', error);
setLoading(false);
});
}, []);
return {
// 状态
user,
isLoggedIn,
loading,
// 方法
loginUser,
logoutUser,
fetchUserInfo,
updateUser,
loadUserFromStorage,
autoLoginByOpenId,
// 工具方法
hasPermission,
hasRole,
getAvatarUrl,
getDisplayName,
getRoleName,
isCertified,
isAdmin,
getBalance,
getPoints,
getUserId,
isSuperAdmin
};
};