diff --git a/client/components/Api/Api.vue b/client/components/Api/Api.vue
index ade4a3d..924c2cf 100644
--- a/client/components/Api/Api.vue
+++ b/client/components/Api/Api.vue
@@ -156,6 +156,16 @@ class Api {
return response;
}
+ async getGenreTree() {
+ const response = await this.request({action: 'get-genre-tree'});
+
+ if (response.error) {
+ throw new Error(response.error);
+ }
+
+ return response;
+ }
+
async getConfig() {
const response = await this.request({action: 'get-config'});
diff --git a/client/components/Search/Search.vue b/client/components/Search/Search.vue
index 14588e8..56c60c2 100644
--- a/client/components/Search/Search.vue
+++ b/client/components/Search/Search.vue
@@ -160,6 +160,8 @@
+
+
@@ -169,6 +171,7 @@ import vueComponent from '../vueComponent.js';
import { reactive } from 'vue';
import PageScroller from './PageScroller/PageScroller.vue';
+import SelectGenreDialog from './SelectGenreDialog/SelectGenreDialog.vue';
import authorBooksStorage from './authorBooksStorage';
import DivBtn from '../share/DivBtn.vue';
import Dialog from '../share/Dialog.vue';
@@ -180,6 +183,7 @@ import _ from 'lodash';
const componentOptions = {
components: {
PageScroller,
+ SelectGenreDialog,
Dialog,
DivBtn
},
@@ -236,6 +240,8 @@ class Search {
loadingMessage = '';
loadingMessage2 = '';
settingsDialogVisible = false;
+ selectGenreDialogVisible = false;
+
page = 1;
pageCount = 1;
@@ -262,6 +268,8 @@ class Search {
totalFound = 0;
bookRowsOnPage = 100;
inpxHash = '';
+ genreTree = [];
+ genreTreeInpxHash = '';
limitOptions = [
{label: '10', value: 10},
@@ -338,7 +346,7 @@ class Search {
}
selectGenre() {
- this.$root.stdDialog.alert('Выбор жанра');
+ this.selectGenreDialogVisible = true;
}
selectLang() {
@@ -455,9 +463,8 @@ class Search {
try {
let result;
- const key = `${authorId}-${this.inpxHash}`;
-
if (this.abCacheEnabled) {
+ const key = `${authorId}-${this.inpxHash}`;
const data = await authorBooksStorage.getData(key);
if (data) {
result = JSON.parse(data);
@@ -516,6 +523,33 @@ class Search {
}
}
+ async updateGenreTreeIfNeeded() {
+ try {
+ if (this.genreTreeInpxHash !== this.inpxHash) {
+ let result;
+
+ if (this.abCacheEnabled) {
+ const key = `genre-tree-${this.inpxHash}`;
+ const data = await authorBooksStorage.getData(key);
+ if (data) {
+ result = JSON.parse(data);
+ } else {
+ result = await this.api.getGenreTree();
+
+ await authorBooksStorage.setData(key, JSON.stringify(result));
+ }
+ } else {
+ result = await this.api.getGenreTree();
+ }
+
+ this.genreTree = result.genreTree;
+ this.genreTreeInpxHash = result.inpxHash;
+ }
+ } catch (e) {
+ this.$root.stdDialog.alert(e.message, 'Ошибка');
+ }
+ }
+
async updateTableData() {
let result = [];
@@ -591,6 +625,7 @@ class Search {
this.inpxHash = result.inpxHash;
this.searchResult = result;
+ await this.updateGenreTreeIfNeeded();
await this.updateTableData();
this.scrollToTop();
} catch (e) {
diff --git a/client/components/Search/SelectGenreDialog/SelectGenreDialog.vue b/client/components/Search/SelectGenreDialog/SelectGenreDialog.vue
new file mode 100644
index 0000000..2f2e7ca
--- /dev/null
+++ b/client/components/Search/SelectGenreDialog/SelectGenreDialog.vue
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/quasar.js b/client/quasar.js
index a2a3243..202a8d2 100644
--- a/client/quasar.js
+++ b/client/quasar.js
@@ -31,7 +31,7 @@ import {QSelect} from 'quasar/src/components/select';
//import {QPopupProxy} from 'quasar/src/components/popup-proxy';
import {QDialog} from 'quasar/src/components/dialog';
//import {QChip} from 'quasar/src/components/chip';
-//import {QTree} from 'quasar/src/components/tree';
+import {QTree} from 'quasar/src/components/tree';
//import {QVirtualScroll} from 'quasar/src/components/virtual-scroll';
//import {QExpansionItem} from 'quasar/src/components/expansion-item';
@@ -63,7 +63,7 @@ const components = {
//QPopupProxy,
QDialog,
//QChip,
- //QTree,
+ QTree,
//QExpansionItem,
//QVirtualScroll,
};
diff --git a/server/controllers/WebSocketController.js b/server/controllers/WebSocketController.js
index af5d496..43e7d5b 100644
--- a/server/controllers/WebSocketController.js
+++ b/server/controllers/WebSocketController.js
@@ -70,6 +70,8 @@ class WebSocketController {
await this.search(req, ws); break;
case 'get-book-list':
await this.getBookList(req, ws); break;
+ case 'get-genre-tree':
+ await this.getGenreTree(req, ws); break;
default:
throw new Error(`Action not found: ${req.action}`);
@@ -133,6 +135,12 @@ class WebSocketController {
this.send(result, req, ws);
}
+
+ async getGenreTree(req, ws) {
+ const result = await this.webWorker.getGenreTree();
+
+ this.send(result, req, ws);
+ }
}
module.exports = WebSocketController;
diff --git a/server/core/DbCreator.js b/server/core/DbCreator.js
index ec736c6..eb154ca 100644
--- a/server/core/DbCreator.js
+++ b/server/core/DbCreator.js
@@ -275,7 +275,10 @@ class DbCreator {
let genre = rec.genre || emptyFieldValue;
genre = rec.genre.split(',');
- for (const g of genre) {
+ for (let g of genre) {
+ if (!g)
+ g = emptyFieldValue;
+
let genreRec;
if (genreMap.has(g)) {
const genreId = genreMap.get(g);
diff --git a/server/core/WebWorker.js b/server/core/WebWorker.js
index 46e45c7..d52b89e 100644
--- a/server/core/WebWorker.js
+++ b/server/core/WebWorker.js
@@ -1,5 +1,6 @@
const os = require('os');
const fs = require('fs-extra');
+const _ = require('lodash');
const WorkerState = require('./WorkerState');
const { JembaDbThread } = require('jembadb');
@@ -9,6 +10,7 @@ const DbSearcher = require('./DbSearcher');
const ayncExit = new (require('./AsyncExit'))();
const log = new (require('./AppLogger'))().log;//singleton
const utils = require('./utils');
+const genreTree = require('./genres');
//server states
const ssNormal = 'normal';
@@ -203,6 +205,43 @@ class WebWorker {
return await this.dbSearcher.getBookList(authorId);
}
+ async getGenreTree() {
+ this.checkMyState();
+
+ const config = await this.dbConfig();
+
+ let result;
+ const db = this.db;
+ if (!db.wwCache.genres) {
+ const genres = _.cloneDeep(genreTree);
+ const last = genres[genres.length - 1];
+
+ const genreValues = new Set();
+ for (const section of genres) {
+ for (const g of section.value)
+ genreValues.add(g.value);
+ }
+
+ //добавим к жанрам те, что нашлись при парсинге
+ const rows = await db.select({table: 'genre', map: `(r) => ({value: r.value})`});
+ for (const row of rows) {
+ if (!genreValues.has(row.value))
+ last.value.push({name: row.value, value: row.value});
+ }
+
+ result = {
+ genreTree: genres,
+ inpxHash: (config.inpxHash ? config.inpxHash : ''),
+ };
+
+ db.wwCache.genres = result;
+ } else {
+ result = db.wwCache.genres;
+ }
+
+ return result;
+ }
+
async logServerStats() {
while (1) {// eslint-disable-line
try {
diff --git a/server/core/Genres/genres.json b/server/core/genres/genresText.js
similarity index 98%
rename from server/core/Genres/genres.json
rename to server/core/genres/genresText.js
index f0f5d1a..2ef4488 100644
--- a/server/core/Genres/genres.json
+++ b/server/core/genres/genresText.js
@@ -1,4 +1,4 @@
-"
+module.exports = `
#---------- Список жанров fb2 ----------
0.1 Фантастика
0.2 Детективы и Триллеры
@@ -276,4 +276,4 @@
0.22.251 tbg_secondary;Учебники и пособия для среднего и специального образования
0.22.252 tbg_higher;Учебники и пособия ВУЗов
#---------- 2021-07-22 11:35:50.469539----------
-"
\ No newline at end of file
+`;
\ No newline at end of file
diff --git a/server/core/genres/index.js b/server/core/genres/index.js
new file mode 100644
index 0000000..22f440a
--- /dev/null
+++ b/server/core/genres/index.js
@@ -0,0 +1,53 @@
+const genresText = require('./genresText.js');
+const genres = [];
+
+const sec2index = {};
+const lines = genresText.split('\n').map(l => l.trim());
+
+let index = 0;
+let other;//прочее в конец
+
+for (const line of lines) {
+ if (!line || line[0] == '#')
+ continue;
+
+ const p = line.indexOf(' ');
+ const num = line.substring(0, p).trim().split('.');
+ if (num.length < 2)
+ continue;
+
+ const section = `${num[0]}.${num[1]}`;
+ if (section == '0.0')
+ continue;
+
+ let name = line.substring(p + 1).trim();
+
+ if (num.length < 3) {//раздел
+ if (section == '0.20') {//прочее
+ other = {name, value: []};
+ } else {
+ if (sec2index[section] === undefined) {
+ if (!genres[index])
+ genres[index] = {name, value: []};
+ sec2index[section] = index;
+ index++;
+ }
+ }
+ } else {//подраздел
+ const n = name.split(';').map(l => l.trim());
+
+ if (section == '0.20') {//прочее
+ other.value.push({name: n[1], value: n[0]});
+ } else {
+ const i = sec2index[section];
+ if (i !== undefined) {
+ genres[i].value.push({name: n[1], value: n[0]});
+ }
+ }
+ }
+}
+
+if (other)
+ genres.push(other);
+
+module.exports = genres;
\ No newline at end of file