Browse Source

Работа над проектом

Book Pauk 2 years ago
parent
commit
705fce73f7

+ 10 - 0
client/components/Api/Api.vue

@@ -54,6 +54,16 @@ class Api {
         return response;
     }
 
+    async getBookList(authorId) {
+        const response = await this.request({action: 'get-book-list', authorId});
+
+        if (response.error) {
+            throw new Error(response.error);
+        }
+
+        return response;
+    }
+
     async getConfig() {
         const response = await this.request({action: 'get-config'});
 

+ 50 - 23
client/components/Search/Search.vue

@@ -83,23 +83,30 @@
 
             <!-- Формирование списка ------------------------------------------------------------------------>
             <div v-for="item in tableData" :key="item.key" class="column" :class="{'odd-author': item.num % 2}" style="font-size: 120%">
-                <div class="row items-center q-ml-md q-my-xs no-wrap">
-                    <div class="clickable q-mr-sm q-pa-xs">
-                        <div v-if="!isExpanded(item)" @click="expandAuthor(item, true)">
-                            <q-icon name="la la-plus-square" size="24px" />
-                        </div>
-                        <div v-else @click="expandAuthor(item, false)">
-                            <q-icon name="la la-minus-square" size="24px" />
-                        </div>
+                <div class="row items-center q-ml-md q-mr-xs no-wrap">
+                    <div style="width: 30px">
+                        <DivBtn v-if="tableData.length > 1" :icon-size="24" icon="la la-check-circle" @click="selectAuthor(item.author)">
+                            <q-tooltip :delay="1500" anchor="bottom right" content-style="font-size: 80%">
+                                Только этот автор
+                            </q-tooltip>
+                        </DivBtn>
                     </div>
 
-                    <div class="clickable" style="font-weight: bold" @click="authorClick(item.author)">
-                        {{ item.name }}
+                    <div class="row items-center clickable q-mr-sm q-pa-xs" @click="expandAuthor(item)">
+                        <div v-if="!isExpanded(item)">
+                            <q-icon name="la la-plus-square" size="28px" />
+                        </div>
+                        <div v-else>
+                            <q-icon name="la la-minus-square" size="28px" />
+                        </div>
+                        <div class="q-ml-xs" style="font-weight: bold">
+                            {{ item.name }}
+                        </div>
                     </div>
                 </div>
 
                 <div v-if="isExpanded(item) && item.books">
-                    {{ item.books[0] }}
+                    {{ item.books }}
                 </div>
             </div>
             <!-- Формирование списка конец ------------------------------------------------------------------>
@@ -118,6 +125,8 @@ import vueComponent from '../vueComponent.js';
 const { reactive } = require('@vue/reactivity');
 
 import PageScroller from './PageScroller/PageScroller.vue';
+import DivBtn from '../share/DivBtn.vue';
+
 import * as utils from '../../share/utils';
 
 import _ from 'lodash';
@@ -125,6 +134,7 @@ import _ from 'lodash';
 const componentOptions = {
     components: {
         PageScroller,
+        DivBtn
     },
     watch: {
         config() {
@@ -301,7 +311,7 @@ class Search {
             this.page = 1;
     }
 
-    authorClick(author) {
+    selectAuthor(author) {
         this.author = `=${author}`;
     }
 
@@ -309,11 +319,11 @@ class Search {
         return this.expanded.indexOf(item.author) >= 0;
     }
 
-    expandAuthor(item, expand = true) {
+    expandAuthor(item) {
         const expanded = _.cloneDeep(this.expanded);
         const author = item.author;
 
-        if (expand) {
+        if (!this.isExpanded(item)) {
             if (expanded.indexOf(author) < 0) {
                 expanded.push(author);
 
@@ -334,26 +344,39 @@ class Search {
         }
     }
 
-    async loadBooks() {
+    async loadBooks(authorId) {
+        let inSearch = true;
+        (async() => {
+            await utils.sleep(500);
+            if (inSearch)
+                this.loadingMessage = 'Загрузка списка книг...';
+        })();
+
+        try {
+            const result = await this.api.getBookList(authorId);
+
+            return result;
+        } catch (e) {
+            this.$root.stdDialog.alert(e.message, 'Ошибка');
+            return;
+        } finally {
+            inSearch = false;
+            this.loadingMessage = '';
+        }
     }
 
     async getBooks(item) {
         if (item.books)
             return;
-        
-        await utils.sleep(1000);
-        item.books = [{name: 'book1'}];
+
+        item.books = await this.loadBooks(item.key);
     }
 
     async updateTableData() {
         let result = [];
 
-        const authors = this.searchResult.author;
-        if (authors.length == 1) {
-            this.expandAuthor(authors[0]);
-        }
-
         const expandedSet = new Set(this.expanded);
+        const authors = this.searchResult.author;
 
         let num = 0;
         for (const rec of authors) {
@@ -373,6 +396,10 @@ class Search {
             result.push(item);
         }
 
+        if (result.length == 1 && !this.isExpanded(result[0])) {
+            this.expandAuthor(result[0]);
+        }        
+
         this.tableData = result;
     }
 

+ 54 - 0
client/components/share/DivBtn.vue

@@ -0,0 +1,54 @@
+<template>
+    <div ref="btn" class="clickable row justify-center items-center">
+        <q-icon :name="icon" :size="`${iconSize}px`" />
+        <slot></slot>
+    </div>
+</template>
+
+<script>
+//-----------------------------------------------------------------------------
+import vueComponent from '../vueComponent.js';
+
+//import * as utils from '../../share/utils';
+
+const componentOptions = {
+    watch: {
+        size() {
+            this.updateSizes();
+        },
+    }
+};
+class DivBtn {
+    _options = componentOptions;
+    _props = {
+        size: { type: Number, default: 24 },
+        icon: { type: String, default: '' },
+        iconSize: { type: Number, default: 14 },
+        round: { type: Boolean },
+    };
+
+    created() {
+    }
+
+    mounted() {
+        this.updateSizes();
+    }
+
+    updateSizes() {
+        this.$refs.btn.style.width = `${this.size}px`;
+        this.$refs.btn.style.height = `${this.size}px`;
+        if (this.round)
+            this.$refs.btn.style.borderRadius = `${this.size}px`;
+    }
+}
+
+export default vueComponent(DivBtn);
+//-----------------------------------------------------------------------------
+</script>
+
+<style scoped>
+.clickable-icon {
+    cursor: pointer;
+}
+
+</style>

+ 2 - 2
client/quasar.js

@@ -21,7 +21,7 @@ import {QIcon} from 'quasar/src/components/icon';
 //import {QSeparator} from 'quasar/src/components/separator';
 //import {QList} from 'quasar/src/components/item';
 //import {QItem, QItemSection, QItemLabel} from 'quasar/src/components/item';
-//import {QTooltip} from 'quasar/src/components/tooltip';
+import {QTooltip} from 'quasar/src/components/tooltip';
 import {QSpinner} from 'quasar/src/components/spinner';
 //import {QTable, QTh, QTr, QTd} from 'quasar/src/components/table';
 //import {QCheckbox} from 'quasar/src/components/checkbox';
@@ -52,7 +52,7 @@ const components = {
     //QSeparator,
     //QList,
     //QItem, QItemSection, QItemLabel,
-    //QTooltip,
+    QTooltip,
     QSpinner,
     //QTable, QTh, QTr, QTd,
     //QCheckbox,

+ 12 - 1
server/controllers/WebSocketController.js

@@ -4,7 +4,7 @@ const _ = require('lodash');
 const WorkerState = require('../core/WorkerState');//singleton
 const WebWorker = require('../core/WebWorker');//singleton
 const log = new (require('../core/AppLogger'))().log;//singleton
-//const utils = require('../core/utils');
+const utils = require('../core/utils');
 
 const cleanPeriod = 1*60*1000;//1 минута
 const closeSocketOnIdle = 5*60*1000;//5 минут
@@ -68,6 +68,8 @@ class WebSocketController {
                     await this.getWorkerState(req, ws); break;
                 case 'search':
                     await this.search(req, ws); break;
+                case 'get-book-list':
+                    await this.getBookList(req, ws); break;
 
                 default:
                     throw new Error(`Action not found: ${req.action}`);
@@ -122,6 +124,15 @@ class WebSocketController {
 
         this.send(result, req, ws);
     }
+
+    async getBookList(req, ws) {
+        if (!utils.hasProp(req, 'authorId'))
+            throw new Error(`authorId is empty`);
+
+        const result = await this.webWorker.getBookList(req.authorId);
+
+        this.send(result, req, ws);
+    }
 }
 
 module.exports = WebSocketController;

+ 37 - 1
server/core/DbSearcher.js

@@ -193,7 +193,7 @@ class DbSearcher {
         //сначала попробуем найти в кеше
         const q = query;
         const keyArr = [q.author, q.series, q.title, q.genre, q.lang];
-        const keyStr = keyArr.join('');
+        const keyStr = `query-${keyArr.join('')}`;
         
         if (!keyStr) {//пустой запрос
             if (db.searchCache.authorIdsAll)
@@ -262,6 +262,42 @@ class DbSearcher {
         }
     }
 
+    async getBookList(authorId) {
+        if (this.closed)
+            throw new Error('DbSearcher closed');
+
+        this.searchFlag++;
+
+        try {
+            const db = this.db;
+
+            //выборка автора по authorId
+            const rows = await db.select({
+                table: 'author',
+                map: `(r) => ({author: r.author, bookId: r.bookId})`,
+                where: `@@id(${db.esc(authorId)})`
+            });
+
+            let author = '';
+            let result = [];
+
+            if (rows.length) {
+                author = rows[0].author;
+
+                //выборка книг по bookId
+                result = await db.select({
+                    table: 'book',
+                    //map: `(r) => ({})`,
+                    where: `@@id(${db.esc(rows[0].bookId)})`,
+                });
+            }
+
+            return {author, books: result};
+        } finally {
+            this.searchFlag--;
+        }
+    }
+
     async periodicCleanCache() {
         this.timer = null;
         const cleanInterval = 5*1000;//this.config.cacheCleanInterval*60*1000;

+ 7 - 1
server/core/WebWorker.js

@@ -195,7 +195,13 @@ class WebWorker {
             totalFound: result.totalFound,
             inpxHash: (config.inpxHash ? config.inpxHash : ''),
         };
-    }    
+    }
+
+    async getBookList(authorId) {
+        this.checkMyState();
+
+        return await this.dbSearcher.getBookList(authorId);
+    }
 
     async logServerStats() {
         while (1) {// eslint-disable-line