Procházet zdrojové kódy

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

Book Pauk před 2 roky
rodič
revize
a46276cf1a

+ 48 - 11
client/components/Search/Search.vue

@@ -29,7 +29,11 @@
                         </div>
                     </div>
                     
-                    <DivBtn class="q-mx-md text-white bg-secondary" :size="30" :icon-size="24" :imt="1" icon="la la-cog" round @click="settingsDialogVisible = true" />
+                    <DivBtn class="q-mx-md text-white bg-secondary" :size="30" :icon-size="24" :imt="1" icon="la la-cog" round @click="settingsDialogVisible = true">
+                        <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
+                            Настройки
+                        </q-tooltip>
+                    </DivBtn>
 
                     <div class="col"></div>
                     <div class="q-px-sm q-py-xs bg-green-12" style="border: 1px solid #aaaaaa; border-radius: 6px">
@@ -57,19 +61,33 @@
                         class="bg-white q-mt-xs" input-style="cursor: pointer" style="width: 200px;" label="Жанр" stack-label outlined dense clearable readonly
                         @click="selectGenre"
                     >
-                        <q-tooltip v-if="genreNames" :delay="500" anchor="bottom right" content-style="font-size: 80%" max-width="400px">
+                        <q-tooltip v-if="genreNames" :delay="500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
                             {{ genreNames }}
-                        </q-tooltip>                    
+                        </q-tooltip>
                     </q-input>
                     <div class="q-mx-xs" />
                     <q-input
                         v-model="lang" :maxlength="inputMaxLength" :debounce="inputDebounce"
                         class="bg-white q-mt-xs" input-style="cursor: pointer" style="width: 80px;" label="Язык" stack-label outlined dense clearable readonly
                         @click="selectLang"
-                    />
-                    <div class="q-mx-xs" />                
+                    >
+                        <q-tooltip v-if="lang" :delay="500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
+                            {{ lang }}
+                        </q-tooltip>
+                    </q-input>
 
-                    <DivBtn class="text-white bg-grey-13" :size="30" :icon-size="24" icon="la la-question" round @click="showSearchHelp" />
+                    <div class="q-mx-xs" />
+                    <DivBtn class="text-white bg-grey-13" :size="30" :icon-size="24" icon="la la-broom" round @click="setDefaults">
+                        <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
+                            Сбросить поиск
+                        </q-tooltip>
+                    </DivBtn>
+                    <div class="q-mx-xs" />
+                    <DivBtn class="text-white bg-grey-13" :size="30" :icon-size="24" icon="la la-question" round @click="showSearchHelp">
+                        <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
+                            Подсказка
+                        </q-tooltip>
+                    </DivBtn>
 
                     <div class="q-mx-xs" />
                     <div class="row items-center q-mt-xs">
@@ -93,7 +111,7 @@
                 <div class="row items-center q-ml-md q-mr-xs no-wrap">
                     <!--div style="min-width: 35px">
                         <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 :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
                                 Только этот автор
                             </q-tooltip>
                         </DivBtn>
@@ -171,6 +189,7 @@
         </Dialog>
 
         <SelectGenreDialog v-model="selectGenreDialogVisible" v-model:genre="genre" :genre-tree="genreTree" />
+        <SelectLangDialog v-model="selectLangDialogVisible" v-model:lang="lang" :lang-list="langList" :lang-default="langDefault" />        
     </div>
 </template>
 
@@ -181,6 +200,8 @@ import { reactive } from 'vue';
 
 import PageScroller from './PageScroller/PageScroller.vue';
 import SelectGenreDialog from './SelectGenreDialog/SelectGenreDialog.vue';
+import SelectLangDialog from './SelectLangDialog/SelectLangDialog.vue';
+
 import authorBooksStorage from './authorBooksStorage';
 import DivBtn from '../share/DivBtn.vue';
 import Dialog from '../share/Dialog.vue';
