Skip to content

Commit

Permalink
feat: add BaseSearchMenu component to search global menu
Browse files Browse the repository at this point in the history
  • Loading branch information
flingyp committed Jun 9, 2023
1 parent 915db72 commit 3604786
Show file tree
Hide file tree
Showing 18 changed files with 205 additions and 31 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"colord": "^2.9.3",
"driver.js": "^0.9.8",
"echarts": "^5.4.2",
"fuse.js": "^6.6.2",
"mitt": "^3.0.0",
"mockjs": "^1.1.0",
"naive-ui": "^2.34.4",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/layout/components/GlobalBreadCrumbMenu.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { useDeepClone } from '@flypeng/tool/browser'
import { MenuOption } from 'naive-ui'
import { VAdmireMenuOption } from 'naive-ui'
import { transformMenu } from '~/utils'
const { t } = useI18n()
Expand All @@ -9,7 +9,7 @@ const routeMenuStore = useRouteMenuStore()
const { breadCrumbMenus } = storeToRefs(routeMenuStore)
const transformMenuList = computed(() => {
const menuOptions = useDeepClone(breadCrumbMenus.value) as MenuOption[]
const menuOptions = useDeepClone(breadCrumbMenus.value) as VAdmireMenuOption[]
const i18nMenu = menuOptions.map((menu) => transformMenu(menu, t))
return i18nMenu
})
Expand Down
20 changes: 10 additions & 10 deletions src/layout/components/GlobalMenu.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { MenuOption } from 'naive-ui'
import { VAdmireMenuOption } from 'naive-ui'
import { useDeepClone } from '@flypeng/tool/browser'
import { transformMenu } from '~/utils'
Expand All @@ -11,7 +11,7 @@ const { breadCrumbMenus, vadmireMenu } = storeToRefs(routeMenuStore)
interface GlobalMenuProps {
mode?: 'vertical' | 'horizontal'
menuOptions: MenuOption[]
menuOptions: VAdmireMenuOption[]
}
const props = withDefaults(defineProps<GlobalMenuProps>(), {
Expand All @@ -20,23 +20,23 @@ const props = withDefaults(defineProps<GlobalMenuProps>(), {
})
const transformMenuList = computed(() => {
const menuOptions = useDeepClone(props.menuOptions) as MenuOption[]
const menuOptions = useDeepClone(props.menuOptions) as VAdmireMenuOption[]
const i18nMenu = menuOptions.map((menu) => transformMenu(menu, t))
return i18nMenu
})
// route key whether include from menu
const isIncludeMenuByKey = (key: string, menu: MenuOption): boolean => {
const isIncludeMenuByKey = (key: string, menu: VAdmireMenuOption): boolean => {
if (key === menu.key) return true
if (menu.children) return menu.children.some((item) => isIncludeMenuByKey(key, item))
return false
}
// get breadcrumb menu
const getBreadCrumbMenu = (key: string, menu: MenuOption) => {
const breadCrumbMenu: MenuOption[] = []
const getBreadCrumbMenu = (key: string, menu: VAdmireMenuOption) => {
const breadCrumbMenu: VAdmireMenuOption[] = []
breadCrumbMenu.push(menu)
const getMenuByKey = (item: MenuOption) => {
const getMenuByKey = (item: VAdmireMenuOption) => {
if (item.children) {
item.children.forEach((child) => {
if (isIncludeMenuByKey(key, child)) {
Expand All @@ -51,8 +51,8 @@ const getBreadCrumbMenu = (key: string, menu: MenuOption) => {
}
// generate breadcrumb menu
const createBreadCrumbMenu = (key: string, menus: MenuOption[]) => {
let parentMenu: MenuOption = {}
const createBreadCrumbMenu = (key: string, menus: VAdmireMenuOption[]) => {
let parentMenu: VAdmireMenuOption = {}
for (let i = 0; i < menus.length; i++) {
const isIncludeRouteKey = isIncludeMenuByKey(key, menus[i])
if (isIncludeRouteKey) parentMenu = menus[i]
Expand All @@ -71,7 +71,7 @@ watchEffect(() => {
})
// click menu
const clickMenu = (key: string, menu: MenuOption) => {
const clickMenu = (key: string, menu: VAdmireMenuOption) => {
if (menu.link === 'EXTERNAL_LINK' && menu.url) {
window.open(menu.url as string)
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/layout/components/layout/BaseButtonTabBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { Swiper, SwiperSlide } from 'swiper/vue'
import { Icon } from '@iconify/vue'
import { useDeepClone } from '@flypeng/tool/browser'
import { MenuOption } from 'naive-ui'
import { VAdmireMenuOption } from 'naive-ui'
import { transformMenu } from '~/utils'
import 'swiper/css'
Expand All @@ -13,7 +13,7 @@ const routeMenuStore = useRouteMenuStore()
const { tabMenuKeys, vadmireTabMenu } = storeToRefs(routeMenuStore)
const transformMenuList = computed(() => {
const menuOptions = useDeepClone(vadmireTabMenu.value) as MenuOption[]
const menuOptions = useDeepClone(vadmireTabMenu.value) as VAdmireMenuOption[]
const i18nMenu = menuOptions.map((menu) => transformMenu(menu, t))
return i18nMenu
})
Expand Down
30 changes: 30 additions & 0 deletions src/layout/components/layout/BaseSearchItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script setup lang="ts">
import { VAdmireMenuOption } from 'naive-ui'
import RenderIconify from '~/components/common/RenderIconify.vue'
interface BaseSearchItemProps {
menu: VAdmireMenuOption
}
const props = defineProps<BaseSearchItemProps>()
const emit = defineEmits(['close'])
const router = useRouter()
const navigatorTo = () => {
router.push({ name: (props.menu.key as string) })
emit('close')
}
</script>

<template>
<div
class="flex items-center p-1 cursor-pointer hover:text-primary"
@click="navigatorTo"
>
<RenderIconify
:icon="menu.iconLabel"
class="w-4 h-4 mr-1"
/>
<span>{{ menu.label }}</span>
</div>
</template>
101 changes: 101 additions & 0 deletions src/layout/components/layout/BaseSearchMenu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<script setup lang="ts">
import { useDebounce } from '@flypeng/tool/browser'
import Fuse from 'fuse.js'
import { VAdmireMenuOption } from 'naive-ui'
import RenderIconify from '~/components/common/RenderIconify.vue'
import { transformMenu } from '~/utils'
import BaseSearchItem from './BaseSearchItem.vue'
const { t } = useI18n()
const { defaultLocales } = storeToRefs(useVAdmireConfigStore())
const { vadmireChildrenMenuByFlat } = storeToRefs(useRouteMenuStore())
const placeholder = computed(() => t('header.searchPlaceholder'))
const isShowSearchMenu = ref(false)
const searchKeyWord = ref('')
watch(searchKeyWord, (val) => {
if (val.length > 0) isShowSearchMenu.value = true
else isShowSearchMenu.value = false
})
const showSearchMenu = () => {
if (searchKeyWord.value === '') return
isShowSearchMenu.value = true
}
const closeSearchMenu = (type: 'CLICK' | 'BLUR') => {
if (type === 'CLICK') {
isShowSearchMenu.value = false
searchKeyWord.value = ''
} else if (type === 'BLUR') {
isShowSearchMenu.value = false
}
}
const inputKeywordRef = ref<HTMLInputElement>()
onClickOutside(inputKeywordRef, () => {
if (!isShowSearchMenu.value) return
isShowSearchMenu.value = false
})
const searchMenuList = ref<VAdmireMenuOption[]>([])
const transformVadmireChildrenMenuByFlat = computed(() => vadmireChildrenMenuByFlat.value.map((item) => {
const newMenu = transformMenu(item, t)
return newMenu
}))
const options = {
keys: ['label'],
threshold: 0.3,
}
let fuse: Fuse<VAdmireMenuOption>
watch(defaultLocales, () => {
nextTick(() => {
fuse = new Fuse(transformVadmireChildrenMenuByFlat.value, options)
})
}, { immediate: true })
const inputKeyword = useDebounce(() => {
// 去除字符串里的空格
searchKeyWord.value = searchKeyWord.value.replace(/\s*/g, '')
const searchResult = fuse.search(searchKeyWord.value)
// @ts-ignore
searchMenuList.value = searchResult.map((data) => data.item)
}, 200)
</script>
<template>
<div class="flex items-center">
<NInput
ref="inputKeywordRef"
v-model:value="searchKeyWord"
:placeholder="placeholder"
@input="inputKeyword"
@focus="showSearchMenu"
@blur="closeSearchMenu('BLUR')"
>
<template #prefix>
<RenderIconify icon="carbon:search" />
</template>
</NInput>

<Transition name="fade-scale">
<div
v-show="isShowSearchMenu && searchMenuList.length"
class="
absolute w-64 z-50 p-2 top-[6%] rounded space-y-1
border border-vBorderLight dark:border-vBorderDark bg-vPageBgColor dark:bg-vPageBgDarkColor
"
>
<template
v-for="item in searchMenuList"
:key="item.key"
>
<BaseSearchItem
:menu="item"
@close="closeSearchMenu('CLICK')"
/>
</template>
</div>
</Transition>
</div>
</template>
2 changes: 2 additions & 0 deletions src/layout/mode/sider-menu-mode/SiderMenuHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import BaseSwitchLocale from '~/layout/components/layout/BaseSwitchLocale.vue'
import GlobalBreadCrumbMenu from '~/layout/components/GlobalBreadCrumbMenu.vue'
import BasePersonalCenter from '~/layout/components/layout/BasePersonalCenter.vue'
import BaseCollapsedSiderbar from '~/layout/components/layout/BaseCollapsedSiderbar.vue'
import BaseSearchMenu from '~/layout/components/layout/BaseSearchMenu.vue'
</script>

Expand All @@ -16,6 +17,7 @@ import BaseCollapsedSiderbar from '~/layout/components/layout/BaseCollapsedSider
<GlobalBreadCrumbMenu />
</div>
<div class="h-full flex items-center">
<BaseSearchMenu />
<BaseGithubIcon />
<BaseFullScreen />
<BaseSwitchLocale />
Expand Down
2 changes: 2 additions & 0 deletions src/layout/mode/sider-mix-menu-mode/SiderMixMenuHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import BaseSwitchLocale from '~/layout/components/layout/BaseSwitchLocale.vue'
import GlobalBreadCrumbMenu from '~/layout/components/GlobalBreadCrumbMenu.vue'
import BasePersonalCenter from '~/layout/components/layout/BasePersonalCenter.vue'
import BaseCollapsedSiderbar from '~/layout/components/layout/BaseCollapsedSiderbar.vue'
import BaseSearchMenu from '~/layout/components/layout/BaseSearchMenu.vue'
</script>

<template>
Expand All @@ -16,6 +17,7 @@ import BaseCollapsedSiderbar from '~/layout/components/layout/BaseCollapsedSider
<GlobalBreadCrumbMenu />
</div>
<div class="flex">
<BaseSearchMenu />
<BaseGithubIcon />
<BaseFullScreen />
<BaseSwitchLocale />
Expand Down
2 changes: 2 additions & 0 deletions src/layout/mode/top-menu-mode/TopMenuHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import BaseGithubIcon from '~/layout/components/layout/BaseGithubIcon.vue'
import BaseSettingIcon from '~/layout/components/layout/BaseSettingIcon.vue'
import BaseSwitchLocale from '~/layout/components/layout/BaseSwitchLocale.vue'
import BasePersonalCenter from '~/layout/components/layout/BasePersonalCenter.vue'
import BaseSearchMenu from '~/layout/components/layout/BaseSearchMenu.vue'
const routeMenuStore = useRouteMenuStore()
</script>
Expand All @@ -24,6 +25,7 @@ const routeMenuStore = useRouteMenuStore()
/>
</div>
<div class="h-full flex items-center">
<BaseSearchMenu />
<BaseGithubIcon />
<BaseFullScreen />
<BaseSwitchLocale />
Expand Down
3 changes: 3 additions & 0 deletions src/locales/cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,8 @@
"transitionSwitch": "过渡切换",
"copySystemConfig": "拷贝系统配置",
"resetSystemConfig": "重置系统配置"
},
"header": {
"searchPlaceholder": "搜索关键字"
}
}
3 changes: 3 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,8 @@
"transitionSwitch": "Transition Switch",
"copySystemConfig": "Copy System Config",
"resetSystemConfig": "Reset System Config"
},
"header": {
"searchPlaceholder": "Search for keywords"
}
}
3 changes: 3 additions & 0 deletions src/locales/kr.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,8 @@
"transitionSwitch": "전환 효과",
"copySystemConfig": "시스템 설정을 복사합니다",
"resetSystemConfig": "시스템 설정을 재설정합니다"
},
"header": {
"searchPlaceholder": "검색 키워드"
}
}
7 changes: 7 additions & 0 deletions src/naive-ui.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { MenuOption } from 'naive-ui'

declare module 'naive-ui' {
type VAdmireMenuOption= MenuOption & {
iconLabel?: string
}
}
12 changes: 6 additions & 6 deletions src/router/utils/generate.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { useCommonType, useDeepClone } from '@flypeng/tool/browser'
import { MenuOption } from 'naive-ui'
import { VAdmireMenuOption } from 'naive-ui'
import { VAdmireRoute } from '../types'

/**
* Generate system menu
* @param routes
* @returns
*/
export const generateSystemMenu = (routes: VAdmireRoute[]): MenuOption[] => {
const menus: MenuOption[] = []
export const generateSystemMenu = (routes: VAdmireRoute[]): VAdmireMenuOption[] => {
const menus: VAdmireMenuOption[] = []

for (let i = 0; i < routes.length; i++) {
const route = routes[i]
const { meta } = route

if (useCommonType.isUndefined(meta?.isShow) || meta?.isShow === true) {
const menu: MenuOption = {}
const menu: VAdmireMenuOption = {}
let handleRoute = useDeepClone(route) as VAdmireRoute

if (handleRoute.children && handleRoute.children.length > 0) {
Expand Down Expand Up @@ -50,8 +50,8 @@ export const generateSystemMenu = (routes: VAdmireRoute[]): MenuOption[] => {
* @param menus
* @returns
*/
export const sortSystemMenu = (menus: MenuOption[]): MenuOption[] => {
const sortMenus = useDeepClone(menus) as MenuOption[]
export const sortSystemMenu = (menus: VAdmireMenuOption[]): VAdmireMenuOption[] => {
const sortMenus = useDeepClone(menus) as VAdmireMenuOption[]
sortMenus.forEach((menu) => {
if (menu.children && menu.children.length > 0) {
menu.children = sortSystemMenu(menu.children)
Expand Down

0 comments on commit 3604786

Please sign in to comment.