mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-07-01 15:32:48 +08:00
feat(uniapp): 集成 Pinia 状态管理并实现用户认证流程
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -40,3 +40,12 @@ result.txt
|
||||
test.php
|
||||
/.env.prod
|
||||
extend/base/common/command/curd/migrate_output.php
|
||||
|
||||
# source 目录:多端工程依赖与构建产物
|
||||
/source/**/node_modules
|
||||
/source/**/dist
|
||||
/source/**/.hbuilderx
|
||||
/source/**/.idea
|
||||
/source/**/.vscode
|
||||
.trae/documents/新增 source 目录与 uni-app(CLI)基础工程.md
|
||||
.trae/documents/uni-app 增加 env_request_utils 与用户状态机制.md
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
// !当前文件的内容应当与 /extend/base/admin/middleware.php 的内容一致,然后再根据实际情况设置
|
||||
|
||||
// 全局中间件定义文件
|
||||
|
||||
use think\middleware\AllowCrossDomain;
|
||||
|
||||
return [
|
||||
|
||||
// Session初始化
|
||||
@@ -14,4 +17,6 @@ return [
|
||||
// Csrf安全校验
|
||||
\app\admin\middleware\CsrfMiddleware::class,
|
||||
|
||||
AllowCrossDomain::class,
|
||||
|
||||
];
|
||||
|
||||
@@ -520,12 +520,12 @@ class AdminControllerBase extends BaseController
|
||||
!in_array($currentController, $adminConfig['no_login_controller']) &&
|
||||
!in_array($currentNode, $adminConfig['no_login_node'])
|
||||
) {
|
||||
empty($adminId) && $this->error('请先登录后台', [], __url('admin/login/index', ['back_url' => $back_url]));
|
||||
empty($adminId) && $this->error('请先登录后台', [], __url('admin/login/index', ['back_url' => $back_url]), 40101);
|
||||
|
||||
// 判断是否登录过期
|
||||
if ($expireTime !== true && time() > $expireTime) {
|
||||
session('admin', null);
|
||||
$this->error('登录已过期,请重新登录', [], __url('admin/login/index', ['back_url' => $back_url]));
|
||||
$this->error('登录已过期,请重新登录', [], __url('admin/login/index', ['back_url' => $back_url]), 40102);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
source/clients/uniapp/.npmrc
Normal file
2
source/clients/uniapp/.npmrc
Normal file
@@ -0,0 +1,2 @@
|
||||
legacy-peer-deps=true
|
||||
|
||||
5304
source/clients/uniapp/package-lock.json
generated
5304
source/clients/uniapp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -52,6 +52,7 @@
|
||||
"@dcloudio/uni-mp-weixin": "3.0.0-4080720251210001",
|
||||
"@dcloudio/uni-mp-xhs": "3.0.0-4080720251210001",
|
||||
"@dcloudio/uni-quickapp-webview": "3.0.0-4080720251210001",
|
||||
"pinia": "2.1.7",
|
||||
"uview-plus": "^3.3.65",
|
||||
"ului.css": "0.0.1",
|
||||
"vue": "^3.4.21",
|
||||
|
||||
23
source/clients/uniapp/src/common/index.js
Normal file
23
source/clients/uniapp/src/common/index.js
Normal file
@@ -0,0 +1,23 @@
|
||||
export function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
export function omitEmpty(obj) {
|
||||
const result = {}
|
||||
if (!obj || typeof obj !== 'object') return result
|
||||
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const value = obj[key]
|
||||
if (value === '' || value === null || value === undefined) return
|
||||
result[key] = value
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export function toQueryString(params) {
|
||||
const data = omitEmpty(params)
|
||||
const parts = Object.keys(data).map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`)
|
||||
return parts.join('&')
|
||||
}
|
||||
|
||||
5
source/clients/uniapp/src/config/env.js
Normal file
5
source/clients/uniapp/src/config/env.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export const env = {
|
||||
apiBaseUrl: 'http://127.0.0.1:8000',
|
||||
timeout: 60000,
|
||||
}
|
||||
|
||||
@@ -4,9 +4,13 @@ import {
|
||||
import App from "./App.vue";
|
||||
import uviewPlus from 'uview-plus'
|
||||
import './styles/uview.scss'
|
||||
import { pinia } from './store'
|
||||
import { useUserStore } from './store/user'
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App);
|
||||
app.use(uviewPlus);
|
||||
app.use(pinia);
|
||||
useUserStore(pinia).init();
|
||||
return {
|
||||
app,
|
||||
};
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { setToken } from '../../utils/auth'
|
||||
import { request } from '../../utils/request'
|
||||
import { useUserStore } from '../../store/user'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@@ -44,6 +44,7 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
const userStore = useUserStore()
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await request({
|
||||
@@ -63,7 +64,7 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
setToken(token)
|
||||
userStore.setToken(token)
|
||||
uni.showToast({ title: '登录成功', icon: 'none' })
|
||||
|
||||
uni.navigateBack({
|
||||
|
||||
@@ -18,18 +18,24 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { clearToken, getToken } from '../../utils/auth'
|
||||
import { request } from '../../utils/request'
|
||||
import { useUserStore } from '../../store/user'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
token: '',
|
||||
}
|
||||
computed: {
|
||||
userStore() {
|
||||
return useUserStore()
|
||||
},
|
||||
token() {
|
||||
return this.userStore.token
|
||||
},
|
||||
isLoggedIn() {
|
||||
return this.userStore.isLoggedIn
|
||||
},
|
||||
},
|
||||
onShow() {
|
||||
this.token = getToken()
|
||||
if (!this.token) {
|
||||
this.userStore.init()
|
||||
if (!this.isLoggedIn) {
|
||||
this.goLogin()
|
||||
}
|
||||
},
|
||||
@@ -38,7 +44,7 @@ export default {
|
||||
uni.navigateTo({ url: '/pages/login/index' })
|
||||
},
|
||||
async logout() {
|
||||
if (!this.token) {
|
||||
if (!this.isLoggedIn) {
|
||||
uni.showToast({ title: '当前未登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
@@ -49,8 +55,7 @@ export default {
|
||||
method: 'POST',
|
||||
})
|
||||
} finally {
|
||||
clearToken()
|
||||
this.token = ''
|
||||
this.userStore.logout()
|
||||
uni.showToast({ title: '已退出', icon: 'none' })
|
||||
uni.switchTab({ url: '/pages/index/index' })
|
||||
}
|
||||
|
||||
4
source/clients/uniapp/src/store/index.js
Normal file
4
source/clients/uniapp/src/store/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
export const pinia = createPinia()
|
||||
|
||||
29
source/clients/uniapp/src/store/user.js
Normal file
29
source/clients/uniapp/src/store/user.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
const TOKEN_KEY = 'ULTHON_ADMIN_TOKEN'
|
||||
|
||||
export const useUserStore = defineStore('user', {
|
||||
state: () => ({
|
||||
token: '',
|
||||
inited: false,
|
||||
}),
|
||||
getters: {
|
||||
isLoggedIn: (state) => !!state.token,
|
||||
},
|
||||
actions: {
|
||||
init() {
|
||||
if (this.inited) return
|
||||
this.token = uni.getStorageSync(TOKEN_KEY) || ''
|
||||
this.inited = true
|
||||
},
|
||||
setToken(token) {
|
||||
this.token = token || ''
|
||||
uni.setStorageSync(TOKEN_KEY, this.token)
|
||||
},
|
||||
logout() {
|
||||
this.token = ''
|
||||
uni.removeStorageSync(TOKEN_KEY)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,18 +1,39 @@
|
||||
import { clearToken, getToken } from './auth'
|
||||
import { env } from '../config/env'
|
||||
import { pinia } from '../store'
|
||||
import { useUserStore } from '../store/user'
|
||||
|
||||
const BASE_URL = ''
|
||||
const AUTH_REQUIRED_CODE = 40101
|
||||
const AUTH_EXPIRED_CODE = 40102
|
||||
|
||||
function buildUrl(url) {
|
||||
if (!url) return ''
|
||||
if (/^https?:\/\//i.test(url)) return url
|
||||
if (url.startsWith('/')) return `${BASE_URL}${url}`
|
||||
return `${BASE_URL}/${url}`
|
||||
const baseUrl = env.apiBaseUrl || ''
|
||||
if (url.startsWith('/')) return `${baseUrl}${url}`
|
||||
return `${baseUrl}/${url}`
|
||||
}
|
||||
|
||||
function isLoginError(resData) {
|
||||
const msg = (resData && resData.msg) || ''
|
||||
const url = (resData && resData.url) || ''
|
||||
return msg.includes('请先登录') || msg.includes('登录已过期') || url.includes('/admin/login')
|
||||
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' })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function request(options) {
|
||||
@@ -24,7 +45,9 @@ export function request(options) {
|
||||
...(options.header || {}),
|
||||
}
|
||||
|
||||
const token = getToken()
|
||||
const userStore = useUserStore(pinia)
|
||||
userStore.init()
|
||||
const token = userStore.token
|
||||
if (token && !header.Authorization) {
|
||||
header.Authorization = `Bearer ${token}`
|
||||
}
|
||||
@@ -39,7 +62,7 @@ export function request(options) {
|
||||
method,
|
||||
data: options.data || {},
|
||||
header,
|
||||
timeout: options.timeout || 60000,
|
||||
timeout: options.timeout || env.timeout || 60000,
|
||||
success: (res) => {
|
||||
const resData = res.data
|
||||
|
||||
@@ -61,9 +84,9 @@ export function request(options) {
|
||||
return
|
||||
}
|
||||
|
||||
if (isLoginError(resData)) {
|
||||
clearToken()
|
||||
uni.navigateTo({ url: '/pages/login/index' })
|
||||
if (resData.code === AUTH_REQUIRED_CODE || resData.code === AUTH_EXPIRED_CODE) {
|
||||
userStore.logout()
|
||||
redirectToLoginIfNeeded()
|
||||
}
|
||||
|
||||
uni.showToast({ title: resData.msg || '请求失败', icon: 'none' })
|
||||
@@ -80,4 +103,3 @@ export function request(options) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user