Ver Fonte

feat: 登录交互完善

liangan há 3 semanas atrás
pai
commit
aac4cf7890

+ 1 - 1
env/.env

@@ -8,7 +8,7 @@ VITE_WX_APPID = 'wxa2abb91f64032a2b'
 VITE_APP_PUBLIC_BASE=/
 
 # 登录页面
-VITE_LOGIN_URL = '/pages/login/index'
+VITE_LOGIN_URL = '/pages/login/login'
 # 第一个请求地址
 VITE_SERVER_BASEURL = 'http://124.222.152.234:8101'
 # 第二个请求地址

+ 0 - 17
src/api/alova-foo.ts

@@ -1,17 +0,0 @@
-import { API_DOMAINS, http } from '@/utils/request/alova'
-
-export interface IFoo {
-  id: number
-  name: string
-}
-
-export function foo() {
-  return http.Get<IFoo>('/foo', {
-    params: {
-      name: '菲鸽',
-      page: 1,
-      pageSize: 10,
-    },
-    meta: { domain: API_DOMAINS.SECONDARY }, // 用于切换请求地址
-  })
-}

+ 6 - 15
src/api/login.ts

@@ -1,20 +1,11 @@
-import type { ICaptcha, IUpdateInfo, IUpdatePassword, IUserLogin } from './types/login'
 import { http } from '@/utils/http'
 
-/**
- * 登录表单
- */
-export interface ILoginForm {
-  account: string
-  pwd: string
-}
-
 /**
  * 获取验证码
  * @returns ICaptcha 验证码
  */
 export function getCode() {
-  return http.get<ICaptcha>('/user/getCode')
+  return http.get<any>('/user/getCode')
 }
 
 /**
@@ -28,8 +19,8 @@ export function register(registerForm: any) {
  * 用户登录
  * @param loginForm 登录表单
  */
