mirror of https://github.com/halo-dev/halo
				
				
				
			refactor: improve code base of menu-related (#5957)
#### What type of PR is this? /area ui /kind improvement /milestone 2.16.x #### What this PR does / why we need it: 优化菜单管理相关的 UI 代码。 1. 使用 vue-draggable-plus 库代替 vuedraggable 库实现拖拽排序。vue-draggable-plus 是在 https://github.com/halo-dev/halo/pull/5914 中引入,替换的原因是 vuedraggable 库已经不再积极维护。 2. 改进菜单和菜单项编辑表单的逻辑,清理无用代码。 #### Special notes for your reviewer: 需要测试: 1. 测试菜单项拖拽排序功能是否表现正常。 2. 测试新增菜单、菜单项和修改菜单、菜单项功能是否表现正常。 #### Does this PR introduce a user-facing change? ```release-note None ```pull/5936/head^2
							parent
							
								
									2feaa20d05
								
							
						
					
					
						commit
						d29a377bbd
					
				|  | @ -1,15 +1,15 @@ | |||
| <script lang="ts" setup> | ||||
| import { | ||||
|   Dialog, | ||||
|   IconAddCircle, | ||||
|   IconListSettings, | ||||
|   Dialog, | ||||
|   Toast, | ||||
|   VButton, | ||||
|   VCard, | ||||
|   VEmpty, | ||||
|   VLoading, | ||||
|   VPageHeader, | ||||
|   VSpace, | ||||
|   VLoading, | ||||
|   Toast, | ||||
| } from "@halo-dev/components"; | ||||
| import MenuItemEditingModal from "./components/MenuItemEditingModal.vue"; | ||||
| import MenuItemListItem from "./components/MenuItemListItem.vue"; | ||||
|  | @ -90,6 +90,7 @@ const handleOpenCreateByParentModal = (menuItem: MenuTreeItem) => { | |||
| const onMenuItemEditingModalClose = () => { | ||||
|   selectedParentMenuItem.value = undefined; | ||||
|   selectedMenuItem.value = undefined; | ||||
|   menuItemEditingModal.value = false; | ||||
| }; | ||||
| 
 | ||||
| const onMenuItemSaved = async (menuItem: MenuItem) => { | ||||
|  | @ -115,10 +116,13 @@ const onMenuItemSaved = async (menuItem: MenuItem) => { | |||
|   await refetch(); | ||||
| }; | ||||
| 
 | ||||
| const batchUpdating = ref(false); | ||||
| 
 | ||||
| const handleUpdateInBatch = useDebounceFn(async () => { | ||||
|   const menuTreeItemsToUpdate = resetMenuItemsTreePriority(menuTreeItems.value); | ||||
|   const menuItemsToUpdate = convertTreeToMenuItems(menuTreeItemsToUpdate); | ||||
|   try { | ||||
|     batchUpdating.value = true; | ||||
|     const promises = menuItemsToUpdate.map((menuItem) => | ||||
|       apiClient.extension.menuItem.updatev1alpha1MenuItem({ | ||||
|         name: menuItem.metadata.name, | ||||
|  | @ -131,6 +135,7 @@ const handleUpdateInBatch = useDebounceFn(async () => { | |||
|   } finally { | ||||
|     await queryClient.invalidateQueries({ queryKey: ["menus"] }); | ||||
|     await refetch(); | ||||
|     batchUpdating.value = false; | ||||
|   } | ||||
| }, 300); | ||||
| 
 | ||||
|  | @ -180,7 +185,7 @@ const handleDelete = async (menuItem: MenuTreeItem) => { | |||
| </script> | ||||
| <template> | ||||
|   <MenuItemEditingModal | ||||
|     v-model:visible="menuItemEditingModal" | ||||
|     v-if="menuItemEditingModal && selectedMenu" | ||||
|     :menu-item="selectedMenuItem" | ||||
|     :parent-menu-item="selectedParentMenuItem" | ||||
|     :menu="selectedMenu" | ||||
|  | @ -194,7 +199,7 @@ const handleDelete = async (menuItem: MenuTreeItem) => { | |||
|   </VPageHeader> | ||||
|   <div class="m-0 md:m-4"> | ||||
|     <div class="flex flex-col gap-4 sm:flex-row"> | ||||
|       <div class="w-96"> | ||||
|       <div class="w-96 flex-none"> | ||||
|         <MenuList v-model:selected-menu="selectedMenu" /> | ||||
|       </div> | ||||
|       <div class="flex-1"> | ||||
|  | @ -251,7 +256,10 @@ const handleDelete = async (menuItem: MenuTreeItem) => { | |||
|           </Transition> | ||||
|           <Transition v-else appear name="fade"> | ||||
|             <MenuItemListItem | ||||
|               :menu-tree-items="menuTreeItems" | ||||
|               v-model="menuTreeItems" | ||||
|               :class="{ | ||||
|                 'cursor-progress opacity-60': batchUpdating, | ||||
|               }" | ||||
|               @change="handleUpdateInBatch" | ||||
|               @delete="handleDelete" | ||||
|               @open-editing="handleOpenEditingModal" | ||||
|  |  | |||
|  | @ -2,33 +2,32 @@ | |||
| import { Toast, VButton, VModal, VSpace } from "@halo-dev/components"; | ||||
| import SubmitButton from "@/components/button/SubmitButton.vue"; | ||||
| import type { Menu } from "@halo-dev/api-client"; | ||||
| import { computed, ref, watch } from "vue"; | ||||
| import { onMounted, ref, toRaw } from "vue"; | ||||
| import { apiClient } from "@/utils/api-client"; | ||||
| import { reset } from "@formkit/core"; | ||||
| import { cloneDeep } from "lodash-es"; | ||||
| import { setFocus } from "@/formkit/utils/focus"; | ||||
| import { useI18n } from "vue-i18n"; | ||||
| import { useQueryClient } from "@tanstack/vue-query"; | ||||
| 
 | ||||
| const props = withDefaults( | ||||
|   defineProps<{ | ||||
|     visible: boolean; | ||||
|     menu?: Menu; | ||||
|   }>(), | ||||
|   { | ||||
|     visible: false, | ||||
|     menu: undefined, | ||||
|   } | ||||
| ); | ||||
| 
 | ||||
| const emit = defineEmits<{ | ||||
|   (event: "update:visible", visible: boolean): void; | ||||
|   (event: "close"): void; | ||||
|   (event: "created", menu: Menu): void; | ||||
| }>(); | ||||
| 
 | ||||
| const queryClient = useQueryClient(); | ||||
| const { t } = useI18n(); | ||||
| 
 | ||||
| const initialFormState: Menu = { | ||||
| const modal = ref(); | ||||
| 
 | ||||
| const formState = ref<Menu>({ | ||||
|   spec: { | ||||
|     displayName: "", | ||||
|     menuItems: [], | ||||
|  | @ -39,25 +38,18 @@ const initialFormState: Menu = { | |||
|     name: "", | ||||
|     generateName: "menu-", | ||||
|   }, | ||||
| }; | ||||
| }); | ||||
| 
 | ||||
| const formState = ref<Menu>(cloneDeep(initialFormState)); | ||||
| const saving = ref(false); | ||||
| 
 | ||||
| const isUpdateMode = computed(() => { | ||||
|   return !!formState.value.metadata.creationTimestamp; | ||||
| }); | ||||
| 
 | ||||
| const modalTitle = computed(() => { | ||||
|   return isUpdateMode.value | ||||
| const modalTitle = props.menu | ||||
|   ? t("core.menu.menu_editing_modal.titles.update") | ||||
|   : t("core.menu.menu_editing_modal.titles.create"); | ||||
| }); | ||||
| 
 | ||||
| const handleCreateMenu = async () => { | ||||
| const handleSaveMenu = async () => { | ||||
|   try { | ||||
|     saving.value = true; | ||||
|     if (isUpdateMode.value) { | ||||
|     if (props.menu) { | ||||
|       await apiClient.extension.menu.updatev1alpha1Menu({ | ||||
|         name: formState.value.metadata.name, | ||||
|         menu: formState.value, | ||||
|  | @ -68,7 +60,10 @@ const handleCreateMenu = async () => { | |||
|       }); | ||||
|       emit("created", data); | ||||
|     } | ||||
|     onVisibleChange(false); | ||||
| 
 | ||||
|     queryClient.invalidateQueries({ queryKey: ["menus"] }); | ||||
| 
 | ||||
|     modal.value.close(); | ||||
| 
 | ||||
|     Toast.success(t("core.common.toast.save_success")); | ||||
|   } catch (e) { | ||||
|  | @ -78,53 +73,21 @@ const handleCreateMenu = async () => { | |||
|   } | ||||
| }; | ||||
| 
 | ||||
| const onVisibleChange = (visible: boolean) => { | ||||
|   emit("update:visible", visible); | ||||
|   if (!visible) { | ||||
|     emit("close"); | ||||
| onMounted(() => { | ||||
|   if (props.menu) { | ||||
|     formState.value = toRaw(props.menu); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const handleResetForm = () => { | ||||
|   formState.value = cloneDeep(initialFormState); | ||||
|   reset("menu-form"); | ||||
| }; | ||||
| 
 | ||||
| watch( | ||||
|   () => props.visible, | ||||
|   (visible) => { | ||||
|     if (visible) { | ||||
|   setFocus("menuDisplayNameInput"); | ||||
|     } else { | ||||
|       handleResetForm(); | ||||
|     } | ||||
|   } | ||||
| ); | ||||
| 
 | ||||
| watch( | ||||
|   () => props.menu, | ||||
|   (menu) => { | ||||
|     if (menu) { | ||||
|       formState.value = cloneDeep(menu); | ||||
|     } else { | ||||
|       handleResetForm(); | ||||
|     } | ||||
|   } | ||||
| ); | ||||
| }); | ||||
| </script> | ||||
| <template> | ||||
|   <VModal | ||||
|     :visible="visible" | ||||
|     :width="500" | ||||
|     :title="modalTitle" | ||||
|     @update:visible="onVisibleChange" | ||||
|   > | ||||
|   <VModal ref="modal" :width="500" :title="modalTitle" @close="emit('close')"> | ||||
|     <FormKit | ||||
|       id="menu-form" | ||||
|       name="menu-form" | ||||
|       type="form" | ||||
|       :config="{ validationVisibility: 'submit' }" | ||||
|       @submit="handleCreateMenu" | ||||
|       @submit="handleSaveMenu" | ||||
|     > | ||||
|       <FormKit | ||||
|         id="menuDisplayNameInput" | ||||
|  | @ -138,14 +101,13 @@ watch( | |||
|     <template #footer> | ||||
|       <VSpace> | ||||
|         <SubmitButton | ||||
|           v-if="visible" | ||||
|           :loading="saving" | ||||
|           type="secondary" | ||||
|           :text="$t('core.common.buttons.submit')" | ||||
|           @submit="$formkit.submit('menu-form')" | ||||
|         > | ||||
|         </SubmitButton> | ||||
|         <VButton @click="onVisibleChange(false)"> | ||||
|         <VButton @click="modal.close()"> | ||||
|           {{ $t("core.common.buttons.cancel_and_shortcut") }} | ||||
|         </VButton> | ||||
|       </VSpace> | ||||
|  |  | |||
|  | @ -1,39 +1,35 @@ | |||
| <script lang="ts" setup> | ||||
| import { Toast, VButton, VModal, VSpace } from "@halo-dev/components"; | ||||
| import SubmitButton from "@/components/button/SubmitButton.vue"; | ||||
| import { computed, nextTick, ref, watch } from "vue"; | ||||
| import { computed, nextTick, onMounted, ref, toRaw } from "vue"; | ||||
| import type { Menu, MenuItem, Ref } from "@halo-dev/api-client"; | ||||
| import { apiClient } from "@/utils/api-client"; | ||||
| import { reset } from "@formkit/core"; | ||||
| import { cloneDeep } from "lodash-es"; | ||||
| import { setFocus } from "@/formkit/utils/focus"; | ||||
| import AnnotationsForm from "@/components/form/AnnotationsForm.vue"; | ||||
| import { useI18n } from "vue-i18n"; | ||||
| 
 | ||||
| const props = withDefaults( | ||||
|   defineProps<{ | ||||
|     visible: boolean; | ||||
|     menu?: Menu; | ||||
|     menu: Menu; | ||||
|     parentMenuItem: MenuItem; | ||||
|     menuItem?: MenuItem; | ||||
|   }>(), | ||||
|   { | ||||
|     visible: false, | ||||
|     menu: undefined, | ||||
|     parentMenuItem: undefined, | ||||
|     menuItem: undefined, | ||||
|   } | ||||
| ); | ||||
| 
 | ||||
| const emit = defineEmits<{ | ||||
|   (event: "update:visible", visible: boolean): void; | ||||
|   (event: "close"): void; | ||||
|   (event: "saved", menuItem: MenuItem): void; | ||||
| }>(); | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| 
 | ||||
| const initialFormState: MenuItem = { | ||||
| const modal = ref(); | ||||
| const selectedParentMenuItem = ref<string>(""); | ||||
| const formState = ref<MenuItem>({ | ||||
|   spec: { | ||||
|     displayName: "", | ||||
|     href: "", | ||||
|  | @ -47,21 +43,14 @@ const initialFormState: MenuItem = { | |||
|     name: "", | ||||
|     generateName: "menu-item-", | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| const selectedParentMenuItem = ref<string>(""); | ||||
| const formState = ref<MenuItem>(cloneDeep(initialFormState)); | ||||
| }); | ||||
| const saving = ref(false); | ||||
| 
 | ||||
| const isUpdateMode = computed(() => { | ||||
|   return !!formState.value.metadata.creationTimestamp; | ||||
| }); | ||||
| const isUpdateMode = !!props.menuItem; | ||||
| 
 | ||||
| const modalTitle = computed(() => { | ||||
|   return isUpdateMode.value | ||||
| const modalTitle = props.menuItem | ||||
|   ? t("core.menu.menu_item_editing_modal.titles.update") | ||||
|   : t("core.menu.menu_item_editing_modal.titles.create"); | ||||
| }); | ||||
| 
 | ||||
| const annotationsFormRef = ref<InstanceType<typeof AnnotationsForm>>(); | ||||
| 
 | ||||
|  | @ -96,14 +85,13 @@ const handleSaveMenuItem = async () => { | |||
|       formState.value.spec.href = undefined; | ||||
|     } | ||||
| 
 | ||||
|     if (isUpdateMode.value) { | ||||
|     if (isUpdateMode) { | ||||
|       const { data } = | ||||
|         await apiClient.extension.menuItem.updatev1alpha1MenuItem({ | ||||
|           name: formState.value.metadata.name, | ||||
|           menuItem: formState.value, | ||||
|         }); | ||||
| 
 | ||||
|       onVisibleChange(false); | ||||
|       emit("saved", data); | ||||
|     } else { | ||||
|       const { data } = | ||||
|  | @ -129,10 +117,11 @@ const handleSaveMenuItem = async () => { | |||
|         }); | ||||
|       } | ||||
| 
 | ||||
|       onVisibleChange(false); | ||||
|       emit("saved", data); | ||||
|     } | ||||
| 
 | ||||
|     modal.value.close(); | ||||
| 
 | ||||
|     Toast.success(t("core.common.toast.save_success")); | ||||
|   } catch (e) { | ||||
|     console.error("Failed to create menu item", e); | ||||
|  | @ -141,58 +130,6 @@ const handleSaveMenuItem = async () => { | |||
|   } | ||||
| }; | ||||
| 
 | ||||
| const onVisibleChange = (visible: boolean) => { | ||||
|   emit("update:visible", visible); | ||||
|   if (!visible) { | ||||
|     emit("close"); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| const handleResetForm = () => { | ||||
|   formState.value = cloneDeep(initialFormState); | ||||
|   selectedRefKind.value = ""; | ||||
|   selectedRefName.value = ""; | ||||
|   selectedParentMenuItem.value = ""; | ||||
|   reset("menuitem-form"); | ||||
| }; | ||||
| 
 | ||||
| watch( | ||||
|   () => props.visible, | ||||
|   (visible) => { | ||||
|     if (visible) { | ||||
|       selectedParentMenuItem.value = props.parentMenuItem?.metadata.name; | ||||
|       setFocus("displayNameInput"); | ||||
| 
 | ||||
|       if (!props.menuItem) { | ||||
|         selectedRefName.value = ""; | ||||
|       } | ||||
|     } else { | ||||
|       handleResetForm(); | ||||
|     } | ||||
|   } | ||||
| ); | ||||
| 
 | ||||
| watch( | ||||
|   () => props.menuItem, | ||||
|   (menuItem) => { | ||||
|     if (menuItem) { | ||||
|       formState.value = cloneDeep(menuItem); | ||||
| 
 | ||||
|       // Set Ref related | ||||
|       const { targetRef } = formState.value.spec; | ||||
| 
 | ||||
|       if (!targetRef) { | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       selectedRefName.value = targetRef.name; | ||||
|       selectedRefKind.value = targetRef.kind as string; | ||||
|     } else { | ||||
|       handleResetForm(); | ||||
|     } | ||||
|   } | ||||
| ); | ||||
| 
 | ||||
| interface MenuItemRef { | ||||
|   label: string; | ||||
|   inputType?: string; | ||||
|  | @ -268,14 +205,27 @@ const selectedRefName = ref<string>(""); | |||
| const onMenuItemSourceChange = () => { | ||||
|   selectedRefName.value = ""; | ||||
| }; | ||||
| 
 | ||||
| onMounted(() => { | ||||
|   if (props.menuItem) { | ||||
|     formState.value = toRaw(props.menuItem); | ||||
| 
 | ||||
|     // Set Ref related | ||||
|     const { targetRef } = formState.value.spec; | ||||
| 
 | ||||
|     if (targetRef) { | ||||
|       selectedRefName.value = targetRef.name; | ||||
|       selectedRefKind.value = targetRef.kind as string; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   selectedParentMenuItem.value = props.parentMenuItem?.metadata.name; | ||||
| 
 | ||||
|   setFocus("displayNameInput"); | ||||
| }); | ||||
| </script> | ||||
| <template> | ||||
|   <VModal | ||||
|     :visible="visible" | ||||
|     :width="700" | ||||
|     :title="modalTitle" | ||||
|     @update:visible="onVisibleChange" | ||||
|   > | ||||
|   <VModal ref="modal" :width="700" :title="modalTitle" @close="emit('close')"> | ||||
|     <FormKit | ||||
|       id="menuitem-form" | ||||
|       name="menuitem-form" | ||||
|  | @ -295,7 +245,7 @@ const onMenuItemSourceChange = () => { | |||
|           </div> | ||||
|           <div class="mt-5 divide-y divide-gray-100 md:col-span-3 md:mt-0"> | ||||
|             <FormKit | ||||
|               v-if="!isUpdateMode && menu && visible" | ||||
|               v-if="!isUpdateMode" | ||||
|               v-model="selectedParentMenuItem" | ||||
|               :label=" | ||||
|                 $t('core.menu.menu_item_editing_modal.fields.parent.label') | ||||
|  | @ -306,7 +256,7 @@ const onMenuItemSourceChange = () => { | |||
|                 ) | ||||
|               " | ||||
|               type="menuItemSelect" | ||||
|               :menu-items="menu?.spec.menuItems || []" | ||||
|               :menu-items="menu.spec.menuItems || []" | ||||
|             /> | ||||
| 
 | ||||
|             <FormKit | ||||
|  | @ -424,14 +374,13 @@ const onMenuItemSourceChange = () => { | |||
|     <template #footer> | ||||
|       <VSpace> | ||||
|         <SubmitButton | ||||
|           v-if="visible" | ||||
|           :loading="saving" | ||||
|           type="secondary" | ||||
|           :text="$t('core.common.buttons.submit')" | ||||
|           @submit="$formkit.submit('menuitem-form')" | ||||
|         > | ||||
|         </SubmitButton> | ||||
|         <VButton @click="onVisibleChange(false)"> | ||||
|         <VButton @click="modal.close()"> | ||||
|           {{ $t("core.common.buttons.cancel_and_shortcut") }} | ||||
|         </VButton> | ||||
|       </VSpace> | ||||
|  |  | |||
|  | @ -1,29 +1,25 @@ | |||
| <script lang="ts" setup> | ||||
| import { | ||||
|   IconList, | ||||
|   VTag, | ||||
|   VStatusDot, | ||||
|   VDropdownItem, | ||||
|   VEntity, | ||||
|   VEntityField, | ||||
|   VDropdownItem, | ||||
|   VStatusDot, | ||||
|   VTag, | ||||
| } from "@halo-dev/components"; | ||||
| import Draggable from "vuedraggable"; | ||||
| import { ref } from "vue"; | ||||
| import { VueDraggable } from "vue-draggable-plus"; | ||||
| import type { MenuTreeItem } from "@console/modules/interface/menus/utils"; | ||||
| import { usePermission } from "@/utils/permission"; | ||||
| import { useI18n } from "vue-i18n"; | ||||
| import type { PropType } from "vue"; | ||||
| 
 | ||||
| const { currentUserHasPermission } = usePermission(); | ||||
| const { t } = useI18n(); | ||||
| 
 | ||||
| withDefaults( | ||||
|   defineProps<{ | ||||
|     menuTreeItems: MenuTreeItem[]; | ||||
|   }>(), | ||||
|   { | ||||
|     menuTreeItems: () => [], | ||||
|   } | ||||
| ); | ||||
| const menuTreeItems = defineModel({ | ||||
|   type: Array as PropType<MenuTreeItem[]>, | ||||
|   default: [], | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits<{ | ||||
|   (event: "change"): void; | ||||
|  | @ -32,8 +28,6 @@ const emit = defineEmits<{ | |||
|   (event: "delete", menuItem: MenuTreeItem): void; | ||||
| }>(); | ||||
| 
 | ||||
| const isDragging = ref(false); | ||||
| 
 | ||||
| function onChange() { | ||||
|   emit("change"); | ||||
| } | ||||
|  | @ -72,20 +66,16 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) { | |||
| } | ||||
| </script> | ||||
| <template> | ||||
|   <draggable | ||||
|     :list="menuTreeItems" | ||||
|   <VueDraggable | ||||
|     v-model="menuTreeItems" | ||||
|     class="box-border h-full w-full divide-y divide-gray-100" | ||||
|     ghost-class="opacity-50" | ||||
|     group="menu-item" | ||||
|     handle=".drag-element" | ||||
|     item-key="metadata.name" | ||||
|     tag="ul" | ||||
|     @change="onChange" | ||||
|     @end="isDragging = false" | ||||
|     @start="isDragging = true" | ||||
|     @sort="onChange" | ||||
|   > | ||||
|     <template #item="{ element: menuItem }"> | ||||
|       <li> | ||||
|     <li v-for="menuItem in menuTreeItems" :key="menuItem.metadata.name"> | ||||
|       <VEntity> | ||||
|         <template #prepend> | ||||
|           <div | ||||
|  | @ -142,7 +132,7 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) { | |||
|         </template> | ||||
|       </VEntity> | ||||
|       <MenuItemListItem | ||||
|           :menu-tree-items="menuItem.spec.children" | ||||
|         v-model="menuItem.spec.children" | ||||
|         class="pl-10 transition-all duration-300" | ||||
|         @change="onChange" | ||||
|         @delete="onDelete" | ||||
|  | @ -150,6 +140,5 @@ function getMenuItemRefDisplayName(menuItem: MenuTreeItem) { | |||
|         @open-create-by-parent="onOpenCreateByParentModal" | ||||
|       /> | ||||
|     </li> | ||||
|     </template> | ||||
|   </draggable> | ||||
|   </VueDraggable> | ||||
| </template> | ||||
|  |  | |||
|  | @ -1,17 +1,17 @@ | |||
| <script lang="ts" setup> | ||||
| import { | ||||
|   Dialog, | ||||
|   Toast, | ||||
|   VButton, | ||||
|   VCard, | ||||
|   VDropdownItem, | ||||
|   VEmpty, | ||||
|   VSpace, | ||||
|   VStatusDot, | ||||
|   VEntity, | ||||
|   VEntityField, | ||||
|   VTag, | ||||
|   VLoading, | ||||
|   Toast, | ||||
|   VDropdownItem, | ||||
|   VSpace, | ||||
|   VStatusDot, | ||||
|   VTag, | ||||
| } from "@halo-dev/components"; | ||||
| import MenuEditingModal from "./MenuEditingModal.vue"; | ||||
| import { onMounted, ref } from "vue"; | ||||
|  | @ -175,9 +175,9 @@ const handleSetPrimaryMenu = async (menu: Menu) => { | |||
| </script> | ||||
| <template> | ||||
|   <MenuEditingModal | ||||
|     v-model:visible="menuEditingModal" | ||||
|     v-if="menuEditingModal" | ||||
|     :menu="selectedMenuToUpdate" | ||||
|     @close="refetch()" | ||||
|     @close="menuEditingModal = false" | ||||
|     @created="handleSelect" | ||||
|   /> | ||||
|   <VCard :body-class="['!p-0']" :title="$t('core.menu.title')"> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Ryan Wang
						Ryan Wang