tommyee

tommyee 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

tommyee 赞了文章 · 4月29日

我理解的关于Vue.nextTick()的正确使用

关于作者

程序开发人员,不拘泥于语言与技术,目前主要从事PHP和前端开发,使用Laravel和VueJs,App端使用Apicloud混合式开发。合适和够用是最完美的追求。

个人网站:http://www.linganmin.cn

最近刚写了一个手机在线播放的H5电影站:http://www.ifilm.ltd

什么是Vue.nextTick()

官方文档解释如下:

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

我理解的官方文档的这句话的侧重点在最后那半句获取更新后的DOM,获取更新后的DOM言外之意就是什么操作需要用到了更新后的DOM而不能使用之前的DOM或者使用更新前的DOM或出问题,所以就衍生出了这个获取更新后的DOM的Vue方法。所以放在Vue.nextTick()回调函数中的执行的应该是会对DOM进行操作的 js代码,比如Swiper扩展包的

var swiper = new Swiper('.swiper-container', {
                    pagination: '.swiper-pagination',
                    nextButton: '.swiper-button-next',
                    prevButton: '.swiper-button-prev',
                    paginationClickable: true,
                    spaceBetween: 30,
                    centeredSlides: true,
                    autoplay: 2500,
                    autoplayDisableOnInteraction: false
                });

什么时候需要用的Vue.nextTick()

  1. 你在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中。原因是什么呢,原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。

  2. 在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick()的回调函数中。

原因是,Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环 (event loop) 当中观察到数据变化的 watcher 推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOm操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。
当你设置 vm.someData = 'new value',DOM 并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。

安小下同学

查看原文

赞 103 收藏 115 评论 17

tommyee 提出了问题 · 2019-11-26

host文件里的内容都被注释了是怎么生效的?

image

如图所示,都被注释了 localhost指向127.0.0.1是咋生效的?

关注 2 回答 2

tommyee 提出了问题 · 2019-11-26

host文件里的内容都被注释掉了它是怎么生效的?

image

如图所示,都被注释了 localhost指向127.0.0.1是咋生效的?

关注 2 回答 1

tommyee 提出了问题 · 2019-05-12

vscode所有依赖的vue文件都有红色波浪线是什么情况

clipboard.png

clipboard.png
如图,应该是vetur的问题,百度很多都没找到解决方法。

关注 4 回答 3

tommyee 提出了问题 · 2019-03-02

在自己单独的分支git pull会产生冲突吗?

比如自己的分支a,这个分支只有我自己用。我每次git pull origin a时是不是都不会有冲突?

关注 2 回答 1

tommyee 提出了问题 · 2019-02-23

解决关于git checkout -b和git push的几个小问题!

1、假如现在在远程拉了代码下来,有两个分支 master和a分支,我在master分支上git checkout -b b,建一个新的b分支,和我切换到a分支上再git checkout -b b有区别吗?
2、现在在b分支上代码写完了要提交到远程仓库,git add .和git commit后是直接git push还是
git push origin b,(和git push origin b:b有啥区别?)
3、现在在b分支上代码写完了要提交到远程仓库,git add .和git commit后git push origin,和git push origin a又是什么情况?

刚学git这几点不是很懂,望大佬解答

关注 3 回答 1

tommyee 提出了问题 · 2019-02-22

解决element table表格通过属性绑定的原理是什么?

<template>

<el-table
  :data="tableData"
  style="width: 100%">
  <el-table-column
    prop="date"
    label="日期"
    width="180">
  </el-table-column>
  <el-table-column
    prop="name"
    label="姓名"
    width="180">
  </el-table-column>
  <el-table-column
    prop="address"
    label="地址">
  </el-table-column>
</el-table>

</template>
像这个prop和label,他的背后原理能简单讲下吗?为什么这里加冒号!直接这样就绑定### 问题描述

问题出现的平台版本及自己尝试过哪些方法

相关代码

