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" />