Series of articles:
SpringBoot + Vue front-end and back-end separation project practice || One: Vue front-end design
SpringBoot + Vue front-end separation project practice || Two: Spring Boot backend and database connection
SpringBoot + Vue front-end separation project practice || Three: Spring Boot backend and Vue frontend connection
SpringBoot + Vue front-end and front-end separation project practice || Four: User management function implementation
SpringBoot + Vue front-end separation project practice || Five: User management function follow-up
Article directory
Video explanation of Bilibili: The most simple but practical SpringBoot+Vue front-end and back-end separation project in 2023.
If you don’t want to watch the video, you can browse the notes of this article for more details.
Environment configuration
Nodejs: 8.17.0
Vue: 3.11.0
Maven: 3.6.0
Java version book: 1.8.0_371
MySQL: 5.5_56
Redis: 5.0.14
一般推荐使用低版本,因为高版本兼容低版本,反之不行
development tools
IDEA 2021 (backend)
VScode (frontend)
Download Vue front-end template
https://panjiachen.gitee.io/vue-element-admin-site/zh/guide/It
is recommended to download 1 and 2 here. The project is mainly developed based on 2, but it will expand a front-end function that requires the use of the code in 1
After selecting 2, you can download other versions. Here I downloadedv3.0.0
After the download is completed, 第2个基础模版
it will be extracted to any directory that can be found (it is not recommended that the directory contains Chinese characters)
Open VScode,选择 文件 -> 打开文件夹
Open successfully as shown below
Front-end project started
Open the integrated terminal and be sure to navigate to package.json
the level directory
Then enter the command in the terminalnpm install
Will automatically download project dependencies
After the dependencies are downloaded, start the project and run the commandnpm run dev
The following message appears to indicate success.
Just open the link.
Front-end description and modification
The overall configuration of the front end is in the vue.config.js
file
Line 16 is the port number and can be modified. Here I modified it to8888
Line 34 opens the browser by default whether to start the project. Because Vue supports hot modification, you don’t need to restart every time you complete the front-end modification. The front-end page will be automatically modified after the modification is saved, so it is set here.false
All pages are saved in src/views
folders. view
The folder here has been modified by me. The modification process will be explained later.
Modify the login page firstsrc/views/login/index.vue
Locate line 6 and modifytitle
Navigate to line 187 and modify the background image. At this time you need to find a background image and put it in src/assets
the folder.
background-image: url(../../assets/bg.png); //背景图
background-size: 100%;
After saving, the page will be modified simultaneously.
Why can I modify the background image here?
F12
We open the developer mode in the browser , select 1, and then move the mouse to select the entire page. At this time, a class name appears in the lower left corner .login.container
. Remember the name.
Open VScode
the search and notice that the second one .login-container
displayed on the page is replaced with "become" . This is where we need to modify the style. Other modifications:.login.container
.
-
.login-container
- To change English to Chinese, you actually need to find the corresponding English word and change it to Chinese. You can remember the English word on the page, then enter the code to find where the corresponding English word is, and then type in Chinese.
- Modify the login form to be semi-transparent, and
查找元素
then searchlogin-form
.login-form { position: relative; width: 520px; max-width: 100%; padding: 30px 35px 0; margin: 0 auto; overflow: hidden; background-color: #2d3a4b; // 背景颜色 border-radius: 5px; // 表单圆角 opacity: 0.85; // 透明度,值越小越越透明,取值为[0,1] }
- Attached is the full code of my
src\views\login\inde.vue
file<template> <div class="login-container"> <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left"> <div class="title-container"> <h3 class="title">神盾局管理系统</h3> </div> <el-form-item prop="username"> <span class="svg-container"> <svg-icon icon-class="user" /> </span> <el-input ref="username" v-model="loginForm.username" placeholder="用户名" name="username" type="text" tabindex="1" auto-complete="on" /> </el-form-item> <el-form-item prop="password"> <span class="svg-container"> <svg-icon icon-class="password" /> </span> <el-input :key="passwordType" ref="password" v-model="loginForm.password" :type="passwordType" placeholder="密码" name="password" tabindex="2" auto-complete="on" @keyup.enter.native="handleLogin" /> <span class="show-pwd" @click="showPwd"> <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" /> </span> </el-form-item> <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">登录</el-button> <!-- <div class="tips"> <span style="margin-right:20px;">username: admin</span> <span> password: any</span> </div> --> </el-form> </div> </template> <script> import { validUsername } from '@/utils/validate' export default { name: 'Login', data() { const validateUsername = (rule, value, callback) => { if (!validUsername(value)) { callback(new Error('请输入正确的用户名')) } else { callback() } } const validatePassword = (rule, value, callback) => { if (value.length < 6) { callback(new Error('密码不能少于6位')) } else { callback() } } return { loginForm: { username: 'admin', password: '123456' }, loginRules: { username: [{ required: true, trigger: 'blur', validator: validateUsername }], password: [{ required: true, trigger: 'blur', validator: validatePassword }] }, loading: false, passwordType: 'password', redirect: undefined } }, watch: { $route: { handler: function(route) { this.redirect = route.query && route.query.redirect }, immediate: true } }, methods: { showPwd() { if (this.passwordType === 'password') { this.passwordType = '' } else { this.passwordType = 'password' } this.$nextTick(() => { this.$refs.password.focus() }) }, handleLogin() { this.$refs.loginForm.validate(valid => { if (valid) { this.loading = true this.$store.dispatch('user/login', this.loginForm).then(() => { this.$router.push({ path: this.redirect || '/' }) this.loading = false }).catch(() => { this.loading = false }) } else { console.log('error submit!!') return false } }) } } } </script> <style lang="scss"> /* 修复input 背景不协调 和光标变色 */ /* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */ $bg:#283443; $light_gray:#fff; $cursor: #fff; @supports (-webkit-mask: none) and (not (cater-color: $cursor)) { .login-container .el-input input { color: $cursor; } } /* reset element-ui css */ .login-container { .el-input { display: inline-block; height: 47px; width: 85%; input { background: transparent; border: 0px; -webkit-appearance: none; border-radius: 0px; padding: 12px 5px 12px 15px; color: $light_gray; height: 47px; caret-color: $cursor; &:-webkit-autofill { box-shadow: 0 0 0px 1000px $bg inset !important; -webkit-text-fill-color: $cursor !important; } } } .el-form-item { border: 1px solid rgba(255, 255, 255, 0.1); background: rgba(0, 0, 0, 0.1); border-radius: 5px; color: #454545; } } </style> <style lang="scss" scoped> $bg:#2d3a4b; $dark_gray:#889aa4; $light_gray:#eee; .login-container { min-height: 100%; width: 100%; background-color: $bg; overflow: hidden; background-image: url(../../assets/bg.png); //背景图 background-size: 100%; display: flex; align-items: center; .login-form { position: relative; width: 520px; max-width: 100%; padding: 30px 35px 0; margin: 0 auto; overflow: hidden; background-color: #2d3a4b; // 背景颜色 border-radius: 5px; // 表单圆角 opacity: 0.85; // 透明度,值越小越越透明,取值为[0,1] } .tips { font-size: 14px; color: #fff; margin-bottom: 10px; span { &:first-of-type { margin-right: 16px; } } } .svg-container { padding: 6px 5px 6px 15px; color: $dark_gray; vertical-align: middle; width: 30px; display: inline-block; } .title-container { position: relative; .title { font-size: 26px; color: $light_gray; margin: 0px auto 40px auto; text-align: center; font-weight: bold; } } .show-pwd { position: absolute; right: 10px; top: 7px; font-size: 16px; color: $dark_gray; cursor: pointer; user-select: none; } } </style>
复制代码后注意背景图的链接地址下要有文件存在
Modify 页面标签
Find src\settings.js
the file and modify line 3title
After the modification is completed, the browser label will change
Modify navigation bar
Click here to see the navigation window
You can customize the modification, find scr\layout\components\Navbar.vue
the file, and modify the code in the red box
<el-dropdown-menu slot="dropdown" class="user-dropdown">
<router-link to="/">
<el-dropdown-item>
个人信息
</el-dropdown-item>
</router-link>
<a target="_blank" href="https://www.chinaums.com/">
<el-dropdown-item>公司主页</el-dropdown-item>
</a>
<a target="_blank" href="https://www.hhu.edu.cn/">
<el-dropdown-item>学校主页</el-dropdown-item>
</a>
<el-dropdown-item divided @click.native="logout">
<span style="display:block;">退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
Custom menus and submenus
First, create new folders src\views\
under sys
and , and then create new , , , and files test
in these two folders , as shown in the figure belowrole.vue
user.vue
test1.vue
test2.vue
test3.vue
Enter to role.vue
write code. Enter in and
automatic code prompts will appear (you need to install a plug-in in). Just press Enter.VScode
vue
VScode
vue
Templates will be automatically generated
Write some code at this time
Other newly created vue
files follow the same procedure.
Then we src\router\index.js
link these newly created vue
files in the route
Add code and comments
{
path: '/sys', // 浏览器访问路径
component: Layout,
redirect: '/sys', // 本地views下的sys文件夹
name: 'sysManage', // 名字,唯一标识
meta: {
title: '系统管理', icon: '1' }, // 标题和图标
children: [ // 子菜单
{
path: 'user', // 子菜单的浏览器访问路径,与父菜单结合 /sys/user 最终形成http://localhost:8888/#/sys/user
name: 'user', // 名字,唯一标识
component: () => import('@/views/sys/user'), // 新建views下的user.vue文件,该文件一定要存在
meta: {
title: '用户管理', icon: '个人中心'} // 标题和图标
},
{
path: 'role', // 子菜单的浏览器访问路径,与父菜单结合 /sys/role
name: 'role',
component: () => import('@/views/sys/role'), // 新建view下的user.vue界面,必须存在
meta: {
title: '角色管理', icon: '新增组织' }
}
]
},
Note that in this code icon:'1'
, this is a customized icon. You can download your favorite icon from the Alibaba vector icon library.
Click on the icon you likeSVG下载
The downloaded icon is renamed and saved in src\icons\svg
the folder .
Then you can use the icon in the code without writing 路径
and后缀名
The icon effect is as follows
all my src\router\index.js
code
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '@/layout'
/**
* Note: sub-menu only appear when route children.length >= 1
* Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
*
* hidden: true if set true, item will not show in the sidebar(default is false)
* alwaysShow: true if set true, will always show the root menu
* if not set alwaysShow, when item has more than one children route,
* it will becomes nested mode, otherwise not show the root menu
* redirect: noRedirect if set noRedirect will no redirect in the breadcrumb
* name:'router-name' the name is used by <keep-alive> (must set!!!)
* meta : {
roles: ['admin','editor'] control the page roles (you can set multiple roles)
title: 'title' the name show in sidebar and breadcrumb (recommend set)
icon: 'svg-name'/'el-icon-x' the icon show in the sidebar
breadcrumb: false if set false, the item will hidden in breadcrumb(default is true)
activeMenu: '/example/list' if set path, the sidebar will highlight the path you set
}
*/
/**
* constantRoutes
* a base page that does not have permission requirements
* all roles can be accessed
*/
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
},
{
path: '/',
component: Layout,
redirect: '/dashboard',
children: [{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/views/dashboard/index'),
meta: {
title: '首页', icon: 'dashboard' , affix:true}
}]
},
{
path: '/sys', // 浏览器访问路径
component: Layout,
redirect: '/sys', // 本地views下的sys文件夹
name: 'sysManage', // 名字,唯一标识
meta: {
title: '系统管理', icon: '1' }, // 标题和图标
children: [ // 子菜单,此子菜单
{
path: 'user', // 子菜单的浏览器访问路径,与父菜单结合 /sys/user 最终形成http://localhost:8888/#/sys/user
name: 'user', // 名字,唯一标识
component: () => import('@/views/sys/user'), // 新建views下的user.vue文件,该文件一定要存在
meta: {
title: '用户管理', icon: '个人中心'} // 标题和图标
},
{
path: 'role', // 子菜单的浏览器访问路径,与父菜单结合 /sys/role
name: 'role',
component: () => import('@/views/sys/role'), // 新建view下的user.vue界面,必须存在
meta: {
title: '角色管理', icon: '新增组织' }
}
]
},
{
path: '/test',
component: Layout,
redirect: '/test',
name: 'test',
meta: {
title: '功能模块', icon: 'tree' },
children: [ //子菜单
{
path: 'test1',
name: 'test1',
component: () => import('@/views/test/test1'), // 新建view下的user.vue界面
meta: {
title: '功能1', icon: '维护管理' }
},
{
path: 'test2',
name: 'test2',
component: () => import('@/views/test/test2'), // 新建view下的user.vue界面,必须存在
meta: {
title: '功能2', icon: '维护管理' }
},
{
path: 'test3',
name: 'test3',
component: () => import('@/views/test/test3'), // 新建view下的user.vue界面,必须存在
meta: {
title: '功能3', icon: '维护管理' }
}
]
},
// 404 page must be placed at the end !!!
{
path: '*', redirect: '/404', hidden: true }
]
const createRouter = () => new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({
y: 0 }),
routes: constantRoutes
})
const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
export default router
注意复制代码时,确认好自己的icon图标文件
Add navigation label function
As shown in the figure below, a new function needs to be added
The code for this function is in the first project introduced before. Now you need to transfer the copy
code
After downloading the latest compressed package, unzip it and find the following three files. Note 这些文件复制到正在开发的项目中对应的路径中去
that the path settings of the two projects are the same.
src/layout/components/TagsView
src/store/modules/tagsView.js
src/store/modules/permission.js
Then enter VScode
and add code:
-
Enter
src/layout/components/AppMain.vue
the file to modify the code in the red box
<keep-alive :include="cachedViews"> <router-view :key="key" /> </keep-alive> export default { name: 'AppMain', computed: { key() { return this.$route.path }, cachedViews() { return this.$store.state.tagsView.cachedViews } } }
-
Modify files
src/store/getters.js
visitedViews: state => state.tagsView.visitedViews, cachedViews: state => state.tagsView.cachedViews, permission_routes: state => state.permission.routes
-
Modify files
src/store/index.js
import Vue from 'vue' import Vuex from 'vuex' import getters from './getters' import app from './modules/app' import settings from './modules/settings' import user from './modules/user' import tagsView from './modules/tagsView' Vue.use(Vuex) const store = new Vuex.Store({ modules: { app, settings, user, tagsView }, getters }) export default store
-
Modify files
src\layout\index.vue
-
Modify files
src\layout\components\index.js
export { default as TagsView } from './TagsView'
You're done! Restart projectnpm run dev
If we want to set a label that cannot be closed, such as 首页
the label in the picture below, we need to src\router\index.js
find it in the file 首页
and add attributesaffix:true
In addition, the label navigation can be right-clicked. Here I changed the English to Chinese.
You can VScode
search for the English that pops up in the browser in China Global, then search it, and then modify it to correspond to Chinese. The modified path here is the middle src\layout\components\TagsView\index.vue
line 20
.
At this time, the modification of the front-end page is almost the same.
Front-end data format
json
Front-end and back-end docking requires knowing the data transfer format of front-end login.
Open the browser interface and press F12
Select网络
Then click 登录
, the browser will record the status
Click and login
the data response format will appear, as shown in the red box. This format will be used when writing code on the backend.
{
"code":20000,"data":{
"token":"admin-token"}}