A: hi! ~ Hello everyone, I am YK bacteria 🐷, a microsystem front-end ✨, love to think, love to summarize, love to record, love to share 🏹, welcome to follow me 😘 ~ [wechat account: Yk2012Yk2012, wechat public account: ykyk2012]

“This is the fifth day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

Project address https://gitee.com/ykang2020/vue_shop

Both back-end database and interface documentation are linked here: pan.baidu.com/s/1Cxd1Ee_y… Extraction code: ykyk

0. Project introduction

E-commerce Management System (Element-UI)

E-commerce background management system is used to manage user accounts, commodity classification, commodity information, orders, data statistics and other business functions

Development mode

E-commerce background management system as a whole adopts the development mode of front and back end separation, in which the front end project is SPA (single page application) project based on Vue technology stack

Front-end technology stack

  • Vue
  • Vue-router
  • Element-UI
  • Axios
  • Echarts

Back-end technology stack

  • Node.js
  • Express
  • Jwt session (simulation)
  • Mysql
  • Sequelize(framework for manipulating databases)

1. Configuration – Initializes the front-end project

① Install Vue scaffolding

② Create projects through Vue scaffolding

3 configure Vue routes

4. Configure the Element-UI component library

⑤ Configure the AXIOS library

⑥ Syntax handling ESLint

7. Initialize git remote repository

8. Host local projects to Github or code cloud

See [Vue] Vue – CLI – Graphical Create Project – Configuration Project – AXIos – elementUI – Code cloud SSH Public Key Settings – Gitee synchronization

2. Configure back-end interfaces

① Install the MySQL database

  1. Use the phpStudy software (close Apache)

2. Import the database file (the path cannot contain Chinese characters)3. If data exists, the import is complete

② Install the Node.js environment

3 configure project information

cd E:\YKcode\web\myProject01\api\vue_api_server
cnpm install
Copy the code

④ Start the project

NPM install or CNPM install several times

node app.js
Copy the code

⑤ Check the API documentation. Md

⑥ Use Postman to test whether the background project interface is normal

Test against documentationDebugging success

3. Login/logout

3.1 Login Service Process

① Enter the user name and password on the login page

② Call the background interface for verification

③ After passing the verification, jump to the project home page according to the response status of the background

3.2 Technical points related to login services

  • HTTP is stateless (does not save communication state)
  • Record state on the client side via cookies (no cross-domain issues between front and back ends)
  • Logging state on the server side through session (no cross-domain issues between front-end and back-end)
  • Maintain status through token (used when there is a cross-domain problem)

Related reference [thoroughly understand cookie,session, token-ink yan, blog park]

3.3 principle of token

3.4 Implementation of login Function

3.4.1 Layout Analysis

Layout is implemented through the Element-UI component

  • el-form
  • el-form-item
  • el-input
  • el-button
  • The fonts icon

3.4.2 Git creates the login branch

Create a branch

git checkout -b login
Copy the code

See the branch

git branch
Copy the code

3.4.3 Render the Login component and implement route redirection

  1. Clear Vue templates and routesmain.js App.vue ./components/HelloWorld.vue ./views
  2. Create components./view/Login.vue
  3. Adding routing Rules./router/index.jsSet the default page redirection tologin
import Login from '.. /components/Login.vue'
const routes = [
  { path: '/'.redirect: '/login' },
  { path: '/login'.component: Login }
]
Copy the code
  1. Add route placeholder app.vue
  <div id="app">
    <! -- Route placeholder -->
    <router-view></router-view>
  </div>
Copy the code

3.4.4 Set the background color and draw the login box in the center of the screen

  1. Install and develop dependencies on less and less-loader in visual tools
  2. Recompile after installing dependencies
  3. Set the global style./assets/ CSS /global.css
/* Global stylesheet */
html.body.#app {
  height: 100%;
  margin: 0;
  padding: 0;
  min-width: 1366px;
}
Copy the code
  1. The entry file imports the global CSS file./main.js
// Import the global stylesheet
import './assets/css/global.css'
Copy the code
  1. Set the background color and draw the Login box login.vue in the center of the screen
