An article takes you to use vue to complete a complete background

introduce

vue-element-admin Is a background front-end solution based on vue and element-ui realization. It uses the latest front-end technology stack, built-in i18 internationalization solution, dynamic routing and permission verification, refines the typical business model and provides rich functional components. It can help you quickly build an enterprise level middle and back-end product prototype. I believe that no matter what your needs are, this project can help you

  • Vue element admin positioning is a background integration scheme, which is not suitable for the secondary development of the basic template. The project integrates many unused functions, resulting in code redundancy
  • Vue admin template is a background basic template. It is recommended to use this template for secondary development
  • Electronic Vue admin is a desktop terminal. This template can be used for desktop terminal development

function

- Sign in / cancellation

- Permission verification
  - Page permissions
  - Instruction authority
  - Permission configuration
  - Two step login

- Multi environment Publishing
  - dev sit stage prod

- Global function
  - International Multilingualism
  - Multiple dynamic skin changes
  - Nested sidebar (supports multi-level dynamic routing)
  - Dynamic bread crumbs
  - Quick navigation(Tab)
  - Svg Sprite Icon
  - local/back-end mock data
  - Screenfull Full screen
  - Adaptive shrink sidebar

- editor 
  - Rich text
  - Markdown
  - JSON Isometric format

- Excel
  - export excel
  - Import excel
  - Front end visualization excel
  - export zip

- form
  - Dynamic table
  - Drag table
  - Inline editing

- Error page
  - 401
  - 404

- Components
  - picture upload 
  - Back to top
  - Drag Dialog
  - Drag Select
  - Drag Kanban
  - List drag
  - SplitPane
  - Dropzone
  - Sticky
  - CountTo

- Comprehensive example
- Error log
- Dashboard
- Guide page
- ECharts Chart
- Clipboard(Clip copy)
- Markdown2html

directory structure

├── build                      # Build correlation
├── mock                       # mock simulation data of the project
├── plop-templates             # Basic template
├── public                     # Static resources
│   │── favicon.ico            # favicon Icon
│   └── index.html             # html template 
├── src                        # source code
│   ├── api                    # All requests
│   ├── assets                 # Static resources such as theme fonts
│   ├── components             # Global common component
│   ├── directive              # Global instruction
│   ├── filters                # Global filter
│   ├── icons                  # All svg icons of the project
│   ├── lang                   # International language
│   ├── layout                 # Global layout
│   ├── router                 # route
│   ├── store                  # Global store management
│   ├── styles                 # Global style
│   ├── utils                  # Global common method
│   ├── vendor                 # Public vendor
│   ├── views                  # views all pages
│   ├── App.vue                # Entry page
│   ├── main.js                # Import file loading, component initialization, etc
│   └── permission.js          # Authority management
├── tests                      # test
├── .env.xxx                   # Environment variable configuration
├── .eslintrc.js               # eslint configuration item
├── .babelrc                   # Babel loader configuration
├── .travis.yml                # Automated CI configuration
├── vue.config.js              # Vue cli configuration
├── postcss.config.js          # postcss configuration
└── package.json               # package.json

install

# Clone project
git clone https://github.com/PanJiaChen/vue-element-admin.git

# Enter project directory
cd vue-element-admin

# Installation dependency
npm install

# If the speed is too slow, you can specify the following method to download the original image
# You can also use nrm to choose to download the original image
# It is recommended not to install with cnpm. There will be various strange bug s. You can solve the problem of slow download speed of npm through the following operations
npm install --registry=https://registry.npm.taobao.org

# Note: the startup of this framework is different from our usual settings. We should use the following methods to start it
# Local development start-up project
npm run dev

After startup, the browser will be opened automatically http://localhost:9527 , you can see the page to prove that your operation is successful

Layout layout

Most pages are based on layout, which is not used except 404 and login

layout integrates all layouts of the page for block display

The whole plate is divided into three parts

Layout main layout

Src directory

Entry file main js


It is useful to access the customized mock file. We need to comment it out
It is suggested to delete the mock folder under src, which will not be used later

App.vue

src, except main JS also has two files, permission JS and settings js

permission.js

permission.js is a file that controls the login permission of the page. We can comment it out first and add it slowly later

settings.js

settings.js refers to the configuration of some project information. There are three attributes * * title (project name), fixedHeader (fixed header) and sidebarLogo (display the left menu logo)
We will use the configuration in other places. Don't move it

Introduction to API module and request encapsulation module

Separate request of API module and encapsulation of request module

Axios interceptor

axios interceptor principle:

Create a new axios instance through create

// A new axios instance was created
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 5000 // request timeout
})

request interceptor
It mainly deals with token_ Unified injection problem_

