halo/ui/console-src/modules/interface/menus/utils/index.ts

266 lines
6.5 KiB
TypeScript
Raw Normal View History

import type { MenuItem, MenuItemSpec } from "@halo-dev/api-client";
import { cloneDeep } from "lodash-es";
export interface MenuTreeItemSpec extends Omit<MenuItemSpec, "children"> {
children: MenuTreeItem[];
}
export interface MenuTreeItem extends Omit<MenuItem, "spec"> {
spec: MenuTreeItemSpec;
}
/**
* Convert a flat array of menu items into flattens a menu tree.
*
* Example:
*
* ```json
* [
* {
* "spec": {
* "displayName": "文章分类",
* "href": "https://ryanc.cc/categories",
* "children": [
* "caeef383-3828-4039-9114-6f9ad3b4a37e",
* "ded1943d-9fdb-4563-83ee-2f04364872e0"
* ]
* },
* "apiVersion": "v1alpha1",
* "kind": "MenuItem",
* "metadata": {
* "name": "40e4ba86-5c7e-43c2-b4c0-cee376d26564",
* "version": 12,
* "creationTimestamp": "2022-08-05T04:19:37.252228Z"
* }
* },
* {
* "spec": {
* "displayName": "Halo",
* "href": "https://ryanc.cc/categories/halo",
* "children": []
* },
* "apiVersion": "v1alpha1",
* "kind": "MenuItem",
* "metadata": {
* "name": "caeef383-3828-4039-9114-6f9ad3b4a37e",
* "version": 4,
* "creationTimestamp": "2022-07-28T06:50:32.777556Z"
* }
* },
* {
* "spec": {
* "displayName": "Java",
* "href": "https://ryanc.cc/categories/java",
* "children": []
* },
* "apiVersion": "v1alpha1",
* "kind": "MenuItem",
* "metadata": {
* "name": "ded1943d-9fdb-4563-83ee-2f04364872e0",
* "version": 1,
* "creationTimestamp": "2022-08-05T04:22:03.377364Z"
* }
* }
* ]
* ```
*
* will be transformed to:
*
* ```json
* [
* {
* "spec": {
* "displayName": "文章分类",
* "href": "https://ryanc.cc/categories",
* "children": [
* {
* "spec": {
* "displayName": "Halo",
* "href": "https://ryanc.cc/categories/halo",
* "children": []
* },
* "apiVersion": "v1alpha1",
* "kind": "MenuItem",
* "metadata": {
* "name": "caeef383-3828-4039-9114-6f9ad3b4a37e",
* "version": 4,
* "creationTimestamp": "2022-07-28T06:50:32.777556Z"
* }
* },
* {
* "spec": {
* "displayName": "Java",
* "href": "https://ryanc.cc/categories/java",
* "children": []
* },
* "apiVersion": "v1alpha1",
* "kind": "MenuItem",
* "metadata": {
* "name": "ded1943d-9fdb-4563-83ee-2f04364872e0",
* "version": 1,
* "creationTimestamp": "2022-08-05T04:22:03.377364Z"
* }
* }
* ]
* },
* "apiVersion": "v1alpha1",
* "kind": "MenuItem",
* "metadata": {
* "name": "40e4ba86-5c7e-43c2-b4c0-cee376d26564",
* "version": 12,
* "creationTimestamp": "2022-08-05T04:19:37.252228Z"
* }
* }
* ]
* ```
*
* @param menuItems
*/
export function buildMenuItemsTree(menuItems: MenuItem[]): MenuTreeItem[] {
const menuItemsToUpdate = cloneDeep(menuItems);
const menuItemsMap = {};
const parentMap = {};
menuItemsToUpdate.forEach((menuItem) => {
menuItemsMap[menuItem.metadata.name] = menuItem;
// @ts-ignore
menuItem.spec.children.forEach((child) => {
parentMap[child] = menuItem.metadata.name;
});
menuItem.spec.children = [];
});
menuItemsToUpdate.forEach((menuItem) => {
const parentName = parentMap[menuItem.metadata.name];
if (parentName && menuItemsMap[parentName]) {
menuItemsMap[parentName].spec.children.push(menuItem);
}
});
const menuTreeItems = menuItemsToUpdate.filter(
(node) => parentMap[node.metadata.name] === undefined
);
return sortMenuItemsTree(menuTreeItems);
}
/**
* Sort a menu tree by priority.
*
* @param menuTreeItems
*/
export function sortMenuItemsTree(
menuTreeItems: MenuTreeItem[] | MenuItem[]
): MenuTreeItem[] {
return menuTreeItems
.sort((a, b) => {
if (a.spec.priority < b.spec.priority) {
return -1;
}
if (a.spec.priority > b.spec.priority) {
return 1;
}
return 0;
})
.map((menuTreeItem) => {
if (menuTreeItem.spec.children.length) {
return {
...menuTreeItem,
spec: {
...menuTreeItem.spec,
children: sortMenuItemsTree(menuTreeItem.spec.children),
},
};
}
return menuTreeItem;
});
}
/**
* Reset the menu tree item's priority.
*
* @param menuItems
*/
export function resetMenuItemsTreePriority(
menuItems: MenuTreeItem[]
): MenuTreeItem[] {
for (let i = 0; i < menuItems.length; i++) {
menuItems[i].spec.priority = i;
if (menuItems[i].spec.children) {
resetMenuItemsTreePriority(menuItems[i].spec.children);
}
}
return menuItems;
}
/**
* Convert a menu tree items into a flat array of menu.
*
* @param menuTreeItems
*/
export function convertTreeToMenuItems(menuTreeItems: MenuTreeItem[]) {
const menuItems: MenuItem[] = [];
const menuItemsMap = new Map<string, MenuItem>();
const convertMenuItem = (node: MenuTreeItem | undefined) => {
if (!node) {
return;
}
const children = node.spec.children || [];
menuItemsMap.set(node.metadata.name, {
...node,
spec: {
...node.spec,
children: children.map((child) => child.metadata.name),
},
});
children.forEach((child) => {
convertMenuItem(child);
});
};
menuTreeItems.forEach((node) => {
convertMenuItem(node);
});
menuItemsMap.forEach((node) => {
menuItems.push(node);
});
return menuItems;
}
export function getChildrenNames(menuTreeItem: MenuTreeItem): string[] {
const childrenNames: string[] = [];
function getChildrenNamesRecursive(menuTreeItem: MenuTreeItem) {
if (menuTreeItem.spec.children) {
menuTreeItem.spec.children.forEach((child) => {
childrenNames.push(child.metadata.name);
getChildrenNamesRecursive(child);
});
}
}
getChildrenNamesRecursive(menuTreeItem);
return childrenNames;
}
/**
* Convert {@link MenuTreeItem} to {@link MenuItem} with flat children name array.
*
* @param menuTreeItem
*/
export function convertMenuTreeItemToMenuItem(
menuTreeItem: MenuTreeItem
): MenuItem {
const childNames = menuTreeItem.spec.children.map(
(child) => child.metadata.name
);
return {
...menuTreeItem,
spec: {
...menuTreeItem.spec,
refactor: method parameters of api client (halo-dev/console#605) <!-- Thanks for sending a pull request! Here are some tips for you: 1. 如果这是你的第一次,请阅读我们的贡献指南:<https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>。 1. If this is your first time, please read our contributor guidelines: <https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>. 2. 请根据你解决问题的类型为 Pull Request 添加合适的标签。 2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request. 3. 请确保你已经添加并运行了适当的测试。 3. Ensure you have added or ran the appropriate tests for your PR. --> #### What type of PR is this? /kind improvement /milestone 2.0 <!-- 添加其中一个类别: Add one of the following kinds: /kind bug /kind cleanup /kind documentation /kind feature /kind optimization 适当添加其中一个或多个类别(可选): Optionally add one or more of the following kinds if applicable: /kind api-change /kind deprecation /kind failing-test /kind flake /kind regression --> #### What this PR does / why we need it: 修改 api-client 的请求参数结构,改为所有参数由一个对象包裹,而不是将各个参数作为方法的参数,防止因为后端参数结构发生改变,或者生成 api-client 时参数顺序发生改变导致请求异常。如: ```diff await apiClient.extension.storage.group.updatestorageHaloRunV1alpha1Group( - formState.value.metadata.name, - formState.value + { + name: formState.value.metadata.name, + group: formState.value, + } ); ``` #### Which issue(s) this PR fixes: <!-- PR 合并时自动关闭 issue。 Automatically closes linked issue when PR is merged. 用法:`Fixes #<issue 号>`,或者 `Fixes (粘贴 issue 完整链接)` Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`. --> None #### Screenshots: <!-- 如果此 PR 有 UI 的改动,最好截图说明这个 PR 的改动。 If there are UI changes to this PR, it is best to take a screenshot to illustrate the changes to this PR. eg. Before: ![screenshot-before](https://user-images.githubusercontent.com/screenshot.png) After: ![screenshot-after](https://user-images.githubusercontent.com/screenshot.png) --> None #### Special notes for your reviewer: /cc @halo-dev/sig-halo-admin #### Does this PR introduce a user-facing change? <!-- 如果当前 Pull Request 的修改不会造成用户侧的任何变更,在 `release-note` 代码块儿中填写 `NONE`。 否则请填写用户侧能够理解的 Release Note。如果当前 Pull Request 包含破坏性更新(Break Change), Release Note 需要以 `action required` 开头。 If no, just write "NONE" in the release-note block below. If yes, a release note is required: Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required". --> ```release-note None ```
2022-09-06 02:26:11 +00:00
children: childNames,
},
};
}