<div class="login_container">
  <div class="login_box"></div>
</div>
Copy the code
.login_container {
  background-color: #409eff;
  height: 100%;
}

.login_box {
  width: 450px;
  height: 300px;
  background-color: #fff;
  border-radius: 3px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}
Copy the code

3.4.5 Drawing the Default Profile picture

<! -- Avatar area -->
<div class="avatar_box">
  <img src=".. /assets/YK.jpg" alt="" />
</div>
Copy the code
.avatar_box {
  height: 130px;
  width: 130px;
  border: 1px solid #eee;
  border-radius: 50%;
  padding: 10px;
  box-shadow: 0 0 10px #ddd;
  position: absolute;
  left: 50%;
  transform: translate(-50%, -50%);
  background-color: #fff;
}
img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background-color: #eee;
}
Copy the code

3.4.6 Drawing the Login form area

  1. Import the element component./plugins/element.js as needed
import Vue from 'vue'
import { Button, Form, FormItem, Input } from 'element-ui'
Vue.use(Button)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
Copy the code
  1. The page uses the imported component tag login.vue
<! -- Login form area -->
<el-form label-width="0px" class="login_form">
  <! -- User name -->
  <el-form-item>
    <el-input></el-input>
  </el-form-item>
  <! - password - >
  <el-form-item>
    <el-input></el-input>
  </el-form-item>
  <! - button - >
  <el-form-item>
    <el-button type="primary" class="btns">The login</el-button>
    <el-button type="info">reset</el-button>
  </el-form-item>
</el-form>
Copy the code
.login_form {
  position: absolute;
  bottom: 0;
  width: 100%;
  padding: 0 20px;
  box-sizing: border-box;
}
.btns {
  display: flex;
  justify-content: flex-end;
}
Copy the code

3.4.7 Drawing the Input box with Icon

Element. The eleme. Cn / # / useful – cn/com… Official icon library element.eleme.cn/#/zh-CN/com…

<el-input prefix-icon="el-icon-user"></el-input>
Copy the code
<el-input prefix-icon="el-icon-lock"></el-input>
Copy the code
  • Ali font icon library use
  1. Download the selected Ali font icon
  2. Demo_fontclass.html View the icon name
  3. Introduce the iconfont. CSS style sheetimport './assets/fonts/iconfont.css'
  4. Class =”iconfont icon-xxx”

3.4.8 Realize data binding of forms

<el-form :model="loginForm">
	<el-input v-model="loginForm.username"></el-input>
	<el-input v-model="loginForm.password" type="password"></el-input>
</el-form>
Copy the code
export default {
  data () {
    return {
      // This is the data binding object for the login form
      loginForm: {
        username: 'admin'.password: '123456'}}}}Copy the code

3.4.9 Realize data verification of the form

<el-form :rules="loginFormRules">
	<el-form-item prop="username">
		<el-input v-model="loginForm.username"></el-input>
	</el-form-item>
	<el-form-item prop="password">
		<el-input v-model="loginForm.password" type="password"></el-input>
	</el-form-item>
</el-form>
Copy the code
export default {
  data () {
    return {
	  loginFormRules: {
	    // Verify that the username is valid
	    username: [{required: true.message: 'Please enter login name'.trigger: 'blur' },
	      { min: 3.max: 5.message: 'Between 3 and 5 characters long'.trigger: 'blur'}].// Verify that the password is valid
	    password: [{required: true.message: 'Please enter your login password'.trigger: 'blur' },
	      { min: 6.max: 15.message: 'Between 6 and 15 characters long'.trigger: 'blur'}]}}}}Copy the code

3.4.10 Resetting the form Function

  1. Add a ref reference to get the form instance object (annotating DOM elements with ref)
<el-form ref="loginFormRef"></el-form>
Copy the code
  1. Reset button bindings click events
<el-button type="info" @click="resetLoginForm">reset</el-button>
Copy the code
  1. Define the reset method (get the DOM element with $refs)
methods: {
  // Click the reset button to reset the login form
  resetLoginForm () {
    // console.log(this) // VueComponent
    this.$refs.loginFormRef.resetFields()
  }
}
Copy the code

