浏览代码

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

Book Pauk 2 年之前
父节点
当前提交
3850a7d624

+ 59 - 0
client/components/Api/Api.vue

@@ -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>

+ 3 - 0
client/components/Api/webSocketConnection.js

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

+ 22 - 0
client/components/App.vue

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

+ 127 - 2
client/components/Search/Search.vue

@@ -1,6 +1,37 @@
 <template>
 <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>
     </div>
 </template>
 </template>
 
 
@@ -14,18 +45,98 @@ const componentOptions = {
     components: {
     components: {
     },
     },
     watch: {
     watch: {
+        config() {
+            this.makeTitle();
+        },
     },
     },
 };
 };
 class Search {
 class Search {
     _options = componentOptions;
     _options = componentOptions;
+    collection = '';
+    projectName = '';
+
+    //search fields
+    author = '';
+    series = '';
+    title = '';
+    genre = '';
+    lang = '';
 
 
     created() {
     created() {
         this.commit = this.$store.commit;
         this.commit = this.$store.commit;
     }
     }
 
 
     mounted() {
     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);
 export default vueComponent(Search);
@@ -35,4 +146,18 @@ export default vueComponent(Search);
 <style scoped>
 <style scoped>
 .root {
 .root {
 }
 }
+
+.tool-panel {
+    border-bottom: 1px solid black;
+}
+
+.header {
+    font-size: 150%;
+    height: 30px;
+}
+
+.clickable {
+    color: blue;
+    cursor: pointer;
+}
 </style>
 </style>

+ 53 - 0
client/share/LockQueue.js

@@ -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;

+ 3 - 3
client/store/root.js

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