Ver código fonte

Добавлена возможность отображения всех книг серии

Book Pauk 2 anos atrás
pai
commit
58f2483b97

+ 2 - 2
client/components/Api/Api.vue

@@ -221,8 +221,8 @@ class Api {
         return response;
         return response;
     }
     }
 
 
-    async getSeriesBookList(seriesId) {
-        const response = await this.request({action: 'get-series-book-list', seriesId});
+    async getSeriesBookList(series) {
+        const response = await this.request({action: 'get-series-book-list', series});
 
 
         if (response.error) {
         if (response.error) {
             throw new Error(response.error);
             throw new Error(response.error);

+ 44 - 30
client/components/Search/BookView/BookView.vue

@@ -1,38 +1,40 @@
 <template>
 <template>
     <div class="row items-center q-my-sm">
     <div class="row items-center q-my-sm">
-        <div v-if="showRate || showDeleted">
-            <div v-if="showRate && !book.del">
-                <div v-if="book.librate">
-                    <q-knob
-                        :model-value="book.librate"
-                        :min="0"
-                        :max="5"
-                        size="18px"
-                        font-size="12px"
-                        :thickness="1"
-                        :color="rateColor"
-                        track-color="grey-4"
-                        readonly
-                    />
-
-                    <q-tooltip :delay="500" anchor="top middle" content-style="font-size: 80%" max-width="400px">
-                        Оценка {{ book.librate }}
-                    </q-tooltip>
+        <div class="row no-wrap">
+            <div v-if="showRate || showDeleted">
+                <div v-if="showRate && !book.del">
+                    <div v-if="book.librate">
+                        <q-knob
+                            :model-value="book.librate"
+                            :min="0"
+                            :max="5"
+                            size="18px"
+                            font-size="12px"
+                            :thickness="1"
+                            :color="rateColor"
+                            track-color="grey-4"
+                            readonly
+                        />
+
+                        <q-tooltip :delay="500" anchor="top middle" content-style="font-size: 80%" max-width="400px">
+                            Оценка {{ book.librate }}
+                        </q-tooltip>
+                    </div>
+                    <div v-else style="width: 18px" />
+                </div>
+                <div v-else class="row justify-center" style="width: 18px">
+                    <q-icon v-if="book.del" class="la la-trash text-bold text-red" size="18px">
+                        <q-tooltip :delay="500" anchor="top middle" content-style="font-size: 80%" max-width="400px">
+                            Удалено
+                        </q-tooltip>
+                    </q-icon>
                 </div>
                 </div>
-                <div v-else style="width: 18px" />
-            </div>
-            <div v-else class="row justify-center" style="width: 18px">
-                <q-icon v-if="book.del" class="la la-trash text-bold text-red" size="18px">
-                    <q-tooltip :delay="500" anchor="top middle" content-style="font-size: 80%" max-width="400px">
-                        Удалено
-                    </q-tooltip>
-                </q-icon>
             </div>
             </div>
-        </div>
 
 
-        <div class="q-ml-sm clickable2" @click="selectTitle">
-            {{ book.serno ? `${book.serno}. ` : '' }}
-            <span class="text-blue-10">{{ book.title }}</span>
+            <div class="q-ml-sm clickable2" @click="selectTitle">
+                {{ book.serno ? `${book.serno}. ` : '' }}
+                <span class="text-blue-10">{{ bookTitle }}</span>
+            </div>
         </div>
         </div>
 
 
         <div class="q-ml-sm">
         <div class="q-ml-sm">
@@ -79,6 +81,7 @@ class BookView {
     _props = {
     _props = {
         book: Object,
         book: Object,
         genreTree: Array,
         genreTree: Array,
+        showAuthor: Boolean,
     };
     };
 
 
     showRate = true;
     showRate = true;
@@ -105,6 +108,17 @@ class BookView {
         return this.$store.state.settings;
         return this.$store.state.settings;
     }
     }
 
 
+    get bookTitle() {
+        if (this.showAuthor && this.book.author) {
+            let a = this.book.author.split(',');
+            const author = a.slice(0, 2).join(', ') + (a.length > 2 ? ' и др.' : '');
+
+            return `${author} - ${this.book.title}`;
+        } else {
+            return this.book.title;
+        }
+    }
+
     get bookSize() {
     get bookSize() {
         let size = this.book.size/1024;
         let size = this.book.size/1024;
         let unit = 'KB';
         let unit = 'KB';

+ 61 - 17
client/components/Search/Search.vue

@@ -172,8 +172,22 @@
                                 </div>
                                 </div>
                             </div>
                             </div>
 
 
-                            <div v-if="isExpandedSeries(book) && book.books" class="book-row column">
-                                <BookView v-for="subbook in book.books" :key="subbook.key" :book="subbook" :genre-tree="genreTree" @book-event="bookEvent" />
+                            <div v-if="isExpandedSeries(book) && book.books">
+                                <div v-if="book.showAllBooks" class="book-row column">
+                                    <BookView v-for="subbook in book.allBooks" :key="subbook.key" :book="subbook" show-author :genre-tree="genreTree" @book-event="bookEvent" />
+                                </div>
+                                <div v-else class="book-row column">
+                                    <BookView v-for="subbook in book.books" :key="subbook.key" :book="subbook" :genre-tree="genreTree" @book-event="bookEvent" />
+                                </div>
+
+                                <div v-if="book.allBooks" class="q-my-sm clickable" style="margin-left: 100px" @click="book.showAllBooks = !book.showAllBooks">
+                                    <div v-if="book.showAllBooks">
+                                        Показать только найденные
+                                    </div>
+                                    <div v-else>
+                                        Показать все книги серии
+                                    </div>
+                                </div>
                             </div>
                             </div>
                         </div>
                         </div>
                         <BookView v-else :book="book" :genre-tree="genreTree" @book-event="bookEvent" />
                         <BookView v-else :book="book" :genre-tree="genreTree" @book-event="bookEvent" />
@@ -191,7 +205,7 @@
                 </div>
                 </div>
             </div>
             </div>
             <!-- Формирование списка конец ------------------------------------------------------------------>
             <!-- Формирование списка конец ------------------------------------------------------------------>
-            
+
             <div v-if="ready && !refreshing && !tableData.length" class="q-ml-md" style="font-size: 120%">
             <div v-if="ready && !refreshing && !tableData.length" class="q-ml-md" style="font-size: 120%">
                 Поиск не дал результатов
                 Поиск не дал результатов
             </div>
             </div>
@@ -851,6 +865,8 @@ class Search {
                 expandedSeries.shift();
                 expandedSeries.shift();
             }
             }
 
 
+            this.getSeriesBooks(seriesItem); //no await
+
             this.setSetting('expandedSeries', expandedSeries);
             this.setSetting('expandedSeries', expandedSeries);
             this.ignoreScroll();
             this.ignoreScroll();
         } else {
         } else {
@@ -879,35 +895,52 @@ class Search {
                 result = await this.api.getBookList(authorId);
                 result = await this.api.getBookList(authorId);
             }
             }
 
 
-            return JSON.parse(result.books);
+            return (result.books ? JSON.parse(result.books) : []);
         } catch (e) {
         } catch (e) {
             this.$root.stdDialog.alert(e.message, 'Ошибка');
             this.$root.stdDialog.alert(e.message, 'Ошибка');
         }
         }
     }
     }
 
 
