rbac-menu.ts 4.75 KB
import type {
  RbacMenuTreeNode,
  SystemMenuDto,
  SystemMenuGetListQuery,
  SystemMenuUpsertInput,
} from './rbac-menu-types';
import type { FlPagedResult } from './types';

import { requestClient } from '#/api/request';

const PATH = '/rbac-menu';

/** 兼容后端 PascalCase 字段 */
export function normalizeSystemMenuDto(row: unknown): SystemMenuDto {
  if (!row || typeof row !== 'object') {
    return { id: '' };
  }
  const r = row as Record<string, unknown>;
  const router = (r.router ?? r.Router) as string | null | undefined;
  const routeUrl = (r.routeUrl ?? r.RouteUrl ?? router) as string | null | undefined;
  return {
    id: String(r.id ?? r.Id ?? ''),
    orderNum: (r.orderNum ?? r.OrderNum) as number | null | undefined,
    state: (r.state ?? r.State) as boolean | null | undefined,
    menuName: (r.menuName ?? r.MenuName) as string | null | undefined,
    routerName: (r.routerName ?? r.RouterName) as string | null | undefined,
    router,
    menuType: (r.menuType ?? r.MenuType) as number | null | undefined,
    permissionCode: (r.permissionCode ?? r.PermissionCode) as
      | string
      | null
      | undefined,
    parentId: (r.parentId ?? r.ParentId) as string | null | undefined,
    menuIcon: (r.menuIcon ?? r.MenuIcon) as string | null | undefined,
    routeUrl,
    link: (r.link ?? r.Link) as string | null | undefined,
    isCache: (r.isCache ?? r.IsCache) as boolean | null | undefined,
    isShow: (r.isShow ?? r.IsShow) as boolean | null | undefined,
    remark: (r.remark ?? r.Remark) as string | null | undefined,
    component: (r.component ?? r.Component) as string | null | undefined,
    menuSource: (r.menuSource ?? r.MenuSource) as number | null | undefined,
    query: (r.query ?? r.Query) as string | null | undefined,
    concurrencyStamp: (r.concurrencyStamp ?? r.ConcurrencyStamp) as
      | string
      | null
      | undefined,
  };
}

function toPagedMenus(raw: unknown): FlPagedResult<SystemMenuDto> {
  if (Array.isArray(raw)) {
    return {
      items: raw.map(normalizeSystemMenuDto),
      totalCount: raw.length,
    };
  }
  const o = raw as Record<string, unknown>;
  const itemsRaw = (o.items ?? o.Items ?? []) as unknown[];
  const total =
    typeof o.totalCount === 'number'
      ? o.totalCount
      : typeof o.TotalCount === 'number'
        ? (o.TotalCount as number)
        : itemsRaw.length;
  return {
    items: itemsRaw.map(normalizeSystemMenuDto),
    totalCount: total,
  };
}

function normalizeRbacMenuTreeNode(row: unknown): RbacMenuTreeNode {
  const base = normalizeSystemMenuDto(row);
  const r = row as Record<string, unknown> | null | undefined;
  const childrenRaw = (r?.children ?? r?.Children) as unknown;
  const children = Array.isArray(childrenRaw)
    ? childrenRaw.map(normalizeRbacMenuTreeNode)
    : undefined;
  return {
    ...base,
    children,
  };
}

function toMenuTree(raw: unknown): RbacMenuTreeNode[] {
  if (Array.isArray(raw)) {
    return raw.map(normalizeRbacMenuTreeNode);
  }
  if (!raw || typeof raw !== 'object') {
    return [];
  }
  const o = raw as Record<string, unknown>;
  const items = (o.items ?? o.Items ?? o.data ?? o.Data) as unknown;
  if (Array.isArray(items)) {
    return items.map(normalizeRbacMenuTreeNode);
  }
  return [];
}

export async function rbacMenuList(params?: SystemMenuGetListQuery) {
  const raw = await requestClient.get<unknown>(PATH, {
    params,
    errorMessageMode: 'message',
  });
  return toPagedMenus(raw);
}

export async function rbacMenuTree() {
  const raw = await requestClient.get<unknown>(`${PATH}/tree`, {
    errorMessageMode: 'message',
  });
  return toMenuTree(raw);
}

/** 父级下拉:所有目录类型菜单(menuType === 0) */
export async function rbacMenuDirectoryOptions(): Promise<SystemMenuDto[]> {
  const byId = new Map<string, SystemMenuDto>();
  let page = 1;
  const size = 500;
  for (;;) {
    const res = await rbacMenuList({ SkipCount: page, MaxResultCount: size });
    const items = res.items ?? [];
    for (const m of items) {
      if (m.menuType !== 0 || !m.id) {
        continue;
      }
      if (!byId.has(m.id)) {
        byId.set(m.id, m);
      }
    }
    if (items.length < size) {
      break;
    }
    page += 1;
    if (page > 100) {
      break;
    }
  }
  return Array.from(byId.values()).sort(
    (a, b) => (a.orderNum ?? 0) - (b.orderNum ?? 0),
  );
}

export function rbacMenuAdd(data: SystemMenuUpsertInput) {
  return requestClient.postWithMsg<SystemMenuDto>(PATH, data);
}

export function rbacMenuUpdate(id: string, data: SystemMenuUpsertInput) {
  return requestClient.putWithMsg<SystemMenuDto>(
    `${PATH}/${encodeURIComponent(id)}`,
    data,
  );
}

/** 与美国版一致:DELETE body 为 ID 数组 */
export function rbacMenuRemove(id: string) {
  return requestClient.deleteWithMsg<void>(PATH, {
    data: [id],
  });
}