From 83e6803a0a6b11a6f3c8686ff1dccb8f7bf3be04 Mon Sep 17 00:00:00 2001 From: augushong Date: Sun, 1 Feb 2026 12:10:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(uniapp):=20=E7=99=BB=E5=BD=95=E5=90=8E?= =?UTF-8?q?=E7=AB=8B=E5=8D=B3=E8=8E=B7=E5=8F=96=E7=94=A8=E6=88=B7=E8=B5=84?= =?UTF-8?q?=E6=96=99=E5=B9=B6=E4=BC=98=E5=8C=96=E5=BA=94=E7=94=A8=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/clients/uniapp/src/App.vue | 8 +- .../clients/uniapp/src/pages/login/index.vue | 1 + .../clients/uniapp/src/pages/mine/index.vue | 39 +++++ source/clients/uniapp/src/store/user.js | 164 +++++++++++++++++- 4 files changed, 208 insertions(+), 4 deletions(-) diff --git a/source/clients/uniapp/src/App.vue b/source/clients/uniapp/src/App.vue index da1e71b..85fdb98 100644 --- a/source/clients/uniapp/src/App.vue +++ b/source/clients/uniapp/src/App.vue @@ -1,13 +1,15 @@ diff --git a/source/clients/uniapp/src/pages/login/index.vue b/source/clients/uniapp/src/pages/login/index.vue index 262594c..73f3186 100644 --- a/source/clients/uniapp/src/pages/login/index.vue +++ b/source/clients/uniapp/src/pages/login/index.vue @@ -65,6 +65,7 @@ export default { } userStore.setToken(token) + await userStore.fetchProfile({ force: true }) uni.showToast({ title: '登录成功', icon: 'none' }) uni.navigateBack({ diff --git a/source/clients/uniapp/src/pages/mine/index.vue b/source/clients/uniapp/src/pages/mine/index.vue index 03751f2..d3b8edc 100644 --- a/source/clients/uniapp/src/pages/mine/index.vue +++ b/source/clients/uniapp/src/pages/mine/index.vue @@ -11,6 +11,37 @@ + + + 用户ID + {{ isLoggedIn ? (profile.id || '-') : '-' }} + + + 用户名 + {{ isLoggedIn ? (profile.username || '-') : '-' }} + + + 昵称 + {{ isLoggedIn ? (profile.nickname || '-') : '-' }} + + + 手机号 + {{ isLoggedIn ? (profile.phone || '-') : '-' }} + + + 备注 + {{ isLoggedIn ? (profile.remark || '-') : '-' }} + + + 头像 + {{ isLoggedIn ? (profile.head_img || '-') : '-' }} + + + 读取状态 + {{ isLoggedIn ? (profileLoading ? '读取中...' : '已读取') : '-' }} + + + @@ -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() { diff --git a/source/clients/uniapp/src/store/user.js b/source/clients/uniapp/src/store/user.js index cc72929..fcd0bf4 100644 --- a/source/clients/uniapp/src/store/user.js +++ b/source/clients/uniapp/src/store/user.js @@ -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) }, }, }) -