3.4.11 Pre-authentication of form data before Login And configuring AXIOS to send a login request

  1. Add click events
<el-button type="primary" @click="login">The login</el-button>
Copy the code
  1. Import the AXIos package main.js and configure axios
import axios from 'axios'
// Configure the request root path
axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/'
Vue.prototype.$http = axios
Copy the code
  1. Import the pop-up prompt component element.js
import { Message } from 'element-ui'
Vue.prototype.$message = Message // The popup prompts the component to hang on the Vue prototype
Copy the code
  1. Write the login method
methods: {
  login () {
    this.$refs.loginFormRef.validate(async (valid) => {
      // console.log(valid) // false/ture
      if(! valid)return
      const { data: result } = await this.$http.post('login'.this.loginForm)
      // console.log(result) //Promise
      if(result.meta.status ! = =200) return this.$message.error('Login failed')
      this.$message.success('Login successful')
      
      // 1. Save the token in the sessionStorage of the client (not in the loaclStorage).
      window.sessionStorage.setItem('token', result.data.token)
      // 2. Use the programmatic navigation to go to the background home page. The route address is /home
      this.$router.push('/home')}}}Copy the code
  1. Write home. vue and import routing rules
import Home from '.. /components/Home.vue'
const routes = [  
  { path: '/home'.component: Home, }
]
Copy the code

3.4.12 Route Navguard Controls page access permissions

If the user is not logged in but accesses a specific page using a URL, he/she needs to navigate to the login page router/index.js again

// Mount the route navigator
router.beforeEach((to, from, next) = > {
  // to Specifies the path to be accessed
  // from indicates the path from which to jump
  Next () next() next('/login') force-jump
  if (to.path === '/login') return next() // Access the login page
  / / access token
  const tokenStr = window.sessionStorage.getItem('token')
  if(! tokenStr)return next('/login') // No token forced jump
  next() // Otherwise, access is allowed directly
})
Copy the code

3.5 Effect Display

3.6 Disabling the Function

3.6.1 Principle of Exiting the Function

Token-based exit is relatively simple. You only need to destroy the local token. In this way, subsequent requests do not carry the token and must be logged in again to generate a new token before the page can be accessed

3.6.2 Specific Operations

<el-button type="info" @click="logout">exit</el-button>
Copy the code
methods: {
  logout() {
    window.sessionStorage.clear()
    this.$router.push('/login')}}Copy the code

3.7 Login Complete code

<template>
  <div class="login_container">
    <div class="login_box">
      <! -- Avatar area -->
      <div class="avatar_box">
        <img src=".. /assets/YK.jpg" alt="" />
      </div>
      <! -- Login form area -->
      <el-form
        ref="loginFormRef"
        :model="loginForm"
        :rules="loginFormRules"
        label-width="0px"
        class="login_form"
      >
        <! -- User name -->
        <el-form-item prop="username">
          <el-input
            prefix-icon="el-icon-user"
            v-model="loginForm.username"
          ></el-input>
        </el-form-item>
        <! - password - >
        <el-form-item prop="password">
          <el-input
            prefix-icon="el-icon-lock"
            v-model="loginForm.password"
            type="password"
          ></el-input>
        </el-form-item>
        <! - button - >
        <el-form-item class="btns">
          <el-button type="primary" @click="login">The login</el-button>
          <el-button type="info" @click="resetLoginForm">reset</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      // This is the data binding object for the login form
      loginForm: {
        username: 'admin'.password: '123456'
      },
      // This is the form validation rule object
      loginFormRules: {
        // Verify that the username is valid
        username: [{required: true.message: 'Please enter login name'.trigger: 'blur' },
          { min: 3.max: 5.message: 'Between 3 and 5 characters long'.trigger: 'blur'}].// Verify that the password is valid
        password: [{required: true.message: 'Please enter your login password'.trigger: 'blur' },
          {
            min: 6.max: 15.message: 'Between 6 and 15 characters long'.trigger: 'blur'}]}}},methods: {
    // Click the reset button to reset the login form
    resetLoginForm () {
      // console.log(this) // VueComponent
      this.$refs.loginFormRef.resetFields()
    },
    login () {
      this.$refs.loginFormRef.validate(async (valid) => {
        // console.log(valid) // false/ture
        if(! valid)return
        const { data: result } = await this.$http.post('login'.this.loginForm)
        // console.log(result)
        if(result.meta.status ! = =200) return this.$message.error('Login failed')
        this.$message.success('Login successful')
        // Save the token in the client's sessionStorage (so not in loaclStorage)
        console.log(result)
        window.sessionStorage.setItem('token', result.data.token)
        // Go to the background home page via programmatic navigation. The route address is /home
        this.$router.push('/home')})}}}</script>

