mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-09-11 16:57:09 +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