Files
template-10559/src/pages/index/Login.tsx
赵忠林 87d6648989 feat(user): 实现扫码登录和推荐人绑定功能- 添加扫码登录相关API接口,包括生成二维码、检查状态、确认登录等
- 在用户注册时支持从邀请参数中获取推荐人ID并绑定
- 修改管理员面板UI,添加统一扫码功能入口- 更新用户管理相关API地址,统一使用SERVER_API_URL
- 调整优惠券卡片样式,移除小程序不支持的CSS属性
- 添加聊天会话和消息管理相关API模块
- 新增分销商银行卡管理API接口
- 修改系统用户模型,增加推荐人ID字段
- 更新广告位查询接口,支持根据code获取广告位
- 调整邀请绑定接口参数,将refereeId改为dealerId
- 修改环境配置中的应用名称为"时里院子市集"
- 移除分享到朋友圈的相关代码
- 添加管理员面板组件,提供统一扫码等管理功能
-修复用户管理API请求参数传递问题
- 添加聊天消息和会话管理的完整CRUD接口
- 更新系统用户相关接口URL,确保正确调用后端服务
- 添加分销商银行卡管理的完整API接口实现
- 修改邀请绑定接口,使用dealerId替代refereeId参数
- 修复扫码登录相关API的URL拼接问题
- 添加二维码内容解析功能,支持多种格式的token提取
- 更新用户信息模型,增加推荐人ID字段
-优化管理员面板样式和交互逻辑- 调整优惠券组件样式,兼容小程序环境限制- 修复用户管理模块的API调用问题
- 添加聊天相关数据模型和接口定义
- 更新环境配置中的应用名称
-修复邀请绑定相关的参数传递问题- 添加扫码登录状态枚举和相关数据结构定义- 优化管理员功能面板的UI展示和交互体验- 修复系统用户管理接口的请求参数问题
- 添加分销商银行卡管理相关接口实现- 调整聊天消息和会话管理接口的数据结构定义
-修复用户管理模块中的API调用路径问题
- 添加扫码登录相关工具函数,如设备信息获取等
- 更新邀请绑定接口的数据模型定义
-优化管理员面板组件的样式和功能实现
-修复系统用户管理接口中的参数传递问题
- 添加聊天相关模块的完整API接口实现
- 调整分销商银行卡管理模块的数据结构定义- 修复扫码登录相关接口的URL拼接问题- 更新用户管理模块中的API调用方式
- 添加聊天消息批量发送等相关接口实现- 修复邀请绑定接口中的参数名称问题- 优化管理员面板组件的功能和交互逻辑
- 调整系统用户管理接口的请求参数传递方式
- 添加分销商银行卡管理模块的完整接口实现
-修复聊天相关接口中的数据结构问题
- 更新扫码登录相关接口的数据模型定义
- 优化管理员功能面板的展示效果和用户体验
2025-09-24 19:23:56 +08:00

157 lines
5.4 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 {useEffect, useState} from "react";
import Taro from '@tarojs/taro'
import {Input, Radio, Button} from '@nutui/nutui-react-taro'
import {TenantId} from "@/config/app";
import './login.scss';
import {saveStorageByLoginUser} from "@/utils/server";
import {handleInviteRelation, getStoredInviteParams} from "@/utils/invite";
// 微信获取手机号回调参数类型
interface GetPhoneNumberDetail {
code?: string;
encryptedData?: string;
iv?: string;
errMsg: string;
}
interface GetPhoneNumberEvent {
detail: GetPhoneNumberDetail;
}
interface LoginProps {
done?: (user: any) => void;
[key: string]: any;
}
// 登录接口返回数据类型
interface LoginResponse {
data: {
data: {
access_token: string;
user: any;
};
};
}
const Login = (props: LoginProps) => {
const [isAgree, setIsAgree] = useState(false)
const [env, setEnv] = useState<string>()
/* 获取用户手机号 */
const handleGetPhoneNumber = ({detail}: GetPhoneNumberEvent) => {
const {code, encryptedData, iv} = detail
// 获取存储的邀请参数
const inviteParams = getStoredInviteParams()
const refereeId = inviteParams?.inviter ? parseInt(inviteParams.inviter) : 0
Taro.login({
success: function () {
if (code) {
Taro.request({
url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone',
method: 'POST',
data: {
code,
encryptedData,
iv,
notVerifyPhone: true,
refereeId: refereeId, // 使用解析出的推荐人ID
sceneType: 'save_referee',
tenantId: TenantId
},
header: {
'content-type': 'application/json',
TenantId
},
success: async function (res: LoginResponse) {
saveStorageByLoginUser(res.data.data.access_token, res.data.data.user)
// 处理邀请关系
if (res.data.data.user?.userId) {
try {
const inviteSuccess = await handleInviteRelation(res.data.data.user.userId)
if (inviteSuccess) {
Taro.showToast({
title: '邀请关系建立成功',
icon: 'success',
duration: 2000
})
}
} catch (error) {
console.error('处理邀请关系失败:', error)
}
}
props.done?.(res.data.data.user);
}
})
} else {
console.log('登录失败!')
}
}
})
}
const reload = () => {
Taro.hideTabBar()
setEnv(Taro.getEnv())
}
useEffect(() => {
reload()
}, [])
return (
<>
<div style={{height: '80vh'}} className={'flex flex-col justify-center px-5'}>
<div className={'text-3xl text-center py-5 font-normal mb-10 '}></div>
{
env === 'WEAPP' && (
<>
<div className={'flex flex-col w-full text-white rounded-full justify-between items-center my-2'} style={{ background: 'linear-gradient(to right, #7e22ce, #9333ea)'}}>
<Button open-type="getPhoneNumber" onGetPhoneNumber={handleGetPhoneNumber}>
</Button>
</div>
</>
)
}
{
env === 'WEB' && (
<>
<div className={'flex flex-col justify-between items-center my-2'}>
<Input type="text" placeholder="手机号" maxLength={11}
style={{backgroundColor: '#ffffff', borderRadius: '8px'}}/>
</div>
<div className={'flex flex-col justify-between items-center my-2'}>
<Input type="password" placeholder="密码" style={{backgroundColor: '#ffffff', borderRadius: '8px'}}/>
</div>
<div className={'flex justify-between my-2 text-left px-1'}>
<a href={'#'} className={'text-blue-600 text-sm'}
onClick={() => Taro.navigateTo({url: '/passport/forget'})}></a>
<a href={'#'} className={'text-blue-600 text-sm'}
onClick={() => Taro.navigateTo({url: '/passport/setting'})}></a>
</div>
<div className={'flex justify-center my-5'}>
<Button type="info" size={'large'} className={'w-full rounded-lg p-2'} disabled={!isAgree}></Button>
</div>
<div className={'w-full bottom-20 my-2 flex justify-center text-sm items-center text-center'}>
<a href={''} onClick={() => Taro.navigateTo({url: '/passport/register'})}
className={'text-blue-600'}></a>
</div>
<div className={'my-2 flex fixed bottom-20 text-sm items-center px-1'}>
<Radio style={{color: '#333333'}} checked={isAgree} onClick={() => setIsAgree(!isAgree)}></Radio>
<span className={'text-gray-400'} onClick={() => setIsAgree(!isAgree)}></span><a
onClick={() => Taro.navigateTo({url: '/passport/agreement'})}
className={'text-blue-600'}></a>
</div>
</>
)
}
</div>
</>
)
}
export default Login