Merge branch 'master' of https://gitee.com/youlaiorg/vue3-element-admin
This commit is contained in:
1
src/assets/icons/search.svg
Normal file
1
src/assets/icons/search.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg t="1723548009478" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1594" width="200" height="200"><path d="M975.474921 917.123519l-152.442347-152.502577c150.575215-165.512276 146.419339-421.550372-13.431309-581.40102a421.610602 421.610602 0 0 0-596.217622 596.277851c138.890578 138.890578 350.47887 160.151799 512.136422 64.626882l161.47686 161.537091a62.518829 62.518829 0 1 0 88.477996-88.538227z m-251.038998-222.851318a301.15043 301.15043 0 0 1-425.886938 0 301.15043 301.15043 0 0 1 0-425.886938 301.15043 301.15043 0 1 1 425.886938 425.886938z" fill="#1E2330" p-id="1595"></path></svg>
|
||||||
|
After Width: | Height: | Size: 639 B |
155
src/components/MenuSearch/index.vue
Normal file
155
src/components/MenuSearch/index.vue
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
<template>
|
||||||
|
<div class="nav-action-item" @click="showSearchModal">
|
||||||
|
<svg-icon icon-class="search" />
|
||||||
|
<el-dialog
|
||||||
|
v-model="visible"
|
||||||
|
width="30%"
|
||||||
|
:append-to-body="true"
|
||||||
|
:show-close="false"
|
||||||
|
@close="hideSearchModal"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<el-input
|
||||||
|
ref="menuSearchInput"
|
||||||
|
v-model="searchKey"
|
||||||
|
size="large"
|
||||||
|
placeholder="搜索"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="inputSearch"
|
||||||
|
@input="inputSearch"
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<el-button icon="Search" />
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</template>
|
||||||
|
<div class="search-content">
|
||||||
|
<ul v-if="searchResult.length > 0">
|
||||||
|
<li
|
||||||
|
v-for="(item, index) in searchResult"
|
||||||
|
:key="index"
|
||||||
|
@click="toMenu(item)"
|
||||||
|
>
|
||||||
|
<el-icon
|
||||||
|
v-if="item.icon && item.icon.startsWith('el-icon')"
|
||||||
|
class="sub-el-icon"
|
||||||
|
>
|
||||||
|
<component :is="item.icon.replace('el-icon-', '')" />
|
||||||
|
</el-icon>
|
||||||
|
<svg-icon v-else-if="item.icon" :icon-class="item.icon" />
|
||||||
|
<svg-icon v-else icon-class="menu" />
|
||||||
|
{{ item.title }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="search-space" v-else>暂无数据</div>
|
||||||
|
</div>
|
||||||
|
<template #footer></template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import router from "@/router";
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
const searchKey = ref("");
|
||||||
|
import { usePermissionStore } from "@/store";
|
||||||
|
import { isExternal } from "@/utils";
|
||||||
|
import { RouteRecordRaw } from "vue-router";
|
||||||
|
const permissionStore = usePermissionStore();
|
||||||
|
//获取当前拥有的路由
|
||||||
|
const menuSearchInput = ref();
|
||||||
|
const noSearchRoutePath = ref(["/redirect", "/login", "/401", "/404"]);
|
||||||
|
const searchData = ref<SearchItem[]>([]);
|
||||||
|
|
||||||
|
const searchResult = ref<SearchItem[]>([]);
|
||||||
|
function showSearchModal() {
|
||||||
|
searchKey.value = "";
|
||||||
|
searchResult.value = [];
|
||||||
|
visible.value = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
menuSearchInput.value.focus();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
function hideSearchModal() {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function inputSearch() {
|
||||||
|
searchResult.value = [];
|
||||||
|
|
||||||
|
if (searchKey.value) {
|
||||||
|
let toLowerCaseKey = searchKey.value.toLowerCase();
|
||||||
|
searchResult.value = searchData.value.filter((item) =>
|
||||||
|
item.toLowerCaseTitle.includes(toLowerCaseKey)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
searchResult.value = [];
|
||||||
|
initSearchData(permissionStore.routes);
|
||||||
|
});
|
||||||
|
|
||||||
|
function initSearchData(routes: RouteRecordRaw[], parentPath: string = "") {
|
||||||
|
parentPath = parentPath === "/" ? "" : parentPath;
|
||||||
|
routes.forEach((item: any) => {
|
||||||
|
if (noSearchRoutePath.value.includes(item.path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let path = item.path.startsWith("/")
|
||||||
|
? item.path
|
||||||
|
: parentPath + "/" + item.path;
|
||||||
|
path = isExternal(item.path) ? item.path : path;
|
||||||
|
if (item.children) {
|
||||||
|
initSearchData(item.children, path);
|
||||||
|
} else if (item.meta && item.meta.title && item.path) {
|
||||||
|
let title = item.meta.title == "dashboard" ? "首页" : item.meta.title;
|
||||||
|
let toLowerCaseTitle = title.toLowerCase();
|
||||||
|
searchData.value.push({
|
||||||
|
title: title,
|
||||||
|
toLowerCaseTitle: toLowerCaseTitle,
|
||||||
|
path: path,
|
||||||
|
name: item.name,
|
||||||
|
icon: item.meta.icon,
|
||||||
|
redirect: item.redirect ? item.redirect : undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toMenu(item: SearchItem) {
|
||||||
|
hideSearchModal();
|
||||||
|
if (isExternal(item.path)) {
|
||||||
|
window.open(item.path, "_blank");
|
||||||
|
} else {
|
||||||
|
router.push(item.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SearchItem {
|
||||||
|
title: string;
|
||||||
|
toLowerCaseTitle: string;
|
||||||
|
path: string;
|
||||||
|
name: string;
|
||||||
|
icon?: string;
|
||||||
|
redirect?: string;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.search-content ul li {
|
||||||
|
padding-left: 10px;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-space {
|
||||||
|
padding: 20px;
|
||||||
|
color: #999;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<template v-if="!isMobile">
|
<template v-if="!isMobile">
|
||||||
|
<!-- 搜索 -->
|
||||||
|
<menu-search />
|
||||||
<!--全屏 -->
|
<!--全屏 -->
|
||||||
<div class="nav-action-item" @click="toggle">
|
<div class="nav-action-item" @click="toggle">
|
||||||
<svg-icon
|
<svg-icon
|
||||||
|
|||||||
1
src/types/components.d.ts
vendored
1
src/types/components.d.ts
vendored
@@ -73,6 +73,7 @@ declare module "vue" {
|
|||||||
IEpClose: (typeof import("~icons/ep/close"))["default"];
|
IEpClose: (typeof import("~icons/ep/close"))["default"];
|
||||||
IEpDownload: (typeof import("~icons/ep/download"))["default"];
|
IEpDownload: (typeof import("~icons/ep/download"))["default"];
|
||||||
LangSelect: (typeof import("./../components/LangSelect/index.vue"))["default"];
|
LangSelect: (typeof import("./../components/LangSelect/index.vue"))["default"];
|
||||||
|
MenuSearch: (typeof import("./../components/MenuSearch/index.vue"))["default"];
|
||||||
LayoutSelect: (typeof import("./../layout/components/Settings/components/LayoutSelect.vue"))["default"];
|
LayoutSelect: (typeof import("./../layout/components/Settings/components/LayoutSelect.vue"))["default"];
|
||||||
MultiUpload: (typeof import("./../components/Upload/MultiUpload.vue"))["default"];
|
MultiUpload: (typeof import("./../components/Upload/MultiUpload.vue"))["default"];
|
||||||
NavBar: (typeof import("./../layout/components/NavBar/index.vue"))["default"];
|
NavBar: (typeof import("./../layout/components/NavBar/index.vue"))["default"];
|
||||||
|
|||||||
Reference in New Issue
Block a user