fix: 更新API请求方法和参数处理,优化路由组件解析逻辑

- 将`updateAnchor`方法的请求方式改为PUT,`deleteAnchor`方法改为DELETE并使用params传递数据
- 在路由组件中添加`normalizeRouteComponent`和`resolveRouteComponent`函数,增强动态路由解析能力
- 更新多个组件中的ID处理逻辑,确保ID始终为字符串类型
- 修改样式以统一选择框的宽度
This commit is contained in:
2026-04-21 15:55:28 +08:00
parent c4bdfe2bb3
commit 4271e7d2d9
11 changed files with 1189 additions and 115 deletions

View File

@@ -10,8 +10,17 @@ import { useRoutesList } from '/@/stores/routesList';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { getUserMenus } from '/@/api/system/menu/index';
// 扩展Window接口添加nextLoading属性
declare global {
interface Window {
nextLoading?: boolean;
}
}
const layouModules: any = import.meta.glob('../layout/routerView/*.{vue,tsx}');
const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}');
const parentView = layouModules['../layout/routerView/parent.vue'];
const notFoundView = viewsModules['../views/error/404.vue'];
// 后端控制路由
@@ -22,6 +31,57 @@ const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}');
*/
const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...layouModules }, { ...viewsModules });
const normalizeRouteComponent = (component?: string) => {
if (!component) return '';
return component
.trim()
.replace(/^\/@\//, '')
.replace(/^\//, '')
.replace(/^views\//, '')
.replace(/\.(vue|tsx)$/i, '')
.replace(/\/index$/i, '');
};
const resolveRouteComponent = (component?: string, path?: string, isParent = false) => {
const normalizedComponent = normalizeRouteComponent(component);
const normalizedPath = normalizeRouteComponent(path);
const candidates = [normalizedComponent, normalizedPath].filter(Boolean);
for (const candidate of candidates) {
const exactViewIndexVueKey = `../views/${candidate}/index.vue`;
if (dynamicViewsModules[exactViewIndexVueKey]) {
return dynamicViewsModules[exactViewIndexVueKey];
}
const exactViewVueKey = `../views/${candidate}.vue`;
if (dynamicViewsModules[exactViewVueKey]) {
return dynamicViewsModules[exactViewVueKey];
}
const exactViewTsxKey = `../views/${candidate}.tsx`;
if (dynamicViewsModules[exactViewTsxKey]) {
return dynamicViewsModules[exactViewTsxKey];
}
const exactLayoutVueKey = `../${candidate}.vue`;
if (dynamicViewsModules[exactLayoutVueKey]) {
return dynamicViewsModules[exactLayoutVueKey];
}
const exactLayoutTsxKey = `../${candidate}.tsx`;
if (dynamicViewsModules[exactLayoutTsxKey]) {
return dynamicViewsModules[exactLayoutTsxKey];
}
if (candidate === 'layout/routerView/parent') {
return parentView;
}
}
if (isParent) return parentView;
return notFoundView || parentView;
};
/**
* 后端控制路由:初始化方法,防止刷新时路由丢失
* @method NextLoading 界面 loading 动画开始执行
@@ -31,25 +91,18 @@ const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...lay
* @method setFilterMenuAndCacheTagsViewRoutes 设置路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
*/
export async function initBackEndControlRoutes() {
// 界面 loading 动画开始执行
if (window.nextLoading === undefined) NextLoading.start();
// 无 token 停止执行下一步
if (!Session.get('token')) return false;
// 触发初始化用户信息 pinia
// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
await useUserInfo().setUserInfos();
await useUserInfo().setPermissions();
// 获取路由菜单数据
await getBackEndControlRoutes();
let menuRoute = Session.get('userMenu');
// 存储接口原始路由未处理component根据需求选择使用
const menuRoute = Session.get('userMenu');
useRequestOldRoutes().setRequestOldRoutes(JSON.parse(JSON.stringify(menuRoute)));
// 处理路由component替换 dynamicRoutes/@/router/route第一个顶级 children 的路由
dynamicRoutes[0].children = [...defaultDynamicRouteChildren];
dynamicRoutes[0].children?.push(...(await backEndComponent(menuRoute)));
// 添加动态路由
await setAddRoute();
// 设置路由到 vuex routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
await setFilterMenuAndCacheTagsViewRoutes();
}
@@ -79,7 +132,7 @@ export function setCacheTagsViewRoutes() {
* @returns 返回替换后的路由数组
*/
export function setFilterRouteEnd() {
let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes));
const filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes));
filterRouteEnd[0].children = [...filterRouteEnd[0].children, ...notFoundAndNoPower];
return filterRouteEnd;
}
@@ -103,8 +156,8 @@ export async function setAddRoute() {
* @returns 返回后端路由菜单数据
*/
export async function getBackEndControlRoutes() {
let menuRoute = Session.get('userMenu');
let permissions = Session.get('permissions');
const menuRoute = Session.get('userMenu');
const permissions = Session.get('permissions');
if (!menuRoute || !permissions) {
await refreshBackEndControlRoutes();
}
@@ -116,7 +169,6 @@ export async function getBackEndControlRoutes() {
* @returns 返回后端路由菜单数据
*/
export async function refreshBackEndControlRoutes() {
// 获取路由
await getUserMenus().then((res: any) => {
Session.set('userMenu', res.data.menuList);
Session.set('permissions', res.data.permissions);
@@ -141,51 +193,21 @@ export function setBackEndControlRefreshRoutes() {
export function backEndComponent(routes: any) {
if (!routes) return [];
return routes.map((item: any) => {
// 递归处理子路由
if (item.children && item.children.length > 0) {
const hasChildren = Array.isArray(item.children) && item.children.length > 0;
if (hasChildren) {
item.children = backEndComponent(item.children);
// 找到第一个非隐藏的子路由作为重定向
const firstVisibleChild = item.children.find((ci: any) => !ci.meta?.isHide);
if (firstVisibleChild) {
item.redirect = firstVisibleChild.path;
}
// 确保父级路由有component
if (!item.component || item.component === false) {
item.component = dynamicViewsModules['../layout/routerView/parent.vue'];
}
const routeComponent = resolveRouteComponent(item.component as string, item.path as string, false);
item.component = routeComponent === notFoundView ? parentView : routeComponent;
} else {
// 确保叶子路由有component
let componentFound = false;
if (item.component && item.component !== false) {
const comp = dynamicImport(dynamicViewsModules, item.component as string);
if (comp) {
item.component = comp;
componentFound = true;
}
}
if (!componentFound) {
// 尝试根据path生成component路径
const path = item.path.replace(/^\//, '');
let comp = dynamicImport(dynamicViewsModules, path);
if (!comp) {
comp = dynamicImport(dynamicViewsModules, path + '/index');
}
if (!comp) {
// 尝试直接拼接views路径
const viewsPath = 'views/' + path + '/index.vue';
const directKey = Object.keys(dynamicViewsModules).find((key) => key.includes(viewsPath));
if (directKey) {
comp = dynamicViewsModules[directKey];
}
}
if (comp) {
item.component = comp;
} else {
// 如果还是找不到,使用一个默认组件
item.component = dynamicViewsModules['../layout/routerView/parent.vue'];
}
}
item.component = resolveRouteComponent(item.component as string, item.path as string, false);
}
return item;
});
}
@@ -197,43 +219,27 @@ export function backEndComponent(routes: any) {
* @returns 返回处理成函数后的 component
*/
export function dynamicImport(dynamicViewsModules: Record<string, Function>, component: string) {
const normalizedComponent = normalizeRouteComponent(component);
if (!normalizedComponent) return false;
const directViewIndexVueKey = `../views/${normalizedComponent}/index.vue`;
if (dynamicViewsModules[directViewIndexVueKey]) return dynamicViewsModules[directViewIndexVueKey];
const directViewVueKey = `../views/${normalizedComponent}.vue`;
if (dynamicViewsModules[directViewVueKey]) return dynamicViewsModules[directViewVueKey];
const directViewTsxKey = `../views/${normalizedComponent}.tsx`;
if (dynamicViewsModules[directViewTsxKey]) return dynamicViewsModules[directViewTsxKey];
const directLayoutVueKey = `../${normalizedComponent}.vue`;
if (dynamicViewsModules[directLayoutVueKey]) return dynamicViewsModules[directLayoutVueKey];
const directLayoutTsxKey = `../${normalizedComponent}.tsx`;
if (dynamicViewsModules[directLayoutTsxKey]) return dynamicViewsModules[directLayoutTsxKey];
const keys = Object.keys(dynamicViewsModules);
// 尝试多种匹配方式
let matchKeys = keys.filter((key) => {
const k = key.replace(/..\/views|../, '');
return k.startsWith(`${component}`) || k.startsWith(`/${component}`);
});
// 如果没有匹配到尝试带index的路径
if (matchKeys?.length === 0) {
matchKeys = keys.filter((key) => {
const k = key.replace(/..\/views|../, '');
return k.startsWith(`${component}/index`) || k.startsWith(`/${component}/index`);
});
}
// 尝试直接匹配完整路径
if (matchKeys?.length === 0) {
matchKeys = keys.filter((key) => {
return key.includes(component);
});
}
// 尝试更宽松的匹配方式
if (matchKeys?.length === 0) {
matchKeys = keys.filter((key) => {
return key.replace(/..\/views\//, '').includes(component.replace(/\//g, ''));
});
}
if (matchKeys?.length === 1) {
const matchKey = matchKeys[0];
return dynamicViewsModules[matchKey];
}
if (matchKeys?.length > 1) {
return false;
}
const fuzzyKey = keys.find((key) => key.includes(`/${normalizedComponent}/`) || key.includes(`/${normalizedComponent}.`));
if (fuzzyKey) return dynamicViewsModules[fuzzyKey];
return false;
}