Работа над приложением

This commit is contained in:
Book Pauk
2022-08-19 00:24:05 +07:00
parent 3877ad15c1
commit adb1d55141
9 changed files with 227 additions and 36 deletions

View File

@@ -24,6 +24,20 @@ class Api {
} }
mounted() { mounted() {
this.updateConfig();//no await
}
async updateConfig() {
try {
const config = await this.getConfig();
this.commit('setConfig', config);
} catch (e) {
this.$root.stdDialog.alert(e.message, 'Ошибка');
}
}
get config() {
return this.$store.state.config;
} }
async request(params) { async request(params) {
@@ -40,7 +54,7 @@ class Api {
return response; return response;
} }
async config() { async getConfig() {
const response = await this.request({action: 'get-config'}); const response = await this.request({action: 'get-config'});
if (response.error) { if (response.error) {

View File

@@ -91,17 +91,6 @@ class App {
this.$root.stdDialog = this.$refs.stdDialog; this.$root.stdDialog = this.$refs.stdDialog;
this.setAppTitle(); this.setAppTitle();
(async() => {
try {
const api = this.$root.api;
const config = await api.config();
this.commit('setConfig', config);
} catch (e) {
this.$root.stdDialog.alert(e.message, 'Ошибка');
}
})();
} }
get config() { get config() {

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="root column fit"> <div class="root column fit">
<div class="tool-panel bg-green-11"> <div class="tool-panel column bg-green-11">
<div class="header q-mx-md q-mt-xs row justify-between items-center"> <div class="header q-mx-md q-mt-sm row justify-between items-center">
<div class="row items-center"> <div class="row items-center">
<div class="q-mr-xs"> <div class="q-mr-xs">
Коллекция: Коллекция:
@@ -15,22 +15,55 @@
</div> </div>
</div> </div>
<div class="row q-mx-md q-my-sm items-center"> <div class="row q-mx-md q-my-sm items-center">
<q-input ref="authorInput" v-model="author" maxlength="1000" class="bg-white" style="width: 300px;" label="Автор" stack-label outlined dense clearable /> <q-input
ref="authorInput" v-model="author" :maxlength="inputMaxLength" :debounce="inputDebounce"
class="bg-white q-mt-xs" style="width: 300px;" label="Автор" stack-label outlined dense clearable
/>
<div class="q-mx-xs" /> <div class="q-mx-xs" />
<q-input v-model="series" maxlength="1000" class="bg-white" style="width: 200px;" label="Серия" stack-label outlined dense clearable /> <q-input
v-model="series" :maxlength="inputMaxLength" :debounce="inputDebounce"
class="bg-white q-mt-xs" style="width: 200px;" label="Серия" stack-label outlined dense clearable
/>
<div class="q-mx-xs" /> <div class="q-mx-xs" />
<q-input v-model="title" maxlength="1000" class="bg-white" style="width: 200px;" label="Название" stack-label outlined dense clearable /> <q-input
v-model="title" :maxlength="inputMaxLength" :debounce="inputDebounce"
class="bg-white q-mt-xs" style="width: 200px;" label="Название" stack-label outlined dense clearable
/>
<div class="q-mx-xs" /> <div class="q-mx-xs" />
<q-input v-model="genre" maxlength="1000" class="bg-white" style="width: 200px;" label="Жанр" stack-label outlined dense clearable readonly /> <q-input
v-model="genre" :maxlength="inputMaxLength" :debounce="inputDebounce"
class="bg-white q-mt-xs" style="width: 200px;" label="Жанр" stack-label outlined dense clearable readonly
@click="selectGenre"
/>
<div class="q-mx-xs" /> <div class="q-mx-xs" />
<q-input v-model="lang" maxlength="1000" class="bg-white" style="width: 80px;" label="Язык" stack-label outlined dense clearable readonly /> <q-input
v-model="lang" :maxlength="inputMaxLength" :debounce="inputDebounce"
class="bg-white q-mt-xs" style="width: 80px;" label="Язык" stack-label outlined dense clearable readonly
@click="selectLang"
/>
<div class="q-mx-xs" /> <div class="q-mx-xs" />
<q-btn round dense style="height: 20px" color="info" icon="la la-question" @click="showSearchHelp" /> <q-btn round dense style="height: 20px" color="info" icon="la la-question" @click="showSearchHelp" />
<div class="q-mx-xs" />
<div class="row items-center q-mt-xs" style="font-size: 120%">
Показаны {{ queryFound }} из {{ totalFound }}
</div>
<div class="q-mx-xs" />
<div class="col row justify-end q-mt-xs">
<q-select
v-model="limit" :options="limitOptions" class="bg-white"
dropdown-icon="la la-angle-down la-sm"
outlined dense emit-value map-options
/>
</div>
</div> </div>
</div> </div>
<div class="col fit" style="overflow: auto"> <div class="col fit column no-wrap" style="overflow: auto">
{{ config }} <div v-for="item in tableData" :key="item.key" style="border-bottom: 1px solid #aaaaaa">
<div class="q-my-sm q-ml-md" style="font-size: 120%">{{ item.value }}</div>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -55,12 +88,32 @@ class Search {
collection = ''; collection = '';
projectName = ''; projectName = '';
//input field consts
inputMaxLength = 1000;
inputDebounce = 400;
//search fields //search fields
author = ''; author = '';
series = ''; series = '';
title = ''; title = '';
genre = ''; genre = '';
lang = ''; lang = '';
limit = 100;
//stuff
queryFound = 0;
totalFound = 0;
limitOptions = [
{label: '10', value: 10},
{label: '20', value: 20},
{label: '50', value: 50},
{label: '100', value: 100},
{label: '1000', value: 1000},
];
searchResult = {};
tableData = [];
created() { created() {
this.commit = this.$store.commit; this.commit = this.$store.commit;
@@ -101,7 +154,23 @@ class Search {
`, 'Статистика по коллекции', {iconName: 'la la-info-circle'}); `, 'Статистика по коллекции', {iconName: 'la la-info-circle'});
} }
selectGenre() {
this.$root.stdDialog.alert('Выбор жанра');
}
selectLang() {
this.$root.stdDialog.alert('Выбор языка');
}
async updateTableData() { async updateTableData() {
let result = [];
let id = 0;
for (const rec of this.searchResult.author) {
result.push({key: id++, value: rec.author});
}
this.tableData = result;
} }
async refresh() { async refresh() {
@@ -111,6 +180,7 @@ class Search {
title: this.title, title: this.title,
genre: this.genre, genre: this.genre,
lang: this.lang, lang: this.lang,
limit: this.limit,
}; };
this.queryExecute = newQuery; this.queryExecute = newQuery;
@@ -125,13 +195,17 @@ class Search {
this.queryExecute = null; this.queryExecute = null;
try { try {
this.searchResult = await this.api.search(query); const result = await this.api.search(query);
this.queryFound = result.author.length;
this.totalFound = result.totalFound;
this.searchResult = result;
this.updateTableData();//no await
} catch (e) { } catch (e) {
this.$root.stdDialog.alert(e.message, 'Ошибка'); this.$root.stdDialog.alert(e.message, 'Ошибка');
return; return;
} }
this.updateTableData();//no await
} }
} finally { } finally {
this.refreshing = false; this.refreshing = false;
@@ -153,7 +227,7 @@ export default vueComponent(Search);
.header { .header {
font-size: 150%; font-size: 150%;
height: 30px; min-height: 30px;
} }
.clickable { .clickable {

View File

@@ -25,7 +25,7 @@ import {QIcon} from 'quasar/src/components/icon';
//import {QSpinner} from 'quasar/src/components/spinner'; //import {QSpinner} from 'quasar/src/components/spinner';
//import {QTable, QTh, QTr, QTd} from 'quasar/src/components/table'; //import {QTable, QTh, QTr, QTd} from 'quasar/src/components/table';
//import {QCheckbox} from 'quasar/src/components/checkbox'; //import {QCheckbox} from 'quasar/src/components/checkbox';
//import {QSelect} from 'quasar/src/components/select'; import {QSelect} from 'quasar/src/components/select';
//import {QColor} from 'quasar/src/components/color'; //import {QColor} from 'quasar/src/components/color';
//import {QPopupProxy} from 'quasar/src/components/popup-proxy'; //import {QPopupProxy} from 'quasar/src/components/popup-proxy';
import {QDialog} from 'quasar/src/components/dialog'; import {QDialog} from 'quasar/src/components/dialog';
@@ -56,7 +56,7 @@ const components = {
//QSpinner, //QSpinner,
//QTable, QTh, QTr, QTd, //QTable, QTh, QTr, QTd,
//QCheckbox, //QCheckbox,
//QSelect, QSelect,
//QColor, //QColor,
//QPopupProxy, //QPopupProxy,
QDialog, QDialog,

View File

@@ -66,6 +66,8 @@ class WebSocketController {
await this.getConfig(req, ws); break; await this.getConfig(req, ws); break;
case 'get-worker-state': case 'get-worker-state':
await this.getWorkerState(req, ws); break; await this.getWorkerState(req, ws); break;
case 'search':
await this.search(req, ws); break;
default: default:
throw new Error(`Action not found: ${req.action}`); throw new Error(`Action not found: ${req.action}`);
@@ -112,6 +114,14 @@ class WebSocketController {
this.send((state ? state : {}), req, ws); this.send((state ? state : {}), req, ws);
} }
async search(req, ws) {
if (!req.query)
throw new Error(`query is empty`);
const result = await this.webWorker.search(req.query);
this.send(result, req, ws);
}
} }
module.exports = WebSocketController; module.exports = WebSocketController;

View File

@@ -302,6 +302,9 @@ class DbCreator {
await db.close({table: 'lang'}); await db.close({table: 'lang'});
utils.freeMemory(); utils.freeMemory();
//кэш-таблицы
callback({job: 'done', jobMessage: ''}); callback({job: 'done', jobMessage: ''});
} }
} }

View File

@@ -1,27 +1,82 @@
//const _ = require('lodash');
const utils = require('./utils');
class DbSearcher { class DbSearcher {
constructor(db) { constructor(db) {
this.db = db; this.db = db;
} }
async search(query) { async selectAuthorIds(query) {
const db = this.db; const db = this.db;
let result = []; let authorRows;
//сначала выберем все id авторов по фильтру
//порядок id соответсвует ASC-сортировке по author
if (query.author) { if (query.author) {
// //
} else { } else {
result = await db.select({ authorRows = await db.select({
table: 'author', table: 'author',
map: `(r) => ({id: r.id, author: r.author})` map: `(r) => ({id: r.id})`,
}); });
} }
if (query.limit) { let authorIds = new Set();
result = result.slice(0, query.limit); for (const row of authorRows)
authorIds.add(row.id);
const idsArr = [];
idsArr.push(authorIds);
//серии
//названия
//жанры
//языки
if (idsArr.length > 1)
authorIds = utils.intersectSet(idsArr);
//сортировка
authorIds = Array.from(authorIds);
authorIds.sort();
return authorIds;
} }
return result; async getAuthorIds(query) {
const db = this.db;
if (!db.searchCache)
db.searchCache = {};
/*const q = query;
const key = JSON.stringify([q.author, ]);
query);
delete q.limit;
q = */
return await this.selectAuthorIds(query);
}
async search(query) {
const db = this.db;
const authorIds = await this.getAuthorIds(query);
const totalFound = authorIds.length;
const limit = (query.limit ? query.limit : 1000);
//выборка найденных авторов
let result = await db.select({
table: 'author',
map: `(r) => ({id: r.id, author: r.author})`,
where: `@@id(${db.esc(authorIds)})`
});
result = result.slice(0, limit);
return {result, totalFound};
} }
} }

View File

@@ -176,6 +176,19 @@ class WebWorker {
return db.wwCache.config; return db.wwCache.config;
} }
async search(query) {
this.checkMyState();
const config = await this.dbConfig();
const result = await this.dbSearcher.search(query);
return {
author: result.result,
totalFound: result.totalFound,
inpxHash: (config.inpxHash ? config.inpxHash : ''),
};
}
async logServerStats() { async logServerStats() {
while (1) {// eslint-disable-line while (1) {// eslint-disable-line
try { try {

View File

@@ -59,6 +59,38 @@ function getBufHash(buf, hashName, enc) {
return hash.digest(enc); return hash.digest(enc);
} }
function intersectSet(arrSet) {
if (!arrSet.length)
return new Set();
let min = 0;
let size = arrSet[0].size;
for (let i = 1; i < arrSet.length; i++) {
if (arrSet[i].size < size) {
min = i;
size = arrSet[i].size;
}
}
const result = new Set();
for (const elem of arrSet[min]) {
let inAll = true;
for (let i = 0; i < arrSet.length; i++) {
if (i === min)
continue;
if (!arrSet[i].has(elem)) {
inAll = false;
break;
}
}
if (inAll)
result.add(elem);
}
return result;
}
module.exports = { module.exports = {
sleep, sleep,
versionText, versionText,
@@ -68,4 +100,5 @@ module.exports = {
freeMemory, freeMemory,
getFileHash, getFileHash,
getBufHash, getBufHash,
intersectSet,
}; };