introduce
vue-element-admin Is a back-end front-end solution based on vue and element-ui Realization. It uses the latest front-end technology stack, built-in i18 internationalization solutions, dynamic routing, privilege validation, extracts a typical business model, provides rich functional components, which can help you quickly build enterprise-level mid-background product prototypes. I believe this project will help you regardless of your needs
- vue-element-admin positioning is a background integration scheme, which is not suitable for secondary development of basic templates. The project integrates many functions that are not needed and will cause code sinking
- vue-admin-template is a background base template and is recommended for secondary development
- Electronic-vue-admin is a desktop terminal that you can use for desktop terminal development
function
- Sign in / Cancellation - Permission Verification - Page Permissions - Instruction Permissions - Permission Configuration - Two-step login - Multi-Environment Publishing - dev sit stage prod - Global functionality - Internationalized Multilingual - Various dynamic skin changes - Dynamic Sidebar (supports multi-level routing nesting) - Dynamic breadcrumbs - Quick Navigation(Tab Page) - Svg Sprite Icon - local/back-end mock data - Screenfull Full screen - Adaptive Shrink Sidebar - editor - Rich text - Markdown - JSON Multi-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 - Return 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 # Project mock simulation data ├── 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 Components │ ├── directive # Global directives │ ├── filters # Global filter │ ├── icons # Project All svg icons │ ├── lang # Internationalized 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 # Initialization of entry file load components, etc. │ └── permission.js # Rights 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 Item git clone https://github.com/PanJiaChen/vue-element-admin.git # Enter Project Directory cd vue-element-admin # Installation Dependency npm install # Slow enough to specify the download mirror using # You can also use nrm to choose to download the mirror # It is not recommended to install with cnpm. There are various weird bug s to solve the problem of slow download speed of npm by doing the following npm install --registry=https://registry.npm.taobao.org # Note: This framework starts differently from what we normally set ourselves up to do by using the following method # Local Development Startup Project npm run dev
Automatically open browser access after startup http://localhost:9527 , you can see the page to prove that your operation was successful
Layout layout
layout-based on most pages except: 404, login, etc.
layout integrates all layouts of the page for block display
The whole plate is divided into three parts
layout Major Arrangement
Src directory
Entry file main.js
There are custom mock file access, we want to comment it out
The mock folder under src is suggested to be deleted, we will not use it later
App.vue
Under src, except main.js has two more files, permission.js and settings.js
permission.js
permission.js is a file that controls the login rights of the page. We can comment it out first and add it slowly later
settings.js
settings.js is a configuration for some project information, which has three properties ** Title (project name), fixedHeader (fixed header), sidebarLogo (display left menu logo)
Configurations we'll use elsewhere, don't move
Introduction of API module and request encapsulation module
Individual requests for API modules and encapsulation of request modules
Axios Interceptor
axios interceptor principle:
Create a new axios instance from 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
Primarily deals with token's _ 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 exceptions and _ 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. // Custom code return values are negotiated and written to suit your needs if (res.code !== 20000) { Message({ message: res.message || 'Error', type: 'error', duration: 5 * 1000 }) // Custom code return values are negotiated and written to suit your 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) } )
Above is src/utils/request. Source code under JS
We just need to keep:
// Export an instance of axios with request interceptor 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
Separate encapsulation of api
We are accustomed to putting all api requests under the api directory for unified management and use by module
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 a fixed local access port and site name
Set up a unified local access port and site title
Local service port: at vue. Config. Setting in JS
vue.config.js is the vue project related compilation, configuration, packaging, start service related configuration file, its core is the webpack, but different from the webpack, equivalent to the improved version of the webpack
We see that the above is an environment variable, not an actual address, so where do 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 debugging, execution is loaded**. env.development** file content
When we run npm run build:prod to package the production environment, the execution is loaded**. env.production** file contents
If you want to set up an interface for your development environment, go directly to **. Env. Write a direct assignment to a variable in the development** file
# just a flag ENV = 'development' # base api VUE_APP_BASE_API = 'api/private/v1/'
If you want to set up an interface for your production environment**. Env. Write a direct assignment to a variable in the production** file
# just a flag ENV = 'production' # base api VUE_APP_BASE_API = 'api/private/v1/'
Site Name
Src/settings. In JS
Tile is the name of the site
We need to restart after configuring, otherwise some configurations will not take effect
Logon 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 the background picture:
Can be changed as needed
/* 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 for el-form form checking
Verification of username 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('User name must have at least 5 digits')) } else if (value.length > 12) { callback(new Error('User name up to 12 digits')) } else { callback() } } const validatePassword = (rule, value, callback) => { if (value.length < 5) { callback(new Error('User name must have at least 5 digits')) } else if (value.length > 16) { callback(new Error('User name up to 16 digits')) } else { callback() } } loginRules: { username: [{ required: true, trigger: 'blur', validator: validateUsername }], password: [ { required: true, trigger: 'blur', validator: validatePassword }, { min: 5, max: 12, trigger: 'blur', message: 'Password length should be 5-12 Between bits' } ] }
Vue-Cli Configuration Cross-Domain Agent
What is the reason for cross-domain?
Because the current trend is to separate front-end and back-end development, front-end projects and back-end interfaces are not under the same domain name, then front-end access back-end interfaces will appear cross-domain
So how can we solve the problem?
The cross-domain that we encounter is in the development environment, and when we actually deploy online, the cross-domain is in the production environment, and the solutions are different
Let's solve the development environment first, the production environment can be solved by packaging online, and then
Solving cross-domain issues in the development environment
Cross-domain issues encountered by our access interface when developing startup services in the vue-cli scaffolding environment. vue-cli has opened a service for us locally which can help us proxy requests and solve cross-domain problems
That is, vue-cli configures the reverse proxy for the webpack
In vue. Config. Reverse proxy configuration in JS
module.exports = { devServer: { proxy: { 'api/private/v1/': { target: 'http://127.0.0.1:8888', //The address we want to proxy, when matched to'api/private/v1/'above, will http://localhost:9528 replace with http://127.0.0.1:8888 changeOrigin: true, // Whether this value needs to be set to true before we can have the local service proxy send us requests pathRewrite: { // Reroute localhost:8888/api/login => http://127.0.0.1:8888/api/login '^/api': '/api', '/hr': '' } } } } }
Also note that we also need to comment out the loading of mocks because mock-server s can cause proxy service exceptions
// before: require('./mock/mock-server.js'), //comment mock-server load
Encapsulate separate login interfaces
export function login(data) { // Return an axios object=> promise // Return a promise object return request({ url: 'login', // Because all interfaces are cross-domain, all interfaces are banded/api method: 'post', data }) }
Encapsulate Vuex's login Action and process token
Managing token s in Vuex
In the figure above, the components work directly with the interface, which is okay, but ta uses keys to transfer each other, so we need vuex to intervene, share the user's token state, and read it more easily
store/modules/user.js configuration
// state const state = {} // modify state const mutations = {} // Execute Asynchronous const actions = {} export default { namespaced: true, state, mutations, actions }
Set token sharing status
const state = { token: null }
Operation token
Utils/auth. In js, the base template has provided us with a way to get token, set token, delete token, and use it 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 state
store/modules/user.js
import { getToken, setToken, removeToken } from '@/utils/auth' const state = { token: getToken() // Set token initial state token persistence=>put in cache }
Provide mutations to modify token
// modify state const mutations = { // Set token setToken(state, token) { state.token = token // Setting token only modifies state's data 123 ="1234 setToken(token) // Synchronization of vuex and cached data }, // Delete Cache removeToken(state) { state.token = null // Remove token of vuex removeToken() // Clear vuex before clearing cache vuex and synchronization of cached data } }
Encapsulate login Action
What to do with the login action, call the login interface, set token to vuex after success, fail and return failure
// Execute Asynchronous const actions = { // Defining a login action also requires parameters passed in when the action is called by the parameter async login(context, data) { const result = await login(data) // A promise result is actually 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 was successful which means that your username and password are correct // There is now a user token // actions must modify state through mutations context.commit('setToken', result.data.data) } } }
For better access to token data for other modules and components, we need to store/getters. Release token values as public access properties in JS
const getters = { sidebar: state => state.app.sidebar, device: state => state.app.device, token: state => state.user.token // Develop properties of sub-modules on root-level getters to show others } export default getters
From this, we can have a brain map
Distinguishing axios'request base addresses in different environments
The front two main distinguish between environment, development environment and production environment
Environment variable $process.env.NODE_ENV #Development when production is development
We can do that at **. env.development and. env.production** Defines a variable that is automatically the value of the current environment
The base template defines the variable VUE_in the file above APP_ BASE_ API, which can be used as baseURL for axios requests
# Base address and proxy correspondence for development environment VUE_APP_BASE_API = '/api' --------- # The / API configuration here means that the reverse proxy corresponding / prod-api address of nginx needs to be configured on the Nginx server for the service VUE_APP_BASE_API = '/prod-api'
Or they can all be written in the same manageable way
Setting baseUrl - benchmark in request
// Create an instance of axios const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, // Set the base address for axios requests timeout: 5000 // Define 5 seconds 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 actions are determined by success or failure if (success) { return data } else { // Business has gone wrong. Can we go in then? No! 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 an execution error to jump out of the current execution chain and enter catch directly })
Logon page calls logon action to handle exceptions
Introducing auxiliary functions
import { mapActions } from 'vuex' // Introducing the auxiliary function of vuex --------------------- methods: { ...mapActions(['user/login']) }
Call login
this.$refs.loginForm.validate(async isOK => { if (isOK) { try { this.loading = true // We call action only if the check passes await this['user/login'](this.loginForm) // Should be logged in after success // Jump to home page after successful login this.$router.push('/') } catch (error) { console.log(error) } finally { // Turn the loop off whether you perform try or catch this.loading = false } } })
analysis
Write using the front form of the elementUI first
Use form validation in the foreground to compare user-entered account passwords, whether they meet the criteria, or if they do not meet the criteria we defined for a prompt
We use v-model for bidirectional data binding of input boxes in the form
Background validation is also required when the user clicks on the login button after the user has finished typing. When we click on the login to send a request to the backstage warehouse to check if the account password is correct, a prompt pops up if it is incorrect
Introduce icon icon with <svg>tag in form
We first created the SvgIcon component under srccomponents
We exposed two attributes
Monitor icon's name and its custom style through computed. When no custom style is specified, the default style is used, otherwise the custom class is 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. Introduce svg component import IconSvg from'@/components/IconSvg'in JS
Use global registration icon-svg Vue.component('icon-svg', IconSvg)
This allows you to use it anywhere in your project
For centralized icon management, place all icons in @/icons/svg
@Represents finding src directory
require.context has three parameters:
- Parameter 1: Describe the directory to retrieve
- Parameter 2: Whether to retrieve subdirectories
- Parameter 3: Regular expression matching file
At @/main. Import'@/icons'is introduced in JS so that components can be used successfully on any page
Use it on the page to use it
<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" >Sign 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('User name must have at least 5 digits')) } else if (value.length > 12) { callback(new Error('User name up to 12 digits')) } else { callback() } } const validatePassword = (rule, value, callback) => { if (value.length < 5) { callback(new Error('User name must have at least 5 digits')) } else if (value.length > 16) { callback(new Error('User name up to 16 digits')) } 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: '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') // Jump to home page after successful login this.$router.push({ path: '/' }) this.loading = false } catch (err) { this.loading = false console.log(err) return false } } } } </script> <style lang="scss"> /* Repair input background discordance 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 page module
Home page token intercepts and handles
Flow Chart for Privilege Interception
We have completed the login process and stored token, but at this time the home page is not controlled for token access
Intercept 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' // Introducing Printing // set ElementUI lang to EN Vue.use(ElementUI, { locale }) // If you want a Chinese version of element-ui, declare it as follows // Vue.use(ElementUI) Vue.use(Print) Vue.config.productionTip = false // Traversal Register Custom Instructions for (const key in directives) { Vue.directive(key, directives[key]) } Vue.use(Commponent) // Register your own plug-ins // Register Global Filters // Traverse registration filters for (const key in filters) { Vue.filter(key, filters[key]) } // Set element to 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 picture src/setttings.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 // Show logo }
Set the 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
Header Component Location layout/components/Navbar.vue
Crumbs when adding company name
<!-- <breadcrumb class="breadcrumb-container" /> --> <!--Crumbs--> <div class="app-breadcrumb"> Beijing Mengna Network Co., Ltd. <span class="breadBtn">v1.0.0</span> </div>
Right avatar and drop-down menu settings
<div class="right-menu"> <!-- Language Switching Plugin --> <lang class="right-menu-item lang_item" /> <!-- Full Screen Plugin --> <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>
Complete code: Style + Events
<template> <div class="navbar"> <hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> <!-- <breadcrumb class="breadcrumb-container" /> --> <div class="app-breadcrumb"> Beijing Mengna Network Co., Ltd. <span class="breadBtn">v1.0.0</span> </div> <div class="right-menu"> <!-- Language Switching Plugin --> <lang class="right-menu-item lang_item" /> <!-- Full Screen Plugin --> <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 data mutations
// Set User Information set_userInfo (state, user) { state.userInfo = user setUSERINFO(user) } // Delete user information removeUserInfo (state) { this.userInfo = {} }
Create a mapping of user names 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 in the page refresh, so we can save it locally and take it out
Implement exit function
Exit: src/store/modules/user.js
// user logout logout (context) { // Delete token context.commit('removeToken') // Not only has the vuex been deleted, but the cache has also been deleted // Delete User Data context.commit('removeUserInfo') // Delete user information },
mutation
removeToken (state) { state.token = null removeToken() removeUSERINFO() removeLocalMenus() }, removeUserInfo (state) { this.userInfo = {} },
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 Mengna Network Co., Ltd. <span class="breadBtn">v1.0.0</span> </div> <div class="right-menu"> <!-- Language Switching Plugin --> <lang class="right-menu-item lang_item" /> <!-- Full Screen Plugin --> <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 invalidation intervention
src/utils/auth.js
const timeKey = 'haitun-setTimeStamp' // Set a unique key // Store the token's timestamp (when the setToken method executes) // Get Timestamp export function setTimeStamp () { return localStorage.setItem(timeKey, Date.now()) } // Get expiration time of 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( // Injection token config => { // do something before request is sent if (store.getters.token) { // Determine if the current token timestamp is out of date // Get the time set by token const tokenTime = getTimeStamp() // Get the current time const currenTime = Date.now() if ((currenTime - tokenTime) / 1000 > timeOut) { // If it is true, it means expired // token is useless because it timed out store.dispatch('user/logout') // Logout Action // Jump to the login page router.push('/login') return Promise.reject(new Error('Logon expired, please log in 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
At login time, if login is successful, we should set 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 Rights Information const permission = await getMenus() const menus = filterPermission(permission) context.commit('set_menus', menus) },
token failure 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, rights management, etc. What pages do you need to develop by yourself, steps are similar
Multilingual switching, tab full screen
Reference to Full Screen Plugin
Install global plugin screenfull
npm i screenfull
Encapsulate the full screen plugin src/components/ScreenFull/index.vue
<template> <!-- Place an icon --> <div> <!-- Place one svg Icons --> <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('Full screen components are not available at this time') return } // document.documentElement.requestFullscreen() native js call // Full screen if available ScreenFull.toggle() } } } </script> <style> </style>
Register the component src/components/index globally. 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 the full screen plugin 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 src/components/index globally. JS
import ThemePicker from './ThemePicker' Vue.component('ThemePicker', ThemePicker)
Place layout/navbar.vue
<theme-picker class="right-menu-item" />
Multilingual implementation
Install international language pack i18n
npm i vue-i18n
Multilingual instantiation file src/lang/index is required. JS
import Vue from 'vue' // Introducing Vue import VueI18n from 'vue-i18n' // Introducing Internationalized Packages import Cookie from 'js-cookie' // Introducing cookie packages import elementEN from 'element-ui/lib/locale/lang/en' // Bring in hungry English bags import elementZH from 'element-ui/lib/locale/lang/zh-CN' // Introducing hungry Chinese bags import customZH from './zh' // Introducing custom Chinese packages import customEN from './en' // Introduction of Custom English Pack Vue.use(VueI18n) // Global Registration Internationalization Package export default new VueI18n({ locale: Cookie.get('language') || 'zh', // Get the language type from the cookie is not Chinese messages: { en: { ...elementEN, // Bring in hungry English language packs ...customEN }, zh: { ...elementZH, // Introduction of hungry Chinese language packs ...customZH } } })
main.js plugin to mount i18n and set element to current language
// Set element to current language Vue.use(ElementUI, { i18n: (key, value) => i18n.t(key, value) }) new Vue({ el: '#app', router, store, i18n, render: h => h(App) })
Introducing custom language packs
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: 'Example', table: 'Data List', // permissions:'rights management', // Employees:'employees', // employeesList:'Employee Management', // employeesInfo:'Personal Information', goods: 'Commodity Management', postInfo: 'Job Information', manageSelf: 'Manager Self-Service', setting: 'Set up', reports: 'Report Analysis', employeesAdd: 'Add Employee', EditiNfo: 'Edit Information', rights: 'Rights Management', print: 'Print Page', form: 'form', basicForm: 'Basic Form', stepForm: 'Step-by-step form', advancedList: 'Advanced Forms', step: 'step', details: 'Detail Page', BasicsDetails: 'Basic Details Page', seniorDetails: 'Advanced Details Page', import: 'Import', // register register: 'Personnel-register', login: 'Personnel-Sign in', // Approval approvals: 'Approval', // Approval salaryApproval: 'Wage Review', enterApproval: 'Enrollment Review', leaveApproval: 'Request Leave', quitApproval: 'Apply for resignation', overtimeApproval: 'Overtime application', securitySetting: 'Approval Settings', // staff employees: 'staff', employeesList: 'Employee List', employeesInfo: 'Personal information', employeesAdjust: 'Relocation', employeesLeave: 'Quit', employeesPrint: 'Print', // wages salarys: 'wages', salarysList: 'Wage List', salarysSetting: 'Wage Settings', salarysDetails: 'Wage details', salarysHistorical: 'Historical Archives', salarysMonthStatement: 'Monthly Report', // social security 'social_securitys': 'social security', socialSecuritys: 'social insurance management', socialDetail: 'details', socialHistorical: 'Historical Archives', 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', // saas enterprise 'saas-clients': 'enterprise', 'saas-clients-details': 'Business Details', // Jurisdiction 'permissions': 'Rights Management' // Rights Management }, navbar: { search: 'Search this site', 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: 'Fill in freely', thirdparty: 'Third party login', thirdpartyTips: 'Local cannot simulate, please simulate with your own business!!!' }, tagsView: { close: 'Close', closeOthers: 'Close Others', closeAll: 'Close All', refresh: 'Refresh' }, table: { title: 'Please enter a user', search: 'search', add: 'Add to', addUser: 'New Users', id: 'Sequence Number', email: 'mailbox', phone: 'Mobile phone', name: 'Full name', entryTime: 'Enrollment Time', hireForm: 'Form of employment', jobNumber: 'Work Number', department: 'department', managementForm: 'Management Form', city: 'Work City', turnPositiveTime: 'Correction 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', // Approval approvals: 'Approvals', // Approval 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' // Rights 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 same language pack was introduced in JS
import customZH from './zh' // Introducing custom Chinese packages import customEN from './en' // Introduction of Custom English Pack Vue.use(VueI18n) // Global Registration Internationalization Package export default new VueI18n({ locale: Cookie.get('language') || 'zh', // Get the language type from the cookie is not Chinese messages: { en: { ...elementEN, // Bring in hungry English language packs ...customEN }, zh: { ...elementZH, // Introduction of hungry Chinese language packs ...customZH } } })
Turn left menu into multilingual presentation text
layout/components/SidebarItem.vue
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="$t('route.'+onlyOneChild.name)" />
Encapsulate multilingual component src/components/lang/index.vue
<template> <el-dropdown trigger="click" @command="changeLanguage"> <!-- You have to 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 Multilingual this.$i18n.locale = lang // Set to Local i18n Plugin this.$message.success('Successful switch to multiple languages') } } } </script>
Register the component src/components/index globally. JS
import lang from './lang' Vue.component('lang', lang) // Register Full Screen Components
Place layout/navbar.vue
<lang class="right-menu-item" />