翻阅了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图标库
- 全局注册svg图表组件
- 自动导入svg资源
require.context('./svg', false, /\.svg$/)
动态加载脚本
Markdown 编辑器
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