当前位置:网站首页>User login 【 I 】

User login 【 I 】

2022-06-12 05:54:00 Snow flies fast

Login process analysis

Interface and logic simplification

template modify

  • Modify the title <h3 class="title"> Xiao Mu reads </h3>
  • Delete prompt area <div style="position:relative">...</div>
  • modify input Of placeholder In Chinese , modify el-button The Chinese characters are Chinese
  • Delete el-dialog Code

script modify

  • Delete SocialSign Reference and registration of , Delete login Component's components Folder
  • Delete showDialog Variable
  • modify data The form verification text in is Chinese
  • Delete created and destroyed Hook functions and annotated afterQRScan Code

Webstorm During the development process, you may encounter script Tag source code eslint About indent The error of , The solution is as follows :ctrl + alt + L After formatting the code ,script There may be indent Warning of , There are two solutions :

  • close eslint Medium indent Check

  • modify Webstorm in indent Set up

    Webstorm => Preferences => Editor => Code Style => HTML => Other

    stay do not indent of children add script that will do

VSCode Inside prettier The plug-in format does not exist. This problem can be ignored

Routing instance processing

Start modifying file :

  1. Create components src/views/book/create.vue , Write something casually

  2. Configure the routing , modify src/router/index.js Of asyncRoutes

    children Your route needs to have name attribute , Yes name Attribute will be in tags-view Show it in

    meta Properties in

    • roles Set the permission of this route , Multiple permissions are supported
    • title Set the route in the sidebar and breadcrumb display text
    • icon Corresponding src/icons/svg, Also support element-ui Of icon

    For other configuration items, please refer to :vue-element-admin Routing and sidebar Configuration item

    export const asyncRoutes = [
      {
          
        path: '/book',
        component: Layout,
        redirect: '/book/create',
        meta: {
           title: ' Book management ', icon: 'documentation', roles: ['admin'] },
        children: [
          {
          
            path: '/book/create',
            component: () => import('@/views/book/create'),
            name: 'book',
            meta: {
           title: ' Upload books ', icon: 'edit', roles: ['admin'] }
          }
        ]
      },
      // 404 page must be placed at the end !!!
      {
           path: '*', redirect: '/404', hidden: true }
    ]
    

Preliminary knowledge

Routing and permission verification

The analysis of the middle and background routes is as follows :

  • Obtained Token:
    • visit /login : Redirect to /
    • visit /login?redirect=/xxx : Redirect to /xxx
    • visit /login Other routes : Direct access /xxx
  • Not obtained Token:
    • visit /login : Direct access /login
    • visit /login Other routes : Such as visit /dashboard, The actual access path is /login?redirect=%2Fdashboard , After logging in, you will be redirected directly /dashboard

Routing logic source code

  1. main.js Global routing guard is loaded in

    import './permission'
    
  2. permission Global routing guard is defined

    //  White list 
    const whiteList = ['/login', '/auth-redirect']
    
    router.beforeEach(async(to, from, next) => {
          
      //  Start progress bar 
      NProgress.start()
    
      //  Modify page title 
      document.title = getPageTitle(to.meta.title)
    
      //  from  Cookie  obtain  Token
      const hasToken = getToken()
    
      //  Judge  Token  Whether there is 
      if (hasToken) {
          
        //  If the current path is  login  Direct redirection to the home page 
        if (to.path === '/login') {
          
          next({
           path: '/' })
          NProgress.done()
        } else {
          
          //  Determine whether the user's role exists 
          const hasRoles = store.getters.roles && store.getters.roles.length > 0
          //  If the user role exists , Direct access to 
          if (hasRoles) {
          
            next()
          } else {
          
            try {
          
              //  Get the user's role asynchronously 
              const {
           roles } = await store.dispatch('user/getInfo')
              //  According to the user role , Dynamically generate routing 
              const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
              //  call  router.addRoutes  Dynamic add route 
              router.addRoutes(accessRoutes)
              //  Use  replace  Access routing , Not in  history  Leave a record in the book 
              next({
           ...to, replace: true })
            } catch (error) {
          
              //  remove  Token  data 
              await store.dispatch('user/resetToken')
              //  Display error message 
              Message.error(error || 'Has Error')
              //  Redirect to login page 
              next(`/login?redirect=${
            to.path}`)
              NProgress.done()
            }
          }
        }
      } else {
          
        //  If you visit  URL  On the white list , Direct access to 
        if (whiteList.indexOf(to.path) !== -1) {
          
          next()
        } else {
          
          //  If you visit  URL  Not on the white list , Redirect directly to the login page , And will visit  URL  Add to  redirect  Parameters in 
          next(`/login?redirect=${
            to.path}`)
          NProgress.done()
        }
      }
    })
    
    router.afterEach(() => {
          
      //  Stop progress bar 
      NProgress.done()
    })
    

