Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7c6b0e7ab | ||
|
|
94922f3926 | ||
|
|
a580b1eb6d | ||
|
|
cd7b8afb29 | ||
|
|
e634893ff3 | ||
|
|
fadc7ddc34 | ||
|
|
ed5dc25d94 | ||
|
|
dd11e8c5ad | ||
|
|
2db2b8cff4 | ||
|
|
4d3661b758 | ||
|
|
891b1e4fe8 | ||
|
|
d588b16885 | ||
|
|
a0e4651607 | ||
|
|
c21b8ffa0e | ||
|
|
f174617f33 | ||
|
|
2de9ad0edf | ||
|
|
a6592f2f8d |
@@ -231,12 +231,12 @@ class Api {
|
|||||||
return await this.request({action: 'get-genre-tree'});
|
return await this.request({action: 'get-genre-tree'});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBookLink(bookId) {
|
async getBookLink(bookUid) {
|
||||||
return await this.request({action: 'get-book-link', bookId}, 120);
|
return await this.request({action: 'get-book-link', bookUid}, 120);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBookInfo(bookId) {
|
async getBookInfo(bookUid) {
|
||||||
return await this.request({action: 'get-book-info', bookId}, 120);
|
return await this.request({action: 'get-book-info', bookUid}, 120);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getConfig() {
|
async getConfig() {
|
||||||
|
|||||||
@@ -238,6 +238,13 @@ class AuthorList extends BaseList {
|
|||||||
const booksToFilter = await this.loadAuthorBooks(item.key);
|
const booksToFilter = await this.loadAuthorBooks(item.key);
|
||||||
const filtered = this.filterBooks(booksToFilter);
|
const filtered = this.filterBooks(booksToFilter);
|
||||||
|
|
||||||
|
if (!filtered.length && this.list.totalFound == 1) {
|
||||||
|
this.list.queryFound = 0;
|
||||||
|
this.list.totalFound = 0;
|
||||||
|
this.searchResult.found = [];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const prepareBook = (book) => {
|
const prepareBook = (book) => {
|
||||||
return Object.assign(
|
return Object.assign(
|
||||||
{
|
{
|
||||||
@@ -345,7 +352,10 @@ class AuthorList extends BaseList {
|
|||||||
if (authors.length > 1 || item.count > this.maxItemCount)
|
if (authors.length > 1 || item.count > this.maxItemCount)
|
||||||
this.getAuthorBooks(item);//no await
|
this.getAuthorBooks(item);//no await
|
||||||
else
|
else
|
||||||
await this.getAuthorBooks(item);
|
if (await this.getAuthorBooks(item) === false) {
|
||||||
|
this.tableData = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push(item);
|
result.push(item);
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ export default class BaseList {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
//подготовка
|
//подготовка
|
||||||
const response = await this.api.getBookLink(book.id);
|
const response = await this.api.getBookLink(book._uid);
|
||||||
|
|
||||||
const link = response.link;
|
const link = response.link;
|
||||||
const href = `${window.location.origin}${link}`;
|
const href = `${window.location.origin}${link}`;
|
||||||
@@ -164,7 +164,7 @@ export default class BaseList {
|
|||||||
}
|
}
|
||||||
} else if (action == 'bookInfo') {
|
} else if (action == 'bookInfo') {
|
||||||
//информация о книге
|
//информация о книге
|
||||||
const response = await this.api.getBookInfo(book.id);
|
const response = await this.api.getBookInfo(book._uid);
|
||||||
this.$emit('listEvent', {action: 'bookInfo', data: response.bookInfo});
|
this.$emit('listEvent', {action: 'bookInfo', data: response.bookInfo});
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
|||||||
@@ -17,10 +17,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row q-mt-sm no-wrap">
|
<div class="row q-mt-sm no-wrap">
|
||||||
<div class="column justify-center" style="height: 300px; width: 200px; min-width: 100px">
|
<div class="poster-size">
|
||||||
<img v-if="coverSrc" :src="coverSrc" class="fit row justify-center items-center" style="object-fit: contain" @error="coverSrc = ''" />
|
<div class="poster-size column justify-center items-center" :class="{poster: coverSrc}" @click.stop.prevent="posterClick">
|
||||||
<div v-if="!coverSrc" class="fit row justify-center items-center text-grey-5" style="border: 1px solid #ccc; font-size: 300%">
|
<img v-if="coverSrc" :src="coverSrc" class="fit row justify-center items-center" style="object-fit: contain" @error="coverSrc = ''" />
|
||||||
<i>{{ book.ext }}</i>
|
<div v-if="!coverSrc" class="fit row justify-center items-center text-grey-5" style="border: 1px solid #ccc; font-size: 300%">
|
||||||
|
<i>{{ book.ext }}</i>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -71,6 +73,19 @@
|
|||||||
OK
|
OK
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<Dialog v-model="posterDialogVisible">
|
||||||
|
<template #header>
|
||||||
|
<div class="row items-center">
|
||||||
|
<div style="font-size: 110%">
|
||||||
|
Обложка
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<img :src="coverSrc" class="fit q-pb-sm" style="height: 100%; max-height: calc(100vh - 140px); object-fit: contain" />
|
||||||
|
</Dialog>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -106,6 +121,7 @@ class BookInfoDialog {
|
|||||||
};
|
};
|
||||||
|
|
||||||
dialogVisible = false;
|
dialogVisible = false;
|
||||||
|
posterDialogVisible = false;
|
||||||
selectedTab = 'fb2';
|
selectedTab = 'fb2';
|
||||||
|
|
||||||
//info props
|
//info props
|
||||||
@@ -185,7 +201,10 @@ class BookInfoDialog {
|
|||||||
return utils.sqlDateFormat(value);
|
return utils.sqlDateFormat(value);
|
||||||
|
|
||||||
if (nodePath == 'fileInfo/del')
|
if (nodePath == 'fileInfo/del')
|
||||||
return (value ? 'Да' : '');
|
return (value ? 'Да' : null);
|
||||||
|
|
||||||
|
if (nodePath == 'fileInfo/insno')
|
||||||
|
return (value ? value : null);
|
||||||
|
|
||||||
if (nodePath == 'titleInfo/author')
|
if (nodePath == 'titleInfo/author')
|
||||||
return value.split(',').join(', ');
|
return value.split(',').join(', ');
|
||||||
@@ -273,6 +292,13 @@ class BookInfoDialog {
|
|||||||
this.book = bookInfo.book;
|
this.book = bookInfo.book;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
posterClick() {
|
||||||
|
if (!this.coverSrc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.posterDialogVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
okClick() {
|
okClick() {
|
||||||
this.dialogVisible = false;
|
this.dialogVisible = false;
|
||||||
}
|
}
|
||||||
@@ -283,6 +309,26 @@ export default vueComponent(BookInfoDialog);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.poster-size {
|
||||||
|
height: 300px;
|
||||||
|
width: 200px;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster:hover {
|
||||||
|
position: relative;
|
||||||
|
top: -1%;
|
||||||
|
left: -1%;
|
||||||
|
width: 102%;
|
||||||
|
height: 102%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
<div class="q-ml-sm column">
|
<div class="q-ml-sm column">
|
||||||
<div v-if="(mode == 'series' || mode == 'title') && bookAuthor" class="row">
|
<div v-if="(mode == 'series' || mode == 'title') && bookAuthor" class="row">
|
||||||
<div class="clickable2 text-green-10" @click="emit('authorClick')">
|
<div class="clickable2 text-green-10" @click.stop.prevent="emit('authorClick')">
|
||||||
{{ bookAuthor }}
|
{{ bookAuthor }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -43,10 +43,10 @@
|
|||||||
<div v-if="book.serno" class="q-mr-xs">
|
<div v-if="book.serno" class="q-mr-xs">
|
||||||
{{ book.serno }}.
|
{{ book.serno }}.
|
||||||
</div>
|
</div>
|
||||||
<div class="clickable2" :class="titleColor" @click="emit('titleClick')">
|
<div class="clickable2" :class="titleColor" @click.stop.prevent="emit('titleClick')">
|
||||||
{{ book.title }}
|
{{ book.title }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="mode == 'title' && bookSeries" class="q-ml-xs clickable2" @click="emit('seriesClick')">
|
<div v-if="mode == 'title' && bookSeries" class="q-ml-xs clickable2" @click.stop.prevent="emit('seriesClick')">
|
||||||
{{ bookSeries }}
|
{{ bookSeries }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -55,19 +55,19 @@
|
|||||||
{{ bookSize }}, {{ book.ext }}
|
{{ bookSize }}, {{ book.ext }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="showInfo" class="q-ml-sm clickable" @click="emit('bookInfo')">
|
<div v-if="showInfo" class="q-ml-sm clickable" @click.stop.prevent="emit('bookInfo')">
|
||||||
(инфо)
|
(инфо)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="q-ml-sm clickable" @click="emit('download')">
|
<div class="q-ml-sm clickable" @click.stop.prevent="emit('download')">
|
||||||
(скачать)
|
(скачать)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="q-ml-sm clickable" @click="emit('copyLink')">
|
<div class="q-ml-sm clickable" @click.stop.prevent="emit('copyLink')">
|
||||||
<q-icon name="la la-copy" size="20px" />
|
<q-icon name="la la-copy" size="20px" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="showReadLink" class="q-ml-sm clickable" @click="emit('readBook')">
|
<div v-if="showReadLink" class="q-ml-sm clickable" @click.stop.prevent="emit('readBook')">
|
||||||
(читать)
|
(читать)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -24,14 +24,14 @@
|
|||||||
<div class="q-mr-xs">
|
<div class="q-mr-xs">
|
||||||
Коллекция
|
Коллекция
|
||||||
</div>
|
</div>
|
||||||
<div class="clickable" @click="showCollectionInfo">
|
<div class="clickable" @click.stop.prevent="showCollectionInfo">
|
||||||
{{ collection }}
|
{{ collection }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col"></div>
|
<div class="col"></div>
|
||||||
|
|
||||||
<DivBtn class="q-ml-md text-white bg-secondary" :size="30" :icon-size="24" icon="la la-question" round @click="showSearchHelp">
|
<DivBtn class="q-ml-md text-white bg-secondary" :size="30" :icon-size="24" icon="la la-question" round @click.stop.prevent="showSearchHelp">
|
||||||
<template #tooltip>
|
<template #tooltip>
|
||||||
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
|
||||||
Памятка
|
Памятка
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</DivBtn>
|
</DivBtn>
|
||||||
|
|
||||||
<DivBtn class="q-ml-sm text-white bg-secondary" :size="30" :icon-size="24" :imt="1" icon="la la-cog" round @click="settingsDialogVisible = true">
|
<DivBtn class="q-ml-sm text-white bg-secondary" :size="30" :icon-size="24" :imt="1" icon="la la-cog" round @click.stop.prevent="settingsDialogVisible = true">
|
||||||
<template #tooltip>
|
<template #tooltip>
|
||||||
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
|
||||||
Настройки
|
Настройки
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
<DivBtn
|
<DivBtn
|
||||||
class="text-grey-5 bg-yellow-1 q-mt-xs" :size="34" :icon-size="24" round
|
class="text-grey-5 bg-yellow-1 q-mt-xs" :size="34" :icon-size="24" round
|
||||||
:icon="(extendedParams ? 'la la-angle-double-up' : 'la la-angle-double-down')"
|
:icon="(extendedParams ? 'la la-angle-double-up' : 'la la-angle-double-down')"
|
||||||
@click="extendedParams = !extendedParams"
|
@click.stop.prevent="extendedParams = !extendedParams"
|
||||||
>
|
>
|
||||||
<template #tooltip>
|
<template #tooltip>
|
||||||
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
<q-input
|
<q-input
|
||||||
v-model="search.lang" :maxlength="inputMaxLength" :debounce="inputDebounce"
|
v-model="search.lang" :maxlength="inputMaxLength" :debounce="inputDebounce"
|
||||||
class="q-mt-xs" :bg-color="inputBgColor()" input-style="cursor: pointer" style="width: 90px;" label="Язык" stack-label outlined dense clearable readonly
|
class="q-mt-xs" :bg-color="inputBgColor()" input-style="cursor: pointer" style="width: 90px;" label="Язык" stack-label outlined dense clearable readonly
|
||||||
@click="selectLang"
|
@click.stop.prevent="selectLang"
|
||||||
>
|
>
|
||||||
<template v-if="search.lang" #append>
|
<template v-if="search.lang" #append>
|
||||||
<q-icon name="la la-times-circle" class="q-field__focusable-action" @click.stop.prevent="search.lang = ''" />
|
<q-icon name="la la-times-circle" class="q-field__focusable-action" @click.stop.prevent="search.lang = ''" />
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
<DivBtn
|
<DivBtn
|
||||||
class="text-grey-8 bg-yellow-1 q-mt-xs" :size="34" :icon-size="24" round
|
class="text-grey-8 bg-yellow-1 q-mt-xs" :size="34" :icon-size="24" round
|
||||||
icon="la la-level-up-alt"
|
icon="la la-level-up-alt"
|
||||||
@click="cloneSearch"
|
@click.stop.prevent="cloneSearch"
|
||||||
>
|
>
|
||||||
<template #tooltip>
|
<template #tooltip>
|
||||||
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%" max-width="400px">
|
||||||
@@ -119,7 +119,7 @@
|
|||||||
<q-input
|
<q-input
|
||||||
v-model="genreNames" :maxlength="inputMaxLength" :debounce="inputDebounce"
|
v-model="genreNames" :maxlength="inputMaxLength" :debounce="inputDebounce"
|
||||||
class="q-mt-xs" :bg-color="inputBgColor()" input-style="cursor: pointer" style="width: 200px;" label="Жанр" stack-label outlined dense clearable readonly
|
class="q-mt-xs" :bg-color="inputBgColor()" input-style="cursor: pointer" style="width: 200px;" label="Жанр" stack-label outlined dense clearable readonly
|
||||||
@click="selectGenre"
|
@click.stop.prevent="selectGenre"
|
||||||
>
|
>
|
||||||
<template v-if="genreNames" #append>
|
<template v-if="genreNames" #append>
|
||||||
<q-icon name="la la-times-circle" class="q-field__focusable-action" @click.stop.prevent="search.genre = ''" />
|
<q-icon name="la la-times-circle" class="q-field__focusable-action" @click.stop.prevent="search.genre = ''" />
|
||||||
@@ -151,7 +151,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #option="scope">
|
<template #option="scope">
|
||||||
<q-item v-bind="scope.itemProps" @click="dateSelectItemClick(scope.opt.value)">
|
<q-item v-bind="scope.itemProps" @click.stop.prevent="dateSelectItemClick(scope.opt.value)">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label>
|
<q-item-label>
|
||||||
{{ scope.opt.label }}
|
{{ scope.opt.label }}
|
||||||
@@ -165,7 +165,7 @@
|
|||||||
<q-input
|
<q-input
|
||||||
v-model="librateNames" :maxlength="inputMaxLength" :debounce="inputDebounce"
|
v-model="librateNames" :maxlength="inputMaxLength" :debounce="inputDebounce"
|
||||||
class="q-mt-xs" :bg-color="inputBgColor()" input-style="cursor: pointer" style="width: 90px;" label="Оценка" stack-label outlined dense clearable readonly
|
class="q-mt-xs" :bg-color="inputBgColor()" input-style="cursor: pointer" style="width: 90px;" label="Оценка" stack-label outlined dense clearable readonly
|
||||||
@click="selectLibRate"
|
@click.stop.prevent="selectLibRate"
|
||||||
>
|
>
|
||||||
<template v-if="librateNames" #append>
|
<template v-if="librateNames" #append>
|
||||||
<q-icon name="la la-times-circle" class="q-field__focusable-action" @click.stop.prevent="search.librate = ''" />
|
<q-icon name="la la-times-circle" class="q-field__focusable-action" @click.stop.prevent="search.librate = ''" />
|
||||||
@@ -176,7 +176,7 @@
|
|||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</q-input>
|
</q-input>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="!extendedParams && extendedParamsMessage" class="row q-mx-md items-center clickable" @click="extendedParams = true">
|
<div v-show="!extendedParams && extendedParamsMessage" class="row q-mx-md items-center clickable" @click.stop.prevent="extendedParams = true">
|
||||||
+{{ extendedParamsMessage }}
|
+{{ extendedParamsMessage }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -204,48 +204,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row justify-center">
|
<div class="row justify-center">
|
||||||
<div class="q-mb-xl q-px-sm q-py-xs bg-cyan-2 clickable2" style="border: 1px solid #aaaaaa; border-radius: 6px; white-space: nowrap;" @click="openReleasePage">
|
<div class="q-mb-lg q-px-sm q-py-xs bg-cyan-2 clickable2" style="border: 1px solid #aaaaaa; border-radius: 6px; white-space: nowrap;" @click.stop.prevent="openReleasePage">
|
||||||
{{ projectName }}
|
{{ projectName }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Dialog v-model="settingsDialogVisible">
|
<SettingsDialog v-model="settingsDialogVisible" />
|
||||||
<template #header>
|
|
||||||
<div class="row items-center" style="font-size: 110%">
|
|
||||||
<q-icon class="q-mr-sm text-green" name="la la-cog" size="28px"></q-icon>
|
|
||||||
Настройки
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="q-mx-md column" style="min-width: 300px; font-size: 120%;">
|
|
||||||
<div class="row items-center q-ml-sm">
|
|
||||||
<div class="q-mr-sm">
|
|
||||||
Результатов на странице
|
|
||||||
</div>
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<q-checkbox v-model="showCounts" size="36px" label="Показывать количество" />
|
|
||||||
<q-checkbox v-model="showRates" size="36px" label="Показывать оценки" />
|
|
||||||
<q-checkbox v-model="showInfo" size="36px" label="Показывать кнопку (инфо)" />
|
|
||||||
<q-checkbox v-model="showGenres" size="36px" label="Показывать жанры" />
|
|
||||||
<q-checkbox v-model="showDates" size="36px" label="Показывать даты поступления" />
|
|
||||||
<q-checkbox v-model="showDeleted" size="36px" label="Показывать удаленные" />
|
|
||||||
<q-checkbox v-model="abCacheEnabled" size="36px" label="Кешировать запросы" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<template #footer>
|
|
||||||
<q-btn class="q-px-md q-ml-sm" color="primary" dense no-caps @click="settingsDialogVisible = false">
|
|
||||||
OK
|
|
||||||
</q-btn>
|
|
||||||
</template>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<SelectGenreDialog v-model="selectGenreDialogVisible" v-model:genre="search.genre" :genre-tree="genreTree" />
|
<SelectGenreDialog v-model="selectGenreDialogVisible" v-model:genre="search.genre" :genre-tree="genreTree" />
|
||||||
<SelectLangDialog v-model="selectLangDialogVisible" v-model:lang="search.lang" :lang-list="langList" :lang-default="langDefault" />
|
<SelectLangDialog v-model="selectLangDialogVisible" v-model:lang="search.lang" :lang-list="langList" :lang-default="langDefault" />
|
||||||
<SelectLibRateDialog v-model="selectLibRateDialogVisible" v-model:librate="search.librate" />
|
<SelectLibRateDialog v-model="selectLibRateDialogVisible" v-model:librate="search.librate" />
|
||||||
@@ -263,6 +228,7 @@ import SeriesList from './SeriesList/SeriesList.vue';
|
|||||||
import TitleList from './TitleList/TitleList.vue';
|
import TitleList from './TitleList/TitleList.vue';
|
||||||
|
|
||||||
import PageScroller from './PageScroller/PageScroller.vue';
|
import PageScroller from './PageScroller/PageScroller.vue';
|
||||||
|
import SettingsDialog from './SettingsDialog/SettingsDialog.vue';
|
||||||
import SelectGenreDialog from './SelectGenreDialog/SelectGenreDialog.vue';
|
import SelectGenreDialog from './SelectGenreDialog/SelectGenreDialog.vue';
|
||||||
import SelectLangDialog from './SelectLangDialog/SelectLangDialog.vue';
|
import SelectLangDialog from './SelectLangDialog/SelectLangDialog.vue';
|
||||||
import SelectLibRateDialog from './SelectLibRateDialog/SelectLibRateDialog.vue';
|
import SelectLibRateDialog from './SelectLibRateDialog/SelectLibRateDialog.vue';
|
||||||
@@ -290,6 +256,7 @@ const componentOptions = {
|
|||||||
SeriesList,
|
SeriesList,
|
||||||
TitleList,
|
TitleList,
|
||||||
PageScroller,
|
PageScroller,
|
||||||
|
SettingsDialog,
|
||||||
SelectGenreDialog,
|
SelectGenreDialog,
|
||||||
SelectLangDialog,
|
SelectLangDialog,
|
||||||
SelectLibRateDialog,
|
SelectLibRateDialog,
|
||||||
@@ -328,27 +295,6 @@ const componentOptions = {
|
|||||||
|
|
||||||
this.updatePageCount();
|
this.updatePageCount();
|
||||||
},
|
},
|
||||||
showCounts(newValue) {
|
|
||||||
this.setSetting('showCounts', newValue);
|
|
||||||
},
|
|
||||||
showRates(newValue) {
|
|
||||||
this.setSetting('showRates', newValue);
|
|
||||||
},
|
|
||||||
showInfo(newValue) {
|
|
||||||
this.setSetting('showInfo', newValue);
|
|
||||||
},
|
|
||||||
showGenres(newValue) {
|
|
||||||
this.setSetting('showGenres', newValue);
|
|
||||||
},
|
|
||||||
showDates(newValue) {
|
|
||||||
this.setSetting('showDates', newValue);
|
|
||||||
},
|
|
||||||
showDeleted(newValue) {
|
|
||||||
this.setSetting('showDeleted', newValue);
|
|
||||||
},
|
|
||||||
abCacheEnabled(newValue) {
|
|
||||||
this.setSetting('abCacheEnabled', newValue);
|
|
||||||
},
|
|
||||||
$route(to) {
|
$route(to) {
|
||||||
this.updateListFromRoute(to);
|
this.updateListFromRoute(to);
|
||||||
this.updateSearchFromRouteQuery(to);
|
this.updateSearchFromRouteQuery(to);
|
||||||
@@ -436,12 +382,6 @@ class Search {
|
|||||||
prevManualDate = '';
|
prevManualDate = '';
|
||||||
|
|
||||||
//settings
|
//settings
|
||||||
showCounts = true;
|
|
||||||
showRates = true;
|
|
||||||
showInfo = true;
|
|
||||||
showGenres = true;
|
|
||||||
showDates = true;
|
|
||||||
showDeleted = false;
|
|
||||||
abCacheEnabled = true;
|
abCacheEnabled = true;
|
||||||
langDefault = '';
|
langDefault = '';
|
||||||
limit = 20;
|
limit = 20;
|
||||||
@@ -464,16 +404,6 @@ class Search {
|
|||||||
|
|
||||||
bookInfo = {};
|
bookInfo = {};
|
||||||
|
|
||||||
limitOptions = [
|
|
||||||
{label: '10', value: 10},
|
|
||||||
{label: '20', value: 20},
|
|
||||||
{label: '50', value: 50},
|
|
||||||
{label: '100', value: 100},
|
|
||||||
{label: '200', value: 200},
|
|
||||||
{label: '500', value: 500},
|
|
||||||
{label: '1000', value: 1000},
|
|
||||||
];
|
|
||||||
|
|
||||||
searchDateOptions = [
|
searchDateOptions = [
|
||||||
{label: 'сегодня', value: 'today'},
|
{label: 'сегодня', value: 'today'},
|
||||||
{label: 'за 3 дня', value: '3days'},
|
{label: 'за 3 дня', value: '3days'},
|
||||||
@@ -530,12 +460,6 @@ class Search {
|
|||||||
this.extendedParams = settings.extendedParams;
|
this.extendedParams = settings.extendedParams;
|
||||||
this.expanded = _.cloneDeep(settings.expanded);
|
this.expanded = _.cloneDeep(settings.expanded);
|
||||||
this.expandedSeries = _.cloneDeep(settings.expandedSeries);
|
this.expandedSeries = _.cloneDeep(settings.expandedSeries);
|
||||||
this.showCounts = settings.showCounts;
|
|
||||||
this.showRates = settings.showRates;
|
|
||||||
this.showInfo = settings.showInfo;
|
|
||||||
this.showGenres = settings.showGenres;
|
|
||||||
this.showDates = settings.showDates;
|
|
||||||
this.showDeleted = settings.showDeleted;
|
|
||||||
this.abCacheEnabled = settings.abCacheEnabled;
|
this.abCacheEnabled = settings.abCacheEnabled;
|
||||||
this.langDefault = settings.langDefault;
|
this.langDefault = settings.langDefault;
|
||||||
}
|
}
|
||||||
|
|||||||
151
client/components/Search/SettingsDialog/SettingsDialog.vue
Normal file
151
client/components/Search/SettingsDialog/SettingsDialog.vue
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog ref="dialog" v-model="dialogVisible">
|
||||||
|
<template #header>
|
||||||
|
<div class="row items-center" style="font-size: 110%">
|
||||||
|
<q-icon class="q-mr-sm text-green" name="la la-cog" size="28px"></q-icon>
|
||||||
|
Настройки
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="q-mx-md column" style="min-width: 300px; font-size: 120%;">
|
||||||
|
<div class="row items-center q-ml-sm">
|
||||||
|
<div class="q-mr-sm">
|
||||||
|
Результатов на странице
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<q-checkbox v-model="showCounts" size="36px" label="Показывать количество" />
|
||||||
|
<q-checkbox v-model="showRates" size="36px" label="Показывать оценки" />
|
||||||
|
<q-checkbox v-model="showInfo" size="36px" label="Показывать кнопку (инфо)" />
|
||||||
|
<q-checkbox v-model="showGenres" size="36px" label="Показывать жанры" />
|
||||||
|
<q-checkbox v-model="showDates" size="36px" label="Показывать даты поступления" />
|
||||||
|
<q-checkbox v-model="showDeleted" size="36px" label="Показывать удаленные" />
|
||||||
|
<q-checkbox v-model="abCacheEnabled" size="36px" label="Кешировать запросы" />
|
||||||
|
</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;
|
||||||
|
},
|
||||||
|
dialogVisible(newValue) {
|
||||||
|
this.$emit('update:modelValue', newValue);
|
||||||
|
},
|
||||||
|
|
||||||
|
settings() {
|
||||||
|
this.loadSettings();
|
||||||
|
},
|
||||||
|
limit(newValue) {
|
||||||
|
this.commit('setSettings', {'limit': newValue});
|
||||||
|
},
|
||||||
|
showCounts(newValue) {
|
||||||
|
this.commit('setSettings', {'showCounts': newValue});
|
||||||
|
},
|
||||||
|
showRates(newValue) {
|
||||||
|
this.commit('setSettings', {'showRates': newValue});
|
||||||
|
},
|
||||||
|
showInfo(newValue) {
|
||||||
|
this.commit('setSettings', {'showInfo': newValue});
|
||||||
|
},
|
||||||
|
showGenres(newValue) {
|
||||||
|
this.commit('setSettings', {'showGenres': newValue});
|
||||||
|
},
|
||||||
|
showDates(newValue) {
|
||||||
|
this.commit('setSettings', {'showDates': newValue});
|
||||||
|
},
|
||||||
|
showDeleted(newValue) {
|
||||||
|
this.commit('setSettings', {'showDeleted': newValue});
|
||||||
|
},
|
||||||
|
abCacheEnabled(newValue) {
|
||||||
|
this.commit('setSettings', {'abCacheEnabled': newValue});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
class SettingsDialog {
|
||||||
|
_options = componentOptions;
|
||||||
|
_props = {
|
||||||
|
modelValue: Boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
dialogVisible = false;
|
||||||
|
|
||||||
|
//settings
|
||||||
|
limit = 20;
|
||||||
|
showCounts = true;
|
||||||
|
showRates = true;
|
||||||
|
showInfo = true;
|
||||||
|
showGenres = true;
|
||||||
|
showDates = true;
|
||||||
|
showDeleted = false;
|
||||||
|
abCacheEnabled = true;
|
||||||
|
|
||||||
|
limitOptions = [
|
||||||
|
{label: '10', value: 10},
|
||||||
|
{label: '20', value: 20},
|
||||||
|
{label: '50', value: 50},
|
||||||
|
{label: '100', value: 100},
|
||||||
|
{label: '200', value: 200},
|
||||||
|
{label: '500', value: 500},
|
||||||
|
{label: '1000', value: 1000},
|
||||||
|
];
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.commit = this.$store.commit;
|
||||||
|
|
||||||
|
this.loadSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
}
|
||||||
|
|
||||||
|
get settings() {
|
||||||
|
return this.$store.state.settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSettings() {
|
||||||
|
const settings = this.settings;
|
||||||
|
|
||||||
|
this.limit = settings.limit;
|
||||||
|
|
||||||
|
this.showCounts = settings.showCounts;
|
||||||
|
this.showRates = settings.showRates;
|
||||||
|
this.showInfo = settings.showInfo;
|
||||||
|
this.showGenres = settings.showGenres;
|
||||||
|
this.showDates = settings.showDates;
|
||||||
|
this.showDeleted = settings.showDeleted;
|
||||||
|
this.abCacheEnabled = settings.abCacheEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
okClick() {
|
||||||
|
this.dialogVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default vueComponent(SettingsDialog);
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "inpx-web",
|
"name": "inpx-web",
|
||||||
"version": "1.2.1",
|
"version": "1.2.4",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "inpx-web",
|
"name": "inpx-web",
|
||||||
"version": "1.2.1",
|
"version": "1.2.4",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "inpx-web",
|
"name": "inpx-web",
|
||||||
"version": "1.2.1",
|
"version": "1.2.4",
|
||||||
"author": "Book Pauk <bookpauk@gmail.com>",
|
"author": "Book Pauk <bookpauk@gmail.com>",
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
"repository": "bookpauk/inpx-web",
|
"repository": "bookpauk/inpx-web",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ module.exports = {
|
|||||||
|
|
||||||
//поправить в случае, если были критические изменения в DbCreator или InpxParser
|
//поправить в случае, если были критические изменения в DbCreator или InpxParser
|
||||||
//иначе будет рассинхронизация между сервером и клиентом на уровне БД
|
//иначе будет рассинхронизация между сервером и клиентом на уровне БД
|
||||||
dbVersion: '6',
|
dbVersion: '7',
|
||||||
dbCacheSize: 5,
|
dbCacheSize: 5,
|
||||||
|
|
||||||
maxPayloadSize: 500,//in MB
|
maxPayloadSize: 500,//in MB
|
||||||
|
|||||||
@@ -165,19 +165,19 @@ class WebSocketController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getBookLink(req, ws) {
|
async getBookLink(req, ws) {
|
||||||
if (!utils.hasProp(req, 'bookId'))
|
if (!utils.hasProp(req, 'bookUid'))
|
||||||
throw new Error(`bookId is empty`);
|
throw new Error(`bookUid is empty`);
|
||||||
|
|
||||||
const result = await this.webWorker.getBookLink(req.bookId);
|
const result = await this.webWorker.getBookLink(req.bookUid);
|
||||||
|
|
||||||
this.send(result, req, ws);
|
this.send(result, req, ws);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBookInfo(req, ws) {
|
async getBookInfo(req, ws) {
|
||||||
if (!utils.hasProp(req, 'bookId'))
|
if (!utils.hasProp(req, 'bookUid'))
|
||||||
throw new Error(`bookId is empty`);
|
throw new Error(`bookUid is empty`);
|
||||||
|
|
||||||
const result = await this.webWorker.getBookInfo(req.bookId);
|
const result = await this.webWorker.getBookInfo(req.bookUid);
|
||||||
|
|
||||||
this.send(result, req, ws);
|
this.send(result, req, ws);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ class DbCreator {
|
|||||||
let librateMap = new Map();//оценка
|
let librateMap = new Map();//оценка
|
||||||
let librateArr = [];
|
let librateArr = [];
|
||||||
|
|
||||||
|
let uidSet = new Set();//уникальные идентификаторы
|
||||||
|
|
||||||
//stats
|
//stats
|
||||||
let authorCount = 0;
|
let authorCount = 0;
|
||||||
let bookCount = 0;
|
let bookCount = 0;
|
||||||
@@ -221,13 +223,14 @@ class DbCreator {
|
|||||||
let filtered = false;
|
let filtered = false;
|
||||||
for (const rec of chunk) {
|
for (const rec of chunk) {
|
||||||
//сначала фильтр
|
//сначала фильтр
|
||||||
if (!filter(rec)) {
|
if (!filter(rec) || uidSet.has(rec._uid)) {
|
||||||
rec.id = 0;
|
rec.id = 0;
|
||||||
filtered = true;
|
filtered = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
rec.id = ++id;
|
rec.id = ++id;
|
||||||
|
uidSet.add(rec._uid);
|
||||||
|
|
||||||
if (!rec.del) {
|
if (!rec.del) {
|
||||||
bookCount++;
|
bookCount++;
|
||||||
@@ -269,6 +272,7 @@ class DbCreator {
|
|||||||
delMap = null;
|
delMap = null;
|
||||||
dateMap = null;
|
dateMap = null;
|
||||||
librateMap = null;
|
librateMap = null;
|
||||||
|
uidSet = null;
|
||||||
|
|
||||||
await db.close({table: 'book'});
|
await db.close({table: 'book'});
|
||||||
await db.freeMemory();
|
await db.freeMemory();
|
||||||
@@ -624,6 +628,12 @@ class DbCreator {
|
|||||||
stats.filesDelCount = res.filesDelCount;
|
stats.filesDelCount = res.filesDelCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//заодно добавим нужный индекс
|
||||||
|
await db.create({
|
||||||
|
in: 'book',
|
||||||
|
hash: {field: '_uid', type: 'string', depth: 100, unique: true},
|
||||||
|
});
|
||||||
|
|
||||||
countDone = true;
|
countDone = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const crypto = require('crypto');
|
||||||
const ZipReader = require('./ZipReader');
|
const ZipReader = require('./ZipReader');
|
||||||
|
|
||||||
const collectionInfo = 'collection.info';
|
const collectionInfo = 'collection.info';
|
||||||
@@ -98,9 +99,13 @@ class InpxParser {
|
|||||||
if (line[line.length - 1] == '\x0D')
|
if (line[line.length - 1] == '\x0D')
|
||||||
line = line.substring(0, line.length - 1);
|
line = line.substring(0, line.length - 1);
|
||||||
|
|
||||||
|
const rec = {};
|
||||||
|
//уникальный идентификатор записи
|
||||||
|
const sha256 = crypto.createHash('sha256');
|
||||||
|
rec._uid = sha256.update(line).digest('base64');
|
||||||
|
|
||||||
//парсим запись
|
//парсим запись
|
||||||
const parts = line.split('\x04');
|
const parts = line.split('\x04');
|
||||||
const rec = {};
|
|
||||||
|
|
||||||
const len = (parts.length > structLen ? structLen : parts.length);
|
const len = (parts.length > structLen ? structLen : parts.length);
|
||||||
for (let i = 0; i < len; i++) {
|
for (let i = 0; i < len; i++) {
|
||||||
|
|||||||
@@ -58,9 +58,9 @@ class RemoteLib {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async downloadBook(bookPath, downFileName) {
|
async downloadBook(bookUid) {
|
||||||
try {
|
try {
|
||||||
const response = await await this.wsRequest({action: 'get-book-link', bookPath, downFileName});
|
const response = await await this.wsRequest({action: 'get-book-link', bookUid});
|
||||||
const link = response.link;
|
const link = response.link;
|
||||||
|
|
||||||
const buf = await this.down.load(`${this.remoteHost}${link}`, {decompress: false});
|
const buf = await this.down.load(`${this.remoteHost}${link}`, {decompress: false});
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ class WebWorker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async restoreBook(bookPath, downFileName) {
|
async restoreBook(bookUid, bookPath, downFileName) {
|
||||||
const db = this.db;
|
const db = this.db;
|
||||||
|
|
||||||
let extractedFile = '';
|
let extractedFile = '';
|
||||||
@@ -364,7 +364,7 @@ class WebWorker {
|
|||||||
extractedFile = await this.extractBook(bookPath);
|
extractedFile = await this.extractBook(bookPath);
|
||||||
hash = await utils.getFileHash(extractedFile, 'sha256', 'hex');
|
hash = await utils.getFileHash(extractedFile, 'sha256', 'hex');
|
||||||
} else {
|
} else {
|
||||||
hash = await this.remoteLib.downloadBook(bookPath, downFileName);
|
hash = await this.remoteLib.downloadBook(bookUid);
|
||||||
}
|
}
|
||||||
|
|
||||||
const link = `${this.config.filesPathStatic}/${hash}`;
|
const link = `${this.config.filesPathStatic}/${hash}`;
|
||||||
@@ -402,7 +402,7 @@ class WebWorker {
|
|||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBookLink(bookId) {
|
async getBookLink(bookUid) {
|
||||||
this.checkMyState();
|
this.checkMyState();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -410,11 +410,11 @@ class WebWorker {
|
|||||||
let link = '';
|
let link = '';
|
||||||
|
|
||||||
//найдем bookPath и downFileName
|
//найдем bookPath и downFileName
|
||||||
let rows = await db.select({table: 'book', where: `@@id(${db.esc(bookId)})`});
|
let rows = await db.select({table: 'book', where: `@@hash('_uid', ${db.esc(bookUid)})`});
|
||||||
if (!rows.length)
|
if (!rows.length)
|
||||||
throw new Error('404 Файл не найден');
|
throw new Error('404 Файл не найден');
|
||||||
|
|
||||||
const book = rows[0];
|
const book = rows[0];
|
||||||
let downFileName = book.file;
|
let downFileName = book.file;
|
||||||
const author = book.author.split(',');
|
const author = book.author.split(',');
|
||||||
const at = [author[0], book.title];
|
const at = [author[0], book.title];
|
||||||
@@ -443,7 +443,7 @@ class WebWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!link) {
|
if (!link) {
|
||||||
link = await this.restoreBook(bookPath, downFileName)
|
link = await this.restoreBook(bookUid, bookPath, downFileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!link)
|
if (!link)
|
||||||
@@ -458,13 +458,13 @@ class WebWorker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBookInfo(bookId) {
|
async getBookInfo(bookUid) {
|
||||||
this.checkMyState();
|
this.checkMyState();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const db = this.db;
|
const db = this.db;
|
||||||
|
|
||||||
let bookInfo = await this.getBookLink(bookId);
|
let bookInfo = await this.getBookLink(bookUid);
|
||||||
const hash = path.basename(bookInfo.link);
|
const hash = path.basename(bookInfo.link);
|
||||||
const bookFile = `${this.config.filesDir}/${hash}`;
|
const bookFile = `${this.config.filesDir}/${hash}`;
|
||||||
const bookFileInfo = `${bookFile}.i.json`;
|
const bookFileInfo = `${bookFile}.i.json`;
|
||||||
@@ -472,7 +472,9 @@ class WebWorker {
|
|||||||
const restoreBookInfo = async(info) => {
|
const restoreBookInfo = async(info) => {
|
||||||
const result = {};
|
const result = {};
|
||||||
|
|
||||||
const rows = await db.select({table: 'book', where: `@@id(${db.esc(bookId)})`});
|
let rows = await db.select({table: 'book', where: `@@hash('_uid', ${db.esc(bookUid)})`});
|
||||||
|
if (!rows.length)
|
||||||
|
throw new Error('404 Файл не найден');
|
||||||
const book = rows[0];
|
const book = rows[0];
|
||||||
|
|
||||||
result.book = book;
|
result.book = book;
|
||||||
|
|||||||
Reference in New Issue
Block a user