/** * /admin-vue/ 基准路径与 History 路由约定(任务 2 冻结的单一事实源)。 * Vite base、Vue Router history base、Nginx location 三者必须共用此常量。 */ export const APP_BASE_PATH = '/admin-vue/' /** 前端路由一律使用 History 模式,不引入 hash(旧 #tab 兼容不在目标契约内)。 */ export const APP_HISTORY_MODE = 'history' as const function invalidBaseMessage(value: unknown): string { return `管理后台 base 路径非法: ${JSON.stringify(value)},必须以 / 开头且形如 /xxx/,不允许空值或 ..` } /** 校验并归一化 base 路径:保证以 / 开头、无 ..、无内部连续斜杠、以单个 / 结尾。 */ export function ensureAppBasePath(value: string): string { if (typeof value !== 'string' || value.length === 0) throw new Error(invalidBaseMessage(value)) if (!value.startsWith('/')) throw new Error(invalidBaseMessage(value)) if (value.includes('//')) throw new Error(invalidBaseMessage(value)) if (value.includes('..')) throw new Error(invalidBaseMessage(value)) return value.endsWith('/') ? value : `${value}/` } /** 把后台页面相对路径段拼成绝对 URL(不含 base 前缀的历史兼容子路径段由路由负责)。 */ export function joinAdminPath(...segments: string[]): string { const parts: string[] = [] for (const segment of segments) { for (const raw of segment.split('/')) { const part = raw.trim() if (!part || part === '.') continue if (part === '..') { throw new Error(`admin 路由段不允许出现 ..(越权跳转): ${segments.join('/')}`) } parts.push(part) } } if (parts.length === 0) return APP_BASE_PATH return `${APP_BASE_PATH}${parts.join('/')}` }