mirror of
https://github.com/un-pany/v3-admin-vite.git
synced 2025-04-22 03:49:19 +08:00
refactor:增加统一错误处理
This commit is contained in:
parent
03928dfce5
commit
6bb345e558
4
.gitignore
vendored
4
.gitignore
vendored
@ -33,3 +33,7 @@ lerna-debug.log*
|
||||
# Use the PNPM
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# history
|
||||
|
||||
.history
|
||||
|
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@ -1,7 +1,7 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
@ -26,5 +26,10 @@
|
||||
},
|
||||
"[scss]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
},
|
||||
"cSpell.words": [
|
||||
"endregion",
|
||||
"nprogress",
|
||||
"pinia"
|
||||
]
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { handleApiError } from "@/utils/error-handler"
|
||||
|
||||
/** 模拟接口响应数据 */
|
||||
const SELECT_RESPONSE_DATA = {
|
||||
code: 0,
|
||||
@ -22,15 +24,12 @@ const SELECT_RESPONSE_DATA = {
|
||||
/** 模拟接口 */
|
||||
export function getSelectDataApi() {
|
||||
return new Promise<typeof SELECT_RESPONSE_DATA>((resolve, reject) => {
|
||||
// 模拟接口响应时间 2s
|
||||
setTimeout(() => {
|
||||
// 模拟接口调用成功
|
||||
if (Math.random() < 0.8) {
|
||||
resolve(SELECT_RESPONSE_DATA)
|
||||
} else {
|
||||
// 模拟接口调用出错
|
||||
reject(new Error("接口发生错误"))
|
||||
}
|
||||
}, 2000)
|
||||
})
|
||||
}).catch(handleApiError)
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { request } from "@/utils/service"
|
||||
import type * as Login from "./types/login"
|
||||
import { handleApiError } from "@/utils/error-handler"
|
||||
|
||||
/** 获取登录验证码 */
|
||||
export function getLoginCodeApi() {
|
||||
return request<Login.LoginCodeResponseData>({
|
||||
url: "login/code",
|
||||
method: "get"
|
||||
})
|
||||
}).catch(handleApiError)
|
||||
}
|
||||
|
||||
/** 登录并返回 Token */
|
||||
@ -15,7 +16,7 @@ export function loginApi(data: Login.LoginRequestData) {
|
||||
url: "users/login",
|
||||
method: "post",
|
||||
data
|
||||
})
|
||||
}).catch(handleApiError)
|
||||
}
|
||||
|
||||
/** 获取用户详情 */
|
||||
@ -23,5 +24,5 @@ export function getUserInfoApi() {
|
||||
return request<Login.UserInfoResponseData>({
|
||||
url: "users/info",
|
||||
method: "get"
|
||||
})
|
||||
}).catch(handleApiError)
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { request } from "@/utils/service"
|
||||
import type * as Table from "./types/table"
|
||||
import { handleApiError } from "@/utils/error-handler"
|
||||
|
||||
/** 增 */
|
||||
export function createTableDataApi(data: Table.CreateTableRequestData) {
|
||||
@ -7,7 +8,7 @@ export function createTableDataApi(data: Table.CreateTableRequestData) {
|
||||
url: "table",
|
||||
method: "post",
|
||||
data
|
||||
})
|
||||
}).catch(handleApiError)
|
||||
}
|
||||
|
||||
/** 删 */
|
||||
@ -15,7 +16,7 @@ export function deleteTableDataApi(id: string) {
|
||||
return request({
|
||||
url: `table/${id}`,
|
||||
method: "delete"
|
||||
})
|
||||
}).catch(handleApiError)
|
||||
}
|
||||
|
||||
/** 改 */
|
||||
@ -24,7 +25,7 @@ export function updateTableDataApi(data: Table.UpdateTableRequestData) {
|
||||
url: "table",
|
||||
method: "put",
|
||||
data
|
||||
})
|
||||
}).catch(handleApiError)
|
||||
}
|
||||
|
||||
/** 查 */
|
||||
@ -33,5 +34,5 @@ export function getTableDataApi(params: Table.GetTableRequestData) {
|
||||
url: "table",
|
||||
method: "get",
|
||||
params
|
||||
})
|
||||
}).catch(handleApiError)
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import routeSettings from "@/config/route"
|
||||
import isWhiteList from "@/config/white-list"
|
||||
import NProgress from "nprogress"
|
||||
import "nprogress/nprogress.css"
|
||||
import { handleApiError } from "@/utils/error-handler"
|
||||
|
||||
const { setTitle } = useTitle()
|
||||
NProgress.configure({ showSpinner: false })
|
||||
@ -61,10 +62,11 @@ router.beforeEach(async (to, _from, next) => {
|
||||
// 确保添加路由已完成
|
||||
// 设置 replace: true, 因此导航将不会留下历史记录
|
||||
next({ ...to, replace: true })
|
||||
} catch (err: any) {
|
||||
} catch (error: any) {
|
||||
// 过程中发生任何错误,都直接重置 Token,并重定向到登录页面
|
||||
handleApiError(error)
|
||||
userStore.resetToken()
|
||||
ElMessage.error(err.message || "路由守卫过程发生错误")
|
||||
ElMessage.error(error.message || "路由守卫过程发生错误")
|
||||
NProgress.done()
|
||||
next("/login")
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import { type RouteRecordRaw } from "vue-router"
|
||||
import { constantRoutes, asyncRoutes } from "@/router"
|
||||
import { flatMultiLevelRoutes } from "@/router/helper"
|
||||
import routeSettings from "@/config/route"
|
||||
import { handleApiError } from "@/utils/error-handler"
|
||||
|
||||
const hasPermission = (roles: string[], route: RouteRecordRaw) => {
|
||||
const routeRoles = route.meta?.roles
|
||||
@ -30,9 +31,15 @@ export const usePermissionStore = defineStore("permission", () => {
|
||||
const dynamicRoutes = ref<RouteRecordRaw[]>([])
|
||||
|
||||
const setRoutes = (roles: string[]) => {
|
||||
const accessedRoutes = routeSettings.async ? filterAsyncRoutes(asyncRoutes, roles) : asyncRoutes
|
||||
routes.value = constantRoutes.concat(accessedRoutes)
|
||||
dynamicRoutes.value = routeSettings.thirdLevelRouteCache ? flatMultiLevelRoutes(accessedRoutes) : accessedRoutes
|
||||
try {
|
||||
const accessedRoutes = routeSettings.async ? filterAsyncRoutes(asyncRoutes, roles) : asyncRoutes
|
||||
routes.value = constantRoutes.concat(accessedRoutes)
|
||||
dynamicRoutes.value = routeSettings.thirdLevelRouteCache ? flatMultiLevelRoutes(accessedRoutes) : accessedRoutes
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
handleApiError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { routes, dynamicRoutes, setRoutes }
|
||||
|
@ -10,6 +10,7 @@ import { loginApi, getUserInfoApi } from "@/api/login"
|
||||
import { type LoginRequestData } from "@/api/login/types/login"
|
||||
import { type RouteRecordRaw } from "vue-router"
|
||||
import routeSettings from "@/config/route"
|
||||
import { handleApiError } from "@/utils/error-handler"
|
||||
|
||||
export const useUserStore = defineStore("user", () => {
|
||||
const token = ref<string>(getToken() || "")
|
||||
@ -24,19 +25,34 @@ export const useUserStore = defineStore("user", () => {
|
||||
const setRoles = (value: string[]) => {
|
||||
roles.value = value
|
||||
}
|
||||
|
||||
/** 登录 */
|
||||
const login = async ({ username, password, code }: LoginRequestData) => {
|
||||
const { data } = await loginApi({ username, password, code })
|
||||
setToken(data.token)
|
||||
token.value = data.token
|
||||
try {
|
||||
const { data } = await loginApi({ username, password, code })
|
||||
setToken(data.token)
|
||||
token.value = data.token
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
handleApiError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取用户详情 */
|
||||
const getInfo = async () => {
|
||||
const { data } = await getUserInfoApi()
|
||||
username.value = data.username
|
||||
// 验证返回的 roles 是否为一个非空数组,否则塞入一个没有任何作用的默认角色,防止路由守卫逻辑进入无限循环
|
||||
roles.value = data.roles?.length > 0 ? data.roles : routeSettings.defaultRoles
|
||||
try {
|
||||
const { data } = await getUserInfoApi()
|
||||
username.value = data.username
|
||||
// 验证返回的 roles 是否为一个非空数组,否则塞入一个没有任何作用的默认角色,防止路由守卫逻辑进入无限循环
|
||||
roles.value = data.roles?.length > 0 ? data.roles : routeSettings.defaultRoles
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
handleApiError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 切换角色 */
|
||||
const changeRoles = async (role: string) => {
|
||||
const newToken = "token-" + role
|
||||
|
46
src/utils/error-handler.ts
Normal file
46
src/utils/error-handler.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { AxiosError } from "./types/axios-error"
|
||||
|
||||
// API 错误处理
|
||||
export function handleApiError(error: Error) {
|
||||
const axiosError = error as AxiosError
|
||||
|
||||
if (axiosError.response) {
|
||||
// 服务器响应了请求,但状态码不在 2xx 范围内
|
||||
console.error("API 错误,状态码:", axiosError.response.status)
|
||||
console.error("错误详情:", axiosError.response.data)
|
||||
// 根据错误码进行分类处理
|
||||
switch (axiosError.response.status) {
|
||||
case 400:
|
||||
console.error("请求参数错误")
|
||||
break
|
||||
case 401:
|
||||
console.error("未授权访问")
|
||||
break
|
||||
case 403:
|
||||
console.error("禁止访问")
|
||||
break
|
||||
case 404:
|
||||
console.error("资源未找到")
|
||||
break
|
||||
case 500:
|
||||
console.error("服务器内部错误")
|
||||
break
|
||||
default:
|
||||
console.error("未知 API 错误")
|
||||
break
|
||||
}
|
||||
} else if (axiosError.request) {
|
||||
// 请求已发出,但未收到响应
|
||||
console.error("服务器无响应")
|
||||
} else {
|
||||
// 在设置请求时发生了一些问题
|
||||
console.error("请求错误:", axiosError.message)
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
// 通用错误处理
|
||||
export function handleGeneralError(error: Error) {
|
||||
console.error("发生错误:", error)
|
||||
return Promise.reject(error)
|
||||
}
|
8
src/utils/types/axios-error.ts
Normal file
8
src/utils/types/axios-error.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export interface AxiosError {
|
||||
response?: {
|
||||
status: number
|
||||
data: any
|
||||
}
|
||||
request?: any
|
||||
message: string
|
||||
}
|
@ -7,6 +7,7 @@ import { User, Lock, Key, Picture, Loading } from "@element-plus/icons-vue"
|
||||
import { getLoginCodeApi } from "@/api/login"
|
||||
import { type LoginRequestData } from "@/api/login/types/login"
|
||||
import ThemeSwitch from "@/components/ThemeSwitch/index.vue"
|
||||
import { handleApiError } from "@/utils/error-handler"
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@ -60,9 +61,11 @@ const createCode = () => {
|
||||
loginFormData.code = ""
|
||||
// 获取验证码
|
||||
codeUrl.value = ""
|
||||
getLoginCodeApi().then((res) => {
|
||||
codeUrl.value = res.data
|
||||
})
|
||||
getLoginCodeApi()
|
||||
.then((res) => {
|
||||
codeUrl.value = res.data
|
||||
})
|
||||
.catch(handleApiError)
|
||||
}
|
||||
|
||||
/** 初始化验证码 */
|
||||
|
@ -5,6 +5,7 @@ import { type GetTableData } from "@/api/table/types/table"
|
||||
import { type FormInstance, type FormRules, ElMessage, ElMessageBox } from "element-plus"
|
||||
import { Search, Refresh, CirclePlus, Delete, Download, RefreshRight } from "@element-plus/icons-vue"
|
||||
import { usePagination } from "@/hooks/usePagination"
|
||||
import { handleApiError } from "@/utils/error-handler"
|
||||
|
||||
defineOptions({
|
||||
// 命名当前组件
|
||||
@ -105,7 +106,8 @@ const getTableData = () => {
|
||||
paginationData.total = res.data.total
|
||||
tableData.value = res.data.list
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((error) => {
|
||||
handleApiError(error)
|
||||
tableData.value = []
|
||||
})
|
||||
.finally(() => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user