<style scoped>
.login_container {
  background-color: #409eff;
  height: 100%;
}

.login_box {
  width: 450px;
  height: 300px;
  background-color: #fff;
  border-radius: 3px;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

.avatar_box {
  height: 130px;
  width: 130px;
  border: 1px solid #eee;
  border-radius: 50%;
  padding: 10px;
  box-shadow: 0 0 10px #ddd;
  position: absolute;
  left: 50%;
  transform: translate(-50%, -50%);
  background-color: #fff;
}

img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background-color: #eee;
}

.login_form {
  position: absolute;
  bottom: 0;
  width: 100%;
  padding: 0 20px;
  box-sizing: border-box;
}

.btns {
  display: flex;
  justify-content: flex-end;
}
</style>

Copy the code

3.8 Submit local code to the code cloud Gitee

  1. Viewing file Status
git status
Copy the code
  1. Add code to the cache
git add .
Copy the code
  1. Commit to a local library
git commit -m "Login is complete"
Copy the code
  1. See the branch
git branch
Copy the code
  1. Switch the branch to the primary branch
git checkout master
Copy the code
  1. Merge the code for the Login branch on the main branch
git merge login
Copy the code
  1. Push to the code cloud
git push
Copy the code
  1. View the code cloud (only one master branch)

Gitee.com/ykang2020/v…

  1. Push the LOGIN branch to the code cloud as well
git checkout login
git push -u origin login
Copy the code

4. Home page layout

4.1 Overall Layout

<el-container class="home_container">
  <! -- Header area -->
  <el-header>Header</el-header>
  <! -- Page body area -->
  <el-container>
    <! -- Left sidebar -->
    <el-aside width="200px">Aside</el-aside>
    <! -- Right content body -->
    <el-main>Main</el-main>
  </el-container>
</el-container>
Copy the code
.home_container {
  height: 100%;
}
.el-header {
  background-color: #373d41;
}
.el-aside {
  background-color: # 333744;
}
.el-main {
  background-color: #eaedf1;
}
Copy the code

4.2 Beautify the home Header area

<! -- Header area -->
<el-header>
  <div>
    <img src=".. /assets/heima.png" alt="" />
    <span>E-commerce background management system</span>
  </div>
  <el-button type="info" @click="logout">exit</el-button>
</el-header>
Copy the code
.el-header {
  background-color: #373d41;
  display: flex;
  justify-content: space-between;
  padding-left: 0;
  align-items: center;
  color: #fff;
  font-size: 20px;
}
.el-header > div {
  display: flex;
  align-items: center;
}
.el-header > div > span {
  margin-left: 15px;
}
Copy the code

4.3 Realize the basic structure of the left navigation menu

Menus have two levels and can be folded

<! -- Sidebar menu area -->
<el-menu>
  <el-submenu>
    <! This template is the content template of the first level menu -->
    <i class="el-icon-menu"></i>
    <span>Level 1 menu</span>
    <! In the first menu, you can nest the second menu.
    <el-menu-item>
  	  <i class="el-icon-menu"></i>
  	  <span slot="title">The secondary menu</span>
  	</el-menu-item>
  </el-submenu>
</el-menu>
Copy the code

4.4 Adding token authentication through axiOS Interceptor

The API that requires Authorization must provide the token in the request header using the Authorization field

Token permission validation is required in the background except for the login interface. We can add token to ensure that we have access to data by adding an AXIos request interceptor. Add the code in main.js before mounting axios to the Vue prototype

