Prechádzať zdrojové kódy

Работа над поиском по типу файла

Book Pauk 2 rokov pred
rodič
commit
d6aa43d01b

+ 28 - 2
client/components/Search/Search.vue

@@ -160,7 +160,7 @@
                             <div class="q-mx-xs" />
                             <q-input
                                 v-model="librateNames" :maxlength="inputMaxLength" :debounce="inputDebounce"
-                                class="q-mt-xs col-1" :bg-color="inputBgColor()" input-style="cursor: pointer" style="min-width: 90px;" label="Оценка" stack-label outlined dense clearable readonly
+                                class="q-mt-xs col-2" :bg-color="inputBgColor()" input-style="cursor: pointer" style="min-width: 140px;" label="Оценка" stack-label outlined dense clearable readonly
                                 @click.stop.prevent="selectLibRate"
                             >
                                 <template v-if="librateNames" #append>
@@ -171,6 +171,21 @@
                                     {{ librateNames }}
                                 </q-tooltip>
                             </q-input>
+
+                            <div class="q-mx-xs" />
+                            <q-input
+                                v-model="search.ext" :maxlength="inputMaxLength" :debounce="inputDebounce"
+                                class="q-mt-xs col-2" :bg-color="inputBgColor()" input-style="cursor: pointer" style="min-width: 140px;" label="Тип файла" stack-label outlined dense clearable readonly
+                                @click.stop.prevent="selectExt"
+                            >
+                                <template v-if="search.ext" #append>
+                                    <q-icon name="la la-times-circle" class="q-field__focusable-action" @click.stop.prevent="search.ext = ''" />
+                                </template>
+
+                                <q-tooltip v-if="search.ext && showTooltips" :delay="500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
+                                    {{ search.ext }}
+                                </q-tooltip>
+                            </q-input>                            
                         </div>
                         <div v-show="!isExtendedSearch && !extendedParams && extendedParamsMessage" class="row q-mx-sm items-center clickable" @click.stop.prevent="extendedParams = true">
                             +{{ extendedParamsMessage }}
@@ -331,6 +346,7 @@
         <SelectLangDialog v-model="selectLangDialogVisible" v-model:lang="search.lang" :lang-list="langList" :lang-default="langDefault" />        
         <SelectLibRateDialog v-model="selectLibRateDialogVisible" v-model:librate="search.librate" />
         <SelectDateDialog v-model="selectDateDialogVisible" v-model:date="search.date" />
+        <SelectExtDialog v-model="selectExtDialogVisible" v-model:ext="search.ext" :ext-list="extList" />        
         <BookInfoDialog v-model="bookInfoDialogVisible" :book-info="bookInfo" />
         <SelectExtSearchDialog v-model="selectExtSearchDialogVisible" v-model:ext-search="extSearch" />        
     </div>
@@ -351,6 +367,7 @@ import SelectGenreDialog from './SelectGenreDialog/SelectGenreDialog.vue';
 import SelectLangDialog from './SelectLangDialog/SelectLangDialog.vue';
 import SelectLibRateDialog from './SelectLibRateDialog/SelectLibRateDialog.vue';
 import SelectDateDialog from './SelectDateDialog/SelectDateDialog.vue';
+import SelectExtDialog from './SelectExtDialog/SelectExtDialog.vue';
 import BookInfoDialog from './BookInfoDialog/BookInfoDialog.vue';
 import SelectExtSearchDialog from './SelectExtSearchDialog/SelectExtSearchDialog.vue';
 
