123 lines
3.1 KiB
TypeScript
123 lines
3.1 KiB
TypeScript
import type { RouteRecordNormalized } from 'vue-router';
|
|
|
|
import { useRouter } from 'vue-router';
|
|
|
|
import { isHttpUrl, openRouteInNewWindow, openWindow } from '@easyflow/utils';
|
|
|
|
function useNavigation() {
|
|
const router = useRouter();
|
|
const routeMetaMap = new Map<string, RouteRecordNormalized>();
|
|
const prefetchedPaths = new Set<string>();
|
|
const prefetchingPaths = new Set<string>();
|
|
|
|
// 初始化路由映射
|
|
const initRouteMetaMap = () => {
|
|
const routes = router.getRoutes();
|
|
routes.forEach((route) => {
|
|
routeMetaMap.set(route.path, route);
|
|
});
|
|
};
|
|
|
|
initRouteMetaMap();
|
|
|
|
// 监听路由变化
|
|
router.afterEach(() => {
|
|
initRouteMetaMap();
|
|
});
|
|
|
|
// 检查是否应该在新窗口打开
|
|
const shouldOpenInNewWindow = (path: string): boolean => {
|
|
if (isHttpUrl(path)) {
|
|
return true;
|
|
}
|
|
const route = routeMetaMap.get(path);
|
|
// 如果有外链或者设置了在新窗口打开,返回 true
|
|
return !!(route?.meta?.link || route?.meta?.openInNewWindow);
|
|
};
|
|
|
|
const resolveHref = (path: string): string => {
|
|
return router.resolve(path).href;
|
|
};
|
|
|
|
const prefetch = (path: string) => {
|
|
if (
|
|
isHttpUrl(path) ||
|
|
prefetchedPaths.has(path) ||
|
|
prefetchingPaths.has(path)
|
|
) {
|
|
return;
|
|
}
|
|
const { matched } = router.resolve(path);
|
|
if (matched.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const componentLoadTasks: Array<Promise<unknown>> = [];
|
|
matched.forEach((route) => {
|
|
const component = route.components?.default;
|
|
if (typeof component !== 'function' || component.length > 0) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const loaded = (component as () => unknown)();
|
|
if (loaded && typeof (loaded as Promise<unknown>).then === 'function') {
|
|
componentLoadTasks.push(loaded as Promise<unknown>);
|
|
}
|
|
} catch {
|
|
// 预取失败不影响正常导航
|
|
}
|
|
});
|
|
|
|
if (componentLoadTasks.length === 0) {
|
|
prefetchedPaths.add(path);
|
|
return;
|
|
}
|
|
|
|
prefetchingPaths.add(path);
|
|
void Promise.allSettled(componentLoadTasks)
|
|
.then(() => {
|
|
prefetchedPaths.add(path);
|
|
})
|
|
.finally(() => {
|
|
prefetchingPaths.delete(path);
|
|
});
|
|
};
|
|
|
|
const navigation = async (path: string) => {
|
|
try {
|
|
const route = routeMetaMap.get(path);
|
|
const { openInNewWindow = false, query = {}, link } = route?.meta ?? {};
|
|
|
|
// 检查是否有外链
|
|
if (link && typeof link === 'string') {
|
|
openWindow(link, { target: '_blank' });
|
|
return;
|
|
}
|
|
|
|
if (isHttpUrl(path)) {
|
|
openWindow(path, { target: '_blank' });
|
|
} else if (openInNewWindow) {
|
|
openRouteInNewWindow(resolveHref(path));
|
|
} else {
|
|
prefetch(path);
|
|
await router.push({
|
|
path,
|
|
query,
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('Navigation failed:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const willOpenedByWindow = (path: string) => {
|
|
return shouldOpenInNewWindow(path);
|
|
};
|
|
|
|
return { navigation, prefetch, willOpenedByWindow };
|
|
}
|
|
|
|
export { useNavigation };
|