SpringBoot + Vue front-end and back-end separation project practice || 1: Vue front-end design

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

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
Insert image description here
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
Insert image description here
Insert image description here

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)
Insert image description here

Open VScode,选择 文件 -> 打开文件夹
Insert image description here
Insert image description here

Open successfully as shown below
Insert image description here

Front-end project started

Open the integrated terminal and be sure to navigate to package.jsonthe level directory
Insert image description here

Then enter the command in the terminalnpm install
Insert image description here

Will automatically download project dependencies
Insert image description here

After the dependencies are downloaded, start the project and run the commandnpm run dev
Insert image description here

The following message appears to indicate success.
Insert image description here
Just open the link.
Insert image description here

Front-end description and modification

The overall configuration of the front end is in the vue.config.jsfile
Insert image description here

Line 16 is the port number and can be modified. Here I modified it to8888
Insert image description here

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
Insert image description here

All pages are saved in src/viewsfolders. viewThe folder here has been modified by me. The modification process will be explained later.
Insert image description here

Modify the login page firstsrc/views/login/index.vue
Insert image description here

Locate line 6 and modifytitle
Insert image description here

Navigate to line 187 and modify the background image. At this time you need to find a background image and put it in src/assetsthe folder.

background-image: url(../../assets/bg.png); //背景图
background-size: 100%;

Insert image description here

After saving, the page will be modified simultaneously.
Insert image description here

Why can I modify the background image here?
Insert image description here

F12We 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.
Insert image description here

Open VScodethe search and notice that the second one .login-containerdisplayed on the page is replaced with "become" . This is where we need to modify the style. Other modifications:.login.container.-.login-container
Insert image description here

  1. 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.
    Insert image description here
  2. Modify the login form to be semi-transparent, and 查找元素then searchlogin-form
    Insert image description here
    Insert image description here
    .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]
    }
    
  3. Attached is the full code of my src\views\login\inde.vuefile
    <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.jsthe file and modify line 3title

Insert image description here

After the modification is completed, the browser label will change
Insert image description here

Modify navigation bar

Click here to see the navigation window
Insert image description here

You can customize the modification, find scr\layout\components\Navbar.vuethe file, and modify the code in the red box
Insert image description here
Insert image description here

<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

Insert image description here

First, create new folders src\views\under sysand , and then create new , , , and files testin these two folders , as shown in the figure belowrole.vueuser.vuetest1.vuetest2.vuetest3.vue
Insert image description here

Enter to role.vuewrite code. Enter in and
automatic code prompts will appear (you need to install a plug-in in). Just press Enter.VScodevueVScodevue
Insert image description here

Templates will be automatically generated
Insert image description here

Write some code at this time
Insert image description here

Other newly created vuefiles follow the same procedure.

Then we src\router\index.jslink these newly created vuefiles in the route
Insert image description here
Insert image description here

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.

Insert image description here

Click on the icon you likeSVG下载
Insert image description here

The downloaded icon is renamed and saved in src\icons\svgthe folder .
Insert image description here

Then you can use the icon in the code without writing 路径and后缀名
Insert image description here

The icon effect is as follows
Insert image description here

all my src\router\index.jscode

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
Insert image description here

The code for this function is in the first project introduced before. Now you need to transfer the copycode
Insert image description here

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

Insert image description here
Insert image description here

Then enter VScodeand add code:

  1. Enter src/layout/components/AppMain.vuethe file to modify the code in the red box
    Insert image description here

    <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
        }
      }
    }
    
  2. Modify filessrc/store/getters.js
    Insert image description here

      visitedViews: state => state.tagsView.visitedViews,
      cachedViews: state => state.tagsView.cachedViews,   
      permission_routes: state => state.permission.routes
    
  3. Modify filessrc/store/index.js
    Insert image description here

    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
    
  4. Modify filessrc\layout\index.vue
    Insert image description here

  5. Modify filessrc\layout\components\index.js
    Insert image description here

    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.jsfind it in the file 首页and add attributesaffix:true
Insert image description here

Insert image description here

In addition, the label navigation can be right-clicked. Here I changed the English to Chinese.
Insert image description here

You can VScodesearch 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.vueline 20.
Insert image description here

At this time, the modification of the front-end page is almost the same.

Front-end data format

jsonFront-end and back-end docking requires knowing the data transfer format of front-end login.

Open the browser interface and press F12Select网络
Insert image description here

Then click 登录, the browser will record the status
Insert image description here

Click and loginthe data response format will appear, as shown in the red box. This format will be used when writing code on the backend.
Insert image description here

{
    
    "code":20000,"data":{
    
    "token":"admin-token"}}

Guess you like

Origin blog.csdn.net/qq_56039091/article/details/131299419