Vue状态模式实现窗口停靠功能(灵动、自由, 管理后台Admin界面)


Posted in Javascript onMarch 06, 2020

昨天做的tabs窗口,非常满意,今天乘胜追击,把它做成了可以根据自身大小改变显示样式,自身宽度过小时,tab页可以浮动停靠其一侧。具体效果:

左侧

 Vue状态模式实现窗口停靠功能(灵动、自由, 管理后台Admin界面)

右侧

 Vue状态模式实现窗口停靠功能(灵动、自由, 管理后台Admin界面)

向来喜欢简单明了的东西,所以想实现的简单一点,无奈现实不允许啊,功能实在有一丢丢复杂。硬着头皮搞了整整一下午,终于完成。

左侧跟右侧窗口,要使用同一个控件,尽量增加代码的可复用性,控件的状态就有些多:正常显示(普通tabs窗口),列表(显示图标跟标题,点击时弹出tab页),迷你列表(只显示图标,点击时弹出tab页)。

控件在界面左侧时,tab页弹出在其右侧。反之,控件在界面右侧时,tab页弹出在其左侧。

从正常tabs,缩小到列表显示时,所有tab都是不被激活的。从列表放大到正常tabs,要默认一个标签(tab)是被选中的。

这么多的状态要求,代码很容易就乱掉。不过还好,设计模式中有一个叫“状态模式”的,可以很好的解决这个问题,缺点就是初期代码量稍大,优点是便于后期管理。

昨天做了两个tabs控件,一个是WidgetTabs,另外一个是PageTabs,后者现在还能满足我们的需求,只需要修改WidgetTabs这一个就行。

昨天实现的一些代码删掉,首先重写模板,根据模板写脚本代码,可以让脚本代码更实用些,就像测试驱动的开发里,先写测试再写代码,是一个道理。

还有,差点忘了。昨天的代码里,把所有的style样式都放在style.css这个文件里了,让后vue全局引入,随着我们写的控件越来越多,这个文件会越来越臃肿,不便于管理。这次把WidgetTabs相关的style代码,拿到vue组件里面。

先看模板代码:

<template>

<div class="widget-tabs" :class="stateClass" ref="widget">

<ul class\="heads"\>
 <li v-for\="tab in tabs" class\="item" :class\="{ 'active': tab.isShow }" @click\="click(tab)"\>
 <div v-show\="showIcon" class\="tab-icon"\><i :class\="tab.icon"\></i\></div\> 
 <span v-show\="showTitle"\> {{ tab.name }}</span\>
 </li\>
</ul\>
<div v-show\="showTabBody" class\="tab-body" :class\="dockLeft?'dock-left':''"\>
 <div v-show\="showTabTitle" class\="tab-title"\>
 <div\>{{selectedTab ? selectedTab.name : ''}}</div\>
 <div class\="tab-close" @click\="close"\>×</div\>
 </div\>
 <slot\></slot\>
</div\>
</div>

</template>

顶层的DIV是我们这个控件的壳子,class对应三个状态的三个css class:

1、缺省状态,空字符串

2、列表状态,middle-size

3、迷你列表状态,mini-size

css代码里根据这个csss class,用不同的方式显示其子元素,从而实现正常显示,或者弹出显示两种风格。

ref相当于给这个DIV定了一个唯一ID,我们可以在代码里通过这个ID,获取相应的dom元素,从而判断当前控件大小,根据这个大小,调整控件显示样式。

ul元素显示的是tabs控件的导航标签部分,根据每个tab页的显示或者隐藏来确定标签是否激活,它还有一个功能就是接受鼠标点击事件,传给控制脚本,模板基本没什么逻辑,主要就是显示和接收事件。

是否显示图标,根据showIcon计算属性确定。

是否显示标题,根据showTitle计算属性确定。

整个选项卡body是否显示,根据showTabBody计算属性确定。因为选项卡body有时停靠在控件左侧,有时停靠在控件右侧,这个停靠方式根据属性dockLeft确定,如果停靠在左边dockLeft为true,反之为false。

tabTitle是停靠时,显示的标题区域:

根据计算属性showTabTitle确定是否显示。关闭按钮负责接收点击事件,传递给控制器脚本。不管用什么样的方式实现,控制脚本只要能满足模板的这个要求就可以了。相当于接口定了,根据接口设计实现方式。

前面已经确定要用状态模式实现,根据状态设计三个状态类:

NormalState(普通tabs控件),MiddleState(列表状态,带标题带图标),MiniState(迷你列表状态,只显示图标)。后两个类有一些共同的操作,比如弹出隐藏选项卡等,可以继承共同的基类:ListState,三个状态类功能上也有一些交集,他们可以有共同的基类State。类关系图如下(好多年没有用UML工具了,用Excel凑合一下):

 Vue状态模式实现窗口停靠功能(灵动、自由, 管理后台Admin界面)

不仔细看,不知道这个图其实是Excel画的,还以为是哪个高端UML工具做的呢。

状态类对应的代码:

class State{

constructor(context){ this.context = context

}

widthChange(width){ if(width <=90){ this.toState(this.context.miniState)

} else if(width <=160){ this.toState(this.context.middleState)
} else{ this.toState(this.context.normalState)
}
}

showTabBody(){ return true }

showTabTitle(){ return false }

showIcon(){ return false }

showTitle(){ return true }

close(){}

toState(state){ if(this.context.state !== state){ if(this.context.state === this.context.normalState){ this.context.selectedTab.isShow = false console.log('dddd')

} if(state === this.context.normalState){ this.context.selectedTab.isShow = true } this.context.state = state
}
}

stateClass(){ return '' }

}

class NormalState extends State{

constructor(context){

super(context)
}

clickTab(clickedTab){ this.context.tabs.forEach(tab => {

tab.isShow \= (tab.name == clickedTab.name) this.context.selectedTab = clickedTab
});
}

} //需要弹出式显示标签内容

class ListState extends State{

constructor(context){

super(context)
}

showTabBody(){ return this.context.selectedTab.isShow

}

showTabTitle(){ return true }

showIcon(){ return true }

showTitle(){ return true }

close(){ this.context.selectedTab.isShow = false }

clickTab(clickedTab){ this.context.tabs.forEach(tab => { if(tab === clickedTab){

tab.isShow \= !tab.isShow this.context.selectedTab = clickedTab
 } else{
 tab.isShow \= false }
});
}

} //该状态显示图标跟标题

class MiddleState extends ListState{

constructor(context){

super(context)
}

stateClass(){ return 'middle-size' }

} //该状态只显示图标

class MiniState extends ListState{

constructor(context){

super(context)
}

showTitle(){ return false }

stateClass(){ return 'mini-size' }

}

控件脚本代码:

export default {

name: 'WidgetTabs',

data() { return {

tabs: \[\],
 state: null,
 selectedTab :null,
 dockLeft:false,
}
},

created() { this.tabs = this.$children; this.normalState = new NormalState(this) this.middleState = new MiddleState(this) this.miniState = new MiniState(this) this.state = this.normalState

},

computed: {

stateClass(){ return this.state.stateClass()
},

showIcon(){ return this.state.showIcon()
},

showTitle(){ return this.state.showTitle()
},

showTabBody(){ return this.state.showTabBody()
},
showTabTitle(){ return this.state.showTabTitle()
},
},

methods: {

click(clickTab) { this.state.clickTab(clickTab)
},

mouseMove(){ if(this.$refs.widget){ this.dockLeft = this.$refs.widget.offsetLeft < 50
 this.state.widthChange(this.$refs.widget.offsetWidth)
 }
},

mouseDown(event){
 document.addEventListener('mousemove', this.mouseMove)
},

mouseUp(event){
 document.removeEventListener('mousemove', this.mouseMove)
},

close(){ this.state.close()
}
},

mounted () {

document.addEventListener('mousedown', this.mouseDown)
document.addEventListener('mouseup', this.mouseUp) this.tabs.forEach(tab => { if(tab.isShow){ this.selectedTab = tab
 }
});
},

beforeDestroyed() {

document.removeEventListener('mousedown', this.mouseDown)
document.removeEventListener('mouseup', this.mouseUp)
},

}

组件创建时初始化各种状态。需要注意的一点是,需要在窗口变化时动态获取控件宽度,来确定控件是处在哪个状态。JS中DIV没有resize事件,可以通过鼠标事件来代替。我们的窗口大小是通过鼠标拖动实现的,所以跟踪鼠标拖动事件,动态查询控件大小,然后分发事件。

这个控件至此就完成了,写这个文章的事件比写代码时间长,天生是个程序员不是writer。

整个项目在这个历史节点的代码,请到我的Github上查看: https://github.com/vularsoft/...

找到该历史节点的方法:

RXEditor是一个Boostrap代码可视化编辑工具,本系列记录了该软件的开发过程,有问题的朋友请在ithub上给我留言。

