3

项目源码地址在文档最后

1.关于根据权限展示不同界面的实现

定义多个子组件,在父组件中引用
如图,红框中index.vue为父组件,上面文件夹均为子组件。
image.png

在父组件中引用并注册子组件
image.png

通过currentRole控制展示哪个组件
image.png

2.插槽 slot
  • 插槽(Slot)是Vue提出来的一个概念,正如名字一样,插槽用于决定将所携带的内容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。
  • 插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制

如图,在父组件中引入子组件并在中放内容
image.png

刚才放置的内容将在子组件的slot标签位置展示。
image.png

结果如图:
image.png
关于插槽还有很多应用,具体请参考:https://www.cnblogs.com/lovey...

3.获取当前页面路由信息
完整url: window.location.href
路由路径: this.$route.path
路由路径参数: this.$route.params
4.结合element-ui实现左侧导航栏

定义好路由信息
image.png

  • icon为对应图标
  • leaf判断是否有二级菜单。为true表示没有

左侧导航栏部分代码如下:

<el-menu
    class="el-menu-vertical-demo"
    background-color="#545c64"
    text-color="rgb(191, 203, 217)"
    router
    unique-opened
    active-text-color="rgb(64, 158, 255)"
    :default-active="$route.path"
    @open="handleOpen"
    @close="handleClose">
        <template v-for="(item,index) in $router.options.routes" v-key='item.path'> 
            <el-submenu :index="index+''" v-if="!item.leaf">
                <template slot="title"><i :class="'iconfont '+item.icon"></i>{{item.name}}</template>
                <el-menu-item  v-for='(child,index) in item.children' :index='child.path' :key='index'>
                    <template slot="title">
                        <span>{{child.name}}</span>
                    </template>
                </el-menu-item>
            </el-submenu>
            <el-menu-item :index='item.children[0].path' v-if="item.leaf">
                <template slot="title">
                    <i :class="'iconfont '+item.icon"></i>
                    <span>{{item.name}}</span>
                </template>
        </el-menu-item>
    </template>
</el-menu>
  • router : 是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转
  • unique-opened : 是否只保持一个子菜单的展开
  • default-active : 默认高亮菜单

解析:遍历全部路由信息 $router.options.routes,通过leaf值判断是否有二级菜单。有二级菜单需用el-submenu

效果图如下:
image.png

5.导航栏增加展开收起功能

el-menu增加collapse属性,通过控制该值控制导航栏展开收起。
image.png
通过点击图标控制isCollapse。左侧导航栏收起时 右侧宽度也需改变,所以此项目选用flex布局,右侧设置样式flex:1
image.png
需增加样式

 .el-menu-vertical-demo:not(.el-menu--collapse) {
        width: 200px;
  }

效果图如下:
image.png

6.浏览器顶部进度条nprogress

安装:npm install \--save nprogress
使用:

//route.js
//导入
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'

router.beforeEach((to, from, next) => {
  NProgress.start()
  next()
})

router.afterEach(() => {
  NProgress.done()
})

效果图:
image.png

可手动修改颜色:

 #nprogress  .bar  { 
    background: red !important; //自定义颜色 
 }
 
7.screenfull实现全屏操作并监听ESC

安装 npm install --save screenfull
引入 import screenfull from 'screenfull'

通过fullFlag展示当前状态(展开/收起)
image.png

full方法实现全屏:

  full:function(){
    if (!screenfull.isEnabled) { // 如果不允许进入全屏,发出不允许提示
        this.$message({
            message: '不支持全屏',
            type: 'warning'
        })
        return false
    }
    this.fullFlag=!this.fullFlag
    screenfull.toggle();
},

当点击ESC退出全屏时,需改变当前状态(即收起要变成展开),这里通过监听浏览器大小变化结合screenfull.isFullscreen实现
image.png

mounted(){
      let me=this;
      window.onresize=function(){
        // 全屏下监控是否按键了ESC
        if (!me.checkFull()) {
        // 全屏下按键esc后要执行的动作
            me.fullFlag = false
        }
      }
  },

checkFull方法如下:
image.png

   checkFull() {
        var isFull = screenfull.isFullscreen;
        if (isFull === undefined) {
            isFull = false;
        }
        return isFull;
    }

全屏状态实现效果:

image.png

8.关于弹框el-dialog及自定义标题

自定义标题:
image.png

通过dialogVisible值控制弹框展示/隐藏:

<el-dialog title="提示" :visible.sync="dialogVisible" width="30%" v-dialogDrag>
     <div slot="title" class="header-title">
         <span class="title-name">
             <i class='iconfont iconapplications'></i>
              自定义标题
         </span>
     </div>
     <span>这是一段信息</span>
     <span slot="footer" class="dialog-footer">
         <el-button @click="dialogVisible = false" size='mini'>取 消</el-button>
         <el-button type="primary" @click="dialogVisible = false" size='mini'>确 定</el-button>
      </span>
