Vue 应用系列笔记 (四) 引入Element做布局

终于是走向正题,引入业务使用的ui组件,Element,也是饿了么开放的组件,屁不多放,开始引入

具体的安装工作应用传送门

采用版本:[email protected]

开发工具:WebStorm

引入sass支持,sass拓展语法写css特别方便

cnpm install sass-loader@latest
​
cnpm install node-sass@latest

布局部分

一、页面布局

通常为左-上-内容,三等分,如图

当然可以根据自己需要,来进行调整 布局传送门

扫描二维码关注公众号,回复: 11575598 查看本文章

①基本布局

形成上方的布局,只需要跟着官方文档,就能拼接出来

<el-container class="page-container">
    <el-aside>        
    </el-aside>    
    <el-container>        
        <el-header>        
        </el-header>        
        <el-main>            
          <router-view />    //存放页面展示的地方
        </el-main>    
    </el-container>
</el-container>

②样式调整

如果什么都不预先调整,页面只能是占一部分,无法全占比,我们可以尝试在main.js中引用公共样式来修正这个问题;

//main.js
import './style/common.scss'th:100%;  height:100%;  margin: 0;}
//common.scss
//用以绽开全屏
html,body,#app{  width:100%;  height:100%;  margin: 0;}
//用以关掉不必要的滑动条,可以不加
::-webkit-scrollbar-track-piece {
  background-color: transparent;
}
::-webkit-scrollbar {
  width: 0px;
  height: 0px;
  background-color: transparent;
}
::-webkit-scrollbar-thumb {
  border-radius: 0px;
  background-color: hsla(220, 4%, 58%, .3);
}

这样,一个基本的模版布局就出来了。

二、左侧导航栏

①引入NavMenu

现成的导航栏,我们其实已经可以直接拷过来,但是这里存在问题,每一个层级都要写进去。这样遍历是无法处理多重结构的,总不能一直自己叠加,这时候就要用到组件的递归调用了。

②菜单格式的确定

首先是确定,菜单的格式,影响下方菜单的制定,下方是简单制定的json传入格式

 {
     "children": [],
     "href": "/test",
     "icon": "el-icon-star-off",
     "id": "1",
     "label": "测试",
     "parentId": "0"
 }

③组件的自我递归

第一层 <el-menu> 毋庸置疑是必须的,无法预测的是子菜单中层级的数量,则这个层级可以作为父组件,向下传值,让不确定的子组件进行自我递归

   // menu/index.vue
   <el-menu
            style="height: 100%"
            :default-active="$route.path" 
            :show-timeout="200"
            background-color="#00142a"
            text-color="hsla(0,0%,100%,.65)"
            active-text-color="#409eff"
            :collapse="isCollapse">
        <child-menu :menu="menu" :isCollapse="isCollapse"/>
    </el-menu>

对于子组件:

1、首先是需要判断是否本身就已经没有子菜单,没有则本身就属于一个链接菜单<el-menu-item>,有则是需要进入 <submenu> 组件,生成子菜单 A,

2、 这个子菜单A又需要进行判断,是否存在子菜单,若不存在则可以归类为<el-menu-item>,若存在,我们又要进行这两个步骤循环

3、这时候我们就需要用到 vue 中name的用法了,重复代码不需要一直叠加下去,要利用name属性进行自我调用

//   menu/child-menu.vue
<template v-for="(item,index) in menu">
<el-menu-item v-if="item.children.length==0"
              :index="item.label"
              @click="open(item)"
              :key="item.label">
    <i :class="item.icon"/>
    <span slot="title">{{item.label}}</span>
    </el-menu-item>
<el-submenu v-else
            :index="item.label"
            :key="item.label">
    <!-- 侧边栏标题 -->
    <template slot="title">
        <span slot="title" >
            <i :class="item.icon"/>{{item.label}}
    </span>
</template>
<!-- 侧边栏子项目 -->
<template v-for="(child,cindex) in item.children">
<el-menu-item
              :index="child.id"
              @click="open(child)"
              v-if="child.children.length==0"
              :key="child.label">
    <span slot="title"> <i :class="child.icon"/>{{child.label}}</span>
    </el-menu-item>
<child-menu v-else
            :menu="[child]"
            :key="cindex"
            :isCollapse="isCollapse"/>
</template>
</el-submenu>
</template>

········
name:"childMenu"