-    async loadSeriesBooks(seriesId) {
+    async loadSeriesBooks(series) {
         try {
         try {
             let result;
             let result;
 
 
             if (this.abCacheEnabled) {
             if (this.abCacheEnabled) {
-                const key = `series-${seriesId}-${this.inpxHash}`;
+                const key = `series-${series}-${this.inpxHash}`;
                 const data = await authorBooksStorage.getData(key);
                 const data = await authorBooksStorage.getData(key);
                 if (data) {
                 if (data) {
                     result = JSON.parse(data);
                     result = JSON.parse(data);
                 } else {
                 } else {
-                    result = await this.api.getBookList(seriesId);
+                    result = await this.api.getSeriesBookList(series);
                     await authorBooksStorage.setData(key, JSON.stringify(result));
                     await authorBooksStorage.setData(key, JSON.stringify(result));
                 }
                 }
             } else {
             } else {
-                result = await this.api.getBookList(seriesId);
+                result = await this.api.getSeriesBookList(series);
             }
             }
 
 
-            return JSON.parse(result.books);
+            return (result.books ? JSON.parse(result.books) : []);
         } catch (e) {
         } catch (e) {
             this.$root.stdDialog.alert(e.message, 'Ошибка');
             this.$root.stdDialog.alert(e.message, 'Ошибка');
         }
         }
     }
     }
 
 
