Table of contents
- Preface
- 1. Front-end projects
- 2. Back-end projects
-
- 1. Check the installed versions of JDK and maven
- 2. Create springboot project
- 3. Create springboot project successfully
- 4. Configure maven and maven library
- 5. Load the maven library
- 6. Create application.yml
- 7. Run the project
- 8. Create a new WebConfig file to process cross-domain
- 9. Use idea to connect to mysql
- 10. Add lombok dependency to pom file
- 11. Create a bean folder for placing entity objects
- 12. Check the successfully constructed entity objects
- 13. Create controller interface
- 14. Create dao interface
- 15. Create mapper mapping file
- 3. Test front-end and back-end functions
Preface
This project is a simple full-stack project. The front-end project uses vue3, ts, Element Plus, axios and other technology stacks; the back-end project uses springboot, jdbc, mysql, maven and other technologies.
1. Front-end projects
1. Create a project using vue scaffolding
1.1 Check vue version
Enter the command vue -V (V must be capitalized), the version needs to be after 4.5.1
1.2 Create a project using vue scaffolding
1. Use the command vue create vue3-element-demo to create a Vue project.
2. Use the up and down keys to select, select Manually select features, and press Enter to configure manually.
3. Use the up and down keys to select, use the space bar to select (use Babel, TypeScript, Router, Vuex, CSS Pre-processors, Linter / Formatter), and press Enter
4. Select vue3 and press Enter
5. Whether to use the Class style decorator, enter [y] here, and then press Enter to confirm;
6. Whether Babel and TS are used together for modern mode, automatically detected polyfills and translating JSX. Enter [y] here and press Enter to confirm;
7. Whether to use history routing Mode, enter [n] here to use the default hash mode, and then press Enter to confirm;
8. Select the CSS preprocessor and press the up and down arrow keys to select. I am used to Less. Then press Enter to confirm;
9. Select the first ESLint with error prevention only (ESLint with error prevention only)
10. Babel, ESLint and other plug-ins Is the configuration configured in a separate file, or are they all in the package.json file? Enter package.json here. You don’t want too many scattered configuration files, and then press Enter to confirm;
11. Whether to save the feature configuration of the currently created Vue3 project and create a Vue project next time , you can select this feature and press Enter to confirm to complete the creation. This does not need to be saved, enter [n], and then press Enter to confirm;
12. The project is being created;
The project is created successfully:
Run the project
2. Delete redundant project files and modify configuration items
2.1. Delete the following files
2.1. Create index file under views
<template>
<div>我的首页</div>
</template>
<script lang="ts">
export default {
name: 'Index'
}
</script>
<style scoped></style>
2.2. Modify the router/index.ts routing file:
An error will be reported during compilation:
vue eslint报错:Component name “index“ should always be multi-word.eslintvue/multi-word-component-names
Reason: In addition to the root component (App.vue), the name of the custom component should be composed of multiple words to prevent conflict with html tags.
The solution is to turn off eslint verification and recompile to normal.
lintOnSave: false
2.3. Modify the App.vue file:
<template>
<div id="app">
<router-view/>
</div>
</template>
<style lang="less">
html,
body,
#app {
width: 100%;
height: 100%;
}
</style>
running result:
2.4. Initialize page style and clear floats
Create a new css/resset.css file and introduce it into the index.html file to initialize the style.
/**
* Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/)
* http://cssreset.com
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video{
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
font-weight: normal;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section{
display: block;
}
ol, ul, li{
list-style: none;
}
blockquote, q{
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after{
content: '';
content: none;
}
table{
border-collapse: collapse;
border-spacing: 0;
}
/* custom */
a{
color: #7e8c8d;
text-decoration: none;
-webkit-backface-visibility: hidden;
}
::-webkit-scrollbar{
width: 5px;
height: 5px;
}
::-webkit-scrollbar-track-piece{
background-color: rgba(0, 0, 0, 0.2);
-webkit-border-radius: 6px;
}
::-webkit-scrollbar-thumb:vertical{
height: 5px;
background-color: rgba(125, 125, 125, 0.7);
-webkit-border-radius: 6px;
}
::-webkit-scrollbar-thumb:horizontal{
width: 5px;
background-color: rgba(125, 125, 125, 0.7);
-webkit-border-radius: 6px;
}
html, body{
width: 100%;
font-family: "Arial", "Microsoft YaHei", "黑体", "宋体", "微软雅黑", sans-serif;
}
body{
line-height: 1;
-webkit-text-size-adjust: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
html{
overflow-y: scroll;
}
/*清除浮动*/
.clearfix:before,
.clearfix:after{
content: " ";
display: inline-block;
height: 0;
clear: both;
visibility: hidden;
}
.clearfix{
*zoom: 1;
}
/*隐藏*/
.dn{
display: none;
}
Quoted in index.html:
3. Introduce the ElementPlus component library
3.1. Import dependency packages
npm i element-plus -D
The first one: mount ElementPlus globally and add it in main.js
The first one: Import components on demand
Introduced into vue.config.js (import on demand), add the following configuration
const {
defineConfig } = require('@vue/cli-service');
const AutoImport = require('unplugin-auto-import/webpack');
const Components = require('unplugin-vue-components/webpack');
const {
ElementPlusResolver } = require('unplugin-vue-components/resolvers');
module.exports = defineConfig({
transpileDependencies: true,
//关闭eslint校验
lintOnSave: false,
configureWebpack: {
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
}
})
Use ElementPlus components in pages
Effect:
3. Create a login page
Create a new Login.vue file under view
<template>
<div class="container" :class="{ 'sign-up-mode': signUpMode }">
<!-- form表单容器 -->
<div class="form-container">
<div class="signin-signup">
<!-- 登录 -->
<el-form
ref="ruleFormRef"
:model="loginUser"
:rules="rules"
label-width="100px"
class="login-form sign-in-form"
>
<el-form-item label="邮箱" prop="email">
<el-input v-model="loginUser.email" placeholder="Enter Email..." />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
v-model="loginUser.password"
type="password"
placeholder="Enter Password..."
/>
</el-form-item>
<el-form-item>
<el-button type="primary" class="submit-btn" @click="submitForm(ruleFormRef)">提交</el-button>
</el-form-item>
<!-- 找回密码 -->
<el-form-item>
<p class="tiparea">忘记密码<a>立即找回</a></p>
</el-form-item>
</el-form>
</div>
</div>
<!-- 左右切换动画 -->
<div class="panels-container">
<div class="panel left-panel">
<div class="content">
<h3>Row,row,row your boat</h3>
<p>Gentlely down the stream</p>
<button @click="signUpMode = !signUpMode" class="btn transparent">
注册
</button>
</div>
</div>
<div class="panel right-panel">
<div class="content">
<h3>Merrily,merrily,merrily,merrily,</h3>
<p>Life is but a dream</p>
<button @click="signUpMode = !signUpMode" class="btn transparent">
登录
</button>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import {
ref, reactive, toRefs, getCurrentInstance } from 'vue'
import type {
FormInstance, FormRules } from 'element-plus'
// Vue3语法糖
// 通过解构getCurrentInstance,获取this,这里的this就是ctx
// const { ctx } = getCurrentInstance()
const {
proxy } = getCurrentInstance()
// 登录/注册模式
const signUpMode = ref(false)
const ruleFormRef = ref<FormInstance>()
// 登录表单
const loginUser = reactive({
email: '',
password: ''
})
// 校验规则
const rules = reactive({
email: [
{
required: true,
type: 'email',
message: 'email格式错误',
trigger: 'blur'
}
],
password: [
{
required: true, message: '密码不得为空', trigger: 'blur' },
{
min: 6, max: 30, message: '密码长度必须在6到30之间', trigger: 'blur' }
]
})
// 触发登录方法
const submitForm = (formEl: FormInstance | undefined) => {
if(!formEl!) return
formEl.validate((valid) => {
if (valid) {
console.log('submit!')
const data = proxy.$http.getUserPassword(loginUser)
console.log('data', data)
} else {
console.log('error submit!')
return false
}
})
}
</script>
<style scoped>
.container {
position: relative;
width: 100%;
min-height: 100vh;
background-color: #fff;
overflow: hidden;
}
.form-container {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.signin-signup {
position: relative;
top: 50%;
left: 75%;
transform: translate(-50%, -50%);
width: 44%;
transition: 1s 0.7s ease-in-out;
display: grid;
grid-template-columns: 1fr;
z-index: 5;
}
/* 左右切换动画 */
.social-text {
padding: 0.7rem 0;
font-size: 1rem;
}
.social-media {
display: flex;
justify-content: center;
}
.social-icon {
height: 46px;
width: 46px;
display: flex;
justify-content: center;
align-items: center;
margin: 0 0.45rem;
color: #333;
border-radius: 50%;
border: 1px solid #333;
text-decoration: none;
font-size: 1.1rem;
transition: 0.3s;
}
.social-icon:hover {
color: #4481eb;
border-color: #4481eb;
}
.btn {
width: 150px;
background-color: #5995fd;
border: none;
outline: none;
height: 49px;
border-radius: 49px;
color: #fff;
text-transform: uppercase;
font-weight: 600;
margin: 10px 0;
cursor: pointer;
transition: 0.5s;
}
.btn:hover {
background-color: #4d84e2;
}
.panels-container {
position: absolute;
height: 100%;
width: 100%;
top: 0;
left: 0;
display: grid;
grid-template-columns: repeat(2, 1fr);
}
.container:before {
content: '';
position: absolute;
height: 2000px;
width: 2000px;
top: -10%;
right: 48%;
transform: translateY(-50%);
background-image: linear-gradient(-45deg, #4481eb 0%, #04befe 100%);
transition: 1.8s ease-in-out;
border-radius: 50%;
z-index: 6;
}
.image {
width: 100%;
transition: transform 1.1s ease-in-out;
transition-delay: 0.4s;
}
.panel {
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: space-around;
text-align: center;
z-index: 6;
}
.left-panel {
pointer-events: all;
padding: 3rem 17% 2rem 12%;
}
.right-panel {
pointer-events: none;
padding: 3rem 12% 2rem 17%;
}
.panel .content {
color: #fff;
transition: transform 0.9s ease-in-out;
transition-delay: 0.6s;
}
.panel h3 {
font-weight: 600;
line-height: 1;
font-size: 1.5rem;
}
.panel p {
font-size: 0.95rem;
padding: 0.7rem 0;
}
.btn.transparent {
margin: 0;
background: none;
border: 2px solid #fff;
width: 130px;
height: 41px;
font-weight: 600;
font-size: 0.8rem;
}
.right-panel .image,
.right-panel .content {
transform: translateX(800px);
}
/* ANIMATION */
.container.sign-up-mode:before {
transform: translate(100%, -50%);
right: 52%;
}
.container.sign-up-mode .left-panel .image,
.container.sign-up-mode .left-panel .content {
transform: translateX(-800px);
}
.container.sign-up-mode .signin-signup {
left: 25%;
}
.container.sign-up-mode form.sign-up-form {
opacity: 1;
z-index: 2;
}
.container.sign-up-mode form.sign-in-form {
opacity: 0;
z-index: 1;
}
.container.sign-up-mode .right-panel .image,
.container.sign-up-mode .right-panel .content {
transform: translateX(0%);
}
.container.sign-up-mode .left-panel {
pointer-events: none;
}
.container.sign-up-mode .right-panel {
pointer-events: all;
}
@media (max-width: 870px) {
.container {
min-height: 800px;
height: 100vh;
}
.signin-signup {
width: 100%;
top: 95%;
transform: translate(-50%, -100%);
transition: 1s 0.8s ease-in-out;
}
.signin-signup,
.container.sign-up-mode .signin-signup {
left: 50%;
}
.panels-container {
grid-template-columns: 1fr;
grid-template-rows: 1fr 2fr 1fr;
}
.panel {
flex-direction: row;
justify-content: space-around;
align-items: center;
padding: 2.5rem 8%;
grid-column: 1 / 2;
}
.right-panel {
grid-row: 3 / 4;
}
.left-panel {
grid-row: 1 / 2;
}
.image {
width: 200px;
transition: transform 0.9s ease-in-out;
transition-delay: 0.6s;
}
.panel .content {
padding-right: 15%;
transition: transform 0.9s ease-in-out;
transition-delay: 0.8s;
}
.panel h3 {
font-size: 1.2rem;
}
.panel p {
font-size: 0.7rem;
padding: 0.5rem 0;
}
.btn.transparent {
width: 110px;
height: 35px;
font-size: 0.7rem;
}
.container:before {
width: 1500px;
height: 1500px;
transform: translateX(-50%);
left: 30%;
bottom: 68%;
right: initial;
top: initial;
transition: 2s ease-in-out;
}
.container.sign-up-mode:before {
transform: translate(-50%, 100%);
bottom: 32%;
right: initial;
}
.container.sign-up-mode .left-panel .image,
.container.sign-up-mode .left-panel .content {
transform: translateY(-300px);
}
.container.sign-up-mode .right-panel .image,
.container.sign-up-mode .right-panel .content {
transform: translateY(0px);
}
.right-panel .image,
.right-panel .content {
transform: translateY(300px);
}
.container.sign-up-mode .signin-signup {
top: 5%;
transform: translate(-50%, 0);
}
}
@media (max-width: 570px) {
form {
padding: 0 1.5rem;
}
.image {
display: none;
}
.panel .content {
padding: 0.5rem 1rem;
}
.container {
padding: 1.5rem;
}
.container:before {
bottom: 72%;
left: 50%;
}
.container.sign-up-mode:before {
bottom: 28%;
left: 50%;
}
}
/* 控制login & register显示 */
form {
padding: 0rem 5rem;
transition: all 0.2s 0.7s;
overflow: hidden;
}
form.sign-in-form {
z-index: 2;
}
form.sign-up-form {
opacity: 0;
z-index: 1;
}
/* register */
.loginForm,
.registerForm {
margin-top: 20px;
background-color: #fff;
padding: 20px 40px 20px 20px;
border-radius: 5px;
box-shadow: 0px 5px 10px #cccc;
}
.submit-btn {
width: 100%;
}
.tiparea {
text-align: right;
font-size: 12px;
color: #333;
width: 100%;
}
.tiparea a {
color: #409eff;
}
</style>
4. Package and use Axios
4.1. Install Axios
npm i axios
4.2. Install NProgress top progress bar
npm i --save-dev @types/nprogress
4.3. Encapsulation request interception
Create a new utils folder in the src directory, then create a new requestUtil.ts file and write the following code
import axios from 'axios'
import Nprogress from 'nprogress'
import 'nprogress/nprogress.css'
import {
ElMessage } from 'element-plus'
const http = axios.create({
baseURL: 'http://localhost:9000',
timeout: 300 * 1000, // 请求超时时间设置为300秒
})
const NETWORK_ERROR = '网络错误,请联系开发人员'
/**
* 请求拦截器
*/
http.interceptors.request.use((req) => {
console.log('请求拦截器 =>', req)
Nprogress.start()
return req;
}, (error) => {
Nprogress.done()
return Promise.reject(error);
});
/**
* 响应拦截器
*/
http.interceptors.response.use(function (res) {
console.log('响应拦截器 =>', res)
Nprogress.done()
if (res.status == 200) {
return res.data
} else {
ElMessage.error((NETWORK_ERROR))
return Promise.reject(NETWORK_ERROR)
}
});
export default http
4.4. Cross-domain front-end settings
Configure the following code in vue.config.js:
const {
defineConfig } = require('@vue/cli-service');
// const AutoImport = require('unplugin-auto-import/webpack');
// const Components = require('unplugin-vue-components/webpack');
// const { ElementPlusResolver } = require('unplugin-vue-components/resolvers');
module.exports = defineConfig({
transpileDependencies: true,
//关闭eslint校验
lintOnSave: false,
// ElementPlus按需导入方式
// configureWebpack: {
// plugins: [
// AutoImport({
// resolvers: [ElementPlusResolver()],
// }),
// Components({
// resolvers: [ElementPlusResolver()],
// }),
// ],
// }
devServer: {
open: true,
host: 'localhost',
port: 8080,
https: false,
// 设置跨域
proxy: {
'/api': {
target: 'http://localhost:9000',
ws: true,
changeOrigin: true,
pathRewrite: {
'^api': ''
}
}
}
}
})
4.5. Configure interface api
Create an api folder in the src directory and create index.ts inside it
import http from '@/utils/requestUtils'
export default {
/**
* 根据用户邮箱、密码查询用户信息
*/
getUserPassword(data: any) {
return http.post(
'/api/getUserPassword',
data,
{
headers: {
'Content-Type': 'application/json'
},
}
)
},
/**
* 保存用户信息
*/
saveUser(data: any) {
return http.post(
'/api/saveUser',
data,
{
headers: {
'Content-Type': 'application/json'
},
}
)
},
}
4.6. Globally encapsulate http requests
Introduce the HTTP request tool in the main.ts file and configure it as a global method
import {
createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import axios from 'axios'
import apiServe from '@/api'
const app = createApp(App)
app.use(store)
app.use(router)
app.use(ElementPlus)
app.mount('#app')
app.config.globalProperties.$http = apiServe
app.config.globalProperties.$axios = axios
2. Back-end projects
1. Check the installed versions of JDK and maven
Enter java -version in cmd
and mvn -v to check the corresponding installation status
2. Create springboot project
Create a project through idea's spring initializr. Instead of choosing maven, choose spring initializr for quick creation. Then check the relevant dependencies.
3. Create springboot project successfully
Project created successfully
4. Configure maven and maven library
Configure local maven library
5. Load the maven library
6. Create application.yml
Create a new application.yml under resources, and configure the database name, password, and port. Try not to use 8080 for the port to avoid it being the same as the front-end port.
Comment out another configuration
# mysql
spring:
datasource:
#MySQL配置
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/easyproject?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
#数据库名和密码
username: root
password: 920724
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.demo.model
server:
port: 9000
7. Run the project
The project runs successfully, port 9000
8. Create a new WebConfig file to process cross-domain
Create a utils folder, create WebConfig under the utils folder, and add the following configuration
package com.springboot.userlogin.springbootdemo.utils;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
// 使用注解说明是全局配置类
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
// 继承跨域请求的类
@Override
public void addCorsMappings(CorsRegistry registry) {
// 跨域处理的方法
registry.addMapping("/**") // 任意访问都允许跨域
.allowedOrigins("http://localhost:8080", "null") // 跨域来源
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE") // 跨域请求类型
.maxAge(3600) // 超时时间
.allowCredentials(true); // 允许携带信息
}
}
Alt+insert shortcut keys can pop up
9. Use idea to connect to mysql
For the mysql installation and configuration method, you can refer to another document I wrote:
https://blog.csdn.net/m0_47791238/article/details/134811414?spm=1001.2014.3001.5501
Enter mysql username and password:
10. Add lombok dependency to pom file
Purpose: To use the @Data annotation
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
11. Create a bean folder for placing entity objects
Create the bean folder in the project, create a new user entity class, use Data annotations, create constructors and get\set methods
package com.springboot.userlogin.springbootdemo.bean;
import lombok.Data;
@Data
public class User {
private int id;
private String username;
private String password;
private String email;
private String role;
private boolean state;
// public User() {
// }
// public User(String username, String password, String email, String role, boolean state) {
// this.username = username;
// this.password = password;
// this.email = email;
// this.role = role;
// this.state = state;
// }
//
// public int getId() {
// return id;
// }
//
// public String getUsername() {
// return username;
// }
//
// public String getPassword() {
// return password;
// }
//
// public String getEmail() {
// return email;
// }
//
// public String getRole() {
// return role;
// }
//
// public boolean getState() {
// return state;
// }
//
// public void setId(int id) {
// this.id = id;
// }
//
// public void setUsername(String username) {
// this.username = username;
// }
//
// public void setPassword(String password) {
// this.password = password;
// }
//
// public void setEmail(String email) {
// this.email = email;
// }
//
// public void setRole(String role) {
// this.role = role;
// }
//
// public void setState(boolean state) {
// this.state = state;
// }
}
12. Check the successfully constructed entity objects
Shortcut key alt + 7
13. Create controller interface
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoginController {
@Autowired
UserDao userDao;
@PostMapping("/api/getUserPassword") // @RequestMapping注解创建接口
public String userLogin(@RequestBody User user) {
// @RequestBody注解方便找到user实体
System.out.println("User : " + user);
String str = "error";
int count = userDao.getUserByMassage(user.getEmail(), user.getPassword());
if (count > 0) {
str = "ok";
}
return str;
}
}
14. Create dao interface
package com.springboot.userlogin.springbootdemo.dao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
@Mapper
public interface UserDao {
int getUserByMassage(@Param("email") String email, @Param("password") String password);
}
15. Create mapper mapping file
Create a mapper folder under resources to store the database mapping file
Create a new UserMapper.xml under the mapper folder
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--对应dao层接口文件的目录-->
<mapper namespace="com.springboot.userlogin.springbootdemo.dao.UserDao">
<!-- id值为UserDao接口方法名; -->
<select id="getUserByMassage" resultType="java.lang.Integer">
SELECT count(id) FROM easyUser
WHERE email=#{
email} AND password=#{
password}
</select>
</mapper>