Bladeren bron

Работа над TitleList

Book Pauk 2 jaren geleden
bovenliggende
commit
4cacecc13b

+ 3 - 3
client/components/Search/AuthorList/AuthorList.vue

@@ -6,7 +6,7 @@
         <LoadingMessage :message="loadingMessage2" z-index="1" />
         <LoadingMessage :message="loadingMessage2" z-index="1" />
 
 
         <!-- Формирование списка ------------------------------------------------------------------------>
         <!-- Формирование списка ------------------------------------------------------------------------>
-        <div v-for="item in tableData" :key="item.key" class="column" :class="{'odd-author': item.num % 2}" style="font-size: 120%">
+        <div v-for="item in tableData" :key="item.key" class="column" :class="{'odd-item': item.num % 2}" style="font-size: 120%">
             <div class="row items-center q-ml-md q-mr-xs no-wrap">
             <div class="row items-center q-ml-md q-mr-xs no-wrap">
                 <div class="row items-center clickable2 q-py-xs no-wrap" @click="expandAuthor(item)">
                 <div class="row items-center clickable2 q-py-xs no-wrap" @click="expandAuthor(item)">
                     <div style="min-width: 30px">
                     <div style="min-width: 30px">
@@ -106,7 +106,7 @@
 
 
                 <div v-if="isExpandedAuthor(item) && item.books && !item.books.length" class="book-row row items-center">
                 <div v-if="isExpandedAuthor(item) && item.books && !item.books.length" class="book-row row items-center">
                     <q-icon class="la la-meh q-mr-xs" size="24px" />
                     <q-icon class="la la-meh q-mr-xs" size="24px" />
-                    По каждому из заданных критериев у этого автора были найдены разные книги, но нет полного совпадения                    
+                    По каждому из заданных критериев у этого автора были найдены разные книги, но нет полного совпадения
                 </div>
                 </div>
             </div>
             </div>
 
 
@@ -456,7 +456,7 @@ export default vueComponent(AuthorList);
     cursor: pointer;
     cursor: pointer;
 }
 }
 
 
-.odd-author {
+.odd-item {
     background-color: #e8e8e8;
     background-color: #e8e8e8;
 }
 }
 
 

+ 3 - 0
client/components/Search/BaseList.js

@@ -198,6 +198,9 @@ export default class BaseList {
             case 'authorClick':
             case 'authorClick':
                 this.selectAuthor(event.book.author);
                 this.selectAuthor(event.book.author);
                 break;
                 break;
+            case 'seriesClick':
+                this.selectSeries(event.book.series);
+                break;
             case 'titleClick':
             case 'titleClick':
                 this.selectTitle(event.book.title);
                 this.selectTitle(event.book.title);
                 break;
                 break;

+ 29 - 2
client/components/Search/BookView/BookView.vue

@@ -31,7 +31,7 @@
                 </div>
                 </div>
             </div>
             </div>
 
 
-            <div class="q-ml-sm row items-center">
+            <div v-if="!titleList" class="q-ml-sm row items-center">
                 {{ book.serno ? `${book.serno}. ` : '' }}
                 {{ book.serno ? `${book.serno}. ` : '' }}
                 <div v-if="showAuthor && book.author">
                 <div v-if="showAuthor && book.author">
                     <span class="clickable2 text-green-10" @click="selectAuthor">{{ bookAuthor }}</span>
                     <span class="clickable2 text-green-10" @click="selectAuthor">{{ bookAuthor }}</span>
@@ -40,6 +40,20 @@
                 </div>
                 </div>
                 <span v-else class="clickable2" :class="titleColor" @click="selectTitle">{{ book.title }}</span>
                 <span v-else class="clickable2" :class="titleColor" @click="selectTitle">{{ book.title }}</span>
             </div>
             </div>
+            <div v-else class="q-ml-sm row items-center">
+                <span class="clickable2" :class="titleColor" @click="selectTitle">{{ book.title }}</span>
+
+                <div v-if="book.author || bookSeries" class="row">
+                    &nbsp;-&nbsp;
+                    <div v-if="book.author">
+                        <span class="clickable2 text-green-10" @click="selectAuthor">{{ bookAuthor }}</span>
+                        &nbsp;
+                    </div>
+                    <div v-if="bookSeries">
+                        <span class="clickable2" @click="selectSeries">{{ bookSeries }}</span>
+                    </div>
+                </div>
+            </div>
         </div>
         </div>
 
 
         <div class="q-ml-sm">
         <div class="q-ml-sm">
@@ -88,6 +102,7 @@ class BookView {
         genreMap: Object,
         genreMap: Object,
         showAuthor: Boolean,
         showAuthor: Boolean,
         showReadLink: Boolean,
         showReadLink: Boolean,
+        titleList: Boolean,
         titleColor: { type: String, default: 'text-blue-10'},
         titleColor: { type: String, default: 'text-blue-10'},
     };
     };
 
 
