feat: 完成登录相关功能

This commit is contained in:
范鹏 2025-01-02 19:26:12 +08:00
parent 0e38f5ab4c
commit 64404ce7b9
11 changed files with 332 additions and 86 deletions

4
env/.env vendored
View File

@ -14,3 +14,7 @@ VITE_APP_PROXY_PREFIX='/api'
VITE_SERVER_BASEURL='https://dev.work.star-lock.cn/api'
VITE_APP_ENV='XHJ_DEV'
VITE_APP_VERSION='1.0.0'
VITE_APP_BUILD_NUMBER=1

View File

@ -17,7 +17,6 @@ const baseUrl = getEnvBaseUrl()
const httpInterceptor = {
// 拦截前触发
invoke(options: CustomRequestOptions) {
// 接口请求支持通过 query 参数配置 queryString
if (options.query) {
const queryStr = qs.stringify(options.query)
if (options.url.includes('?')) {
@ -26,7 +25,6 @@ const httpInterceptor = {
options.url += `?${queryStr}`
}
}
// 非 http 开头需拼接地址
if (!options.url.startsWith('http')) {
// #ifdef H5
// console.log(__VITE_APP_PROXY__)
@ -42,16 +40,13 @@ const httpInterceptor = {
// #endif
// TIPS: 如果需要对接多个后端服务,也可以在这里处理,拼接成所需要的地址
}
// 1. 请求超时
options.timeout = 10000 // 10s
// 2. (可选)添加小程序端请求头标识
options.timeout = 3000
options.header = {
platform, // 可选,与 uniapp 定义的平台一致,告诉后台来源
platform,
version: import.meta.env.VITE_APP_VERSION + '+' + import.meta.env.VITE_APP_BUILD_NUMBER,
...options.header
}
// 3. 添加 token 请求头标识
const userStore = useUserStore()
const { token } = userStore.userInfo as unknown as IUserInfo
const token = uni.getStorageSync('token')
if (token) {
options.header.Authorization = `Bearer ${token}`
}

View File

@ -38,6 +38,13 @@
</template>
<script setup lang="ts">
import { codeLoginApi, CodeLoginRequest } from '@/service/user'
import { PlatId } from '@/typings'
import { Result } from '@/constants/result'
import { useBasicStore } from '@/store'
const $basic = useBasicStore()
const phone = ref<string>('')
const code = ref<string>('')
@ -48,6 +55,12 @@
const type = ref<string>('')
const systemInfo = ref<GetSystemInfoResult>(null)
onMounted(async () => {
systemInfo.value = await $basic.getSystemInfo()
})
onLoad(async options => {
phone.value = options.phone
type.value = options.type
@ -55,14 +68,55 @@
countDownStart()
})
watch(code, newVal => {
watch(code, async newVal => {
if (newVal.length === 6) {
//
if (type.value === 'login') {
console.log('login')
await uni.showLoading({
title: '登录中',
mask: true
})
try {
const result = await codeLoginApi<CodeLoginRequest>({
platId: PlatId.app,
phone: phone.value,
verificationCode: code.value,
deviceInfo: {
deviceNo: systemInfo.value?.deviceId,
deviceName: `${systemInfo.value?.deviceBrand} ${systemInfo.value?.deviceModel}`,
deviceBrand: systemInfo.value?.deviceBrand,
deviceModel: systemInfo.value?.deviceModel
}
})
uni.hideLoading()
if (result.errorCode === Result.Success.code) {
uni.setStorageSync({
key: 'token',
data: result.data.token
})
await uni.switchTab({
url: '/pages/home/home'
})
await uni.showToast({
title: '登录成功',
icon: 'none'
})
} else {
await uni.showToast({
title: result.errorMsg,
icon: 'none'
})
}
} catch (err) {
console.log(err)
uni.hideLoading()
await uni.showToast({
title: '登录失败,请稍后再试',
icon: 'none'
})
}
} else if (type.value === 'reset-password') {
uni.navigateTo({
url: `/pages/reset-password/reset-password`
await uni.redirectTo({
url: `/pages/reset-password/reset-password?phone=${phone.value}&code=${code.value}`
})
}
}

View File

@ -32,6 +32,9 @@
<script setup lang="ts">
import { phoneRegExp } from '@/constants/regular-expressions'
import { AccountChannel, CodeType, IResData } from '@/typings'
import { getCodeApi, UserGetCodeRequest } from '@/service/user'
import { Result } from '@/constants/result'
const phone = ref<string>('')
const phonePass = computed(() => phoneRegExp.test(phone.value))
@ -42,18 +45,42 @@
}
})
const codeLogin = () => {
const codeLogin = async () => {
if (!phonePass.value) {
return
}
uni.navigateTo({
url: `/pages/code/code?phone=${phone.value}&type=reset-password`
await uni.showLoading({
title: '加载中',
mask: true
})
try {
const result: IResData = await getCodeApi<UserGetCodeRequest>({
account: phone.value,
channel: AccountChannel.phone,
codeType: CodeType.reset
})
uni.hideLoading()
if (result.errorCode === Result.Success.code) {
await uni.navigateTo({
url: `/pages/code/code?phone=${phone.value}&type=reset-password`
})
} else {
await uni.showToast({
title: result.errorMsg,
icon: 'none'
})
}
} catch (err) {
console.log(err)
uni.hideLoading()
await uni.showToast({
title: '获取验证码失败,请稍后再试',
icon: 'none'
})
}
}
const phoneChange = (value: string) => {
phone.value = value
}
</script>
<style scoped lang="scss"></style>

View File

@ -18,13 +18,14 @@
</template>
<script lang="ts" setup>
import { useUserStore } from '@/store'
import CustomTab from '@/pages/home/CustomTab.vue'
const $user = useUserStore()
import { useUserStore } from '@/store'
import { Result } from '@/constants/result'
const appName = import.meta.env.VITE_APP_TITLE
const $user = useUserStore()
const list = ref<Array<TabBarItem>>([
{
title: '首页',
@ -221,11 +222,19 @@
}
])
// uni API
onLoad(() => {
$user.setUserInfo({ nickname: '1', avatar: '1', token: '1' })
console.log($user.userInfo.nickname)
onMounted(async () => {
const tokenStorage: string = uni.getStorageSync('token')
if (tokenStorage) {
const result: boolean = await $user.getUserInfo()
if (result.code === Result.Success.code) {
console.log('获取用户信息成功')
} else {
await uni.showToast({
title: result.message,
icon: 'none'
})
}
}
})
const change = data => {

View File

@ -150,8 +150,13 @@
<script lang="ts" setup>
import { useBasicStore } from '@/store'
import { passwordRegExp, phoneRegExp } from '@/constants/regular-expressions'
import { getCodeApi, UserGetCodeRequest } from '@/service/user'
import { AccountChannel, CodeType, IResData } from '@/typings'
import {
getCodeApi,
passwordLoginApi,
PasswordLoginRequest,
UserGetCodeRequest
} from '@/service/user'
import { AccountChannel, CodeType, IResData, PlatId } from '@/typings'
import { Result } from '@/constants/result'
const $basic = useBasicStore()
@ -179,7 +184,7 @@
systemInfo.value = await $basic.getSystemInfo()
})
const passwordLogin = () => {
const passwordLogin = async () => {
if (!phonePass.value || !passwordPass.value) {
return
}
@ -188,7 +193,46 @@
agreementModal.value.showModal()
return
}
console.log('登录')
await uni.showLoading({
title: '登录中',
mask: true
})
try {
const result = await passwordLoginApi<PasswordLoginRequest>({
platId: PlatId.app,
username: phone.value,
password: password.value,
deviceInfo: {
deviceNo: systemInfo.value?.deviceId,
deviceName: `${systemInfo.value?.deviceBrand} ${systemInfo.value?.deviceModel}`,
deviceBrand: systemInfo.value?.deviceBrand,
deviceModel: systemInfo.value?.deviceModel
}
})
console.log(111111, result)
uni.hideLoading()
if (result.errorCode === Result.Success.code) {
await uni.switchTab({
url: '/pages/home/home'
})
await uni.showToast({
title: '登录成功',
icon: 'success'
})
} else {
await uni.showToast({
title: result.errorMsg,
icon: 'none'
})
}
} catch (err) {
console.log(err)
uni.hideLoading()
await uni.showToast({
title: '获取验证码失败,请稍后再试',
icon: 'none'
})
}
}
const codeLogin = async () => {

View File

@ -45,7 +45,7 @@
:disabled="!(passwordPass && confirmPasswordPass && password === confirmPassword)"
:round="false"
size="large"
@click="codeLogin"
@click="resetPassword"
>
提交
</wd-button>
@ -55,6 +55,11 @@
<script setup lang="ts">
import { passwordRegExp } from '@/constants/regular-expressions'
import { resetPasswordApi } from '@/service/user'
import { Result } from '@/constants/result'
const phone = ref<string>('')
const code = ref<string>('')
const password = ref<string>('')
const confirmPassword = ref<string>('')
@ -62,6 +67,64 @@
const passwordPass = computed(() => passwordRegExp.test(password.value))
const confirmPasswordPass = computed(() => password.value === confirmPassword.value)
onLoad(options => {
phone.value = options.phone
code.value = options.code
})
const resetPassword = async () => {
if (password.value === '' || confirmPassword.value === '') {
uni.showToast({
title: '密码不能为空',
icon: 'none'
})
return
} else if (!passwordPass.value || !confirmPasswordPass.value) {
uni.showToast({
title: '密码格式不正确',
icon: 'none'
})
return
} else if (password.value !== confirmPasswordPass.value) {
uni.showToast({
title: '两次密码不一致',
icon: 'none'
})
return
}
await uni.showLoading({
title: '修改中'
})
try {
const resetPasswordApiResult = await resetPasswordApi({
newPassword: password.value,
account: phone.value,
verificationCode: code.value
})
uni.hideLoading()
if (resetPasswordApiResult.errorCode === Result.Success.code) {
await uni.navigateBack({ delta: 2 })
await uni.showToast({
title: '密码重置成功',
icon: 'icon'
})
} else {
await uni.showToast({
title: resetPasswordApiResult.errorMsg,
icon: 'none'
})
}
} catch (err) {
console.log(err)
uni.hideLoading()
await uni.showToast({
title: '密码重置失败,请稍后再试',
icon: 'none'
})
}
}
const passwordChange = (value: string) => {
password.value = value
}

View File

@ -11,3 +11,69 @@ export interface UserGetCodeRequest {
export const getCodeApi = (params: UserGetCodeRequest) => {
return http.post('/v1/common/sendValidationCode', params)
}
export interface PasswordLoginRequest {
platId: number
username: string
password: string
deviceInfo?: DeviceInfo
}
interface DeviceInfo {
deviceNo?: string
deviceName?: string
deviceBrand?: string
deviceModel?: string
}
// 密码登录
export const passwordLoginApi = (params: PasswordLoginRequest) => {
return http.post('/v1/user/pwdLogin', params)
}
export interface CodeLoginRequest {
platId: number
phone: string
verificationCode: string
deviceInfo?: DeviceInfo
}
// 验证码登录
export const codeLoginApi = (params: CodeLoginRequest) => {
return http.post('/v1/user/codeLogin', params)
}
export interface UserInfo {
id?: number
accountNo?: string
phone?: string
nickname?: string
headUrl?: string
createdAt?: string
lastLoginTime?: string
currentTeamNo?: string
teamList?: Array<TeamInfo>
}
interface TeamInfo {
teamNo?: string
teamName?: string
teamCode?: string
}
// 获取用户信息
export const getUserInfoApi = (params: UserInfo) => {
return http.post('/v1/user/detail', params)
}
export interface resetPasswordRequest {
countryCode?: string
account: string
verificationCode: number
newPassword: string
}
// 重置密码
export const resetPasswordApi = (params: resetPasswordRequest) => {
return http.post('/v1/user/resetPassword', params)
}

View File

@ -1,32 +1,40 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
const initState = { nickname: '', avatar: '' }
import { getUserInfoApi, UserInfo } from '@/service/user'
import { Result } from '@/constants/result'
export const useUserStore = defineStore(
'user',
() => {
const userInfo = ref<IUserInfo>({ ...initState })
const userInfo = ref<UserInfo>(null)
const loginStatus = ref(false)
const setUserInfo = (val: IUserInfo) => {
userInfo.value = val
const getUserInfo = async () => {
const result = await getUserInfoApi()
if (result.errorCode === Result.Success.code) {
userInfo.value = result.data
loginStatus.value = true
uni.setStorageSync({
key: 'userInfo',
data: userInfo.value
})
return new Result(result.errorCode, userInfo.value, result.errorMsg)
} else {
const userInfoStorage = uni.getStorageSync('userInfo')
if (userInfoStorage) {
userInfo.value = userInfoStorage
loginStatus.value = true
return new Result(Result.Success.code, userInfo.value, Result.Success.message)
} else {
loginStatus.value = false
return Result.Fail
}
}
}
const clearUserInfo = () => {
userInfo.value = { ...initState }
}
// 一般没有reset需求不需要的可以删除
const reset = () => {
userInfo.value = { ...initState }
}
const isLogined = computed(() => !!userInfo.value.token)
return {
userInfo,
setUserInfo,
clearUserInfo,
isLogined,
reset
getUserInfo
}
},
{

View File

@ -6,36 +6,20 @@ export type IResData<T> = {
data: T
}
// uni.uploadFile文件上传参数
type IUniUploadFileOptions = {
file?: File
files?: UniApp.UploadFileOptionFiles[]
filePath?: string
name?: string
formData?: any
}
type IUserInfo = {
nickname?: string
avatar?: string
openid?: string
token?: string
}
type TabBarItem = {
export type TabBarItem = {
icon: string
title: string
isDot?: boolean
value?: number
}
type HomeTabItem = {
export type HomeTabItem = {
icon: string
title: string
id: number
}
type HomeTab = {
export type HomeTab = {
title: string
list: HomeTabItem[]
}
@ -56,3 +40,10 @@ export enum CodeType {
deleteLock = '8',
updatePassword = '9'
}
export enum PlatId {
web = 1,
app = 2,
miniProgram = 3,
pc = 4
}

View File

@ -12,10 +12,8 @@ export const http = <T>(options: CustomRequestOptions) => {
// #endif
// 响应成功
success(res) {
// 状态码 2xx参考 axios 的设计
if (res.statusCode >= 200 && res.statusCode < 300) {
if (res.data.errorCode === 403) {
// 401错误 -> 清理用户信息,跳转到登录页
// userStore.clearUserInfo()
// uni.navigateTo({ url: '/pages/login/login' })
reject(res)
@ -39,6 +37,7 @@ export const http = <T>(options: CustomRequestOptions) => {
reject(err)
},
complete(res) {
console.log(1111, options)
console.log(options.url, {
env: import.meta.env.VITE_APP_ENV,
statusCode: res?.statusCode,
@ -47,6 +46,7 @@ export const http = <T>(options: CustomRequestOptions) => {
url: options.url,
req: options.data,
res: res?.data?.data,
header: options.header,
message: res?.data?.errorMsg,
duration: new Date().getTime() - timestamp
})
@ -55,20 +55,6 @@ export const http = <T>(options: CustomRequestOptions) => {
})
}
/**
* GET
* @param url
* @param query query参数
* @returns
*/
export const httpGet = <T>(url: string, query?: Record<string, any>) => {
return http<T>({
url,
query,
method: 'GET'
})
}
/**
* POST
* @param url
@ -89,5 +75,4 @@ export const httpPost = <T>(
})
}
http.get = httpGet
http.post = httpPost