-export function login(loginForm: ILoginForm) {
-  return http.post<IUserLogin>('/cif/api/user/login', loginForm)
+export function login(loginForm: any) {
+  return http.post<any>('/cif/api/user/login', loginForm)
 }
 
 /**
@@ -49,14 +40,14 @@ export function logout() {
 /**
  * 修改用户信息
  */
-export function updateInfo(data: IUpdateInfo) {
+export function updateInfo(data: any) {
   return http.post('/user/updateInfo', data)
 }
 
 /**
  * 修改用户密码
  */
-export function updateUserPassword(data: IUpdatePassword) {
+export function updateUserPassword(data: any) {
   return http.post('/user/updatePassword', data)
 }
 
@@ -84,5 +75,5 @@ export function getWxCode() {
  * @returns Promise 包含登录结果
  */
 export function wxLogin(data: { code: string }) {
-  return http.post<IUserLogin>('/user/wxLogin', data)
+  return http.post<any>('/user/wxLogin', data)
 }

+ 0 - 57
src/api/types/login.ts

@@ -1,57 +0,0 @@
-/**
- * 用户信息
- */
-export interface IUserInfoVo {
-  id: number
-  username: string
-  avatar: string
-  token: string
-}
-
-/**
- * 登录返回的信息
- */
-export interface IUserLogin {
-  id: string
-  username: string
-  token: string
-}
-
-/**
- * 获取验证码
- */
-export interface ICaptcha {
-  captchaEnabled: boolean
-  uuid: string
-  image: string
-}
-/**
- * 上传成功的信息
- */
-export interface IUploadSuccessInfo {
-  fileId: number
-  originalName: string
-  fileName: string
-  storagePath: string
-  fileHash: string
-  fileType: string
-  fileBusinessType: string
-  fileSize: number
-}
-/**
- * 更新用户信息
- */
-export interface IUpdateInfo {
-  id: number
-  name: string
-  sex: string
-}
-/**
- * 更新用户信息
- */
-export interface IUpdatePassword {
-  id: number
-  oldPassword: string
-  newPassword: string
-  confirmPassword: string
-}

+ 2 - 1
src/interceptors/route.ts

@@ -12,7 +12,7 @@ const loginRoute = import.meta.env.VITE_LOGIN_URL
 
 function isLogined() {
   const userStore = useUserStore()
-  return !!userStore.userInfo.username
+  return !!userStore.token
 }
 
 const isDev = import.meta.env.DEV
@@ -32,6 +32,7 @@ const navigateToInterceptor = {
       const baseDir = normalizedCurrentPath.substring(0, normalizedCurrentPath.lastIndexOf('/'))
       path = `${baseDir}/${path}`
     }
+    console.log(url)
 
     let needLoginPages: string[] = []
     // 为了防止开发时出现BUG,这里每次都获取一下。生产环境可以移到函数外,性能更好

+ 1 - 0
src/pages.json

@@ -183,6 +183,7 @@
       "path": "pages/productDetail/checkOut",
       "type": "page",
       "layout": "default",
+      "needLogin": true,
       "style": {
         "navigationBarTitleText": "Checkout",
         "navigationBarBackgroundColor": "#fff"

+ 30 - 73
src/pages/login/login.vue

@@ -8,12 +8,8 @@
 </route>
 
 <script lang="ts" setup>
-import type { ILoginForm } from '@/api/login'
-// 必须导入需要用到的页面生命周期(即使在当前页面上没有直接使用到)
-// eslint-disable-next-line unused-imports/no-unused-imports
-import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
-import { login } from '@/api/login'
 import { useUserStore } from '@/store/user'
+import { goBack, toPage } from '@/utils/page'
 import { toast } from '@/utils/toast'
 
 defineOptions({
@@ -33,74 +29,26 @@ const formData = ref({
   password: '',
 })
 
-// 登录处理
-async function handleLogin() {
-  try {
-    // 表单验证
-    const isValid = await validateForm()
-    if (!isValid)
-      return
-
-    // 显示加载状态
-    uni.showLoading({
-      title: 'Logging in...',
-      mask: true,
-    })
-
-    // 准备登录数据,根据 ILoginForm 接口要求
-    const loginData: ILoginForm = {
-      account: formData.value.username,
-      pwd: formData.value.password,
-    }
-
-    // 调用登录接口
-    const res = await login(loginData)
-    uni.hideLoading()
-    // 登录成功,使用 userStore 管理用户状态和 token
-    uni.setStorageSync('token', res.data.token)
-    // 使用状态管理工具设置用户信息,会自动处理 token 存储
-    userStore.getUserInfo()
-    // 登录成功提示
-    toast.success('Login successful!')
-  }
-  catch (error) {
-    uni.hideLoading()
-    uni.showToast({
-      title: error.message || 'Login failed',
-      icon: 'none',
-    })
-  }
-}
-
 // 表单验证
 function validateForm() {
   return new Promise((resolve) => {
     // 验证用户名
     if (!formData.value.username.trim()) {
-      uni.showToast({
-        title: 'Please enter username or phone number',
-        icon: 'none',
-      })
+      toast.error('Please enter username or phone number')
       resolve(false)
       return
     }
 
     // 验证密码
     if (!formData.value.password.trim()) {
-      uni.showToast({
-        title: 'Please enter password',
-        icon: 'none',
-      })
+      toast.error('Please enter password')
       resolve(false)
       return
     }
 
     // 验证密码长度
     if (formData.value.password.length < 6 || formData.value.password.length > 20) {
-      uni.showToast({
-        title: 'Password should be 6-20 characters',
-        icon: 'none',
-      })
+      toast.error('Password should be 6-20 characters')
       resolve(false)
       return
     }
@@ -109,23 +57,32 @@ function validateForm() {
   })
 }
 
-// 跳转注册页
-function toRegister() {
-  uni.navigateTo({
-    url: '/pages/register/register',
-  })
-}
+// 登录处理
+async function handleLogin() {
+  try {
+    // 表单验证
+    const isValid = await validateForm()
+    if (!isValid)
+      return
 
-// 跳转忘记密码页
-function toForgotPassword() {
-  uni.navigateTo({
-    url: '/pages/forgotPassword/forgotPassword',
-  })
-}
+    // 准备登录数据
+    const loginData = {
+      account: formData.value.username,
+      pwd: formData.value.password,
+    }
 
-// 返回上一页
-function goBack() {
-  uni.navigateBack()
+    // 获取页面参数中的跳转地址(如果有的话)
+    const pages = getCurrentPages()
+    const currentPage = pages[pages.length - 1] as any
+    const redirectUrl = currentPage?.options?.redirect
+
+    // 调用 userStore 中的登录方法,传入跳转地址
+    await userStore.login(loginData, redirectUrl)
+  }
+  catch (error) {
+    // 错误处理已在 userStore.login 中处理
+    console.error('Login error:', error)
+  }
 }
 </script>
 
@@ -193,7 +150,7 @@ function goBack() {
             type="error"
             plain
             custom-style="width: 200rpx; height: 72rpx; border-radius: 36rpx; font-size: 28rpx;"
-            @click="toRegister"
+            @click="toPage('/pages/register/register')"
           >
             Register
           </wd-button>
@@ -202,7 +159,7 @@ function goBack() {
 
       <!-- 忘记密码 -->
       <view class="text-center">
-        <text class="text-28rpx text-#5C5C5C" @click="toForgotPassword">
+        <text class="text-28rpx text-#5C5C5C" @click="toPage('/pages/forgotPassword/forgotPassword')">
           Forgot Password?
         </text>
       </view>

+ 1 - 0
src/pages/productDetail/checkOut.vue

@@ -1,6 +1,7 @@
 <route lang="json5" type="page">
 {
   layout: 'default',
+  needLogin: true,
   style: {
     navigationBarTitleText: 'Checkout',
     navigationBarBackgroundColor: '#fff',

+ 57 - 3
src/store/user.ts

@@ -1,13 +1,14 @@
 import { defineStore } from 'pinia'
 import { ref } from 'vue'
-import { getUserInfo as _getUserInfo } from '@/api/login'
+import { getUserInfo as _getUserInfo, login as _login } from '@/api/login'
+import { toPage } from '@/utils/page'
+import { toast } from '@/utils/toast'
 
 // 初始化状态
 const userInfoState: any = {
   id: 0,
   name: '',
   avatar: '/static/images/default-avatar.png',
-  token: '',
 }
 
 export const useUserStore = defineStore(
@@ -15,10 +16,11 @@ export const useUserStore = defineStore(
   () => {
     // 定义用户信息
     const userInfo = ref<any>({ ...userInfoState })
+    const token = ref<string>('')
     // 设置用户信息
     const setUserInfo = (val: any) => {
       console.log('设置用户信息', val)
-      userInfo.value = { ...userInfo, val }
+      userInfo.value = val
     }
     // 删除用户信息
     const removeUserInfo = () => {
@@ -37,11 +39,63 @@ export const useUserStore = defineStore(
       return res
     }
 
+    /**
+     * 用户登录
+     * @param loginData 登录数据
+     * @param redirectUrl 登录成功后的跳转地址,默认跳转到首页
+     */
+    const login = async (loginData: any, redirectUrl?: string) => {
+      try {
+        // 显示加载状态
+        uni.showLoading({
+          title: 'Logging in...',
+          mask: true,
+        })
+
+        // 调用登录接口
+        const res = await _login(loginData)
+
+        uni.hideLoading()
+
+        // 登录成功,保存 token
+        if (res.code === '200' && res.data && res.data.token) {
+          token.value = res.data.token
+          uni.setStorageSync('token', res.data.token)
+          // 获取用户信息
+          await getUserInfo()
+        }
+
+        // 登录成功提示
+        toast.success('Login successful!')
+
+        // 跳转到指定页面或默认首页
+        setTimeout(() => {
+          if (redirectUrl) {
+            // 使用 toPage 方法,自动判断是否为 tabBar 页面
+            toPage(redirectUrl)
+          }
+          else {
+            // 默认跳转到首页
+            toPage('/pages/index/index')
+          }
+        }, 1500)
+
+        return res
+      }
+      catch (error) {
+        uni.hideLoading()
+        toast.error(error.message || 'Login failed')
+        throw error
+      }
+    }
+
     return {
       userInfo,
+      token,
       getUserInfo,
       setUserInfo,
       removeUserInfo,
+      login,
     }
   },
 )

+ 1 - 1
src/typings.d.ts

@@ -2,7 +2,7 @@
 
 declare global {
   interface IResData<T> {
-    code: number
+    code: number | string
     message: string
     data: T
   }

+ 11 - 2
src/utils/page.ts

@@ -7,13 +7,22 @@ export function goBack(delta = 1) {
 // 示例: toPage('/pages/productDetail/productDetail', { id: 123 })
 export function toPage(url: string, params?: Record<string, any>) {
   let targetUrl = url
+  const tabBarPages = ['/pages/index/index', '/pages/income/income', '/pages/mine/mine']
 
   if (params) {
     const data = JSON.stringify(params)
     targetUrl = `${url}?params=${encodeURIComponent(data)}`
   }
-
-  uni.navigateTo({ url: targetUrl })
+  if (tabBarPages.includes(url)) {
+    uni.switchTab({
+      url: targetUrl,
+    })
+  }
+  else {
+    uni.navigateTo({
+      url: targetUrl,
+    })
+  }
 }
 
 // page参数解析方法

+ 0 - 111
src/utils/request/alova.ts

@@ -1,111 +0,0 @@
-import type { uniappRequestAdapter } from '@alova/adapter-uniapp'
-import type { IResponse } from './types'
-import AdapterUniapp from '@alova/adapter-uniapp'
-import { createAlova } from 'alova'
-import { createServerTokenAuthentication } from 'alova/client'
-import VueHook from 'alova/vue'
-import { toast } from '@/utils/toast'
-import { ContentTypeEnum, ResultEnum, ShowMessage } from './enum'
-
-// 配置动态Tag
-export const API_DOMAINS = {
-  DEFAULT: import.meta.env.VITE_SERVER_BASEURL,
-  SECONDARY: import.meta.env.VITE_API_SECONDARY_URL,
-}
-
-/**
- * 创建请求实例
- */
-const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication<
-  typeof VueHook,
-  typeof uniappRequestAdapter
->({
-  refreshTokenOnError: {
-    isExpired: (error) => {
-      return error.response?.status === ResultEnum.Unauthorized
-    },
-    handler: async () => {
-      try {
-        // await authLogin();
-      }
-      catch (error) {
-        // 切换到登录页
-        await uni.reLaunch({ url: '/pages/common/login/index' })
-        throw error
-      }
-    },
-  },
-})
-
-/**
- * alova 请求实例
- */
-const alovaInstance = createAlova({
-  baseURL: import.meta.env.VITE_API_BASE_URL,
-  ...AdapterUniapp(),
-  timeout: 5000,
-  statesHook: VueHook,
-
-  beforeRequest: onAuthRequired((method) => {
-    // 设置默认 Content-Type
-    method.config.headers = {
-      ContentType: ContentTypeEnum.JSON,
-      Accept: 'application/json, text/plain, */*',
-      ...method.config.headers,
-    }
-
-    const { config } = method
-    const ignoreAuth = !config.meta?.ignoreAuth
-    console.log('ignoreAuth===>', ignoreAuth)
-    // 处理认证信息   自行处理认证问题
-    if (ignoreAuth) {
-      const token = 'getToken()'
-      if (!token) {
-        throw new Error('[请求错误]:未登录')
-      }
-      // method.config.headers.token = token;
-    }
-
-    // 处理动态域名
-    if (config.meta?.domain) {
-      method.baseURL = config.meta.domain
-      console.log('当前域名', method.baseURL)
-    }
-  }),
-
-  responded: onResponseRefreshToken((response, method) => {
-    const { config } = method
-    const { requestType } = config
-    const {
-      statusCode,
-      data: rawData,
-      errMsg,
-    } = response as UniNamespace.RequestSuccessCallbackResult
-
-    // 处理特殊请求类型(上传/下载)
-    if (requestType === 'upload' || requestType === 'download') {
-      return response
-    }
-
-    // 处理 HTTP 状态码错误
-    if (statusCode !== 200) {
-      const errorMessage = ShowMessage(statusCode) || `HTTP请求错误[${statusCode}]`
-      console.error('errorMessage===>', errorMessage)
-      toast.error(errorMessage)
-      throw new Error(`${errorMessage}:${errMsg}`)
-    }
-
-    // 处理业务逻辑错误
-    const { code, message, data } = rawData as IResponse
-    if (code !== ResultEnum.Success) {
-      if (config.meta?.toast !== false) {
-        toast.warning(message)
-      }
-      throw new Error(`请求错误[${code}]:${message}`)
-    }
-    // 处理成功响应,返回业务数据
-    return data
-  }),
-})
-
-export const http = alovaInstance

+ 0 - 66
src/utils/request/enum.ts

@@ -1,66 +0,0 @@
-export enum ResultEnum {
-  Success = 0, // 成功
-  Error = 400, // 错误
-  Unauthorized = 401, // 未授权
-  Forbidden = 403, // 禁止访问(原为forbidden)
-  NotFound = 404, // 未找到(原为notFound)
-  MethodNotAllowed = 405, // 方法不允许(原为methodNotAllowed)
-  RequestTimeout = 408, // 请求超时(原为requestTimeout)
-  InternalServerError = 500, // 服务器错误(原为internalServerError)
-  NotImplemented = 501, // 未实现(原为notImplemented)
-  BadGateway = 502, // 网关错误(原为badGateway)
-  ServiceUnavailable = 503, // 服务不可用(原为serviceUnavailable)
-  GatewayTimeout = 504, // 网关超时(原为gatewayTimeout)
-  HttpVersionNotSupported = 505, // HTTP版本不支持(原为httpVersionNotSupported)
-}
-export enum ContentTypeEnum {
-  JSON = 'application/json;charset=UTF-8',
-  FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
-  FORM_DATA = 'multipart/form-data;charset=UTF-8',
-}
-/**
- * 根据状态码,生成对应的错误信息
- * @param {number|string} status 状态码
- * @returns {string} 错误信息
- */
-export function ShowMessage(status: number | string): string {
-  let message: string
-  switch (status) {
-    case 400:
-      message = '请求错误(400)'
-      break
-    case 401:
-      message = '未授权,请重新登录(401)'
-      break
-    case 403:
-      message = '拒绝访问(403)'
-      break
-    case 404:
-      message = '请求出错(404)'
-      break
-    case 408:
-      message = '请求超时(408)'
-      break
-    case 500:
-      message = '服务器错误(500)'
-      break
-    case 501:
-      message = '服务未实现(501)'
-      break
-    case 502:
-      message = '网络错误(502)'
-      break
-    case 503:
-      message = '服务不可用(503)'
-      break
-    case 504:
-      message = '网络超时(504)'
-      break
-    case 505:
-      message = 'HTTP版本不受支持(505)'
-      break
-    default:
-      message = `连接出错(${status})!`
-  }
-  return `${message},请检查网络或联系管理员!`
-}

+ 0 - 22
src/utils/request/types.ts

@@ -1,22 +0,0 @@
-// 通用响应格式
-export interface IResponse<T = any> {
-  code: number | string
-  data: T
-  message: string
-  status: string | number
-}
-
-// 分页请求参数
-export interface PageParams {
-  page: number
-  pageSize: number
-  [key: string]: any
-}
-
-// 分页响应数据
-export interface PageResult<T> {
-  list: T[]
-  total: number
-  page: number
-  pageSize: number
-}