feat:登录的功能完成

This commit is contained in:
李伊帆
2026-09-21 09:36:38 +08:00
parent ce93816fd5
commit 24d001e0f5
20 changed files with 299 additions and 40 deletions

View File

@@ -1,3 +1,3 @@
VITE_API_BASE_URL=http://192.168.1.187:8080 VITE_API_BASE_URL=http://192.168.0.206:8080
# 后台「系统管理 -> 客户端管理」中 APP 端的 clientId # APP 端 OAuth 客户端 ID(与 Web 端区分)
VITE_AUTH_CLIENT_ID= VITE_AUTH_CLIENT_ID=428a8310cd442757ae699df5d894f051

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
import type { LoginResult, PasswordLoginParams, RegisterParams, RegisterResult, SendSmsCodeParams } from '@/types/auth' import type { LoginResult, PasswordLoginParams, RegisterParams, RegisterResult, ResetPasswordParams, SendSmsCodeParams, SmsLoginParams } from '@/types/auth'
import { AUTH_CLIENT_ID } from '@/constants/config' import { AUTH_CLIENT_ID } from '@/constants/config'
import { post } from '@/utils/request' import { post } from '@/utils/request'
@@ -6,6 +6,16 @@ const AUTH_API = {
register: '/app/auth/register', register: '/app/auth/register',
sendSmsCode: '/app/auth/sms/code', sendSmsCode: '/app/auth/sms/code',
loginByPassword: '/app/auth/login/password', loginByPassword: '/app/auth/login/password',
loginBySms: '/app/auth/login/sms',
resetPassword: '/app/auth/password/reset',
logout: '/app/auth/logout',
}
const CLIENT_ID_MISSING_MESSAGE = '未配置认证客户端 ID,请联系后端获取 clientId 并写入 .env.development'
function rejectMissingClientId() {
uni.showToast({ title: CLIENT_ID_MISSING_MESSAGE, icon: 'none', duration: 3000 })
return Promise.reject(new Error(CLIENT_ID_MISSING_MESSAGE))
} }
export function sendSmsCode(data: SendSmsCodeParams) { export function sendSmsCode(data: SendSmsCodeParams) {
@@ -20,11 +30,8 @@ export function loginByPassword(
data: PasswordLoginParams, data: PasswordLoginParams,
options?: { showErrorToast?: boolean }, options?: { showErrorToast?: boolean },
) { ) {
if (!AUTH_CLIENT_ID) { if (!AUTH_CLIENT_ID)
const message = '未配置认证客户端 ID,请联系后端获取 clientId 并写入 .env.development' return rejectMissingClientId()
uni.showToast({ title: message, icon: 'none', duration: 3000 })
return Promise.reject(new Error(message))
}
return post<LoginResult>( return post<LoginResult>(
AUTH_API.loginByPassword, AUTH_API.loginByPassword,
@@ -36,3 +43,29 @@ export function loginByPassword(
options, options,
) )
} }
export function loginBySms(
data: SmsLoginParams,
options?: { showErrorToast?: boolean },
) {
if (!AUTH_CLIENT_ID)
return rejectMissingClientId()
return post<LoginResult>(
AUTH_API.loginBySms,
{
clientId: AUTH_CLIENT_ID,
phoneNumber: data.phoneNumber,
smsCode: data.smsCode,
},
options,
)
}
export function logout() {
return post<void>(AUTH_API.logout, {}, { showErrorToast: false })
}
export function resetPassword(data: ResetPasswordParams) {
return post<void>(AUTH_API.resetPassword, data)
}

View File

@@ -17,9 +17,13 @@ function getTabIcon(tab: typeof tabs[number]) {
return tab.key === props.active ? tab.activeIcon : tab.icon return tab.key === props.active ? tab.activeIcon : tab.icon
} }
import { ensureLoggedIn } from '@/utils/auth-guard'
function handleSwitch(path: string, key: TabKey) { function handleSwitch(path: string, key: TabKey) {
if (key === props.active) if (key === props.active)
return return
if (key === 'home' && !ensureLoggedIn())
return
uni.redirectTo({ url: path }) uni.redirectTo({ url: path })
} }
</script> </script>

View File

@@ -1,5 +1,5 @@
/** 后端服务地址 */ /** 后端服务地址 */
export const API_HOST = '192.168.1.187' export const API_HOST = '192.168.0.206'
export const API_PORT = 8080 export const API_PORT = 8080
@@ -8,5 +8,6 @@ export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || `http://${API_H
/** 接口请求超时(毫秒) */ /** 接口请求超时(毫秒) */
export const API_TIMEOUT = 15000 export const API_TIMEOUT = 15000
/** APP 端 OAuth 客户端 ID(sys_client 表,需与后端「客户端管理」一致) */ /** APP 端 OAuth 客户端 ID(与 Web 端区分,可用 VITE_AUTH_CLIENT_ID 覆盖) */
export const AUTH_CLIENT_ID = import.meta.env.VITE_AUTH_CLIENT_ID || '' export const AUTH_CLIENT_ID =
import.meta.env.VITE_AUTH_CLIENT_ID || '428a8310cd442757ae699df5d894f051'

View File

@@ -1,6 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { resetPassword } from '@/api/auth'
import PasswordStrength from '@/pages/Login/user-register/components/PasswordStrength.vue'
import { getPasswordStrength } from '@/utils/password-strength'
import { navigateBackWithFallback } from '@/utils/navigate-back'
import { navigateToResultPage } from '@/utils/navigate-result' import { navigateToResultPage } from '@/utils/navigate-result'
import { validatePhone } from '@/utils/sms-code'
import CodeInput from './components/CodeInput.vue' import CodeInput from './components/CodeInput.vue'
import ForgotInput from './components/ForgotInput.vue' import ForgotInput from './components/ForgotInput.vue'
import PasswordField from './components/PasswordField.vue' import PasswordField from './components/PasswordField.vue'
@@ -10,12 +15,16 @@ const code = ref('')
const newPassword = ref('') const newPassword = ref('')
const confirmPassword = ref('') const confirmPassword = ref('')
const agreed = ref(false) const agreed = ref(false)
const submitting = ref(false)
function toggleAgreed() { function toggleAgreed() {
agreed.value = !agreed.value agreed.value = !agreed.value
} }
function handleSubmit() { async function handleSubmit() {
if (submitting.value)
return
if (!agreed.value) { if (!agreed.value) {
uni.showToast({ title: '请先同意用户协议', icon: 'none' }) uni.showToast({ title: '请先同意用户协议', icon: 'none' })
return return
@@ -24,15 +33,40 @@ function handleSubmit() {
uni.showToast({ title: '请填写完整信息', icon: 'none' }) uni.showToast({ title: '请填写完整信息', icon: 'none' })
return return
} }
if (!validatePhone(phone.value))
return
if (newPassword.value !== confirmPassword.value) { if (newPassword.value !== confirmPassword.value) {
uni.showToast({ title: '两次密码输入不一致', icon: 'none' }) uni.showToast({ title: '两次密码输入不一致', icon: 'none' })
return return
} }
if (getPasswordStrength(newPassword.value).isTooWeak) {
uni.showToast({ title: '密码等级过低,请重新输入', icon: 'none' })
return
}
submitting.value = true
uni.showLoading({ title: '提交中...', mask: true })
try {
await resetPassword({
phoneNumber: phone.value.trim(),
smsCode: code.value.trim(),
newPassword: newPassword.value,
confirmPassword: confirmPassword.value,
})
navigateToResultPage('resetPassword') navigateToResultPage('resetPassword')
}
catch {
// 错误提示由 request 统一处理
}
finally {
submitting.value = false
uni.hideLoading()
}
} }
function handleLogin() { function handleLogin() {
uni.navigateBack() navigateBackWithFallback('/pages/Login/user-login/index')
} }
function handleUserAgreement() { function handleUserAgreement() {
@@ -75,6 +109,7 @@ function handleUserAgreement() {
v-model="newPassword" v-model="newPassword"
placeholder="请输入新密码" placeholder="请输入新密码"
/> />
<PasswordStrength :password="newPassword" />
</view> </view>
<view class="forgot-page__field"> <view class="forgot-page__field">
@@ -126,9 +161,10 @@ function handleUserAgreement() {
<button <button
class="forgot-page__submit" class="forgot-page__submit"
:disabled="submitting"
@tap="handleSubmit" @tap="handleSubmit"
> >
确认修改 {{ submitting ? '提交中...' : '确认修改' }}
</button> </button>
</view> </view>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { loginByPassword } from '@/api/auth' import { loginByPassword } from '@/api/auth'
import { setAccessToken } from '@/utils/auth-storage' import { setAccessToken, setLoginPhoneNumber } from '@/utils/auth-storage'
import { navigateToResultPage } from '@/utils/navigate-result' import { navigateToResultPage } from '@/utils/navigate-result'
import { validatePhone } from '@/utils/sms-code' import { validatePhone } from '@/utils/sms-code'
import LoginInput from './components/LoginInput.vue' import LoginInput from './components/LoginInput.vue'
@@ -46,6 +46,8 @@ async function handleLogin() {
if (result.access_token) if (result.access_token)
setAccessToken(result.access_token) setAccessToken(result.access_token)
setLoginPhoneNumber(phone.value.trim())
uni.redirectTo({ url: '/pages/home-index/index' }) uni.redirectTo({ url: '/pages/home-index/index' })
} }
catch (error) { catch (error) {
@@ -87,21 +89,10 @@ function handleUserAgreement() {
url: '/pages/Login/user-agreement/index', url: '/pages/Login/user-agreement/index',
}) })
} }
function handleGuestMode() {
uni.redirectTo({ url: '/pages/home-index/index' })
}
</script> </script>
<template> <template>
<view class="login-page"> <view class="login-page">
<text
class="login-page__guest"
@tap="handleGuestMode"
>
游客模式
</text>
<view class="login-page__header"> <view class="login-page__header">
<text class="login-page__title"> <text class="login-page__title">
欢迎回来 欢迎回来

View File

@@ -2,6 +2,7 @@
import { ref } from 'vue' import { ref } from 'vue'
import { register } from '@/api/auth' import { register } from '@/api/auth'
import { getPasswordStrength } from '@/utils/password-strength' import { getPasswordStrength } from '@/utils/password-strength'
import { navigateBackWithFallback } from '@/utils/navigate-back'
import { navigateToResultPage } from '@/utils/navigate-result' import { navigateToResultPage } from '@/utils/navigate-result'
import CodeInput from './components/CodeInput.vue' import CodeInput from './components/CodeInput.vue'
import PasswordField from './components/PasswordField.vue' import PasswordField from './components/PasswordField.vue'
@@ -62,7 +63,7 @@ async function handleRegister() {
} }
function handleLogin() { function handleLogin() {
uni.navigateBack() navigateBackWithFallback('/pages/Login/user-login/index')
} }
function handleUserAgreement() { function handleUserAgreement() {

View File

@@ -1,17 +1,30 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { loginBySms } from '@/api/auth'
import { setAccessToken, setLoginPhoneNumber } from '@/utils/auth-storage'
import { navigateBackWithFallback } from '@/utils/navigate-back'
import { navigateToResultPage } from '@/utils/navigate-result'
import { validatePhone } from '@/utils/sms-code'
import CodeInput from './components/CodeInput.vue' import CodeInput from './components/CodeInput.vue'
import LoginInput from './components/LoginInput.vue' import LoginInput from './components/LoginInput.vue'
const phone = ref('') const phone = ref('')
const code = ref('') const code = ref('')
const agreed = ref(false) const agreed = ref(false)
const submitting = ref(false)
function toggleAgreed() { function toggleAgreed() {
agreed.value = !agreed.value agreed.value = !agreed.value
} }
function handleLogin() { function shouldNavigateToRegister(msg: string) {
return /未注册|不存在|请先注册|创建账号/.test(msg)
}
async function handleLogin() {
if (submitting.value)
return
if (!agreed.value) { if (!agreed.value) {
uni.showToast({ title: '请先同意用户协议', icon: 'none' }) uni.showToast({ title: '请先同意用户协议', icon: 'none' })
return return
@@ -20,13 +33,43 @@ function handleLogin() {
uni.showToast({ title: '请输入手机号和验证码', icon: 'none' }) uni.showToast({ title: '请输入手机号和验证码', icon: 'none' })
return return
} }
if (!validatePhone(phone.value))
return
submitting.value = true
uni.showLoading({ title: '登录中...', mask: true })
try {
const result = await loginBySms({
phoneNumber: phone.value.trim(),
smsCode: code.value.trim(),
}, { showErrorToast: false })
if (result.access_token)
setAccessToken(result.access_token)
setLoginPhoneNumber(phone.value.trim())
// TODO: 接入登录接口
uni.redirectTo({ url: '/pages/home-index/index' }) uni.redirectTo({ url: '/pages/home-index/index' })
}
catch (error) {
const message = error instanceof Error ? error.message : '登录失败'
if (message.includes('未配置认证客户端'))
return
if (shouldNavigateToRegister(message)) {
navigateToResultPage('loginFailed')
return
}
uni.showToast({ title: message, icon: 'none' })
}
finally {
submitting.value = false
uni.hideLoading()
}
} }
function handlePasswordLogin() { function handlePasswordLogin() {
uni.navigateBack() navigateBackWithFallback('/pages/Login/user-login/index')
} }
function handleRegister() { function handleRegister() {
@@ -111,9 +154,10 @@ function handleUserAgreement() {
<button <button
class="sms-login-page__submit" class="sms-login-page__submit"
:disabled="submitting"
@tap="handleLogin" @tap="handleLogin"
> >
登录 {{ submitting ? '登录中...' : '登录' }}
</button> </button>
</view> </view>

View File

@@ -1,8 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app' import { onLoad, onShow } from '@dcloudio/uni-app'
import { getBusinessDetail } from '@/constants/business-detail' import { getBusinessDetail } from '@/constants/business-detail'
import type { BusinessDetailContent } from '@/types/business-detail' import type { BusinessDetailContent } from '@/types/business-detail'
import { ensureLoggedIn } from '@/utils/auth-guard'
import { navigateBackWithFallback } from '@/utils/navigate-back' import { navigateBackWithFallback } from '@/utils/navigate-back'
import heroBg from '@/static/icons/background.png' import heroBg from '@/static/icons/background.png'
@@ -18,6 +19,10 @@ onLoad((options) => {
} }
}) })
onShow(() => {
ensureLoggedIn()
})
function handleBack() { function handleBack() {
navigateBackWithFallback('/pages/home-index/index') navigateBackWithFallback('/pages/home-index/index')
} }

View File

@@ -1,8 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { onShow } from '@dcloudio/uni-app'
import AppTabBar from '@/components/AppTabBar.vue' import AppTabBar from '@/components/AppTabBar.vue'
import { useGreeting } from '@/hooks/use-greeting' import { useGreeting } from '@/hooks/use-greeting'
import { useQuickEntries } from '@/hooks/use-quick-entries' import { useQuickEntries } from '@/hooks/use-quick-entries'
import type { ProjectItem, QuickEntryItem, ServiceItem } from '@/types/home' import type { ProjectItem, QuickEntryItem, ServiceItem } from '@/types/home'
import { ensureLoggedIn } from '@/utils/auth-guard'
import { navigateQuickEntry } from '@/utils/quick-entry-navigate' import { navigateQuickEntry } from '@/utils/quick-entry-navigate'
import headerBg from '@/static/icons/1.png' import headerBg from '@/static/icons/1.png'
import softwareIllus from '@/static/icons/software.png' import softwareIllus from '@/static/icons/software.png'
@@ -25,6 +27,10 @@ const {
addToPinned, addToPinned,
} = useQuickEntries() } = useQuickEntries()
onShow(() => {
ensureLoggedIn()
})
const services: ServiceItem[] = [ const services: ServiceItem[] = [
{ {
id: '1', id: '1',

View File

@@ -1,7 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app' import { onLoad, onShow } from '@dcloudio/uni-app'
import { BUSINESS_TYPE_DETAIL_ID, BUSINESS_TYPE_OPTIONS } from '@/constants/business-types' import { BUSINESS_TYPE_DETAIL_ID, BUSINESS_TYPE_OPTIONS } from '@/constants/business-types'
import { ensureLoggedIn } from '@/utils/auth-guard'
import { navigateBackWithFallback } from '@/utils/navigate-back' import { navigateBackWithFallback } from '@/utils/navigate-back'
import { navigateToResultPage } from '@/utils/navigate-result' import { navigateToResultPage } from '@/utils/navigate-result'
import deleteIcon from '@/static/icons/X.png' import deleteIcon from '@/static/icons/X.png'
@@ -41,6 +42,10 @@ onLoad((options) => {
} }
}) })
onShow(() => {
ensureLoggedIn()
})
function handleBack() { function handleBack() {
closeBusinessTypeDropdown() closeBusinessTypeDropdown()
navigateBackWithFallback(`/pages/home-index/business-detail/index?id=${detailId.value}`) navigateBackWithFallback(`/pages/home-index/business-detail/index?id=${detailId.value}`)

View File

@@ -94,13 +94,31 @@
color: $color-text-placeholder; color: $color-text-placeholder;
} }
.account-security__delete-btn { .account-security__logout-btn {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 88rpx; height: 88rpx;
margin-top: 120rpx; margin-top: 120rpx;
font-size: 30rpx; font-size: 30rpx;
color: $color-primary;
background: #ffffff;
border: 2rpx solid $color-primary;
border-radius: 12rpx;
box-sizing: border-box;
}
.account-security__logout-btn--disabled {
opacity: 0.6;
}
.account-security__delete-btn {
display: flex;
align-items: center;
justify-content: center;
height: 88rpx;
margin-top: 24rpx;
font-size: 30rpx;
color: #ffffff; color: #ffffff;
background: $color-primary; background: $color-primary;
border-radius: 12rpx; border-radius: 12rpx;

View File

@@ -1,9 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'
import { ACCOUNT_SECURITY_ITEMS } from '@/constants/account-security' import { ACCOUNT_SECURITY_ITEMS } from '@/constants/account-security'
import type { AccountSecurityItem } from '@/types/account-security' import type { AccountSecurityItem } from '@/types/account-security'
import { navigateBackWithFallback } from '@/utils/navigate-back' import { navigateBackWithFallback } from '@/utils/navigate-back'
import { performLogout } from '@/utils/logout'
const securityItems = ACCOUNT_SECURITY_ITEMS const securityItems = ACCOUNT_SECURITY_ITEMS
const loggingOut = ref(false)
function handleBack() { function handleBack() {
navigateBackWithFallback('/pages/personal-center/index') navigateBackWithFallback('/pages/personal-center/index')
@@ -40,6 +43,32 @@ function handleDeleteAccount() {
}, },
}) })
} }
function handleLogout() {
if (loggingOut.value)
return
uni.showModal({
title: '退出登录',
content: '确认退出当前账号吗?',
confirmText: '退出',
confirmColor: '#2563EB',
success: async (res) => {
if (!res.confirm)
return
loggingOut.value = true
uni.showLoading({ title: '退出中...', mask: true })
try {
await performLogout()
}
finally {
loggingOut.value = false
uni.hideLoading()
}
},
})
}
</script> </script>
<template> <template>
@@ -88,6 +117,14 @@ function handleDeleteAccount() {
</view> </view>
</view> </view>
<view
class="account-security__logout-btn"
:class="{ 'account-security__logout-btn--disabled': loggingOut }"
@tap="handleLogout"
>
退出登录
</view>
<view <view
class="account-security__delete-btn" class="account-security__delete-btn"
@tap="handleDeleteAccount" @tap="handleDeleteAccount"

View File

@@ -1,4 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { onShow } from '@dcloudio/uni-app'
import { ref } from 'vue'
import AppTabBar from '@/components/AppTabBar.vue' import AppTabBar from '@/components/AppTabBar.vue'
import { import {
PERSONAL_CENTER_MENU, PERSONAL_CENTER_MENU,
@@ -6,9 +8,19 @@ import {
} from '@/constants/personal-center' } from '@/constants/personal-center'
import type { PersonalCenterMenuItem } from '@/types/personal-center' import type { PersonalCenterMenuItem } from '@/types/personal-center'
import editIcon from '@/static/icon/edit.svg' import editIcon from '@/static/icon/edit.svg'
import { getLoginPhoneNumber } from '@/utils/auth-storage'
const profile = PERSONAL_CENTER_PROFILE const profile = PERSONAL_CENTER_PROFILE
const menuList = PERSONAL_CENTER_MENU const menuList = PERSONAL_CENTER_MENU
const displayPhone = ref('')
function refreshProfilePhone() {
displayPhone.value = getLoginPhoneNumber() || '未登录'
}
onShow(() => {
refreshProfilePhone()
})
function handleEditNickname() { function handleEditNickname() {
uni.showToast({ title: '编辑昵称开发中', icon: 'none' }) uni.showToast({ title: '编辑昵称开发中', icon: 'none' })
@@ -47,7 +59,7 @@ function handleMenuTap(item: PersonalCenterMenuItem) {
/> />
</view> </view>
<text class="personal-center__phone"> <text class="personal-center__phone">
{{ profile.phone }} {{ displayPhone }}
</text> </text>
</view> </view>
</view> </view>

View File

@@ -22,8 +22,20 @@ export interface PasswordLoginParams {
password: string password: string
} }
export interface SmsLoginParams {
phoneNumber: string
smsCode: string
}
export interface LoginResult { export interface LoginResult {
access_token: string access_token: string
expire_in?: number expire_in?: number
client_id?: string client_id?: string
} }
export interface ResetPasswordParams {
phoneNumber: string
smsCode: string
newPassword: string
confirmPassword: string
}

19
src/utils/auth-guard.ts Normal file
View File

@@ -0,0 +1,19 @@
import { getAccessToken } from '@/utils/auth-storage'
export const LOGIN_PAGE = '/pages/Login/user-login/index'
export function isLoggedIn() {
return Boolean(getAccessToken())
}
/** 未登录时跳转登录页,返回是否已登录 */
export function ensureLoggedIn(showToast = true) {
if (isLoggedIn())
return true
if (showToast)
uni.showToast({ title: '请先登录', icon: 'none' })
uni.reLaunch({ url: LOGIN_PAGE })
return false
}

View File

@@ -1,4 +1,5 @@
const ACCESS_TOKEN_KEY = 'access_token' const ACCESS_TOKEN_KEY = 'access_token'
const LOGIN_PHONE_KEY = 'login_phone_number'
export function setAccessToken(token: string) { export function setAccessToken(token: string) {
uni.setStorageSync(ACCESS_TOKEN_KEY, token) uni.setStorageSync(ACCESS_TOKEN_KEY, token)
@@ -11,3 +12,20 @@ export function getAccessToken() {
export function clearAccessToken() { export function clearAccessToken() {
uni.removeStorageSync(ACCESS_TOKEN_KEY) uni.removeStorageSync(ACCESS_TOKEN_KEY)
} }
export function setLoginPhoneNumber(phoneNumber: string) {
uni.setStorageSync(LOGIN_PHONE_KEY, phoneNumber)
}
export function getLoginPhoneNumber() {
return uni.getStorageSync(LOGIN_PHONE_KEY) as string
}
export function clearLoginPhoneNumber() {
uni.removeStorageSync(LOGIN_PHONE_KEY)
}
export function clearAuthSession() {
clearAccessToken()
clearLoginPhoneNumber()
}

17
src/utils/logout.ts Normal file
View File

@@ -0,0 +1,17 @@
import { logout as logoutApi } from '@/api/auth'
import { LOGIN_PAGE } from '@/utils/auth-guard'
import { clearAuthSession } from '@/utils/auth-storage'
/** 调用退出接口并清除本地登录态,跳转登录页 */
export async function performLogout() {
try {
await logoutApi()
}
catch {
// 网络或服务异常时仍清除本地会话,避免无法退出
}
finally {
clearAuthSession()
uni.reLaunch({ url: LOGIN_PAGE })
}
}

View File

@@ -16,7 +16,7 @@ export default defineConfig({
server: { server: {
proxy: { proxy: {
'/api': { '/api': {
target: 'http://192.168.1.187:8080', target: 'http://192.168.0.206:8080',
changeOrigin: true, changeOrigin: true,
}, },
}, },