总结

到此这篇关于Vue状态模式实现窗口停靠功能(灵动、自由, 管理后台Admin界面)的文章就介绍到这了,更多相关vue状态模式实现窗口停靠内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
Javascript 面向对象 命名空间
May 13 Javascript
吐槽一下我所了解的Node.js
Oct 08 Javascript
JavaScript匿名函数用法分析
Feb 13 Javascript
JavaScript按值删除数组元素的方法
Apr 24 Javascript
Javascript中prototype属性实现给内置对象添加新的方法
May 14 Javascript
JS基于面向对象实现的拖拽功能示例
Dec 20 Javascript
jQuery插件HighCharts实现气泡图效果示例【附demo源码】
Mar 13 Javascript
详谈js遍历集合(Array,Map,Set)
Apr 06 Javascript
学习Vue组件实例
Apr 28 Javascript
vue ajax 拦截原理与实现方法示例
Nov 29 Javascript
Vue-cli 移动端布局和动画使用详解
Aug 10 Javascript
JS实现斐波那契数列的五种方式(小结)
Sep 09 Javascript
javascript中可能用得到的全部的排序算法
Mar 05 #Javascript
js的Object.assign用法示例分析
Mar 05 #Javascript
Element的el-tree控件后台数据结构的生成以及方法的抽取
Mar 05 #Javascript
element-ui树形控件后台返回的数据+生成组织树的工具类
Mar 05 #Javascript
vue中使用vue-print.js实现多页打印
Mar 05 #Javascript
koa2的中间件功能及应用示例
Mar 05 #Javascript
微信小程序利用for循环解决内容变更问题
Mar 05 #Javascript
You might like
php 智能404跳转代码,适合换域名没改变目录的网站
2010/06/04 PHP
第4章 数据处理-php字符串的处理-郑阿奇(续)
2011/07/04 PHP
第七章 php自定义函数实现代码
2011/12/30 PHP
用 Composer构建自己的 PHP 框架之设计 MVC
2014/10/30 PHP
PHP常见漏洞攻击分析
2016/02/21 PHP
几个比较实用的JavaScript 测试及效验工具
2010/04/18 Javascript
js 时间函数应用加、减、比较、格式转换的示例代码
2013/08/23 Javascript
js判读浏览器是否支持html5的canvas的代码
2013/11/18 Javascript
javascript列表框操作函数集合汇总
2013/11/28 Javascript
JavaScript学习笔记之Cookie对象
2015/01/22 Javascript
jQuery中的pushStack实现原理和应用实例
2015/02/03 Javascript
JavaScript实现点击按钮复制指定区域文本(推荐)
2016/11/25 Javascript
利用JS轻松实现获取表单数据
2016/12/06 Javascript
jquery插入兄弟节点的操作方法
2016/12/07 Javascript
jQuery验证表单格式的使用方法
2017/01/10 Javascript
jQuery Validate 数组 全部验证问题
2017/01/12 Javascript
基于MVC方式实现三级联动(JavaScript)
2017/01/23 Javascript
JavaScript实现动态添加Form表单元素的方法示例
2017/08/14 Javascript
简单说说angular.json文件的使用
2018/10/29 Javascript
微信公众平台获取access_token的方法步骤
2019/03/29 Javascript
Node.js一行代码实现静态文件服务器的方法步骤
2019/05/07 Javascript
layui多图上传实现删除功能的例子
2019/09/23 Javascript
python打开url并按指定块读取网页内容的方法
2015/04/29 Python
对TensorFlow的assign赋值用法详解
2018/07/30 Python
python中的线程threading.Thread()使用详解
2019/12/17 Python
python GUI库图形界面开发之PyQt5窗口背景与不规则窗口实例
2020/02/25 Python
Python API len函数操作过程解析
2020/03/05 Python
Python读取pdf表格写入excel的方法
2021/01/22 Python
法国票务网站:Ticketmaster法国
2018/07/09 全球购物
夏威夷灵感服装及配饰:Reyn Spooner
2018/09/18 全球购物
安全检查管理制度
2014/02/02 职场文书
二手房购房协议书范本
2014/10/05 职场文书
市场督导岗位职责
2015/04/10 职场文书
2016年校园植树节广播稿
2015/12/17 职场文书
springboot新建项目pom.xml文件第一行报错的解决
2022/01/18 Java/Android
唤醒紫霞仙子,携手再游三界!大话手游X《大话西游》电影合作专属剧情任务
2022/04/03 其他游戏