-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
401 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -141,4 +141,5 @@ YiLe-Admin | |
|
||
- [ ] 菜单搜索功能 | ||
- [ ] 登录页设计修改 | ||
- [ ] 组件自动引入和Vue导入,引发问题类型报错 | ||
- [-] 组件自动引入和Vue导入,引发问题类型报错 | ||
- [ ] icon解决方案 |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { ref } from "vue"; | ||
|
||
export function useBoolean(initValue = false) { | ||
const bool = ref(initValue); | ||
|
||
function setBool(value: boolean) { | ||
bool.value = value; | ||
} | ||
function setTrue() { | ||
setBool(true); | ||
} | ||
function setFalse() { | ||
setBool(false); | ||
} | ||
function toggle() { | ||
setBool(!bool.value); | ||
} | ||
|
||
return { | ||
bool, | ||
setBool, | ||
setTrue, | ||
setFalse, | ||
toggle | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 0 additions & 113 deletions
113
src/layouts/components/Header/components/SearchMenu.vue
This file was deleted.
Oops, something went wrong.
28 changes: 28 additions & 0 deletions
28
src/layouts/components/Header/components/SearchMenu/components/SearchFooter.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<script setup lang="ts"> | ||
const props = withDefaults(defineProps<{ total: number }>(), { | ||
total: 0 | ||
}); | ||
</script> | ||
|
||
<template> | ||
<div class="text-3.5 flex text-[#333] dark:text-white"> | ||
<span class="flex-center mr3"> | ||
<Kbd> O </Kbd> | ||
确认 | ||
</span> | ||
<span class="flex-center mr3"> | ||
<Kbd> | ||
<el-icon><CaretTop /></el-icon> | ||
</Kbd> | ||
<Kbd> | ||
<el-icon><CaretBottom /></el-icon> | ||
</Kbd> | ||
切换 | ||
</span> | ||
<span class="flex-center mr3"> | ||
<Kbd> ESC </Kbd> | ||
关闭 | ||
</span> | ||
<p v-if="props.total > 0" class="absolute right-5 m0">共{{ props.total }}项</p> | ||
</div> | ||
</template> |
158 changes: 158 additions & 0 deletions
158
src/layouts/components/Header/components/SearchMenu/components/SearchModal.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
<script setup lang="ts"> | ||
import { useRouter } from "vue-router"; | ||
import SearchResult from "./SearchResult.vue"; | ||
import SearchFooter from "./SearchFooter.vue"; | ||
import { useDebounceFn, onKeyStroke } from "@vueuse/core"; | ||
import { useAuthStore } from "@/stores/modules/auth"; | ||
interface Props { | ||
/** 弹窗显隐 */ | ||
value: boolean; | ||
} | ||
interface Emits { | ||
(e: "update:value", val: boolean): void; | ||
} | ||
//手机端 | ||
const emit = defineEmits<Emits>(); | ||
const props = withDefaults(defineProps<Props>(), {}); | ||
const router = useRouter(); | ||
// const { locale } = useI18n(); | ||
const keyword = ref(""); | ||
const scrollbarRef = ref(); | ||
const resultRef = ref(); | ||
const activePath = ref(""); | ||
const inputRef = ref<HTMLInputElement | null>(null); | ||
const resultOptions = shallowRef([]); | ||
const handleSearch = useDebounceFn(search, 300); | ||
/** 菜单树形结构 */ | ||
const authStore = useAuthStore(); | ||
const menusData = computed(() => authStore.flatMenuListGet.filter(item => !item.meta.isHide)); | ||
const show = computed({ | ||
get() { | ||
return props.value; | ||
}, | ||
set(val: boolean) { | ||
emit("update:value", val); | ||
} | ||
}); | ||
/** 将菜单树形结构扁平化为一维数组,用于菜单查询 */ | ||
function flatTree(arr) { | ||
const res = []; | ||
function deep(arr) { | ||
arr.forEach(item => { | ||
res.push(item); | ||
item.children && deep(item.children); | ||
}); | ||
} | ||
deep(arr); | ||
return res; | ||
} | ||
/** 查询 */ | ||
function search() { | ||
const flatMenusData = flatTree(menusData.value); | ||
resultOptions.value = flatMenusData.filter(menu => | ||
keyword.value | ||
? menu.meta?.title.toLocaleLowerCase().includes(keyword.value.toLocaleLowerCase().trim()) | ||
: // || | ||
// (locale.value === "zh" && | ||
// !isAllEmpty(match(menu.meta?.title.toLocaleLowerCase(), keyword.value.toLocaleLowerCase().trim()))) | ||
false | ||
); | ||
if (resultOptions.value?.length > 0) { | ||
activePath.value = resultOptions.value[0].path; | ||
} else { | ||
activePath.value = ""; | ||
} | ||
} | ||
function handleClose() { | ||
show.value = false; | ||
/** 延时处理防止用户看到某些操作 */ | ||
setTimeout(() => { | ||
resultOptions.value = []; | ||
keyword.value = ""; | ||
}, 200); | ||
} | ||
function scrollTo(index) { | ||
const scrollTop = resultRef.value.handleScroll(index); | ||
scrollbarRef.value.setScrollTop(scrollTop); | ||
} | ||
/** key up */ | ||
function handleUp() { | ||
const { length } = resultOptions.value; | ||
if (length === 0) return; | ||
const index = resultOptions.value.findIndex(item => item.path === activePath.value); | ||
if (index === 0) { | ||
activePath.value = resultOptions.value[length - 1].path; | ||
scrollTo(resultOptions.value.length - 1); | ||
} else { | ||
activePath.value = resultOptions.value[index - 1].path; | ||
scrollTo(index - 1); | ||
} | ||
} | ||
/** key down */ | ||
function handleDown() { | ||
const { length } = resultOptions.value; | ||
if (length === 0) return; | ||
const index = resultOptions.value.findIndex(item => item.path === activePath.value); | ||
if (index + 1 === length) { | ||
activePath.value = resultOptions.value[0].path; | ||
} else { | ||
activePath.value = resultOptions.value[index + 1].path; | ||
} | ||
scrollTo(index + 1); | ||
} | ||
/** key enter */ | ||
function handleEnter() { | ||
const { length } = resultOptions.value; | ||
if (length === 0 || activePath.value === "") return; | ||
router.push(activePath.value); | ||
handleClose(); | ||
} | ||
onKeyStroke("Enter", handleEnter); | ||
onKeyStroke("ArrowUp", handleUp); | ||
onKeyStroke("ArrowDown", handleDown); | ||
</script> | ||
|
||
<template> | ||
<el-dialog | ||
v-model="show" | ||
top="5vh" | ||
:show-close="false" | ||
:width="'40vw'" | ||
:before-close="handleClose" | ||
:style="{ | ||
borderRadius: '6px' | ||
}" | ||
@opened="inputRef!.focus()" | ||
@closed="inputRef!.blur()" | ||
class="search-dialog" | ||
> | ||
<el-input ref="inputRef" v-model="keyword" size="large" clearable placeholder="搜索菜单" @input="handleSearch"> | ||
<template #prefix> | ||
<el-icon class="text-primary w-[24px] h-[24px]"><Search /></el-icon> | ||
</template> | ||
</el-input> | ||
<div class="search-result-container"> | ||
<el-scrollbar ref="scrollbarRef" max-height="calc(90vh - 140px)"> | ||
<el-empty v-if="resultOptions.length === 0" description="暂无搜索结果" /> | ||
<SearchResult v-else ref="resultRef" v-model:value="activePath" :options="resultOptions" @click="handleEnter" /> | ||
</el-scrollbar> | ||
</div> | ||
<template #footer> | ||
<SearchFooter :total="resultOptions.length" /> | ||
</template> | ||
</el-dialog> | ||
</template> | ||
|
||
<style lang="scss" scoped></style> |
Oops, something went wrong.