94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
/**
|
||
* 页头导航菜单配置
|
||
*
|
||
* 数据来源:后端「官网管理 - 导航菜单」(yz_platform_website_nav),
|
||
* 由 GET /site/nav 下发(仅启用且显示中的菜单,已按 pid 组好树)。
|
||
* 接口异常或后台未配置任何菜单时,回退到 config/site.ts 里的静态 navItems,
|
||
* 保证页头导航不会为空。
|
||
*
|
||
* 菜单类型(与平台端一致):
|
||
* 1 目录 仅分组,本身可带 path;配置了子菜单时页头渲染为下拉
|
||
* 2 页面 路由地址
|
||
* 3 外链 完整 URL,新窗口打开
|
||
* 4 单页 路由地址(内容取自「单页管理」)
|
||
*/
|
||
import { ref } from "vue";
|
||
import { getJSON } from "@/api/request";
|
||
import { navItems } from "./site";
|
||
import { sections } from "./sections";
|
||
|
||
export interface NavMenuItem {
|
||
/** v-for key:后端菜单用 nav-{id},静态回退用 static-{path} */
|
||
key: string;
|
||
label: string;
|
||
path: string;
|
||
/** 是否站外链接:后台菜单类型为「外链」或 path 填写了完整 URL */
|
||
external: boolean;
|
||
children: NavMenuItem[];
|
||
}
|
||
|
||
/** /site/nav 返回的节点结构 */
|
||
interface RemoteNavNode {
|
||
id: number;
|
||
pid: number;
|
||
title: string;
|
||
type: number;
|
||
path: string;
|
||
icon?: string;
|
||
external?: boolean;
|
||
children?: RemoteNavNode[];
|
||
}
|
||
|
||
const isExternalPath = (path: string): boolean => /^https?:\/\//i.test(path);
|
||
|
||
function toMenuItem(node: RemoteNavNode): NavMenuItem {
|
||
const path = node.path || "/";
|
||
return {
|
||
key: `nav-${node.id}`,
|
||
label: node.title || "",
|
||
path,
|
||
external: node.external ?? (node.type === 3 || isExternalPath(path)),
|
||
children: (node.children ?? []).map(toMenuItem),
|
||
};
|
||
}
|
||
|
||
/** 静态回退菜单:后台未配置时使用 */
|
||
const fallbackMenus = (): NavMenuItem[] =>
|
||
navItems.map((item) => {
|
||
const menuItem: NavMenuItem = {
|
||
key: `static-${item.path}`,
|
||
label: item.label,
|
||
path: item.path,
|
||
external: isExternalPath(item.path),
|
||
children: [],
|
||
};
|
||
|
||
// 新闻中心:默认把「新闻中心」下的分类作为二级菜单(鼠标悬停展开)
|
||
if (item.path === "/news") {
|
||
menuItem.children = sections.news.categories.map((cat) => ({
|
||
key: `static-news-${cat.slug}`,
|
||
label: cat.name,
|
||
path: `/news/category/${cat.slug}`,
|
||
external: false,
|
||
children: [],
|
||
}));
|
||
}
|
||
|
||
return menuItem;
|
||
});
|
||
|
||
/** 页头导航菜单(应用启动时由 loadNavMenus 拉取,初始为静态回退) */
|
||
export const navMenus = ref<NavMenuItem[]>(fallbackMenus());
|
||
|
||
/**
|
||
* 拉取后台配置的导航菜单(应用启动时调用一次)。
|
||
* 接口异常或后台未配置时保持静态回退菜单,不置空。
|
||
*/
|
||
export async function loadNavMenus(): Promise<void> {
|
||
const data = await getJSON<{ list: RemoteNavNode[] }>("/site/nav");
|
||
const list = data?.list ?? [];
|
||
if (list.length > 0) {
|
||
navMenus.value = list.map(toMenuItem);
|
||
}
|
||
}
|