增加新布局
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* 通用的别名路径解析工具
|
||||
* 用于在动态导入时解析 @ 别名路径
|
||||
*/
|
||||
|
||||
// 使用 import.meta.glob 预加载所有组件
|
||||
const viewsModules = import.meta.glob('../views/**/*.vue');
|
||||
|
||||
// 创建路径映射表
|
||||
const pathMap = new Map();
|
||||
|
||||
// 初始化路径映射
|
||||
Object.keys(viewsModules).forEach(relativePath => {
|
||||
// relativePath: ../views/dashboard/index.vue
|
||||
|
||||
// 1. 别名格式: @/views/dashboard/index.vue
|
||||
const aliasPath = relativePath.replace('../views', '@/views');
|
||||
pathMap.set(aliasPath, viewsModules[relativePath]);
|
||||
|
||||
// 2. 数据库格式: /dashboard/index.vue
|
||||
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]);
|
||||
});
|
||||
|
||||
/**
|
||||
* 解析别名路径为实际模块加载器
|
||||
* @param {string} path - 支持的路径格式:
|
||||
* - @/views/dashboard/index.vue (别名格式)
|
||||
* - /dashboard/index.vue (数据库格式,带前导斜杠)
|
||||
* - dashboard/index.vue (相对格式)
|
||||
* @returns {Function|null} 返回模块加载器函数,找不到时返回 null
|
||||
*/
|
||||
export function resolveComponent(path) {
|
||||
if (!path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 尝试多种路径格式
|
||||
const searchPaths = [
|
||||
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(searchPath);
|
||||
if (loader) {
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果精确匹配失败,尝试模糊匹配(按文件名)
|
||||
const fileName = path.split('/').pop();
|
||||
for (const [mappedPath, loader] of pathMap.entries()) {
|
||||
if (mappedPath.endsWith(fileName)) {
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建组件加载器
|
||||
* @param {string} componentPath - 组件路径
|
||||
* @returns {Function} Vue 路由组件加载函数
|
||||
*/
|
||||
export function createComponentLoader(componentPath) {
|
||||
const loader = resolveComponent(componentPath);
|
||||
|
||||
if (loader) {
|
||||
return loader;
|
||||
}
|
||||
|
||||
// 找不到组件时,返回一个占位组件
|
||||
console.warn(`⚠️ 组件未找到: ${componentPath}`);
|
||||
return () => Promise.resolve({
|
||||
default: {
|
||||
template: `
|
||||
<div style="padding: 40px; text-align: center; color: #999;">
|
||||
<h3>组件加载失败</h3>
|
||||
<p>路径: ${componentPath}</p>
|
||||
<p style="font-size: 12px; margin-top: 10px;">请检查组件文件是否存在</p>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有已加载的模块路径(用于调试)
|
||||
* @returns {Array<string>} 所有可用的路径列表
|
||||
*/
|
||||
export function getAllModulePaths() {
|
||||
return Array.from(pathMap.keys());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import axios from 'axios';
|
||||
|
||||
// 创建axios实例
|
||||
const service = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL,
|
||||
timeout: 10000, // 请求超时时间
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
service.interceptors.request.use(
|
||||
config => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
config.headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
error => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(
|
||||
response => {
|
||||
return response.data;
|
||||
},
|
||||
error => {
|
||||
if (error.response) {
|
||||
switch (error.response.status) {
|
||||
case 401:
|
||||
// 处理未授权的错误 - 清除token并跳转登录页
|
||||
console.error('未授权,请重新登录');
|
||||
localStorage.removeItem('token');
|
||||
// 避免循环导入,直接使用路由
|
||||
if (window.location.hash !== '#/login') {
|
||||
window.location.href = '#/login';
|
||||
}
|
||||
break;
|
||||
case 404:
|
||||
// 处理资源不存在的错误
|
||||
console.error('请求的资源不存在');
|
||||
break;
|
||||
default:
|
||||
console.error('请求失败,请稍后再试');
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.error('请求失败,请检查网络连接');
|
||||
} else {
|
||||
console.error('请求配置错误');
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default service;
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 主题管理工具
|
||||
*/
|
||||
|
||||
const THEME_KEY = 'app-theme';
|
||||
const THEME_LIGHT = 'light';
|
||||
const THEME_DARK = 'dark';
|
||||
|
||||
/**
|
||||
* 获取当前主题
|
||||
* @returns {string} 'light' | 'dark'
|
||||
*/
|
||||
export function getTheme() {
|
||||
// 优先从 localStorage 读取
|
||||
const savedTheme = localStorage.getItem(THEME_KEY);
|
||||
if (savedTheme === THEME_LIGHT || savedTheme === THEME_DARK) {
|
||||
return savedTheme;
|
||||
}
|
||||
|
||||
// 如果 localStorage 中没有,检查系统偏好
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
return THEME_DARK;
|
||||
}
|
||||
|
||||
// 默认返回亮色主题
|
||||
return THEME_LIGHT;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主题
|
||||
* @param {string} theme - 'light' | 'dark'
|
||||
*/
|
||||
export function setTheme(theme) {
|
||||
if (theme !== THEME_LIGHT && theme !== THEME_DARK) {
|
||||
console.warn(`Invalid theme: ${theme}, using default: ${THEME_LIGHT}`);
|
||||
theme = THEME_LIGHT;
|
||||
}
|
||||
|
||||
// 保存到 localStorage
|
||||
localStorage.setItem(THEME_KEY, theme);
|
||||
|
||||
// 应用到 HTML 元素
|
||||
const html = document.documentElement;
|
||||
if (theme === THEME_DARK) {
|
||||
html.setAttribute('data-theme', 'dark');
|
||||
} else {
|
||||
html.removeAttribute('data-theme');
|
||||
}
|
||||
|
||||
// 触发主题变更事件
|
||||
window.dispatchEvent(new CustomEvent('theme-change', { detail: { theme } }));
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换主题
|
||||
* @returns {string} 新的主题
|
||||
*/
|
||||
export function toggleTheme() {
|
||||
const currentTheme = getTheme();
|
||||
const newTheme = currentTheme === THEME_LIGHT ? THEME_DARK : THEME_LIGHT;
|
||||
setTheme(newTheme);
|
||||
return newTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化主题
|
||||
*/
|
||||
export function initTheme() {
|
||||
const theme = getTheme();
|
||||
setTheme(theme);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user