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 相关文章推荐
jQuery 可以拖动的div实现代码 脚本之家修正版
Jun 26 Javascript
javascript cookie操作类的实现代码小结附使用方法
Jun 02 Javascript
js判断是否为数组的函数: isArray()
Oct 30 Javascript
javascript常用功能汇总
Jul 05 Javascript
JavaScript学习总结之JS、AJAX应用
Jan 29 Javascript
Bootstrap的Refresh Icon也spin起来
Jul 13 Javascript
用jQuery实现圆点图片轮播效果
Mar 19 Javascript
详解微信小程序设置底部导航栏目方法
Jun 29 Javascript
Vue组件库发布到npm详解
Feb 17 Javascript
ionic grid(栅格)九宫格制作详解
Jun 30 Javascript
浅析微信小程序自定义日历组件及flex布局最后一行对齐问题
Oct 29 Javascript
前端JavaScript大管家 package.json
Nov 02 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+DBM的同学录程序(4)
2006/10/09 PHP
CI框架源码阅读,系统常量文件constants.php的配置
2013/02/28 PHP
简单了解PHP编程中数组的指针的使用
2015/11/30 PHP
PHP实现简单计算器小程序
2020/08/28 PHP
LazyForm jQuery plugin 定制您的CheckBox Radio和Select
2009/10/24 Javascript
Extjs入门之动态加载树代码
2010/04/09 Javascript
javascript批量修改文件编码格式的方法
2015/01/27 Javascript
JavaScript实现彩虹文字效果的方法
2015/04/16 Javascript
Nodejs初级阶段之express
2015/11/23 NodeJs
JavaScript实现获取某个元素相邻兄弟节点的prev与next方法
2016/01/25 Javascript
基于vue2.0+vuex的日期选择组件功能实现
2017/03/13 Javascript
JS实现图片放大镜插件详解
2017/11/06 Javascript
jQuery实现ajax回调函数带入参数的方法示例
2018/06/26 jQuery
vue3.0 CLI - 2.2 - 组件 home.vue 的初步改造
2018/09/14 Javascript
如何用Node写页面爬虫的工具集
2018/10/26 Javascript
Vue 实现手动刷新组件的方法
2019/02/19 Javascript
记录一次开发微信网页分享的步骤
2019/05/07 Javascript
vue+高德地图写地图选址组件的方法
2019/05/18 Javascript
简单了解JavaScript作用域
2020/07/31 Javascript
VsCode里的Vue模板的实现
2020/08/12 Javascript
[42:24]完美世界DOTA2联赛循环赛 LBZS vs DM BO2第一场 11.01
2020/11/02 DOTA
详解Python迭代和迭代器
2016/03/28 Python
python移位运算的实现
2019/07/15 Python
使用Python代码实现Linux中的ls遍历目录命令的实例代码
2019/09/07 Python
python日志模块logbook使用方法
2019/09/19 Python
利用python实现冒泡排序算法实例代码
2019/12/01 Python
python读取ini配置的类封装代码实例
2020/01/08 Python
Windows下pycharm安装第三方库失败(通用解决方案)
2020/09/17 Python
大唐面试试题(CPU,UNIX等等)
2012/01/11 面试题
实习教师自我鉴定
2013/09/27 职场文书
大学生在校学习的自我评价
2014/02/18 职场文书
黄金酒广告词
2014/03/21 职场文书
师德师风整改措施
2014/10/24 职场文书
党员干部作风建设思想汇报范文
2014/10/25 职场文书
《检阅》教学反思
2016/02/22 职场文书
Mysql 如何合理地统计一个数据库里的所有表的数据量
2022/04/18 MySQL