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 相关文章推荐
静态图片的十一种滤镜效果--不支持Ie7及非IE浏览器。
Mar 06 Javascript
javascript 特殊字符串
Feb 25 Javascript
在IE6下发生Internet Explorer cannot open the Internet site错误
Jun 21 Javascript
JS+CSS实现下拉列表框美化效果(3款)
Aug 15 Javascript
ES6实现的遍历目录函数示例
Apr 07 Javascript
JavaScript伪数组用法实例分析
Dec 22 Javascript
JS实现点击下拉菜单把选择的内容同步到input输入框内的实例
Jan 23 Javascript
开发一个Parcel-vue脚手架工具(详细步骤)
Sep 22 Javascript
JS实现图片拖拽交换效果
Nov 30 Javascript
vue+element+Java实现批量删除功能
Apr 08 Javascript
vue使用keep-alive保持滚动条位置的实现方法
Apr 09 Javascript
javascript Canvas动态粒子连线
Jan 01 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
一个用mysql_odbc和php写的serach数据库程序
2006/10/09 PHP
基于mysql的论坛(4)
2006/10/09 PHP
php对象在内存中的存在形式分析
2015/02/03 PHP
Yii中srbac权限扩展模块工作原理与用法分析
2016/07/14 PHP
PHP实现的mysql主从数据库状态检测功能示例
2017/07/20 PHP
使用prototype.js 的时候应该特别注意的几个问题.
2007/04/12 Javascript
Jsonp 跨域的原理以及Jquery的解决方案
2010/05/18 Javascript
Jquery 插件开发笔记整理
2011/01/17 Javascript
setInterval,setTimeout与jquery混用的问题
2013/04/08 Javascript
node.js中的fs.mkdir方法使用说明
2014/12/17 Javascript
JQuery实现的按钮倒计时效果
2015/12/23 Javascript
JavaScript仿支付宝密码输入框
2015/12/29 Javascript
微信公众号-获取用户信息(网页授权获取)实现步骤
2016/10/21 Javascript
转换layUI的数据表格中的日期格式方法
2019/09/19 Javascript
[42:20]Secret vs Liquid 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
windows下numpy下载与安装图文教程
2019/04/02 Python
Python 的AES加密与解密实现
2019/07/09 Python
python3.7 openpyxl 删除指定一列或者一行的代码
2019/10/08 Python
Pandas-Cookbook 时间戳处理方式
2019/12/07 Python
Windows下python3安装tkinter的问题及解决方法
2020/01/06 Python
PyCharm最新激活码(2020/10/27全网最新)
2020/10/27 Python
使用before和:after伪类制作css3圆形按钮
2014/04/08 HTML / CSS
Kenneth Cole官网:纽约时尚优雅品牌
2016/11/14 全球购物
Styleonme中文网:韩国高档人气品牌
2017/06/21 全球购物
Carolina工作鞋官网:Carolina Footwear
2019/03/14 全球购物
介绍Ibatis的核心类
2013/11/18 面试题
C语言笔试题
2014/09/04 面试题
外贸业务员岗位职责
2013/11/24 职场文书
天网工程实施方案
2014/03/26 职场文书
认购协议书范本
2014/04/22 职场文书
自主招生推荐信范文
2014/05/10 职场文书
农民工讨薪标语
2014/06/26 职场文书
小学生五一劳动节演讲稿
2015/03/18 职场文书
勤俭节约主题班会
2015/08/13 职场文书
Html5页面播放M4a音频文件
2021/03/30 HTML / CSS
九大龙王魂骨,山龙王留下躯干骨,榜首死的最憋屈(被捏碎)
2022/03/18 国漫