// 请把代码文本粘贴到下方(请勿用图片代替代码)

你期待的结果是什么?实际看到的错误信息又是什么?

关注 1 回答 1

tommyee 评论了文章 · 2019-02-20

Vue的slot-scope的场景的个人理解

Vue的slot-scope的场景的个人理解

这篇文章不是单纯把文档的话和api拿来翻译和演示,而是谈谈我对于slot-scope的使用场景的个人理解,如果理解错误,欢迎讨论!

Vue的插槽slot,分为3种

  • 匿名插槽
  • 具名插槽
  • 作用域插槽

前两种很好理解,无论就是子组件里定义一个slot占位符,父组件调用时,在slot对应的位置填充模板就好了。

作用域插槽的慨念,文档却只有一句简单的描述

有的时候你希望提供的组件带有一个可从子组件获取数据的可复用的插槽。

网络上大多数文章,也是千篇一律的翻译这句话,可是仅凭这一句话,我想象不到slot-scope的使用场景。

前言

介绍了写这篇文章的来由,接下来简述一下本文的脉络

  • 首先,我会结合文档上todo-list的例子,来简单说明一下slot-scope的使用方式
  • 其次,会使用但不理解什么时候用,就没有什么意义,所以本文第二部分,谈一下个人对于其使用场景的理解
  • 最后,是我翻阅网络上关于slot-scope时,看到的一些我觉得不太恰当的案例

官方文档slot-scope的示例

下面是2个父子的vue组件,先解释一下2个组件做了什么事情

  • 父组件仅仅是调用了子组件
  • 子组件内部实现了一个todolist列表

我建议从数据流动的角度,理解插槽作用域的使用方式,(先学会怎么用,暂时不用理解为什么要这么用,使用场景是第二部分)

  • 1.父组件传递了todos数组给子组件
  • 2.子组件通过props接受了数组数据,这里应该没有任何问题
  • 3.子组件拿到数组后v-for渲染列表,并且通过 <slot :todo="todo">的方式,把数组内的每一个todo对象,传递给父组件
  • 4.父组件通过slot-scope="slotProps"的方式,接受todo对象,之后就可以通过slotProps.todo.xxx的方式来使用了

所以数据的流动经历了

  • 父组件传递todos数组给子组件
  • 子组件遍历todos数组,把里面的todo对象传递给父组件

好啦,这就是slot-scope的使用方法,就这么简单,完结撒花~

我贴出全部代码,方便大家自己研究

父组件的源码,也就是调用者

<template>
  <todo-list :todos="todos">
    <template slot-scope="slotProps">
      <span v-if="slotProps.todo.isComplete">✓</span>
      <span>{{slotProps.todo.text}}</span>
    </template>
  </todo-list>
</template>

<script>
import todoList from './todoList'
export default {
  data () {
    return {
      todos: [
        {
          id: 0,
          text: 'ziwei0',
          isComplete: false
        },
        {
          text: 'ziwei1',
          id: 1,
          isComplete: true
        },
        {
          text: 'ziwei2',
          id: 2,
          isComplete: false
        },
        {
          text: 'ziwei3',
          id: 3,
          isComplete: false
        }
      ]
    }
  },

  components: {
    todoList
  },

}
</script>

子组件源码,也就是封装组件的人

<template>
  <ul>
    <li v-for="todo in todos" :key="todo.id">
      <slot :todo="todo">
      </slot>
    </li>
  </ul>
</template>

<script>
export default {
  props: {
    todos: {
      type: Array
    }
  }
}
</script>

slot-scope的使用场景的个人理解

想象一个场景:

当你要给同事封装一个列表组件,你就需要使用作用域插槽(注意是列表或者类似列表的组件)

你开发的这个列表组件要如何使用呢?

