Работа над клиентской частью

This commit is contained in:
Book Pauk
2022-08-18 18:53:06 +07:00
parent 41d1dc1441
commit 3850a7d624
6 changed files with 267 additions and 5 deletions

View File

@@ -0,0 +1,59 @@
<template>
<div>
</div>
</template>
<script>
//-----------------------------------------------------------------------------
import vueComponent from '../vueComponent.js';
import wsc from './webSocketConnection';
//import _ from 'lodash';
const componentOptions = {
components: {
},
watch: {
},
};
class Api {
_options = componentOptions;
created() {
this.commit = this.$store.commit;
}
mounted() {
}
async request(params) {
return await wsc.message(await wsc.send(params));
}
async search(query) {
const response = await this.request({action: 'search', query});
if (response.error) {
throw new Error(response.error);
}
return response;
}
async config() {
const response = await this.request({action: 'get-config'});
if (response.error) {
throw new Error(response.error);
}
return response;
}
}
export default vueComponent(Api);
//-----------------------------------------------------------------------------
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,3 @@
import WebSocketConnection from '../../../server/core/WebSocketConnection';
export default new WebSocketConnection();

View File

@@ -1,5 +1,6 @@
<template>
<div class="fit row">
<Api ref="api" />
<Notify ref="notify" />
<StdDialog ref="stdDialog" />
@@ -19,10 +20,12 @@ import vueComponent from './vueComponent.js';
import Notify from './share/Notify.vue';
import StdDialog from './share/StdDialog.vue';
import Api from './Api/Api.vue';
import Search from './Search/Search.vue';
const componentOptions = {
components: {
Api,
Notify,
StdDialog,
@@ -36,6 +39,8 @@ class App {
_options = componentOptions;
created() {
this.commit = this.$store.commit;
//root route
let cachedRoute = '';
let cachedPath = '';
@@ -49,6 +54,7 @@ class App {
}
this.$root.isMobileDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
this.$root.setAppTitle = this.setAppTitle;
//global keyHooks
this.keyHooks = [];
@@ -80,10 +86,26 @@ class App {
}
mounted() {
this.$root.api = this.$refs.api;
this.$root.notify = this.$refs.notify;
this.$root.stdDialog = this.$refs.stdDialog;
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() {
return this.$store.state.config;
}
get rootRoute() {

View File

@@ -1,6 +1,37 @@
<template>
<div class="root row fit">
<div>Search</div>
<div class="root column fit">
<div class="tool-panel bg-green-11">
<div class="header q-mx-md q-mt-xs row justify-between items-center">
<div class="row items-center">
<div class="q-mr-xs">
Коллекция:
</div>
<div class="clickable" @click="showCollectionInfo">
{{ collection }}
</div>
</div>
<div class="q-ml-md">
{{ projectName }}
</div>
</div>
<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 />
<div class="q-mx-xs" />
<q-input v-model="series" maxlength="1000" class="bg-white" style="width: 200px;" label="Серия" stack-label outlined dense clearable />
<div class="q-mx-xs" />
<q-input v-model="title" maxlength="1000" class="bg-white" style="width: 200px;" label="Название" stack-label outlined dense clearable />
<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 />
<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 />
<div class="q-mx-xs" />
<q-btn round dense style="height: 20px" color="info" icon="la la-question" @click="showSearchHelp" />
</div>
</div>
<div class="col fit" style="overflow: auto">
{{ config }}
</div>
</div>
</template>
@@ -14,18 +45,98 @@ const componentOptions = {
components: {
},
watch: {
config() {
this.makeTitle();
},
},
};
class Search {
_options = componentOptions;
collection = '';
projectName = '';
//search fields
author = '';
series = '';
title = '';
genre = '';
lang = '';
created() {
this.commit = this.$store.commit;
}
mounted() {
this.api = this.$root.api;
this.$refs.authorInput.focus();
this.refresh();//no await
}
get config() {
return this.$store.state.config;
}
makeTitle() {
const collection = this.config.dbConfig.inpxInfo.collection.split('\n');
this.collection = collection[0].trim();
this.projectName = `${this.config.name} v${this.config.version}`;
}
showSearchHelp() {
this.$root.stdDialog.alert(`
<p>
Здесь должна быть подсказка<br>
</p>
`, 'Подсказка', {iconName: 'la la-info-circle'});
}
showCollectionInfo() {
this.$root.stdDialog.alert(`
<p>
Здесь должна быть информация о коллекции<br>
</p>
`, 'Статистика по коллекции', {iconName: 'la la-info-circle'});
}
async updateTableData() {
}
async refresh() {
const newQuery = {
author: this.author,
series: this.series,
title: this.title,
genre: this.genre,
lang: this.lang,
};
this.queryExecute = newQuery;
if (this.refreshing)
return;
this.refreshing = true;
try {
while (this.queryExecute) {
const query = this.queryExecute;
this.queryExecute = null;
try {
this.searchResult = await this.api.search(query);
} catch (e) {
this.$root.stdDialog.alert(e.message, 'Ошибка');
return;
}
this.updateTableData();//no await
}
} finally {
this.refreshing = false;
}
}
}
export default vueComponent(Search);
@@ -35,4 +146,18 @@ export default vueComponent(Search);
<style scoped>
.root {
}
.tool-panel {
border-bottom: 1px solid black;
}
.header {
font-size: 150%;
height: 30px;
}
.clickable {
color: blue;
cursor: pointer;
}
</style>

53
client/share/LockQueue.js Normal file
View File

@@ -0,0 +1,53 @@
class LockQueue {
constructor(queueSize) {
this.queueSize = queueSize;
this.freed = true;
this.waitingQueue = [];
}
//async
get(take = true) {
return new Promise((resolve, reject) => {
if (this.freed) {
if (take)
this.freed = false;
resolve();
return;
}
if (this.waitingQueue.length < this.queueSize) {
this.waitingQueue.push({resolve, reject});
} else {
reject(new Error('Lock queue is too long'));
}
});
}
ret() {
if (this.waitingQueue.length) {
this.waitingQueue.shift().resolve();
} else {
this.freed = true;
}
}
//async
wait() {
return this.get(false);
}
retAll() {
while (this.waitingQueue.length) {
this.waitingQueue.shift().resolve();
}
}
errAll(error = 'rejected') {
while (this.waitingQueue.length) {
this.waitingQueue.shift().reject(new Error(error));
}
}
}
export default LockQueue;

View File

@@ -1,6 +1,6 @@
// initial state
const state = {
apiError: null,
config: {},
};
// getters
@@ -11,8 +11,8 @@ const actions = {};
// mutations
const mutations = {
setApiError(state, value) {
state.apiError = value;
setConfig(state, value) {
state.config = value;
},
};