فهرست منبع

update searchMenu style

LiuHao 2 سال پیش
والد
کامیت
b263918426
4فایلهای تغییر یافته به همراه178 افزوده شده و 7 حذف شده
  1. 2 1
      .eslintrc.js
  2. 4 4
      src/components/HeaderSearch/index.vue
  3. 14 2
      src/layout/components/Navbar.vue
  4. 158 0
      src/layout/components/topBar/search.vue

+ 2 - 1
.eslintrc.js

@@ -29,7 +29,8 @@ module.exports = {
         // 关闭空类型检查 {}
         extendDefaults: true,
         types: {
-          '{}': false
+          '{}': false,
+          'Function': false
         }
       }
     ]

+ 4 - 4
src/components/HeaderSearch/index.vue

@@ -17,7 +17,7 @@
   </div>
 </template>
 
-<script setup lang="ts">
+<script setup lang="ts" name="HeaderSearch">
 import Fuse from 'fuse.js'
 import { getNormalPath } from '@/utils/ruoyi'
 import { isHttp } from '@/utils/validate'
@@ -123,9 +123,9 @@ onMounted(() => {
     searchPool.value = generateRoutes(routes.value);
 })
 
-watchEffect(() => {
-    searchPool.value = generateRoutes(routes.value)
-})
+// watchEffect(() => {
+//     searchPool.value = generateRoutes(routes.value)
+// })
 
 watch(show, (value) => {
     if (value) {

+ 14 - 2
src/layout/components/Navbar.vue

@@ -20,8 +20,13 @@
           <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
         </el-select>
 
-        <header-search id="header-search" class="right-menu-item" />
-
+        <!-- <header-search id="header-search" class="right-menu-item" /> -->
+        <search-menu ref="searchMenuRef" />
+        <el-tooltip content="搜索" effect="dark" placement="bottom">
+          <div class="right-menu-item hover-effect" @click="openSearchMenu">
+            <svg-icon class-name="search-icon" icon-class="search" />
+          </div>
+        </el-tooltip>
         <el-tooltip content="Github" effect="dark" placement="bottom">
           <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
         </el-tooltip>
@@ -68,6 +73,7 @@
 </template>
 
 <script setup lang="ts">
+import SearchMenu from './topBar/search.vue'
 import useAppStore from '@/store/modules/app'
 import useUserStore from '@/store/modules/user'
 import useSettingsStore from '@/store/modules/settings'
@@ -89,6 +95,12 @@ const tenantList = ref<TenantVO[]>([]);
 const dynamic = ref(false);
 // 租户开关
 const tenantEnabled = ref(true);
+// 搜索菜单
+const searchMenuRef = ref<InstanceType<typeof SearchMenu>>();
+
+const openSearchMenu = () => {
+  searchMenuRef.value?.openSearch()
+}
 
 // 动态切换
 const dynamicTenantEvent = async (tenantId: string) => {

+ 158 - 0
src/layout/components/topBar/search.vue

@@ -0,0 +1,158 @@
+<template>
+  <div class="layout-search-dialog">
+    <el-dialog v-model="state.isShowSearch" destroy-on-close :show-close="false">
+      <template #footer>
+        <el-autocomplete
+          v-model="state.menuQuery"
+          :fetch-suggestions="menuSearch"
+          placeholder="搜索"
+          ref="layoutMenuAutocompleteRef"
+          @select="onHandleSelect"
+          :fit-input-width="true"
+        >
+          <template #prefix>
+            <svg-icon class-name="search-icon" icon-class="search" />
+          </template>
+          <template #default="{ item }">
+            <div>
+              <svg-icon :icon-class="item.icon" class="mr5" />
+              {{ item.title }}
+            </div>
+          </template>
+        </el-autocomplete>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts" name="layoutBreadcrumbSearch">
+import { getNormalPath } from '@/utils/ruoyi';
+import { isHttp } from '@/utils/validate';
+import usePermissionStore from '@/store/modules/permission';
+import { RouteOption } from 'vue-router';
+type Router = Array<{
+	path: string;
+	icon: string;
+	title: string[];
+}>
+type SearchState<T = any> = {
+	isShowSearch: boolean;
+	menuQuery: string;
+	menuList: T[];
+};
+// 定义变量内容
+const layoutMenuAutocompleteRef = ref();
+const router = useRouter();
+const routes = computed(() => usePermissionStore().routes);
+const state = reactive<SearchState>({
+	isShowSearch: false,
+	menuQuery: '',
+	menuList: [],
+});
+
+// 搜索弹窗打开
+const openSearch = () => {
+	state.menuQuery = '';
+	state.isShowSearch = true;
+	state.menuList = generateRoutes(routes.value);
+	nextTick(() => {
+		setTimeout(() => {
+			layoutMenuAutocompleteRef.value.focus();
+		});
+	});
+};
+// 搜索弹窗关闭
+const closeSearch = () => {
+	state.isShowSearch = false;
+};
+// 菜单搜索数据过滤
+const menuSearch = (queryString: string, cb: Function) => {
+	let options = state.menuList.filter((item) => {
+		return item.title.indexOf(queryString) > -1;
+	});
+	cb(options);
+};
+
+// Filter out the routes that can be displayed in the sidebar
+// And generate the internationalized title
+const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = []) => {
+	let res: Router = []
+	routes.forEach(r => {
+        // skip hidden router
+		if (!r.hidden) {
+			const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
+				const data: any = {
+					path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
+					icon: r.meta?.icon,
+					title: [...prefixTitle]
+				}
+				if (r.meta && r.meta.title) {
+						data.title = [...data.title, r.meta.title];
+						if (r.redirect !== 'noRedirect') {
+								// only push the routes with title
+								// special case: need to exclude parent router without redirect
+								res.push(data);
+						}
+				}
+				// recursive child routes
+				if (r.children) {
+						const tempRoutes = generateRoutes(r.children, data.path, data.title);
+						if (tempRoutes.length >= 1) {
+								res = [...res, ...tempRoutes];
+						}
+				}
+		}
+	})
+	res.forEach((item: any) => {
+		if (item.title instanceof Array) {
+			item.title = item.title.join('/');
+		}
+	});
+	return res;
+}
+// 当前菜单选中时
+const onHandleSelect = (val: any) => {
+	const paths = val.path;
+	if (isHttp(paths)) {
+			// http(s):// 路径新窗口打开
+			const pindex = paths.indexOf("http");
+			window.open(paths.substring(pindex, paths.length), "_blank");
+	} else {
+			router.push(paths)
+	}
+	state.menuQuery = ''
+	closeSearch();
+
+};
+
+// 暴露变量
+defineExpose({
+	openSearch
+});
+</script>
+
+<style scoped lang="scss">
+.layout-search-dialog {
+	position: relative;
+	:deep(.el-dialog) {
+		.el-dialog__header,
+		.el-dialog__body {
+			display: none;
+		}
+		.el-dialog__footer {
+			width: 100%;
+			position: absolute;
+			left: 50%;
+			transform: translateX(-50%);
+			top: -53vh;
+		}
+	}
+	:deep(.el-autocomplete) {
+		width: 560px;
+		position: absolute;
+		top: 150px;
+		left: 50%;
+		transform: translateX(-50%);
+	}
+}
+</style>