目录
项目源码:https://github.com/Atopos-suyu/Local-TODO
Vaadin框架介绍:Vaadin介绍与开发练习之一(总体介绍与创建项目)-CSDN博客
基于Vaadin框架(web应用程序开发框架)编写前端登录页、注册页及各组件的界面。(即Java后端语言编写前端页面)
Spring Boot框架(依赖注入)、JavaSE(导入包、Java变量、对象实例化、构造函数、方法调用、Lambda表达式、继承、注解)
引入Spring Boot框架:Spring Initializr
项目源码:https://github.com/Atopos-suyu/Local-TODO
Vaadin框架介绍:Vaadin介绍与开发练习之一(总体介绍与创建项目)-CSDN博客
一、项目简介
基于Vaadin框架(web应用程序开发框架)编写前端登录页、注册页及各组件的界面。(即Java后端语言编写前端页面)
二、项目效果展示
登录界面:
注册页面:
登录成功页面:
增加字词:
三、所需技术:
Spring Boot框架(依赖注入)、JavaSE(导入包、Java变量、对象实例化、构造函数、方法调用、Lambda表达式、继承、注解)
四、环境搭建:
引入Spring Boot框架:Spring Initializr
Vaadin框架:Vaadin Docs
主目录:
User类(对象封装)
package org.vaadin.marcus.spring.model;
import com.fasterxml.jackson.annotation.JsonFormat; //用于指定在序列化和反序列化过程中如何格式化日期时间属性
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; //用于指定在反序列化过程中使用的自定义反序列化类
import com.fasterxml.jackson.databind.annotation.JsonSerialize; //用于指定在序列化过程中使用的自定义序列化类
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; //用于将JSON字符串转换为java.time.LocalDateTime对象
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; //用于将java.time.LocalDateTime对象转换为JSON字符串
import java.time.LocalDateTime; ///用于表示年、月、日、时、分、秒等信息的不可变日期时间对象
//类定义了一个包含日期时间属性的类,并使用Jackson库提供的注解和类来控制日期时间属性的序列化和反序列化行为
/**
● @author joe
● @date 2021/6/7
*/
public class User {
private String userName;
private String password;
//反序列化过程中使用LocalDateTimeDeserializer类,将JSON字符串转换为LocalDateTime对象
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
//序列化过程中将LocalDateTime对象转换为JSON字符串
@JsonSerialize(using = LocalDateTimeSerializer.class)
//指定了日期时间的格式化模式
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime gmtCreated;
public String getUserName() {
return userName;
}//获取当前对象的用户名,并将其作为字符串类型的结果返回
//封装
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public LocalDateTime getGmtCreated() {
return gmtCreated;
}
public void setGmtCreated(LocalDateTime gmtCreated) {
this.gmtCreated = gmtCreated;
}
}
LoginView(用户登录界面)
package org.vaadin.marcus.spring;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.login.AbstractLogin;
import com.vaadin.flow.component.login.LoginForm;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.VaadinService;
import org.vaadin.marcus.spring.model.User;
//用于在Web应用中存储和获取HTTP请求的cookie数据
import javax.servlet.http.Cookie;
/**
● @author suyu
● @date 2021/6/15 用户登录界面
*/
@Route("login.html") //基于Vaadin框架实现的用户登录界面的类
public class LoginView extends VerticalLayout {
//创建LoginForm对象loginform,用于展示用户登录表单
private LoginForm loginForm = new LoginForm();
public LoginView() {
setJustifyContentMode(JustifyContentMode.CENTER);
setAlignItems(Alignment.CENTER); //设置界面布局的对齐方式
//隐藏登录表单中的“忘记密码”按钮,true为显示
loginForm.setForgotPasswordButtonVisible(false);
//创建一个H1对象h1,显示一个标题
H1 h1 = new H1("TODO");
HorizontalLayout regLayout = new HorizontalLayout(); //放置注册的相关组件
regLayout.setAlignItems(Alignment.CENTER);
Label tips = new Label("Don’t have an account?");
Button regBtn = new Button("Sign up here.");
regBtn.setThemeName("tertiary"); //表示该按钮将应用与 "tertiary" 主题相关的样式
regBtn.addClickListener(this::onSign); //给注册按钮regBtn添加一个点击事件的监听器,即this::onSign方法
regLayout.add(tips, regBtn);
//给登录表单loginForm添加一个登录事件的监听器,即this::onLogin方法
loginForm.addLoginListener(this::onLogin);
//依次添加到垂直布局VerticalLayout中
add(h1, regLayout, loginForm);
} //实现一个简单的用户登录界面,在界面中展示了标题、注册相关的提示和按钮,以及一个登录表单,并为注册按钮和登录表单添加了事件处理方法
public void onSign(ClickEvent event){ //获取当前用户界面(UI)的方法
getUI().ifPresent(ui -> {
ui.navigate("/reg.html"); //通过navigate()方法导航到指定的URL
});
} //用户点击注册按钮时,通过导航到 /reg.html 这个URL,从而跳转到注册页面
public void onLogin(AbstractLogin.LoginEvent event){ //onLogin 方法是一个事件处理方法,用于处理登录表单提交事件
String userName = event.getUsername();
String password = event.getPassword(); //分别获取用户在登录表单中输入的用户名和密码
for (User user : Reg.users) { //通过循环遍历Reg.users列表中的每个用户对象
if (user.getUserName().equals(userName) && user.getPassword().equals(password)){//判断用户输入的用户名和密码是否与列表中的用户匹配
Notification notification = new Notification();
notification.setDuration(3000);
notification.setText("login success");
notification.open(); //创建一个Notification对象,并设置其显示时间和文本内容
Cookie cookie = new Cookie("username",userName);
cookie.setPath("/"); //向客户端添加一个名为"username"的 Cookie,值为当前登录的用户名,并设置路径为根路径
VaadinService.getCurrentResponse().addCookie(cookie);
//用于向客户端添加一个名为"username"的 Cookie,值为当前登录的用户名。在客户端上可以通过该 Cookie 来存储和获取用户的登录信息
System.out.println(getUI().isPresent());
//用来检查当前是否存在用户界面(UI)
getUI().ifPresent(ui -> {
ui.navigate("/"); //获取当前用户界面(如果存在)并将其导航到根路径 ("/")
});
return; //用来结束方法的执行
} //用户登录成功后,设置Cookie以存储登录信息,然后检查当前是否存在用户界面,如果存在,则将用户界面导航到根路径
}
loginForm.setError(true); //循环结束后未找到匹配的用户,就将登录表单的setError属性设置为true,表示登录失败
}
}
Reg类(基于Vaadin框架的简单用户界面类)
package org.vaadin.marcus.spring;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;
import org.apache.commons.io.FileUtils;
import org.vaadin.marcus.spring.model.User;
import java.io.File;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
//基于Vaadin框架的简单用户界面类
/**
● @author suyu
● @date 2021/6/7 实现注册功能
*/
//标记注解,用于将这个类映射到一个URL路由上,使得当用户访问这个URL时,可以呈现这个类定义的界面
@Route("/reg.html")
public class Reg extends VerticalLayout { //Reg类继承自Vaadin的VerticalLayout类
public static List users = new ArrayList<>(); //定义静态变量 users,类型为List,并初始化为空的ArrayList
private static ObjectMapper mapper = new ObjectMapper(); //定义静态变量 mapper,类型为ObjectMapper,用于将 JSON 数据转换为 Java 对象
private static File usersFile = new File("./data/users.json"); //定义静态变量usersFile,类型为File,表示用于存储用户数据的JSON文件
static {
if (usersFile.exists()) {
try {
String content = FileUtils.readFileToString(usersFile, "utf-8");
List<User> userList = mapper.readValue(content, new TypeReference<List<User>>() {
}); //如果文件存在,就读取其中的内容,并将其转换为User对象的列表,最后将列表添加到 users 变量中
users.addAll(userList);
} catch (Exception e) {
e.printStackTrace();
}
}
} //通过读取文件中已有的数据,并将其转换为User对象的列表,来初始化users变量,以便后续的操作可以使用到这些用户数据
public Reg() { //构造函数声明
TextField userNameField = new TextField(); //创建文本字段,输入用户名
initUserNameField(userNameField); //调用方法进行初始化
TextField passwordField = new TextField(); //输入密码
initPasswordField(passwordField);
TextField confirmPasswordField = new TextField(); //确认密码
initConfirmPasswordField(confirmPasswordField);
Button regButton = new Button("Reg"); //添加一个名为“Reg”的按钮
regButton.addClickListener(click -> { //添加按钮点击监听器
onReg(userNameField, passwordField, confirmPasswordField);
}); //注册按钮点击事件处理
add(userNameField);
add(passwordField);
add(confirmPasswordField);
add(regButton); //向布局中添加组件
} //当用户点击注册按钮时,会调用相应的处理方法,将这些组件添加到垂直布局中以显示在用户界面上
public void initUserNameField(TextField userNameField) { //TextField作为参数,命名为userNameField
String userName = "UserName";
userNameField.setLabel(userName);
userNameField.setPlaceholder("please input username");
} //设置用户名文本字段的标签和占位符,以便在用户界面上显示正确的提示信息
public void initPasswordField(TextField passwordField) {
String password = "Password";
passwordField.setLabel(password);
passwordField.setPlaceholder("please input password");
}
public void initConfirmPasswordField(TextField passwordField) {
String password = "Confirm Password";
passwordField.setLabel(password);
passwordField.setPlaceholder("please input confirm password");
}
//onReg方法用于处理注册按钮时的逻辑处理
public void onReg(TextField userNameField, TextField passwordField, TextField confirmPasswordField) {
String userNameText = userNameField.getValue();
String pwdText = passwordField.getValue();
String confirmPwdText = confirmPasswordField.getValue();
//调用相应的文本字段.getValue方法获得用户输入的内容储存在字符串变量中
Notification notification = new Notification(); //创建notification对象显示通知消息
notification.setDuration(3000); // 消息持续显示时间为3000ms
if (userNameText == null || "".equals(userNameText)) { //如果 userNameText 为空或者空字符串,则认为用户名未输入
notification.setText("need input username"); //将通知消息设置为 "need input username"
notification.open(); //调用open()显示通知消息"need input username"
// 程序执行到这就结束啦,后面的代码不再执行咯
return;
}
if (pwdText != null && !"".equals(pwdText) && pwdText.equals(confirmPwdText)) {
//如果密码不为空且与确认密码相等
for (User user : users) {
//循环遍历用户列表中的每个用户对象
if (userNameText.equals(user.getUserName())){
notification.setText("username already reg");
notification.open();
return;
}
}
//用于注册新用户并将用户信息保存到文件中
User user = new User(); //当用户名和密码的验证都通过后,首先创建一个 User 对象
user.setUserName(userNameText);
user.setPassword(pwdText); //将输入的用户名和密码分别设置为User对象的用户名和密码
LocalDateTime gmtCreated = LocalDateTime.now();
user.setGmtCreated(gmtCreated); //获取当前时间作为用户对象的创建时间,并将其设置为 User 对象的 gmtCreated 属性
users.add(user); //将创建好的 User 对象添加到用户列表中
String userName = user.getUserName();
notification.setText("reg success " + userName);
try {
String content = mapper.writeValueAsString(users); //将用户列表转换成 JSON 格式的字符串,并将其写入到名为 usersFile 的文件中
FileUtils.writeStringToFile(usersFile, content, "utf-8");
} catch (Exception e) {
e.printStackTrace();
} //如果写入过程中遇到异常,会将异常对象的堆栈信息打印出来。
} else {
notification.setText("confirm pwd error");
}
notification.open();
} //将用户输入的用户名和密码保存到文件中,并在界面上显示注册结果的通知消息
}
todo类(TODO页面的呈现)
package org.vaadin.marcus.spring;
//com.vaadin.flow.component:这是一个Vaadin Flow框架的组件包,里面包含各种UI组件
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.html.H4;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.*; //com.vaadin.flow.router:这是Vaadin Flow框架的路由器包,用于管理页面路由和导航
import com.vaadin.flow.server.VaadinService; //com.vaadin.flow.server:这是Vaadin Flow框架的服务器包,用于处理与服务器的交互
import javax.servlet.http.Cookie; //javax.servlet.http:这是Java Servlet API的一部分,提供了处理HTTP请求和响应的类和接口
import java.awt.*; //java.awt:这是Java Abstract Window Toolkit (AWT)的一部分,它提供了用于创建图形用户界面的类和方法
//通过导入这些类和包,可以使用它们提供的功能和特性来开发基于Vaadin Flow框架的Web应用程序
@Route("/") //注解,组件将会被映射到根路径"/"
public class Todo extends VerticalLayout implements AfterNavigationObserver{
//定义名为"Todo"的公共类,它继承自VerticalLayout类,并实现AfterNavigationObserver接口
private VerticalLayout totoList = new VerticalLayout(); //创建了一个私有的VerticalLayout实例对象"totolist",用于存储待办事项文本
private TextField todoField = new TextField(); //创建私有的TextFiled实例对象"todoField",用于输入待办事项的文本
public Todo() { //Todo类的构造函数
Button addButton = new Button("Add"); //创建了一个"Add"按钮的实例对像"addButton"
addButton.addClickListener(this::onAdd); //添加点击事件监听器
add(new H1("TODO"), new H4("Hello "+getUserName()),totoList,new HorizontalLayout(todoField,addButton));
//使用add方法将各个组件添加到Todo类的实例中
}//创建了一个名为 "Todo" 的页面组件,用于显示待办事项列表。在界面上还有一个文本输入框和一个 "Add" 按钮,用于向列表中添加新的待办事项
//同时,页面顶部显示了一个标题和当前登录用户的欢迎信息
public String getUserName(){ //返回当前登录用户的用户名
for (Cookie cookie : VaadinService.getCurrentRequest().getCookies()) { //遍历当前请求中的所有cookie
if (cookie.getName().equals("username")){ //判断当前cookie是否包含名为"username"的键
return cookie.getValue(); //返回当前登录用户的用户名
}
}
return ""; //如果没有找到名为"username"的cookie或对应值为空,则返回一个空字符串
} //该方法通过从Vaadin服务的当前请求中获取cookie来获取当前登录用户的用户名
public void onAdd(ClickEvent event){ //处理"Add"按钮的点击事件
String todoVal = todoField.getValue(); //获取输入框 "todoField" 中用户输入的文本值,并将其赋值给字符串类型的变量 "todoVal"
Checkbox checkbox = new Checkbox(todoVal);//将用户输入的待办事项文本值作为参数传递给构造函数,创建一个带有默认标签文本的复选框
totoList.add(checkbox); //将新创建的复选框添加到待办事项列表中 "totoList"
}//它在点击 "Add" 按钮时,获取文本输入框中用户输入的文本值,创建一个复选框对象,并将其添加到待办事项列表中
@Override //表示重写了父类或接口的同名方法
public void afterNavigation(AfterNavigationEvent afterNavigationEvent) { //实现用户导航到相应页面后自动执行一些逻辑
//实现AfterNavigationObserver接口所必需的方法,用于在用户导航到相应的页面后执行逻辑
for (Cookie cookie : VaadinService.getCurrentRequest().getCookies()) {
if (cookie.getName().equals("username")){
return;
}
}
getUI().ifPresent(ui -> {
ui.navigate("/login.html");
}); //如果当前请求中不包含名为 "username" 的cookie,则跳转到登录页面
} //实现用户导航到相应的页面后自动检查是否存在名为 "username" 的cookie
}