一般来说作为列表组件的调用者,你的同事先做ajax请求,拿到一个这样的数组

    todos: [
        {
          id: 0,
          text: 'ziwei0',
          isComplete: false
        },
        {
          text: 'ziwei1',
          id: 1,
          isComplete: true
        },
        {
          text: 'ziwei2',
          id: 2,
          isComplete: false
        },
        {
          text: 'ziwei3',
          id: 3,
          isComplete: false
        }
      ]

之后会把todso传递给列表组件吧,那么列表组件内部做什么事情呢?

列表内部肯定会v-for去帮你的同事渲染这个数组嘛。 就类似element-ui里的table组件一样

问题的关键就在这里

列表组件的循环,是发生在组件内部的,所以通过 v-for="todo in todos" ,列表组件很容易拿到每一项todo,但列表拿到数据没用呀,列表只是一个瓜皮,它又不懂业务逻辑

这个数据是你同事的业务数据,所以这个数据必须得交给组件的调用者,也就是把数据交给你的同事才对。

那么你怎样才能把每一项的todo数据给传递出去呢?

你会发现没有办法!

无论是用$emit、vuex还是localStorage,可以考虑一下,会发现没有合适的时机,能让你把todo传递出去

所以为了应对这个场景下,发明了作用域插槽,列表组件可以通过<slot :todo="todo"></slot>传递todo出去

你的同事可以通过 slot-scope="slotsProps"拿到todo。

回答几个疑问,其实如果你看懂上面的问题,应该可以回答下面的问题。这也是我曾经的疑问

疑问1:一般不是我们传参数来调用组件吗?为什么组件还把数据传递回来?

的确,调用ui组件时一般是我们传递配置参数给他们。

但是就像elemnt-ui的table组件,你把数组传递给table后,是不是有时候需要拿到某一行的row对象

并根据row对象里的字段,来判断一些内容的显示隐藏?

因为循环的过程发生在table组件内部,所以table组件可以方便的获取到每一项数据,但是这些数据最终不是给组件的,而是我们自己要用的业务数据。所以也需要一个方式,让调用者能拿到自己想要的数据
疑问2: 既然子组件最终还要把我给他的数据,再返还给我,那我当初还干嘛给它,能不能就自己在父组件里玩?
如果你不把数据给子组件当然可以。但是就等于抛弃掉了子组件的封装,只能你直接在父组件自己写一个列表

毕竟你不把数据给子组件,子组件还渲染个锤子?没有父子关系的话,也就不用什么插槽了。

但是咱不是为了封装后,可以复用嘛,总不能永远不用组件嘛
疑问3: 父组件需要子组件的数据?那不会有$emit和vuex嘛,为什么要有slot-scope?
$emit和vuex是数据传递的一种方法,但是你可以尝试用$emit和vuex把todo传递给父组件。

你会发现的确没有合适的钩子、时机来$emit数据

一些网上个人认为不太恰当的例子

我认为几种说法是不太恰当的,也是给我造成一些困惑的

  • slot-scope是什么?就是把子组件的数据传递给父组件的一种方式

这种说法,会让我觉得slot-scope跟emit和vuex是一类东西

  • 在一些例子中,把数据定义在写死在列表组件中,展示如何把数据传递出去

这些举例子的不恰当之处,我觉得是不应该把数据定义在子组件里。

因为真正的使用场景下,子组件的数据都是来自父组件的。作为组件内部应该保持纯净。

就像element-ui里的table组件,肯定不会定义一些数据在组件内部,然后传递给你。

table组件的数据都是来自调用者的,然后table会把每一行的row,在开发者需要时,传递出去。

这些例子虽然不是错误,但是我觉得反而不利于理解slot-scope

查看原文

tommyee 提出了问题 · 2019-01-18

解决const声明的变量不得改变值。为什么这样值变了却可以?

const scrollTop = document.documentElement.scrollTop
滚动条事件,拖滚动条打印scrollTop每次打印的值不一样!不是说值不能改变吗?为什么这样可以呢?

关注 4 回答 3

tommyee 评论了文章 · 2019-01-17