service.interceptors.request.use(
  config => {
    if (store.getters.token) {
      config.headers['X-Token'] = getToken()
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

Response interceptor
Handle returned data and exceptions_ Data structure_ problem

	// Response interceptor
service.interceptors.response.use(
  response => {
    const res = response.data
    // if the custom code is not 20000, it is judged as an error.
    // The return value of user-defined code is negotiated and written according to their own needs
    if (res.code !== 20000) {
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      })
          // The return value of user-defined code is negotiated and written according to their own needs
      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
        // to re-login
        MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
          confirmButtonText: 'Re-Login',
          cancelButtonText: 'Cancel',
          type: 'warning'
        }).then(() => {
          store.dispatch('user/resetToken').then(() => {
            location.reload()
          })
        })
      }
      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return res
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

The above is in Src / utils / request JS source code
We just need to keep:

// Export an instance of axios, and this instance should have a request interceptor and a response interceptor
import axios from 'axios'
const service = axios.create() // Create an instance of axios
service.interceptors.request.use() // request interceptor 
service.interceptors.response.use() // Response interceptor
export default service // Export axios instance

Encapsulate api separately

We are used to putting all api requests under the api directory for unified management and using them according to modules

api/user.js

import request from '@/utils/request'

export function login(data) {
  return request({
    url: '/vue-admin-template/user/login',
    method: 'post',
    data
  })
}

export function getInfo(token) {
  return request({
    url: '/vue-admin-template/user/info',
    method: 'get',
    params: { token }
  })
}

export function logout() {
  return request({
    url: '/vue-admin-template/user/logout',
    method: 'post'
  })
}

We just need to keep the following code and add it later

import request from '@/utils/request'

export function login(data) {

}

export function getInfo(token) {

}

export function logout() {

}

Login module

Set fixed local access port and website name

Set a unified local access port and website title

Local service port: in Vue config. JS

vue.config.js is the configuration file related to compiling, configuring, packaging and starting services of Vue project. Its core lies in webpack, but it is different from webpack, which is equivalent to the improved version of webpack

We can see that the above is an environment variable instead of the actual address, so where did we set it

Under the project, we will find two files

Development = > development environment

Production = > production environment

When we run npm run dev for development and debugging, we will load and execute * * env.development * * file content

When the production environment is packaged, we will execute * * run: Build: prod env.production * * file content

If you want to set the interface of the development environment, go directly to * * env. Write in the development * * file and directly assign values to variables

# just a flag
ENV = 'development'

# base api
VUE_APP_BASE_API = 'api/private/v1/'

If you want to set the interface of the production environment * * env. Write in the production * * file and assign a value to the variable directly

# just a flag
ENV = 'production'

# base api
VUE_APP_BASE_API = 'api/private/v1/'

Website name

src/settings.js
title is the name of the website

After configuration, we need to restart, otherwise some configurations will not take effect

Login page


Set header name:

<!-- Place Title picture @Is the alias of the setting-->
<div class="title-container">
        <h3 class="title">Dolphin e-commerce background management platform</h3>
 </div>

Set background picture:
It can be changed as required

/* reset element-ui css */
.login-container {
  background-image: url('~@/assets/common/bgc.jpg'); // Set background picture 
  background-position: center; // Set the picture position to fill the entire screen
}

Corresponding code:

Verification of login form

Conditions of El form verification

Verification of user name and password:

<el-form-item prop="username">
        <span class="svg-container">
          <svg-icon icon-class="user" />
        </span>
        <el-input
          ref="username"
          v-model="loginForm.username"
          v-focus
          placeholder="Username"
          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="Password"
          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>


const validateUsername = (rule, value, callback) => {
      if (value.length < 5) {
        callback(new Error('The user name must have at least 5 characters'))
      } else if (value.length > 12) {
        callback(new Error('The maximum length of user name is 12 characters'))
      } else {
        callback()
      }
    }
    const validatePassword = (rule, value, callback) => {
      if (value.length < 5) {
        callback(new Error('The user name must have at least 5 characters'))
      } else if (value.length > 16) {
        callback(new Error('The maximum length of user name is 16 bits'))
      } else {
        callback()
      }
    }

loginRules: {
        username: [{ required: true, trigger: 'blur', validator: validateUsername }],
        password: [
          { required: true, trigger: 'blur', validator: validatePassword },
          { min: 5, max: 12, trigger: 'blur', message: 'The password length should be 5-12 Between bits' }
        ]
      }

Vue cli configuring cross domain agents

What are the reasons for cross domain?
At present, it is popular to separate the front and back ends and develop them separately. The front-end project and the back-end interface are not under the same domain name, so the front-end access to the back-end interface appears cross domain
So the problem comes, how to solve it?
The cross domain we encounter is located in the development environment. When we deploy online, the cross domain is the production environment, and the solutions are different
Let's solve the development environment first. The production environment can be solved when it is packaged and launched. We'll talk about it later

Solve the cross domain problem of development environment

The cross domain of the development environment, that is, the cross domain problems encountered by our access interface when developing and starting services in the Vue cli scaffolding environment. Vue cli opens a service locally for us, which can help us proxy requests and solve cross domain problems
That is, Vue cli configures the reverse proxy of webpack

In Vue config. JS for reverse proxy configuration

module.exports = {
  devServer: {
   proxy: {
      'api/private/v1/': {
        target: 'http://127.0.0.1:8888 '/ / when the address we want to proxy matches' api/private/v1 /' above, it will http://localhost:9528 Replace with http://127.0.0.1:8888
        changeOrigin: true, // Is it necessary to set this value to true before the local service agent can send requests to us
        pathRewrite: {
        // Reroute localhost: 8888 / API / login = > http://127.0.0.1:8888/api/login
          '^/api': '/api',
          '/hr': ''
        }
      }
    }
  }
}

At the same time, it should also be noted that we need to comment out the loading of mock, because mock server will lead to the exception of proxy service

// Before: require ('. / mock / mock server. JS'), / / comment: mock server loading

Encapsulate a separate login interface

export function login(data) {
  // An axios object = > promise / / a promise object is returned
  return request({
    url: 'login', // Because all interfaces should be cross domain, all interfaces should have / api
    method: 'post',
    data
  })
}

Encapsulate the login Action of Vuex and handle the token

Manage token s in Vuex


In the figure above, the components directly interact with the interface, which is no problem, but ta uses the key to transfer each other. We need to let vuex intervene to share the user's token state for easier reading

store/modules/user.js configuration

// state
const state = {}
// modify state
const mutations = {}
// Execute asynchronously
const actions = {}
export default {
  namespaced: true,
  state,
  mutations,
  actions
}

Set token sharing status

const state = {
  token: null
}

Operation token

utils/auth.js, the basic template has provided us with methods to obtain, set and delete token s, which can be used directly

const TokenKey = 'haitun_token'

export function getToken() {
  // return Cookies.get(TokenKey)
  return localStorage.getItem(TokenKey)
}

export function setToken(token) {
  // return Cookies.set(TokenKey, token)
  return localStorage.setItem(TokenKey, token)
}

export function removeToken() {
  // return Cookies.remove(TokenKey)
  return localStorage.removeItem(TokenKey)
}

Initialize token status

store/modules/user.js

import { getToken, setToken, removeToken } from '@/utils/auth'
const state = {
  token: getToken() // Set token initial state token persistence = > put into cache
}

Provide changes to modify token

// modify state
const mutations = {
  // Set token
  setToken(state, token) {
    state.token = token // Setting token only modifies the data of state 123 = "1234"
    setToken(token) // Synchronization of vuex and cached data
  },
  // Delete cache
  removeToken(state) {
    state.token = null // Delete token of vuex
    removeToken() // Clear vuex first, and then clear the synchronization of cached vuex and cached data
  }
}

Encapsulate login Action

To do the login action, call the login interface, set the token to vuex after success, and return failure if failure

// Execute asynchronously
const actions = {
  // Defining login action also requires parameters, which are passed when calling action
  async login(context, data) {
    const result = await login(data)  // In fact, it is a promise result, which is the result of execution
    // axios adds a layer of data to the data by default
    if (result.data.success) {
      // Indicates that the login interface call is successful, which means that your user name and password are correct
      // User token now
      // actions to modify state, you must pass mutations
      context.commit('setToken', result.data.data)
    }
  }
}

In order to better enable other modules and components to better obtain token data, we need to store / getters JS takes the token value as a public access attribute

const getters = {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device,
  token: state => state.user.token // Open the attributes of sub modules on the getters at the root level to show others for use
}
export default getters

Through this content, we can have a brain picture

Distinguish the request base address of axios in different environments


The two front-end environments are mainly divided into development environment and production environment

Environment variable $process env. NODE_ Env # is the production environment when it is production and the development environment when it is development
We can do it in * * env.development and env.production * * defines a variable, which is automatically the value of the current environment
The basic template defines the variable Vue in the above file_ APP_ BASE_ API, which can be used as the baseURL of axios request

# Base address and proxy correspondence of development environment
VUE_APP_BASE_API = '/api'

---------

# The configuration of / api here means that the address of / prod api corresponding to nginx reverse proxy needs to be configured for the service on the nginx server 
VUE_APP_BASE_API = '/prod-api'  

It can also be written in the same way to facilitate management

Set baseUrl - benchmark in request

// Create an instance of axios
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // Set the base address of the axios request
  timeout: 5000 // Define 5-second timeout
}) 