@@ -112,7 +127,7 @@ class BookView {
     }
     }
 
 
     get bookAuthor() {
     get bookAuthor() {
-        if (this.showAuthor && this.book.author) {
+        if ((this.showAuthor || this.titleList) && this.book.author) {
             let a = this.book.author.split(',');
             let a = this.book.author.split(',');
             return a.slice(0, 2).join(', ') + (a.length > 2 ? ' и др.' : '');
             return a.slice(0, 2).join(', ') + (a.length > 2 ? ' и др.' : '');
         }
         }
@@ -120,6 +135,14 @@ class BookView {
         return '';
         return '';
     }
     }
 
 
+    get bookSeries() {
+        if (this.book.series) {
+            return `(${this.book.series})`;
+        }
+
+        return '';
+    }
+
     get bookSize() {
     get bookSize() {
         let size = this.book.size/1024;
         let size = this.book.size/1024;
         let unit = 'KB';
         let unit = 'KB';
@@ -155,6 +178,10 @@ class BookView {
         this.$emit('bookEvent', {action: 'authorClick', book: this.book});
         this.$emit('bookEvent', {action: 'authorClick', book: this.book});
     }
     }
 
 
+    selectSeries() {
+        this.$emit('bookEvent', {action: 'seriesClick', book: this.book});
+    }
+
     selectTitle() {
     selectTitle() {
         this.$emit('bookEvent', {action: 'titleClick', book: this.book});
         this.$emit('bookEvent', {action: 'titleClick', book: this.book});
     }
     }

+ 1 - 1
client/components/Search/LoadingMessage/LoadingMessage.vue

@@ -1,5 +1,5 @@
 <template>
 <template>
-    <div v-show="message" class="fit row justify-center items-center" :style="`position: absolute; top: 0; left: 0; background-color: rgba(0, 0, 0, 0.2); z-index: ${zIndex}`">
+    <div v-show="message" class="fit row justify-center items-center" :style="`position: fixed; top: 0; left: 0; background-color: rgba(0, 0, 0, 0.2); z-index: ${zIndex}`">
         <div class="bg-white row justify-center items-center q-px-lg" style="min-width: 180px; height: 50px; border-radius: 10px; box-shadow: 2px 2px 10px #333333">
         <div class="bg-white row justify-center items-center q-px-lg" style="min-width: 180px; height: 50px; border-radius: 10px; box-shadow: 2px 2px 10px #333333">
             <q-icon class="la la-spinner icon-rotate text-blue-8" size="28px" />
             <q-icon class="la la-spinner icon-rotate text-blue-8" size="28px" />
             <div class="q-ml-sm">
             <div class="q-ml-sm">

+ 4 - 1
client/components/Search/Search.vue

@@ -169,6 +169,7 @@ import vueComponent from '../vueComponent.js';
 
 
 import AuthorList from './AuthorList/AuthorList.vue';
 import AuthorList from './AuthorList/AuthorList.vue';
 import SeriesList from './SeriesList/SeriesList.vue';
 import SeriesList from './SeriesList/SeriesList.vue';
+import TitleList from './TitleList/TitleList.vue';
 
 
 import PageScroller from './PageScroller/PageScroller.vue';
 import PageScroller from './PageScroller/PageScroller.vue';
 import SelectGenreDialog from './SelectGenreDialog/SelectGenreDialog.vue';
 import SelectGenreDialog from './SelectGenreDialog/SelectGenreDialog.vue';
@@ -186,13 +187,14 @@ import _ from 'lodash';
 const route2component = {
 const route2component = {
     'author': {component: 'AuthorList', label: 'Авторы'},
     'author': {component: 'AuthorList', label: 'Авторы'},
     'series': {component: 'SeriesList', label: 'Серии'},
     'series': {component: 'SeriesList', label: 'Серии'},
-    'book': {component: 'AuthorList', label: 'Книги'},
+    'book': {component: 'TitleList', label: 'Книги'},
 };
 };
 
 
 const componentOptions = {
 const componentOptions = {
     components: {
     components: {
         AuthorList,
         AuthorList,
         SeriesList,
         SeriesList,
+        TitleList,
         PageScroller,
         PageScroller,
         SelectGenreDialog,
         SelectGenreDialog,
         SelectLangDialog,
         SelectLangDialog,
@@ -263,6 +265,7 @@ const componentOptions = {
         selectedList(newValue) {
         selectedList(newValue) {
             this.selectedListComponent = (route2component[newValue] ? route2component[newValue].component : null);
             this.selectedListComponent = (route2component[newValue] ? route2component[newValue].component : null);
             this.pageCount = 1;
             this.pageCount = 1;
+            this.foundCountMessage = '';
 
 
             if (this.getListRoute() != newValue) {
             if (this.getListRoute() != newValue) {
                 this.updateRouteQueryFromSearch();
                 this.updateRouteQueryFromSearch();

+ 3 - 3
client/components/Search/SeriesList/SeriesList.vue

@@ -6,7 +6,7 @@
         <LoadingMessage :message="loadingMessage2" z-index="1" />
         <LoadingMessage :message="loadingMessage2" z-index="1" />
 
 
         <!-- Формирование списка ------------------------------------------------------------------------>
         <!-- Формирование списка ------------------------------------------------------------------------>
-        <div v-for="item in tableData" :key="item.key" class="column" :class="{'odd-author': item.num % 2}" style="font-size: 120%">
+        <div v-for="item in tableData" :key="item.key" class="column" :class="{'odd-item': item.num % 2}" style="font-size: 120%">
             <div class="row items-center q-ml-md q-mr-xs no-wrap">
             <div class="row items-center q-ml-md q-mr-xs no-wrap">
                 <div class="row items-center clickable2 q-py-xs no-wrap" @click="expandSeries(item)">
                 <div class="row items-center clickable2 q-py-xs no-wrap" @click="expandSeries(item)">
                     <div style="min-width: 30px">
                     <div style="min-width: 30px">
@@ -56,7 +56,7 @@
 
 
                 <div v-if="!item.showAllBooks && isExpandedSeries(item) && item.books && !item.books.length" class="book-row row items-center">
                 <div v-if="!item.showAllBooks && isExpandedSeries(item) && item.books && !item.books.length" class="book-row row items-center">
                     <q-icon class="la la-meh q-mr-xs" size="24px" />
                     <q-icon class="la la-meh q-mr-xs" size="24px" />
-                    По каждому из заданных критериев у этой серии были найдены разные книги, но нет полного совпадения                    
+                    Возможно у этой серии были найдены книги, помеченные как удаленные, но подходящие по критериям
                 </div>
                 </div>
 
 
                 <div
                 <div
@@ -288,7 +288,7 @@ export default vueComponent(SeriesList);
     cursor: pointer;
     cursor: pointer;
 }
 }
 
 
-.odd-author {
+.odd-item {
     background-color: #e8e8e8;
     background-color: #e8e8e8;
 }
 }
 
 

+ 158 - 0
client/components/Search/TitleList/TitleList.vue

@@ -0,0 +1,158 @@
+<template>
+    <div>
+        <a ref="download" style="display: none;"></a>
+
+        <LoadingMessage :message="loadingMessage" z-index="2" />
+        <LoadingMessage :message="loadingMessage2" z-index="1" />
+
+        <!-- Формирование списка ------------------------------------------------------------------------>
+        <div v-for="item in tableData" :key="item.key" class="column" :class="{'odd-item': item.num % 2}" style="font-size: 120%">
+            <BookView
+                class="book-row"
+                title-list
+                :book="item.book" :genre-map="genreMap" :show-read-link="showReadLink" @book-event="bookEvent"
+            />
+            <BookView
+                v-for="book in item.books" :key="book.id"
+                :book="book" :genre-map="genreMap"
+                class="book-row"
+                title-list
+                :show-read-link="showReadLink"
+                @book-event="bookEvent"
+            />
+        </div>
+        <!-- Формирование списка конец ------------------------------------------------------------------>
+
+        <div v-if="!refreshing && !tableData.length" class="row items-center q-ml-md" style="font-size: 120%">
+            <q-icon class="la la-meh q-mr-xs" size="28px" />
+            Поиск не дал результатов
+        </div>
+    </div>
+</template>
+
+<script>
+//-----------------------------------------------------------------------------
+import vueComponent from '../../vueComponent.js';
+import { reactive } from 'vue';
+
+import BaseList from '../BaseList';
+
+import * as utils from '../../../share/utils';
+
+import _ from 'lodash';
+
+class TitleList extends BaseList {
+    get foundCountMessage() {
+        return `Найден${utils.wordEnding(this.list.totalFound, 4)} ${this.list.totalFound} уникальн${utils.wordEnding(this.list.totalFound, 6)} назван${utils.wordEnding(this.list.totalFound, 3)}`;
+    }
+
+    async updateTableData() {
+        let result = [];
+
+        const title = this.searchResult.title;
+        if (!title)
+            return;
+
+        let num = 0;
+        for (const rec of title) {
+            const item = reactive({
+                key: rec.id,
+                title: rec.title,
+                num,
+
+                book: false,
+                books: [],
+            });
+
+            if (rec.books) {
+                const filtered = this.filterBooks(rec.books);
+
+                for (let i = 0; i < filtered.length; i++) {
+                    if (i === 0)
+                        item.book = filtered[i];
+                    else
+                        item.books.push(filtered[i]);                    
+                }
+
+                if (filtered.length) {
+                    num++;
+                    result.push(item);
+                }
+            }
+        }
+
+        this.tableData = result;
+    }
+
+    async refresh() {
+        //параметры запроса
+        let newQuery = _.cloneDeep(this.search);
+        newQuery = newQuery.setDefaults(newQuery);
+        delete newQuery.setDefaults;
+        newQuery.offset = (newQuery.page - 1)*newQuery.limit;
+
+        if (_.isEqual(newQuery, this.prevQuery))
+            return;
+        this.prevQuery = newQuery;
+
+        this.queryExecute = newQuery;
+
+        if (this.refreshing)
+            return;
+
+        this.refreshing = true;
+
+        (async() => {
+            await utils.sleep(500);
+            if (this.refreshing)
+                this.loadingMessage = 'Поиск книг...';
+        })();
+
+        try {
+            while (this.queryExecute) {
+                const query = this.queryExecute;
+                this.queryExecute = null;
+
+                try {
+                    const result = await this.api.titleSearch(query);
+
+                    this.list.queryFound = result.title.length;
+                    this.list.totalFound = result.totalFound;
+                    this.list.inpxHash = result.inpxHash;
+
+                    this.searchResult = result;
+
+                    await utils.sleep(1);
+                    if (!this.queryExecute) {
+                        await this.updateTableData();
+                        this.scrollToTop();
+                        this.highlightPageScroller(query);
+                    }
+                } catch (e) {
+                    this.$root.stdDialog.alert(e.message, 'Ошибка');
+                }
+            }
+        } finally {
+            this.refreshing = false;
+            this.loadingMessage = '';
+        }
+    }
+}
+
+export default vueComponent(TitleList);
+//-----------------------------------------------------------------------------
+</script>
+
+<style scoped>
+.clickable2 {
+    cursor: pointer;
+}
+
+.odd-item {
+    background-color: #e8e8e8;
+}
+
+.book-row {
+    margin-left: 50px;
+}
+</style>

+ 2 - 0
client/share/utils.js

@@ -40,6 +40,8 @@ export function wordEnding(num, type = 0) {
         ['о', '', 'о', 'о', 'о', 'о', 'о', 'о', 'о', 'о'],
         ['о', '', 'о', 'о', 'о', 'о', 'о', 'о', 'о', 'о'],
         ['ий', 'ие', 'ия', 'ия', 'ия', 'ий', 'ий', 'ий', 'ий', 'ий'],
         ['ий', 'ие', 'ия', 'ия', 'ия', 'ий', 'ий', 'ий', 'ий', 'ий'],
         ['о', 'а', 'о', 'о', 'о', 'о', 'о', 'о', 'о', 'о'],
         ['о', 'а', 'о', 'о', 'о', 'о', 'о', 'о', 'о', 'о'],
+        ['ок', 'ка', 'ки', 'ки', 'ки', 'ок', 'ок', 'ок', 'ок', 'ок'],
+        ['ых', 'ое', 'ых', 'ых', 'ых', 'ых', 'ых', 'ых', 'ых', 'ых'],
     ];
     ];
     const deci = num % 100;
     const deci = num % 100;
     if (deci > 10 && deci < 20) {
     if (deci > 10 && deci < 20) {