基于iview的后台管理模板布局页面

代码在线预览

最近项目使用iview来开发,iview UI设计还是蛮好的相对于element-ui,但是后台模板布局是块硬伤,所以自己写了一个通用页面,以便以后可以直接拿来用,下面贴上代码,分享一下:
view admin的后台管理系统模板,但是个人愚见感觉有点太重了,所以自己结合iview的页面布局自己写了一套通用的模板页面,方便后续开发使用。

clipboard.png

clipboard.png

首先,我们新建一个Layout.vue页面,这个页面就是整个布局模板的页面,我们设置好sidebar、topbar、以及中间的content就好,然后content,我们放上<router-view>就可以了,路由就随便你怎么跳转了。

Layout.vue

<style lang="less" scoped>
@import '../../../assets/gls-theme/common.less';
.ivu-layout.ivu-layout-has-sider{
    height: 100%;
}
.ivu-layout-sider{
    background: #fff;
}
.ivu-layout-header{
    height: 100px;
    line-height: 18px;
}
.ivu-menu{
    height: 100%;
}
.admin-layout-container{
    position: absolute;
    width: 100%;
    height: 100%;
    .layout{
        background: #f5f7f9;
        position: relative;
        overflow: hidden;
        height: 100%;
        & .dropdown-wrap{
            background: rgb(73, 80, 96);
        }
        & .logo{
            background: #4c364f80;
            border-bottom: 1px solid #363e4f;
            width: auto;
            height: 60px;
            display: flex;
            text-align: center;
            align-items: center;
            justify-content: center;
            cursor: pointer;
        }
    }
    .layout-header-bar{
        background: #fff;
    }
    .layout-logo-left{
        width: 90%;
        height: 30px;
        background: #5b6270;
        border-radius: 3px;
        margin: 15px auto;
    }
    .menu-icon{
        transition: all .3s;
    }
    .rotate-icon{
        transform: rotate(-90deg);
    }
    .menu-item span{
        display: inline-block;
        overflow: hidden;
        width: 69px;
        text-overflow: ellipsis;
        white-space: nowrap;
        vertical-align: bottom;
        transition: width .2s ease .2s;
    }
    .menu-item i{
        transform: translateX(0px);
        transition: font-size .2s ease, transform .2s ease;
        vertical-align: middle;
        font-size: 16px;
    }
    .collapsed-menu span{
        width: 0px;
        transition: width .2s ease;
    }
    .collapsed-menu i{
        transform: translateX(5px);
        transition: font-size .2s ease .2s, transform .2s ease .2s;
        vertical-align: middle;
        font-size: 22px;
    }
}