Response interceptor for handling axios

// Response interceptor
service.interceptors.response.use(response => {
  // axios adds a layer of data by default
  const { success, message, data } = response.data
  //   The following operations should be determined according to the success
  if (success) {
    return data
  } else {
    // Business has been wrong, can you enter then? No! You should catch
    Message.error(message) // Prompt error message
    return Promise.reject(new Error(message))
  }
}, error => {
  Message.error(error.message) // Prompt error message
  return Promise.reject(error) // Return the execution error to make the current execution chain jump out successfully and directly enter the catch
})

The login page calls the login action to handle exceptions


Introducing auxiliary function

import { mapActions } from 'vuex'  // Introducing auxiliary function of vuex
---------------------
methods: {
    ...mapActions(['user/login'])
}

Call login

  this.$refs.loginForm.validate(async isOK => {
        if (isOK) {
          try {
            this.loading = true
            // Only when the verification passes can we call action
            await this['user/login'](this.loginForm)
            // You should log in successfully
             // After successful login, jump to the home page
            this.$router.push('/')
          } catch (error) {
            console.log(error)
          } finally {
            //  Whether you execute try or catch, turn off the turn
            this.loading = false
          }
        }
      })

analysis

First, use the from form of elementUI to write

In the middle, form verification is used at the front desk to compare the account passwords entered by the user to see whether they meet the standards. If they do not meet the standards defined by us, a prompt will be given

We do two-way data binding to the input box in the form and use v-model

When the user clicks the login button after inputting, background verification is also required. When we click login, we send a request to the background to check whether the account password is correct. If it is incorrect, a prompt will pop up

The < SVG > tag is used in the form to introduce the icon icon

We first created the SvgIcon component under srccomponents

We exposed two properties

Monitor the icon name and its custom style through computed. When no custom style is specified, the default style will be adopted, otherwise the custom class will be added

 iconName() {
      return `#icon-${this.iconClass}`
    },
    svgClass() {
      if (this.className) {
        return 'svg-icon ' + this.className
      } else {
        return 'svg-icon'
      }
    }

Then write the default style

Index in srcicons import IconSvg from '@/components/IconSvg'

Register icon SVG Vue using global component('icon-svg', IconSvg)

This can be used anywhere in the project

To facilitate centralized icon management, all icons are placed in @ / icons/svg

@Represents finding the src directory

require.context has three parameters:

  • Parameter 1: indicates the directory to be retrieved
  • Parameter 2: retrieve subdirectory
  • Parameter 3: regular expression of matching file

At @ / main import '@/icons' is introduced into JS so that components can be successfully used on any page

You can use it in the page

<svg-icon icon-class="password" class-name="password" />

Complete code

<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">Dolphin e-commerce background management platform</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"
          v-focus
          placeholder="Username"
          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="Password"
          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"
        >Log in now</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 (value.length < 5) {
        callback(new Error('The user name must have at least 5 characters'))
      } else if (value.length > 12) {
        callback(new Error('The maximum length of user name is 12 characters'))
      } else {
        callback()
      }
    }
    const validatePassword = (rule, value, callback) => {
      if (value.length < 5) {
        callback(new Error('The user name must have at least 5 characters'))
      } else if (value.length > 16) {
        callback(new Error('The maximum length of user name is 16 bits'))
      } else {
        callback()
      }
    }
    return {
      loginForm: {
        username: 'admin',
        password: '123456'
      },
      loginRules: {
        username: [{ required: true, trigger: 'blur', validator: validateUsername }],
        password: [
          { required: true, trigger: 'blur', validator: validatePassword },
          { min: 5, max: 12, trigger: 'blur', message: 'The password length should be 5-12 Between bits' }
        ]
      },
      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()
      })
    },
    async handleLogin () {
      try {
        await this.$refs.loginForm.validate()
        this.loading = true
        await this.$store.dispatch('user/login', this.loginForm)
        // console.log('ssss')
        // After successful login, jump to the home page
        this.$router.push({ path: '/' })
        this.loading = false
      } catch (err) {
        this.loading = false
        console.log(err)
        return false
      }
    }
  }
}
</script>

<style lang="scss">
/* Fix input background disharmony and cursor discoloration */
/* 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;

  .login-form {
    position: relative;
    width: 520px;
    max-width: 100%;
    padding: 160px 35px 0;
    margin: 0 auto;
    overflow: hidden;
  }

  .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>

Home module

The home page token is intercepted and processed

Flow chart of permission interception

We have completed the login process and stored the token, but the home page is not controlled by the presence or absence of the token

Interception processing code

src/permission.js

import Vue from 'vue'

import 'normalize.css/normalize.css' // A modern alternative to CSS resets

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import locale from 'element-ui/lib/locale/lang/zh-CN' // lang i18n

import '@/styles/index.scss' // global css

import App from './App'
import store from './store'
import router from './router'
import i18n from '@/lang/index'
import '@/icons' // icon
import '@/permission' // permission control
import directives from './directives'
import Commponent from '@/components'
import filters from './filter'
import Print from 'vue-print-nb' // Import printing

// set ElementUI lang to EN
Vue.use(ElementUI, { locale })

// If you want the Chinese version of element UI, declare it as follows
// Vue.use(ElementUI)
Vue.use(Print)
Vue.config.productionTip = false
// Traversal registration custom instruction
for (const key in directives) {
  Vue.directive(key, directives[key])
}
Vue.use(Commponent) // Register your own plug-in
// Register global filters
// Traverse registration filter
for (const key in filters) {
  Vue.filter(key, filters[key])
}
// Set element to the current language
Vue.use(ElementUI, {
  i18n: (key, value) => i18n.t(key)
})
new Vue({
  el: '#app',
  router,
  store,
  i18n,
  render: h => h(App)
})

Left Navigation

Style file styles / siderbar scss
Set background picture

.scrollbar-wrapper { 
    background: url('~@/assets/common/leftnavBg.png') no-repeat 0 100%;
}

Left logo image Src / settings js

module.exports = {

  title: 'Dolphin e-commerce background management platform',

  /**
   * @type {boolean} true | false
   * @description Whether fix the header
   */
  fixedHeader: false,

  /**
   * @type {boolean} true | false
   * @description Whether show the logo in sidebar
   */
  sidebarLogo: true   // Display logo
}

Set header picture structure Src / layout / components / sidebar / logo vue

<div class="sidebar-logo-container" :class="{ collapse: collapse }">
    <transition name="sidebarLogoFade">
      <router-link
        v-if="collapse"
        key="collapse"
        class="sidebar-logo-link"
        to="/"
      >
        <img v-if="logo" src="@/assets/common/hai.png" class="sidebar-logo" />
        <h1 v-else class="sidebar-title">{{ title }}</h1>
      </router-link>
      <router-link v-else key="expand" class="sidebar-logo-link" to="/">
        <img v-if="logo" src="@/assets/common/hai.png" class="sidebar-logo" />
        <h1 class="sidebar-title">{{ title }}</h1>
      </router-link>
    </transition>
  </div>

