Merge pull request '3-parse-rule' (#10) from 3-parse-rule into master
All checks were successful
CI fuzzy controller / container-test-job (push) Successful in 2m31s

Reviewed-on: #10
This commit is contained in:
romanov73 2025-02-25 13:34:08 +04:00
commit 8f3e5196da
37 changed files with 332 additions and 294 deletions

View File

@ -0,0 +1,16 @@
package ru.ulstu.fc.config;
import com.fuzzylite.Engine;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FuzzyEngine {
@Bean
public Engine getFuzzyEngine() {
Engine engine = new Engine();
engine.setName("Fuzzy rules");
engine.setDescription("");
return engine;
}
}

View File

@ -0,0 +1,37 @@
package ru.ulstu.fc.project.controller;
import io.swagger.v3.oas.annotations.Hidden;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import ru.ulstu.fc.project.service.ProjectRulesService;
import ru.ulstu.fc.project.service.ProjectService;
import ru.ulstu.fc.project.service.ProjectVariableService;
import ru.ulstu.fc.user.model.UserRoleConstants;
@Controller
@Hidden
@RequestMapping("runProject")
@Secured({UserRoleConstants.ADMIN})
public class ProjectRunController {
private final ProjectService projectService;
private final ProjectRulesService projectRulesService;
private final ProjectVariableService projectVariableService;
public ProjectRunController(ProjectService projectService,
ProjectRulesService projectRulesService,
ProjectVariableService projectVariableService) {
this.projectService = projectService;
this.projectRulesService = projectRulesService;
this.projectVariableService = projectVariableService;
}
@GetMapping("init/{projectId}")
public String getProjects(@PathVariable(value = "projectId") Integer projectId, Model model) {
model.addAttribute("project", projectService.getById(projectId));
return "project/init";
}
}

View File

@ -1,7 +1,5 @@
package ru.ulstu.fc.project.model;
import java.util.Date;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.ManyToOne;
@ -9,9 +7,11 @@ import jakarta.validation.constraints.NotEmpty;
import ru.ulstu.fc.core.model.BaseEntity;
import ru.ulstu.fc.user.model.User;
import java.util.Date;
@Entity
public class Project extends BaseEntity {
@NotEmpty(message = "Текст новости не может быть пустым")
@NotEmpty(message = "Название проекта не может быть пустым")
private String name;
private Date createDate = new Date();
@ManyToOne(cascade = CascadeType.MERGE)

View File

@ -1,9 +1,12 @@
package ru.ulstu.fc.project.model;
import jakarta.validation.constraints.NotEmpty;
import java.util.Date;
public class ProjectForm {
private Integer id;
@NotEmpty(message = "Название проекта не может быть пустым")
private String name;
private Date createDate;

View File

@ -1,13 +1,12 @@
package ru.ulstu.fc.project.service;
import org.springframework.stereotype.Service;
import ru.ulstu.fc.rule.model.FuzzyRule;
import ru.ulstu.fc.rule.repository.FuzzyRuleRepository;
import java.util.Collections;
import java.util.List;
import org.springframework.stereotype.Service;
import ru.ulstu.fc.rule.repository.FuzzyRuleRepository;
import ru.ulstu.fc.rule.model.FuzzyRule;
@Service
public class ProjectRulesService {
private final FuzzyRuleRepository ruleRepository;

View File

@ -1,13 +1,12 @@
package ru.ulstu.fc.project.service;
import org.springframework.stereotype.Service;
import ru.ulstu.fc.rule.model.Variable;
import ru.ulstu.fc.rule.repository.VariableRepository;
import java.util.Collections;
import java.util.List;
import org.springframework.stereotype.Service;
import ru.ulstu.fc.rule.repository.VariableRepository;
import ru.ulstu.fc.rule.model.Variable;
@Service
public class ProjectVariableService {
private final VariableRepository variableRepository;

View File

@ -1,5 +1,6 @@
package ru.ulstu.fc.rule.controller;
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
@ -7,17 +8,15 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import jakarta.validation.Valid;
import ru.ulstu.fc.rule.model.FuzzyRuleForm;
import ru.ulstu.fc.rule.service.FuzzyRuleService;
@Controller
@RequestMapping("rule")
public class RuleController {
public class FuzzyRuleController {
private final FuzzyRuleService ruleService;
public RuleController(FuzzyRuleService ruleService) {
public FuzzyRuleController(FuzzyRuleService ruleService) {
this.ruleService = ruleService;
}

View File

@ -1,5 +1,6 @@
package ru.ulstu.fc.rule.controller;
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
@ -7,8 +8,6 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import jakarta.validation.Valid;
import ru.ulstu.fc.rule.model.FuzzyTermForm;
import ru.ulstu.fc.rule.service.FuzzyTermService;

View File

@ -7,8 +7,8 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import ru.ulstu.fc.rule.model.InferenceForm;
import ru.ulstu.fc.rule.model.FuzzyTerm;
import ru.ulstu.fc.rule.model.InferenceForm;
import ru.ulstu.fc.rule.model.Variable;
import ru.ulstu.fc.rule.service.FuzzyInferenceService;

View File

@ -1,5 +1,6 @@
package ru.ulstu.fc.rule.controller;
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
@ -7,8 +8,6 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import jakarta.validation.Valid;
import ru.ulstu.fc.rule.model.VariableForm;
import ru.ulstu.fc.rule.service.FuzzyTermService;
import ru.ulstu.fc.rule.service.VariableService;

View File

@ -1,8 +1,5 @@
package ru.ulstu.fc.rule.model;
import java.util.ArrayList;
import java.util.List;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
@ -14,6 +11,9 @@ import jakarta.validation.constraints.Size;
import ru.ulstu.fc.core.model.BaseEntity;
import ru.ulstu.fc.project.model.Project;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Variable extends BaseEntity {
@Size(min = 3, max = 250, message = "Длина должна быть от 3 до 250")

View File

@ -1,12 +1,11 @@
package ru.ulstu.fc.rule.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.ulstu.fc.project.model.Project;
import ru.ulstu.fc.rule.model.FuzzyRule;
import java.util.List;
public interface FuzzyRuleRepository extends JpaRepository<FuzzyRule, Integer> {
List<FuzzyRule> findByProject(Project project);

View File

@ -1,7 +1,6 @@
package ru.ulstu.fc.rule.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.ulstu.fc.rule.model.FuzzyTerm;
public interface FuzzyTermRepository extends JpaRepository<FuzzyTerm, Integer> {

View File

@ -1,12 +1,11 @@
package ru.ulstu.fc.rule.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.ulstu.fc.project.model.Project;
import ru.ulstu.fc.rule.model.Variable;
import java.util.List;
public interface VariableRepository extends JpaRepository<Variable, Integer> {
List<Variable> findByProject(Project project);

View File

@ -14,8 +14,8 @@ import com.fuzzylite.variable.OutputVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import ru.ulstu.fc.rule.model.OutputValue;
import ru.ulstu.fc.rule.model.FuzzyTerm;
import ru.ulstu.fc.rule.model.OutputValue;
import ru.ulstu.fc.rule.model.Variable;
import java.util.List;
@ -30,14 +30,17 @@ public class FuzzyInferenceService {
+ OUTPUT_VARIABLE_NAME
+ " is %s";
private final static String NO_RESULT = "Нет результата";
private final Engine fuzzyEngine;
public FuzzyInferenceService(Engine fuzzyEngine) {
this.fuzzyEngine = fuzzyEngine;
}
private List<String> getDemoRules() {
return List.of(
String.format(RULE_TEMPLATE, "возраст", "молодой", "доход", "высокий", "большой"),
String.format(RULE_TEMPLATE, "возраст", "средний", "доход", "высокий", "средний"),
String.format(RULE_TEMPLATE, "возраст", "старый", "доход", "высокий", "средний"),
String.format(RULE_TEMPLATE, "возраст", "старый", "доход", "небольшой", "небольшой"),
String.format(RULE_TEMPLATE, "возраст", "молодой", "доход", "небольшой", "небольшой")
String.format(RULE_TEMPLATE, "возраст", "молодой", "доход", "высокий", "средний"),
String.format(RULE_TEMPLATE, "возраст", "средний", "доход", "высокий", "большой"),
String.format(RULE_TEMPLATE, "возраст", "старый", "доход", "средний", "средний")
);
}
@ -102,13 +105,6 @@ public class FuzzyInferenceService {
return mamdani;
}
private Engine getFuzzyEngine() {
Engine engine = new Engine();
engine.setName("Fuzzy rules");
engine.setDescription("");
return engine;
}
private List<OutputValue> getConsequent(Engine engine, Map<String, Double> variableValues) {
OutputVariable outputVariable = engine.getOutputVariable(OUTPUT_VARIABLE_NAME);
for (Map.Entry<String, Double> variableValue : variableValues.entrySet()) {
@ -152,8 +148,8 @@ public class FuzzyInferenceService {
Map<String, Double> values,
List<Variable> inputVariables,
Variable outputVariable) {
Engine engine = getFuzzyEngine();
engine.addRuleBlock(getRuleBlock(engine, rules, inputVariables, outputVariable));
return getConsequent(engine, values);
fuzzyEngine.getRuleBlocks().clear();
fuzzyEngine.addRuleBlock(getRuleBlock(fuzzyEngine, rules, inputVariables, outputVariable));
return getConsequent(fuzzyEngine, values);
}
}

View File

@ -0,0 +1,44 @@
package ru.ulstu.fc.rule.service;
import com.fuzzylite.Engine;
import com.fuzzylite.rule.Rule;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class FuzzyRuleParseService {
private final static String RU_IF_STATEMENT = "если";
private final static String ENG_IF_STATEMENT = "if";
private final static String RU_THEN_STATEMENT = "то";
private final static String ENG_THEN_STATEMENT = "then";
private final static String RU_AND_STATEMENT = "и";
private final static String ENG_AND_STATEMENT = "and";
private final static String RU_IS_STATEMENT = "является";
private final static String RU_EQ_STATEMENT = "равно";
private final static String ENG_IS_STATEMENT = "is";
private final Engine fuzzyEngine;
public FuzzyRuleParseService(Engine fuzzyEngine) {
this.fuzzyEngine = fuzzyEngine;
}
public List<Rule> parseRules(List<String> stringRules) {
return stringRules
.stream()
.map(s -> Rule.parse(replaceEnglishKeywords(s), fuzzyEngine))
.collect(Collectors.toList());
}
private String replaceEnglishKeywords(String stringRule) {
stringRule = stringRule.toLowerCase();
return stringRule
.replaceFirst(RU_IF_STATEMENT, ENG_IF_STATEMENT)
.replaceFirst(RU_AND_STATEMENT, ENG_AND_STATEMENT)
.replaceFirst(RU_IS_STATEMENT, ENG_IS_STATEMENT)
.replaceFirst(RU_EQ_STATEMENT, ENG_IS_STATEMENT)
.replaceAll(RU_THEN_STATEMENT, ENG_THEN_STATEMENT);
}
}

View File

@ -1,11 +1,10 @@
package ru.ulstu.fc.rule.service;
import org.springframework.stereotype.Service;
import ru.ulstu.fc.rule.repository.FuzzyRuleRepository;
import ru.ulstu.fc.project.service.ProjectService;
import ru.ulstu.fc.rule.model.FuzzyRule;
import ru.ulstu.fc.rule.model.FuzzyRuleForm;
import ru.ulstu.fc.rule.repository.FuzzyRuleRepository;
@Service
public class FuzzyRuleService {

View File

@ -1,14 +1,13 @@
package ru.ulstu.fc.rule.service;
import java.util.Collections;
import java.util.List;
import org.springframework.stereotype.Service;
import ru.ulstu.fc.rule.model.FuzzyTerm;
import ru.ulstu.fc.rule.model.FuzzyTermForm;
import ru.ulstu.fc.rule.repository.FuzzyTermRepository;
import java.util.Collections;
import java.util.List;
@Service
public class FuzzyTermService {
private final FuzzyTermRepository fuzzyTermRepository;

View File

@ -1,7 +1,6 @@
package ru.ulstu.fc.rule.service;
import org.springframework.stereotype.Service;
import ru.ulstu.fc.project.service.ProjectService;
import ru.ulstu.fc.rule.model.FuzzyTerm;
import ru.ulstu.fc.rule.model.Variable;

View File

@ -1,5 +1,7 @@
package ru.ulstu.fc.user.controller;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
@ -7,9 +9,6 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.context.request.WebRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import ru.ulstu.fc.user.model.User;
import ru.ulstu.fc.user.model.UserDto;
import ru.ulstu.fc.user.service.UserService;

View File

@ -1,8 +1,5 @@
package ru.ulstu.fc.user.model;
import java.util.HashSet;
import java.util.Set;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
@ -15,6 +12,9 @@ import jakarta.validation.constraints.Size;
import ru.ulstu.fc.config.Constants;
import ru.ulstu.fc.core.model.BaseEntity;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "is_users")
public class User extends BaseEntity {

View File

@ -1,19 +1,18 @@
package ru.ulstu.fc.user.service;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import ru.ulstu.fc.config.Constants;
import java.io.IOException;
@Component
public class UserSessionLoginHandler extends SavedRequestAwareAuthenticationSuccessHandler {
private final Logger log = LoggerFactory.getLogger(UserSessionLoginHandler.class);

View File

@ -1,19 +1,18 @@
package ru.ulstu.fc.user.service;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.stereotype.Component;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.stereotype.Component;
import ru.ulstu.fc.config.Constants;
import java.io.IOException;
@Component
public class UserSessionLogoutHandler extends SimpleUrlLogoutSuccessHandler {
private final Logger log = LoggerFactory.getLogger(UserSessionLogoutHandler.class);

View File

@ -13,6 +13,7 @@ extractor.custom-projects-dir=
server.error.include-stacktrace=always
server.error.include-exception=true
server.error.include-message=always
# go to http://localhost:8080/h2-console
spring.datasource.url=jdbc:h2:file:./data/db
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa

View File

@ -1,83 +0,0 @@
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"
layout:decorate="~{default}">
<head>
<title>Простая обработка формы на Spring MVC</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<div class="container" layout:fragment="content">
<form action="/listRule" th:action="${@route.ADD_RULE}" th:object="${addRuleForm}" method="post">
<input type="hidden" th:field="*{ruleId}">
<div class="row">
<div class="col-md-2 col-sm-12">
Если
</div>
<div class="col-md-6 col-sm-12">
<select id="select-antecedent" class="selectpicker m-2" data-live-search="true"
th:field="*{firstAntecedentId}"
data-width="90%">
<option th:each="antecedent : ${antecedents}"
th:value="${antecedent}"
th:utext="${antecedent.description}">
</option>
</select>
</div>
<div class="col-md-2 col-sm-12">
имеет тенденцию
</div>
<div class="col-md-2 col-sm-12">
<select id="select-measures" class="selectpicker m-2" data-live-search="true"
th:field="*{firstAntecedentValueId}"
data-width="100%">
<option th:each="antecedentValue : ${antecedentValues}"
th:value="${antecedentValue.id}"
th:utext="${antecedentValue.antecedentValue}">
</option>
</select>
</div>
<div class="col-md-2 col-sm-12">
и
</div>
<div class="col-md-6 col-sm-12">
<select id="select-second-antecedent" class="selectpicker m-2" data-live-search="true"
th:field="*{secondAntecedentId}"
data-width="90%">
<option th:each="antecedent : ${antecedents}"
th:value="${antecedent}"
th:utext="${antecedent.description}">
</option>
</select>
</div>
<div class="col-md-2 col-sm-12">
имеет тенденцию
</div>
<div class="col-md-2 col-sm-12">
<select id="select-second-measures" class="selectpicker m-2" data-live-search="true"
th:field="*{secondAntecedentValueId}"
data-width="100%">
<option th:each="antecedentValue : ${antecedentValues}"
th:value="${antecedentValue.id}"
th:utext="${antecedentValue.antecedentValue}">
</option>
</select>
</div>
<div class="col-md-2 col-sm-12">
то:
</div>
<div class="col-md-6 col-sm-12">
<input type="text" class="form-control m-1" th:field="*{consequent}">
</div>
</div>
<div class="row">
<div class="col-md-4 col-sm-12"></div>
<div class="col-md-4 col-sm-12">
<input type="submit" class="btn btn-outline-success m-2" th:if="*{ruleId == null}"
value="Создать правило"/>
<input type="submit" class="btn btn-outline-success m-2" th:if="*{ruleId != null}"
value="Сохранить правило"/>
</div>
</div>
</form>
</div>
</html>

View File

@ -48,39 +48,52 @@
<div class="row" th:each="r, iter : ${rules}">
<div class="col col-md-12">
<a th:href="@{'/rule/edit/' + ${projectId}+'/'+${r.id}}">
<span class="badge badge-light" th:text="${iter.index+1} + '. ' + ${r.content}"></span>
<div class="rule row" th:text="${r.content}"></div>
</a>
</div>
</div>
<!-- <div class="col col-md-2 offset-md-3">
<span class="badge badge-primary">Переменная</span>
</div>
<div class="col col-md-2">
<span class="badge badge-light">есть</span>
</div>
<div class="col col-md-2">
<span class="badge badge-success">значение</span>
</div>
<div class="col col-md-1">
<span class="badge badge-danger">И / ИЛИ</span>
</div>
<div class="col col-md-2 offset-md-3">
<span class="badge badge-primary">Переменная</span>
</div>
<div class="col col-md-2">
<span class="badge badge-light">есть</span>
</div>
<div class="col col-md-2">
<span class="badge badge-success">значение</span>
</div>
<div class="col col-md-1">
<span class="badge badge-danger">И / ИЛИ</span>
</div>-->
</div>
<a th:href="@{'/rule/edit/' + ${projectId}+'/0'}" th:if="${not #lists.isEmpty(variables)}"
class="btn btn-outline-dark">Добавить правило</a>
</div>
</div>
<script>
function getAntecedent(rule) {
withoutIf = rule.split('if');
return withoutIf[1].trim().split('then')[0].trim();
}
function getAntecedentComponents(antecedent) {
return antecedent.split('and').map((i) => i.trim());
}
function getVariable(antecedent) {
return antecedent.split('is')[0].trim();
}
function getVariableValue(antecedent) {
return antecedent.split('is')[1].trim();
}
function addRule(index, el, rule) {
ruleHtml = "<div class='col col-md-12'><span class='badge badge-light'>"+(index+1) +". Если</span></div>"
antecedentComponents = getAntecedentComponents(getAntecedent(rule));
for (let i = 0; i < antecedentComponents.length; i++) {
a = antecedentComponents[i];
if (i > 0) {
ruleHtml += "<div class='col col-md-12'><span class='badge badge-danger'>И</span></div>";
}
ruleHtml += "<div class='col col-md-2 offset-md-1'><span class='badge badge-primary'>"+getVariable(a)+"</span></div>";
ruleHtml += "<div class='col col-md-2'><span class='badge badge-light'>есть</span></div>";
ruleHtml += "<div class='col col-md-2'><span class='badge badge-success'>"+getVariableValue(a)+"</span></div>";
}
$(el).html(ruleHtml);
}
$('.rule').each(function(index ) {
addRule(index, $(this), $(this).text());
});
</script>
</div>
</html>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:th="http://www.w3.org/1999/xhtml"
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>

View File

@ -12,9 +12,9 @@
<input type="hidden" th:field="*{projectId}">
<input type="hidden" th:field="*{id}">
<div class="form-group">
<label for="content">Правило</label>
<label for="ruleContent">Правило</label>
<input th:field="*{content}"
id="content"
id="ruleContent"
type="text"
required
class="form-control"
@ -24,6 +24,30 @@
Не может быть пустым
</p>
</div>
<div class="form-group">
<label class="col col-md-1">Если</label>
<select id="select-variable" class="selectpicker m-2" data-live-search="true" data-width="70%">
<option>Скорость</option>
</select>
</div>
<div class="form-group">
<label class="col col-md-1">есть</label>
<select id="select-val" class="selectpicker m-2" data-live-search="true" data-width="70%">
<option>Высокая</option>
</select>
</div>
<div class="form-group">
<label class="col col-md-1">то</label>
<select id="select-val1" class="selectpicker m-2" data-live-search="true" data-width="70%">
<option>Действие</option>
</select>
</div>
<div class="form-group">
<label class="col col-md-1">есть</label>
<select id="select-val4" class="selectpicker m-2" data-live-search="true" data-width="70%">
<option>Сливать воду</option>
</select>
</div>
<button name="save" type="submit" class="btn btn-outline-dark">Сохранить</button>
<button name="delete"

View File

@ -45,7 +45,8 @@
</div>
</div>
</div>
<a th:href="@{'/fuzzyTerm/edit/' + ${projectId} + '/' + ${variableId}+'/0'}" class="btn btn-outline-dark">Добавить терм</a>
<a th:href="@{'/fuzzyTerm/edit/' + ${projectId} + '/' + ${variableId}+'/0'}"
class="btn btn-outline-dark">Добавить терм</a>
</div>
</div>
</form>