Read the vue-element-admin source code to get some tips in writing a backend management system, such as how to look at the big guy code is elegant, how to package business components, how to plan a backend management system project architecture and so on……
# svg icon
## Attributes and events are passed in for secondary encapsulation. ‘V-on =”$listeners” ‘v-bind=”$attrs”‘ can pass in unrecognized attributes

layout

Vuex Centralized management status

Sidebar state, whether mobile, whether floating header, whether display labels, whether display Settings render different classes according to state

Mobile adaptation

Listen for page size changes to determine if the business is stripped separately for mobile using mixins

Distinguish between internal routes and external links

Dynamic components are equipped with a unique way of writing V-bind, which you have never seen before. You can use different V-bind bindings depending on your needs

<component :is="type" v-bind="linkProps(to)">
<slot />
</component>
methods: {
  linkProps(to) {
    if (this.isExternal) {
      return {
        href: to,
        target: '_blank'.rel: 'noopener'}}return {
    	to: to
    }
  }
}
Copy the code

History label

1. Horizontal scroll bar allows you to scroll with the mouse wheel. 2

Handles horizontal scrollbars

The Scroll Pane component listens for mouse wheel events,

<el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
<slot />
</el-scrollbar>
Copy the code

Change the horizontal displacement of the scroll bar

handleScroll(e) {
  const eventDelta = e.wheelDelta || -e.deltaY * 40
  const $scrollWrapper = this.scrollWrapper
  $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
}
Copy the code

Right-click menu

@contextmenu.prevent.native

Scroll to the specified position

Don’t use jquery dom operation const container = this. The container = this. The container = this. Refs. ScrollContainer. El < br / > constel < br / > const el

constcontainerWidth = $container.offsetWidth

moveToTarget(currentTag) {
      const $container = this.$refs.scrollContainer.$el
      const $containerWidth = $container.offsetWidth
      const $scrollWrapper = this.scrollWrapper
      const tagList = this.$parent.$refs.tag

      let firstTag = null
      let lastTag = null

      // find first tag and last tag
      if (tagList.length > 0) {
        firstTag = tagList[0]
        lastTag = tagList[tagList.length - 1]}if (firstTag === currentTag) {
        $scrollWrapper.scrollLeft = 0
      } else if (lastTag === currentTag) {
        $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
      } else {
        // find preTag and nextTag
        const currentIndex = tagList.findIndex(item= > item === currentTag)
        const prevTag = tagList[currentIndex - 1]
        const nextTag = tagList[currentIndex + 1]

        // the tag's offsetLeft after of nextTag
        const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing

        // the tag's offsetLeft before of prevTag
        const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing

        if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
          $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
        } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
          $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
        }
      }
    }
Copy the code

RgihtPanel

Mount to body

insertToBody() {
  const elx = this.$refs.rightPanel
  // const elx = this.$
  const body = document.querySelector('body')
  body.insertBefore(elx, body.firstChild)
}
Copy the code

Encapsulate the Create method

import Vue from 'vue'
function create(Component, props) {
  const vm = new Vue({
    render(h) {
    return h(Component, {props})
	}
}).$mount();
  document.body.appendChild(vm.$el);
  const comp = vm.$children[0];
  comp.remove = () = > {
  document.body.removeChild(vm.$el);
  vm.$destroy();
	}
	return comp;
}
export default create;
Copy the code

Click on the mask layer to close the script

Closest judge if the closest area is part of the mask

closeSidebar(evt) {
const parent = evt.target.closest('.rightPanel')
if(! parent) {this.show = false
window.removeEventListener('click'.this.closeSidebar)
Copy the code

Route switchover transition animation

<template>
  <section class="app-main">
    <transition name="fade-transform" mode="out-in">
      <keep-alive :include="cachedViews">
        <router-view :key="key" />
      </keep-alive>
    </transition>
  </section>
</template>
Copy the code
/* fade-transform */
.fade-transform-leave-active..fade-transform-enter-active {
  transition: all .5s;
}

.fade-transform-enter {
  opacity: 0;
  transform: translateX(-30px);
}

.fade-transform-leave-to {
  opacity: 0;
  transform: translateX(30px);
}
Copy the code

Page progress bar

import NProgress from 'nprogress'; // progress bar
import 'nprogress/nprogress.css'; // progress bar style

NProgress.configure({ showSpinner: false }); // NProgress Configuration

router.beforeEach((to, from, next) = > {
  // token start
  NProgress.start();
  next();
  NProgress.done();
});
Copy the code

Button level permissions

Custom instruction V-permission

Logic: Pass in the permission of the button, obtain the permission of the current user, determine whether the user permission is in the button permission, if not, remove the DOM of the button.

Placed at the top

Smooth scrolling

The source code is not easy to understand, and using Interval, I optimized it myself, using the animation frame smooth top way

backToTop() {
  if (this.isMoving) return
  const scrollTop = window.pageYOffset
  if (scrollTop > this.backPosition) {
    window.requestAnimationFrame(this.backToTop)
    window.scrollTo(0, scrollTop - scrollTop / 20)}}Copy the code

SVG icon library

  1. Globally register the SVG diagram component
  2. Automatically import SVG resources
require.context('./svg'.false./\.svg$/)
Copy the code

Dynamically loading scripts

Markdown editor

Tui-editor plug-in configuration items are managed separately with JS

Sticky

Listen for scrolling, using the getBoundingClientRect() API to get the size of the element and its position relative to the viewport. When the height is less than the passed stickyTop, set the element’s position to fixed.

handleScroll() {
  const width = this.$el.getBoundingClientRect().width
  this.width = width || 'auto'
  const offsetTop = this.$el.getBoundingClientRect().top
  if (offsetTop < this.stickyTop) {
    this.sticky()
    return
  }
  this.handleReset()
},
sticky() {
  if (this.active) {
    return
  }
  this.position = 'fixed'
  this.active = true
  this.width = this.width + 'px'
  this.isSticky = true
},
Copy the code

Button wave effect

When clicking on an element, the V-waves command creates a circular absolute span in the element, depending on the width of the clicked element and the position of the mouse click, plus animations for opacity and scale. Note: listen and unbind for the click event

.if(! el[context]) { el[context] = {removeHandle: handle
    }
  } else {
    el[context].removeHandle = handle
  }

  return handle
}

export default {
  bind(el, binding) {
    el.addEventListener('click', handleClick(el, binding), false)},update(el, binding) {
    el.removeEventListener('click', el[context].removeHandle, false)
    el.addEventListener('click', handleClick(el, binding), false)},unbind(el) {
    el.removeEventListener('click', el[context].removeHandle, false)
    el[context] = null
    delete el[context]
  }
}
Copy the code

The chart

resize mixin

Put the listening handling of viewport REsize in the mixin, taking note of the unbinding of resize events

form

Filter writing

<el-table-column label="Status" class-name="status-col" width="100">
  <template slot-scope="{row}">
    <el-tag :type="row.status | statusFilter">
      {{ row.status }}
    </el-tag>
	</template>
</el-table-column>  

filters: {
    statusFilter(status) {
      const statusMap = {
        published: 'success'.draft: 'info'.deleted: 'danger'
      }
      return statusMap[status]
    },
    typeFilter(type) {
      return calendarTypeKeyValue[type]
    }
  },
Copy the code