feat: tenant-scoped routing and portal navigation

This commit is contained in:
2026-01-08 21:30:46 +08:00
parent f3aa92078a
commit 3e095c57f3
52 changed files with 1111 additions and 670 deletions

View File

@@ -1,7 +1,9 @@
// Simple Fetch Wrapper
const BASE_URL = '/v1';
import { getTenantCode } from './tenant';
export async function request(endpoint, options = {}) {
const tenantCode = getTenantCode();
const baseUrl = tenantCode ? `/t/${tenantCode}/v1` : '/v1';
const token = localStorage.getItem('token');
const headers = {
@@ -22,7 +24,7 @@ export async function request(endpoint, options = {}) {
}
try {
const res = await fetch(`${BASE_URL}${endpoint}`, {
const res = await fetch(`${baseUrl}${endpoint}`, {
...options,
headers
});
@@ -40,9 +42,10 @@ export async function request(endpoint, options = {}) {
if (res.status === 401) {
localStorage.removeItem('token');
localStorage.removeItem('user');
const loginPath = tenantCode ? `/t/${tenantCode}/auth/login` : '/auth/login';
// Redirect to login if not already there
if (!window.location.pathname.startsWith('/auth/login')) {
window.location.href = '/auth/login';
if (!window.location.pathname.includes('/auth/login')) {
window.location.href = loginPath;
}
}

View File

@@ -0,0 +1,18 @@
export function getTenantCode() {
const match = window.location.pathname.match(/^\/t\/([^/]+)(?:\/|$)/);
return match ? match[1] : '';
}
export function resolveTenantCode(route) {
if (route && route.params && route.params.tenantCode) {
return String(route.params.tenantCode);
}
return getTenantCode();
}
export function tenantPath(path, route) {
const tenantCode = resolveTenantCode(route);
const base = tenantCode ? `/t/${tenantCode}` : '';
const normalized = path.startsWith('/') ? path : `/${path}`;
return `${base}${normalized}`;
}