// Request the interceptor to add the token through AXIos to guarantee the access to the data
axios.interceptors.request.use(config= > {
  // Add the Token authentication Authorization field for the request header object
  // console.log(config)
  config.headers.Authorization = window.sessionStorage.getItem('token')
  // You must return config at the end
  return config
})
Copy the code

4.5 Obtaining the menu data on the left

The data is retrieved before the home.vue page loads and the lifecycle function is defined

data() {
  return {
    // Left menu data
    menulist: []}},created() {
  this.getMenuList()
  this.activePath = window.sessionStorage.getItem('activePath')},methods: {
  // Get all the menus
  async getMenuList() {
    const { data: result } = await this.$http.get('menus')
    if(result.meta.status ! = =200) return this.$message.error(result.meta.msg)
    this.menulist = result.data
    console.log(result)
  }
}
Copy the code

4.6 Render the left menu through a double-layer for loop

<! -- Level 1 menu -->
  <el-submenu :index="item.id+''" v-for="item in menulist" :key="item.id">
    <! -- Template area of level 1 menu -->
    <template slot="title">
      <! -- -- -- > text
      <span>{{item.authName}}</span>
  </template>
  <! -- Level 2 menu -->
  <el-menu-item :index="subItem.id+''" v-for="subItem in item.children" :key="subItem.id">
    <! - - - >
    <i class="el-icon-menu"></i>
    <! -- -- -- > text
    <span>{{subItem.authName}}</span>
  </el-menu-item>
</el-submenu>
Copy the code

4.7 Set font colors for selected items and add category ICONS

You can change the active-text-color property of el-Menu to set the text color of the activation item clicked in the sidebar menu

<el-menu background-color="# 333744" text-color="#fff" active-text-color="RGB (64158255)">
Copy the code

The icon in the left menu bar can be set by changing the class name of the I tag in the menu item template

Add an iconsObj to the data, and then data bind the icon class name

iconsObj: {
  125: 'el-icon-s-custom'.103: 'el-icon-s-check'.101: 'el-icon-s-goods'.102: 'el-icon-s-order'.145: 'el-icon-s-marketing'
}
Copy the code

Level 1 menu icon bind iconsObj data:

<! - - - ><i :class="iconsObj[item.id]"></i>
Copy the code

4.8 You can open only one menu item at a time and resolve the border problem

In order to keep the left menu open one at a time and display its submenus, we can add a property in el-menu called unique-Opened or set it to data binding (true is considered a bool, not a string) : unique-Opened = “true”

<el-menu unique-opened>
Copy the code

Resolving border issues

.el-menu {
  border-right: none;
}
Copy the code

4.9 Achieve the effect of folding and expanding the sidebar

<! -- Left sidebar -->
<el-aside :width="isCollapse ? '64px':'200px'">
  <div class="toggle-button" @click="toggleCollapse">|||</div>
  <! -- Sidebar menu area -->
  <el-menu :collapse="isCollapse" :collapse-transition="false">
Copy the code
.toggle-button {
  background-color: #4A5064;
  font-size: 10px;
  line-height: 24px;
  color: rgb(144.147.153);
  text-align: center;
  letter-spacing: 0.2 em;
  cursor: pointer
}
Copy the code
// Click the button to toggle menu collapse and expansion
toggleCollapse() {
  this.isCollapse = !this.isCollapse
}
Copy the code

4.10 Redirection effect of home Page Routes

  1. Added sub-routing components components/ welcome.vue
  2. Import the sublevel routing component in router/index.js and set the routing rules and default redirection for sublevel routes
import Welcome from '.. /components/Welcome.vue'
const routes = [
  {
    path: '/home'.component: Home,
    redirect: '/welcome'.children: [{ path: '/welcome'.component: Welcome }]
  }
]
Copy the code
  1. Open home.vue and add a route placeholder to the main body structure
<! -- Right content body -->
<el-main>
  <! -- Route placeholder -->
  <router-view></router-view>
</el-main>
Copy the code

4.11 Realize the transformation of the sidebar routing link

  1. All secondary menus in the sidebar need to be transformed into sub-level routing links. We only need to set the router attribute of el-Menu to true. At this time, when we click the secondary menu, the route jump will be performed according to the index attribute of the menu, such as /110,