</style>
<template>
  <section class="admin-layout-container">
      <div class="layout">
        <Layout>
            <Sider ref="side1" hide-trigger collapsible :collapsed-width="78" v-model="isCollapsed" style="background: rgb(73, 80, 96);">
                <div class="logo" >
                    <img :data-original="logo" width="100" v-if="!isCollapsed"/>
                    <Avatar icon="person" size="large" v-else/>
                </div>
                <Menu 
                    ref="side_menu"
                    :active-name="activeMenuName" 
                    :open-names="openMenuName"
                    theme="dark"
                    width="auto" 
                    :class="menuitemClasses"
                    @on-select="choosedMenu"
                    v-if="!isCollapsed">
                    <template v-for="(menu,menu_index) in menus">
                        <Submenu :name="menu.name" v-if="menu.children">
                            <template slot="title">
                                <Icon :size="20" :type="menu.icon"></Icon>
                                {{menu.title}}
                            </template>
                            <MenuItem :name="child.name" v-for="(child ,child_index) in menu.children" :key="child_index">
                                <Icon :size="20" :type="child.icon"></Icon>
                                {{child.title}}
                            </MenuItem>
                        </Submenu>
                        <MenuItem :name="menu.name" v-if="!menu.children && menu.showInMenus">
                             <Icon :size="20" :type="menu.icon"></Icon>
                            {{menu.title}}
                        </MenuItem>
                    </template>                    
                </Menu>
                <div class="dropdown-wrap">
                    <template v-for="(menu,menu_index) in menus" v-if="isCollapsed">
                        <Dropdown transfer placement="right-start" v-if="menu.children" @on-click="dropdownClick">
                            <Button style="width: 85px;margin-left: -5px;padding:10px 0;" type="text">
                                <Icon :size="25" color="#fff" :type="menu.icon"></Icon>
                            </Button>
                            <DropdownMenu style="width: 200px;" slot="list">
                                <template v-for="(child, i) in menu.children">
                                    <DropdownItem :name="child.name">
                                        <div style="display:flex;align-items:center;">
                                            <Icon :size="16" :type="child.icon"></Icon>
                                            <span style="padding-left:10px;">
                                                {{ child.title }}
                                            </span>
                                        </div>
                                    </DropdownItem>
                                </template>  
                            </DropdownMenu>
                        </Dropdown>
                        <Dropdown transfer v-if="!menu.children && menu.showInMenus" placement="right-start" @on-click="dropdownClick">
                            <Button style="width: 85px;margin-left: -5px;padding:10px 0;" type="text">
                                <Icon :size="25" color="#fff" :type="menu.icon"></Icon>
                            </Button>
                            <DropdownMenu style="width: 200px;" slot="list">
                                <DropdownItem :name="menu.name">
                                    <div style="display:flex;align-items:center;">
                                        <Icon :size="16" :type="menu.icon"></Icon>
                                        <span style="padding-left:10px;">
                                            {{ menu.title }}
                                        </span>
                                    </div>
                                </DropdownItem>
                            </DropdownMenu>
                        </Dropdown>
                    </template>         
                </div>            
            </Sider>
            <Layout>
                <Header :style="{position: 'fixed',
                        width: isCollapsed?'calc(100% - 78px)':'calc(100% - 200px)',
                        padding: 0,
                        display:'flex',
                        flexDirection:'column',
                        zIndex:20
                    }" class="layout-header-bar">
                    <div style="
                        display:flex;
                        align-tems:center;
                        justify-content:space-between;
                        position: relative;
                        height:60px;
                        line-height: 60px;
                        z-index: 1;
                        box-shadow: 0 2px 1px 1px rgba(100, 100, 100, 0.1);">
                        <div style="display:flex;align-items:center;">
                            <Icon @click.native="collapsedSider" :class="rotateIcon" :style="{margin: '0 20px 0'}" type="navicon-round" size="24"></Icon>
                            <span style="font-size:18px;font-weight:bold">{{user.mechanism.name}}后台管理系统</span>
                        </div>
                        <div style="margin-right:20px">
                            <!-- <Button type="text" icon="person" size="large">个人中心</Button>
                            <Button type="text" icon="android-notifications" size="large" @click="clickNotice">消息通知</Button> -->
                            <Button type="text" icon="android-exit" size="large" @click="quit">退出系统</Button>
                        </div>
                    </div>     
                    <div style="display: flex;
                                position: relative;
                                padding-left:10px;
                                height: 40px;
                                background: #f5f7f9;
                                align-items: center;
                                box-shadow: 0 2px 1px 1px rgba(100, 100, 100, 0.1);">
                        <template v-for="(tab,tab_index) in tags">
                            <Tag type="dot" 
                            :closable="tab.closable" 
                            :color="tab.choosed ? 'blue':'#e9eaec'"
                            :name="tab.name"
                            @click.native="clickTag(tab)"
                            @on-close="closeTag" >
                                {{tab.title}}
                            </Tag>
                        </template>
                    </div>                  
                </Header>                
                <Content :style="{
                    height: 'calc(100% - 100px)',
                    position: 'absolute',
                    top: '100px',
                    overflow: 'auto',
                    padding: '10px',
                    width:isCollapsed?'calc(100% - 78px)':'calc(100% - 200px)'
                    }">
                    <!--保存组件状态到内存,避免重新渲染-->
                    <keep-alive>
                        <router-view/>    
                    </keep-alive>               
                </Content>
            </Layout>
        </Layout>
    </div>
  </section>
