diff --git a/backend/app/Db.js b/backend/app/Db.js
index 445c5c6..f0a813c 100644
--- a/backend/app/Db.js
+++ b/backend/app/Db.js
@@ -31,6 +31,7 @@ class Db {
await dbo.createCollection(c);
}catch(err){}
}
+ await dbo.collection('assets').createIndex({ name: 'text', 'description': 'text', tags: 'text' }, {name:'fts', default_language: "none" });
}finally{
}
}
diff --git a/backend/app/bl/GameObjectsManager.js b/backend/app/bl/GameObjectsManager.js
index 487e5e9..ba87a64 100644
--- a/backend/app/bl/GameObjectsManager.js
+++ b/backend/app/bl/GameObjectsManager.js
@@ -9,6 +9,8 @@ const execFile = util.promisify(npExecFile);
import fs from 'fs';
import path from 'path';
+import Utils from "../Utils.js";
+
const collection = 'assets';
/**
@@ -144,6 +146,11 @@ class GameObjectsManager{
project: { name:1, id:1, type:1, asset:1}
});
}
+
+ this.getTags = async function(q){
+ let objects = await db.distinct(collection, 'tags', q ? {tags: {$regex: Utils.escapeRegExp(q), $options: 'i'}} : {});
+ return objects;
+ }
}
/**
diff --git a/backend/controllers/api/GameObjectsController.js b/backend/controllers/api/GameObjectsController.js
index d3cdb47..a3dd526 100644
--- a/backend/controllers/api/GameObjectsController.js
+++ b/backend/controllers/api/GameObjectsController.js
@@ -16,7 +16,7 @@ class GameObjectsController{
* @param {App} app The application instance, апликация
*/
init(app){
- const { gameObject, am } = app;
+ const { gameObject, am, db } = app;
const router = express.Router();
/**
@@ -26,7 +26,7 @@ class GameObjectsController{
*/
router.put('/', multipartMiddleware, async (req, res)=>{
try{
- let data = req.body;
+ let data = JSON.parse(req.body.object);
let action = data.id ? 'update' : 'create';
let object = await gameObject[action](req, data)
res.json({status: 'OK', object});
@@ -45,11 +45,16 @@ class GameObjectsController{
* @memberof GameObjectsController
*/
router.post('/', async (req, res)=>{
- let result = await gameObject.list(req.body);
+ let result = await gameObject.list(db.sanitizeQuery(req.body));
res.json(result);
am.audit(req, `game-object-list`, null, {q: req.body});
})
+ router.post('/tags', async (req, res)=>{
+ let tags = await gameObject.getTags(req.body.q);
+ res.json(tags);
+ })
+
/**
* API: GET /api/game-object/:id Retrieve game object by ID, извличане на обект по идентификатор
* @function read
diff --git a/src/components/AssetsManagement/AssetBrowser.vue b/src/components/AssetsManagement/AssetBrowser.vue
index 9a7050f..a8e7c13 100644
--- a/src/components/AssetsManagement/AssetBrowser.vue
+++ b/src/components/AssetsManagement/AssetBrowser.vue
@@ -1,7 +1,14 @@
-
-
-
+
+ f.value)" filter>
+
+
+
+
+
+
@@ -26,7 +33,7 @@
\ No newline at end of file
diff --git a/src/mixins/GameEnvironmentMixin.js b/src/mixins/GameEnvironmentMixin.js
index 6eaf8d2..67acfad 100644
--- a/src/mixins/GameEnvironmentMixin.js
+++ b/src/mixins/GameEnvironmentMixin.js
@@ -39,6 +39,7 @@ export default {
engine.clearScene();
engine.stop();
engine.tm?.setGame(null);
+ engine.destroy();
},
computed:{
diff --git a/src/mixins/GlobalMixin.js b/src/mixins/GlobalMixin.js
index 78f9aa8..b6247ce 100644
--- a/src/mixins/GlobalMixin.js
+++ b/src/mixins/GlobalMixin.js
@@ -1,5 +1,7 @@
import { useAppStore } from '@/stores/app';
+const debounceData = [];
+
export default {
data(){
return {
@@ -56,6 +58,16 @@ export default {
if (this.store?.prefs?.debug){
console.log(...args);
}
- }
+ },
+ debounce(fn){
+ let f = debounceData.find(f=>f.fn == fn);
+ if (f){
+ clearTimeout(f.to);
+ }else{
+ f = {fn};
+ debounceData.push(f);
+ }
+ f.to = setTimeout(...arguments);
+ },
}
}
\ No newline at end of file
diff --git a/src/pages/manage/game-objects/[[id]].vue b/src/pages/manage/game-objects/[[id]].vue
index 07f6385..8171b56 100644
--- a/src/pages/manage/game-objects/[[id]].vue
+++ b/src/pages/manage/game-objects/[[id]].vue
@@ -16,8 +16,17 @@
-
-
{{ object.asset.name }} | {{ object.asset.ofn }}
+
+
+
+
+
+
+
+
+
+
+ {{ object.asset.name }} | {{ object.asset.ofn }}
v ? true : this.l.fieldRequired,
@@ -66,11 +77,9 @@ export default {
if (this.id && this.id != 'add') {
this.object = (await this.$api.gameObject.load(this.id)).data;
}
+ this.tagList = (await this.$api.gameObject.getTags()).data;
},
computed: {
- fileToUpload() {
- return this.object?.file instanceof File
- },
id() {
return this.$route.params?.id;
},
@@ -83,17 +92,16 @@ export default {
this.loading = true;
try {
let fd = new FormData();
- let keys = ['name', 'type'];
- if (this.fileToUpload) keys.push('file');
- if (this.id != 'add') keys.push('id');
- if (this.object.thumb) keys.push('thumb');
-
- keys.forEach(e => fd.append(e, this.object[e]))
+ if (this.file) {
+ fd.append('file', this.file)
+ }
+ fd.append('object', JSON.stringify(this.object));
let result = await this.$api.gameObject.save(fd);
Object.assign(this.object, result.data.object);
if (this.id == 'add') {
this.$router.replace({ params: { id: this.object.id }, query:{ tab:'preview' } });
}
+ this.debug(this.object)
// await this.$nextTick();
// this.panel = 'preview';
// if (!params?.thumbOnly) await this.$refs.assetPreview.loadAsset();
diff --git a/src/plugins/api.js b/src/plugins/api.js
index 8c05caa..15996a5 100644
--- a/src/plugins/api.js
+++ b/src/plugins/api.js
@@ -33,6 +33,9 @@ export default {
},
async remove(id){
return await $ax.delete(`/game-object/${id}`)
+ },
+ async getTags(q){
+ return await $ax.post('/game-object/tags', {q});
}
},
scenario:{
diff --git a/src/plugins/lang.js b/src/plugins/lang.js
index c2568ac..d21a7bb 100644
--- a/src/plugins/lang.js
+++ b/src/plugins/lang.js
@@ -10,6 +10,8 @@ const lang = {
name: 'Name',
id: 'Identifier',
description: 'Description',
+ tags: 'Tags',
+ search: 'Search',
fieldRequired: 'Field is required',
objectType: 'Object type',
objectFile: 'File',
@@ -163,6 +165,8 @@ const lang = {
name: 'Име',
id: 'Идентификатор',
description: 'Описание',
+ tags: 'Етикети',
+ search: 'Търсене',
fieldRequired: 'Полето е задължително',
objectType: 'Тип обект',
objectFile: 'Файл',
diff --git a/src/plugins/vuetify.js b/src/plugins/vuetify.js
index 47cc85f..f18c9b3 100644
--- a/src/plugins/vuetify.js
+++ b/src/plugins/vuetify.js
@@ -24,6 +24,9 @@ export default createVuetify({
VSelect: {
variant: 'outlined'
},
+ VCombobox: {
+ variant: 'outlined'
+ },
VTextField: {
variant: 'outlined'
},
diff --git a/src/styles/style.scss b/src/styles/style.scss
index 85ffb1c..ae42cc2 100644
--- a/src/styles/style.scss
+++ b/src/styles/style.scss
@@ -115,7 +115,7 @@ video{
overflow: hidden;
width: 100%;
max-width: 100vw;
- height: calc(100vh - 244px);
+ height: calc(100vh - 277px);
&.pan {
cursor: grab;
}
@@ -144,4 +144,5 @@ audio {
left:unset !important;
bottom: 0 !important;
right: 0 !important;
-}
\ No newline at end of file
+}
+