①上述open(是用于模拟打开路由的)

②slot="title",引用官方: 【通过 el-menu-item-group 组件可以实现菜单进行分组,分组名可以通过title属性直接设定,也可以通过具名 slot 来设定。】

③递归重新调用自己的时候 ,加上[],是因为递归的时候是数组,而传入的是对象,要转换一下类型

三、顶部tags

①分析如何构造顶部标签栏

1、element-tag

我们可以从element 中tags看到tag的形式,能不能做到路由展示的结果形成一个个标签栏,在头部滑动呢?

基本是可以满足,如果是固定的话则会出现

就会出现无法简单控制限制换行的情况,element中tag布局基础是flex布局,其实这个不是重点,重点是无法 达到我想要的H5触摸的方式

2、element-button

与上述是一致的,其实都可以使用,进行css调整以达到通常的标签支持

3、原生dom标签

为了支持我h5触摸滚动的高逼格方式,还是得用原生html标签。(样式上是可以通用到上述 tags、button的通常用标签格式的。

②自定义tag

1、dom骨架

首先外层用于控制触发方法,内层才是内容展示

<div ref="tagsList"> //后面用于触发h5 api
    <div ref="tag"> //后面触发切换
        <span ></span> //放置内容
        <i class="el-icon-close"/> //关闭图标展示
    </div>
</div>

2、scss样式/骨架补充

// tags
<div class="tags-container">  //想了下还是要用定位
    <div class="tags-box">
        <div ref="tagsList" class="tags-list" >
            <div ref="tag" class="el-tag el-tag--plain">
                <span class="tag-text"></span>
                <i class="el-icon-close "/>
            </div>
        </div>
    </div>
</div>

scss

​
 .tags-container {
        //父级样式设置定位元素,为子项目定位提供参照系,不让子元素超过父元素的内容框
        //阴影部分凸显不一样
        position: relative;
        box-sizing: border-box;
        overflow: hidden;
        box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
     
     .tags-box {
         //给内容部分提供最大化展示,设置固定高度
         position: relative;
         box-sizing: border-box;
         width: 100%;
         height: 40px;
     }
     
     .tags-list {
         //实际上这个开始已经是为单个独立标签进行设置,设置绝对定位是为了后续给其滑动使用
         position: absolute;
         padding: 2px 10px;
         overflow: visible;//可以让其飘出去,左/右边
         white-space: nowrap;//内容不换行
         transition: left .3s ease;//纯粹简单滑动过渡
     }
     
     .tag-item {
         //展示内容元素
         position: relative;
         display: inline-block; //限制为行内块级元素
         height: 30px;
         line-height: 30px;
         margin: 2px 4px 2px 0;
         padding: 0 10px;
         border: 1px solid #eee;
         border-radius: 3px;
         color: #495060 !important;//字体颜色定义
         font-size: 12px;
         vertical-align: middle;//垂直居中对齐
         opacity: 1;
         overflow: hidden;//内容滑出同时隐藏
         cursor: pointer;
         //为后续用以区分是否展示或隐藏做准备的两个串联样式
         &.is-active {
             border-color: #409eff;
         }
​
         &.is-not-active {
             border-color: #67C23A !important;
         }
     }
     //下列两个纯粹优化展示格式,使得文字更显居中,同时关闭符号与内容存在一定间隙
     .tag-text {
         margin-left: 8px;
     }
​
     .tag-close {
         margin-left: 8px;
     }
     //仅仅区别待选择时稍微隐藏一下,看起来好看点的样子
     .tag-item:hover {
         opacity: .5;
     }
}

3、数据填充,展示初步效果

json数据

[
    {
        "label": "标签一",
        "type": ""
    },
    {
        "label": "标签二",
        "type": "success"
    },
    {
        "label": "标签三",
        "type": "info"
    },
    {
        "label": "标签四",
        "type": "danger"
    },
    {
        "label": "标签五",
        "type": "warning"
    }
]

骨架填充:

//主要是增加了遍历数据而已
<div class="tags-container">
    <div class="tags-box">
        <div ref="tagsList" class="tags-list">
            <div ref="tag" class="tag-item"  v-for="item in items" >
                <span class="tag-text">{{item.label}}</span>
                <i class="el-icon-close tag-close"/>
            </div>
        </div>
    </div>
</div>

临时效果:

③标签的滑动与隐藏(额外)

上面两步多出的样式,其实是为了这一步而存在的,标签总会长度到一定程度,会超出页面展示的范围,我们又想看到它,只能允许其滑动出视界外,同时能滑回来才是稳健的,合理的。

为此我们需要用到以下事件:

//鼠标四事件 ->电脑
1、mousewheel  监听转动鼠标滚轮
2、mouseup   释放按下的鼠标
3、mousemove  鼠标移动
4、mousedown  按下鼠标键
//触摸触发事件 -> 假装有手机用手指滑的样子
5、touchend   当用户手指从屏幕上离开时触发 
6、touchmove  当用户手指在屏幕上移动时触发
7、touchstart 当用户手指放到屏幕上触发
​
其实mousewheel 触发的效果与 2、3、4 整合一起的效果及5、6、7的效果是一致的,为什么我要做出来呢,因为我脑抽了

成型后的骨架

<div class="tags-container">
    <div class="tags-box" ref="tagBox">
        <div ref="tagsList"  class="tags-list"
            @mousewheel="handleMouseWheel"
            @mouseup="handleMouseUp"
            @mousemove="handleMouse"
            @mousedown="handleMouseStart"
            @touchup="handleMouseUp"
            @touchmove="handleMouse"
            @touchstart="handleMouseStart">       
        </div>
            <div ref="tag"  class="tag-item" v-for="item in items" >
                <span class="tag-text">{{item.label}}</span>
                <i class="el-icon-close tag-close"/>
            </div>
        </div>
    </div>
</div>

1、mousewheel

//mehtods:
handleMouseWheel(e) {
    const step = 0.8 * 90; // 模拟一个tag长度,懒得一个个拿
    const boundarystart = 0,   //梦开始的地方
          //外层包装减去后,给自己留100 余地
          boundaryend = this.$refs.tagsList.offsetWidth - this.$refs.tagBox.offsetWidth + 100;
    //最少给自己留大概的存在tag位置显示出来,当然这里可以自定义更好的数值
    // Y>0向左滑动
    if (e.deltaY > 0 && this.tagBodyLeft >= -boundaryend) {
        this.tagBodyLeft = this.tagBodyLeft - step;
        // Y<0向右滑动
    } else if (e.deltaY < 0 && this.tagBodyLeft < boundarystart) {
        this.tagBodyLeft = this.tagBodyLeft + step;
    }
}
    
//data
tagBodyLeft:0 //用于保存最低限度
  
//watch
tagBodyLeft(value) { //给标签列表最小的宽度限定
    this.$refs.tagsList.style.left = value + 'px'
}

2、handleMouseUp、handleMouse、handleMouseStart

//methods
handleMouseUp(e) {
    this.lock = false;
},
  
handleMouse(e){
    const boundarystart = 0,
          boundaryend =
          this.$refs.tagsList.offsetWidth - this.$refs.tagBox.offsetWidth + 100;
    if (!this.lock) {
        return;
    }
    //鼠标滑动
    if (e.clientX && e.clientY) {
        this.endX = e.clientX;
        this.endY = e.clientY;
        //触摸屏滑动
    } else {
        //获取滑动屏幕时的X,Y
        this.endX = e.changedTouches[0].pageX;
        this.endY = e.changedTouches[0].pageY;
    }
    //获取滑动距离
    let distanceX = this.endX - this.startX;
    //判断滑动方向——向右滑动
    distanceX = parseInt(distanceX * 0.8);
    if (distanceX > 0 && this.tagBodyLeft < boundarystart) {
        this.tagBodyLeft = this.tagBodyLeft + distanceX;
        //判断滑动方向——向左滑动
    } else if (distanceX < 0 && this.tagBodyLeft >= -boundaryend) {
        this.tagBodyLeft = this.tagBodyLeft + distanceX;
    }
},    
    
    
handleMouseStart(e){
    this.lock = true;
    //获取当前操作的位置
    if (e.clientX && e.clientY) {
        //针对鼠标
        this.startX = e.clientX;
        this.startY = e.clientY;
    } else {
        //针对手指,第一个点
        this.startX = e.changedTouches[0].pageX;
        this.startY = e.changedTouches[0].pageY;
    }
},
    
  

结果:

猜你喜欢

转载自blog.csdn.net/CoffeeAndIce/article/details/105280069
今日推荐