Complete code

<template>
  <div class="sidebar-logo-container" :class="{ collapse: collapse }">
    <transition name="sidebarLogoFade">
      <router-link
        v-if="collapse"
        key="collapse"
        class="sidebar-logo-link"
        to="/"
      >
        <img v-if="logo" src="@/assets/common/hai.png" class="sidebar-logo" />
        <h1 v-else class="sidebar-title">{{ title }}</h1>
      </router-link>
      <router-link v-else key="expand" class="sidebar-logo-link" to="/">
        <img v-if="logo" src="@/assets/common/hai.png" class="sidebar-logo" />
        <h1 class="sidebar-title">{{ title }}</h1>
      </router-link>
    </transition>
  </div>
</template>

<script>
export default {
  name: 'SidebarLogo',
  props: {
    collapse: {
      type: Boolean,
      required: true
    }
  },
  data () {
    return {
      title: 'Dolphin e-commerce background management platform',
      logo: '@/assets/common/hai.png'
    }
  }
}
</script>

<style lang="scss" scoped>
.sidebarLogoFade-enter-active {
  transition: opacity 1.5s;
}

.sidebarLogoFade-enter,
.sidebarLogoFade-leave-to {
  opacity: 0;
}

.sidebar-logo-container {
  position: relative;
  width: 100%;
  height: 50px;
  line-height: 50px;
  background: #2b2f3a;
  text-align: center;
  overflow: hidden;

  & .sidebar-logo-link {
    height: 100%;
    width: 100%;

    & .sidebar-logo {
      width: 32px;
      height: 32px;
      vertical-align: middle;
      margin-right: 12px;
    }

    & .sidebar-title {
      display: inline-block;
      margin: 0;
      color: #fff;
      font-weight: 600;
      line-height: 50px;
      font-size: 14px;
      font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
      vertical-align: middle;
    }
  }

  &.collapse {
    .sidebar-logo {
      margin-right: 0px;
    }
  }
}
</style>

Layout and style of header content

Head assembly location layout / components / navbar vue

Breadcrumbs when adding company names

<!-- <breadcrumb class="breadcrumb-container" /> -->   <!--crumbs-->
    <div class="app-breadcrumb">
      Beijing dreamy Network Co., Ltd
      <span class="breadBtn">v1.0.0</span>
    </div>

Right side avatar and drop-down menu settings

<div class="right-menu">
      <!-- Language switching plug-in -->
      <lang class="right-menu-item lang_item" />
      <!-- Full screen plug-in -->
      <screen-full class="right-menu-item" />
      <!-- Dynamic theme plugin -->
      <theme-picker class="right-menu-item" />
      <el-dropdown class="avatar-container" trigger="click">
        <div class="avatar-wrapper">
          <img
            v-imgerr="defaultImg"
            src="https://bing.ioliu.cn/v1/rand?w=100&h=100"
            class="user-avatar"
          />
          <span class="name">{{ username }}</span>

          <i class="el-icon-caret-bottom" />
        </div>
        <el-dropdown-menu slot="dropdown" class="user-dropdown">
          <router-link to="/">
            <el-dropdown-item> homepage </el-dropdown-item>
          </router-link>
          <a href="javascript:;">
            <el-dropdown-item>mailbox</el-dropdown-item>
          </a>
          <a href="javascript:;">
            <el-dropdown-item>set up</el-dropdown-item>
          </a>
          <el-dropdown-item @click.native="logout">
            <span style="display: block">sign out</span>
          </el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>

Full code: style + Event

<template>
  <div class="navbar">
    <hamburger
      :is-active="sidebar.opened"
      class="hamburger-container"
      @toggleClick="toggleSideBar"
    />
    <!-- <breadcrumb class="breadcrumb-container" /> -->
    <div class="app-breadcrumb">
      Beijing dreamy Network Co., Ltd
      <span class="breadBtn">v1.0.0</span>
    </div>
    <div class="right-menu">
      <!-- Language switching plug-in -->
      <lang class="right-menu-item lang_item" />
      <!-- Full screen plug-in -->
      <screen-full class="right-menu-item" />
      <!-- Dynamic theme plugin -->
      <theme-picker class="right-menu-item" />
      <el-dropdown class="avatar-container" trigger="click">
        <div class="avatar-wrapper">
          <img
            v-imgerr="defaultImg"
            src="https://bing.ioliu.cn/v1/rand?w=100&h=100"
            class="user-avatar"
          />
          <span class="name">{{ username }}</span>

          <i class="el-icon-caret-bottom" />
        </div>
        <el-dropdown-menu slot="dropdown" class="user-dropdown">
          <router-link to="/">
            <el-dropdown-item> homepage </el-dropdown-item>
          </router-link>
          <a href="javascript:;">
            <el-dropdown-item>mailbox</el-dropdown-item>
          </a>
          <a href="javascript:;">
            <el-dropdown-item>set up</el-dropdown-item>
          </a>
          <el-dropdown-item @click.native="logout">
            <span style="display: block">sign out</span>
          </el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
export default {
  components: {
    Breadcrumb,
    Hamburger
  },
  data () {
    return {
      username: 'Super administrator',
      defaultImg: require('@/assets/common/bigUserHeader.png')
    }
  },
  created () {
    this.usereee()
  },
  computed: {
    ...mapGetters([
      'sidebar',
      'avatar'
    ])
  },
  methods: {
    usereee () {
      const res = localStorage.getItem('haitunuser')
      // const res = sessionStorage.getItem('user_info')
      const username = JSON.parse(res).username
      this.username = username
    },
    toggleSideBar () {
      this.$store.dispatch('app/toggleSideBar')
    },
    async logout () {
      await this.$store.dispatch('user/logout')
      this.$router.push(`/login`)
    }
  }
}
</script>

