Remove app dev mode frontend routes

This commit is contained in:
2026-06-09 00:29:12 +08:00
parent a89b24f97a
commit 0303ba977e
6 changed files with 24 additions and 126 deletions
-5
View File
@@ -110,19 +110,14 @@ export interface MenuStats {
/** /**
* 获取用户所有菜单(路由树) * 获取用户所有菜单(路由树)
* @param applicationCode 应用编码,用于过滤应用专属菜单 * @param applicationCode 应用编码,用于过滤应用专属菜单
* @param devMode 开发模式:true只返回系统菜单,false只返回应用菜单
*/ */
export async function getAllMenusApi( export async function getAllMenusApi(
applicationCode?: string, applicationCode?: string,
devMode?: boolean,
) { ) {
const params: Record<string, any> = {}; const params: Record<string, any> = {};
if (applicationCode) { if (applicationCode) {
params.application_code = applicationCode; params.application_code = applicationCode;
} }
if (devMode !== undefined) {
params.devMode = devMode;
}
return requestClient.get<RouteRecordStringComponent[]>( return requestClient.get<RouteRecordStringComponent[]>(
'/api/core/menu/route/tree', '/api/core/menu/route/tree',
{ params }, { params },
+6 -19
View File
@@ -7,24 +7,16 @@ import { overridesPreferences } from './preferences';
* 从 URL 路径中解析 appCode 和模式 * 从 URL 路径中解析 appCode 和模式
* URL 格式: * URL 格式:
* - /app/:appCode/... (正常模式) * - /app/:appCode/... (正常模式)
* - /app-dev/:appCode/... (开发模式)
*/ */
function getAppInfoFromUrl(): { appCode: null | string; isDevMode: boolean } { function getAppInfoFromUrl(): { appCode: null | string } {
const path = window.location.pathname; const path = window.location.pathname;
// 先尝试匹配开发模式 /app-dev/{code}/
const devMatch = path.match(/^\/app-dev\/([^/]+)/);
if (devMatch?.[1]) {
return { appCode: devMatch[1], isDevMode: true };
}
// 再尝试匹配正常模式 /app/{code}/
const appMatch = path.match(/^\/app\/([^/]+)/); const appMatch = path.match(/^\/app\/([^/]+)/);
if (appMatch?.[1]) { if (appMatch?.[1]) {
return { appCode: appMatch[1], isDevMode: false }; return { appCode: appMatch[1] };
} }
return { appCode: null, isDevMode: false }; return { appCode: null };
} }
/** /**
@@ -106,17 +98,15 @@ async function initApplication() {
const baseNamespace = `${import.meta.env.VITE_APP_NAMESPACE}-${appVersion}-${env}`; const baseNamespace = `${import.meta.env.VITE_APP_NAMESPACE}-${appVersion}-${env}`;
// 1. 检查是否为子应用模式,获取 appCode 和 applicationId // 1. 检查是否为子应用模式,获取 appCode 和 applicationId
const { appCode, isDevMode } = getAppInfoFromUrl(); const { appCode } = getAppInfoFromUrl();
let applicationId: string | undefined; let applicationId: string | undefined;
if (appCode) { if (appCode) {
const appInfo = await getApplicationByCode(appCode); const appInfo = await getApplicationByCode(appCode);
applicationId = appInfo?.id; applicationId = appInfo?.id;
const modeLabel = isDevMode ? '开发模式' : '正常模式'; console.log('[应用初始化] 子应用模式', {
console.log(`[应用初始化] 子应用${modeLabel}`, {
appCode, appCode,
applicationId, applicationId,
isDevMode,
}); });
} else { } else {
console.log('[应用初始化] 主应用模式'); console.log('[应用初始化] 主应用模式');
@@ -125,12 +115,9 @@ async function initApplication() {
// 2. 根据应用模式生成不同的 namespace,确保主应用和子应用的 localStorage 隔离 // 2. 根据应用模式生成不同的 namespace,确保主应用和子应用的 localStorage 隔离
// 主应用: vben-admin-1.0.0-dev // 主应用: vben-admin-1.0.0-dev
// 子应用正常模式: vben-admin-1.0.0-dev-crm // 子应用正常模式: vben-admin-1.0.0-dev-crm
// 子应用开发模式: vben-admin-1.0.0-dev-crm-dev
let namespace = baseNamespace; let namespace = baseNamespace;
if (appCode) { if (appCode) {
namespace = isDevMode namespace = `${baseNamespace}-${appCode}`;
? `${baseNamespace}-${appCode}-dev`
: `${baseNamespace}-${appCode}`;
} }
// 3. 从后端加载UI配置(传递 applicationId 获取对应应用的配置) // 3. 从后端加载UI配置(传递 applicationId 获取对应应用的配置)
+2 -7
View File
@@ -58,14 +58,9 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
// 获取应用上下文 // 获取应用上下文
const appContextStore = useAppContextStore(); const appContextStore = useAppContextStore();
// 如果是子应用模式,传递 appCode 和 devMode 参数 // 如果是子应用模式,传递 appCode 过滤应用专属菜单
// - 开发模式 (isDevMode=true): 只返回系统菜单
// - 正常模式 (isDevMode=false): 只返回应用专属菜单
if (appContextStore.appCode) { if (appContextStore.appCode) {
return await getAllMenusApi( return await getAllMenusApi(appContextStore.appCode);
appContextStore.appCode,
appContextStore.isDevMode,
);
} }
// 主应用模式,不传参数 // 主应用模式,不传参数
+11 -25
View File
@@ -26,17 +26,10 @@ function setupCommonGuard(router: Router) {
const tabHistoryByApp = new Map<string, any>(); const tabHistoryByApp = new Map<string, any>();
router.beforeEach((to) => { router.beforeEach((to) => {
// 检测应用切换(支持 /app/ 和 /app-dev/ 两种模式) // 检测应用切换
const devMatch = to.path.match(/^\/app-dev\/([^/]+)/);
const appMatch = to.path.match(/^\/app\/([^/]+)/); const appMatch = to.path.match(/^\/app\/([^/]+)/);
const newAppCode = const newAppCode = (appMatch && appMatch[1]) || null;
(devMatch && devMatch[1]) || (appMatch && appMatch[1]) || null; const appKey = newAppCode || 'main';
const isDevMode = !!(devMatch && devMatch[1]);
const appKey = newAppCode
? (isDevMode
? `${newAppCode}-dev`
: newAppCode)
: 'main';
// 如果应用切换了,保存当前应用的 tab 历史,加载新应用的 tab 历史 // 如果应用切换了,保存当前应用的 tab 历史,加载新应用的 tab 历史
if (newAppCode !== currentAppCode) { if (newAppCode !== currentAppCode) {
@@ -85,13 +78,10 @@ function setupCommonGuard(router: Router) {
// 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行 // 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行
loadedPaths.add(to.path); loadedPaths.add(to.path);
// 保存当前应用的 tab 历史到 sessionStorage(支持 /app/ 和 /app-dev/ 两种模式) // 保存当前应用的 tab 历史到 sessionStorage
const devMatch = to.path.match(/^\/app-dev\/([^/]+)/);
const appMatch = to.path.match(/^\/app\/([^/]+)/); const appMatch = to.path.match(/^\/app\/([^/]+)/);
const appCode = const appCode = (appMatch && appMatch[1]) || null;
(devMatch && devMatch[1]) || (appMatch && appMatch[1]) || null; const appKey = appCode || 'main';
const isDevMode = !!(devMatch && devMatch[1]);
const appKey = appCode ? (isDevMode ? `${appCode}-dev` : appCode) : 'main';
const storageKey = `vben-admin-tabs-${appKey}`; const storageKey = `vben-admin-tabs-${appKey}`;
const tabbarStore = useTabbarStore(); const tabbarStore = useTabbarStore();
@@ -166,16 +156,13 @@ function setupAccessGuard(router: Router) {
? decodeURIComponent(redirectParam) ? decodeURIComponent(redirectParam)
: to.path; : to.path;
// 支持 /app-dev/ 和 /app/ 两种模式 // 支持 /app/ 子应用路径
const devMatch = targetPath.match(/^\/app-dev\/([^/]+)/);
const appMatch = targetPath.match(/^\/app\/([^/]+)/); const appMatch = targetPath.match(/^\/app\/([^/]+)/);
const detectedAppCode = const detectedAppCode = (appMatch && appMatch[1]) || null;
(devMatch && devMatch[1]) || (appMatch && appMatch[1]) || null;
const detectedDevMode = !!(devMatch && devMatch[1]);
if (detectedAppCode && !appContextStore.appCode) { if (detectedAppCode && !appContextStore.appCode) {
// 从重定向路径中检测到子应用,初始化 appContextStore // 从重定向路径中检测到子应用,初始化 appContextStore
await appContextStore.setAppCode(detectedAppCode, detectedDevMode); await appContextStore.setAppCode(detectedAppCode);
} }
// 生成路由表 // 生成路由表
@@ -196,10 +183,9 @@ function setupAccessGuard(router: Router) {
accessStore.setAccessRoutes(accessibleRoutes); accessStore.setAccessRoutes(accessibleRoutes);
accessStore.setIsAccessChecked(true); accessStore.setIsAccessChecked(true);
// 子应用根路径重定向(/app/hr -> /app/hr/xxx 或 /app-dev/hr -> /app-dev/hr/xxx // 子应用根路径重定向(/app/hr -> /app/hr/xxx
if (appContextStore.isSubApp) { if (appContextStore.isSubApp) {
const pathPrefix = appContextStore.isDevMode ? '/app-dev' : '/app'; const subAppRootPath = `/app/${appContextStore.appCode}`;
const subAppRootPath = `${pathPrefix}/${appContextStore.appCode}`;
if (to.path === subAppRootPath) { if (to.path === subAppRootPath) {
const defaultHome = const defaultHome =
@@ -19,15 +19,6 @@ const routes: RouteRecordRaw[] = [
path: '/app/:appCode/ai-platform/workflow-runs', path: '/app/:appCode/ai-platform/workflow-runs',
component: () => import('#/views/ai-platform/workflow-runs/index.vue'), component: () => import('#/views/ai-platform/workflow-runs/index.vue'),
}, },
{
meta: {
hideInMenu: true,
title: '执行历史',
},
name: 'SubAppDevAIWorkflowRuns',
path: '/app-dev/:appCode/ai-platform/workflow-runs',
component: () => import('#/views/ai-platform/workflow-runs/index.vue'),
},
// 工作流编辑器 - 主应用 // 工作流编辑器 - 主应用
{ {
meta: { meta: {
@@ -50,17 +41,6 @@ const routes: RouteRecordRaw[] = [
path: '/app/:appCode/ai-platform/workflow/editor/:id', path: '/app/:appCode/ai-platform/workflow/editor/:id',
component: () => import('#/views/ai-platform/workflow/editor/index.vue'), component: () => import('#/views/ai-platform/workflow/editor/index.vue'),
}, },
// 工作流编辑器 - 子应用开发模式
{
meta: {
hideInMenu: true,
title: '工作流编辑器',
noBasicLayout: true,
},
name: 'SubAppDevAIWorkflowEditor',
path: '/app-dev/:appCode/ai-platform/workflow/editor/:id',
component: () => import('#/views/ai-platform/workflow/editor/index.vue'),
},
// 智能体编辑器 - 主应用 // 智能体编辑器 - 主应用
{ {
meta: { meta: {
@@ -83,17 +63,6 @@ const routes: RouteRecordRaw[] = [
path: '/app/:appCode/ai-platform/agent/editor/:id', path: '/app/:appCode/ai-platform/agent/editor/:id',
component: () => import('#/views/ai-platform/agent/editor/index.vue'), component: () => import('#/views/ai-platform/agent/editor/index.vue'),
}, },
// 智能体编辑器 - 子应用开发模式
{
meta: {
hideInMenu: true,
title: '智能体编辑器',
noBasicLayout: true,
},
name: 'SubAppDevAIAgentEditor',
path: '/app-dev/:appCode/ai-platform/agent/editor/:id',
component: () => import('#/views/ai-platform/agent/editor/index.vue'),
},
// 知识库详情 - 主应用 // 知识库详情 - 主应用
{ {
meta: { meta: {
@@ -114,16 +83,6 @@ const routes: RouteRecordRaw[] = [
path: '/app/:appCode/ai-platform/knowledge/detail/:id', path: '/app/:appCode/ai-platform/knowledge/detail/:id',
component: () => import('#/views/ai-platform/knowledge/detail/index.vue'), component: () => import('#/views/ai-platform/knowledge/detail/index.vue'),
}, },
// 知识库详情 - 子应用开发模式
{
meta: {
hideInMenu: true,
title: '知识库详情',
},
name: 'SubAppDevAIKnowledgeDetail',
path: '/app-dev/:appCode/ai-platform/knowledge/detail/:id',
component: () => import('#/views/ai-platform/knowledge/detail/index.vue'),
},
]; ];
export default routes; export default routes;
+5 -29
View File
@@ -17,9 +17,6 @@ export const useAppContextStore = defineStore('app-context', () => {
// 当前应用详情 // 当前应用详情
const currentApp = ref<Application | null>(null); const currentApp = ref<Application | null>(null);
// 是否为开发模式(/app-dev/{code}/
const isDevMode = ref<boolean>(false);
// 是否为子应用模式 // 是否为子应用模式
const isSubApp = computed(() => !!appCode.value); const isSubApp = computed(() => !!appCode.value);
@@ -30,32 +27,19 @@ export const useAppContextStore = defineStore('app-context', () => {
* 从 URL 路径中初始化应用上下文 * 从 URL 路径中初始化应用上下文
* URL 格式: * URL 格式:
* - /app/:appCode/... (正常模式) * - /app/:appCode/... (正常模式)
* - /app-dev/:appCode/... (开发模式)
*/ */
async function initFromUrl() { async function initFromUrl() {
const path = window.location.pathname; const path = window.location.pathname;
// 先尝试匹配开发模式 /app-dev/{code}/
const devMatch = path.match(/^\/app-dev\/([^/]+)/);
if (devMatch && devMatch[1]) {
appCode.value = devMatch[1];
isDevMode.value = true;
await loadCurrentApp();
return;
}
// 再尝试匹配正常模式 /app/{code}/
const appMatch = path.match(/^\/app\/([^/]+)/); const appMatch = path.match(/^\/app\/([^/]+)/);
if (appMatch && appMatch[1]) { if (appMatch && appMatch[1]) {
appCode.value = appMatch[1]; appCode.value = appMatch[1];
isDevMode.value = false;
await loadCurrentApp(); await loadCurrentApp();
return; return;
} }
// 主应用模式 // 主应用模式
appCode.value = null; appCode.value = null;
isDevMode.value = false;
currentApp.value = null; currentApp.value = null;
} }
@@ -80,11 +64,9 @@ export const useAppContextStore = defineStore('app-context', () => {
/** /**
* 设置当前应用 * 设置当前应用
* @param code 应用编码 * @param code 应用编码
* @param devMode 是否为开发模式
*/ */
async function setAppCode(code: null | string, devMode: boolean = false) { async function setAppCode(code: null | string) {
appCode.value = code; appCode.value = code;
isDevMode.value = devMode;
if (code) { if (code) {
await loadCurrentApp(); await loadCurrentApp();
} else { } else {
@@ -97,32 +79,28 @@ export const useAppContextStore = defineStore('app-context', () => {
*/ */
function clear() { function clear() {
appCode.value = null; appCode.value = null;
isDevMode.value = false;
currentApp.value = null; currentApp.value = null;
} }
/** /**
* 获取子应用 URL * 获取子应用 URL
* @param code 应用编码 * @param code 应用编码
* @param devMode 是否为开发模式
*/ */
function getSubAppUrl(code: string, devMode: boolean = false): string { function getSubAppUrl(code: string): string {
const baseUrl = window.location.origin; const baseUrl = window.location.origin;
const prefix = devMode ? 'app-dev' : 'app'; return `${baseUrl}/app/${code}`;
return `${baseUrl}/${prefix}/${code}`;
} }
/** /**
* 获取当前上下文的路由路径 * 获取当前上下文的路由路径
* 在子应用模式下会自动添加 /app/{code} 或 /app-dev/{code} 前缀 * 在子应用模式下会自动添加 /app/{code} 前缀
* @param path 原始路由路径(如 /ai-platform/workflow/editor/123 * @param path 原始路由路径(如 /ai-platform/workflow/editor/123
*/ */
function getContextPath(path: string): string { function getContextPath(path: string): string {
if (!appCode.value) { if (!appCode.value) {
return path; return path;
} }
const prefix = isDevMode.value ? 'app-dev' : 'app'; return `/app/${appCode.value}${path}`;
return `/${prefix}/${appCode.value}${path}`;
} }
/** /**
@@ -130,14 +108,12 @@ export const useAppContextStore = defineStore('app-context', () => {
*/ */
function $reset() { function $reset() {
appCode.value = null; appCode.value = null;
isDevMode.value = false;
currentApp.value = null; currentApp.value = null;
} }
return { return {
appCode, appCode,
currentApp, currentApp,
isDevMode,
isSubApp, isSubApp,
isMainApp, isMainApp,
initFromUrl, initFromUrl,