@@ -193,6 +214,7 @@ const componentOptions = {
     components: {
         PageScroller,
         SelectGenreDialog,
+        SelectLangDialog,
         Dialog,
         DivBtn
     },
@@ -250,6 +272,7 @@ class Search {
     loadingMessage2 = '';
     settingsDialogVisible = false;
     selectGenreDialogVisible = false;
+    selectLangDialogVisible = false;
 
     page = 1;
     pageCount = 1;
@@ -271,6 +294,7 @@ class Search {
     showCounts = true;
     showDeleted = false;
     abCacheEnabled = true;
+    langDefault = '';
 
     //stuff
     queryFound = -1;
@@ -278,6 +302,7 @@ class Search {
     bookRowsOnPage = 100;
     inpxHash = '';
     genreTree = [];
+    langList = [];
     genreTreeInpxHash = '';
 
     limitOptions = [
@@ -308,6 +333,10 @@ class Search {
             if (!this.$root.isMobileDevice)
                 this.$refs.authorInput.focus();
 
+            this.setDefaults();
+
+            //query from url parse
+
             this.ready = true;
             this.refresh();//no await
         })();
@@ -321,6 +350,7 @@ class Search {
         this.showCounts = settings.showCounts;
         this.showDeleted = settings.showDeleted;
         this.abCacheEnabled = settings.abCacheEnabled;
+        this.langDefault = settings.langDefault;
     }
 
     get config() {
@@ -373,7 +403,7 @@ class Search {
     }    
 
     selectLang() {
-        this.$root.stdDialog.alert('Выбор языка');
+        this.selectLangDialogVisible = true;
     }
     
     onScroll() {
@@ -434,9 +464,7 @@ class Search {
     }
 
     setSetting(name, newValue) {
-        const newSettings = _.cloneDeep(this.settings);
-        newSettings[name] = _.cloneDeep(newValue);
-        this.commit('setSettings', newSettings);
+        this.commit('setSettings', {[name]: _.cloneDeep(newValue)});
     }
 
     expandAuthor(item) {
@@ -566,6 +594,7 @@ class Search {
                 }
 
                 this.genreTree = result.genreTree;
+                this.langList = result.langList;
                 this.genreTreeInpxHash = result.inpxHash;
             }
         } catch (e) {
@@ -606,6 +635,14 @@ class Search {
         this.tableData = result;
     }
 
+    setDefaults() {
+        this.author = '';
+        this.series = '';
+        this.title = '';
+        this.genre = '';
+        this.lang = this.langDefault;
+    }
+
     async refresh() {
         if (!this.ready)
             return;

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

@@ -8,7 +8,7 @@
             </div>
         </template>
 
-        <div ref="box" class="column q-mt-xs overflow-auto no-wrap" style="width: 350px; padding: 0px 10px 10px 10px;">
+        <div ref="box" class="column q-mt-xs overflow-auto no-wrap" style="width: 370px; padding: 0px 10px 10px 10px;">
             <div class="row items-center top-panel bg-grey-3">
                 <q-input ref="search" v-model="search" class="col" outlined dense bg-color="white" placeholder="Найти" clearable />
             </div>

+ 190 - 0
client/components/Search/SelectLangDialog/SelectLangDialog.vue

@@ -0,0 +1,190 @@
+<template>
+    <Dialog ref="dialog" v-model="dialogVisible">
+        <template #header>
+            <div class="row items-center">
+                <div style="font-size: 130%">
+                    Выбрать язык
+                </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="langList.length" class="checkbox-tick-all">
+                <div class="row items-center">
+                    <q-option-group
+                        v-model="ticked"
+                        :options="optionsPre"
+                        type="checkbox"
+                        inline
+                    />
+
+                    <div class="col" />
+                    <div v-show="lang != langDefault" class="clickable" @click="setAsDefaults">
+                        Установить по умолчанию
+                    </div>
+                </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"
+                inline
+            >
+                <template #label="opt">
+                    <div class="row items-center" style="width: 35px">
+                        <span>{{ opt.label }}</span>
+                    </div>
+                </template>
+            </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 SelectLangDialog {
+    _options = componentOptions;
+    _props = {
+        modelValue: Boolean,
+        lang: {type: String, value: ''},
+        langDefault: {type: String, value: ''},
+        langList: Array,
+    };
+
+    dialogVisible = false;
+
+    ticked = [];
+    tickAll = false;
+
+    created() {
+        this.commit = this.$store.commit;
+    }
+
+    mounted() {
+    }
+
+    async init() {
+        //await this.$refs.dialog.waitShown();
+    }
+
+    get options() {
+        const result = [];
+
+        for (const lang of this.langList) {
+            result.push({label: lang, value: lang});
+        }
+
+        return result;
+    }
+
+    get optionsPre() {
+        const result = [];
+
+        for (const lang of this.langList) {
+            if (['ru', 'en'].includes(lang)) {
+                result.push({label: lang, value: lang});
+            }
+        }
+
+        return result.reverse();
+    }
+
+    makeTickAll() {
+        if (this.tickAll) {
+            const newTicked = [];
+            for (const lang of this.langList) {
+                newTicked.push(lang);
+            }
+            this.ticked = newTicked;
+        } else {
+            this.ticked = [];
+            this.tickAll = false;
+        }
+    }
+
+    checkAllTicked() {
+        const ticked = new Set(this.ticked);
+
+        let newTickAll = !!(this.langList.length);
+        for (const lang of this.langList) {
+            if (!ticked.has(lang)) {
+                newTickAll = false;
+                break;
+            }
+        }
+
+        if (this.ticked.length && !newTickAll) {
+            this.tickAll = undefined;
+        } else {
+            this.tickAll = newTickAll;
+        }
+    }
+
+    updateTicked() {
+        this.ticked = this.lang.split(',').filter(s => s);
+    }
+
+    updateLang() {
+        this.$emit('update:lang', this.ticked.join(','));
+    }
+
+    setAsDefaults() {
+        this.commit('setSettings', {langDefault: this.lang});
+    }
+
+    okClick() {
+        this.dialogVisible = false;
+    }
+}
+
+export default vueComponent(SelectLangDialog);
+//-----------------------------------------------------------------------------
+</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>

+ 3 - 1
client/quasar.js

@@ -33,8 +33,9 @@ import {QDialog} from 'quasar/src/components/dialog';
 //import {QChip} from 'quasar/src/components/chip';
 import {QTree} from 'quasar/src/components/tree';
 //import {QVirtualScroll} from 'quasar/src/components/virtual-scroll';
-
 //import {QExpansionItem} from 'quasar/src/components/expansion-item';
+import {QOptionGroup} from 'quasar/src/components/option-group';
+
 
 const components = {
     //QLayout,
@@ -66,6 +67,7 @@ const components = {
     QTree,
     //QExpansionItem,
     //QVirtualScroll,
+    QOptionGroup,
 };
 
 //directives 

+ 2 - 1
client/store/root.js

@@ -7,6 +7,7 @@ const state = {
         showCounts: true,
         showDeleted: false,
         abCacheEnabled: true,
+        langDefault: '',
     },
 };
 
@@ -22,7 +23,7 @@ const mutations = {
         state.config = value;
     },
     setSettings(state, value) {
-        state.settings = value;
+        state.settings = Object.assign({}, state.settings, value);
     },
 };
 

+ 6 - 1
server/core/WebWorker.js

@@ -224,7 +224,7 @@ class WebWorker {
 
             //добавим к жанрам те, что нашлись при парсинге
             const genreParsed = new Set();
-            const rows = await db.select({table: 'genre', map: `(r) => ({value: r.value})`});
+            let rows = await db.select({table: 'genre', map: `(r) => ({value: r.value})`});
             for (const row of rows) {
                 genreParsed.add(row.value);
 
@@ -245,8 +245,13 @@ class WebWorker {
                     genres.splice(j--, 1);
             }
 
+            // langs
+            rows = await db.select({table: 'lang', map: `(r) => ({value: r.value})`});
+            const langs = rows.map(r => r.value);            
+
             result = {
                 genreTree: genres,
+                langList: langs,
                 inpxHash: (config.inpxHash ? config.inpxHash : ''),
             };