修复component问题
This commit is contained in:
parent
bec473e62a
commit
da82e82e7c
@ -1,45 +1,46 @@
|
|||||||
import { createComponentLoader } from '@/utils/pathResolver';
|
import { createComponentLoader } from '@/utils/pathResolver';
|
||||||
|
import { h, resolveComponent as resolveVueComponent } from 'vue';
|
||||||
|
|
||||||
// 递归转换嵌套菜单为嵌套路由
|
// 递归转换嵌套菜单为嵌套路由
|
||||||
export function convertMenusToRoutes(menus) {
|
export function convertMenusToRoutes(menus) {
|
||||||
if (!menus || menus.length === 0) return [];
|
if (!menus || menus.length === 0) return [];
|
||||||
|
|
||||||
return menus.map(menu => {
|
return menus.map(menu => {
|
||||||
// 基础路由配置
|
|
||||||
const route = {
|
const route = {
|
||||||
path: menu.path || '', // 优先使用菜单path,无则为空字符串
|
path: menu.path || '',
|
||||||
name: `menu_${menu.id}`,
|
name: `menu_${menu.id}`,
|
||||||
meta: {
|
meta: {
|
||||||
icon: menu.icon,
|
|
||||||
title: menu.title,
|
title: menu.title,
|
||||||
|
icon: menu.icon,
|
||||||
id: menu.id,
|
id: menu.id,
|
||||||
parentId: menu.pid,
|
|
||||||
menuPath: menu.path,
|
|
||||||
componentPath: menu.component_path
|
componentPath: menu.component_path
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理组件路径
|
// 1. 处理组件加载
|
||||||
if (menu.type === 4) {
|
if (menu.type === 4) {
|
||||||
// 单页类型:使用单页组件,根据路由从单页表获取内容
|
// 单页类型
|
||||||
route.component = () => import('@/views/onepage/index.vue');
|
route.component = () => import('@/views/onepage/index.vue');
|
||||||
route.meta.menuType = 4; // 标记为单页类型
|
} else if (menu.component_path && menu.component_path.trim() !== '') {
|
||||||
} else if (menu.component_path) {
|
// 正常页面
|
||||||
// 页面类型:直接使用component_path加载组件
|
|
||||||
route.component = createComponentLoader(menu.component_path);
|
route.component = createComponentLoader(menu.component_path);
|
||||||
} else if (menu.children && menu.children.length > 0) {
|
} else if (menu.children && menu.children.length > 0) {
|
||||||
// 无组件但有子路由的父菜单,添加默认空组件(避免路由报错)
|
// 目录节点:必须给组件,否则父级无法渲染子级
|
||||||
route.component = () => import('@/views/layouts/EmptyLayout.vue');
|
route.component = () => import('@/views/layouts/EmptyLayout.vue');
|
||||||
// 为父菜单添加重定向,指向第一个有效子路由
|
} else {
|
||||||
const firstValidChild = findFirstValidRoute(menu.children);
|
// 异常:既没路径也没子菜单
|
||||||
if (firstValidChild && firstValidChild.path) {
|
route.component = () => import('@/views/404/404.vue');
|
||||||
route.redirect = firstValidChild.path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 递归处理子菜单为子路由
|
// 2. 递归子路由
|
||||||
if (menu.children && menu.children.length > 0) {
|
if (menu.children && menu.children.length > 0) {
|
||||||
route.children = convertMenusToRoutes(menu.children);
|
route.children = convertMenusToRoutes(menu.children);
|
||||||
|
|
||||||
|
// 目录节点添加重定向,防止点击父菜单页面空白
|
||||||
|
const firstChild = menu.children[0];
|
||||||
|
if (firstChild && firstChild.path) {
|
||||||
|
route.redirect = firstChild.path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return route;
|
return route;
|
||||||
|
|||||||
@ -11,25 +11,19 @@ const pathMap = new Map();
|
|||||||
|
|
||||||
// 初始化路径映射
|
// 初始化路径映射
|
||||||
Object.keys(viewsModules).forEach(relativePath => {
|
Object.keys(viewsModules).forEach(relativePath => {
|
||||||
// relativePath: ../views/dashboard/index.vue
|
// relativePath 示例: ../views/system/users.vue
|
||||||
|
|
||||||
// 1. 别名格式: @/views/dashboard/index.vue
|
// 统一去掉扩展名进行存储,方便各种格式匹配
|
||||||
const aliasPath = relativePath.replace('../views', '@/views');
|
const baseNoExt = relativePath.replace('../views/', '').replace('.vue', '');
|
||||||
pathMap.set(aliasPath, viewsModules[relativePath]);
|
|
||||||
|
|
||||||
// 2. 数据库格式: /dashboard/index.vue 或 /apps/cms/articles/index.vue
|
// 1. 存储标准路径
|
||||||
const dbPath = relativePath.replace('../views/', '/');
|
|
||||||
pathMap.set(dbPath, viewsModules[relativePath]);
|
|
||||||
|
|
||||||
// 3. 相对路径格式: dashboard/index.vue
|
|
||||||
const relativePathFormat = relativePath.replace('../views/', '');
|
|
||||||
pathMap.set(relativePathFormat, viewsModules[relativePath]);
|
|
||||||
|
|
||||||
// 4. 原始相对路径也保留
|
|
||||||
pathMap.set(relativePath, viewsModules[relativePath]);
|
pathMap.set(relativePath, viewsModules[relativePath]);
|
||||||
|
// 2. 存储 @/views 路径
|
||||||
// 5. 完整路径格式(用于调试)
|
pathMap.set(relativePath.replace('../views', '@/views'), viewsModules[relativePath]);
|
||||||
pathMap.set(`/views${dbPath}`, viewsModules[relativePath]);
|
// 3. 存储 /system/users 格式
|
||||||
|
pathMap.set(`/${baseNoExt}`, viewsModules[relativePath]);
|
||||||
|
// 4. 存储 system/users 格式
|
||||||
|
pathMap.set(baseNoExt, viewsModules[relativePath]);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,37 +35,26 @@ Object.keys(viewsModules).forEach(relativePath => {
|
|||||||
* @returns {Function|null} 返回模块加载器函数,找不到时返回 null
|
* @returns {Function|null} 返回模块加载器函数,找不到时返回 null
|
||||||
*/
|
*/
|
||||||
export function resolveComponent(path) {
|
export function resolveComponent(path) {
|
||||||
if (!path) {
|
if (!path) return null;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试多种路径格式
|
// 预处理 path:去掉可能的 .vue 后缀统一查找
|
||||||
const searchPaths = [
|
const cleanPath = path.replace('.vue', '');
|
||||||
path, // 原始路径
|
|
||||||
// 如果是以 / 开头的数据库格式,转换为别名格式
|
|
||||||
path.startsWith('/') && !path.startsWith('@/') ? `@/views${path}` : null,
|
|
||||||
// 如果是相对路径但不在 views 下,添加 @/views 前缀
|
|
||||||
!path.includes('@') && !path.includes('/views') && !path.startsWith('/')
|
|
||||||
? `@/views/${path}`
|
|
||||||
: null,
|
|
||||||
].filter(Boolean);
|
|
||||||
|
|
||||||
// 遍历所有可能的路径格式
|
// 尝试直接匹配
|
||||||
for (const searchPath of searchPaths) {
|
const loader = pathMap.get(path) || pathMap.get(cleanPath);
|
||||||
const loader = pathMap.get(searchPath);
|
if (loader) return loader;
|
||||||
if (loader) {
|
|
||||||
return loader;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果精确匹配失败,尝试模糊匹配(按文件名)
|
// 数据库格式补全匹配 (针对 /system/users)
|
||||||
const fileName = path.split('/').pop();
|
const dbFormat = cleanPath.startsWith('/') ? cleanPath : `/${cleanPath}`;
|
||||||
|
if (pathMap.get(dbFormat)) return pathMap.get(dbFormat);
|
||||||
|
|
||||||
|
// 模糊匹配:文件名匹配
|
||||||
|
const fileName = cleanPath.split('/').pop();
|
||||||
for (const [mappedPath, loader] of pathMap.entries()) {
|
for (const [mappedPath, loader] of pathMap.entries()) {
|
||||||
if (mappedPath.endsWith(fileName)) {
|
if (mappedPath.endsWith(`${fileName}.vue`) || mappedPath.endsWith(fileName)) {
|
||||||
return loader;
|
return loader;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,22 +65,16 @@ export function resolveComponent(path) {
|
|||||||
*/
|
*/
|
||||||
export function createComponentLoader(componentPath) {
|
export function createComponentLoader(componentPath) {
|
||||||
const loader = resolveComponent(componentPath);
|
const loader = resolveComponent(componentPath);
|
||||||
|
if (loader) return loader;
|
||||||
|
|
||||||
if (loader) {
|
console.error(`❌ [路由错误] 未找到组件: ${componentPath}`);
|
||||||
return loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 找不到组件时,返回一个占位组件
|
// 返回一个标准的 Vue 组件对象,确保 Router 不报错
|
||||||
console.warn(`⚠️ 组件未找到: ${componentPath}`);
|
|
||||||
return () => Promise.resolve({
|
return () => Promise.resolve({
|
||||||
default: {
|
name: 'ComponentNotFound',
|
||||||
template: `
|
render: () => {
|
||||||
<div style="padding: 40px; text-align: center; color: #999;">
|
import('element-plus').then(El => El.ElMessage.error(`路径错误: ${componentPath}`));
|
||||||
<h3>组件加载失败</h3>
|
return h('div', { style: 'padding:20px; color:red;' }, `组件路径不存在: ${componentPath}`);
|
||||||
<p>路径: ${componentPath}</p>
|
|
||||||
<p style="font-size: 12px; margin-top: 10px;">请检查组件文件是否存在</p>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ service.interceptors.request.use(
|
|||||||
config.headers['Authorization'] = `Bearer ${token}`;
|
config.headers['Authorization'] = `Bearer ${token}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 禁止 GET 请求缓存:添加时间戳参数
|
// 禁止 GET 请求缓存:添加时间戳参数到 query string
|
||||||
if (config.method === 'get') {
|
if (config.method === 'get') {
|
||||||
config.params = {
|
config.params = {
|
||||||
...config.params,
|
...config.params,
|
||||||
@ -23,10 +23,10 @@ service.interceptors.request.use(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST 请求也添加时间戳防止缓存
|
// POST/PUT/PATCH 请求也添加时间戳到 query string 防止缓存
|
||||||
if (config.method === 'post') {
|
if (['post', 'put', 'patch'].includes(config.method?.toLowerCase())) {
|
||||||
config.data = {
|
config.params = {
|
||||||
...config.data,
|
...config.params,
|
||||||
_t: Date.now()
|
_t: Date.now()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -146,7 +146,7 @@ const cateOptions = ref([]);
|
|||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
title: '',
|
title: '',
|
||||||
author: '美天科技',
|
author: '云泽网',
|
||||||
cate: '',
|
cate: '',
|
||||||
content: '',
|
content: '',
|
||||||
image: '',
|
image: '',
|
||||||
@ -394,7 +394,7 @@ function resetForm() {
|
|||||||
// 重置表单数据
|
// 重置表单数据
|
||||||
Object.assign(form, {
|
Object.assign(form, {
|
||||||
title: '',
|
title: '',
|
||||||
author: '美天科技',
|
author: '云泽网',
|
||||||
cate: '',
|
cate: '',
|
||||||
content: '',
|
content: '',
|
||||||
image: '',
|
image: '',
|
||||||
|
|||||||
@ -3,6 +3,5 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
// 简单的容器,用于嵌套路由展示
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
Loading…
Reference in New Issue
Block a user