java不同版本特性示例
This commit is contained in:
13
demo3/src/main/java/top/yexuejc/demo/Demo2Application.java
Normal file
13
demo3/src/main/java/top/yexuejc/demo/Demo2Application.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package top.yexuejc.demo;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Demo2Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Demo2Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package top.yexuejc.demo.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* @author maxiaofeng
|
||||
* @date 2025/7/3 18:27
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public Object handleValidationExceptions(MethodArgumentNotValidException ex, HttpServletRequest request) {
|
||||
|
||||
log.error("参数校验异常: ", ex);
|
||||
|
||||
BindingResult result = ex.getBindingResult();
|
||||
List<String> errorMessages = result.getAllErrors().stream().map(ObjectError::getDefaultMessage).toList();
|
||||
|
||||
if (request.getContentType() != null && request.getContentType().contains("application/json")) {
|
||||
// 返回 JSON 错误信息
|
||||
return new ResponseEntity<>(errorMessages, HttpStatus.BAD_REQUEST);
|
||||
} else {
|
||||
// 返回错误页面或重定向到原页面
|
||||
ModelAndView modelAndView = new ModelAndView("login"); // 示例页面名
|
||||
modelAndView.addObject("error", errorMessages);
|
||||
return modelAndView;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package top.yexuejc.demo.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/", "/home", "/login/**", "/register", "/css/**", "/js/**", "/images/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.formLogin(form -> form
|
||||
.loginPage("/login")
|
||||
.defaultSuccessUrl("/home")
|
||||
.failureUrl("/login?error=true")
|
||||
.permitAll()
|
||||
)
|
||||
.logout(logout -> logout
|
||||
.logoutSuccessUrl("/login?logout=true")
|
||||
.permitAll()
|
||||
);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package top.yexuejc.demo.config;
|
||||
|
||||
import nz.net.ultraq.thymeleaf.layoutdialect.LayoutDialect;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.thymeleaf.spring6.SpringTemplateEngine;
|
||||
import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
|
||||
|
||||
/**
|
||||
* @author maxiaofeng
|
||||
* @date 2025/7/4 18:28
|
||||
*/
|
||||
@Configuration
|
||||
public class ThymeleafConfig {
|
||||
@Bean
|
||||
public SpringTemplateEngine templateEngine(SpringResourceTemplateResolver templateResolver) {
|
||||
SpringTemplateEngine engine = new SpringTemplateEngine();
|
||||
engine.addDialect(new LayoutDialect());
|
||||
engine.setTemplateResolver(templateResolver);
|
||||
return engine;
|
||||
}
|
||||
}
|
||||
22
demo3/src/main/java/top/yexuejc/demo/model/LoginVO.java
Normal file
22
demo3/src/main/java/top/yexuejc/demo/model/LoginVO.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package top.yexuejc.demo.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* @author maxiaofeng
|
||||
* @date 2025/7/3 16:39
|
||||
*/
|
||||
public record LoginVO(@NotBlank(message = "账号不能为空") String username,
|
||||
@NotBlank(message = "密码不能为空") String password) implements Serializable {
|
||||
|
||||
}
|
||||
|
||||
//@Data
|
||||
//public class LoginVO implements Serializable {
|
||||
// @NotBlank
|
||||
// private String username;
|
||||
// private String password;
|
||||
//}
|
||||
|
||||
18
demo3/src/main/java/top/yexuejc/demo/web/IndexCtrl.java
Normal file
18
demo3/src/main/java/top/yexuejc/demo/web/IndexCtrl.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package top.yexuejc.demo.web;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* @author maxiaofeng
|
||||
* @date 2025/7/3 18:43
|
||||
*/
|
||||
@Controller
|
||||
public class IndexCtrl {
|
||||
|
||||
@RequestMapping("/")
|
||||
public ModelAndView index() {
|
||||
return new ModelAndView("index");
|
||||
}
|
||||
}
|
||||
56
demo3/src/main/java/top/yexuejc/demo/web/LoginCtrl.java
Normal file
56
demo3/src/main/java/top/yexuejc/demo/web/LoginCtrl.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package top.yexuejc.demo.web;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import jakarta.validation.Valid;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import top.yexuejc.demo.model.LoginVO;
|
||||
|
||||
/**
|
||||
* @author maxiaofeng
|
||||
* @date 2025/7/3 16:37
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/login")
|
||||
public class LoginCtrl {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LoginCtrl.class);
|
||||
|
||||
@GetMapping("")
|
||||
public ModelAndView page(ModelAndView mv, @RequestParam Map<String, Object> params) {
|
||||
mv.addAllObjects(params);
|
||||
mv.setViewName("login");
|
||||
return mv;
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
public String loginGet(@Valid LoginVO loginVO) throws JsonProcessingException {
|
||||
log.info(new JsonMapper().writeValueAsString(loginVO));
|
||||
return "redirect:/";
|
||||
}
|
||||
|
||||
@PostMapping(value = "/postJson", consumes = MediaType.APPLICATION_JSON_VALUE)
|
||||
public String loginPostJson(@RequestBody @Validated LoginVO loginVO) throws JsonProcessingException {
|
||||
log.info(new JsonMapper().writeValueAsString(loginVO));
|
||||
return "redirect:/";
|
||||
}
|
||||
|
||||
@PostMapping(value = "/postFrom", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public String loginPostFrom(@Validated LoginVO loginVO) throws JsonProcessingException {
|
||||
log.info(new JsonMapper().writeValueAsString(loginVO));
|
||||
return "redirect:/";
|
||||
}
|
||||
}
|
||||
9
demo3/src/main/resources/application.properties
Normal file
9
demo3/src/main/resources/application.properties
Normal file
@@ -0,0 +1,9 @@
|
||||
spring.application.name=demo2
|
||||
|
||||
|
||||
spring.thymeleaf.prefix=classpath:/templates/
|
||||
spring.thymeleaf.suffix=.html
|
||||
spring.thymeleaf.mode=HTML
|
||||
spring.thymeleaf.cache=false
|
||||
spring.thymeleaf.encoding=UTF-8
|
||||
spring.thymeleaf.servlet.content-type=text/html
|
||||
127
demo3/src/main/resources/static/css/common/main.css
Normal file
127
demo3/src/main/resources/static/css/common/main.css
Normal file
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
*
|
||||
* @author maxiaofeng
|
||||
* @date 2025/7/4 17:33
|
||||
*/
|
||||
/* 移动设备样式 */
|
||||
@media screen and (max-width: 760px) {
|
||||
.ui-menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ui-main-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ui-header {
|
||||
height: 50px;
|
||||
background-color: #14ccff50;
|
||||
}
|
||||
|
||||
.ui-content {
|
||||
flex: 1;
|
||||
background: #d2fe8650;
|
||||
}
|
||||
}
|
||||
|
||||
/* 桌面设备样式 */
|
||||
@media screen and (min-width: 760px) {
|
||||
.ui-menu {
|
||||
display: block;
|
||||
width: 200px;
|
||||
height: 100vh;
|
||||
background-color: #834f4f50;
|
||||
position: fixed; /* 固定位置 */
|
||||
left: 0;
|
||||
top: 0;
|
||||
transition: width 0.3s ease-in-out;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ui-main-container {
|
||||
margin-left: 200px; /* 给侧边栏留出空间 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh; /* 占满整个视口高度 */
|
||||
transition: width 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.ui-content {
|
||||
flex: 1;
|
||||
background: #d2fe8650;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.ui-header {
|
||||
height: 50px;
|
||||
background-color: #14ccff50;
|
||||
}
|
||||
|
||||
.h-container {
|
||||
display: flex;
|
||||
flex-direction: row; /* 横向排列 */
|
||||
align-items: center; /* 垂直居中对齐 */
|
||||
justify-content: space-between; /* 左右撑开 */
|
||||
width: 100%; /* 确保占满宽度 */
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.h-icon-menu {
|
||||
height: 20px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
.h-icon-menu-hide {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.h-icon-menu-show {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.h-sys-name {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.h-right-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.h-notice {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.h-user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 10px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.h-user-avatar {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.h-user-avatar img {
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.h-user-name {
|
||||
margin: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
38
demo3/src/main/resources/static/css/login.css
Normal file
38
demo3/src/main/resources/static/css/login.css
Normal file
@@ -0,0 +1,38 @@
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 10px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
color: #495057;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #0d6efd;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #0b5ed7;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
padding: 12px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: #86b7fe;
|
||||
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
|
||||
.alert {
|
||||
border-radius: 5px;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
27
demo3/src/main/resources/templates/common/header.html
Normal file
27
demo3/src/main/resources/templates/common/header.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<body>
|
||||
<div class="h-container">
|
||||
<svg class="h-icon-menu h-icon-menu-show" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.776 899.072h1000.96v59.904H11.776zM12.8 65.024H1013.76v59.904H12.8zM10.24 473.088h636.416v59.904H10.24zM765.44 728.064l244.224-229.888-244.224-229.888z"></path>
|
||||
</svg>
|
||||
<svg class="h-icon-menu h-icon-menu-hide" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.264 64.512h1001.472v59.904H11.264zM10.24 899.072h1001.472v59.904H10.24zM376.32 490.496h636.928v59.904H376.32zM258.56 295.424l-244.736 230.4 244.736 229.888z"></path>
|
||||
</svg>
|
||||
<div class="h-sys-name">超级管理系统</div>
|
||||
<div class="h-right-container">
|
||||
<div class="h-notice">
|
||||
<svg class="h-icon-menu" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M512 42.666667c63.637333 0 116.288 50.432 120.682667 114.005333 103.125333 46.058667 175.552 149.12 175.552 269.696v203.306667C867.477333 677.952 896 741.525333 896 820.458667c0 17.557333-15.36 32.896-32.917333 32.896H640l-0.085333 4.8A128 128 0 0 1 384 853.333333L160.917333 853.333333C143.36 853.333333 128 837.973333 128 820.437333c0-76.736 28.522667-142.506667 87.765333-190.741333v-203.306667c0-120.618667 72.426667-223.658667 175.552-269.717333C395.712 93.098667 448.362667 42.666667 512 42.666667z m64 810.688L448 853.333333l0.106667 3.754667A64 64 0 0 0 576 853.354667zM512 106.666667c-29.525333 0-54.741333 23.914667-56.832 54.421333l-2.645333 38.357333-35.114667 15.68c-83.072 37.077333-137.642667 119.104-137.642667 211.242667v233.749333l-23.594666 19.2c-35.136 28.608-55.744 65.173333-62.08 110.016h635.904l-1.066667-7.082666c-7.210667-41.962667-27.2-75.306667-61.098667-102.933334l-23.594666-19.2V426.368c0-92.16-54.570667-174.165333-137.642667-211.242667l-35.114667-15.68-2.645333-38.357333C566.741333 130.581333 541.525333 106.666667 512 106.666667z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="h-user-info">
|
||||
<div class="h-user-name">maxiaofeng</div>
|
||||
<div class="h-user-avatar">
|
||||
<img src="https://picsum.photos/id/237/200/200" alt="">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
10
demo3/src/main/resources/templates/common/menu.html
Normal file
10
demo3/src/main/resources/templates/common/menu.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
71
demo3/src/main/resources/templates/index.html
Normal file
71
demo3/src/main/resources/templates/index.html
Normal file
@@ -0,0 +1,71 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title layout:title-pattern="${contentTitle} - 我的应用">首页</title>
|
||||
<link rel="stylesheet" th:href="@{/css/common/main.css}" href="../static/css/common/main.css">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="ui-menu" layout:insert="~{common/menu}">
|
||||
|
||||
</div>
|
||||
<div class="ui-main-container">
|
||||
<header class="ui-header">
|
||||
<div layout:insert="~{common/header}">
|
||||
<svg class="h-icon-menu h-icon-menu-hide" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.264 64.512h1001.472v59.904H11.264zM10.24 899.072h1001.472v59.904H10.24zM376.32 490.496h636.928v59.904H376.32zM258.56 295.424l-244.736 230.4 244.736 229.888z"></path>
|
||||
</svg>
|
||||
<svg class="h-icon-menu h-icon-menu-show" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.776 899.072h1000.96v59.904H11.776zM12.8 65.024H1013.76v59.904H12.8zM10.24 473.088h636.416v59.904H10.24zM765.44 728.064l244.224-229.888-244.224-229.888z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</header>
|
||||
<div class="ui-content" layout:fragment="content">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script>$(document).ready(function () {
|
||||
const $menu = $('.ui-menu');
|
||||
const $mainContainer = $('.ui-main-container');
|
||||
const $iconHide = $('.h-icon-menu-hide');
|
||||
const $iconShow = $('.h-icon-menu-show');
|
||||
|
||||
// 初始状态:菜单显示
|
||||
let isMenuVisible = true;
|
||||
|
||||
function updateLayout() {
|
||||
if (isMenuVisible) {
|
||||
$menu.css('width', '200px');
|
||||
$mainContainer.css({
|
||||
'margin-left': '200px',
|
||||
'width': 'calc(100% - 200px)'
|
||||
});
|
||||
} else {
|
||||
$menu.css('width', '0');
|
||||
$mainContainer.css({
|
||||
'margin-left': '0',
|
||||
'width': '100%'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 点击 hide 图标:隐藏菜单
|
||||
$iconHide.on('click', function () {
|
||||
isMenuVisible = false;
|
||||
updateLayout();
|
||||
$iconHide.hide();
|
||||
$iconShow.show();
|
||||
});
|
||||
|
||||
// 点击 show 图标:显示菜单
|
||||
$iconShow.on('click', function () {
|
||||
isMenuVisible = true;
|
||||
updateLayout();
|
||||
$iconShow.hide();
|
||||
$iconHide.show();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
60
demo3/src/main/resources/templates/login.html
Normal file
60
demo3/src/main/resources/templates/login.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>登录 - 我的应用</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link th:href="@{/css/login.css}" rel="stylesheet">
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center mt-5">
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body p-4">
|
||||
<div class="text-center mb-4">
|
||||
<h2 class="card-title">用户登录</h2>
|
||||
</div>
|
||||
|
||||
<!-- 错误消息 -->
|
||||
<div th:if="${error}" class="alert alert-danger" role="alert">
|
||||
<span th:text="${error}"></span>
|
||||
</div>
|
||||
|
||||
<!-- 注销消息 -->
|
||||
<div th:if="${message}" class="alert alert-success" role="alert">
|
||||
<span th:text="${message}"></span>
|
||||
</div>
|
||||
|
||||
<!-- 登录表单 -->
|
||||
<form th:action="@{/login/postFrom}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">用户名</label>
|
||||
<input type="text" class="form-control" id="username" name="username"
|
||||
placeholder="请输入用户名" required autofocus>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">密码</label>
|
||||
<input type="password" class="form-control" id="password" name="password"
|
||||
placeholder="请输入密码" required>
|
||||
</div>
|
||||
<div class="d-grid gap-2 mb-3">
|
||||
<button type="submit" class="btn btn-primary">登录</button>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<a th:href="@{/register}" class="text-decoration-none">注册新账户</a>
|
||||
<span class="mx-2">|</span>
|
||||
<a href="#" class="text-decoration-none">忘记密码?</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,13 @@
|
||||
package top.yexuejc.demo;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class Demo2ApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user