Vue-Element-Admin 中获得的Vue技巧__Vue.js
发布于 4 年前 作者 banyungong 2379 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

翻阅了Vue-Element-Admin源码之后获得的一些在写后台管理系统时的一些小技巧,比如看看大佬的代码是如何优雅,大佬如何封装业务组件,大佬如何规划一个后台管理系统的项目架构等等……

svg icon

二次封装时传入属性和事件

v-on="$listeners"封装组件时,可传入未识别的事件。v-bind="$attrs" 可传入未识别的属性

layout

Vuex集中管理状态

sidebar的状态,是否是移动端,是否浮动的header,是否展示标签,是否显示设置
根据状态渲染不同的class

移动端适配

将监听页面尺寸修改从而判断是否为移动端的业务 单独剥离 采用mixin的方式

区分内部路由和外部链接

动态组件配上 v-bind的独特写法 之前没接触过这种写法,可以根据不同需求进行不同的v-bind绑定

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

历史记录标签

1.横向滚动条可以用鼠标滚轮滚动
2.右键弹出菜单
3.滚动条可以滚动到特定的位置

处理横向滚动条

scroll-pane组件,监听鼠标滚轮事件,

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

更改滚动条的横向位移

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

右键菜单

@contextmenu.prevent.native

滚动到指定位置

不借助jquery操作dom
const $container = this.$refs.scrollContainer.$el
const $containerWidth = $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
        }
      }
    }

RgihtPanel

挂载到body上

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

封装create方法

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;

点击遮罩层关闭的写法

evt.target.closest 判断点击区域是否是遮罩处

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

路由切换过渡动画

<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>
/* 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);
}

页面进度条

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();
});

按钮级权限

自定义指令v-permission

逻辑: 传入该按钮的权限,获取当前用户的权限,判断用户权限是否在按钮权限中,如果不在代表没有权限,移除该按钮的DOM。

置顶

平滑滚动

源代码不容易理解 ,且使用了Interval,自己优化了一下,采用动画帧平滑置顶的方式

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

svg图标库

  1. 全局注册svg图表组件
  2. 自动导入svg资源
require.context('./svg', false, /\.svg$/)

动态加载脚本

Markdown 编辑器

tui-editor插件
配置项单独js管理

Sticky

监听滚动,使用getBoundingClientRect() API 获得元素的大小及其相对于视口的位置。
当高度小于传入的stickyTop时,将元素的position设为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
},

按钮水波效果

创建v-waves 指令
点击元素时,在元素中创建一个圆形的absolute的span,大小取决于点击元素的宽,位置取决于鼠标点击的位置,加上opacity和scale的动画。

注意: click事件的监听和解绑

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

图表

resize mixin

将视口resize的监听处理放在mixin中,要注意resize事件的解绑

表格

过滤器写法

<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]
    }
  },

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: Viva49641 原文链接:https://juejin.im/post/6859972542199758861

回到顶部