</template>
<script>
import {mapActions,mapState} from 'vuex'

export default {
    data(){
        return{
            logo:`${this.$qiniuFileUrl}${process.env.LOGO}`,
            isCollapsed: false,
            // ------------------------------  菜单操作开始  --------------------------------
            title:'首页',
            activeMenuName:'admin',
            openMenuName:[],
            menus:[
                {
                    title:'首页',
                    num:1,
                    name:'admin',
                    icon:'home',
                    href:'/admin',
                    closable:false,
                    showInTags:true,
                    showInMenus:true,
                    choosed:true,
                },
                {
                    title:'课程管理',
                    name:'course-manage',
                    icon:'ios-bookmarks',
                    href:'/admin/course',
                    closable:true,
                    showInTags:false,
                    showInMenus:true,
                    choosed:false,
                },
                {
                    title:'老师管理',
                    name:'teacher-manage',
                    icon:'person-stalker',
                    href:'/admin/teacher',
                    closable:true,
                    showInTags:false,
                    showInMenus:true,
                    choosed:false,
                },               
                {
                    title:'学生管理',
                    name:'student-manage',
                    icon:'university',
                    href:'/admin/student',
                    closable:true,
                    showInTags:false,
                    showInMenus:true,
                    choosed:false,
                },
                {
                    title:'课堂',
                    name:'class-manage-parent',
                    icon:'easel',
                    children:[
                        {
                            title:'课堂管理',
                            name:'classroom-manage',
                            icon:'erlenmeyer-flask',
                            href:'/admin/classroom',
                            closable:true,
                            showInTags:false,
                            showInMenus:true,
                            choosed:false,
                        },
                        {
                            title:'上课管理',
                            name:'class-manage',
                            icon:'android-time',
                            href:'/admin/class',
                            closable:true,
                            showInTags:false,
                            showInMenus:true,
                            choosed:false,
                        }
                    ]
                },
                {
                    title:'APK管理',
                    name:'apk-manage',
                    icon:'social-android',
                    href:'/admin/apk',
                    closable:true,
                    showInTags:false,
                    showInMenus:true,
                    choosed:false,
                },
                {
                    title:'设置',
                    name:'setting',
                    icon:'gear-a',
                    href:'/admin/setting',
                    closable:true,
                    showInTags:false,
                    showInMenus:true,
                    choosed:false,
                },
                {
                    title:'消息通知',
                    name:'notice',
                    icon:'ios-navigate',
                    href:'/notice',
                    closable:true,
                    showInTags:false,
                    showInMenus:false,
                    choosed:false,
                }
            ]
            // ------------------------------  菜单操作结束  --------------------------------   
        }
    },
    computed: {
        ...mapState(
            {
                user:state=>state.user
            }
        ),
        // 筛选menus中选中的menu
        tags(){
            let tags = [];
            // 将menus中showInTags=true的标签放到tags数组中
            this.menus.forEach(menu=>{
                if(menu.showInTags){
                    tags.push(menu);
                }else if(menu.children){
                    menu.children.forEach(child=>{
                        if(child.showInTags){
                            tags.push(child)
                        }
                    })
                }
            });
            console.log('tags=>',tags)

            //标签数组排序,从小到到
            tags.sort((a,b)=>{
                return (a.num - b.num)
            })
            return tags;
        },
        rotateIcon () {
            return [
                'menu-icon',
                this.isCollapsed ? 'rotate-icon' : ''
            ];
        },
        menuitemClasses () {
            return [
                'menu-item',
                this.isCollapsed ? 'collapsed-menu' : ''
            ]
        }
    },
    // ------------------------------  菜单操作开始  --------------------------------
    //刷新页面之后保存并选中最后一次菜单和标签
    beforeRouteEnter (to, from, next) {
        next(vm => {
            // 通过 `vm` 访问组件实例
            let activeMenuName = localStorage.activeMenuName;
            vm.activeMenuName = activeMenuName;

            let tags_last_num = vm.tags[vm.tags.length - 1].num; 

            if(activeMenuName && activeMenuName.length != 0){
                vm.menus.forEach(_menu=>{
                    if(activeMenuName == _menu.name){                        
                        _menu.choosed = true;
                        _menu.showInTags = true;
                        _menu.num = tags_last_num + 1;
                    }
                    else if(_menu.children){
                        _menu.children.forEach(child=>{
                            if(activeMenuName == child.name){
                                child.choosed = true;
                                child.showInTags = true;
                                child.num = tags_last_num + 1;
                                vm.openMenuName = [_menu.name];      
                            }
                        })                 
                    }
                    else{
                        // 排除首页
                        if(_menu.name != 'admin'){
                            _menu.choosed = false;
                            _menu.showInTags = false;
                        }else{
                            _menu.choosed = false;
                        }
                    }
                })
            }
            vm.$nextTick(()=>{
                vm.$refs.side_menu.updateOpened();
                vm.$refs.side_menu.updateActiveName();
            });           
        })        
    },
    // ------------------------------  菜单操作结束  --------------------------------
    methods: {
        ...mapActions([
            'logout'
        ]),
        quit(){
            this.logout();
            localStorage.removeItem('token');
            localStorage.removeItem('activeMenuName');
            this.$router.push('/login')
        },
        clickNotice(){
            this.choosedMenu('notice');
        },
        collapsedSider() {
            this.$refs.side1.toggleCollapse();
        },
        // ------------------------------  菜单操作开始  --------------------------------
        closeTag(event, name){
            // 判断该标签是否是选中状态
            // 如果是那么就要设置标签数组中最后一个标签成选中状态
            // 如果否那么就直接删除就好
            let is_choosed = false;
            this.menus.forEach((menu,_index)=>{
                if(menu.name == name){
                    is_choosed = menu.choosed;
                    menu.showInTags = false;
                }else if(menu.children){
                    menu.children.forEach(child=>{
                        if(child.name == name){
                            is_choosed = child.choosed;
                            child.showInTags = false;
                        }
                    })
                }
            })          
            // 关闭标签并选中tags中最后一个标签高亮  
            if(is_choosed){
                let last_tag = this.tags[this.tags.length-1];
                last_tag.choosed = true;
                this.$router.push(last_tag.href);
                this.activeMenuName = last_tag.name;
                localStorage.activeMenuName = this.activeMenuName;
            }            
        },
        clickTag(tag){
            this.tags.forEach(_tag=>{
                if(_tag.name == tag.name){
                    _tag.choosed=true;
                }else{
                    _tag.choosed= false;
                }
            })
            // 设置菜单选中name
            this.activeMenuName = tag.name;
            localStorage.activeMenuName = this.activeMenuName;
            // 刷新菜单
            this.$nextTick(()=>{
                if(this.$refs.side_menu){
                    this.$refs.side_menu.updateActiveName()
                }
            });
            //点击tab跳转
            this.$router.push(`${tag.href}`);
        },
        choosedMenu(name){
            // 获取标签数组最后一个元素的num
            let tags_last_num = this.tags[this.tags.length - 1].num;
            // 设置选中菜单name
            this.activeMenuName = name;
            localStorage.activeMenuName = this.activeMenuName;
            let if_tab = false;

            //根据name查找对应的菜单对象
            let menu = null;
            this.menus.forEach(_menu=>{
                if(_menu.name == name){   
                    // 只有不在tags数组中的元素才能设置num                 
                    if(!_menu.showInTags){                   
                        _menu.num = tags_last_num + 1;
                    }
                    menu = _menu;
                    _menu.showInTags = true;
                    _menu.choosed = true;                
                                        
                }
                else if(_menu.children){
                    _menu.children.forEach(child=>{
                        if(child.name == name){     
                            // 只有不在tags数组中的元素才能设置num                       
                            if(!_menu.showInTags){
                                child.num = tags_last_num + 1; 
                            }            
                            menu = child;                
                            child.showInTags = true;
                            child.choosed = true;
                            
                        }else{
                            child.choosed = false;
                        }
                    })
                }
                else {
                    _menu.choosed = false;
                }
            })
            this.$router.push(`${menu.href}`);
        },
        dropdownClick(name){
            this.choosedMenu(name);
        }
        // ------------------------------  菜单操作结束  --------------------------------
    }
}
</script>

