diff --git a/Frontend/vue-project/src/components/CatalogGenres.vue b/Frontend/vue-project/src/components/CatalogGenres.vue new file mode 100644 index 0000000..f29daf6 --- /dev/null +++ b/Frontend/vue-project/src/components/CatalogGenres.vue @@ -0,0 +1,46 @@ + + + \ No newline at end of file diff --git a/Frontend/vue-project/src/components/Catalogs.vue b/Frontend/vue-project/src/components/Catalogs.vue new file mode 100644 index 0000000..b95dcad --- /dev/null +++ b/Frontend/vue-project/src/components/Catalogs.vue @@ -0,0 +1,23 @@ + + + \ No newline at end of file diff --git a/Frontend/vue-project/src/components/DataTable.vue b/Frontend/vue-project/src/components/DataTable.vue new file mode 100644 index 0000000..036f145 --- /dev/null +++ b/Frontend/vue-project/src/components/DataTable.vue @@ -0,0 +1,70 @@ + + + + + \ No newline at end of file diff --git a/Frontend/vue-project/src/components/Header.vue b/Frontend/vue-project/src/components/Header.vue index 225f150..01a052a 100644 --- a/Frontend/vue-project/src/components/Header.vue +++ b/Frontend/vue-project/src/components/Header.vue @@ -23,6 +23,9 @@ + diff --git a/Frontend/vue-project/src/components/Modal.vue b/Frontend/vue-project/src/components/Modal.vue new file mode 100644 index 0000000..f12c004 --- /dev/null +++ b/Frontend/vue-project/src/components/Modal.vue @@ -0,0 +1,63 @@ + + + + + \ No newline at end of file diff --git a/Frontend/vue-project/src/components/Table.vue b/Frontend/vue-project/src/components/Table.vue new file mode 100644 index 0000000..732105b --- /dev/null +++ b/Frontend/vue-project/src/components/Table.vue @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/Frontend/vue-project/src/components/ToolBar.vue b/Frontend/vue-project/src/components/ToolBar.vue new file mode 100644 index 0000000..0a21290 --- /dev/null +++ b/Frontend/vue-project/src/components/ToolBar.vue @@ -0,0 +1,46 @@ + + + + + \ No newline at end of file diff --git a/Frontend/vue-project/src/main.js b/Frontend/vue-project/src/main.js index 2281dd6..75e6397 100644 --- a/Frontend/vue-project/src/main.js +++ b/Frontend/vue-project/src/main.js @@ -6,15 +6,21 @@ import Player from './components/Player.vue' import Films from './components/Films.vue' import About_us from './components/About_us.vue' import Form from './components/Form.vue' +import Table from './components/Table.vue' +import Catalogs from './components/Catalogs.vue' +import CatalogGenres from './components/CatalogGenres.vue' const routes = [ { path: '/', redirect: '/index' }, - { path: '/index', component: Index }, + { path: '/index', component: Index}, { path: '/player', component: Player}, { path: '/films', component: Films}, { path: '/about_us', component: About_us}, - { path: '/form', component: Form} + { path: '/form', component: Form}, + { path: '/table', component: Table}, + { path: '/catalogs', component: Catalogs}, + { path: '/catalogs/genres', component: CatalogGenres} ] const router = createRouter({ diff --git a/Frontend/vue-project/src/mixins/CatalogMixins.js b/Frontend/vue-project/src/mixins/CatalogMixins.js new file mode 100644 index 0000000..4b493d2 --- /dev/null +++ b/Frontend/vue-project/src/mixins/CatalogMixins.js @@ -0,0 +1,102 @@ +import ToolBar from '../components/ToolBar.vue'; +import DataTable from '../components/DataTable.vue'; +import Modal from '../components/Modal.vue'; +import DataService from '../services/DataService'; + +const CatalogMixin = { + components: { + ToolBar, DataTable, Modal + }, + data() { + return { + getAllUrl: undefined, + dataUrl: undefined, + transformer: undefined, + headers: [], + items: [], + selectedItems: [], + modal: { + header: undefined, + confirm: undefined, + }, + modalShow: false, + data: undefined, + isEdit: false + } + }, + created() { + this.loadItems(); + }, + methods: { + loadItems() { + this.getItems(); + this.data = this.transformer(); + }, + getItems() { + DataService.readAll(this.getAllUrl, this.transformer) + .then(data => { + this.items = data; + }); + }, + showAddModal() { + this.isEdit = false; + this.data = this.transformer(); + this.modal.header = 'Добавление элемента'; + this.modal.confirm = 'Добавить'; + this.modalShow = true; + }, + showEditModal() { + if (this.selectedItems.length === 0) { + return; + } + this.showEditModalDblClick(this.selectedItems[0]); + }, + showEditModalDblClick(editId) { + DataService.read(this.dataUrl + "/" + editId, this.transformer) + .then(data => { + this.data = data; + this.isEdit = true; + this.modal.header = 'Редактирование элемента'; + this.modal.confirm = 'Сохранить'; + this.modalShow = true; + }); + }, + saveItem() { + if (!this.isEdit) { + DataService.create(this.dataUrl + "/", this.data) + .then(() => { + this.getItems(); + }); + } else { + DataService.update(this.dataUrl + "/" + this.data.id, this.data) + .then(() => { + this.getItems(); + }); + } + }, + removeSelectedItems() { + if (this.selectedItems.length === 0) { + return; + } + if (confirm('Удалить выбранные элементы?')) { + const promises = []; + const self = this; + this.selectedItems.forEach(item => { + promises.push(DataService.delete(this.dataUrl + item)); + }); + Promise.all(promises).then((results) => { + results.forEach(function (id) { + const index = self.selectedItems.indexOf(id); + if (index === - 1) { + return; + } + self.selectedItems.splice(index, 1); + }); + this.getItems(); + }); + } + } + } +} + +export default CatalogMixin; \ No newline at end of file diff --git a/Frontend/vue-project/src/models/Collection.js b/Frontend/vue-project/src/models/Collection.js new file mode 100644 index 0000000..dbef5df --- /dev/null +++ b/Frontend/vue-project/src/models/Collection.js @@ -0,0 +1,26 @@ +export default class Collection { + constructor(data) { + this._id = data?.id; + this._name = data?.name; + this._filmIds = data?.filmIds; + } + + get id() { + return this._id; + } + + get name() { + return this._name; + } + + set name(value) { + if (typeof value !== 'string' || value === null || value.length == 0) { + throw 'New name value ' + value + ' is not a string or empty'; + } + this._name = value; + } + + get filmIds() { + return this.filmIds; + } +} \ No newline at end of file diff --git a/Frontend/vue-project/src/models/Film.js b/Frontend/vue-project/src/models/Film.js new file mode 100644 index 0000000..aa7c7d0 --- /dev/null +++ b/Frontend/vue-project/src/models/Film.js @@ -0,0 +1,26 @@ +export default class Film { + constructor(data) { + this._id = data?.id; + this._name = data?.name; + this._genreIds = data?.genreIds; + } + + get id() { + return this._id; + } + + get name() { + return this._name; + } + + set name(value) { + if (typeof value !== 'string' || value === null || value.length == 0) { + throw 'New name value ' + value + ' is not a string or empty'; + } + this._name = value; + } + + get genreIds() { + return this._genreIds; + } +} \ No newline at end of file diff --git a/Frontend/vue-project/src/models/Genre.js b/Frontend/vue-project/src/models/Genre.js new file mode 100644 index 0000000..80b53da --- /dev/null +++ b/Frontend/vue-project/src/models/Genre.js @@ -0,0 +1,21 @@ +export default class Genre { + constructor(data) { + this._id = data?.id; + this._name = data?.name; + } + + get id() { + return this._id; + } + + get name() { + return this._name; + } + + set name(value) { + if (typeof value !== 'string' || value === null || value.length == 0) { + throw 'New name value ' + value + ' is not a string or empty'; + } + this._name = value; + } +} \ No newline at end of file diff --git a/Frontend/vue-project/src/services/DataService.js b/Frontend/vue-project/src/services/DataService.js new file mode 100644 index 0000000..54810c9 --- /dev/null +++ b/Frontend/vue-project/src/services/DataService.js @@ -0,0 +1,42 @@ +import axios from 'axios'; + +function toJSON(data) { + const jsonObj = {}; + const fields = Object.getOwnPropertyNames(data); + for (const field of fields) { + if (data[field] === undefined) { + continue; + } + jsonObj[field.substring(1)] = data[field]; + } + return jsonObj; +} + +export default class DataService { + static dataUrlPrefix = 'http://localhost:8080/'; + + static async readAll(url, transformer) { + const response = await axios.get(this.dataUrlPrefix + url); + return response.data.map(item => transformer(item)); + } + + static async read(url, transformer) { + const response = await axios.get(this.dataUrlPrefix + url); + return transformer(response.data); + } + + static async create(url, data) { + const response = await axios.post(this.dataUrlPrefix + url, toJSON(data)); + return true; + } + + static async update(url, data) { + const response = await axios.put(this.dataUrlPrefix + url, toJSON(data)); + return true; + } + + static async delete(url) { + const response = await axios.delete(this.dataUrlPrefix + url); + return response.data.id; + } +} \ No newline at end of file diff --git a/data.mv.db b/data.mv.db index 9be51e6..39ecef2 100644 Binary files a/data.mv.db and b/data.mv.db differ diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/controller/GenreController.java b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/GenreController.java index 3453274..d0db0aa 100644 --- a/src/main/java/ru/ulstu/is/lab1/DataBase/controller/GenreController.java +++ b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/GenreController.java @@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import ru.ulstu.is.lab1.DataBase.model.Genre; import ru.ulstu.is.lab1.DataBase.service.GenreService; import java.util.List; diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/service/CollectionService.java b/src/main/java/ru/ulstu/is/lab1/DataBase/service/CollectionService.java index 6dc63cd..14bac03 100644 --- a/src/main/java/ru/ulstu/is/lab1/DataBase/service/CollectionService.java +++ b/src/main/java/ru/ulstu/is/lab1/DataBase/service/CollectionService.java @@ -3,7 +3,6 @@ package ru.ulstu.is.lab1.DataBase.service; import ru.ulstu.is.lab1.DataBase.model.Film; import ru.ulstu.is.lab1.DataBase.model.Collection; import ru.ulstu.is.lab1.DataBase.Repository.ICollectionRepository; -import ru.ulstu.is.lab1.DataBase.model.Genre; import ru.ulstu.is.lab1.util.validation.ValidatorUtil; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;