From d5751d305c7ae5f21923494c64396215c9801fd2 Mon Sep 17 00:00:00 2001 From: YuanHuakk <1751826683@qq.com> Date: Tue, 23 Sep 2025 18:24:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(custom):=20=E5=A4=9AAPI=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 7 +- .env.production | 1 + build/constant.js | 35 ++++------ src/main.js | 4 ++ src/router/guard/permission-guard.js | 16 ++++- src/router/index.js | 35 +++++++++- src/store/modules/user/index.js | 5 +- src/utils/api-config.js | 61 +++++++++++++++++ src/utils/http/index.js | 94 +++++++++++++++++++++++++- src/views/business/mer_list/index.vue | 2 +- src/views/login/api.js | 10 ++- src/views/login/index.vue | 96 +++++++++++++++++++++++++-- vite.config.js | 17 +++-- 13 files changed, 336 insertions(+), 47 deletions(-) create mode 100644 src/utils/api-config.js diff --git a/.env.development b/.env.development index 634baf7..b17cc05 100644 --- a/.env.development +++ b/.env.development @@ -8,7 +8,12 @@ VITE_USE_MOCK=false VITE_USE_PROXY=true # base api -VITE_BASE_API='https://test.wanzhuanyongcheng.cn/admin' +VITE_BASE_API='/api' + +VITE_BASE_API_1='/api1' +# VITE_BASE_API='https://test.wanzhuanyongcheng.cn/admin' + +# VITE_BASE_API_1='https://api.gxwzwh.com/admin' VITE_WS1_URL='game.wanzhuanyongcheng.cn/dice/home' diff --git a/.env.production b/.env.production index 22d5d42..0d00410 100644 --- a/.env.production +++ b/.env.production @@ -6,6 +6,7 @@ VITE_USE_MOCK=false # base api VITE_BASE_API='//www.wanzhuanyongcheng.cn/admin' +VITE_BASE_API_1='//api.gxwzwh.com/admin' # 是否启用压缩 VITE_USE_COMPRESS=true diff --git a/build/constant.js b/build/constant.js index 1cd507d..c21db6f 100644 --- a/build/constant.js +++ b/build/constant.js @@ -2,32 +2,23 @@ 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 */ - '/admin': { + '/api': { target: 'https://test.wanzhuanyongcheng.cn', changeOrigin: true, - // rewrite: (path) => path.replace(new RegExp('^/api'), ''), + rewrite: (path) => path.replace(/^\/api/, '/admin'), }, /** - * @desc 不替换匹配值 - * @请求路径 http://localhost:3100/api/v2/user - * @转发路径 http://localhost:8080/api/v2/user + * @desc 备用接口代理 + * @请求路径 http://localhost:3100/api1/login + * @转发路径 http://localhost:3001/api/login */ - // '/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'), ''), - // }, + '/api1': { + target: 'https://api.gxwzwh.com', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api1/, '/admin'), + }, } diff --git a/src/main.js b/src/main.js index 44310ae..5eeafc8 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 29b296b..090229b 100644 --- a/src/router/guard/permission-guard.js +++ b/src/router/guard/permission-guard.js @@ -1,4 +1,5 @@ import { getToken, isNullOrWhitespace } from '@/utils' +import { addDynamicRoutes } from '@/router' const WHITE_LIST = ['/login', '/404'] export function createPermissionGuard(router) { @@ -14,7 +15,20 @@ export function createPermissionGuard(router) { /** 有token的情况 */ if (to.path === '/login') return { path: '/' } - // refreshAccessToken() + // 确保动态路由已加载 + 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' } + } + } + return true }) } diff --git a/src/router/index.js b/src/router/index.js index ef1fdaa..88da3ea 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -30,11 +30,42 @@ export async function addDynamicRoutes() { try { const permissionStore = usePermissionStore() const accessRoutes = permissionStore.generateRoutes() + + // 确保路由按正确顺序添加 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 530d21a..4291986 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..ecf8bf2 --- /dev/null +++ b/src/utils/api-config.js @@ -0,0 +1,61 @@ +// 判断是否为开发环境 +const isDev = import.meta.env.DEV + +// API接口线路配置管理 +export const API_ENDPOINTS = { + primary: { + name: '主要线路', + baseURL: import.meta.env.VITE_BASE_API, + timeout: 10000, + }, + backup1: { + name: '备用线路', + baseURL: import.meta.env.VITE_BASE_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('是否使用代理:', 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..284016f 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,93 @@ export function createAxios(options = {}) { return service } -export const request = createAxios({ - baseURL: import.meta.env.VITE_BASE_API, -}) +// 创建支持多接口的请求实例 +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: endpoint.baseURL, + timeout: endpoint.timeout, + }) + }) + } + return instances + } + + return { + // 使用当前选中的接口发送请求 + async request(config) { + const instances = ensureInstances() + const currentEndpoint = getCurrentEndpoint() + + console.log('当前接口配置:', currentEndpoint) + console.log('可用实例:', Object.keys(instances)) + + const instance = instances[currentEndpoint.key] + + if (!instance) { + throw new Error(`接口实例不存在: ${currentEndpoint.key}`) + } + + console.log(`使用接口: ${currentEndpoint.name} (${currentEndpoint.baseURL})`) + 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/views/business/mer_list/index.vue b/src/views/business/mer_list/index.vue index 6dc8f68..35c808b 100644 --- a/src/views/business/mer_list/index.vue +++ b/src/views/business/mer_list/index.vue @@ -369,7 +369,7 @@ const columns = ref([ window.open( `${import.meta.env.VITE_MER_LOGIN_URL}?redirect=/workbench&type=${ res.data.type - }&tk=${res.data.token}` + }&tk=${res.data.token}&api=${localStorage.getItem('api_endpoint')}` ) }, }, 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 2d2387b..11ed670 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -14,7 +14,17 @@ {{ title }} -
+ +
+ +
+
-
+
({ + 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() +}) + +// 获取第一个可用的路由页面 +function getFirstAvailableRoute(menuData) { + if (!menuData || !Array.isArray(menuData)) return null + + // 递归查找第一个type为2的路由(页面路由) + function findFirstPageRoute(routes) { + for (const route of routes) { + if (route.type === 2 && route.route) { + return route.route + } + if (route.subMenu && route.subMenu.length) { + const found = findFirstPageRoute(route.subMenu) + if (found) return found + } + } + return null + } + + return findFirstPageRoute(menuData) +} + async function handleLogin() { const { name, password } = loginInfo.value if (!name || !password) { @@ -99,7 +170,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('登录成功') window.localStorage.setItem('menu', JSON.stringify(res.data.auth)) setToken(res.data.token) @@ -108,17 +186,23 @@ async function handleLogin() { } else { lStorage.remove('loginInfo') } + + // 先添加动态路由 await addDynamicRoutes() - // console.log(query) + + // 获取第一个可用的路由页面 + const firstRoute = getFirstAvailableRoute(res.data.auth) + if (query.redirect) { const path = query.redirect Reflect.deleteProperty(query, 'redirect') router.push({ path, query }) } else { - router.push('/workbench') + // 跳转到第一个可用路由,如果没有则跳转到工作台 + router.push(firstRoute || '/workbench') } - // router.push('/workbench') } catch (error) { + console.error('登录请求失败:', error) $message.removeMessage() } loading.value = false diff --git a/vite.config.js b/vite.config.js index 923e3db..14f0885 100644 --- a/vite.config.js +++ b/vite.config.js @@ -13,6 +13,13 @@ export default defineConfig(({ command, mode }) => { const viteEnv = convertEnv(env) const { VITE_PORT, VITE_PUBLIC_PATH, VITE_USE_PROXY, VITE_BASE_API, VITE_SENTRY } = viteEnv + // 调试代理配置 + console.log('=== Vite代理配置调试 ===') + console.log('VITE_USE_PROXY:', VITE_USE_PROXY) + console.log('VITE_BASE_API:', VITE_BASE_API) + console.log('PROXY_CONFIG:', PROXY_CONFIG) + console.log('所有环境变量:', viteEnv) + return { base: VITE_PUBLIC_PATH || '/', resolve: { @@ -26,12 +33,10 @@ export default defineConfig(({ command, mode }) => { host: '0.0.0.0', port: VITE_PORT, open: false, - proxy: VITE_USE_PROXY - ? { - [VITE_BASE_API]: PROXY_CONFIG[VITE_BASE_API], - '/api/v2': PROXY_CONFIG['/api/v2'], - } - : undefined, + proxy: { + '/api1': PROXY_CONFIG['/api1'], + '/api': PROXY_CONFIG['/api'], + }, }, build: { target: 'es2015',