mirror of
https://github.com/gotson/komga.git
synced 2026-05-09 05:10:19 +02:00
parent
a5c7b17829
commit
604ccf1192
2 changed files with 124 additions and 96 deletions
|
|
@ -1,113 +1,94 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="searchbox">
|
<div>
|
||||||
<v-text-field v-model="search"
|
<v-autocomplete
|
||||||
|
v-model="selectedItem"
|
||||||
|
:placeholder="$t('search.search')"
|
||||||
|
:no-data-text="$t('searchbox.no_results')"
|
||||||
|
:loading="loading"
|
||||||
|
:items="results"
|
||||||
|
:hide-no-data="!showResults"
|
||||||
|
clearable
|
||||||
solo
|
solo
|
||||||
hide-details
|
hide-details
|
||||||
clearable
|
no-filter
|
||||||
|
return-object
|
||||||
prepend-inner-icon="mdi-magnify"
|
prepend-inner-icon="mdi-magnify"
|
||||||
:label="$t('search.search')"
|
append-icon=""
|
||||||
:loading="loading"
|
item-text="id"
|
||||||
@click:clear="clear"
|
auto-select-first
|
||||||
|
:search-input.sync="search"
|
||||||
|
:menu-props="{maxHeight: $vuetify.breakpoint.height * .9, minWidth: $vuetify.breakpoint.mdAndUp ? $vuetify.breakpoint.width * .4 : $vuetify.breakpoint.width * .8}"
|
||||||
@keydown.esc="clear"
|
@keydown.esc="clear"
|
||||||
@keydown.enter="searchDetails"
|
ref="searchbox"
|
||||||
/>
|
|
||||||
<v-menu nudge-bottom="57"
|
|
||||||
nudge-right="52"
|
|
||||||
attach="#searchbox"
|
|
||||||
v-model="showResults"
|
|
||||||
:max-height="$vuetify.breakpoint.height * .9"
|
|
||||||
:min-width="$vuetify.breakpoint.mdAndUp ? $vuetify.breakpoint.width * .4 : $vuetify.breakpoint.width * .8"
|
|
||||||
>
|
>
|
||||||
<v-list>
|
<template v-slot:selection>
|
||||||
<v-list-item
|
</template>
|
||||||
v-if="series.length === 0 && books.length === 0 && collections.length === 0 && readLists.length === 0">
|
|
||||||
{{ $t('searchbox.no_results') }}
|
|
||||||
</v-list-item>
|
|
||||||
|
|
||||||
<template v-if="series.length !== 0">
|
<template v-slot:item="data">
|
||||||
<v-subheader class="text-uppercase">{{ $t('common.series') }}</v-subheader>
|
<template v-if="typeof data.item !== 'object'">
|
||||||
<v-list-item v-for="item in series"
|
<v-list-item-content v-text="data.item"></v-list-item-content>
|
||||||
:key="item.id"
|
</template>
|
||||||
link
|
|
||||||
:to="{name: 'browse-series', params: {seriesId: item.id}}"
|
<template v-if="data.item.type === 'search'">
|
||||||
>
|
<v-list-item-content>{{ $t('searchbox.search_all') }}</v-list-item-content>
|
||||||
<v-img :src="seriesThumbnailUrl(item.id)"
|
</template>
|
||||||
|
|
||||||
|
<template v-if="data.item.type === 'series'">
|
||||||
|
<v-img :src="seriesThumbnailUrl(data.item.id)"
|
||||||
height="50"
|
height="50"
|
||||||
max-width="35"
|
max-width="35"
|
||||||
class="my-1 mx-3"
|
class="my-1 mx-3"
|
||||||
>
|
>
|
||||||
<span v-if="item.booksUnreadCount !== 0"
|
<span v-if="data.item.booksUnreadCount !== 0"
|
||||||
class="white--text pa-0 px-1 text-caption"
|
class="white--text pa-0 px-1 text-caption"
|
||||||
:style="{background: 'orange', position: 'absolute', right: 0}"
|
:style="{background: 'orange', position: 'absolute', right: 0}"
|
||||||
>
|
>
|
||||||
{{ item.booksUnreadCount }}
|
{{ data.item.booksUnreadCount }}
|
||||||
</span>
|
</span>
|
||||||
</v-img>
|
</v-img>
|
||||||
<v-list-item-content>
|
<v-list-item-content>
|
||||||
<v-list-item-title v-text="item.metadata.title"/>
|
<v-list-item-title v-text="data.item.metadata.title"/>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
</v-list-item>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="books.length !== 0">
|
<template v-if="data.item.type === 'book'">
|
||||||
<v-subheader class="text-uppercase">{{ $t('common.books') }}</v-subheader>
|
<v-img :src="bookThumbnailUrl(data.item.id)"
|
||||||
<v-list-item v-for="item in books"
|
|
||||||
:key="item.id"
|
|
||||||
link
|
|
||||||
:to="{name: 'browse-book', params: {bookId: item.id}}"
|
|
||||||
>
|
|
||||||
<v-img :src="bookThumbnailUrl(item.id)"
|
|
||||||
height="50"
|
height="50"
|
||||||
max-width="35"
|
max-width="35"
|
||||||
class="my-1 mx-3"
|
class="my-1 mx-3"
|
||||||
>
|
>
|
||||||
<div class="unread" v-if="isUnread(item)"/>
|
<div class="unread" v-if="isUnread(data.item)"/>
|
||||||
</v-img>
|
</v-img>
|
||||||
|
|
||||||
<v-list-item-content>
|
<v-list-item-content>
|
||||||
<v-list-item-title v-text="item.metadata.title"/>
|
<v-list-item-title v-text="data.item.metadata.title"/>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
</v-list-item>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="collections.length !== 0">
|
<template v-if="data.item.type === 'collection'">
|
||||||
<v-subheader class="text-uppercase">{{ $t('common.collections') }}</v-subheader>
|
<v-img :src="collectionThumbnailUrl(data.item.id)"
|
||||||
<v-list-item v-for="item in collections"
|
|
||||||
:key="item.id"
|
|
||||||
link
|
|
||||||
:to="{name: 'browse-collection', params: {collectionId: item.id}}"
|
|
||||||
>
|
|
||||||
<v-img :src="collectionThumbnailUrl(item.id)"
|
|
||||||
height="50"
|
height="50"
|
||||||
max-width="35"
|
max-width="35"
|
||||||
class="my-1 mx-3"
|
class="my-1 mx-3"
|
||||||
/>
|
/>
|
||||||
<v-list-item-content>
|
<v-list-item-content>
|
||||||
<v-list-item-title v-text="item.name"/>
|
<v-list-item-title v-text="data.item.name"/>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
</v-list-item>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="readLists.length !== 0">
|
<template v-if="data.item.type === 'readlist'">
|
||||||
<v-subheader class="text-uppercase">{{ $t('common.readlists') }}</v-subheader>
|
<v-img :src="readListThumbnailUrl(data.item.id)"
|
||||||
<v-list-item v-for="item in readLists"
|
|
||||||
:key="item.id"
|
|
||||||
link
|
|
||||||
:to="{name: 'browse-readlist', params: {readListId: item.id}}"
|
|
||||||
>
|
|
||||||
<v-img :src="readListThumbnailUrl(item.id)"
|
|
||||||
height="50"
|
height="50"
|
||||||
max-width="35"
|
max-width="35"
|
||||||
class="my-1 mx-3"
|
class="my-1 mx-3"
|
||||||
/>
|
/>
|
||||||
<v-list-item-content>
|
<v-list-item-content>
|
||||||
<v-list-item-title v-text="item.name"/>
|
<v-list-item-title v-text="data.item.name"/>
|
||||||
</v-list-item-content>
|
</v-list-item-content>
|
||||||
</v-list-item>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</v-list>
|
</template>
|
||||||
</v-menu>
|
</v-autocomplete>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -124,6 +105,7 @@ export default Vue.extend({
|
||||||
name: 'SearchBox',
|
name: 'SearchBox',
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
|
selectedItem: null as unknown as any,
|
||||||
search: null,
|
search: null,
|
||||||
showResults: false,
|
showResults: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
@ -135,6 +117,25 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
selectedItem(val, old) {
|
||||||
|
if (val && val.hasOwnProperty('type')) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.selectedItem = undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
if (val.type === 'series') this.$router.push({name: 'browse-series', params: {seriesId: val.id}})
|
||||||
|
else if (val.type === 'book') this.$router.push({name: 'browse-book', params: {bookId: val.id}})
|
||||||
|
else if (val.type === 'collection') this.$router.push({
|
||||||
|
name: 'browse-collection',
|
||||||
|
params: {collectionId: val.id},
|
||||||
|
})
|
||||||
|
else if (val.type === 'readlist') this.$router.push({name: 'browse-readlist', params: {readListId: val.id}})
|
||||||
|
else if (val.type === 'search') this.searchDetails()
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
|
this.$refs.searchbox.blur()
|
||||||
|
}
|
||||||
|
},
|
||||||
search(val) {
|
search(val) {
|
||||||
this.searchItems(val)
|
this.searchItems(val)
|
||||||
},
|
},
|
||||||
|
|
@ -142,6 +143,31 @@ export default Vue.extend({
|
||||||
!val && this.clear()
|
!val && this.clear()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
results(): object[] {
|
||||||
|
const results = []
|
||||||
|
if (this.search) {
|
||||||
|
results.push({type: 'search'})
|
||||||
|
if (this.series.length > 0) {
|
||||||
|
results.push({header: this.$t('common.series').toString().toUpperCase()})
|
||||||
|
results.push(...this.series.map(o => ({...o, type: 'series'})))
|
||||||
|
}
|
||||||
|
if (this.books.length > 0) {
|
||||||
|
results.push({header: this.$t('common.books').toString().toUpperCase()})
|
||||||
|
results.push(...this.books.map(o => ({...o, type: 'book'})))
|
||||||
|
}
|
||||||
|
if (this.collections.length > 0) {
|
||||||
|
results.push({header: this.$t('common.collections').toString().toUpperCase()})
|
||||||
|
results.push(...this.collections.map(o => ({...o, type: 'collection'})))
|
||||||
|
}
|
||||||
|
if (this.readLists.length > 0) {
|
||||||
|
results.push({header: this.$t('common.readlists').toString().toUpperCase()})
|
||||||
|
results.push(...this.readLists.map(o => ({...o, type: 'readlist'})))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
searchItems: debounce(async function (this: any, query: string) {
|
searchItems: debounce(async function (this: any, query: string) {
|
||||||
if (query) {
|
if (query) {
|
||||||
|
|
@ -158,6 +184,7 @@ export default Vue.extend({
|
||||||
}, 500),
|
}, 500),
|
||||||
clear() {
|
clear() {
|
||||||
this.search = null
|
this.search = null
|
||||||
|
// this.selectedItem = null
|
||||||
this.showResults = false
|
this.showResults = false
|
||||||
this.series = []
|
this.series = []
|
||||||
this.books = []
|
this.books = []
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@
|
||||||
"collections": "Collections",
|
"collections": "Collections",
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
|
"download": "Download",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
"filter_no_matches": "The active filter has no matches",
|
"filter_no_matches": "The active filter has no matches",
|
||||||
"genre": "Genre",
|
"genre": "Genre",
|
||||||
|
|
@ -133,15 +134,14 @@
|
||||||
"pages_n": "No pages | 1 page | {count} pages",
|
"pages_n": "No pages | 1 page | {count} pages",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"publisher": "Publisher",
|
"publisher": "Publisher",
|
||||||
|
"read": "Read",
|
||||||
"readlists": "Read Lists",
|
"readlists": "Read Lists",
|
||||||
"required": "Required",
|
"required": "Required",
|
||||||
"roles": "Roles",
|
"roles": "Roles",
|
||||||
"series": "Series",
|
"series": "Series",
|
||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
"use_filter_panel_to_change_filter": "Use the filter panel to change the active filter",
|
"use_filter_panel_to_change_filter": "Use the filter panel to change the active filter",
|
||||||
"year": "year",
|
"year": "year"
|
||||||
"download": "Download",
|
|
||||||
"read": "Read"
|
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"keep_reading": "Keep Reading",
|
"keep_reading": "Keep Reading",
|
||||||
|
|
@ -423,7 +423,8 @@
|
||||||
"search_results_for": "Search results for \"{name}\""
|
"search_results_for": "Search results for \"{name}\""
|
||||||
},
|
},
|
||||||
"searchbox": {
|
"searchbox": {
|
||||||
"no_results": "No results"
|
"no_results": "No results",
|
||||||
|
"search_all": "Search all…"
|
||||||
},
|
},
|
||||||
"server": {
|
"server": {
|
||||||
"server_management": {
|
"server_management": {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue