@@ -3,7 +3,7 @@ import type { DataTableColumn } from 'naive-ui'
33import type { SyncEntry } from ' @/models/mirrors'
44import { CheckmarkOutline , CloseOutline , CloudDoneOutline , GlobeOutline , HelpCircleOutline , SearchOutline } from ' @vicons/ionicons5'
55import { NButton , NCheckbox , NDataTable , NFlex , NH2 , NHighlight , NIcon , NInput , NTag , useThemeVars } from ' naive-ui'
6- import { computed , h , ref , shallowRef } from ' vue'
6+ import { computed , h , onMounted , onUnmounted , ref , shallowRef } from ' vue'
77import { useI18n } from ' vue-i18n'
88import { useRouter } from ' vue-router'
99import { useDebounce , usePromiseEffect } from ' @/hooks'
@@ -21,6 +21,25 @@ const filter = ref('')
2121const searchInput = ref (' ' )
2222const statusFilter = ref <string []>([])
2323const onSearchInput = useDebounce ((value : string ) => filter .value = value )
24+ const searchInputRef = ref <HTMLElement >()
25+
26+ function handleKeyDown(event : KeyboardEvent ) {
27+ if ((event .ctrlKey || event .metaKey ) && event .key === ' f' ) {
28+ event .preventDefault ()
29+ if (searchInputRef .value ) {
30+ searchInputRef .value .focus ();
31+ (searchInputRef .value as HTMLInputElement ).select ()
32+ }
33+ }
34+ }
35+
36+ onMounted (() => {
37+ document .addEventListener (' keydown' , handleKeyDown )
38+ })
39+
40+ onUnmounted (() => {
41+ document .removeEventListener (' keydown' , handleKeyDown )
42+ })
2443
2544usePromiseEffect (fetchEntries , (res ) => {
2645 entries .value = res .sort ((a , b ) => a .name .localeCompare (b .name ))
@@ -46,11 +65,23 @@ function renderNextUpdate(data: SyncEntry) {
4665 return data .nextUpdate ? timeFromNow (data .nextUpdate , locale .value as ' zh' | ' en' ) : ' -'
4766}
4867
68+ function getAbsolutePath(path : string ) {
69+ if (path .startsWith (' http' )) {
70+ return path
71+ }
72+ else if (path .startsWith (' /' )) {
73+ return window .location .href .replace (/ \/ $ / , ' ' ) + path
74+ }
75+ return ` ${window .location .href .replace (/ \/ $ / , ' ' )}/${path } `
76+ }
77+
4978function renderName(data : SyncEntry ) {
5079 const doc = store .docItems .find (value => value .name === data .name )
5180 const docButton = doc
5281 ? h (NButton , {
82+ tag: ' a' ,
5383 text: true ,
84+ href: getAbsolutePath (doc .redirect || ` ${window .location .href }${doc ?.name } ` ),
5485 onClick : () => {
5586 if (doc .redirect ) {
5687 window .location .href = doc .redirect
@@ -61,10 +92,13 @@ function renderName(data: SyncEntry) {
6192 },
6293 }, { default : () => h (NIcon , () => h (HelpCircleOutline )) })
6394 : undefined
95+ const linkHref = getAbsolutePath (data .route ? data .route : (data .path ? data .path : ` /${data .name } ` ))
6496 return h (NFlex , { align: ' center' , inline: true , style: { gap: ' 4px' } }, {
6597 default : () => [
6698 h (NButton , {
99+ tag: ' a' ,
67100 text: true ,
101+ href: linkHref ,
68102 size: ' large' ,
69103 onClick : () => {
70104 if (data .route ) {
@@ -279,7 +313,15 @@ const filteredEntries = computed(() => {
279313<template >
280314 <NH2 prefix="bar">
281315 <span >{{ t('header.mirrors') }}</span >
282- <NInput v-model :value =" searchInput " :placeholder =" t (' table.searchText' )" clearable style =" max-width : 300px " @input =" onSearchInput " >
316+ <NInput
317+ id="mirror-search-input"
318+ ref="searchInputRef"
319+ v-model :value =" searchInput "
320+ :placeholder =" t (' table.searchText' )"
321+ clearable
322+ style =" max-width : 300px "
323+ @input =" onSearchInput "
324+ >
283325 <template #prefix >
284326 <NIcon >
285327 <SearchOutline />
0 commit comments