From b11e763fd9aceebc60c5e05bba8ec598348cae39 Mon Sep 17 00:00:00 2001 From: goynov Date: Fri, 13 Feb 2026 22:02:31 +0200 Subject: [PATCH] #19, added tags, and tags filtering --- backend/app/Db.js | 1 + backend/app/bl/GameObjectsManager.js | 7 ++++ .../controllers/api/GameObjectsController.js | 11 ++++-- .../AssetsManagement/AssetBrowser.vue | 34 +++++++++++++++---- src/mixins/GameEnvironmentMixin.js | 1 + src/mixins/GlobalMixin.js | 14 +++++++- src/pages/manage/game-objects/[[id]].vue | 30 ++++++++++------ src/plugins/api.js | 3 ++ src/plugins/lang.js | 4 +++ src/plugins/vuetify.js | 3 ++ src/styles/style.scss | 5 +-- 11 files changed, 90 insertions(+), 23 deletions(-) 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 @@ \ 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 +} +