@@ -384,6 +401,7 @@ const componentOptions = {
         SelectLangDialog,
         SelectLibRateDialog,
         SelectDateDialog,
+        SelectExtDialog,
         BookInfoDialog,
         SelectExtSearchDialog,
         Dialog,
@@ -495,6 +513,7 @@ class Search {
     selectLangDialogVisible = false;
     selectLibRateDialogVisible = false;
     selectDateDialogVisible = false;
+    selectExtDialogVisible = false;
     bookInfoDialogVisible = false;
     selectExtSearchDialogVisible = false;
 
@@ -531,6 +550,7 @@ class Search {
     genreTree = [];
     genreMap = new Map();
     langList = [];
+    extList = [];
     genreTreeInpxHash = '';
     showTooltips = true;
 
@@ -561,7 +581,7 @@ class Search {
         this.commit = this.$store.commit;
         this.api = this.$root.api;
 
-        this.generateDefaults(this.search, ['author', 'series', 'title', 'genre', 'lang', 'date', 'librate']);
+        this.generateDefaults(this.search, ['author', 'series', 'title', 'genre', 'lang', 'date', 'librate', 'ext']);
         this.search.setDefaults(this.search);
 
         this.loadSettings();
@@ -941,6 +961,11 @@ class Search {
         this.selectLibRateDialogVisible = true;
     }
 
+    selectExt() {
+        this.hideTooltip();
+        this.selectExtDialogVisible = true;
+    }
+
     selectExtSearch() {
         this.hideTooltip();
         this.selectExtSearchDialogVisible = true;
@@ -1170,6 +1195,7 @@ class Search {
                 }
 
                 this.langList = result.langList;
+                this.extList = result.extList;
                 this.genreTreeInpxHash = result.inpxHash;
             }
         } catch (e) {

+ 175 - 0
client/components/Search/SelectExtDialog/SelectExtDialog.vue

@@ -0,0 +1,175 @@
+<template>
+    <Dialog ref="dialog" v-model="dialogVisible">
+        <template #header>
+            <div class="row items-center">
+                <div style="font-size: 110%">
+                    Выбрать типы файлов
+                </div>
+            </div>
+        </template>
+
+        <div ref="box" class="column q-mt-xs overflow-auto no-wrap" style="width: 370px; padding: 0px 10px 10px 10px;">
+            <div v-show="extList.length" class="checkbox-tick-all">
+                <div class="row items-center">
+                    <q-option-group
+                        v-model="ticked"
+                        :options="optionsPre"
+                        type="checkbox"
+                        inline
+                    />
+                </div>
+
+                <q-checkbox v-model="tickAll" label="Выбрать/снять все" toggle-order="ft" @update:model-value="makeTickAll" />
+            </div>
+
+            <q-option-group
+                v-model="ticked"
+                :options="options"
+                type="checkbox"
+            >
+            </q-option-group>
+        </div>
+
+        <template #footer>
+            <q-btn class="q-px-md q-ml-sm" color="primary" dense no-caps @click="okClick">
+                OK
+            </q-btn>
+        </template>
+    </Dialog>
+</template>
+
+<script>
+//-----------------------------------------------------------------------------
+import vueComponent from '../../vueComponent.js';
+
+import Dialog from '../../share/Dialog.vue';
+
+const componentOptions = {
+    components: {
+        Dialog
+    },
+    watch: {
+        modelValue(newValue) {
+            this.dialogVisible = newValue;
+            if (newValue)
+                this.init();//no await
+        },
+        dialogVisible(newValue) {
+            this.$emit('update:modelValue', newValue);
+        },
+        lang() {
+            this.updateTicked();
+        },
+        ticked() {
+            this.checkAllTicked();
+            this.updateLang();
+        },
+    }
+};
+class SelectExtDialog {
+    _options = componentOptions;
+    _props = {
+        modelValue: Boolean,
+        ext: {type: String, value: ''},
+        extList: Array,
+    };
+
+    dialogVisible = false;
+
+    ticked = [];
+    tickAll = false;
+
+    created() {
+        this.commit = this.$store.commit;
+    }
+
+    mounted() {
+        this.updateTicked();
+    }
+
+    async init() {
+        //await this.$refs.dialog.waitShown();
+    }
+
+    get options() {
+        const result = [];
+
+        for (const ext of this.extList) {
+            result.push({label: ext, value: ext});
+        }
+
+        return result;
+    }
+
+    get optionsPre() {
+        const result = [];
+
+        for (const ext of ['fb2', 'pdf']) {
+            if (this.extList.includes(ext)) {
+                result.push({label: ext, value: ext});
+            }
+        }
+
+        return result;
+    }
+
+    makeTickAll() {
+        if (this.tickAll) {
+            const newTicked = [];
+            for (const ext of this.extList) {
+                newTicked.push(ext);
+            }
+            this.ticked = newTicked;
+        } else {
+            this.ticked = [];
+            this.tickAll = false;
+        }
+    }
+
+    checkAllTicked() {
+        const ticked = new Set(this.ticked);
+
+        let newTickAll = !!(this.extList.length);
+        for (const ext of this.extList) {
+            if (!ticked.has(ext)) {
+                newTickAll = false;
+                break;
+            }
+        }
+
+        if (this.ticked.length && !newTickAll) {
+            this.tickAll = undefined;
+        } else {
+            this.tickAll = newTickAll;
+        }
+    }
+
+    updateTicked() {
+        this.ticked = this.ext.split(',').filter(s => s);
+    }
+
+    updateLang() {
+        this.$emit('update:ext', this.ticked.join(','));
+    }
+
+    okClick() {
+        this.dialogVisible = false;
+    }
+}
+
+export default vueComponent(SelectExtDialog);
+//-----------------------------------------------------------------------------
+</script>
+
+<style scoped>
+.checkbox-tick-all {
+    border-bottom: 1px solid #bbbbbb;
+    margin-bottom: 7px;
+    padding: 5px 5px 2px 0px;
+}
+
+.clickable {
+    color: blue;
+    cursor: pointer;
+}
+</style>

+ 1 - 1
server/config/base.js

@@ -21,7 +21,7 @@ module.exports = {
 
     //поправить в случае, если были критические изменения в DbCreator или InpxParser
     //иначе будет рассинхронизация по кешу между сервером и клиентом на уровне БД
-    dbVersion: '11',
+    dbVersion: '12',
     dbCacheSize: 5,
 
     maxPayloadSize: 500,//in MB

+ 10 - 0
server/core/DbCreator.js

@@ -64,6 +64,8 @@ class DbCreator {
         let dateArr = [];
         let librateMap = new Map();//оценка
         let librateArr = [];
+        let extMap = new Map();//тип файла
+        let extArr = [];
 
         let uidSet = new Set();//уникальные идентификаторы
 
@@ -215,6 +217,9 @@ class DbCreator {
 
             //оценка
             parseField(rec.librate, librateMap, librateArr, rec.id);
+
+            //тип файла
+            parseField(rec.ext, extMap, extArr, rec.id);
         };
 
         //основная процедура парсинга
@@ -272,6 +277,8 @@ class DbCreator {
         delMap = null;
         dateMap = null;
         librateMap = null;
+        extMap = null;
+
         uidSet = null;
 
         await db.close({table: 'book'});
@@ -408,6 +415,9 @@ class DbCreator {
         //librate
         await saveTable('librate', librateArr, () => {librateArr = null}, 'number');
 
+        //ext
+        await saveTable('ext', extArr, () => {extArr = null});
+
         //кэш-таблицы запросов
         await db.create({table: 'query_cache'});
         await db.create({table: 'query_time'});

+ 36 - 0
server/core/DbSearcher.js

@@ -288,6 +288,42 @@ class DbSearcher {
             idsArr.push(ids);
         }
 
+        //тип файла
+        if (query.ext) {
+            const key = `book-ids-ext-${query.ext}`;
+            let ids = await this.getCached(key);
+
+            if (ids === null) {
+                const extRows = await db.select({
+                    table: 'ext',
+                    rawResult: true,
+                    where: `
+                        const exts = ${db.esc(query.ext.split(','))};
+
+                        const ids = new Set();
+                        for (const l of exts) {
+                            for (const id of @indexLR('value', l, l))
+                                ids.add(id);
+                        }
+                        
+                        const result = new Set();
+                        for (const id of ids) {
+                            const row = @unsafeRow(id);
+                            for (const bookId of row.bookIds)
+                                result.add(bookId);
+                        }
+
+                        return new Uint32Array(result);
+                    `
+                });
+
+                ids = extRows[0].rawResult;
+                await this.putCached(key, ids);
+            }
+
+            idsArr.push(ids);
+        }
+
         if (idsArr.length > 1) {
             //ищем пересечение множеств
             let proc = 0;

+ 5 - 0
server/core/WebWorker.js

@@ -350,9 +350,14 @@ class WebWorker {
             rows = await db.select({table: 'lang', map: `(r) => ({value: r.value})`});
             const langs = rows.map(r => r.value);            
 
+            // exts
+            rows = await db.select({table: 'ext', map: `(r) => ({value: r.value})`});
+            const exts = rows.map(r => r.value);            
+
             result = {
                 genreTree: genres,
                 langList: langs,
+                extList: exts,
                 inpxHash: (config.inpxHash ? config.inpxHash : ''),
             };