diff --git a/.env.development b/.env.development index 9c106c8..2a114bf 100644 --- a/.env.development +++ b/.env.development @@ -8,6 +8,8 @@ VITE_USE_MOCK=false VITE_USE_PROXY=true # base api -VITE_BASE_API='/store' +VITE_BASE_API='/api' +VITE_BASE_API_1='/api1' VITE_ADMIN_API='/admin' +VITE_ADMIN_API_1='/admin1' diff --git a/.env.production b/.env.production index 91b5a7e..3f1e58d 100644 --- a/.env.production +++ b/.env.production @@ -6,8 +6,10 @@ VITE_USE_MOCK=false # base api VITE_BASE_API='//www.wanzhuanyongcheng.cn/store' +VITE_BASE_API_1='//api.gxwzwh.com/store' VITE_ADMIN_API='//www.wanzhuanyongcheng.cn' +VITE_ADMIN_API_1='//api.gxwzwh.com' # 是否启用压缩 VITE_USE_COMPRESS=true diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index bf2de99..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# 默认忽略的文件 -/shelf/ -/workspace.xml -# 基于编辑器的 HTTP 客户端请求 -/httpRequests/ diff --git a/.idea/git_toolbox_blame.xml b/.idea/git_toolbox_blame.xml deleted file mode 100644 index 04ede99..0000000 --- a/.idea/git_toolbox_blame.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/jdt-mer.iml b/.idea/jdt-mer.iml deleted file mode 100644 index 0b872d8..0000000 --- a/.idea/jdt-mer.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 8aab5b9..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index c8397c9..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/build/constant.js b/build/constant.js index 2aa3a0d..c95065a 100644 --- a/build/constant.js +++ b/build/constant.js @@ -2,37 +2,36 @@ export const OUTPUT_DIR = 'dist' export const PROXY_CONFIG = { /** - * @desc 替换匹配值 - * @请求路径 http://localhost:3100/api/user - * @转发路径 http://localhost:8080/user + * @desc 主接口代理 + * @请求路径 http://localhost:3100/api/login + * @转发路径 http://localhost:3000/api/login */ - '/store': { + '/api': { target: 'https://test.wanzhuanyongcheng.cn', changeOrigin: true, - // rewrite: (path) => path.replace(new RegExp('^/api'), ''), + rewrite: (path) => path.replace(/^\/api/, '/store'), }, + /** + * @desc 备用接口代理 + * @请求路径 http://localhost:3100/api1/login + * @转发路径 http://localhost:3001/api/login + */ + '/api1': { + target: 'https://api.gxwzwh.com', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api1/, '/store'), + }, + /** + * @desc null + */ '/admin': { target: 'https://test.wanzhuanyongcheng.cn', changeOrigin: true, - // rewrite: (path) => path.replace(new RegExp('^/admin'), ''), + rewrite: (path) => path.replace(/^\/admin/, ''), + }, + '/admin1': { + target: 'https://api.gxwzwh.com', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/admin1/, ''), }, - /** - * @desc 不替换匹配值 - * @请求路径 http://localhost:3100/api/v2/user - * @转发路径 http://localhost:8080/api/v2/user - */ - // '/api/v2': { - // target: 'http://localhost:8080', - // changeOrigin: true, - // }, - /** - * @desc 替换部分匹配值 - * @请求路径 http://localhost:3100/api/v3/user - * @转发路径 http://localhost:8080/user - */ - // '/api/v3': { - // target: 'http://localhost:8080', - // changeOrigin: true, - // rewrite: (path) => path.replace(new RegExp('^/api'), ''), - // }, } diff --git a/src/main.js b/src/main.js index 2390124..82b6a92 100644 --- a/src/main.js +++ b/src/main.js @@ -9,10 +9,14 @@ import { setupRouter } from '@/router' import { setupStore } from '@/store' import App from './App.vue' import { setupNaiveDiscreteApi } from './utils' +import { initApiEndpoint } from '@/utils/api-config' async function setupApp() { const app = createApp(App) + // 初始化接口配置 + initApiEndpoint() + setupStore(app) setupNaiveDiscreteApi() diff --git a/src/router/guard/permission-guard.js b/src/router/guard/permission-guard.js index 165788e..26552c5 100644 --- a/src/router/guard/permission-guard.js +++ b/src/router/guard/permission-guard.js @@ -1,4 +1,5 @@ import { getToken, refreshAccessToken, isNullOrWhitespace } from '@/utils' +import { addDynamicRoutes } from '@/router' const WHITE_LIST = ['/login', '/404'] export function createPermissionGuard(router) { @@ -17,6 +18,20 @@ export function createPermissionGuard(router) { /** 有token的情况 */ if (to.path === '/login') return { path: '/' } + // 确保动态路由已加载 + if (token && !router.hasRoute('Dashboard')) { + try { + await addDynamicRoutes() + // 如果当前路径不存在,重定向到工作台 + if (to.path !== '/' && !router.hasRoute(to.name)) { + return { path: '/workbench' } + } + } catch (error) { + console.error('动态路由加载失败:', error) + return { path: '/login' } + } + } + refreshAccessToken() return true }) diff --git a/src/router/index.js b/src/router/index.js index d5439a6..6ee0308 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -42,13 +42,45 @@ export async function addDynamicRoutes() { const permissionStore = usePermissionStore() !userStore.userId && (await userStore.getUserInfo()) const accessRoutes = permissionStore.generateRoutes(userStore.role) + + // 确保路由按正确顺序添加 accessRoutes.forEach((route) => { - !router.hasRoute(route.name) && router.addRoute(route) + if (!router.hasRoute(route.name)) { + router.addRoute(route) + } }) - router.hasRoute(EMPTY_ROUTE.name) && router.removeRoute(EMPTY_ROUTE.name) + + // 移除空路由,添加404路由 + if (router.hasRoute(EMPTY_ROUTE.name)) { + router.removeRoute(EMPTY_ROUTE.name) + } router.addRoute(NOT_FOUND_ROUTE) + + // 确保根路径重定向到工作台 + if (!router.hasRoute('Dashboard')) { + const workbenchRoute = { + name: 'Dashboard', + path: '/', + component: () => import('@/layout/index.vue'), + redirect: '/workbench', + children: [ + { + name: 'Workbench', + path: 'workbench', + component: () => import('@/views/workbench/index.vue'), + meta: { + title: '工作台', + icon: 'mdi:index', + order: 0, + }, + }, + ], + } + router.addRoute(workbenchRoute) + } } catch (error) { console.error(error) + throw error } } diff --git a/src/store/modules/user/index.js b/src/store/modules/user/index.js index ee45919..aea7fc9 100644 --- a/src/store/modules/user/index.js +++ b/src/store/modules/user/index.js @@ -18,10 +18,7 @@ export const useUserStore = defineStore('user', { return this.userInfo?.name }, avatar() { - return ( - this.userInfo?.avatar || - 'https://pic3.58cdn.com.cn/nowater/webim/big/n_v21bc7874294754e63a22b80febac9cf51.jpg' - ) + return this.userInfo?.avatar || 'https://v2.xxapi.cn/api/head?return=302' }, role() { return this.userInfo?.role || [] diff --git a/src/utils/api-config.js b/src/utils/api-config.js new file mode 100644 index 0000000..0b6214b --- /dev/null +++ b/src/utils/api-config.js @@ -0,0 +1,65 @@ +// 判断是否为开发环境 +const isDev = import.meta.env.DEV + +// API接口线路配置管理 +export const API_ENDPOINTS = { + primary: { + name: '主要线路', + baseURL: import.meta.env.VITE_BASE_API, + base_admin_url: import.meta.env.VITE_ADMIN_API, + timeout: 10000, + }, + backup1: { + name: '备用线路', + baseURL: import.meta.env.VITE_BASE_API_1, + base_admin_url: import.meta.env.VITE_ADMIN_API_1, + timeout: 10000, + }, +} + +// 调试信息 +console.log('=== 多接口配置信息 ===') +console.log('当前环境:', isDev ? '开发环境' : '生产环境') +console.log('API配置:', API_ENDPOINTS) +console.log('环境变量 VITE_BASE_API:', import.meta.env.VITE_BASE_API) +console.log('环境变量 VITE_BASE_API_1:', import.meta.env.VITE_BASE_API_1) +console.log('环境变量 VITE_BASE_ADMIN:', import.meta.env.VITE_ADMIN_API) +console.log('环境变量 VITE_BASE_ADMIN_1:', import.meta.env.VITE_ADMIN_API_1) +console.log('是否使用代理:', import.meta.env.VITE_USE_PROXY) + +// 当前使用的接口线路 +let currentEndpoint = 'primary' + +// 获取当前接口配置 +export function getCurrentEndpoint() { + return { + key: currentEndpoint, + ...API_ENDPOINTS[currentEndpoint], + } +} + +// 设置当前接口 +export function setCurrentEndpoint(endpoint) { + if (API_ENDPOINTS[endpoint]) { + currentEndpoint = endpoint + localStorage.setItem('api_endpoint', endpoint) + return true + } + return false +} + +// 获取所有可用接口 +export function getAvailableEndpoints() { + return Object.entries(API_ENDPOINTS).map(([key, config]) => ({ + key, + ...config, + })) +} + +// 初始化时从本地存储恢复接口选择 +export function initApiEndpoint() { + const savedEndpoint = localStorage.getItem('api_endpoint') + if (savedEndpoint && API_ENDPOINTS[savedEndpoint]) { + currentEndpoint = savedEndpoint + } +} diff --git a/src/utils/http/index.js b/src/utils/http/index.js index 6fa57ef..f273c78 100644 --- a/src/utils/http/index.js +++ b/src/utils/http/index.js @@ -1,5 +1,6 @@ import axios from 'axios' import { resReject, resResolve, reqReject, reqResolve } from './interceptors' +import { getCurrentEndpoint, getAvailableEndpoints } from '../api-config' export function createAxios(options = {}) { const defaultOptions = { @@ -14,6 +15,114 @@ export function createAxios(options = {}) { return service } -export const request = createAxios({ - baseURL: import.meta.env.VITE_BASE_API, -}) +// 检测是否为admin类接口 +function isAdminEndpoint(url) { + console.log('url', url) + // 检查URL是否包含admin相关路径 + return url.includes('/admin') +} + +// 根据URL类型选择对应的baseURL +function getBaseURLByUrlType(endpoint, url) { + console.log('endpoint', endpoint) + console.log('url', url) + if (isAdminEndpoint(url)) { + return endpoint.base_admin_url || endpoint.baseURL + } + return endpoint.baseURL +} + +// 创建支持多接口的请求实例 +export function createMultiEndpointRequest() { + let instances = null + + // 延迟创建axios实例 + function ensureInstances() { + if (!instances) { + instances = {} + const endpoints = getAvailableEndpoints() + + console.log('创建axios实例,接口列表:', endpoints) + + endpoints.forEach((endpoint) => { + console.log(`创建实例 ${endpoint.key}:`, endpoint.baseURL) + instances[endpoint.key] = createAxios({ + baseURL: isAdminEndpoint(endpoint.baseURL) ? endpoint.base_admin_url : endpoint.baseURL, + timeout: endpoint.timeout, + }) + }) + } + return instances + } + + return { + // 使用当前选中的接口发送请求 + async request(config) { + const currentEndpoint = getCurrentEndpoint() + const url = config.url || '' + + // 根据URL类型选择baseURL + const baseURL = getBaseURLByUrlType(currentEndpoint, url) + + console.log('当前接口配置:', currentEndpoint) + console.log('请求URL:', url) + console.log('是否为admin接口:', isAdminEndpoint(url)) + console.log('选择的baseURL:', baseURL) + + // 创建临时axios实例 + const instance = createAxios({ + baseURL: baseURL, + timeout: currentEndpoint.timeout, + }) + + console.log('请求配置:', config) + + return await instance(config) + }, + + // 获取当前接口实例 + getCurrentInstance() { + const instances = ensureInstances() + const currentEndpoint = getCurrentEndpoint() + return instances[currentEndpoint.key] + }, + + // 获取所有接口实例 + getAllInstances() { + return ensureInstances() + }, + } +} + +// export const request = createAxios({ +// baseURL: import.meta.env.VITE_BASE_API, +// }) + +// 支持多接口的请求实例 +export const multiRequest = createMultiEndpointRequest() + +// 创建自动适配多接口的request实例 +const multiEndpointInstance = createMultiEndpointRequest() + +// 创建支持axios方法的request实例 +export const request = { + // 基础请求方法 + request: (config) => multiEndpointInstance.request(config), + + // 自动适配axios方法 + get: (url, config = {}) => multiEndpointInstance.request({ method: 'get', url, ...config }), + post: (url, data, config = {}) => + multiEndpointInstance.request({ method: 'post', url, data, ...config }), + put: (url, data, config = {}) => + multiEndpointInstance.request({ method: 'put', url, data, ...config }), + delete: (url, config = {}) => multiEndpointInstance.request({ method: 'delete', url, ...config }), + patch: (url, data, config = {}) => + multiEndpointInstance.request({ method: 'patch', url, data, ...config }), + head: (url, config = {}) => multiEndpointInstance.request({ method: 'head', url, ...config }), + options: (url, config = {}) => + multiEndpointInstance.request({ method: 'options', url, ...config }), + + // 获取当前接口实例(用于直接访问axios实例) + getCurrentInstance: () => multiEndpointInstance.getCurrentInstance(), + getAllInstances: () => multiEndpointInstance.getAllInstances(), +} diff --git a/src/utils/http/interceptors.js b/src/utils/http/interceptors.js index 44ad5e4..3fc651a 100644 --- a/src/utils/http/interceptors.js +++ b/src/utils/http/interceptors.js @@ -2,13 +2,11 @@ import { getToken } from '@/utils' import { resolveResError } from './helpers' export function reqResolve(config) { - if (config.url.includes('/admin')) { - config.url = config.url.replace(new RegExp('^/admin'), '') - console.log(config) - config.baseURL = import.meta.env.VITE_ADMIN_API - } else { - config.baseURL = import.meta.env.VITE_BASE_API - } + // if (config.url.includes('/admin')) { + // config.url = config.url.replace(new RegExp('^/admin'), '') + // console.log(config) + // config.baseURL = import.meta.env.VITE_ADMIN_API + // } // 处理不需要token的请求 if (config.noNeedToken) { return config diff --git a/src/views/login/api.js b/src/views/login/api.js index 940f30b..5054509 100644 --- a/src/views/login/api.js +++ b/src/views/login/api.js @@ -1,5 +1,13 @@ -import { request } from '@/utils' +import { request, multiRequest } from '@/utils' export default { login: (data) => request.post('/login', data, { noNeedToken: true }), + // 使用多接口的登录方法 + loginWithMultiEndpoint: (data) => + multiRequest.request({ + method: 'post', + url: '/login', + data, + noNeedToken: true, + }), } diff --git a/src/views/login/index.vue b/src/views/login/index.vue index 31e3c7f..838243a 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -14,7 +14,17 @@ {{ title }} -
+ +
+ +
+
-
+
{ console.log(query) $message.success('登录成功') setToken(query.tk) + setCurrentEndpoint(query.api) window.localStorage.setItem('type', query.type) await addDynamicRoutes() if (query.redirect) { @@ -113,6 +130,39 @@ function initLoginInfo() { const isRemember = useStorage('isRemember', false) const loading = ref(false) + +// 接口线路相关 +const selectedEndpoint = ref('primary') +const endpointOptions = ref([]) + +// 初始化接口配置 +initApiEndpoint() +const currentEndpoint = getCurrentEndpoint() +selectedEndpoint.value = currentEndpoint.key + +// 加载接口选项 +function loadEndpointOptions() { + const endpoints = getAvailableEndpoints() + endpointOptions.value = endpoints.map((endpoint) => ({ + label: endpoint.name, + value: endpoint.key, + })) +} + +// 处理接口切换 +function handleEndpointChange(value) { + if (setCurrentEndpoint(value)) { + selectedEndpoint.value = value + $message.success(`已切换到${endpointOptions.value.find((opt) => opt.value === value)?.label}`) + } else { + $message.error('接口切换失败') + } +} + +// 初始化 +onMounted(() => { + loadEndpointOptions() +}) async function handleLogin() { const { name, password } = loginInfo.value if (!name || !password) { @@ -122,7 +172,14 @@ async function handleLogin() { try { loading.value = true $message.loading('正在验证...') - const res = await api.login({ phone: name, password: password.toString() }) + + console.log('开始登录请求...') + console.log('登录数据:', { phone: name, password: password.toString() }) + console.log('当前选中接口:', selectedEndpoint.value) + + const res = await api.loginWithMultiEndpoint({ phone: name, password: password.toString() }) + + console.log('登录响应:', res) $message.success('登录成功') setToken(res.data.token) window.localStorage.setItem('type', res.data.type) @@ -140,7 +197,7 @@ async function handleLogin() { router.push('/') } } catch (error) { - console.error(error) + console.error('登录请求失败:', error) $message.removeMessage() } loading.value = false diff --git a/vite.config.js b/vite.config.js index dcf1473..1575c2f 100644 --- a/vite.config.js +++ b/vite.config.js @@ -20,6 +20,14 @@ export default defineConfig(({ command, mode }) => { VITE_SENTRY, } = viteEnv + // 调试代理配置 + console.log('=== Vite代理配置调试 ===') + console.log('VITE_USE_PROXY:', VITE_USE_PROXY) + console.log('VITE_BASE_API:', VITE_BASE_API) + console.log('VITE_ADMIN_API:', VITE_ADMIN_API) + console.log('PROXY_CONFIG:', PROXY_CONFIG) + console.log('所有环境变量:', viteEnv) + return { base: VITE_PUBLIC_PATH || '/', resolve: { @@ -34,12 +42,12 @@ export default defineConfig(({ command, mode }) => { https: false, port: VITE_PORT, open: false, - proxy: VITE_USE_PROXY - ? { - [VITE_BASE_API]: PROXY_CONFIG[VITE_BASE_API], - [VITE_ADMIN_API]: PROXY_CONFIG[VITE_ADMIN_API], - } - : undefined, + proxy: { + '/api1': PROXY_CONFIG['/api1'], + '/api': PROXY_CONFIG['/api'], + '/admin1': PROXY_CONFIG['/admin1'], + '/admin': PROXY_CONFIG['/admin'], + }, }, build: { target: 'es2015',