menuExtras.ts 4.86 KB
import type { MenuNode } from "@/api/admin";

function normPath(p: string | undefined): string {
  if (!p) return "";
  const s = p.trim();
  return s.startsWith("/") ? s : `/${s}`.replace(/^\/+/, "/");
}

function hasMpUserMenu(nodes: MenuNode[]): boolean {
  for (const n of nodes) {
    if (normPath(n.path) === "/system/mp-user") return true;
    if (n.children?.length && hasMpUserMenu(n.children)) return true;
  }
  return false;
}

function hasDownloadAssetsMenu(nodes: MenuNode[]): boolean {
  for (const n of nodes) {
    if (normPath(n.path) === "/content/download-assets") return true;
    if (n.children?.length && hasDownloadAssetsMenu(n.children)) return true;
  }
  return false;
}

/** 去掉「内容管理」目录,子项(作品审核、轮播等)提升为侧栏一级 */
export function flattenContentMenus(list: MenuNode[]): MenuNode[] {
  if (!list.length) return list;
  const out: MenuNode[] = [];
  for (const n of list) {
    const p = normPath(n.path);
    const isContentDir =
      n.type === 1 && (p === "/content" || n.name === "内容管理");
    if (isContentDir && n.children?.length) {
      const parentSort = n.sort ?? 10;
      const sorted = [...n.children].sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
      sorted.forEach((ch, idx) => {
        out.push({
          ...ch,
          parent_id: 0,
          sort: parentSort + idx + 1,
        });
      });
      continue;
    }
    out.push(n);
  }
  out.sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
  return out;
}

/** 将「用户管理」从系统管理下提升为侧栏一级(排在内容菜单后、系统管理前) */
export function flattenMpUserMenu(list: MenuNode[]): MenuNode[] {
  if (!list.length) return list;
  const hoisted: MenuNode[] = [];
  const out: MenuNode[] = [];
  const hoistSort = 15;

  for (const n of list) {
    const p = normPath(n.path);
    const isSystemDir = n.type === 1 && (p === "/system" || n.name === "系统管理");
    if (isSystemDir && n.children?.length) {
      const rest: MenuNode[] = [];
      for (const ch of n.children) {
        if (normPath(ch.path) === "/system/mp-user") {
          hoisted.push({
            ...ch,
            parent_id: 0,
            sort: hoistSort,
          });
        } else {
          rest.push(ch);
        }
      }
      out.push({
        ...n,
        children: rest.length ? rest : undefined,
      });
      continue;
    }
    out.push(n);
  }

  const merged = [...out, ...hoisted];
  merged.sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
  return merged;
}

/** 接口未返回用户管理菜单时,在根级补一项(一级侧栏) */
export function withMpUserMenu(list: MenuNode[]): MenuNode[] {
  if (!list.length || hasMpUserMenu(list)) {
    return list;
  }
  const item: MenuNode = {
    id: 999001,
    parent_id: 0,
    name: "用户管理",
    type: 2,
    path: "/system/mp-user",
    component: "",
    perms: "",
    icon: "avatar",
    sort: 15,
  };
  return [...list, item].sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
}

/** 接口未返回下载素材菜单时,在根级补一项(一级侧栏) */
export function withDownloadAssetsMenu(list: MenuNode[]): MenuNode[] {
  if (!list.length || hasDownloadAssetsMenu(list)) {
    return list;
  }
  const item: MenuNode = {
    id: 999002,
    parent_id: 0,
    name: "下载素材",
    type: 2,
    path: "/content/download-assets",
    component: "",
    perms: "",
    icon: "picture",
    sort: 11,
  };
  return [...list, item].sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
}

/**
 * 保证轮播图、作品审核排在「系统管理」之上(避免库中 sort 相同时按 id 把系统顶到前面)
 */
export function ensureContentBeforeSystem(list: MenuNode[]): MenuNode[] {
  if (!list.length) return list;
  const contentOrder: Record<string, number> = {
    "/content/download-assets": 10,
    "/content/banner": 11,
    "/content/work": 12,
  };
  const patched = list.map((n) => {
    const p = normPath(n.path);
    if (n.type === 1 && (p === "/system" || n.name === "系统管理")) {
      const s = n.sort ?? 0;
      return { ...n, sort: s < 20 ? 20 : s };
    }
    if (contentOrder[p] !== undefined) {
      return { ...n, sort: contentOrder[p] };
    }
    return n;
  });
  patched.sort((a, b) => {
    const sa = a.sort ?? 0;
    const sb = b.sort ?? 0;
    if (sa !== sb) return sa - sb;
    return (a.id ?? 0) - (b.id ?? 0);
  });
  return patched;
}

/** 接口未配置「工作台」菜单时,前置仪表盘入口 */
export function withDashboardMenu(list: MenuNode[]): MenuNode[] {
  const dash: MenuNode = {
    id: 0,
    parent_id: 0,
    name: "工作台",
    type: 2,
    path: "/dashboard",
    component: "",
    perms: "",
    icon: "odometer",
    sort: -999,
  };
  const has = list.some(
    (m) => m.path === "/dashboard" || m.children?.some((c) => c.path === "/dashboard")
  );
  if (has) {
    return list;
  }
  return [dash, ...list];
}