нет слоф

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 {
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 'com.h2database:h2:2.1.210'
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'
implementation 'org.springframework.boot:spring-boot-starter-validation'
}

Binary file not shown.

View File

@ -12,9 +12,10 @@ export default {
return {
getAllUrl: 'component/',
dataUrl: 'component',
transformer: (data) => new Comment(data),
transformer: (data) => new Component(data),
headers: [
{ name: 'componentName', label: 'Название компонента' }
{ name: 'componentName', label: 'Название компонента' },
{ name: 'amount', label: 'Кол-во' }
],
dataFilterUrl: 'component/filter?'
}
@ -23,8 +24,17 @@ export default {
filter() {
let urlParams = ""
if (document.getElementById('componentNameFilterInput').value != "") {
if (urlParams != "") {
urlParams += "&";
}
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))
.then(data => {
this.items = data;
@ -34,6 +44,7 @@ export default {
this.loadItems();
this.id = null;
this.componentName = null;
this.amount = null;
}
}
}
@ -42,6 +53,8 @@ export default {
<template>
<div class="input-group mb-3">
<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"
@click.prevent="filter">Сформировать</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>
<input type="text" class="form-control" id="name" required v-model="data.componentName">
</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>
</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' },
],
dataFilterUrl: 'favor/filter?',
cabinetUrl: 'cabinet/',
cabinets: [],
monitorUrl: 'monitor/',
monitors: []
orderUrl: 'order/',
orders: [],
componentUrl: 'component/',
components: []
}
},
created() {
DataService.readAll(this.cabinetUrl, (data) => new Cabinet(data))
DataService.readAll(this.orderUrl, (data) => new Order(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 => {
this.monitors = data;
this.components = data;
});
},
methods: {
@ -51,17 +51,17 @@ export default {
}
urlParams += "serialNum=" + this.serialNumber;
}
if (document.getElementById('monitorFilterSelect').value != "") {
if (document.getElementById('componentFilterSelect').value != "") {
if (urlParams != "") {
urlParams += "&";
}
urlParams += "monitorId=" + this.monitorId;
urlParams += "componentId=" + this.componentId;
}
if (document.getElementById('cabinetFilterSelect').value != "") {
if (document.getElementById('orderFilterSelect').value != "") {
if (urlParams != "") {
urlParams += "&";
}
urlParams += "cabinetId=" + this.cabinetId;
urlParams += "orderId=" + this.orderId;
}
DataService.readAll(this.dataFilterUrl + urlParams, (data) => new Favor(data))
.then(data => {
@ -73,27 +73,27 @@ export default {
this.id = null;
this.modelName = null;
this.serialNumber = null;
this.monitorId = null;
this.cabinetId = null;
this.componentId = null;
this.orderId = null;
},
addCabinetToFavor(favorId) {
let cabinetId = document.getElementById('cabinets').value;
let response = axios.post(`http://localhost:8080/api/favor/${favorId}/cabinet?cabinetId=${cabinetId}`);
addOrderToFavor(favorId) {
let orderId = document.getElementById('orders').value;
let response = axios.post(`http://localhost:8080/api/favor/${favorId}/order?orderId=${orderId}`);
},
itemsComps(favors) {
if (typeof favors === 'undefined') {
return;
}
favors.forEach(favor => {
this.monitors.forEach(monitor => {
if (favor.monitorId === monitor.id) {
favor.monitor = monitor;
this.components.forEach(component => {
if (favor.componentId === component.id) {
favor.component = component;
}
});
this.cabinets.forEach(cabinet => {
cabinet.favorIds.forEach(favorId => {
this.orders.forEach(order => {
order.favorIds.forEach(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">
<select class="form-select" id="monitorFilterSelect" v-model="monitorId">
<select class="form-select" id="componentFilterSelect" v-model="componentId">
<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 class="form-select" id="cabinetFilterSelect" v-model="cabinetId">
<select class="form-select" id="orderFilterSelect" v-model="orderId">
<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>
<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">
</div>
<div class="mb-3">
<label for="monitor" class="form-label">Монитор</label>
<select class="form-select" id="monitor" v-model="data.monitorId">
<label for="component" class="form-label">Монитор</label>
<select class="form-select" id="component" v-model="data.componentId">
<option disabled value="">Выберите монитор</option>
<option v-for="monitor in this.monitors"
:value="monitor.id"
:selected="data.monitorId && monitor.id === data.monitorId">
{{ monitor.modelName }}
<option v-for="component in this.components"
:value="component.id"
:selected="data.componentId && component.id === data.componentId">
{{ component.modelName }}
</option>
</select>
</div>
<!-- <div class="mb-3">
<label for="cabinet" class="form-label">Номер кабинета</label>
<select class="form-select" id="cabinet" v-model="data.cabinetId">
<label for="order" class="form-label">Номер кабинета</label>
<select class="form-select" id="order" v-model="data.orderId">
<option disabled value="">Выберите номер кабинета</option>
<option v-for="cabinet in this.cabinets"
:value="cabinet.id"
:selected="data.cabinetId && cabinet.id === data.cabinetId">
{{ cabinet.number }}
<option v-for="order in this.orders"
:value="order.id"
:selected="data.orderId && order.id === data.orderId">
{{ order.number }}
</option>
</select>
</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 { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue'
import Cabinets from './components/Components.vue'
import Computers from './components/Favors.vue.vue'
import Monitors from './components/Orders.vue'
import Components from './components/Components.vue'
import Favors from './components/Favors.vue'
import Orders from './components/Orders.vue'
const routes = [
{ path: '/', redirect: '/components' },

View File

@ -1,9 +1,9 @@
export default class Component {
constructor(data) {
this._id = data?.id;
this._amount = data?.amount;
this._componentName = data?.componentName;
this._favorIds = data?._favorIds;
this._amount = data?.amount;
this._favorIds = data?.favorIds;
}
get id() {
@ -15,7 +15,7 @@ export default class Component {
}
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';
}
this._amount = value;

View File

@ -3,11 +3,17 @@ package ru.ulstu.is.sbapp;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
WebMvcConfigurer.super.addViewControllers(registry);
registry.addViewController("rest-test");
}
@Override
public void addCorsMappings(CorsRegistry registry){
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.service.ComponentService;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
@RestController
@ -14,9 +16,10 @@ public class ComponentController {
public ComponentController(ComponentService productService){
this.componentService = productService;
}
@PostMapping
public ComponentDTO addComponent(@RequestParam("name") String name, @RequestParam("amount") Integer amount) {
return new ComponentDTO(componentService.addComponent(name, amount));
@PostMapping("/")
public ComponentDTO addComponent(@RequestBody @Valid ComponentDTO componentDTO) throws IOException {
return new ComponentDTO(componentService.addComponent(componentDTO.getComponentName(), componentDTO.getAmount()));
}
@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.amount = amount;
favorsListFromComponents = new ArrayList<>();

View File

@ -5,6 +5,7 @@ package ru.ulstu.is.sbapp.repair.service;
import org.springframework.stereotype.Service;
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.model.Component;
import ru.ulstu.is.sbapp.repair.repository.ComponentRepository;
@ -27,7 +28,11 @@ public class ComponentService {
@Transactional
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);
return componentRepository.save(component);
}