mirror of
				https://github.com/continew-org/continew-admin-ui.git
				synced 2025-10-31 10:57:10 +08:00 
			
		
		
		
	feat(header-search): add select and enter shortcut (#50)
This commit is contained in:
		
							
								
								
									
										1
									
								
								src/assets/icons/select.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/assets/icons/select.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="12" height="12" viewBox="0 0 24 24"><path fill="currentColor" d="m17.25 4l-.1.007a.75.75 0 0 0-.65.743v12.692l-3.22-3.218l-.084-.072a.75.75 0 0 0-.976 1.134l4.504 4.5l.084.072a.75.75 0 0 0 .976-.073l4.497-4.5l.072-.084a.75.75 0 0 0-.073-.977l-.084-.072a.75.75 0 0 0-.977.073L18 17.446V4.75l-.006-.102A.75.75 0 0 0 17.251 4m-11.036.22L1.72 8.715l-.073.084a.75.75 0 0 0 .073.976l.084.073a.75.75 0 0 0 .976-.073l3.217-3.218v12.698l.008.102a.75.75 0 0 0 .743.648l.101-.007a.75.75 0 0 0 .649-.743L7.497 6.559l3.223 3.217l.084.072a.75.75 0 0 0 .975-1.134L7.275 4.22l-.085-.072a.75.75 0 0 0-.976.073"></path></svg> | ||||||
| After Width: | Height: | Size: 727 B | 
							
								
								
									
										1
									
								
								src/assets/icons/shortcut-enter.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/assets/icons/shortcut-enter.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="12" height="12" viewBox="0 0 24 24"><path fill="currentColor" d="M21.25 4a.75.75 0 0 1 .75.75v6.5A3.75 3.75 0 0 1 18.25 15H4.587l3.72 3.72a.75.75 0 0 1 .072.976l-.072.084a.75.75 0 0 1-.977.073l-.084-.073l-5-5a.75.75 0 0 1-.073-.976l.073-.084l5-5a.75.75 0 0 1 1.133.976l-.072.084l-3.72 3.72h13.665a2.25 2.25 0 0 0 2.244-2.096l.006-.154v-6.5a.75.75 0 0 1 .75-.75"></path></svg> | ||||||
| After Width: | Height: | Size: 495 B | 
| @@ -22,6 +22,7 @@ const searchInput = ref<HTMLInputElement | null>(null) | |||||||
| const searchKeyword = ref('') | const searchKeyword = ref('') | ||||||
| const searchHistory = ref<SearchHistory[]>([]) | const searchHistory = ref<SearchHistory[]>([]) | ||||||
| const searchResults = ref<SearchResult[]>([]) | const searchResults = ref<SearchResult[]>([]) | ||||||
|  | const selectedIndex = ref(-1) | ||||||
|  |  | ||||||
| const searchRoutes = (keyword: string) => { | const searchRoutes = (keyword: string) => { | ||||||
|   const result: SearchResult[] = [] |   const result: SearchResult[] = [] | ||||||
| @@ -81,6 +82,18 @@ useEventListener('keydown', (e) => { | |||||||
| const handleKeyDown = (e: KeyboardEvent) => { | const handleKeyDown = (e: KeyboardEvent) => { | ||||||
|   if (e.key === 'Escape') { |   if (e.key === 'Escape') { | ||||||
|     visible.value = false |     visible.value = false | ||||||
|  |   } else if (e.key === 'ArrowDown') { | ||||||
|  |     if (selectedIndex.value < searchResults.value.length - 1) { | ||||||
|  |       selectedIndex.value++ | ||||||
|  |     } | ||||||
|  |   } else if (e.key === 'ArrowUp') { | ||||||
|  |     if (selectedIndex.value > 0) { | ||||||
|  |       selectedIndex.value-- | ||||||
|  |     } | ||||||
|  |   } else if (e.key === 'Enter') { | ||||||
|  |     if (selectedIndex.value >= 0 && selectedIndex.value < searchResults.value.length) { | ||||||
|  |       handleResultClick(searchResults.value[selectedIndex.value]) | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -92,6 +105,7 @@ watch(visible, (newValue) => { | |||||||
|   } else { |   } else { | ||||||
|     searchResults.value = [] |     searchResults.value = [] | ||||||
|     searchKeyword.value = '' |     searchKeyword.value = '' | ||||||
|  |     selectedIndex.value = -1 | ||||||
|   } |   } | ||||||
| }) | }) | ||||||
|  |  | ||||||
| @@ -113,7 +127,6 @@ watch(searchKeyword, (newValue) => { | |||||||
|   <div class="search-modal"> |   <div class="search-modal"> | ||||||
|     <a-modal |     <a-modal | ||||||
|       :visible="visible" |       :visible="visible" | ||||||
|       :footer="false" |  | ||||||
|       :mask-closable="true" |       :mask-closable="true" | ||||||
|       :align-center="false" |       :align-center="false" | ||||||
|       :closable="false" |       :closable="false" | ||||||
| @@ -124,12 +137,7 @@ watch(searchKeyword, (newValue) => { | |||||||
|       <template #title> |       <template #title> | ||||||
|         <div class="search-input-wrapper"> |         <div class="search-input-wrapper"> | ||||||
|           <icon-search :size="24" /> |           <icon-search :size="24" /> | ||||||
|           <input |           <input ref="searchInput" v-model="searchKeyword" placeholder="搜索页面" class="search-input"> | ||||||
|             ref="searchInput" |  | ||||||
|             v-model="searchKeyword" |  | ||||||
|             placeholder="搜索页面" |  | ||||||
|             class="search-input" |  | ||||||
|           > |  | ||||||
|           <div class="esc-tip"> |           <div class="esc-tip"> | ||||||
|             ESC 退出 |             ESC 退出 | ||||||
|           </div> |           </div> | ||||||
| @@ -142,10 +150,8 @@ watch(searchKeyword, (newValue) => { | |||||||
|           </div> |           </div> | ||||||
|           <div class="result-list"> |           <div class="result-list"> | ||||||
|             <div |             <div | ||||||
|               v-for="item in searchResults" |               v-for="(item, index) in searchResults" :key="item.path" class="result-item" | ||||||
|               :key="item.path" |               :class="{ selected: selectedIndex === index }" @click="handleResultClick(item)" | ||||||
|               class="result-item" |  | ||||||
|               @click="handleResultClick(item)" |  | ||||||
|             > |             > | ||||||
|               <icon-file :size="18" style="margin-right: 6px;" /> |               <icon-file :size="18" style="margin-right: 6px;" /> | ||||||
|               <div class="result-title"> |               <div class="result-title"> | ||||||
| @@ -159,21 +165,12 @@ watch(searchKeyword, (newValue) => { | |||||||
|             <div class="history-title"> |             <div class="history-title"> | ||||||
|               搜索历史 |               搜索历史 | ||||||
|             </div> |             </div> | ||||||
|             <a-button |             <a-button type="text" size="small" class="text-xs" @click="clearHistory"> | ||||||
|               type="text" |  | ||||||
|               size="small" |  | ||||||
|               class="text-xs" |  | ||||||
|               @click="clearHistory" |  | ||||||
|             > |  | ||||||
|               清空历史 |               清空历史 | ||||||
|             </a-button> |             </a-button> | ||||||
|           </div> |           </div> | ||||||
|           <div class="history-list"> |           <div class="history-list"> | ||||||
|             <div |             <div v-for="item in searchHistory" :key="item.path" class="history-item" @click="handleHistoryClick(item)"> | ||||||
|               v-for="item in searchHistory" :key="item.path" |  | ||||||
|               class="history-item" |  | ||||||
|               @click="handleHistoryClick(item)" |  | ||||||
|             > |  | ||||||
|               <icon-history :size="18" style="margin-right: 6px;" /> |               <icon-history :size="18" style="margin-right: 6px;" /> | ||||||
|               <div class="result-title"> |               <div class="result-title"> | ||||||
|                 {{ item.title }} |                 {{ item.title }} | ||||||
| @@ -182,6 +179,16 @@ watch(searchKeyword, (newValue) => { | |||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|  |       <template #footer> | ||||||
|  |         <div class="shortcut"> | ||||||
|  |           <GiSvgIcon name="select" :size="12" class="shortcut-icon" /> | ||||||
|  |           <div>切换</div> | ||||||
|  |         </div> | ||||||
|  |         <div class="shortcut"> | ||||||
|  |           <GiSvgIcon name="shortcut-enter" :size="12" class="shortcut-icon" /> | ||||||
|  |           <div>选择</div> | ||||||
|  |         </div> | ||||||
|  |       </template> | ||||||
|     </a-modal> |     </a-modal> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| @@ -339,4 +346,39 @@ watch(searchKeyword, (newValue) => { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .selected { | ||||||
|  |   color: #000; | ||||||
|  |   background-color: #f3f4f6; | ||||||
|  |  | ||||||
|  |   .dark & { | ||||||
|  |     color: #fff; | ||||||
|  |     background-color: var(--color-bg-4); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | :deep(.arco-modal-footer){ | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: center; | ||||||
|  |   align-items: center; | ||||||
|  |   gap: 1.5rem; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .shortcut{ | ||||||
|  |   display: flex; | ||||||
|  |   justify-items: center; | ||||||
|  |   align-items: center; | ||||||
|  |   gap: 8px; | ||||||
|  |  | ||||||
|  |   &-icon{ | ||||||
|  |     padding: 4px; | ||||||
|  |     background-color: #e5e7eb; | ||||||
|  |     border-radius: 6px; | ||||||
|  |     color: var(--color-text-1); | ||||||
|  |  | ||||||
|  |     .dark & { | ||||||
|  |       background-color: var(--color-bg-5); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| </style> | </style> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 ppxb
					ppxb