<style lang="scss" scoped>
.navbar {
  height: 50px;
  overflow: hidden;
  position: relative;
  background-image: linear-gradient(left, #3d6df8, #5b8cff);
  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
  .app-breadcrumb {
    display: inline-block;
    font-size: 18px;
    line-height: 50px;
    margin-left: 15px;
    color: #fff;
    cursor: text;
    .breadBtn {
      background: #84a9fe;
      font-size: 14px;
      padding: 0 10px;
      display: inline-block;
      height: 30px;
      line-height: 30px;
      border-radius: 10px;
      margin-left: 15px;
    }
  }
  .hamburger-container {
    line-height: 46px;
    height: 100%;
    float: left;
    cursor: pointer;
    transition: background 0.3s;
    -webkit-tap-highlight-color: transparent;

    &:hover {
      background: rgba(0, 0, 0, 0.025);
    }
  }

  .breadcrumb-container {
    float: left;
  }

  .right-menu {
    float: right;
    height: 100%;
    line-height: 50px;

    &:focus {
      outline: none;
    }

    .right-menu-item {
      display: inline-block;
      vertical-align: middle;
      padding: 0 8px;
      height: 100%;
      font-size: 18px;
      color: #5a5e66;
      vertical-align: text-bottom;

      &.hover-effect {
        cursor: pointer;
        transition: background 0.3s;

        &:hover {
          background: rgba(0, 0, 0, 0.025);
        }
      }
    }

    .avatar-container {
      margin-right: 30px;

      .avatar-wrapper {
        display: flex;
        margin-top: 5px;
        position: relative;
        .user-avatar {
          cursor: pointer;
          width: 40px;
          height: 40px;
          border-radius: 10px;
          vertical-align: middle;
          margin-bottom: 10px;
        }
        .name {
          color: #fff;
          vertical-align: middle;
          margin-left: 5px;
        }
        .user-dropdown {
          color: #fff;
        }
        .el-icon-caret-bottom {
          cursor: pointer;
          position: absolute;
          right: -20px;
          top: 25px;
          font-size: 12px;
        }
      }
    }
  }
}
.lang_item {
  // background-color: aqua;
}
</style>

Store user information

New variable: Src / store / modules / user js

const getDefaultState = () => {
  return {
    token: getToken(),
    userInfo: {}, // Store user information
  }
}

Setting and deleting user profile changes

// Set user information
 set_userInfo (state, user) {
    state.userInfo = user
    setUSERINFO(user)
  }
    // Delete user information
 removeUserInfo (state) {
    this.userInfo = {}
  }

Establish the mapping of user name Src / store / getters js

const getters = {  
  token: state => state.user.token,
  username: state => state.user.userInfo.username
}
export default getters

Finally, we can change to the real name

<div class="avatar-wrapper">
    <img src="@/assets/common/bigUserHeader.png" class="user-avatar" />
    <span class="name">{{ username }}</span>
    <i class="el-icon-caret-bottom" style="color: #fff" />
</div>

There may be a problem here. We can't get the data when the page is refreshed. We can save it locally and then take it out

Realize exit function


Exit: Src / store / modules / user js

 // user logout
  logout (context) {
    // Delete token
    context.commit('removeToken') // Not only in vuex, but also in cache
    // Delete user data
    context.commit('removeUserInfo') // Delete user information
  },

mutation

 removeToken (state) {
    state.token = null
    removeToken()
    removeUSERINFO()
    removeLocalMenus()
  },
  removeUserInfo (state) {
    this.userInfo = {}
  },

The header menu calls Src / layout / components / navbar vue

  async logout () {
      await this.$store.dispatch('user/logout')
      this.$router.push(`/login`)
    }

Full code:

<template>
  <div class="navbar">
    <hamburger
      :is-active="sidebar.opened"
      class="hamburger-container"
      @toggleClick="toggleSideBar"
    />
    <!-- <breadcrumb class="breadcrumb-container" /> -->
    <div class="app-breadcrumb">
      Beijing dreamy Network Co., Ltd
      <span class="breadBtn">v1.0.0</span>
    </div>
    <div class="right-menu">
      <!-- Language switching plug-in -->
      <lang class="right-menu-item lang_item" />
      <!-- Full screen plug-in -->
      <screen-full class="right-menu-item" />
      <!-- Dynamic theme plugin -->
      <theme-picker class="right-menu-item" />
      <el-dropdown class="avatar-container" trigger="click">
        <div class="avatar-wrapper">
          <img
            v-imgerr="defaultImg"
            src="https://bing.ioliu.cn/v1/rand?w=100&h=100"
            class="user-avatar"
          />
          <span class="name">{{ username }}</span>

          <i class="el-icon-caret-bottom" />
        </div>
        <el-dropdown-menu slot="dropdown" class="user-dropdown">
          <router-link to="/">
            <el-dropdown-item> homepage </el-dropdown-item>
          </router-link>
          <a href="javascript:;">
            <el-dropdown-item>mailbox</el-dropdown-item>
          </a>
          <a href="javascript:;">
            <el-dropdown-item>set up</el-dropdown-item>
          </a>
          <el-dropdown-item @click.native="logout">
            <span style="display: block">sign out</span>
          </el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
export default {
  components: {
    Breadcrumb,
    Hamburger
  },
  data () {
    return {
      username: 'Super administrator',
      defaultImg: require('@/assets/common/bigUserHeader.png')
    }
  },
  created () {
    this.usereee()
  },
  computed: {
    ...mapGetters([
      'sidebar',
      'avatar'
    ])
  },
  methods: {
    usereee () {
      const res = localStorage.getItem('haitunuser')
      // const res = sessionStorage.getItem('user_info')
      const username = JSON.parse(res).username
      this.username = username
    },
    toggleSideBar () {
      this.$store.dispatch('app/toggleSideBar')
    },
    async logout () {
      await this.$store.dispatch('user/logout')
      this.$router.push(`/login`)
    }
  }
}
</script>

<style lang="scss" scoped>
.navbar {
  height: 50px;
  overflow: hidden;
  position: relative;
  background-image: linear-gradient(left, #3d6df8, #5b8cff);
  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
  .app-breadcrumb {
    display: inline-block;
    font-size: 18px;
    line-height: 50px;
    margin-left: 15px;
    color: #fff;
    cursor: text;
    .breadBtn {
      background: #84a9fe;
      font-size: 14px;
      padding: 0 10px;
      display: inline-block;
      height: 30px;
      line-height: 30px;
      border-radius: 10px;
      margin-left: 15px;
    }
  }
  .hamburger-container {
    line-height: 46px;
    height: 100%;
    float: left;
    cursor: pointer;
    transition: background 0.3s;
    -webkit-tap-highlight-color: transparent;

    &:hover {
      background: rgba(0, 0, 0, 0.025);
    }
  }

  .breadcrumb-container {
    float: left;
  }

  .right-menu {
    float: right;
    height: 100%;
    line-height: 50px;

    &:focus {
      outline: none;
    }

    .right-menu-item {
      display: inline-block;
      vertical-align: middle;
      padding: 0 8px;
      height: 100%;
      font-size: 18px;
      color: #5a5e66;
      vertical-align: text-bottom;

      &.hover-effect {
        cursor: pointer;
        transition: background 0.3s;

        &:hover {
          background: rgba(0, 0, 0, 0.025);
        }
      }
    }

    .avatar-container {
      margin-right: 30px;

      .avatar-wrapper {
        display: flex;
        margin-top: 5px;
        position: relative;
        .user-avatar {
          cursor: pointer;
          width: 40px;
          height: 40px;
          border-radius: 10px;
          vertical-align: middle;
          margin-bottom: 10px;
        }
        .name {
          color: #fff;
          vertical-align: middle;
          margin-left: 5px;
        }
        .user-dropdown {
          color: #fff;
        }
        .el-icon-caret-bottom {
          cursor: pointer;
          position: absolute;
          right: -20px;
          top: 25px;
          font-size: 12px;
        }
      }
    }
  }
}
.lang_item {
  // background-color: aqua;
}
</style>

token failure intervention


src/utils/auth.js

const timeKey = 'haitun-setTimeStamp' // Set a unique key
// Store the timestamp of the token (save the execution time of the setToken method)
// Get timestamp
export function setTimeStamp () {
  return localStorage.setItem(timeKey, Date.now())
}
// Get the expiration time of the token
export function getTimeStamp () {
  return localStorage.getItem(timeKey)
}

src/utils/request.js

import axios from 'axios'
import { Message } from 'element-ui'
import store from '@/store'
import router from '../router'
import { getToken, getTimeStamp, removeToken } from '@/utils/auth'

// Define token timeout
const timeOut = 3600 * 24 * 3
// create an axios instance
const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  timeout: 5000 // request timeout
})