<el-menu router>
Copy the code
  1. It is not appropriate to use index ID as the jump path, we can rebind index :index=”/” + subitem.path”
<! -- Level 2 menu -->
<el-menu-item :index="'/'+subItem.path" v-for="subItem in item.children" :key="subItem.id">
Copy the code

4.12 Home complete code

<template>
  <el-container class="home_container">
    <! -- Header area -->
    <el-header>
      <div>
        <img src=".. /assets/heima.png" alt="" />
        <span>E-commerce background management system</span>
      </div>
      <el-button type="info" @click="logout">exit</el-button>
    </el-header>
    <! -- Page body area -->
    <el-container>
      <! -- Left sidebar -->
      <el-aside :width="isCollapse ? '64px':'200px'">
        <div class="toggle-button" @click="toggleCollapse">|||</div>
        <! -- Sidebar menu area -->
        <el-menu
          background-color="# 333744"
          text-color="#fff"
          active-text-color="RGB (64158255)"
          unique-opened
          :collapse="isCollapse"
          :collapse-transition="false"
          router
          :default-active="activePath"
        >
        <! -- Level 1 menu -->
          <el-submenu :index="item.id+''" v-for="item in menulist" :key="item.id">
            <! -- Template area of level 1 menu -->
            <template slot="title">
              <! - - - >
              <i :class="iconsObj[item.id]"></i>
              <! -- -- -- > text
              <span>{{item.authName}}</span>
            </template>
            <! -- Level 2 menu -->
            <el-menu-item :index="'/'+subItem.path" v-for="subItem in item.children" :key="subItem.id" @click="saveNavState('/'+subItem.path)">
              <! - - - >
              <i class="el-icon-menu"></i>
              <! -- -- -- > text
              <span>{{subItem.authName}}</span>
            </el-menu-item>
          </el-submenu>
        </el-menu>
      </el-aside>
      <! -- Right content body -->
      <el-main>
        <! -- Route placeholder -->
        <router-view></router-view>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
export default {
  data() {
    return {
      // Left menu data
      menulist: [].// Define a font icon object
      iconsObj: {
        125: 'el-icon-s-custom'.103: 'el-icon-s-check'.101: 'el-icon-s-goods'.102: 'el-icon-s-order'.145: 'el-icon-s-marketing'
      },
      isCollapse: false.// The address of the active link
      activePath: ' '}},created() {
    this.getMenuList()
    this.activePath = window.sessionStorage.getItem('activePath')},methods: {
    logout() {
      window.sessionStorage.clear()
      this.$router.push('/login')
      this.$message.warning('Logged out')},// Get all the menus
    async getMenuList() {
      const { data: result } = await this.$http.get('menus')
      if(result.meta.status ! = =200) return this.$message.error(result.meta.msg)
      this.menulist = result.data
      console.log(result)
    },
    // Click the button to toggle menu collapse and expansion
    toggleCollapse() {
      this.isCollapse = !this.isCollapse
    },
    // Save the active state of the link
    saveNavState(activePath) {
      window.sessionStorage.setItem('activePath', activePath)
      this.activePath = activePath
    }
  }
}
</script>

<style scoped>
.home_container {
  height: 100%;
}
.el-header {
  background-color: #373d41;
  display: flex;
  justify-content: space-between;
  padding-left: 0;
  align-items: center;
  color: #fff;
  font-size: 20px;
}
.el-header > div {
  display: flex;
  align-items: center;
}
.el-header > div > span {
  margin-left: 15px;
}
.el-aside {
  background-color: # 333744;
}
.toggle-button {
  background-color: #4A5064;
  font-size: 10px;
  line-height: 24px;
  color: rgb(144.147.153);
  text-align: center;
  letter-spacing: 0.2 em;
  cursor: pointer
}
.el-menu {
  border-right: none;
}

.el-main {
  background-color: #eaedf1;
}

</style>
Copy the code

5. Show

Project address https://gitee.com/ykang2020/vue_shop

Finally, please pay attention to my column and make good friends with YK bacteria