在路由页面里面,我引入了几个页面,你们可以根据我的路径自己新建一下即可,然后把组件和路由结合起来就可以成功运行并应用我的Layout.vue页面进行后台整个页面的布局了,是不是超方便的。

Router.vue

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/views/admin/Home.vue'
import AdminLayout from '@/components/admin/Layout.vue'
import Admin from '@/views/admin/Admin.vue'
import UserManage from '@/views/admin/UserManage.vue'
import CityManage from '@/views/admin/CityManage.vue'
import ConditionManage from '@/views/admin/ConditionManage.vue'
import ConditionTypeManage from '@/views/admin/ConditionTypeManage.vue'
import IndustryManage from '@/views/admin/IndustryManage.vue'
import Setting from '@/views/admin/Setting.vue'
import Notice from '@/views/admin/Notice.vue'

import Login from '@/views/login/Login.vue'

Vue.use(Router)

let router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'admin',
      component: AdminLayout,
      children:[
        {
          path:'',
          name:'index',
          meta:{
            title:'首页',
          },
          component:Admin
        },        
        {
          path:'usermanage',
          name:'user-manage',
          meta:{
            title:'用户管理',
          },
          component:UserManage
        },
        {
          path:'citymanage',
          name:'citymanage',
          meta:{
            title:'城市管理'
          },
          component:CityManage
        },
        {
          path:'conditiontypemanage',
          name:'conditiontypemanage',
          meta:{
            title:'条件类型管理'
          },
          component:ConditionTypeManage
        },
        {
          path:'conditionmanage',
          name:'conditionmanage',
          meta:{
            title:'条件管理'
          },
          component:ConditionManage
        },
        {
          path:'industrymanage',
          name:'industrymanage',
          meta:{
            title:'一级行业'
          },
          component:IndustryManage
        },
        {
          path:'setting',
          name:'setting',
          meta:{
            title:'设置'
          },
          component:Setting
        },
        {
          path:'notice',
          name:'notice',
          meta:{
            title:'通知'
          },
          component:Notice
        },
        {
          path:'test',
          name:'test',
          meta:{
            title:'测试'
          },
          component:Setting
        }
      ]
    },
    {
      path:'/login',
      name:'login',
      meta:{
        title:'登录',
      },
      component:Login
    },
  ]
})


router.beforeEach((to, from, next) => {
  let token = localStorage.token;
  if(token && to.name != 'login'){
    next()
  }else if(token && to.name == 'login'){
    next('/');
  }else if(!token && to.name != 'login'){
    next('/login')
  }else{
    next()
  }
})

export default router;

最后,总结一下:其实网上有好多类似的模板管理页面,但是个人感觉一般,所以自己写了一个,相信在不久的将来这样的模板布局页面会越来越多的。

1、iview官网


以下内容是2018年12月14日更新

clipboard.png

clipboard.png

查看原文

认证与成就

  • 获得 2 次点赞
  • 获得 15 枚徽章 获得 0 枚金徽章, 获得 2 枚银徽章, 获得 13 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-08-02
个人主页被 237 人浏览