diff --git a/client/components/Search/BookView/BookView.vue b/client/components/Search/BookView/BookView.vue new file mode 100644 index 0000000..75a2519 --- /dev/null +++ b/client/components/Search/BookView/BookView.vue @@ -0,0 +1,36 @@ + + + + + \ No newline at end of file diff --git a/client/components/Search/Search.vue b/client/components/Search/Search.vue index f5c6546..83fcf33 100644 --- a/client/components/Search/Search.vue +++ b/client/components/Search/Search.vue @@ -44,17 +44,29 @@ + > + + {{ search.author }} + +
+ > + + {{ search.series }} + +
+ > + + {{ search.title }} + +
- -
@@ -138,21 +142,40 @@
-
- {{ book.title }} -
{{ book.src }} +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ Серия: {{ book.series }} +
+
+ +
+ +
+
+
+
-
- +{{ hiddenCount }} результатов скрыты + {{ hiddenResultsMessage }}
+
@@ -203,6 +226,7 @@ import { reactive } from 'vue'; import PageScroller from './PageScroller/PageScroller.vue'; import SelectGenreDialog from './SelectGenreDialog/SelectGenreDialog.vue'; import SelectLangDialog from './SelectLangDialog/SelectLangDialog.vue'; +import BookView from './BookView/BookView.vue'; import authorBooksStorage from './authorBooksStorage'; import DivBtn from '../share/DivBtn.vue'; @@ -218,6 +242,7 @@ const componentOptions = { PageScroller, SelectGenreDialog, SelectLangDialog, + BookView, Dialog, DivBtn }, @@ -291,6 +316,7 @@ class Search { //settings expanded = []; + expandedSeries = []; showCounts = true; showDeleted = false; abCacheEnabled = true; @@ -353,6 +379,7 @@ class Search { this.search.limit = settings.limit; this.expanded = _.cloneDeep(settings.expanded); + this.expandedSeries = _.cloneDeep(settings.expandedSeries); this.showCounts = settings.showCounts; this.showDeleted = settings.showDeleted; this.abCacheEnabled = settings.abCacheEnabled; @@ -459,6 +486,10 @@ class Search { return `Найден${utils.wordEnding(this.totalFound, 2)} ${this.totalFound} автор${utils.wordEnding(this.totalFound)}`; } + get hiddenResultsMessage() { + return `+${this.hiddenCount} результат${utils.wordEnding(this.hiddenCount)} скрыты`; + } + updatePageCount() { const prevPageCount = this.pageCount; @@ -480,35 +511,47 @@ class Search { this.scrollToTop(); } - selectTitle(title) { - this.search.title = `=${title}`; + selectSeries(series) { + this.search.series = `=${series}`; + } + + bookEvent(event) { + switch (event.action) { + case 'titleClick': + this.search.title = `=${event.book.title}`; + break; + } } isExpanded(item) { return this.expanded.indexOf(item.author) >= 0; } + isExpandedSeries(seriesItem) { + return this.expandedSeries.indexOf(seriesItem.key) >= 0; + } + setSetting(name, newValue) { this.commit('setSettings', {[name]: _.cloneDeep(newValue)}); } expandAuthor(item) { const expanded = _.cloneDeep(this.expanded); - const author = item.author; + const key = item.author; if (!this.isExpanded(item)) { - expanded.push(author); + expanded.push(key); this.getBooks(item); - if (expanded.length > 10) { + if (expanded.length > 100) { expanded.shift(); } this.setSetting('expanded', expanded); this.ignoreScroll(); } else { - const i = expanded.indexOf(author); + const i = expanded.indexOf(key); if (i >= 0) { expanded.splice(i, 1); this.setSetting('expanded', expanded); @@ -516,6 +559,28 @@ class Search { } } + expandSeries(seriesItem) { + const expandedSeries = _.cloneDeep(this.expandedSeries); + const key = seriesItem.key; + + if (!this.isExpandedSeries(seriesItem)) { + expandedSeries.push(key); + + if (expandedSeries.length > 100) { + expandedSeries.shift(); + } + + this.setSetting('expandedSeries', expandedSeries); + this.ignoreScroll(); + } else { + const i = expandedSeries.indexOf(key); + if (i >= 0) { + expandedSeries.splice(i, 1); + this.setSetting('expandedSeries', expandedSeries); + } + } + } + getBookCount(item) { let result = ''; if (!this.showCounts || item.count === undefined) @@ -665,13 +730,56 @@ class Search { const filtered = this.filterBooks(loadedBooks); - filtered.sort((a, b) => a.title.localeCompare(b.title)); + const prepareBook = (book) => { + return { + key: book.id, + type: 'book', + title: book.title, + series: book.series, + } + }; + //объединение по сериям const books = []; + const seriesIndex = {}; for (const book of filtered) { - books.push({key: book.id, title: book.title, src: book}); + if (book.series) { + let index = seriesIndex[book.series]; + if (index === undefined) { + index = books.length; + books.push({ + key: `${item.author}-${book.series}`, + type: 'series', + series: book.series, + books: [], + }); + + seriesIndex[book.series] = index; + } + + books[index].books.push(prepareBook(book)); + } else { + books.push(prepareBook(book)); + } } + //сортировка + books.sort((a, b) => { + if (a.type == 'series') { + if (b.type == 'series') + return a.key.localeCompare(b.key); + else + return -1; + } else { + if (b.type == 'book') + return a.title.localeCompare(b.title); + else + return 1; + } + }); + + //сортировка внутри серий + item.books = books; } finally { this.getBooksFlag--; @@ -909,6 +1017,6 @@ export default vueComponent(Search); } .book-row { - margin-left: 50px; + margin-left: 70px; } diff --git a/client/store/root.js b/client/store/root.js index 3226b5a..c335931 100644 --- a/client/store/root.js +++ b/client/store/root.js @@ -4,6 +4,7 @@ const state = { settings: { limit: 50, expanded: [], + expandedSeries: [], showCounts: true, showDeleted: false, abCacheEnabled: true,