Enhance backend functionality and frontend UI
- Updated main.py to include authentication for brand, model, and OTA routers. - Added new OTA schemas in schemas.py for version management. - Enhanced model retrieval with sorting options in models.py. - Improved model update functionality to support multipart/form-data uploads. - Updated frontend layout and styles for a more modern look, including new font integration. - Implemented login route and authentication checks in router/index.js. - Added sorting capabilities in model table and improved file handling in model view. - Updated requirements.txt to include PyJWT for token management.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
const TOKEN_KEY = 'dashboard_access_token'
|
||||
|
||||
export function getToken() {
|
||||
return localStorage.getItem(TOKEN_KEY)
|
||||
}
|
||||
|
||||
export function setToken(token) {
|
||||
if (token) {
|
||||
localStorage.setItem(TOKEN_KEY, token)
|
||||
} else {
|
||||
localStorage.removeItem(TOKEN_KEY)
|
||||
}
|
||||
}
|
||||
|
||||
export function clearToken() {
|
||||
localStorage.removeItem(TOKEN_KEY)
|
||||
}
|
||||
|
||||
/** 解析 JWT 过期时间(毫秒时间戳),失败返回 null */
|
||||
export function getTokenExpiresAt(token) {
|
||||
if (!token) return null
|
||||
try {
|
||||
const part = token.split('.')[1]
|
||||
if (!part) return null
|
||||
let base64 = part.replace(/-/g, '+').replace(/_/g, '/')
|
||||
const pad = base64.length % 4
|
||||
if (pad) base64 += '='.repeat(4 - pad)
|
||||
const payload = JSON.parse(atob(base64))
|
||||
if (payload.exp == null) return null
|
||||
return payload.exp * 1000
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function isTokenExpired(token = getToken()) {
|
||||
const exp = getTokenExpiresAt(token)
|
||||
if (!exp) return true
|
||||
return Date.now() >= exp
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import axios from 'axios'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { getToken, clearToken } from '@/utils/auth'
|
||||
|
||||
// 创建 axios 实例
|
||||
const request = axios.create({
|
||||
@@ -10,6 +11,11 @@ const request = axios.create({
|
||||
// 请求拦截器
|
||||
request.interceptors.request.use(
|
||||
config => {
|
||||
const token = getToken()
|
||||
const url = config.url || ''
|
||||
if (token && !url.includes('/auth/login')) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
@@ -32,6 +38,21 @@ request.interceptors.response.use(
|
||||
return res
|
||||
},
|
||||
error => {
|
||||
const status = error.response?.status
|
||||
if (status === 401) {
|
||||
clearToken()
|
||||
const detail = error.response?.data?.detail
|
||||
if (detail) {
|
||||
ElMessage.warning(typeof detail === 'string' ? detail : '登录已失效')
|
||||
}
|
||||
const path = window.location.pathname || ''
|
||||
if (!path.startsWith('/login')) {
|
||||
window.location.assign(
|
||||
`/login?redirect=${encodeURIComponent(path + (window.location.search || ''))}`
|
||||
)
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
console.error('响应错误:', error)
|
||||
ElMessage.error(error.message || '网络错误')
|
||||
return Promise.reject(error)
|
||||
|
||||
Reference in New Issue
Block a user