// request interceptor
service.interceptors.request.use(
  // Inject token
  config => {
    // do something before request is sent

    if (store.getters.token) {
      // Judge whether the timestamp of the current token has expired
      // Get the time set by token
      const tokenTime = getTimeStamp()
      // Get current time
      const currenTime = Date.now()
      if ((currenTime - tokenTime) / 1000 > timeOut) {
        // If it is true, it means it has expired
        // The token is useless because it timed out
        store.dispatch('user/logout') // Logout operation
        // Jump to login page
        router.push('/login')
        return Promise.reject(new Error('Login expired, please login again'))
      }
      config.headers['Authorization'] = getToken()
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

// response interceptor
service.interceptors.response.use(
  response => {
    const { meta: { status, msg }, data } = response.data
    // if the custom code is not 20000, it is judged as an error.
    if (status !== 200 && status !== 201) {
      // Handle token expiration
      if (status === 400 && msg === 'invalid token') {
        removeToken()
        store.dispatch('user/logout')
        router.push('login')
      }
      Message({
        message: msg || 'Error',
        type: 'error',
        duration: 5 * 1000
      })
      return Promise.reject(new Error(msg || 'Error'))
    } else {
      return data
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service

When logging in, if the login is successful, we should set the timestamp
src/store/modules

async login (context, userInfo) {
    const { username, password } = userInfo
    const res = await login({ username: username.trim(), password: password })
    // Set user information
    const token = res.token
    context.commit('set_token', token)
    context.commit('set_userInfo', res)
    // Set user permission information
    const permission = await getMenus()
    const menus = filterPermission(permission)
    context.commit('set_menus', menus)
  },

token invalidation handling

src/utils/request.js

response => {
    const { meta: { status, msg }, data } = response.data
    // if the custom code is not 20000, it is judged as an error.
    if (status !== 200 && status !== 201) {
      // Handle token expiration
      if (status === 400 && msg === 'invalid token') {
        removeToken()
        store.dispatch('user/logout')
        router.push('login')
      }
      Message({
        message: msg || 'Error',
        type: 'error',
        duration: 5 * 1000
      })
      return Promise.reject(new Error(msg || 'Error'))
    } else {
      return data
    }

Routing, page, user management, permission management, etc. you can develop your own pages. The steps are similar

Multi language switching, tab page full screen

Full screen plug-in reference

Install the global plug-in screenfull

npm i screenfull

Encapsulate the full screen plug-in Src / components / screenfull / index vue

<template>
  <!-- Place an icon -->
  <div>
    <!-- Place a svg Icon for -->
    <svg-icon
      icon-class="fullscreen"
      style="color: #fff; width: 20px; height: 20px"
      @click="changeScreen"
    />
    <!-- <i class="el-icon-rank" @click="changeScreen" /> -->
  </div>
</template>

<script>
import ScreenFull from 'screenfull'
export default {
  methods: {
    //   Change full screen
    changeScreen () {
      if (!ScreenFull.isEnabled) {
        // Full screen is not available at this time
        this.$message.warning('The full screen component is not available at this time')
        return
      }
      // document.documentElement.requestFullscreen() native js call
      //   Full screen if available
      ScreenFull.toggle()
    }
  }
}
</script>

<style>
</style>

Register the component globally Src / components / index js

import ScreenFull from './ScreenFull'
Vue.component('ScreenFull', ScreenFull) // Register full screen components

Place layout / navbar vue

<screen-full class="right-menu-item" />

-------------------------------

.right-menu-item {
   vertical-align: middle;
}

Set dynamic theme

Encapsulate full screen plug-in Src / components / themepicker / index vue

<template>
  <el-color-picker
    v-model="theme"
    :predefine="[
      '#409EFF',
      '#1890ff',
      '#304156',
      '#212121',
      '#11a983',
      '#13c2c2',
      '#6959CD',
      '#f5222d',
    ]"
    class="theme-picker"
    popper-class="theme-picker-dropdown"
  />
</template>

<script>
const version = require('element-ui/package.json').version // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // default color
export default {
  data () {
    return {
      chalk: '', // content of theme-chalk css
      theme: ''
    }
  },
  computed: {
    defaultTheme () {
      return this.$store.state.settings.theme
    }
  },
  watch: {
    defaultTheme: {
      handler: function (val, oldVal) {
        this.theme = val
      },
      immediate: true
    },
    async theme (val) {
      const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
      if (typeof val !== 'string') return
      const themeCluster = this.getThemeCluster(val.replace('#', ''))
      const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
      console.log(themeCluster, originalCluster)
      const $message = this.$message({
        message: '  Compiling the theme',
        customClass: 'theme-message',
        type: 'success',
        duration: 0,
        iconClass: 'el-icon-loading'
      })
      const getHandler = (variable, id) => {
        return () => {
          const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
          const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
          let styleTag = document.getElementById(id)
          if (!styleTag) {
            styleTag = document.createElement('style')
            styleTag.setAttribute('id', id)
            document.head.appendChild(styleTag)
          }
          styleTag.innerText = newStyle
        }
      }
      if (!this.chalk) {
        const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
        await this.getCSSString(url, 'chalk')
      }
      const chalkHandler = getHandler('chalk', 'chalk-style')
      chalkHandler()
      const styles = [].slice.call(document.querySelectorAll('style'))
        .filter(style => {
          const text = style.innerText
          return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
        })
      styles.forEach(style => {
        const { innerText } = style
        if (typeof innerText !== 'string') return
        style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
      })
      this.$emit('change', val)
      $message.close()
    }
  },
  methods: {
    updateStyle (style, oldCluster, newCluster) {
      let newStyle = style
      oldCluster.forEach((color, index) => {
        newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
      })
      return newStyle
    },
    getCSSString (url, variable) {
      return new Promise(resolve => {
        const xhr = new XMLHttpRequest()
        xhr.onreadystatechange = () => {
          if (xhr.readyState === 4 && xhr.status === 200) {
            this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
            resolve()
          }
        }
        xhr.open('GET', url)
        xhr.send()
      })
    },
    getThemeCluster (theme) {
      const tintColor = (color, tint) => {
        let red = parseInt(color.slice(0, 2), 16)
        let green = parseInt(color.slice(2, 4), 16)
        let blue = parseInt(color.slice(4, 6), 16)
        if (tint === 0) { // when primary color is in its rgb space
          return [red, green, blue].join(',')
        } else {
          red += Math.round(tint * (255 - red))
          green += Math.round(tint * (255 - green))
          blue += Math.round(tint * (255 - blue))
          red = red.toString(16)
          green = green.toString(16)
          blue = blue.toString(16)
          return `#${red}${green}${blue}`
        }
      }
      const shadeColor = (color, shade) => {
        let red = parseInt(color.slice(0, 2), 16)
        let green = parseInt(color.slice(2, 4), 16)
        let blue = parseInt(color.slice(4, 6), 16)
        red = Math.round((1 - shade) * red)
        green = Math.round((1 - shade) * green)
        blue = Math.round((1 - shade) * blue)
        red = red.toString(16)
        green = green.toString(16)
        blue = blue.toString(16)
        return `#${red}${green}${blue}`
      }
      const clusters = [theme]
      for (let i = 0; i <= 9; i++) {
        clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
      }
      clusters.push(shadeColor(theme, 0.1))
      return clusters
    }
  }
}
</script>

<style>
.theme-message,
.theme-picker-dropdown {
  z-index: 99999 !important;
}
.theme-picker .el-color-picker__trigger {
  height: 26px !important;
  width: 26px !important;
  padding: 2px;
}
.theme-picker-dropdown .el-color-dropdown__link-btn {
  display: none;
}
.el-color-picker {
  height: auto !important;
}
</style>

Register the component globally Src / components / index js

import ThemePicker from './ThemePicker'
Vue.component('ThemePicker', ThemePicker)

Place layout / navbar vue

  <theme-picker class="right-menu-item" />

Multilingual implementation

Install international language packs i18n

npm i vue-i18n

Multilingual instantiation file Src / Lang / index. Is required js

import Vue from 'vue' // Introduce Vue
import VueI18n from 'vue-i18n' // Introducing internationalized packages
import Cookie from 'js-cookie' // Introduce cookie package
import elementEN from 'element-ui/lib/locale/lang/en' // Introduce hungry English bag
import elementZH from 'element-ui/lib/locale/lang/zh-CN' // Introduce hungry Chinese bag
import customZH from './zh' // Import custom Chinese package
import customEN from './en' // Import custom English package
Vue.use(VueI18n) // Global registration internationalization package
export default new VueI18n({
  locale: Cookie.get('language') || 'zh', // Get the language type from the cookie. If you can't get it, it's Chinese
  messages: {
    en: {
      ...elementEN, // Introduce hungry English language pack
      ...customEN
    },
    zh: {
      ...elementZH, // Introduce hungry Chinese language pack
      ...customZH
    }
  }
})

main.js, and set element to the current language

// Set element to the current language
Vue.use(ElementUI, {
  i18n: (key, value) => i18n.t(key, value)
})

new Vue({
  el: '#app',
  router,
  store,
  i18n,
  render: h => h(App)
})

Import custom language pack
src/lang/zh.js , src/lang/en.js
zh

export default {
  route: {
    Dashboard: 'home page',
    manage: 'back-stage management',
    users: 'user management ',
    menus: 'Menu management',
    logs: 'Log management',
    example: 'Examples',
    table: 'Data list',
    // permissions: 'permission management',
    // Employees: 'employees',
    // employeesList: 'employee management',
    // employeesInfo: 'personal information',
    goods: 'Commodity management',
    postInfo: 'Position information',
    manageSelf: 'Manager Self-Service ',
    setting: 'set up',
    reports: 'Report analysis',
    employeesAdd: 'Add employee',
    EditiNfo: 'Edit information',
    rights: 'Authority management',
    print: 'Print page',
    form: 'form ',
    basicForm: 'Basic form',
    stepForm: 'Step by step form',
    advancedList: 'Advanced form',
    step: 'step',
    details: 'Detail page',
    BasicsDetails: 'Basic details page',
    seniorDetails: 'Advanced details page',
    import: 'Import',
    // register
    register: 'Human resources-register',
    login: 'Human resources-Sign in',
    // Examine and approve
    approvals: 'Examine and approve', // Examine and approve
    salaryApproval: 'Salary review',
    enterApproval: 'Employment review',
    leaveApproval: 'Apply for leave',
    quitApproval: 'Apply for resignation',
    overtimeApproval: 'Overtime application',
    securitySetting: 'Approval settings',
    // staff
    employees: 'staff',
    employeesList: 'Employee list',
    employeesInfo: 'personal information',
    employeesAdjust: 'Post transfer',
    employeesLeave: 'quit',
    employeesPrint: 'Print',

    // wages
    salarys: 'wages',
    salarysList: 'Payroll list',
    salarysSetting: 'Salary setting',
    salarysDetails: 'Salary details',
    salarysHistorical: 'Historical archiving',
    salarysMonthStatement: 'Monthly report',
    // social security
    'social_securitys': 'social security',
    socialSecuritys: 'Social security management',
    socialDetail: 'details',
    socialHistorical: 'Historical archiving',
    socialMonthStatement: 'Current month report',
    // organizational structure
    departments: 'organizational structure',
    'departments-import': 'introduce',
    // company
    settings: 'Company settings',
    // check work attendance
    attendances: 'check work attendance',
    usersApprovals: 'User approval',
    // Enterprise saas
    'saas-clients': 'enterprise',
    'saas-clients-details': 'Enterprise details',
    // jurisdiction
    'permissions': 'Authority management' // Authority management
  },
  navbar: {
    search: 'Station search',
    logOut: 'Log out',
    dashboard: 'home page',
    github: 'Project address',
    screenfull: 'Full screen',
    theme: 'skin peeler',
    lang: 'Multilingual',
    error: 'Error log'
  },
  login: {
    title: 'Human resource management system',
    login: 'Sign in',
    username: 'account number',
    password: 'password',
    any: 'Just fill it in',
    thirdparty: 'Third party login',
    thirdpartyTips: 'Local cannot be simulated, please simulate in combination with your own business!!!'
  },
  tagsView: {
    close: 'close',
    closeOthers: 'Close other',
    closeAll: 'Close all',
    refresh: 'Refresh'
  },
  table: {
    title: 'Please enter user',
    search: 'search',
    add: 'add to',
    addUser: 'New user',
    id: 'Serial number',

    email: 'mailbox',
    phone: 'mobile phone',
    name: 'full name',
    entryTime: 'Entry time',
    hireForm: 'Employment form',
    jobNumber: 'Job number',
    department: 'department',
    managementForm: 'Management form',
    city: 'Work city',
    turnPositiveTime: 'Regular time',

    permissionNew: 'Add permission group',
    permissionUser: 'Permission group name',
    imdsAi: 'Advanced interface authorization',
    avatar: 'head portrait',
    introduction: 'introduce',
    paddword: 'password',
    powerCode: 'Permission code',
    powerDistriB: 'Permission assignment',
    powerTitle: 'Permission title',
    powerNav: 'Main navigation',
    actions: 'operation',
    edit: 'edit',
    delete: 'delete',
    cancel: 'Cancel',
    confirm: 'determine',
    return: 'return',
    operationType: 'Operation type',
    operationDate: 'Operation time',
    date: 'date',
    submit: 'Submit',
    operator: 'Operator',
    results: 'results of enforcement',
    describe: 'describe',
    save: 'preservation',
    signOut: 'sign out',
    reset: 'Reset',
    know: 'I got it!',
    view: 'see'

  }
}

en

export default {
  route: {
    dashboard: 'Dashboard',
    manage: 'manage',
    users: 'users',
    menus: 'menus',
    // permissions: 'permissions',
    logs: 'logs',
    example: 'example',
    table: 'table',

    postInfo: 'Job information',
    manageSelf: 'Manager self-help',
    setting: 'setting',
    reports: 'report',
    employeesAdd: 'add employees',
    EditiNfo: 'Edit information',
    print: 'print',

    form: 'form',
    basicForm: 'basic form',
    stepForm: 'step form',
    advancedList: 'advanced form',
    step: 'step',

    details: 'details',
    BasicsDetails: 'Basic details page',
    seniorDetails: 'Advanced details page',
    import: 'Import',
    register: 'HRM-Register',

    // Sign in
    login: 'HRM-Login',
    // Examine and approve
    approvals: 'Approvals', // Examine and approve
    salaryApproval: 'Salary-Approval',
    enterApproval: 'Enter-Approval',
    leaveApproval: 'Leave-Approval',
    quitApproval: 'Quit-Approval',
    overtimeApproval: 'Overtime-Approval',
    securitySetting: 'Security-Setting',
    // staff
    employees: 'Employees',
    employeesList: 'Employees-List',
    employeesInfo: 'Employees-Info',
    employeesAdjust: 'Employees-Adjust',
    employeesLeave: 'Employees-Leave',
    employeesPrint: 'Employees-Print',
    // wages
    salarys: 'salarys',
    salarysList: 'Salarys-List',
    salarysSetting: 'Salarys-Setting',
    salarysDetails: 'Salarys-Details',
    salarysHistorical: 'Salarys-Historical',
    salarysMonthStatement: 'Salarys-Month',
    // social security
    'social_securitys': 'Social',
    socialSecuritys: 'Social-Securitys',
    socialDetail: 'Social-Detail',
    socialHistorical: 'Social-Historical',
    socialMonthStatement: 'Social-Month',
    // organizational structure
    departments: 'departments',
    'departments-import': 'import',

    // company
    settings: 'Company-Settings',
    // check work attendance
    attendances: 'Attendances',
    // User approval
    usersApprovals: 'Users-Approvals',
    // enterprise
    'saas-clients': 'Saas-Clients',
    'saas-clients-details': 'Saas-Details',
    'permissions': 'permissions' // Authority management

  },
  navbar: {
    search: 'search',
    logOut: 'Log Out',
    dashboard: 'Dashboard',
    github: 'Github',
    screenfull: 'screenfull',
    theme: 'theme',
    lang: 'i18n',
    error: 'error log'
  },
  login: {
    title: 'itheima login',
    login: 'Log in',
    name: 'name',
    entryTime: 'entry time',
    hireForm: 'hire form',
    jobNumber: 'job number',
    department: 'department',
    managementForm: 'management form',
    city: 'city',
    turnPositiveTime: 'turn positive time',

    password: 'Password',
    any: 'any',
    thirdparty: 'Third',
    thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !'
  },
  tagsView: {
    close: 'Close',
    closeOthers: 'Close Others',
    closeAll: 'Close All',
    refresh: 'refresh'

  },
  table: {
    title: 'Title',
    search: 'Search',
    add: 'add',
    addUser: 'addUser',
    id: 'ID',
    email: 'Email',
    phone: 'Phone',
    username: 'User',
    permissionNew: 'permissionNew',
    permissionUser: 'Permission',
    imdsAi: 'Advanced interface authorization',
    avatar: 'Avatar',
    introduction: 'Introduction',
    paddword: 'paddWord',
    powerCode: 'Permission code',
    powerTitle: 'Permission title',
    actions: 'Actions',
    edit: 'Edit',
    delete: 'Delete',
    cancel: 'Cancel',
    confirm: 'Confirm',
    operationType: 'operationType',
    operationDate: 'operationDate',
    date: 'Date',
    operator: 'operator',
    results: 'results of enforcement',
    describe: 'Pedagogical operation',
    save: 'save',
    signOut: 'sign out',
    submit: 'submit',
    reset: 'reset',
    know: 'I Know',
    return: 'return',
    view: 'view'

  }
}

index. The language pack is also introduced into JS

import customZH from './zh' // Import custom Chinese package
import customEN from './en' // Import custom English package
Vue.use(VueI18n) // Global registration internationalization package
export default new VueI18n({
  locale: Cookie.get('language') || 'zh', // Get the language type from the cookie. If you can't get it, it's Chinese
  messages: {
    en: {
      ...elementEN, // Introduce hungry English language pack
      ...customEN
    },
    zh: {
      ...elementZH, // Introduce hungry Chinese language pack
      ...customZH
    }
  }
})

Turn the left menu into multilingual display text
layout/components/SidebarItem.vue

<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="$t('route.'+onlyOneChild.name)" />

Encapsulating multilingual components Src / components / Lang / index vue

<template>
  <el-dropdown trigger="click" @command="changeLanguage">
    <!-- You must add one here div -->
    <div>
      <svg-icon style="color: #fff; font-size: 20px" icon-class="language" />
    </div>
    <el-dropdown-menu slot="dropdown">
      <el-dropdown-item command="zh" :disabled="'zh' === $i18n.locale"
        >chinese</el-dropdown-item
      >
      <el-dropdown-item command="en" :disabled="'en' === $i18n.locale"
        >en</el-dropdown-item
      >
    </el-dropdown-menu>
  </el-dropdown>
</template>

<script>
import Cookie from 'js-cookie'
export default {
  methods: {
    changeLanguage (lang) {
      Cookie.set('language', lang) // Switch multiple languages
      this.$i18n.locale = lang // Set to local i18n plug-ins
      this.$message.success('Switching multiple languages succeeded')
    }
  }
}
</script>

Register the component globally Src / components / index js

import lang from './lang'
Vue.component('lang', lang) // Register full screen components

Place layout / navbar vue

<lang class="right-menu-item" />

Keywords: Javascript Front-end Vue.js html elementUI

Added by fandelem on Sat, 05 Mar 2022 21:37:07 +0200