Почти всё, ура

This commit is contained in:
maxnes3 2023-05-12 19:23:50 +04:00
parent fa6f53bd8a
commit 3a9239b77e
13 changed files with 673 additions and 3 deletions

View File

@ -26,6 +26,11 @@ public class AuthorController {
return authorService.findAllAuthors().stream().map(AuthorDto::new).toList();
}
@GetMapping("/{id}/books")
public List<BookDto> getAuthorBooks(@PathVariable Long id){
return authorService.authorBooks(id).stream().map(BookDto::new).toList();
}
@PostMapping
public AuthorDto createAuthor(@RequestBody @Valid AuthorDto authorDto) throws IOException {
return new AuthorDto(authorService.addAuthor(authorDto));

View File

@ -26,6 +26,11 @@ public class BookController {
return bookService.findAllBooks().stream().map(BookDto::new).toList();
}
@GetMapping("/{id}/genres")
public List<GenreDto> getBookGenres(@PathVariable Long id){
return bookService.bookGenres(id).stream().map(GenreDto::new).toList();
}
@PostMapping
public BookDto createBook(@RequestBody @Valid BookDto bookDto) throws IOException {
return new BookDto(bookService.addBook(bookDto));

View File

@ -66,6 +66,11 @@ public class AuthorService {
return authorRepository.findAll();
}
@Transactional(readOnly = true)
public List<Book> authorBooks(Long id){
return authorRepository.findById(id).get().getBooks();
}
@Transactional
public Author updateAuthor(Long id, String firstname, String lastname, File photo){
if (!StringUtils.hasText(firstname) || !StringUtils.hasText(lastname)) {

View File

@ -6,6 +6,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import ru.ip.labworks.labworks.bookshop.controller.BookDto;
import ru.ip.labworks.labworks.bookshop.model.Book;
import ru.ip.labworks.labworks.bookshop.model.Genre;
import ru.ip.labworks.labworks.bookshop.repository.BookRepository;
import ru.ip.labworks.labworks.bookshop.repository.GenreRepository;
import ru.ip.labworks.labworks.util.validation.ValidatorUtil;
@ -70,6 +71,11 @@ public class BookService {
return bookRepository.findAll();
}
@Transactional(readOnly = true)
public List<Genre> bookGenres(Long id){
return bookRepository.findById(id).get().getGenres();
}
@Transactional
public Book updateBook(Long id, String name, String release, File cover) {
if (!StringUtils.hasText(name) || !StringUtils.hasText(release)) {

View File

@ -1,5 +1,5 @@
<template>
<footer class="container pt-4 my-md-5 pt-md-5 text-center fixed-bottom border-top">
<footer class="container pt-4 my-md-5 pt-md-5 text-center border-top">
<div class="row">
<div class="col-12 col-md">
<h5 class=""><strong>End</strong></h5>

View File

@ -1,10 +1,20 @@
<template>
<header class="fixed-top">
<nav class="navbar navbar-expand-lg bg-primary" data-bs-theme="dark">
<nav class="navbar navbar-expand-lg bg-success" data-bs-theme="dark">
<div class="container">
<a class="navbar-brand" href="#">
<strong>{ DTC } Data Type Calculator</strong>
<strong>{ OBS } Online Book Service</strong>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-link active" aria-current="page" href="/authors">Authors</a>
<a class="nav-link" href="/books">Books</a>
<a class="nav-link" href="/genres">Genres</a>
</div>
</div>
</div>
</nav>
</header>

View File

@ -0,0 +1,8 @@
export default class Author{
constructor(data){
this.id = data?.id;
this.firstname = data?.firstname;
this.lastname = data?.lastname;
this.photo = data?.photo;
}
}

View File

@ -0,0 +1,8 @@
export default class Book{
constructor(data){
this.id = data?.id;
this.name = data?.name;
this.release = data?.release;
this.cover = data?.cover;
}
}

View File

@ -0,0 +1,6 @@
export default class Genre{
constructor(data){
this.id = data?.id;
this.name = data?.name;
}
}

View File

@ -0,0 +1,245 @@
<script>
import 'axios';
import axios from "axios";
import Header from '../components/Header.vue';
import Footer from '../components/Footer.vue';
import Author from '../models/Author';
import Book from '../models/Book';
export default{
components:{
Header,
Footer
},
created(){
this.getAuthors();
},
mounted() {
const addModal = document.getElementById('editModal');
addModal.addEventListener('shown.bs.modal', function () {
})
},
data(){
return{
authors: [],
authorBooks: [],
allBooks: [],
bookid: 0,
URL: "http://localhost:8080/",
author: new Author(),
}
},
methods:{
getAuthors(){
axios.get(this.URL + "author")
.then(response => {
this.authors = response.data;
console.log(response.data);
})
.catch(error => {
console.log(error);
});
},
async addAuthor(){
await this.toBase64();
console.log(this.author);
axios.post(this.URL + "author", this.author)
.then(() => {
this.getAuthors();
this.closeModal();
})
.catch(error => {
console.log(error);
});
},
deleteAuthor(id){
axios.delete(this.URL + `author/${id}`)
.then(() =>{
this.getAuthors();
})
},
async updateAuthor(author){
if(author.photo === undefined) await this.toBase64();
axios.put(this.URL + `author/${author.id}`, this.author)
.then(() =>{
this.getAuthors();
})
this.closeModal();
},
openModal() {
document.getElementById("editModal").style.display = "block";
},
openManyToManyModal(){
this.getAuthorBooks();
this.getAllBooks();
document.getElementById("manyToManyModal").style.display = "block";
},
closeModal() {
document.getElementById("editModal").style.display = "none";
this.author = new Object();
},
closeManyToManyModal() {
document.getElementById("manyToManyModal").style.display = "none";
},
async toBase64(){
var file = document.getElementById("photo").files[0];
var reader = new FileReader();
var phototemp = this.author;
reader.readAsDataURL(file);
await new Promise((resolve, reject) => {
reader.onload = function () {
phototemp.photo = reader.result;
console.log(phototemp);
resolve();
};
reader.onerror = function (error) {
console.log('Error: ', error);
reject(error);
};
});
},
getAllBooks(){
axios.get(this.URL + "book")
.then(response => {
this.allBooks = response.data;
console.log(response.data);
})
.catch(error => {
console.log(error);
});
},
getAuthorBooks(){
axios.get(this.URL + `author/${this.author.id}/books`)
.then(response => {
this.authorBooks = response.data;
console.log(response.data);
})
.catch(error => {
console.log(error);
});
},
addBook(){
axios.post(this.URL + `author/${this.author.id}/Book/${this.bookid}`)
.then(() => {
this.getAuthorBooks();
})
.catch(error => {
console.log(error);
});
},
removeBook(id){
axios.delete(this.URL + `author/${this.author.id}/Book/${id}`)
.then(() =>{
this.getAuthorBooks();
})
}
}
}
</script>
<template>
<Header></Header>
<main style="margin-top: 50pt;">
<div class="container mt-4">
<h1 class="text-center mb-4">Author Table:</h1>
<button class="btn btn-success mr-2" @click="openModal(); author = new Object(); author.status = `create`">Добавить</button>
<table class="table table-striped">
<thead>
<tr>
<th>Имя:</th>
<th>Фамилия:</th>
<th>Фото:</th>
<th>Реадактировать запись:</th>
</tr>
</thead>
<tbody>
<tr v-for="aut in authors" :key="aut.id">
<td>{{ aut.firstname }}</td>
<td>{{ aut.lastname }}</td>
<td><img :src="aut.photo" class="img-thumbnail mw-50 mh-50"/></td>
<td>
<button class="btn btn-warning" @click="openModal(); author = Object.assign({}, aut); author.status = `edit`">Изменить</button>
<button class="btn btn-danger" @click="deleteAuthor(aut.id)">Удалить</button>
<button class="btn btn-primary" @click="author = aut; openManyToManyModal()">Книги</button>
</td>
</tr>
</tbody>
</table>
</div>
</main>
<div class="modal" tabindex="-1" id="editModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Author</h5>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label for="firstname">Имя:</label>
<input type="text" class="form-control" id="firstname" name="firstname" v-model="author.firstname">
<label for="lastname">Фамилия:</label>
<input type="text" class="form-control" id="lastname" name="lastname" v-model="author.lastname">
<label for="photo">Фотография:</label>
<input class="form-control" type="file" id="photo" name="photo" @change="toBase64()">
<img :src="author.photo" class="img-thumbnail"/>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="editModal" @click="closeModal()">Закрыть</button>
<button type="button" class="btn btn-success" v-if="author.status === `create`"
@click="addAuthor">Создать</button>
<button type="button" class="btn btn-primary" v-else
@click="updateAuthor(author)">Сохранить</button>
</div>
</div>
</div>
</div>
<div class="modal" tabindex="-1" id="manyToManyModal">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add Books to Author</h5>
</div>
<div class="modal-body">
<form>
<table class="table table-striped">
<thead>
<tr>
<th>Название:</th>
<th>Дата релиза:</th>
<th>Обложка:</th>
<th>Реадактировать запись:</th>
</tr>
</thead>
<tbody>
<tr v-for="bk in authorBooks" :key="bk.id">
<td>{{ bk.name }}</td>
<td>{{ bk.release }}</td>
<td><img :src="bk.cover" class="img-thumbnail mw-50 mh-50"/></td>
<td>
<button class="btn btn-primary" type="button" @click="removeBook(bk.id)">Удалить</button>
</td>
</tr>
</tbody>
</table>
<div class="input-group mb-3">
<select class="form-select" v-model="bookid">
<option v-for="sbk in allBooks" :key="sbk.id" :value="sbk.id">{{ sbk.name }}</option>
</select>
<button class="btn btn-outline-secondary" type="button" @click="addBook()">Добавить</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="editModal" @click="closeManyToManyModal()">Закрыть</button>
</div>
</div>
</div>
</div>
<Footer></Footer>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,239 @@
<script>
import 'axios';
import axios from "axios";
import Header from '../components/Header.vue';
import Footer from '../components/Footer.vue';
import Book from '../models/Book';
export default{
components:{
Header,
Footer
},
created(){
this.getBooks();
},
mounted() {
const addModal = document.getElementById('editModal');
addModal.addEventListener('shown.bs.modal', function () {
})
},
data(){
return{
books: [],
bookGenres: [],
allGenres: [],
genreid: 0,
URL: "http://localhost:8080/",
book: new Book(),
}
},
methods:{
getBooks(){
axios.get(this.URL + "book")
.then(response => {
this.books = response.data;
console.log(response.data);
})
.catch(error => {
console.log(error);
});
},
async addBook(){
await this.toBase64();
console.log(this.book);
axios.post(this.URL + "book", this.book)
.then(() => {
this.getBooks();
this.closeModal();
})
.catch(error => {
console.log(error);
});
},
deleteBook(id){
axios.delete(this.URL + `book/${id}`)
.then(() =>{
this.getBooks();
})
},
async updateBook(book){
if(book.cover === undefined) await this.toBase64();
axios.put(this.URL + `book/${book.id}`, this.book)
.then(() =>{
this.getBooks();
})
this.closeModal();
},
openModal() {
document.getElementById("editModal").style.display = "block";
},
openManyToManyModal(){
this.getBookGenres();
this.getAllGenres();
document.getElementById("manyToManyModal").style.display = "block";
},
closeModal() {
document.getElementById("editModal").style.display = "none";
},
closeManyToManyModal() {
document.getElementById("manyToManyModal").style.display = "none";
},
async toBase64(){
var file = document.getElementById("cover").files[0];
var reader = new FileReader();
var phototemp = this.book;
reader.readAsDataURL(file);
await new Promise((resolve, reject) => {
reader.onload = function () {
phototemp.cover = reader.result;
console.log(phototemp);
resolve();
};
reader.onerror = function (error) {
console.log('Error: ', error);
reject(error);
};
});
},
getAllGenres(){
axios.get(this.URL + "genre")
.then(response => {
this.allGenres = response.data;
console.log(response.data);
})
.catch(error => {
console.log(error);
});
},
getBookGenres(){
axios.get(this.URL + `book/${this.book.id}/genres`)
.then(response => {
this.bookGenres = response.data;
console.log(response.data);
})
.catch(error => {
console.log(error);
});
},
addGenre(){
axios.post(this.URL + `book/${this.book.id}/Genre/${this.genreid}`)
.then(() => {
this.getBookGenres();
})
.catch(error => {
console.log(error);
});
},
removeGenre(id){
axios.delete(this.URL + `book/${this.book.id}/Genre/${id}`)
.then(() =>{
this.getBookGenres();
})
}
}
}
</script>
<template>
<Header></Header>
<main style="margin-top: 50pt;">
<div class="container mt-4">
<h1 class="text-center mb-4">Book Table:</h1>
<button class="btn btn-success mr-2" @click="openModal(); book = new Object(); book.status = `create`">Добавить</button>
<table class="table table-striped">
<thead>
<tr>
<th>Название:</th>
<th>Дата релиза:</th>
<th>Обложка:</th>
<th>Реадактировать запись:</th>
</tr>
</thead>
<tbody>
<tr v-for="bk in books" :key="bk.id">
<td>{{ bk.name }}</td>
<td>{{ bk.release }}</td>
<td><img :src="bk.cover" class="img-thumbnail mw-50 mh-50"/></td>
<td>
<button class="btn btn-warning" @click="openModal(); book = Object.assign({},bk); book.status = `edit`">Изменить</button>
<button class="btn btn-danger" @click="deleteBook(bk.id)">Удалить</button>
<button class="btn btn-primary" @click="book = bk; openManyToManyModal()">Жанры</button>
</td>
</tr>
</tbody>
</table>
</div>
</main>
<div class="modal" tabindex="-1" id="editModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Book</h5>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label for="name">Название:</label>
<input type="text" class="form-control" id="name" name="name" v-model="book.name">
<label for="release">Дата релиза:</label>
<input type="date" class="form-control" id="release" name="release" v-model="book.release">
<label for="cover">Обложка:</label>
<input class="form-control" type="file" id="cover" name="cover" @change="toBase64()">
<img :src="book.cover" class="img-thumbnail"/>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="editModal" @click="closeModal()">Закрыть</button>
<button type="button" class="btn btn-success" v-if="book.status === `create`"
@click="addBook">Создать</button>
<button type="button" class="btn btn-primary" v-else
@click="updateBook(book)">Сохранить</button>
</div>
</div>
</div>
</div>
<div class="modal" tabindex="-1" id="manyToManyModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add Genres to Book</h5>
</div>
<div class="modal-body">
<form>
<table class="table table-striped">
<thead>
<tr>
<th>Название:</th>
<th>Реадактировать запись:</th>
</tr>
</thead>
<tbody>
<tr v-for="gen in bookGenres" :key="gen.id">
<td>{{ gen.name }}</td>
<td>
<button class="btn btn-primary" type="button" @click="removeGenre(gen.id)">Удалить</button>
</td>
</tr>
</tbody>
</table>
<div class="input-group mb-3">
<select class="form-select" v-model="genreid">
<option v-for="sgen in allGenres" :key="sgen.id" :value="sgen.id">{{ sgen.name }}</option>
</select>
<button class="btn btn-outline-secondary" type="button" @click="addGenre()">Добавить</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="editModal" @click="closeManyToManyModal()">Закрыть</button>
</div>
</div>
</div>
</div>
<Footer></Footer>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,127 @@
<script>
import 'axios';
import axios from "axios";
import Header from '../components/Header.vue';
import Footer from '../components/Footer.vue';
import Genre from '../models/Genre';
export default{
components:{
Header,
Footer
},
created(){
this.getGenres();
},
mounted() {
const addModal = document.getElementById('editModal');
addModal.addEventListener('shown.bs.modal', function () {
})
},
data(){
return{
genres: [],
URL: "http://localhost:8080/",
genre: new Genre(),
}
},
methods:{
getGenres(){
axios.get(this.URL + "genre")
.then(response => {
this.genres = response.data;
console.log(response.data);
})
.catch(error => {
console.log(error);
});
},
addGenre(){
console.log(this.genre);
axios.post(this.URL + "genre", this.genre)
.then(() => {
this.getGenres();
this.closeModal();
})
.catch(error => {
console.log(error);
});
},
deleteGenre(id){
axios.delete(this.URL + `genre/${id}`)
.then(() =>{
this.getGenres();
})
},
updateGenre(genre){
this.genre = genre;
axios.put(this.URL + `genre/${genre.id}`, this.genre)
.then(() =>{
this.getGenres();
})
this.closeModal();
},
openModal() {
document.getElementById("editModal").style.display = "block";
},
closeModal() {
document.getElementById("editModal").style.display = "none";
}
}
}
</script>
<template>
<Header></Header>
<main style="margin-top: 50pt;">
<div class="container mt-4">
<h1 class="text-center mb-4">Genre Table:</h1>
<button class="btn btn-success mr-2" @click="openModal(); genre = new Object(); genre.status = `create`">Добавить</button>
<table class="table table-striped">
<thead>
<tr>
<th>Название:</th>
<th>Реадактировать запись:</th>
</tr>
</thead>
<tbody>
<tr v-for="gen in genres" :key="gen.id">
<td>{{ gen.name }}</td>
<td>
<button class="btn btn-warning" @click="openModal(); genre = Object.assign({},gen); genre.status = `edit`">Изменить</button>
<button class="btn btn-danger" @click="deleteGenre(gen.id)">Удалить</button>
</td>
</tr>
</tbody>
</table>
</div>
</main>
<div class="modal" tabindex="-1" id="editModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Genre</h5>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label for="name">Название:</label>
<input type="text" class="form-control" id="name" name="name" v-model="genre.name">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="editModal" @click="closeModal()">Закрыть</button>
<button type="button" class="btn btn-success" v-if="genre.status === `create`"
@click="addGenre">Создать</button>
<button type="button" class="btn btn-primary" v-else
@click="updateGenre(genre)">Сохранить</button>
</div>
</div>
</div>
</div>
<Footer></Footer>
</template>
<style scoped>
</style>

View File

@ -1,8 +1,14 @@
import {createRouter, createWebHistory} from "vue-router"
import Index from '../pages/Index.vue'
import Authors from '../pages/Authors.vue'
import Books from '../pages/Books.vue'
import Genres from '../pages/Genres.vue'
const routes = [
{path: '/', component: Index},
{path: '/authors', component: Authors},
{path: '/books', component: Books},
{path: '/genres', component: Genres},
]
const router = createRouter({