update searchMenu style
This commit is contained in:
		| @ -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
									
								
								src/layout/components/topBar/search.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								src/layout/components/topBar/search.vue
									
									
									
									
									
										Normal file
									
								
							| @ -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> | ||||
		Reference in New Issue
	
	Block a user
	 LiuHao
					LiuHao