+    async getSeriesBooks(seriesItem) {
+        //асинхронно подгружаем все книги серии, блокируем повторный вызов
+        if (seriesItem.allBooks === null) {
+            seriesItem.allBooks = undefined;
+            (async() => {
+                seriesItem.allBooks = await this.loadSeriesBooks(seriesItem.series);
+
+                if (seriesItem.allBooks) {
+                    seriesItem.allBooks = seriesItem.allBooks.filter(book => (this.showDeleted || !book.del));
+                    this.sortSeriesBooks(seriesItem.allBooks);
+                } else {
+                    seriesItem.allBooks = null;
+                }
+            })();
+        }
+    }
+
     filterBooks(loadedBooks) {
     filterBooks(loadedBooks) {
         const s = this.search;
         const s = this.search;
 
 
@@ -1016,6 +1049,15 @@ class Search {
         }
         }
     }
     }
 
 
+    sortSeriesBooks(books) {
+        books.sort((a, b) => {
+            const dserno = (a.serno || Number.MAX_VALUE) - (b.serno || Number.MAX_VALUE);
+            const dtitle = a.title.localeCompare(b.title);
+            const dext = a.ext.localeCompare(b.ext);
+            return (dserno ? dserno : (dtitle ? dtitle : dext));
+        });
+    }
+
     async getBooks(item) {
     async getBooks(item) {
         if (item.books) {
         if (item.books) {
             if (item.count > maxItemCount) {
             if (item.count > maxItemCount) {
@@ -1064,13 +1106,15 @@ class Search {
                     let index = seriesIndex[book.series];
                     let index = seriesIndex[book.series];
                     if (index === undefined) {
                     if (index === undefined) {
                         index = books.length;
                         index = books.length;
-                        books.push({
+                        books.push(reactive({
                             key: `${item.author}-${book.series}`,
                             key: `${item.author}-${book.series}`,
                             type: 'series',
                             type: 'series',
                             series: book.series,
                             series: book.series,
+                            allBooks: null,
+                            showAllBooks: false,
 
 
                             books: [],
                             books: [],
-                        });
+                        }));
 
 
                         seriesIndex[book.series] = index;
                         seriesIndex[book.series] = index;
                     }
                     }
@@ -1093,12 +1137,12 @@ class Search {
             //сортировка внутри серий
             //сортировка внутри серий
             for (const book of books) {
             for (const book of books) {
                 if (book.type == 'series') {
                 if (book.type == 'series') {
-                    book.books.sort((a, b) => {
-                        const dserno = (a.serno || Number.MAX_VALUE) - (b.serno || Number.MAX_VALUE);
-                        const dtitle = a.title.localeCompare(b.title);
-                        const dext = a.ext.localeCompare(b.ext);
-                        return (dserno ? dserno : (dtitle ? dtitle : dext));
-                    });
+                    this.sortSeriesBooks(book.books);
+
+                    //асинхронно подгрузим все книги серии, если она раскрыта
+                    if (this.isExpandedSeries(book)) {
+                        this.getSeriesBooks(book);//no await
+                    }
                 }
                 }
             }
             }
 
 

+ 3 - 3
server/controllers/WebSocketController.js

@@ -153,10 +153,10 @@ class WebSocketController {
     }
     }
 
 
     async getSeriesBookList(req, ws) {
     async getSeriesBookList(req, ws) {
-        if (!utils.hasProp(req, 'seriesId'))
-            throw new Error(`seriesId is empty`);
+        if (!utils.hasProp(req, 'series'))
+            throw new Error(`series is empty`);
 
 
-        const result = await this.webWorker.getSeriesBookList(req.seriesId);
+        const result = await this.webWorker.getSeriesBookList(req.series);
 
 
         this.send(result, req, ws);
         this.send(result, req, ws);
     }
     }

+ 4 - 3
server/core/DbSearcher.js

@@ -292,7 +292,7 @@ class DbSearcher {
         }
         }
     }
     }
 
 
-    async getSeriesBookList(seriesId) {
+    async getSeriesBookList(series) {
         if (this.closed)
         if (this.closed)
             throw new Error('DbSearcher closed');
             throw new Error('DbSearcher closed');
 
 
@@ -301,10 +301,11 @@ class DbSearcher {
         try {
         try {
             const db = this.db;
             const db = this.db;
 
 
-            //выборка серии по seriesId
+            series = series.toLowerCase();
+            //выборка серии по названию серии
             const rows = await db.select({
             const rows = await db.select({
                 table: 'series',
                 table: 'series',
-                where: `@@id(${db.esc(seriesId)})`
+                where: `@@dirtyIndexLR('value', ${db.esc(series)}, ${db.esc(series)})`
             });
             });
 
 
             return {books: (rows.length ? rows[0].books : '')};
             return {books: (rows.length ? rows[0].books : '')};