NProgress

//  Start progress bar 
NProgress.start()
//  End progress bar 
NProgress.done()
//  Controls whether the circular progress bar on the right displays 
NProgress.configure({
     showSpinner: false })

Error in dynamically generating route or getting route

  1. Distribution execution store.dispatch('user/resetToken'), eliminate Token 、 Reset roles
  2. Display error message Message.error(error || 'Has Error')
  3. Redirect to landing page next('/login?redirect=${to.path}'), Then end the progress bar
const actions = {
    
  resetToken({
      commit }) {
    
    return new Promise(resolve => {
    
      commit('SET_TOKEN', '')
      commit('SET_ROLES', [])
      removeToken()
      resolve()
    })
  },
}

const TokenKey = 'Admin-Token'
export function removeToken() {
    
  return Cookies.remove(TokenKey)
}

Dynamic routing analysis

There are two kinds of routes here :

  • constantRoutes: Represents routes that do not require dynamic judgment permission , Like landing page 、404、 And other common pages
  • asyncRoutes: On behalf of those requirements, dynamically judge permissions and pass addRoutes Dynamically added pages

When a user logs in to the system , Routes will be generated dynamically , among constantRoutes Inevitable inclusion ,asyncRoutes It will be filtered

asyncRoutes The logic of filtering is to see whether the route contains meta and meta.roles attribute

  • If this property is not available , Is a general routing , Permission verification is not required

  • If you include roles attribute , It will determine whether the user's role has hit any permission in the route

    If hit , Save the route

    If you miss , Then directly discard the route

asyncRoutes After processing , Hui He constantRoutes Merge into a new routing object , And save to vuex Of permission/routes in

  • After the user logs in to the system , The sidebar will start from vuex In order to get state.permission.routes , Dynamically render the user menu according to the route

Get user rights , Generate dynamic routing

const {
     roles } = await store.dispatch('user/getInfo')
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)

The source code for generating dynamic routing is located in src/store/modules/permission.js Medium generateRoutes Method

import {
     asyncRoutes, constantRoutes } from '@/router'

