mirror of https://github.com/halo-dev/halo
feat: add support for selecting parent menu item when creating menu item
Signed-off-by: Ryan Wang <i@ryanc.cc>pull/3445/head
parent
2a12feccf1
commit
2ac0c525de
|
@ -28,8 +28,9 @@ import { useDebounceFn } from "@vueuse/core";
|
|||
|
||||
const menuItems = ref<MenuItem[]>([] as MenuItem[]);
|
||||
const menuTreeItems = ref<MenuTreeItem[]>([] as MenuTreeItem[]);
|
||||
const selectedMenu = ref<Menu | undefined>();
|
||||
const selectedMenuItem = ref<MenuItem | null>(null);
|
||||
const selectedMenu = ref<Menu>();
|
||||
const selectedMenuItem = ref<MenuItem>();
|
||||
const selectedParentMenuItem = ref<MenuItem>();
|
||||
const loading = ref(false);
|
||||
const menuListRef = ref();
|
||||
const menuItemEditingModal = ref();
|
||||
|
@ -66,6 +67,16 @@ const handleOpenEditingModal = (menuItem: MenuTreeItem) => {
|
|||
menuItemEditingModal.value = true;
|
||||
};
|
||||
|
||||
const handleOpenCreateByParentModal = (menuItem: MenuTreeItem) => {
|
||||
selectedParentMenuItem.value = convertMenuTreeItemToMenuItem(menuItem);
|
||||
menuItemEditingModal.value = true;
|
||||
};
|
||||
|
||||
const onMenuItemEditingModalClose = () => {
|
||||
selectedParentMenuItem.value = undefined;
|
||||
selectedMenuItem.value = undefined;
|
||||
};
|
||||
|
||||
const onMenuItemSaved = async (menuItem: MenuItem) => {
|
||||
const menuToUpdate = cloneDeep(selectedMenu.value);
|
||||
|
||||
|
@ -143,7 +154,9 @@ const handleDelete = async (menuItem: MenuTreeItem) => {
|
|||
<MenuItemEditingModal
|
||||
v-model:visible="menuItemEditingModal"
|
||||
:menu-item="selectedMenuItem"
|
||||
@close="selectedMenuItem = null"
|
||||
:parent-menu-item="selectedParentMenuItem"
|
||||
:menu="selectedMenu"
|
||||
@close="onMenuItemEditingModalClose"
|
||||
@saved="onMenuItemSaved"
|
||||
/>
|
||||
<VPageHeader title="菜单">
|
||||
|
@ -214,6 +227,7 @@ const handleDelete = async (menuItem: MenuTreeItem) => {
|
|||
@change="handleUpdateInBatch"
|
||||
@delete="handleDelete"
|
||||
@open-editing="handleOpenEditingModal"
|
||||
@open-create-by-parent="handleOpenCreateByParentModal"
|
||||
/>
|
||||
</VCard>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { VButton, VModal, VSpace } from "@halo-dev/components";
|
||||
import SubmitButton from "@/components/button/SubmitButton.vue";
|
||||
import { computed, ref, watch } from "vue";
|
||||
import type { MenuItem, Post, SinglePage } from "@halo-dev/api-client";
|
||||
import type { Menu, MenuItem, Post, SinglePage } from "@halo-dev/api-client";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { apiClient } from "@/utils/api-client";
|
||||
import { reset } from "@formkit/core";
|
||||
|
@ -10,15 +10,20 @@ import cloneDeep from "lodash.clonedeep";
|
|||
import { usePostCategory } from "@/modules/contents/posts/categories/composables/use-post-category";
|
||||
import { usePostTag } from "@/modules/contents/posts/tags/composables/use-post-tag";
|
||||
import { setFocus } from "@/formkit/utils/focus";
|
||||
import type { FormKitOptionsProp } from "@formkit/inputs";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
visible: boolean;
|
||||
menuItem: MenuItem | null;
|
||||
menu?: Menu;
|
||||
parentMenuItem: MenuItem;
|
||||
menuItem?: MenuItem;
|
||||
}>(),
|
||||
{
|
||||
visible: false,
|
||||
menuItem: null,
|
||||
menu: undefined,
|
||||
parentMenuItem: undefined,
|
||||
menuItem: undefined,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -42,6 +47,8 @@ const initialFormState: MenuItem = {
|
|||
},
|
||||
};
|
||||
|
||||
const menuItemMap = ref<FormKitOptionsProp>();
|
||||
const selectedParentMenuItem = ref<string>("");
|
||||
const formState = ref<MenuItem>(cloneDeep(initialFormState));
|
||||
const saving = ref(false);
|
||||
|
||||
|
@ -49,6 +56,25 @@ const isUpdateMode = computed(() => {
|
|||
return !!formState.value.metadata.creationTimestamp;
|
||||
});
|
||||
|
||||
const handleFetchMenuItems = async () => {
|
||||
try {
|
||||
const { data } = await apiClient.extension.menuItem.listv1alpha1MenuItem({
|
||||
fieldSelector: [`name=(${props.menu?.spec.menuItems?.join(",")})`],
|
||||
});
|
||||
menuItemMap.value = [
|
||||
{ label: "无", value: undefined },
|
||||
...data.items.map((menuItem) => {
|
||||
return {
|
||||
label: menuItem.status?.displayName as string,
|
||||
value: menuItem.metadata.name,
|
||||
};
|
||||
}),
|
||||
];
|
||||
} catch (error) {
|
||||
console.log("Failed to fetch menu items", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveMenuItem = async () => {
|
||||
try {
|
||||
saving.value = true;
|
||||
|
@ -81,6 +107,25 @@ const handleSaveMenuItem = async () => {
|
|||
await apiClient.extension.menuItem.createv1alpha1MenuItem({
|
||||
menuItem: formState.value,
|
||||
});
|
||||
|
||||
// if parent menu item is selected, add the new menu item to the parent menu item
|
||||
if (selectedParentMenuItem.value) {
|
||||
const { data: menuItemToUpdate } =
|
||||
await apiClient.extension.menuItem.getv1alpha1MenuItem({
|
||||
name: selectedParentMenuItem.value,
|
||||
});
|
||||
|
||||
menuItemToUpdate.spec.children = [
|
||||
...(menuItemToUpdate.spec.children || []),
|
||||
data.metadata.name,
|
||||
];
|
||||
|
||||
await apiClient.extension.menuItem.updatev1alpha1MenuItem({
|
||||
name: menuItemToUpdate.metadata.name,
|
||||
menuItem: menuItemToUpdate,
|
||||
});
|
||||
}
|
||||
|
||||
onVisibleChange(false);
|
||||
emit("saved", data);
|
||||
}
|
||||
|
@ -103,6 +148,7 @@ const handleResetForm = () => {
|
|||
formState.value.metadata.name = uuid();
|
||||
selectedMenuItemSource.value = menuItemSources[0].value;
|
||||
selectedRef.value = "";
|
||||
selectedParentMenuItem.value = "";
|
||||
reset("menuitem-form");
|
||||
};
|
||||
|
||||
|
@ -110,6 +156,7 @@ watch(
|
|||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
selectedParentMenuItem.value = props.parentMenuItem?.metadata.name;
|
||||
setFocus("displayNameInput");
|
||||
|
||||
if (!props.menuItem) {
|
||||
|
@ -287,6 +334,7 @@ watch(
|
|||
() => props.visible,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
handleFetchMenuItems();
|
||||
handleFetchCategories();
|
||||
handleFetchTags();
|
||||
handleFetchPosts();
|
||||
|
@ -310,6 +358,14 @@ watch(
|
|||
:config="{ validationVisibility: 'submit' }"
|
||||
@submit="handleSaveMenuItem"
|
||||
>
|
||||
<FormKit
|
||||
v-if="!isUpdateMode && menuItemMap"
|
||||
v-model="selectedParentMenuItem"
|
||||
label="上级菜单项"
|
||||
type="select"
|
||||
:options="menuItemMap"
|
||||
/>
|
||||
|
||||
<FormKit
|
||||
v-model="selectedMenuItemSource"
|
||||
:options="menuItemSources"
|
||||
|
@ -317,8 +373,7 @@ watch(
|
|||
label="类型"
|
||||
type="select"
|
||||
@change="onMenuItemSourceChange"
|
||||
>
|
||||
</FormKit>
|
||||
/>
|
||||
|
||||
<FormKit
|
||||
v-if="selectedMenuItemSource === 'custom'"
|
||||
|
@ -328,7 +383,8 @@ watch(
|
|||
type="text"
|
||||
name="displayName"
|
||||
validation="required"
|
||||
></FormKit>
|
||||
/>
|
||||
|
||||
<FormKit
|
||||
v-if="selectedMenuItemSource === 'custom'"
|
||||
v-model="formState.spec.href"
|
||||
|
@ -336,7 +392,7 @@ watch(
|
|||
type="text"
|
||||
name="href"
|
||||
validation="required"
|
||||
></FormKit>
|
||||
/>
|
||||
|
||||
<FormKit
|
||||
v-if="selectedMenuItemSource === 'post'"
|
||||
|
@ -345,7 +401,7 @@ watch(
|
|||
type="select"
|
||||
:options="postMap"
|
||||
validation="required"
|
||||
></FormKit>
|
||||
/>
|
||||
|
||||
<FormKit
|
||||
v-if="selectedMenuItemSource === 'singlePage'"
|
||||
|
@ -354,7 +410,7 @@ watch(
|
|||
type="select"
|
||||
:options="singlePageMap"
|
||||
validation="required"
|
||||
></FormKit>
|
||||
/>
|
||||
|
||||
<FormKit
|
||||
v-if="selectedMenuItemSource === 'tag'"
|
||||
|
@ -363,7 +419,7 @@ watch(
|
|||
type="select"
|
||||
:options="tagMap"
|
||||
validation="required"
|
||||
></FormKit>
|
||||
/>
|
||||
|
||||
<FormKit
|
||||
v-if="selectedMenuItemSource === 'category'"
|
||||
|
@ -372,7 +428,7 @@ watch(
|
|||
type="select"
|
||||
:options="categoryMap"
|
||||
validation="required"
|
||||
></FormKit>
|
||||
/>
|
||||
</FormKit>
|
||||
<template #footer>
|
||||
<VSpace>
|
||||
|
|
|
@ -26,6 +26,7 @@ withDefaults(
|
|||
const emit = defineEmits<{
|
||||
(event: "change"): void;
|
||||
(event: "open-editing", menuItem: MenuTreeItem): void;
|
||||
(event: "open-create-by-parent", menuItem: MenuTreeItem): void;
|
||||
(event: "delete", menuItem: MenuTreeItem): void;
|
||||
}>();
|
||||
|
||||
|
@ -39,6 +40,10 @@ function onOpenEditingModal(menuItem: MenuTreeItem) {
|
|||
emit("open-editing", menuItem);
|
||||
}
|
||||
|
||||
function onOpenCreateByParentModal(menuItem: MenuTreeItem) {
|
||||
emit("open-create-by-parent", menuItem);
|
||||
}
|
||||
|
||||
function onDelete(menuItem: MenuTreeItem) {
|
||||
emit("delete", menuItem);
|
||||
}
|
||||
|
@ -114,6 +119,14 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) {
|
|||
>
|
||||
修改
|
||||
</VButton>
|
||||
<VButton
|
||||
v-close-popper
|
||||
block
|
||||
type="default"
|
||||
@click="onOpenCreateByParentModal(menuItem)"
|
||||
>
|
||||
添加子菜单项
|
||||
</VButton>
|
||||
<VButton
|
||||
v-close-popper
|
||||
block
|
||||
|
@ -130,6 +143,7 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) {
|
|||
@change="onChange"
|
||||
@delete="onDelete"
|
||||
@open-editing="onOpenEditingModal"
|
||||
@open-create-by-parent="onOpenCreateByParentModal"
|
||||
/>
|
||||
</li>
|
||||
</template>
|
||||
|
|
Loading…
Reference in New Issue