feat(uniapp): 登录后立即获取用户资料并优化应用启动流程

This commit is contained in:
augushong
2026-02-01 12:10:24 +08:00
parent 1a88ff286e
commit 83e6803a0a
4 changed files with 208 additions and 4 deletions

View File

@@ -1,13 +1,15 @@
<script>
import { pinia } from './store'
import { useUserStore } from './store/user'
export default {
onLaunch: function () {
console.log('App Launch')
useUserStore(pinia).bootstrap({ forceProfile: true })
},
onShow: function () {
console.log('App Show')
useUserStore(pinia).bootstrap({ forceProfile: false })
},
onHide: function () {
console.log('App Hide')
},
}
</script>

View File

@@ -65,6 +65,7 @@ export default {
}
userStore.setToken(token)
await userStore.fetchProfile({ force: true })
uni.showToast({ title: '登录成功', icon: 'none' })
uni.navigateBack({

View File

@@ -11,6 +11,37 @@
</view>
</view>
<view class="card">
<view class="row">
<view class="label">用户ID</view>
<view class="value">{{ isLoggedIn ? (profile.id || '-') : '-' }}</view>
</view>
<view class="row">
<view class="label">用户名</view>
<view class="value">{{ isLoggedIn ? (profile.username || '-') : '-' }}</view>
</view>
<view class="row">
<view class="label">昵称</view>
<view class="value">{{ isLoggedIn ? (profile.nickname || '-') : '-' }}</view>
</view>
<view class="row">
<view class="label">手机号</view>
<view class="value">{{ isLoggedIn ? (profile.phone || '-') : '-' }}</view>
</view>
<view class="row">
<view class="label">备注</view>
<view class="value">{{ isLoggedIn ? (profile.remark || '-') : '-' }}</view>
</view>
<view class="row">
<view class="label">头像</view>
<view class="value">{{ isLoggedIn ? (profile.head_img || '-') : '-' }}</view>
</view>
<view class="row">
<view class="label">读取状态</view>
<view class="value">{{ isLoggedIn ? (profileLoading ? '读取中...' : '已读取') : '-' }}</view>
</view>
</view>
<up-button type="primary" text="重新登录" @click="goLogin" />
<view style="height: 16rpx" />
<up-button type="error" text="退出登录" @click="logout" />
@@ -32,12 +63,20 @@ export default {
isLoggedIn() {
return this.userStore.isLoggedIn
},
profile() {
return this.userStore.profile || {}
},
profileLoading() {
return this.userStore.profileLoading
},
},
onShow() {
this.userStore.init()
if (!this.isLoggedIn) {
this.goLogin()
return
}
this.userStore.fetchProfile({ force: true })
},
methods: {
goLogin() {

View File

@@ -1,11 +1,99 @@
import { defineStore } from 'pinia'
import { env } from '../config/env'
const TOKEN_KEY = 'ULTHON_ADMIN_TOKEN'
const PROFILE_KEY = 'ULTHON_ADMIN_PROFILE'
const PROFILE_FETCHED_AT_KEY = 'ULTHON_ADMIN_PROFILE_FETCHED_AT'
const AUTH_REQUIRED_CODE = 40101
const AUTH_EXPIRED_CODE = 40102
function buildUrl(url) {
if (!url) return ''
if (/^https?:\/\//i.test(url)) return url
const baseUrl = env.apiBaseUrl || ''
if (url.startsWith('/')) return `${baseUrl}${url}`
return `${baseUrl}/${url}`
}
function getCurrentRoutePath() {
try {
const pages = getCurrentPages()
const current = pages && pages[pages.length - 1]
const route = (current && (current.route || (current.$page && current.$page.fullPath))) || ''
return String(route)
} catch (e) {
return ''
}
}
function redirectToLoginIfNeeded() {
const route = getCurrentRoutePath()
if (route.includes('pages/login/index')) return
uni.navigateTo({
url: '/pages/login/index',
fail: () => {
uni.reLaunch({ url: '/pages/login/index' })
},
})
}
function requestJson(options) {
const url = buildUrl(options.url)
const method = (options.method || 'GET').toUpperCase()
const header = {
Accept: 'application/json',
...(options.header || {}),
}
if (options.token && !header.Authorization) {
header.Authorization = `Bearer ${options.token}`
}
return new Promise((resolve, reject) => {
uni.request({
url,
method,
data: options.data || {},
header,
timeout: options.timeout || env.timeout || 60000,
success: (res) => {
const resData = res.data
if (!res || typeof res !== 'object') {
reject(new Error('Invalid response'))
return
}
if (res.statusCode && res.statusCode >= 400) {
reject(new Error(`HTTP ${res.statusCode}`))
return
}
if (resData && typeof resData === 'object' && 'code' in resData) {
if (resData.code === 0) {
resolve(resData)
return
}
reject(resData)
return
}
resolve(resData)
},
fail: (err) => reject(err),
})
})
}
export const useUserStore = defineStore('user', {
state: () => ({
token: '',
inited: false,
profile: null,
profileFetchedAt: 0,
profileLoading: false,
}),
getters: {
isLoggedIn: (state) => !!state.token,
@@ -14,16 +102,90 @@ export const useUserStore = defineStore('user', {
init() {
if (this.inited) return
this.token = uni.getStorageSync(TOKEN_KEY) || ''
this.profile = uni.getStorageSync(PROFILE_KEY) || null
this.profileFetchedAt = Number(uni.getStorageSync(PROFILE_FETCHED_AT_KEY) || 0) || 0
this.inited = true
},
async bootstrap(options = {}) {
this.init()
if (!this.token) return
await this.fetchProfile({ force: !!options.forceProfile })
},
setToken(token) {
this.token = token || ''
uni.setStorageSync(TOKEN_KEY, this.token)
if (!this.token) {
this.profile = null
this.profileFetchedAt = 0
uni.removeStorageSync(PROFILE_KEY)
uni.removeStorageSync(PROFILE_FETCHED_AT_KEY)
}
},
async fetchProfile(options = {}) {
this.init()
if (!this.token) {
this.profile = null
this.profileFetchedAt = 0
return null
}
const now = Date.now()
const cacheMs = Number(options.cacheMs || 60 * 1000) || 60 * 1000
if (!options.force && this.profileFetchedAt && now - this.profileFetchedAt < cacheMs) {
return this.profile
}
if (this._profilePromise) {
return this._profilePromise
}
this.profileLoading = true
this._profilePromise = (async () => {
try {
const res = await requestJson({
url: '/admin/index/editAdmin',
method: 'GET',
data: { get_page_data: 1 },
token: this.token,
})
const payload = (res && res.data) || {}
const profile = payload.row || payload.admin || payload.session_admin || null
this.profile = profile
this.profileFetchedAt = Date.now()
uni.setStorageSync(PROFILE_KEY, this.profile || null)
uni.setStorageSync(PROFILE_FETCHED_AT_KEY, this.profileFetchedAt)
return this.profile
} catch (e) {
const code = e && typeof e === 'object' ? e.code : null
if (code === AUTH_REQUIRED_CODE || code === AUTH_EXPIRED_CODE) {
this.logout()
redirectToLoginIfNeeded()
return null
}
return this.profile
} finally {
this.profileLoading = false
}
})()
try {
return await this._profilePromise
} finally {
this._profilePromise = null
}
},
logout() {
this.token = ''
this.profile = null
this.profileFetchedAt = 0
uni.removeStorageSync(TOKEN_KEY)
uni.removeStorageSync(PROFILE_KEY)
uni.removeStorageSync(PROFILE_FETCHED_AT_KEY)
},
},
})