/** * 页头导航菜单配置 * * 数据来源:后端「官网管理 - 导航菜单」(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(fallbackMenus()); /** * 拉取后台配置的导航菜单(应用启动时调用一次)。 * 接口异常或后台未配置时保持静态回退菜单,不置空。 */ export async function loadNavMenus(): Promise { const data = await getJSON<{ list: RemoteNavNode[] }>("/site/nav"); const list = data?.list ?? []; if (list.length > 0) { navMenus.value = list.map(toMenuItem); } }