const actions = {
    
  generateRoutes({
      commit }, roles) {
    
    //  return  Promise  object 
    return new Promise(resolve => {
    
      let accessedRoutes
      if (roles.includes('admin')) {
    
        //  If the role contains  admin, Skip judgment directly , Direct will  asyncRoutes  All return 
        accessedRoutes = asyncRoutes || []
      } else {
    
        //  If the role does not contain  admin, Call  filterAsyncRoutes  Filter routes 
        accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      }
      //  Save route to  vuex  in 
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}

commit('SET_ROUTES', accessedRoutes) Save the route in store in

const state = {
    
  routes: [],
  addRoutes: []
}
const mutations = {
    
  SET_ROUTES: (state, routes) => {
    
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}

If the role is not admin, It will call filterAsyncRoutes Method :

export function filterAsyncRoutes(routes, roles) {
    
  const res = []
  
  //  Traverse all routes 
  routes.forEach(route => {
    
    //  Make a shallow copy of the route , Be careful  children  No copying , Because there's no need to be right about  children  Judge , So you can use a shallow copy 
    const tmp = {
     ...route }
    //  Check whether the user has access to the route 
    if (hasPermission(roles, tmp)) {
    
      //  When the route has access , Determine whether the route has  children  attribute 
      if (tmp.children) {
    
        //  When the route contains  children  when , Yes  children  Iterative call  filterAsyncRoutes  Method 
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }
      //  When the route has access , take  temp  Save to  res  in 
      res.push(tmp)
    }
  })

  return res
}

Check permission method hasPermission Source code is as follows :

function hasPermission(roles, route) {
    
  //  Check whether the route contains  meta  and  meta.roles  attribute 
  if (route.meta && route.meta.roles) {
    
    //  Judge  route.meta.roles  Whether user roles are included in the  roles  Any one of the permissions in 
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    
    //  If the route does not  meta  or  meta.roles  attribute , It is deemed that the route does not need permission control ( All users have access to this route )
    return true
  }
}

Sidebar

  • sidebar Quote from layout Components , Component is located src/layout/index.vue
  • sidebar The component source code is located in src/layout/components/Sidebar/index.vue

el-menu Usage analysis

<template>
  <el-row class="tac">
    <el-col :span="12">
      <el-menu default-active="1-1" background-color="#545c64" text-color="#fff" active-text-color="#ffd04b" mode="vertical" unique-opened :collapse="isCollapse" :collapse-transition="false" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" @select="handleSelect" >
        <el-submenu index="1">
          <template slot="title">
            <i class="el-icon-location"></i>
            <span> Navigation one </span>
          </template>
          <el-menu-item-group>
            <template slot="title"> Group one </template>
            <el-menu-item index="1-1"> Options 1</el-menu-item>
            <el-menu-item index="1-2"> Options 2</el-menu-item>
          </el-menu-item-group>
          <el-menu-item-group title=" grouping 2">
            <el-menu-item index="1-3"> Options 3</el-menu-item>
          </el-menu-item-group>
          <el-submenu index="1-4">
            <template slot="title"> Options 4</template>
            <el-menu-item index="1-4-1"> Options 1</el-menu-item>
          </el-submenu>
        </el-submenu>
        <el-submenu index="2">
          <template slot="title">
            <i class="el-icon-menu"></i>
            <span slot="title"> Navigation two </span>
          </template>
          <el-menu-item index="2-1"> Options 2-1</el-menu-item>
        </el-submenu>
        <el-menu-item index="3" disabled>
          <i class="el-icon-document"></i>
          <span slot="title"> Navigation three </span>
        </el-menu-item>
        <el-menu-item index="4">
          <i class="el-icon-setting"></i>
          <span slot="title"> Navigation four </span>
        </el-menu-item>
      </el-menu>
    </el-col>
    <el-col>
      <el-button @click="isCollapse = !isCollapse"> Fold </el-button>
    </el-col>
  </el-row>
</template>

<script> export default {
       data() {
       return {
       isCollapse: false } }, methods: {
       handleSelect(index, indexPath) {
       console.log('handleSelect', index, indexPath) }, handleOpen(index, indexPath) {
       console.log('handleOpen', index, indexPath) }, handleClose(index, indexPath) {
       console.log('handleClose', index, indexPath) } } } </script>

el-menu Represents the menu container component :

  • default-active: Current active menu index, Be careful : If there is a submenu , You need to fill in the submenu ID

  • unique-opened: Whether to keep only one submenu expanded

  • collapse: Is the menu folded up horizontally ( Only in mode by vertical You can use )

    It can be used in combination with buttons , Click to collapse and click to expand

  • collapse-transition: Whether to display the collapse animation

  • @select: Click menu event . Callback function :index: Select... Of the menu item index;indexPath: Select... Of the menu item index path, You can get 1-4-1 Of all the parent menus of the menu ID

    handleSelect 1-4-1 ['1', '1-4', '1-4-1']
    
  • @open: The event is triggered when the parent menu is opened

  • @close: The event is triggered when the parent menu is closed

el-submenu Represents the submenu container :el-submenu And el-menu Different ,el-menu Represents the entire menu , and el-submenu Represents a specific menu ,el-submenu It can be customized slot Of title Customize menu styles :

<el-submenu index="1">
  <template slot="title">
    <i class="el-icon-location"></i>
    <span> Navigation one </span>
  </template>
</el-submenu>

el-submenu In container default Of slot Used to store submenus , It can contain three submenu components :

  • el-menu-item-group: Menu grouping , Add a title to a group of menus ,el-menu-item-group The contents of the container need to be stored el-menu-item Components , Support title Of slot To customize the title style
  • el-submenuel-submenu Support circular nesting el-submenu, This enables more than two levels of subcomponents to be implemented
  • el-menu-item: Submenu component

sidebar Source code analysis

  • src/layout/components/Sidebar/index.vue
<template>
  <div :class="{
     'has-logo':showLogo}">
    <logo v-if="showLogo" :collapse="isCollapse" />
    <el-scrollbar wrap-class="scrollbar-wrapper">
      <el-menu :default-active="activeMenu" :collapse="isCollapse" :background-color="variables.menuBg" :text-color="variables.menuText" :unique-opened="false" :active-text-color="variables.menuActiveText" :collapse-transition="false" mode="vertical" >
        <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
      </el-menu>
    </el-scrollbar>
  </div>
</template>

<script> import {
       mapGetters } from 'vuex' import Logo from './Logo' import SidebarItem from './SidebarItem' import variables from '@/styles/variables.scss' export default {
       components: {
       SidebarItem, Logo }, computed: {
       ...mapGetters([ 'permission_routes', 'sidebar' ]), activeMenu() {
       const route = this.$route const {
       meta, path } = route if (meta.activeMenu) {
       return meta.activeMenu } return path }, showLogo() {
       return this.$store.state.settings.sidebarLogo }, variables() {
       return variables }, isCollapse() {
       return !this.sidebar.opened } } } const state = {
       sidebar: {
       opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true } } </script>
  • :default-active="activeMenu": adopt meta.activeMenu attribute , Specify the highlighted menu corresponding to the route

    meta.activeMenu You need to provide a legal route , Otherwise, it will not take effect

  • :collapse="isCollapse"NavBar Click the button in , Will modify Cookie Medium sidebarStatus, from vuex When the value is taken, the sidebarStatus To Boolean, And determine whether the left menu bar needs to be shrunk by default

  • v-if="showLogo": Judge settings.js Whether the configuration items in need to be displayed Logo

  • text-color="variables.menuText": from @/styles/variables.scss In order to get scss object , To get the style

sidebar Pass through v-for Loop traversal sidebar-item Implement submenu :

<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />

sidebar-item Source code analysis

  • src/layout/components/Sidebar/SidebarItem.vue

Be careful : Components can call their own... In their own templates , But you need to give this component name attribute , Will report a mistake For recursive components, make sure to provide the "name" option.

<template>
  <div v-if="!item.hidden">
    <template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
      <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
        <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{
     'submenu-title-noDropdown':!isNest}">
          <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
        </el-menu-item>
      </app-link>
    </template>

    <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
      <template slot="title">
        <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
      </template>
      <sidebar-item v-for="child in item.children" :key="child.path" :is-nest="true" :item="child" :base-path="resolvePath(child.path)" class="nest-menu" />
    </el-submenu>
  </div>
</template>

<script> import path from 'path' import {
       isExternal } from '@/utils/validate' import Item from './Item' import AppLink from './Link' import FixiOSBug from './FixiOSBug' export default {
       name: 'SidebarItem', components: {
       Item, AppLink }, mixins: [FixiOSBug], props: {
       // route object item: {
       type: Object, required: true }, isNest: {
       type: Boolean, default: false }, basePath: {
       type: String, default: '' } }, data() {
       // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237 // TODO: refactor with render function this.onlyOneChild = null return {
      } }, methods: {
       hasOneShowingChild(children = [], parent) {
       const showingChildren = children.filter(item => {
       if (item.hidden) {
       return false } else {
       // Temp set(will be used if only has one showing child) this.onlyOneChild = item return true } }) // When there is only one child router, the child router is displayed by default if (showingChildren.length === 1) {
       return true } // Show parent if there are no child router to display if (showingChildren.length === 0) {
       this.onlyOneChild = {
       ... parent, path: '', noShowingChildren: true } return true } return false }, resolvePath(routePath) {
       if (isExternal(routePath)) {
       return routePath } if (isExternal(this.basePath)) {
       return this.basePath } return path.resolve(this.basePath, routePath) } } } </script>

sidebar-item Of props as follows :

  • item: Routing objects
  • basePath: Routing path

sidebar-item Show logical analysis :

  • adopt item.hidden Control whether the menu is displayed

  • adopt hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.always Show logic template Whether the menu is displayed ,template Represents a single menu

    • hasOneShowingChild: Determine whether there is only one sub route to be displayed
    • !onlyOneChild.children||onlyOneChild.noShowingChildren : Determine the submenu to be displayed , Does it include children attribute , If you include , It means that there may be sub menus in the sub menu , At this point, we need to judge again noShowingChildren attribute
    • !item.alwaysShow : Determine whether there is... In the route alwaysShow attribute , If there is , Then return to false, Don't show template menu , That is to say, as long as you configure alwaysShow Attributes will go directly to el-submenu Components

hasOneShowingChild Method source code details :

  • childrenrouter Object's children attribute
  • itemrouter object

hasOneShowingChild(children = [], parent) {
    
  const showingChildren = children.filter(item => {
    
    //  If  children  The route in contains  hidden  attribute , Then return to  false
    if (item.hidden) {
    
      return false
    } else {
    
      //  Assign a sub route to  onlyOneChild, Used to display when only one route is included  
      this.onlyOneChild = item
      return true
    }
  })

  //  If after filtering , Show only one route , Then return to  true
  if (showingChildren.length === 1) {
    
    return true
  }

  //  If there are no sub routes to show , Will  onlyOneChild  Of  path  Set an empty route , And add  noShowingChildren  attribute , Indicates that although there are sub routes , But there is no need to show the sub route 
  if (showingChildren.length === 0) {
    
    this.onlyOneChild = {
     ...parent, path: '', noShowingChildren: true }
    return true
  }

  //  return  false, It means that there is no need to show sub routes , Or more than one sub route to be shown 
  return false
}

If displayed template Components , First, we will show app-link Components , And then there was el-menu-item, The innermost nest is item Components

item Components need to be routed meta Contained in the title and icon attribute , Otherwise, the content will be rendered as empty vnode object

<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
  <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{
     'submenu-title-noDropdown':!isNest}">
    <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
  </el-menu-item>
</app-link>

If template The menu does not show , Show el-submenu menu ,el-submenu Nested components are used in logic , take sidebar-item Nested in el-submenu in :

<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
  <template slot="title">
    <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
  </template>
  <sidebar-item v-for="child in item.children" :key="child.path" :is-nest="true" :item="child" :base-path="resolvePath(child.path)" class="nest-menu" />
</el-submenu>

app-link Source code analysis

app-link Is a dynamic component , Through analysis to Parameters , If you include http The prefix becomes a a label , Otherwise it will become a router-link Components

  • component Is a built-in component

    propsisinline-template

    effect : according to is, To decide which component is rendered

<template>
  <component :is="type" v-bind="linkProps(to)">
    <slot />
  </component>
</template>

<script> import {
       isExternal } from '@/utils/validate' export default {
       props: {
       to: {
       type: String, required: true } }, computed: {
       isExternal() {
       return isExternal(this.to) }, type() {
       if (this.isExternal) {
       return 'a' } return 'router-link' } }, methods: {
       linkProps(to) {
       if (this.isExternal) {
       return {
       href: to, target: '_blank', rel: 'noopener' } } return {
       to: to } } } } </script>

isExternal Function is matched by a regular expression http link :

export function isExternal(path) {
    
  return /^(https?:|mailto:|tel:)/.test(path)
}

item Source code analysis

item Components are defined by render Function to complete component rendering

  • If item Can't get meta Medium icon The parent route will be taken icon

render Function parameter :

  • createElement function : The return value is a dummy DOM, namely VNode, That is, the rendered node

    createElement There are three parameters :

    1. To render html label 、 Components : { String | Object | Function }
    2. html Properties of :{ Object }
    3. Virtual child nodes VNodes, At present html The child elements of the tag :{ String | Array }
  • context : It contains props Equal parameter

<script> export default {
       name: 'MenuItem', functional: true, props: {
       icon: {
       type: String, default: '' }, title: {
       type: String, default: '' } }, render(h, context) {
       const {
       icon, title } = context.props const vnodes = [] if (icon) {
       if (icon.includes('el-icon')) {
       vnodes.push(<i class={
      [icon, 'sub-el-icon']} />) } else {
       vnodes.push(<svg-icon icon-class={
      icon}/>) } } if (title) {
       vnodes.push(<span slot='title'>{
      (title)}</span>) } return vnodes } } </script>

summary

sidebarsidebar It mainly includes el-menu Container assembly ,el-menu Middle traversal vuex Medium routes, Generate sidebar-item Components .sidebar The main configuration items are as follows :

  • activeMenu: According to the current route meta.activeMenu Properties control the highlighted menu in the sidebar
  • isCollapse: according to Cookie Of sidebarStatus Controls whether the sidebar is collapsed
  • variables: adopt @/styles/variables.scss fill el-menu Basic style of

sidebar-item : It is mainly divided into two parts :

  • The first part is when only one children Or not children Show when , The components shown include :
    • app-link: Dynamic components ,path When is a link , Is shown as a label ,path When routing , Is shown as router-link Components
    • el-menu-item: A menu item , When sidebar-item For the wrong nest When the component ,el-menu-item Will increase submenu-title-noDropdown Of class
    • itemel-menu-item Contents of Li , Mainly icon and title, When title Empty is , The entire menu item will not show
  • The second part is when children Show when there are more than two items , The components shown include :
    • el-submenu: Submenu component container , For nested submenu components
    • sidebar-itemel-submenu Iterations nested sidebar-item Components , stay sidebar-item There are two changes in the component :
      • Set up is-nest The attribute is true
      • according to child.path Generated base-path Property passed in sidebar-item Components

vue elementui navmenu Multi level navigation menu ( level 、 vertical )

Front end development and high-end operation

Type conversion

Fast turn Number

var a = '1'

console.log(typeof a)
console.log(typeof Number(a)) //  Common writing 
console.log(typeof +a)        //  High end writing 

Fast turn Boolean

var a = 0

console.log(typeof a)
console.log(typeof Boolean(a)) //  Common writing 
console.log(typeof !!a)        //  High end writing 

Mixed writing

  • First to Number, And then to Boolean
var a = '0'

console.log(!!a)  //  Direct transfer will get  true, Fall short of expectations 
console.log(!!+a) //  First to  Number  And then to  Boolean, In line with expectations 

JS and CSS Dual purpose style

template You need to dynamically define styles in , General practice :

<template>
  <div :style="{ color: textColor }">Text</div>
</template>

<script> export default {
       data() {
       return {
       textColor: '#ff5000' } } } </script>

Definition SCSS file

$menuActiveText:#409EFF;

:export {
  menuActiveText: $menuActiveText;
}

stay JS I quote :

  • Use import quote SCSS file
  • Definition computed take styles Object becomes a responsive object
  • stay template Use in syles object
<template>
  <div :style="{ color: styles.menuActiveText }">Text</div>
</template>

<script> import styles from '@/styles/variables.scss' export default {
       computed: {
       styles() {
       return styles } } } </script>

Continuous deconstruction

Extract an attribute from the first object element of the array , such as :err Object contains a errors Array ,errors Each object of the array contains a msg attribute

err = {
    
  errors: [
    {
    
      msg: 'this is a message'
    }
  ]
}

//  The fast extraction method is 
const [{
     msg }] = err.errors
//  If you don't use deconstruction, it is written as 
const msg = err.errors[0].msg

Redirect

Login redirection

  • src/views/login/index.vue Chinese vs $route monitor :
watch: {
    
  $route: {
    
    handler: function(route) {
    
      const query = route.query
      if (query) {
    
        this.redirect = query.redirect
        this.otherQuery = this.getOtherQuery(query)
      }
    },
    immediate: true
  }
}

this.getOtherQuery(query) The purpose of is to get the division redirect Query criteria other than , After successful login :

this.$store
  .dispatch('user/login', this.loginForm)
  .then(() => {
    
    this.$router.push({
    
      path: this.redirect || '/',
      query: this.otherQuery
    })
    this.loading = false
  })
  .catch(() => {
    
    this.loading = false
  })

Complete the redirected code :

this.$router.push({
    
  path: this.redirect || '/',
  query: this.otherQuery
})

vue-element-admin Special redirection components are provided , Source code is as follows :

<script> export default {
       created() {
       const {
       params, query } = this.$route const {
       path } = params this.$router.replace({
       path: '/' + path, query }) }, render: function(h) {
       return h() // avoid warning message } } </script>

Redirection component

The redirection component is configured with dynamic routing :

  • * Indicates that zero or more routes are matched , For example, the route is /redirect when , Can still match redirect Components . If you will * Get rid of

    At this point the route /redirect Will only match to Layout Components , And cannot match redirect Components

{
    
  path: '/redirect',
  component: Layout,
  hidden: true,
  children: [
    {
    
      path: '/redirect/:path(.*)',
      component: () => import('@/views/redirect/index')
    }
  ]
},

Breadcrumb navigation

el-breadcrumb-item

  • el-breadcrumb: Breadcrumb navigation container ,separator Controls the breadcrumb navigation text divider
  • el-breadcrumb-item: Bread crumbs sub project , have access to to Property to switch routes ,slot Can contain a Label to jump to the outer chain

Use to Properties and a The difference between label switching and routing is :to Attribute switching routing is a dynamic replacement App.vue Routing content in , and a Label switching routes will refresh the page

<el-breadcrumb separator="/">
  <el-breadcrumb-item :to="{ path: '/' }"> home page </el-breadcrumb-item>
  <el-breadcrumb-item><a href="/"> Activity management </a></el-breadcrumb-item>
  <el-breadcrumb-item> Activity list </el-breadcrumb-item>
  <el-breadcrumb-item> Activity details </el-breadcrumb-item>
</el-breadcrumb>

Routing and breadcrumb navigation mapping

Breadcrumb navigation template source code :

  • el-breadcrumb-item I made a judgment , If it is the last element or route redirect Property specified as noRedirect Then no link is generated , Otherwise... Will be used a Tag event triggers , But here we use @click.prevent The default... Is blocked a Tag event triggers , Instead of using custom handleLink Method to handle route jumps
<el-breadcrumb class="app-breadcrumb" separator="/">
  <transition-group name="breadcrumb">
    <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
      <span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{
   { item.meta.title }}</span>
      <a v-else @click.prevent="handleLink(item)">{
   { item.meta.title }}</a>
    </el-breadcrumb-item>
  </transition-group>
</el-breadcrumb>

The more important thing is levelList , It's through getBreadcrumb Method generated

The breadcrumb navigation implementation logic is as follows :

  • obtain this.$route.matched, And filter that it does not contain item.meta.title The item , Generate a new breadcrumb navigation array matched
  • Judge matched Whether the first item is dashboard, If not , Then add dashboard Navigate the first item for breadcrumbs
  • Filter again matched in item.meta.title Empty items and item.meta.breadcrumb by false The item
getBreadcrumb() {
    
  // only show routes with meta.title
  let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
  const first = matched[0]

  if (!this.isDashboard(first)) {
    
    matched = [{
     path: '/dashboard', meta: {
     title: 'Dashboard' }}].concat(matched)
  }

  this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
},

isDashboard(route) {
    
  const name = route && route.name
  if (!name) {
    
    return false
  }
  return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
},

When the route is switched , Will also call again getBreadcrumb Method generation

watch: {
    
  $route(route) {
    
    // if you go to the redirect page, do not update the breadcrumbs
    if (route.path.startsWith('/redirect/')) {
    
      return
    }
    this.getBreadcrumb()
  }
},

handleLink The source code of the method is as follows :

  • there pathCompile Used to solve the matching problem of dynamic routing
handleLink(item) {
    
  const {
     redirect, path } = item
  if (redirect) {
    
    this.$router.push(redirect)
    return
  }
  this.$router.push(this.pathCompile(path))
}

pathCompile(path) {
    
  // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
  const {
     params } = this.$route
  var toPath = pathToRegexp.compile(path)
  return toPath(params)
},
原网站

版权声明
本文为[Snow flies fast]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/03/202203010613541613.html