</el-dialog>
9.Vue.directive自定义指令实现可拖动的弹框el-dialog

我们先来说说Vue.directive

Vue.directive:自定义指令,类似内置的v-if,v-show。即可根据自己需求增加指令。

来看下官网的案例:
需求:实现在打开页面后还没点击过任何内容,这个输入框就处于聚焦状态。

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

再来介绍下他的钩子函数
一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
  • componentUpdated:指令所在组件的 VNode及其子 VNode全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

接下来我们来看一下钩子函数的参数 (即elbindingvnodeoldVnode)。
指令钩子函数会被传入以下参数:
钩子函数参数:

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下 property:

    • name:指令名,不包括v-前缀。
    • value:指令的绑定值,例如:v-my-directive="1 + 1"中,绑定值为2
    • oldValue:指令绑定的前一个值,仅在updatecomponentUpdated钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。例如v-my-directive="1 + 1"中,表达式为"1 + 1"
    • arg:传给指令的参数,可选。例如v-my-directive:foo中,参数为"foo"
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar中,修饰符对象为{ foo: true, bar: true }
  • vnode:Vue 编译生成的虚拟节点。移步VNode API来了解更多详情。
  • oldVnode:上一个虚拟节点,仅在updatecomponentUpdated钩子中可用。

除了el之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的dataset来进行。

具体请看官网:https://cn.vuejs.org/v2/guide...

接下来实现弹框的拖拽:
main.js中定义:

//main.js
Vue.directive('dialogDrag', {
  bind(el, binding, vnode, oldVnode) {
    const dialogHeaderEl = el.querySelector('.el-dialog__header')
    const dragDom = el.querySelector('.el-dialog')
    dialogHeaderEl.style.cursor = 'move'

    // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
    const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null)

    dialogHeaderEl.onmousedown = (e) => {
      // 鼠标按下,计算当前元素距离可视区的距离
      const disX = e.clientX - dialogHeaderEl.offsetLeft;
      const disY = e.clientY - dialogHeaderEl.offsetTop;

      const screenWidth = document.body.clientWidth;
      const screenHeight = document.body.clientHeight;
      const minDragDomLeft = dragDom.offsetLeft;
      const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDom.offsetWidth;
      const minDragDomTop = dragDom.offsetTop;
      const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDom.offsetHeight;

      // 获取到的值带px 正则匹配替换
      let styL, styT

      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
      if (sty.left.includes('%')) {
        styL = +screenWidth * (+sty.left.replace(/\%/g, '') / 100)
        styT = +screenHeight * (+sty.top.replace(/\%/g, '') / 100)
      } else {
        styL = +sty.left.replace(/\px/g, '')
        styT = +sty.top.replace(/\px/g, '')
      }
      document.onmousemove = function(e) {
        // 通过事件委托,计算移动的距离
        var  l = e.clientX - disX;
        var  t = e.clientY - disY;
        // 边界处理
        if (-(l) > minDragDomLeft) {
            l = -minDragDomLeft
        } else if (l > maxDragDomLeft) {
            l = maxDragDomLeft
        }
        if (-(t) > minDragDomTop) {
            t = -minDragDomTop
        } 
        else  if (t > maxDragDomTop) {
            t = maxDragDomTop
        }
        // 移动当前元素
        dragDom.style.left = `${l + styL}px`
        dragDom.style.top = `${t + styT}px`
      }
      document.onmouseup = function(e) {
        document.onmousemove = null
        document.onmouseup = null
      }
    }
  }
})

dialog中增加该属性,就可以实现啦。
image.png

基于上面的自定义拖拽方法,画了个简图以便理解(top同理):
未命名文件.png

10.router.beforeEach结合addRouters实现根据用户权限控制路由(导航栏)

先来简单介绍下router.beforeEach
导航守卫
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中

每个守卫方法接收三个参数:

  • to: Route: 即将要进入的目标
  • from: Route: 当前导航正要离开的路由
  • next: Function: 一定要调用该方法来resolve这个钩子。执行效果依赖next方法的调用参数。

    • next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是confirmed(确认的)。
    • next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到from路由对应的地址。
    • next('/')或者next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向next传递任意位置对象,且允许设置诸如replace: truename: 'home'之类的选项以及任何用在router-link的toprop中的选项。
    • next(error): (2.4.0+) 如果传入next的参数是一个Error实例,则导航会被终止且该错误会被传递给router.onError()注册过的回调。

确保要调用next方法,否则钩子就不会被 resolved。
好吧 我摊牌了 我是从官网复制来的,想看更多欢迎去官网:https://router.vuejs.org/zh/g...

接下来我们来看看具体如何实现:

定义路由的文件中分成几个数组
图中constantRoutes为所有用户都可以访问的路由,adminRoutes为用户名为‘admin’登录才可以访问的路由。
image.png
先赋值当前可以访问的路由:

