mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-09-09 20:57:17 +08:00
refactor: http util and route store (#43)
This commit is contained in:
13
src/router/asyncModules.ts
Normal file
13
src/router/asyncModules.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
type ImportVueFileType = typeof import('*.vue')
|
||||
type ImportVueFileFnType = () => Promise<ImportVueFileType>
|
||||
|
||||
const moduleFiles = import.meta.glob<ImportVueFileType>('@/views/**/*.vue')
|
||||
|
||||
export const asyncRouteModules = Object.entries(moduleFiles).reduce((routes, [url, importFn]) => {
|
||||
if (!/\/(views\/login|components)\//.test(url)) {
|
||||
const path = url.replace('/src/views/', '').replace('.vue', '')
|
||||
routes[path] = importFn
|
||||
}
|
||||
|
||||
return routes
|
||||
}, {} as Recordable<ImportVueFileFnType>)
|
@@ -1,51 +1,36 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import type { DictState, LabelValueState } from '@/types/global'
|
||||
import type { LabelValueState } from '@/types/global'
|
||||
|
||||
const storeSetup = () => {
|
||||
const dictData = ref<DictState[]>([])
|
||||
const dictData = ref(new Map<string, LabelValueState[]>())
|
||||
|
||||
// 设置字典
|
||||
const setDict = (_code: string, items: Array<LabelValueState>) => {
|
||||
if (_code !== null && _code !== '') {
|
||||
dictData.value.push({
|
||||
code: _code,
|
||||
items,
|
||||
})
|
||||
const setDict = (code: string, items: Array<LabelValueState>) => {
|
||||
if (code) {
|
||||
dictData.value.set(code, items)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取字典
|
||||
const getDict = (_code: string) => {
|
||||
if (_code == null || _code === '') {
|
||||
return null
|
||||
}
|
||||
for (let i = 0; i < dictData.value.length; i += 1) {
|
||||
if (dictData.value[i].code === _code) {
|
||||
return dictData.value[i].items
|
||||
}
|
||||
}
|
||||
return null
|
||||
const getDict = (code: string) => {
|
||||
if (!code) return null
|
||||
return dictData.value.get(code) || null
|
||||
}
|
||||
|
||||
// 删除字典
|
||||
const deleteDict = (_code: string) => {
|
||||
let bln = false
|
||||
const deleteDict = (code: string) => {
|
||||
try {
|
||||
for (let i = 0; i < dictData.value.length; i += 1) {
|
||||
if (dictData.value[i].code === _code) {
|
||||
dictData.value.splice(i, 1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return dictData.value.delete(code)
|
||||
} catch (e) {
|
||||
bln = false
|
||||
return false
|
||||
}
|
||||
return bln
|
||||
}
|
||||
|
||||
// 清空字典
|
||||
const cleanDict = () => {
|
||||
dictData.value = []
|
||||
dictData.value.clear()
|
||||
}
|
||||
|
||||
return {
|
||||
setDict,
|
||||
getDict,
|
||||
|
@@ -4,36 +4,18 @@ import type { RouteRecordRaw } from 'vue-router'
|
||||
import { mapTree, toTreeArray } from 'xe-utils'
|
||||
import { cloneDeep, omit } from 'lodash-es'
|
||||
import { constantRoutes, systemRoutes } from '@/router/route'
|
||||
import ParentView from '@/components/ParentView/index.vue'
|
||||
import { type RouteItem, getUserRoute } from '@/apis'
|
||||
import { transformPathToName } from '@/utils'
|
||||
import { asyncRouteModules } from '@/router/asyncModules'
|
||||
|
||||
const Layout = () => import('@/layout/index.vue')
|
||||
|
||||
// 匹配views里面所有的.vue文件
|
||||
const modules = import.meta.glob('@/views/**/*.vue')
|
||||
|
||||
/** 加载模块 */
|
||||
export const loadView = (view: string) => {
|
||||
let res
|
||||
for (const path in modules) {
|
||||
const dir = path.split('views/')[1].split('.vue')[0]
|
||||
if (dir === view) {
|
||||
res = () => modules[path]()
|
||||
}
|
||||
}
|
||||
return res
|
||||
const layoutComponentMap = {
|
||||
Layout: () => import('@/layout/index.vue'),
|
||||
ParentView: () => import('@/components/ParentView/index.vue'),
|
||||
}
|
||||
|
||||
/** 将component由字符串转成真正的模块 */
|
||||
const transformComponentView = (component: string) => {
|
||||
if (component === 'Layout') {
|
||||
return Layout as never
|
||||
} else if (component === 'ParentView') {
|
||||
return ParentView as never
|
||||
} else {
|
||||
return loadView(component) as never
|
||||
}
|
||||
return layoutComponentMap[component as keyof typeof layoutComponentMap] || asyncRouteModules[component]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,16 +26,20 @@ const transformComponentView = (component: string) => {
|
||||
*/
|
||||
const formatAsyncRoutes = (menus: RouteItem[]) => {
|
||||
if (!menus.length) return []
|
||||
|
||||
const pathMap = new Map()
|
||||
const routes = mapTree(menus, (item) => {
|
||||
return mapTree(menus, (item) => {
|
||||
pathMap.set(item.id, item.path)
|
||||
if (item.children && item.children.length) {
|
||||
item.children.sort((a, b) => (a?.sort ?? 0) - (b?.sort ?? 0)) // 排序
|
||||
|
||||
if (item.children?.length) {
|
||||
item.children.sort((a, b) => (a?.sort ?? 0) - (b?.sort ?? 0))
|
||||
}
|
||||
|
||||
// 部分子菜单,例如:通知公告新增、查看详情,需要选中其父菜单
|
||||
if (item.parentId && item.type === 2 && item.permission) {
|
||||
item.activeMenu = pathMap.get(item.parentId)
|
||||
}
|
||||
|
||||
return {
|
||||
path: item.path,
|
||||
name: item.name ?? transformPathToName(item.path),
|
||||
@@ -68,30 +54,24 @@ const formatAsyncRoutes = (menus: RouteItem[]) => {
|
||||
activeMenu: item.activeMenu,
|
||||
},
|
||||
}
|
||||
})
|
||||
return routes as RouteRecordRaw[]
|
||||
}) as unknown as RouteRecordRaw[]
|
||||
}
|
||||
|
||||
/** 判断路由层级是否大于 2 */
|
||||
export const isMultipleRoute = (route: RouteRecordRaw) => {
|
||||
const children = route.children
|
||||
if (children?.length) {
|
||||
// 只要有一个子路由的 children 长度大于 0,就说明是三级及其以上路由
|
||||
return children.some((child) => child.children?.length)
|
||||
}
|
||||
return false
|
||||
return route.children?.some((child) => child.children?.length) ?? false
|
||||
}
|
||||
|
||||
/** 路由降级(把三级及其以上的路由转化为二级路由) */
|
||||
export const flatMultiLevelRoutes = (routes: RouteRecordRaw[]) => {
|
||||
const cloneRoutes = cloneDeep(routes)
|
||||
cloneRoutes.forEach((route) => {
|
||||
if (isMultipleRoute(route)) {
|
||||
const flatRoutes = toTreeArray(route.children)
|
||||
route.children = flatRoutes.map((i) => omit(i, 'children')) as RouteRecordRaw[]
|
||||
return cloneDeep(routes).map((route) => {
|
||||
if (!isMultipleRoute(route)) return route
|
||||
|
||||
return {
|
||||
...route,
|
||||
children: toTreeArray(route.children).map((item) => omit(item, 'children')) as RouteRecordRaw[],
|
||||
}
|
||||
})
|
||||
return cloneRoutes
|
||||
}
|
||||
|
||||
const storeSetup = () => {
|
||||
@@ -109,17 +89,12 @@ const storeSetup = () => {
|
||||
}
|
||||
|
||||
// 生成路由
|
||||
const generateRoutes = (): Promise<RouteRecordRaw[]> => {
|
||||
return new Promise((resolve) => {
|
||||
// 向后端请求路由数据 这个接口已经根据用户角色过滤了没权限的路由(后端根据用户角色过滤路由显得比较安全些)
|
||||
getUserRoute().then((res) => {
|
||||
const asyncRoutes = formatAsyncRoutes(res.data)
|
||||
setRoutes(asyncRoutes)
|
||||
const cloneRoutes = cloneDeep(asyncRoutes)
|
||||
const flatRoutes = flatMultiLevelRoutes(cloneRoutes as RouteRecordRaw[])
|
||||
resolve(flatRoutes)
|
||||
})
|
||||
})
|
||||
const generateRoutes = async (): Promise<RouteRecordRaw[]> => {
|
||||
const { data } = await getUserRoute()
|
||||
const asyncRoutes = formatAsyncRoutes(data)
|
||||
const flatRoutes = flatMultiLevelRoutes(cloneDeep(asyncRoutes))
|
||||
setRoutes(asyncRoutes)
|
||||
return flatRoutes
|
||||
}
|
||||
|
||||
return {
|
||||
|
6
src/types/global.d.ts
vendored
6
src/types/global.d.ts
vendored
@@ -14,10 +14,8 @@ export interface LabelValueState {
|
||||
extra?: string
|
||||
}
|
||||
|
||||
/** 字典类型 */
|
||||
export interface DictState {
|
||||
code: string
|
||||
items: Array<LabelValueState>
|
||||
declare global{
|
||||
type Recordable<T = any> = Record<string, T>
|
||||
}
|
||||
|
||||
/** 状态(1:启用;2:禁用) */
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios'
|
||||
import qs from 'query-string'
|
||||
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||
import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
||||
import { useUserStore } from '@/stores'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import modalErrorWrapper from '@/utils/modal-error-wrapper'
|
||||
@@ -34,6 +34,16 @@ const http: AxiosInstance = axios.create({
|
||||
timeout: 30 * 1000,
|
||||
})
|
||||
|
||||
const handleError = (msg: string) => {
|
||||
if (msg.length >= 15) {
|
||||
return notificationErrorWrapper(msg || '服务器端错误')
|
||||
}
|
||||
return messageErrorWrapper({
|
||||
content: msg || '服务器端错误',
|
||||
duration: 5 * 1000,
|
||||
})
|
||||
}
|
||||
|
||||
// 请求拦截器
|
||||
http.interceptors.request.use(
|
||||
(config: AxiosRequestConfig) => {
|
||||
@@ -46,9 +56,7 @@ http.interceptors.request.use(
|
||||
}
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error)
|
||||
},
|
||||
(error) => Promise.reject(error),
|
||||
)
|
||||
|
||||
// 响应拦截器
|
||||
@@ -56,11 +64,8 @@ http.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
const { data } = response
|
||||
const { success, code, msg } = data
|
||||
if (response.request.responseType === 'blob') {
|
||||
return response
|
||||
}
|
||||
// 成功
|
||||
if (success) {
|
||||
|
||||
if (response.request.responseType === 'blob' || success) {
|
||||
return response
|
||||
}
|
||||
|
||||
@@ -79,104 +84,68 @@ http.interceptors.response.use(
|
||||
},
|
||||
})
|
||||
} else {
|
||||
// 如果错误信息长度过长,使用 Notification 进行提示
|
||||
if (msg.length <= 15) {
|
||||
messageErrorWrapper({
|
||||
content: msg || '服务器端错误',
|
||||
duration: 5 * 1000,
|
||||
})
|
||||
} else {
|
||||
notificationErrorWrapper(msg || '服务器端错误')
|
||||
}
|
||||
handleError(msg)
|
||||
}
|
||||
return Promise.reject(new Error(msg || '服务器端错误'))
|
||||
},
|
||||
(error) => {
|
||||
const response = Object.assign({}, error.response)
|
||||
response
|
||||
&& messageErrorWrapper({
|
||||
content: StatusCodeMessage[response.status] || '服务器暂时未响应,请刷新页面并重试。若无法解决,请联系管理员',
|
||||
duration: 5 * 1000,
|
||||
})
|
||||
(error: AxiosError) => {
|
||||
if (!error.response) {
|
||||
handleError('网络连接失败,请检查您的网络')
|
||||
return Promise.reject(error)
|
||||
}
|
||||
const status = error.response?.status
|
||||
const errorMsg = StatusCodeMessage[status] || '服务器暂时未响应,请刷新页面并重试。若无法解决,请联系管理员'
|
||||
handleError(errorMsg)
|
||||
return Promise.reject(error)
|
||||
},
|
||||
)
|
||||
|
||||
const request = <T = unknown>(config: AxiosRequestConfig): Promise<ApiRes<T>> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
http
|
||||
.request<T>(config)
|
||||
.then((res: AxiosResponse) => resolve(res.data))
|
||||
.catch((err: { msg: string }) => reject(err))
|
||||
})
|
||||
const request = async <T = unknown>(config: AxiosRequestConfig): Promise<ApiRes<T>> => {
|
||||
return http.request<T>(config)
|
||||
.then((res: AxiosResponse) => res.data)
|
||||
.catch((err: { msg: string }) => Promise.reject(err))
|
||||
}
|
||||
|
||||
const requestNative = <T = unknown>(config: AxiosRequestConfig): Promise<AxiosResponse> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
http
|
||||
.request<T>(config)
|
||||
.then((res: AxiosResponse) => resolve(res))
|
||||
.catch((err: { msg: string }) => reject(err))
|
||||
})
|
||||
const requestNative = async <T = unknown>(config: AxiosRequestConfig): Promise<AxiosResponse> => {
|
||||
return http.request<T>(config)
|
||||
.then((res: AxiosResponse) => res)
|
||||
.catch((err: { msg: string }) => Promise.reject(err))
|
||||
}
|
||||
|
||||
const get = <T = any>(url: string, params?: object, config?: AxiosRequestConfig): Promise<ApiRes<T>> => {
|
||||
return request({
|
||||
method: 'get',
|
||||
url,
|
||||
params,
|
||||
paramsSerializer: (obj) => {
|
||||
return qs.stringify(obj)
|
||||
},
|
||||
...config,
|
||||
})
|
||||
const createRequest = (method: string) => {
|
||||
return <T = any>(url: string, params?: object, config?: AxiosRequestConfig): Promise<ApiRes<T>> => {
|
||||
return request({
|
||||
method,
|
||||
url,
|
||||
[method === 'get' ? 'params' : 'data']: params,
|
||||
...(method === 'get'
|
||||
? {
|
||||
paramsSerializer: (obj) => qs.stringify(obj),
|
||||
}
|
||||
: {}),
|
||||
...config,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const post = <T = any>(url: string, params?: object, config?: AxiosRequestConfig): Promise<ApiRes<T>> => {
|
||||
return request({
|
||||
method: 'post',
|
||||
url,
|
||||
data: params,
|
||||
...config,
|
||||
})
|
||||
}
|
||||
|
||||
const put = <T = any>(url: string, params?: object, config?: AxiosRequestConfig): Promise<ApiRes<T>> => {
|
||||
return request({
|
||||
method: 'put',
|
||||
url,
|
||||
data: params,
|
||||
...config,
|
||||
})
|
||||
}
|
||||
|
||||
const patch = <T = any>(url: string, params?: object, config?: AxiosRequestConfig): Promise<ApiRes<T>> => {
|
||||
return request({
|
||||
method: 'patch',
|
||||
url,
|
||||
data: params,
|
||||
...config,
|
||||
})
|
||||
}
|
||||
|
||||
const del = <T = any>(url: string, params?: object, config?: AxiosRequestConfig): Promise<ApiRes<T>> => {
|
||||
return request({
|
||||
method: 'delete',
|
||||
url,
|
||||
data: params,
|
||||
...config,
|
||||
})
|
||||
}
|
||||
const download = (url: string, params?: object, config?: AxiosRequestConfig): Promise<AxiosResponse> => {
|
||||
return requestNative({
|
||||
method: 'get',
|
||||
url,
|
||||
responseType: 'blob',
|
||||
params,
|
||||
paramsSerializer: (obj) => {
|
||||
return qs.stringify(obj)
|
||||
},
|
||||
paramsSerializer: (obj) => qs.stringify(obj),
|
||||
...config,
|
||||
})
|
||||
}
|
||||
export default { get, post, put, patch, del, request, requestNative, download }
|
||||
|
||||
export default {
|
||||
get: createRequest('get'),
|
||||
post: createRequest('post'),
|
||||
put: createRequest('put'),
|
||||
patch: createRequest('patch'),
|
||||
del: createRequest('delete'),
|
||||
request,
|
||||
requestNative,
|
||||
download,
|
||||
}
|
||||
|
Reference in New Issue
Block a user