нет слоф

This commit is contained in:
VictoriaPresnyakova 2023-05-01 23:42:39 +04:00
parent b3b5f24f65
commit 58f188941b
13 changed files with 366 additions and 54 deletions

View File

@ -14,13 +14,22 @@ repositories {
dependencies { dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-devtools'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
implementation 'org.webjars:bootstrap:5.1.3'
implementation 'org.webjars:jquery:3.6.0'
implementation 'org.webjars:font-awesome:6.1.0'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.h2database:h2:2.1.210' implementation 'com.h2database:h2:2.1.210'
implementation 'org.hibernate.validator:hibernate-validator' implementation 'org.hibernate.validator:hibernate-validator'
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
implementation 'org.springdoc:springdoc-openapi-ui:1.6.5'
testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-validation'
} }

Binary file not shown.

View File

@ -12,9 +12,10 @@ export default {
return { return {
getAllUrl: 'component/', getAllUrl: 'component/',
dataUrl: 'component', dataUrl: 'component',
transformer: (data) => new Comment(data), transformer: (data) => new Component(data),
headers: [ headers: [
{ name: 'componentName', label: 'Название компонента' } { name: 'componentName', label: 'Название компонента' },
{ name: 'amount', label: 'Кол-во' }
], ],
dataFilterUrl: 'component/filter?' dataFilterUrl: 'component/filter?'
} }
@ -23,8 +24,17 @@ export default {
filter() { filter() {
let urlParams = "" let urlParams = ""
if (document.getElementById('componentNameFilterInput').value != "") { if (document.getElementById('componentNameFilterInput').value != "") {
if (urlParams != "") {
urlParams += "&";
}
urlParams += "componentName=" + this.componentName; urlParams += "componentName=" + this.componentName;
} }
if (document.getElementById('componentAmountFilterInput').value != "") {
if (urlParams != "") {
urlParams += "&";
}
urlParams += "componentAmount=" + this.serialNumber;
}
DataService.readAll(this.dataFilterUrl + urlParams, (data) => new Component(data)) DataService.readAll(this.dataFilterUrl + urlParams, (data) => new Component(data))
.then(data => { .then(data => {
this.items = data; this.items = data;
@ -34,6 +44,7 @@ export default {
this.loadItems(); this.loadItems();
this.id = null; this.id = null;
this.componentName = null; this.componentName = null;
this.amount = null;
} }
} }
} }
@ -42,6 +53,8 @@ export default {
<template> <template>
<div class="input-group mb-3"> <div class="input-group mb-3">
<input type="text" class="form-control" id="componentNameFilterInput" placeholder="Name" required v-model="componentName"> <input type="text" class="form-control" id="componentNameFilterInput" placeholder="Name" required v-model="componentName">
<input type="number" class="form-control" id="componentAmountFilterInput" placeholder="Amount" required v-model="amount">
<button class="btn btn-primary" type="button" id="report-button" <button class="btn btn-primary" type="button" id="report-button"
@click.prevent="filter">Сформировать</button> @click.prevent="filter">Сформировать</button>
<button class="btn btn-outline-secondary" type="button" id="report-button" <button class="btn btn-outline-secondary" type="button" id="report-button"
@ -67,5 +80,9 @@ export default {
<label for="name" class="form-label">Name</label> <label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" required v-model="data.componentName"> <input type="text" class="form-control" id="name" required v-model="data.componentName">
</div> </div>
<div class="mb-3">
<label for="amount" class="form-label">Amount</label>
<input type="number" class="form-control" id="amount" required v-model="data.amount">
</div>
</Modal> </Modal>
</template> </template>

View File

@ -0,0 +1,70 @@
<script>
export default {
props: {
headers: Array,
items: Array,
selectedItems: Array
},
emits: {
dblclick: null
},
methods: {
rowClick(id) {
if (this.isSelected(id)) {
var index = this.selectedItems.indexOf(id);
if (index !== -1) {
this.selectedItems.splice(index, 1);
}
} else {
this.selectedItems.push(id);
}
},
rowDblClick(id) {
this.$emit('dblclick', id);
},
isSelected(id) {
return this.selectedItems.includes(id);
},
dataConvert(data) {
return data;
}
}
}
</script>
<template>
<table class="table table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th v-for="header in this.headers"
:id="header.name"
scope="col">{{ header.label }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in this.items"
@click="rowClick(item.id)"
@dblclick="rowDblClick(item.id)"
:class="{selected: isSelected(item.id)}">
<th scope="row">{{ index + 1 }}</th>
<td v-for="header in this.headers">
{{ dataConvert(item[header.name]) }}
</td>
</tr>
</tbody>
</table>
</template>
<style scoped>
tbody tr:hover {
cursor: pointer;
}
tr.selected {
background-color: #0d6efd;
opacity: 80%;
}
tbody tr {
user-select: none;
}
</style>

View File

@ -20,20 +20,20 @@ export default {
{ name: 'price', label: 'price' }, { name: 'price', label: 'price' },
], ],
dataFilterUrl: 'favor/filter?', dataFilterUrl: 'favor/filter?',
cabinetUrl: 'cabinet/', orderUrl: 'order/',
cabinets: [], orders: [],
monitorUrl: 'monitor/', componentUrl: 'component/',
monitors: [] components: []
} }
}, },
created() { created() {
DataService.readAll(this.cabinetUrl, (data) => new Cabinet(data)) DataService.readAll(this.orderUrl, (data) => new Order(data))
.then(data => { .then(data => {
this.cabinets = data; this.orders = data;
}); });
DataService.readAll(this.monitorUrl, (data) => new Monitor(data)) DataService.readAll(this.componentUrl, (data) => new Component(data))
.then(data => { .then(data => {
this.monitors = data; this.components = data;
}); });
}, },
methods: { methods: {
@ -51,17 +51,17 @@ export default {
} }
urlParams += "serialNum=" + this.serialNumber; urlParams += "serialNum=" + this.serialNumber;
} }
if (document.getElementById('monitorFilterSelect').value != "") { if (document.getElementById('componentFilterSelect').value != "") {
if (urlParams != "") { if (urlParams != "") {
urlParams += "&"; urlParams += "&";
} }
urlParams += "monitorId=" + this.monitorId; urlParams += "componentId=" + this.componentId;
} }
if (document.getElementById('cabinetFilterSelect').value != "") { if (document.getElementById('orderFilterSelect').value != "") {
if (urlParams != "") { if (urlParams != "") {
urlParams += "&"; urlParams += "&";
} }
urlParams += "cabinetId=" + this.cabinetId; urlParams += "orderId=" + this.orderId;
} }
DataService.readAll(this.dataFilterUrl + urlParams, (data) => new Favor(data)) DataService.readAll(this.dataFilterUrl + urlParams, (data) => new Favor(data))
.then(data => { .then(data => {
@ -73,27 +73,27 @@ export default {
this.id = null; this.id = null;
this.modelName = null; this.modelName = null;
this.serialNumber = null; this.serialNumber = null;
this.monitorId = null; this.componentId = null;
this.cabinetId = null; this.orderId = null;
}, },
addCabinetToFavor(favorId) { addOrderToFavor(favorId) {
let cabinetId = document.getElementById('cabinets').value; let orderId = document.getElementById('orders').value;
let response = axios.post(`http://localhost:8080/api/favor/${favorId}/cabinet?cabinetId=${cabinetId}`); let response = axios.post(`http://localhost:8080/api/favor/${favorId}/order?orderId=${orderId}`);
}, },
itemsComps(favors) { itemsComps(favors) {
if (typeof favors === 'undefined') { if (typeof favors === 'undefined') {
return; return;
} }
favors.forEach(favor => { favors.forEach(favor => {
this.monitors.forEach(monitor => { this.components.forEach(component => {
if (favor.monitorId === monitor.id) { if (favor.componentId === component.id) {
favor.monitor = monitor; favor.component = component;
} }
}); });
this.cabinets.forEach(cabinet => { this.orders.forEach(order => {
cabinet.favorIds.forEach(favorId => { order.favorIds.forEach(favorId => {
if (favor.id === favorId) { if (favor.id === favorId) {
favor.cabinet = cabinet; favor.order = order;
} }
}); });
}); });
@ -110,14 +110,14 @@ export default {
<input type="text" class="form-control" id="serialNumberFilterInput" placeholder="Серийный номер" required v-model="serialNumber"> <input type="text" class="form-control" id="serialNumberFilterInput" placeholder="Серийный номер" required v-model="serialNumber">
<select class="form-select" id="monitorFilterSelect" v-model="monitorId"> <select class="form-select" id="componentFilterSelect" v-model="componentId">
<option disabled value="" selected>Выберите монитор</option> <option disabled value="" selected>Выберите монитор</option>
<option v-for="monitor in monitors" :value="monitor.id">{{ monitor.modelName }}</option> <option v-for="component in components" :value="component.id">{{ component.modelName }}</option>
</select> </select>
<select class="form-select" id="cabinetFilterSelect" v-model="cabinetId"> <select class="form-select" id="orderFilterSelect" v-model="orderId">
<option disabled value="" selected>Выберите номер кабинета</option> <option disabled value="" selected>Выберите номер кабинета</option>
<option v-for="cabinet in cabinets" :value="cabinet.id">{{ cabinet.number }}</option> <option v-for="order in orders" :value="order.id">{{ order.number }}</option>
</select> </select>
<button class="btn btn-primary" type="button" id="report-button" <button class="btn btn-primary" type="button" id="report-button"
@ -150,24 +150,24 @@ export default {
<input type="text" class="form-control" id="serialNum" required v-model="data.serialNum"> <input type="text" class="form-control" id="serialNum" required v-model="data.serialNum">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="monitor" class="form-label">Монитор</label> <label for="component" class="form-label">Монитор</label>
<select class="form-select" id="monitor" v-model="data.monitorId"> <select class="form-select" id="component" v-model="data.componentId">
<option disabled value="">Выберите монитор</option> <option disabled value="">Выберите монитор</option>
<option v-for="monitor in this.monitors" <option v-for="component in this.components"
:value="monitor.id" :value="component.id"
:selected="data.monitorId && monitor.id === data.monitorId"> :selected="data.componentId && component.id === data.componentId">
{{ monitor.modelName }} {{ component.modelName }}
</option> </option>
</select> </select>
</div> </div>
<!-- <div class="mb-3"> <!-- <div class="mb-3">
<label for="cabinet" class="form-label">Номер кабинета</label> <label for="order" class="form-label">Номер кабинета</label>
<select class="form-select" id="cabinet" v-model="data.cabinetId"> <select class="form-select" id="order" v-model="data.orderId">
<option disabled value="">Выберите номер кабинета</option> <option disabled value="">Выберите номер кабинета</option>
<option v-for="cabinet in this.cabinets" <option v-for="order in this.orders"
:value="cabinet.id" :value="order.id"
:selected="data.cabinetId && cabinet.id === data.cabinetId"> :selected="data.orderId && order.id === data.orderId">
{{ cabinet.number }} {{ order.number }}
</option> </option>
</select> </select>
</div> --> </div> -->

View File

@ -0,0 +1,67 @@
<script>
export default {
props: {
header: String,
confirm: String,
visible: Boolean
},
emits: {
done: null,
'update:visible': (value) => {
if (typeof value !== 'boolean') {
throw 'Value is not a boolean';
}
return true;
}
},
methods: {
hide() {
this.$emit('update:visible', false);
},
done() {
if (this.$refs.form.checkValidity()) {
this.$emit('done');
this.hide();
} else {
this.$refs.form.reportValidity();
}
}
}
}
</script>
<template>
<div class="modal fade" tabindex="-1" aria-hidden="true"
:class="{ 'modal-show': this.visible, 'show': this.visible }">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">{{ header }}</h1>
<button type="button" class="btn-close" aria-label="Close"
@click.prevent="hide"></button>
</div>
<div class="modal-body">
<form @submit.prevent="done" ref="form">
<slot></slot>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary"
@click.prevent="hide">Закрыть</button>
<button type="button" class="btn btn-primary"
@click.prevent="done">{{ confirm }}</button>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.modal-show {
display: block;
}
.modal-content {
width: max-content;
}
</style>

View File

@ -0,0 +1,135 @@
<script>
import axios from 'axios';
import CatalogMixins from '../mixins/CatalogMixins.js';
import Order from "../models/Order";
import Favor from '../models/Favor';
import Component from '../models/Component';
import DataService from '../services/DataService';
export default {
mixins: [
CatalogMixins
],
data() {
return {
getAllUrl: 'order/',
dataUrl: 'order',
transformer: (data) => new Order(data),
headers: [
{ name: 'number', label: 'Номер кабинета' }
],
headersComps: [
{ name: 'modelName', label: 'Модель' },
{ name: 'serialNum', label: 'Серийный номер' },
{ name: 'componentName', label: 'Модель монитора' }
],
selectedItemsComps: [],
dataFilterUrl: 'order/filter?',
favorUrl: 'favor/',
favors: [],
componentUrl: 'component/',
components: []
}
},
created() {
DataService.readAll(this.favorUrl, (data) => new Favor(data))
.then(data => {
this.favors = data;
});
DataService.readAll(this.componentUrl, (data) => new component(data))
.then(data => {
this.components = data;
});
},
methods: {
filter() {
let urlParams = ""
if (document.getElementById('numberFilterInput').value != "") {
urlParams += "number=" + this.number;
}
DataService.readAll(this.dataFilterUrl + urlParams, (data) => new Order(data))
.then(data => {
this.items = data;
});
},
clearFilters() {
this.loadItems();
this.id = null;
this.number = null;
},
addFavorInOrder(orderId) {
let favorId = document.getElementById('favors').value;
let response = axios.post(`http://localhost:8080/api/order/${orderId}/favor?favorId=${favorId}`);
console.log(response);
},
itemsComps(favorIds) {
let result = [];
if (typeof favorIds === 'undefined') {
return;
}
this.favors.forEach(favor => {
for (let i = 0; i < favorIds.length; i++) {
if (favor.id === favorIds[i]) {
for (let j = 0; j < this.components.length; j++) {
if (favor.componentId === this.components[j].id) {
favor._component = this.components[j];
}
}
result.push(favor);
}
}
});
return result;
}
}
}
</script>
<template>
<div class="input-group mb-3">
<input type="text" class="form-control" id="numberFilterInput" placeholder="Номер кабинета" required v-model="number">
<button class="btn btn-primary" type="button" id="report-button"
@click.prevent="filter">Сформировать</button>
<button class="btn btn-outline-secondary" type="button" id="report-button"
@click.prevent="clearFilters">Очистить</button>
</div>
<ToolBar
@add="showAddModal"
@edit="showEditModal"
@remove="removeSelectedItems">
</ToolBar>
<DataTable
:headers="this.headers"
:items="this.items"
:selectedItems="this.selectedItems"
@dblclick="showEditModalDblClick">
</DataTable>
<Modal
:header="this.modal.header"
:confirm="this.modal.confirm"
v-model:visible="this.modalShow"
@done="saveItem">
<div class="mb-3">
<label for="number" class="form-label">Номер кабинета</label>
<input type="text" class="form-control" id="number" required v-model="data.number">
</div>
<DataTable
:headers="this.headersComps"
:items="itemsComps(data.favorIds)"
:selectedItems="this.selectedItemsComps">
</DataTable>
<div class="mb-3">
<label for="favors" class="form-label">Добавить компьютер</label>
<select class="form-select" id="favors" required>
<option disabled value="">Выберите компьютер</option>
<option v-for="favor in this.favors"
:value="favor.id">
{{ favor.modelName }}
</option>
</select>
</div>
<button class="btn btn-outline-secondary" type="button" id="addFavorButton"
@click.prevent="addFavorInOrder(data.id)">Добавить</button>
</Modal>
</template>

View File

@ -1,9 +1,9 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue' import App from './App.vue'
import Cabinets from './components/Components.vue' import Components from './components/Components.vue'
import Computers from './components/Favors.vue.vue' import Favors from './components/Favors.vue'
import Monitors from './components/Orders.vue' import Orders from './components/Orders.vue'
const routes = [ const routes = [
{ path: '/', redirect: '/components' }, { path: '/', redirect: '/components' },

View File

@ -1,9 +1,9 @@
export default class Component { export default class Component {
constructor(data) { constructor(data) {
this._id = data?.id; this._id = data?.id;
this._amount = data?.amount;
this._componentName = data?.componentName; this._componentName = data?.componentName;
this._favorIds = data?._favorIds; this._amount = data?.amount;
this._favorIds = data?.favorIds;
} }
get id() { get id() {
@ -15,7 +15,7 @@ export default class Component {
} }
set amount(value) { set amount(value) {
if (typeof value !== 'string' || value === null || value.length == 0) { if (typeof value !== 'number' || value === null || value.length == 0) {
throw 'New amount value ' + value + ' is not a string or empty'; throw 'New amount value ' + value + ' is not a string or empty';
} }
this._amount = value; this._amount = value;

View File

@ -3,11 +3,17 @@ package ru.ulstu.is.sbapp;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistration; import org.springframework.web.servlet.config.annotation.CorsRegistration;
import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration @Configuration
public class WebConfiguration implements WebMvcConfigurer { public class WebConfiguration implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
WebMvcConfigurer.super.addViewControllers(registry);
registry.addViewController("rest-test");
}
@Override @Override
public void addCorsMappings(CorsRegistry registry){ public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/**").allowedMethods("*"); registry.addMapping("/**").allowedMethods("*");

View File

@ -5,6 +5,8 @@ import org.springframework.web.bind.annotation.*;
import ru.ulstu.is.sbapp.repair.dtos.ComponentDTO; import ru.ulstu.is.sbapp.repair.dtos.ComponentDTO;
import ru.ulstu.is.sbapp.repair.service.ComponentService; import ru.ulstu.is.sbapp.repair.service.ComponentService;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List; import java.util.List;
@RestController @RestController
@ -14,9 +16,10 @@ public class ComponentController {
public ComponentController(ComponentService productService){ public ComponentController(ComponentService productService){
this.componentService = productService; this.componentService = productService;
} }
@PostMapping @PostMapping("/")
public ComponentDTO addComponent(@RequestParam("name") String name, @RequestParam("amount") Integer amount) { public ComponentDTO addComponent(@RequestBody @Valid ComponentDTO componentDTO) throws IOException {
return new ComponentDTO(componentService.addComponent(name, amount));
return new ComponentDTO(componentService.addComponent(componentDTO.getComponentName(), componentDTO.getAmount()));
} }
@PutMapping("/{id}") @PutMapping("/{id}")

View File

@ -26,7 +26,7 @@ public class Component {
} }
public Component(String componentName, Integer amount) { public Component(Integer amount, String componentName) {
this.componentName = componentName; this.componentName = componentName;
this.amount = amount; this.amount = amount;
favorsListFromComponents = new ArrayList<>(); favorsListFromComponents = new ArrayList<>();

View File

@ -5,6 +5,7 @@ package ru.ulstu.is.sbapp.repair.service;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import ru.ulstu.is.sbapp.repair.exception.ComponentNotFoundException; import ru.ulstu.is.sbapp.repair.exception.ComponentNotFoundException;
import ru.ulstu.is.sbapp.repair.model.Component; import ru.ulstu.is.sbapp.repair.model.Component;
import ru.ulstu.is.sbapp.repair.repository.ComponentRepository; import ru.ulstu.is.sbapp.repair.repository.ComponentRepository;
@ -27,7 +28,11 @@ public class ComponentService {
@Transactional @Transactional
public Component addComponent(String name, int amount) { public Component addComponent(String name, int amount) {
final Component component = new Component(name, amount); if (!StringUtils.hasText(name)) {
throw new IllegalArgumentException("name is null or empty");
}
final Component component = new Component(amount, name);
validatorUtil.validate(component); validatorUtil.validate(component);
return componentRepository.save(component); return componentRepository.save(component);
} }