const router = new Router({
  routes: constantRoutes,
});
export default router;

登录时,在session里存入用户名,用来判断是否已登录及用户名

login:function(){
    let me=this;
    me.$refs.ruleForm.validate((valid) => {
        if (valid) {
            sessionStorage.setItem('user',me.loginForm.user);
            if(sessionStorage.getItem('user')){
                me.$router.push({path:"/index"})
            }
        }
    })
}

main.js中控制路由

let flag=false;//此字段用来防止刷新时重复添加路由
router.beforeEach((to, from, next) => {
  NProgress.start()
  let user=sessionStorage.getItem('user');
  if(to.path=='/login'){//用来防止一直跳入login,进入死循环
    next();
  }
  //判断是否登录了,当然真实项目判断权限不会这么简单,此案例只提供思路及基本使用
  if(user){
    if(to.path=='/login'){
      next('/');//登录后在想跳入login时直接跳入首页
    }
    if(flag){
      next();
    }
    else {
      let routes=[];
      if(user=='admin'){//判断为管理员
        routes=adminRoutes;
        router.addRoutes(routes);//添加管理员的路由信息
        router.options.routes = router.options.routes.concat(routes);
        //由于addRoutes只会注册路由表,并没有更新router.options.routes的数据
      }
      flag=true;
      next({...to, replace: true});
    }
  }
  else {
    if(to.path!='/login'){
      next('/login');
    }
    NProgress.done()
  }
})

成果:当没有登陆时会强制跳转至登录页,登录后用户名为admin时展示:
image.png
image.png

当不为admin时:
image.png

11.密码的显示隐藏
//通过控制type属性实现控制密码的展示隐藏
<el-form-item label="" prop="password">
    <el-input placeholder="请输入密码" v-model="loginForm.password" :type="passwordType" auto-complete="new-password">
        <i slot="prefix" class="iconfont iconmima"></i>
        <i slot="suffix" class="iconfont iconbiyan pointer" @click='passwordType=""' v-if='passwordType=="password"'></i>
        <i slot="suffix" class="iconfont iconeye pointer" @click='passwordType="password"' v-else></i>
    </el-input>
</el-form-item>

实现效果:
image.png

image.png

12.登出功能

使用element的el-dropdown

<el-dropdown class='avaImg' trigger="click">
//trigger 设置下拉框展示触发方式,默认为触摸展示
<span class="el-dropdown-link">
    <img src="../assets/img.png" alt="">
    <div>
        <i class="iconfont iconxiala" style='padding:0 5px;font-size:15px;'></i>
    </div>
</span>
<el-dropdown-menu slot="dropdown" >
    <el-dropdown-item>
        <span @click='logOut'>登出</span>
    </el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>

登出方法:

logOut:function(){
    let me=this;
    this.$confirm('确认要登出吗?', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    }).then(() => {
        sessionStorage.setItem('user','');
        me.$router.push('/login');
        window.location.reload();//更新main.js的flag值,重新根据角色获取路由
    }).catch(() => {
    });
}

实现效果:
image.png

13.结合breadcrumb实现展示面包屑
<transition mode="out-in">
    <el-breadcrumb separator="/">
        <el-breadcrumb-item :to="{ path: '/index' }">首页</el-breadcrumb-item>
        <el-breadcrumb-item v-for='item in breadcrumb' :key='item.name'>{{item.name}}</el-breadcrumb-item>
    </el-breadcrumb>
</transition>

监听路由变化实时更新(进入界面也要初始化,方法同):

watch: {
    $route(route) {
        let match=route.matched;
        match=match.filter(item => item.name&&item.name!='首页' );
        this.breadcrumb=match;
    }
  }

效果图:
image.png

14.结合clipboard实现文本复制

实现步骤:

安装clipboard

npm install clipboard --save

初始化

import Clipboard from 'clipboard'
var clipboard = new Clipboard('.btn');
 <el-input id="foo" placeholder="请输入内容" v-model="ipt">
    <el-tooltip class="item" effect="dark" content="点我复制哦" placement="top" slot="append">
        <el-button  class="btn" data-clipboard-target="#foo">
            <i class='iconfont iconfuzhi'></i>
        </el-button>
    </el-tooltip>
</el-input>

定义回调函数

clipboard.on('success', function(e) {
    me.$message({
        message: '复制成功',
        type: 'success'
    });
    e.clearSelection();
});

clipboard.on('error', function(e) {
    me.$message({
        message: '复制失败',
        type: 'success'
    });
});

实现效果:
image.png
详细请看:http://www.clipboardjs.cn/

源码地址:https://github.com/myweiwei/vue-
将不断更新完善,期待您的批评指正!


薇薇
298 声望24 粉丝

路漫漫其修远兮,吾将上下而求索