Browse Source

feat(user): 实现自动登录并优化用户相关功能

- 添加自动登录功能,通过 OpenID 实现一键登录
- 优化用户数据加载和保存逻辑,确保数据完整性
- 处理邀请关系,自动登录时建立邀请关系
- 更新订单统计钩子,增加用户身份检查
- 修复首页轮播图点击事件,实现跳转功能
master
科技小王子 3 weeks ago
parent
commit
0a10afcea2
  1. 12
      src/admin/index.tsx
  2. 3
      src/hooks/useOrderStats.ts
  3. 105
      src/hooks/useUser.ts
  4. 5
      src/pages/index/Banner.tsx

12
src/admin/index.tsx

@ -1,5 +1,6 @@
import {useEffect} from 'react'
import {useUser} from "@/hooks/useUser";
import {Empty} from '@nutui/nutui-react-taro';
import {Text} from '@tarojs/components';
function Admin() {
@ -12,7 +13,16 @@ function Admin() {
if (!isAdmin()) {
return (
<Text></Text>
<Empty
description="您不是管理员"
imageSize={80}
style={{
backgroundColor: 'transparent',
height: 'calc(100vh - 200px)'
}}
>
</Empty>
);
}
return (

3
src/hooks/useOrderStats.ts

@ -28,6 +28,9 @@ export const useOrderStats = () => {
setLoading(true);
setError(null);
if(!Taro.getStorageSync('UserId')){
return false;
}
// TODO 读取订单数量
const pending = await pageShopOrder({ page: 1, limit: 1, userId: Taro.getStorageSync('UserId'), statusFilter: 0})
const paid = await pageShopOrder({ page: 1, limit: 1, userId: Taro.getStorageSync('UserId'), statusFilter: 1})

105
src/hooks/useUser.ts

@ -1,7 +1,9 @@
import { useState, useEffect } from 'react';
import Taro from '@tarojs/taro';
import { User } from '@/api/system/user/model';
import { getUserInfo, updateUserInfo } from '@/api/layout';
import { getUserInfo, updateUserInfo, loginByOpenId } from '@/api/layout';
import { TenantId } from '@/config/app';
import {getStoredInviteParams, handleInviteRelation} from '@/utils/invite';
// 用户Hook
export const useUser = () => {
@ -9,8 +11,62 @@ export const useUser = () => {
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(_ => {
// 首次注册,跳转到邀请注册页面
const pages = Taro.getCurrentPages();
const currentPage = pages[pages.length - 1];
const inviteParams = getStoredInviteParams()
if (currentPage?.route !== 'dealer/apply/add' && inviteParams?.inviter) {
return Taro.navigateTo({
url: '/dealer/apply/add'
});
}
});
},
fail: reject
});
});
return res;
} catch (error) {
console.error('自动登录失败:', error);
return null;
}
};
// 从本地存储加载用户数据
const loadUserFromStorage = () => {
const loadUserFromStorage = async () => {
try {
const token = Taro.getStorageSync('access_token');
const userData = Taro.getStorageSync('User');
@ -26,8 +82,13 @@ export const useUser = () => {
setIsLoggedIn(true);
setUser({ userId, tenantId } as User);
} else {
setUser(null);
setIsLoggedIn(false);
// 没有本地登录信息,尝试自动登录
console.log('没有本地登录信息,尝试自动登录...');
const autoLoginResult = await autoLoginByOpenId();
if (!autoLoginResult) {
setUser(null);
setIsLoggedIn(false);
}
}
} catch (error) {
console.error('加载用户数据失败:', error);
@ -43,9 +104,24 @@ export const useUser = () => {
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);
// 确保关键字段不为空时才保存,避免覆盖现有数据
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);
}
@ -114,9 +190,16 @@ export const useUser = () => {
}
try {
const updatedUser = { ...user, ...userData };
// 先获取最新的用户信息,确保我们有完整的数据
const latestUserInfo = await getUserInfo();
// 合并最新的用户信息和要更新的数据
const updatedUser = { ...latestUserInfo, ...userData };
// 调用API更新用户信息
await updateUserInfo(updatedUser);
// 更新本地状态
setUser(updatedUser);
// 更新本地存储
@ -216,7 +299,10 @@ export const useUser = () => {
// 初始化时加载用户数据
useEffect(() => {
loadUserFromStorage();
loadUserFromStorage().catch(error => {
console.error('初始化用户数据失败:', error);
setLoading(false);
});
}, []);
return {
@ -231,6 +317,7 @@ export const useUser = () => {
fetchUserInfo,
updateUser,
loadUserFromStorage,
autoLoginByOpenId,
// 工具方法
hasPermission,

5
src/pages/index/Banner.tsx

@ -3,6 +3,7 @@ import { Swiper } from '@nutui/nutui-react-taro'
import {CmsAd} from "@/api/cms/cmsAd/model";
import {Image} from '@nutui/nutui-react-taro'
import {getCmsAd} from "@/api/cms/cmsAd";
import navTo from "@/utils/common";
const MyPage = () => {
const [item, setItem] = useState<CmsAd>()
@ -18,10 +19,10 @@ const MyPage = () => {
return (
<>
<Swiper defaultValue={0} height={item?.height} indicator style={{ height: item?.height + 'px', display: 'none' }}>
<Swiper defaultValue={0} height={item?.height} indicator style={{ height: item?.height + 'px' }}>
{item?.imageList?.map((item) => (
<Swiper.Item key={item}>
<Image width="100%" height="100%" src={item.url} mode={'scaleToFill'} lazyLoad={false} style={{ height: item.height + 'px' }} />
<Image width="100%" height="100%" src={item.url} mode={'scaleToFill'} onClick={() => navTo(`${item.path}`)} lazyLoad={false} style={{ height: item.height + 'px' }} />
</Swiper.Item>
))}
</Swiper>

Loading…
Cancel
Save