feat: 初始化提交
笑笑 authored at 2025-05-13 21:25:06
5.74 KiB
cube-front
import { defineStore } from 'pinia';
import { getConfig } from 'cube-front/core/configure';
import { getDataByKey, isPromise } from '../utils/common';
import { type AxiosRequestConfig } from 'axios';
import request from '../utils/request';

export interface FlatMenuItem {
  id: string;
  name: string;
  path: string;
  title?: string;
  icon?: string;
  sort?: number;
  parentId?: string | null;
  active?: boolean;
}

export interface TreeMenuItem {
  id: string;
  name: string;
  path: string;
  title?: string;
  icon?: string;
  sort?: number;
  parentId?: string | null;
  parentMenu?: TreeMenuItem;
  children?: TreeMenuItem[];
  active?: boolean;
}

/** 平铺菜单转换成树形菜单 */
export function convertFlatMenuToTreeMenu(flatMenu: FlatMenuItem[]): TreeMenuItem[] {
  const menuMap: { [id: string]: TreeMenuItem } = {};

  // Create a map of menu items using their IDs as keys
  flatMenu.forEach((item) => {
    menuMap[item.id] = item;
  });

  const treeMenu: TreeMenuItem[] = [];

  // Iterate over the flat menu items and build the tree menu
  flatMenu.forEach((item) => {
    const menuItem = menuMap[item.id];

    if (item.parentId) {
      const parentItem = menuMap[item.parentId];
      if (parentItem) {
        parentItem.children = parentItem.children || [];
        parentItem.children.push(menuItem);
        menuItem.parentMenu = parentItem;
      }
    } else {
      treeMenu.push(menuItem);
    }
  });

  return treeMenu;
}

/** 树形菜单转换成平铺菜单 */
export function convertTreeMenuToFlatMenu(
  treeMenuList: TreeMenuItem[],
  parentMenu?: TreeMenuItem,
): FlatMenuItem[] {
  const flatMenu: FlatMenuItem[] = [];

  const flatten = (menuList: TreeMenuItem[], parentMenu2?: TreeMenuItem) => {
    menuList.forEach((item) => {
      item.parentMenu = parentMenu2;
      flatMenu.push(item);
      if (item.children) {
        flatten(item.children, item);
      }
    });
  };

  flatten(treeMenuList, parentMenu);

  return flatMenu;
}

/** 将菜单数据按照配置的字段名称进行转换,支持平铺数据和树形数据 */
const covertMenu = (list: never[]) => {
  const menuConfig = getConfig().menu;
  const newList = list.map((item) => {
    const newItem: TreeMenuItem = {
      ...(item as TreeMenuItem),
      id: item[menuConfig.idField],
      parentId: item[menuConfig.parentField],
      name: item[menuConfig.nameField],
      path: item[menuConfig.pathField],
      title: item[menuConfig.titleField],
      icon: item[menuConfig.iconField],
      sort: item[menuConfig.sortField],
      children: item[menuConfig.childrenField]
        ? covertMenu(item[menuConfig.childrenField])
        : undefined,
    };

    return newItem;
  });

  return newList;
};

const state: {
  /** 平铺菜单  */
  flatMenus: Array<FlatMenuItem> | undefined;
  /** 树形菜单 */
  treeMenus: Array<TreeMenuItem> | undefined;
  activeMenu: TreeMenuItem | undefined;
  loading: boolean;
} = {
  flatMenus: undefined,
  treeMenus: undefined,
  activeMenu: undefined,
  loading: false,
};

const getTopLevelMenu = (menu: TreeMenuItem) => {
  if (menu.parentMenu) {
    return getTopLevelMenu(menu.parentMenu);
  }
  return menu;
};

export const useMenuStore = defineStore('menu', {
  state: () => state,
  getters: {
    hasMenus: (state) =>
      state.flatMenus &&
      state.treeMenus &&
      state.flatMenus.length > 0 &&
      state.treeMenus.length > 0,
    topLevelActiveMenu: (state) => {
      return state.activeMenu && getTopLevelMenu(state.activeMenu);
    },
  },
  actions: {
    setFlatMenus(data: Array<FlatMenuItem>) {
      this.flatMenus = data;
      this.treeMenus = convertFlatMenuToTreeMenu(data);
    },
    setTreeMenus(data: Array<TreeMenuItem>) {
      this.treeMenus = data;
      this.flatMenus = convertTreeMenuToFlatMenu(data);
    },
    setActiveMenu(menu: TreeMenuItem) {
      this.activeMenu = menu;
    },
    setActiveMenuByPath(path: string) {
      const activeMenu = this.flatMenus?.find((item) => item.path === path);
      this.setActiveMenu(activeMenu as TreeMenuItem);
    },
    async fetchMenuAsync() {
      if (this.loading) return;
      this.loading = true;
      // 如果没有菜单信息,则获取菜单信息
      if (!this.hasMenus) {
        const menuConfig = getConfig().menu;
        const getMenuAxiosConfig = menuConfig.getMenuAxiosConfig;

        // 处理不同类型的 getMenuAxiosConfig
        const processMenuRequestAsync = async (config: AxiosRequestConfig) => {
          try {
            const res = await request(config);
            // 增加延时,避免下面的赋值触发更新再次请求
            setTimeout(() => {
              this.loading = false;
            }, 1000);

            const data = getDataByKey(res as never, menuConfig.dataKey);

            if (menuConfig.isMenuTree) {
              this.setTreeMenus(covertMenu(data as never[]));
            } else {
              this.setFlatMenus(covertMenu(data as never[]));
            }
            this.setActiveMenuByPath(window.location.pathname);
          } catch (error) {
            // 如果报错,增加延时,避免下面的赋值触发更新再次请求
            setTimeout(() => {
              this.loading = false;
            }, 1000);
          }
        };

        // 根据类型执行不同操作
        if (typeof getMenuAxiosConfig === 'function') {
          const config = getMenuAxiosConfig();
          if (config instanceof Promise) {
            // 处理返回 Promise 类型的配置
            await processMenuRequestAsync(await config);
          } else {
            await processMenuRequestAsync(config);
          }
        } else {
          // 处理直接配置对象
          await processMenuRequestAsync(getMenuAxiosConfig);
        }
      } else {
        this.loading = false;
      }
    },
  },
});