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 validate使用攻略 第四步
Jul 01 Javascript
JavaScript面向对象之Prototypes和继承
Jul 12 Javascript
js实现带缓冲效果的仿QQ面板折叠菜单代码
Sep 06 Javascript
jQuery实现根据生日计算年龄 星座 生肖
Nov 23 Javascript
关于Node.js的events.EventEmitter用法介绍
Apr 01 Javascript
Bootstrap Table使用整理(一)
Jun 09 Javascript
JS原生数据双向绑定实现代码
Aug 14 Javascript
vue 中引用gojs绘制E-R图的方法示例
Aug 24 Javascript
解决angular2 获取到的数据无法实时更新的问题
Aug 31 Javascript
javascript for循环性能测试示例
Aug 07 Javascript
Jquery Fade用法详解
Nov 06 jQuery
vue-treeselect的基本用法以及解决点击无法出现拉下菜单
Apr 30 Vue.js
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
mac os快速切换多个PHP版本的方法
2017/03/07 PHP
php制作圆形用户头像的实例_自定义封装类源代码
2017/09/18 PHP
使用PHPStorm+XDebug搭建单步调试环境
2017/11/19 PHP
js 图片轮播(5张图片)
2008/12/30 Javascript
JS打开图片另存为对话框实现代码
2012/12/26 Javascript
javascript中this做事件参数相关问题解答
2013/03/17 Javascript
JS和JQUERY获取页面大小,滚动条位置,元素位置(示例代码)
2013/12/14 Javascript
append和appendTo的区别以及appendChild用法
2013/12/24 Javascript
Javascript函数式编程简单介绍
2015/10/11 Javascript
浅谈jQuery hover(over, out)事件函数
2016/12/03 Javascript
微信小程序图片右边加两行文字的代码
2020/04/23 Javascript
vue基于better-scroll仿京东分类列表
2020/06/30 Javascript
JSONObject与JSONArray使用方法解析
2020/09/28 Javascript
js实现头像上传并且可预览提交
2020/12/25 Javascript
javascript中闭包closure的深入讲解
2021/03/03 Javascript
Python中用post、get方式提交数据的方法示例
2017/09/22 Python
异步任务队列Celery在Django中的使用方法
2018/06/07 Python
Tensorflow 同时载入多个模型的实例讲解
2018/07/27 Python
python3 实现验证码图片切割的方法
2018/12/07 Python
Python 使用 prettytable 库打印表格美化输出功能
2019/12/26 Python
利用python制作拼图小游戏的全过程
2020/12/04 Python
世界上最大的隐形眼镜商店:1-800 Contacts
2018/11/03 全球购物
业务代表的岗位职责
2013/11/16 职场文书
医药销售求职信范文
2014/02/01 职场文书
家教广告词
2014/03/19 职场文书
新教师培训方案
2014/06/08 职场文书
班风口号
2014/06/18 职场文书
在职党员进社区活动总结
2014/07/05 职场文书
会计试用期自我评价怎么写
2014/09/18 职场文书
党员民主评议个人总结
2014/10/20 职场文书
山东省召开党的群众路线教育实践活动总结大会新闻稿
2014/10/21 职场文书
2014年初中班主任工作总结
2014/11/08 职场文书
2015年班组工作总结
2015/04/20 职场文书
有关三国演义的读书笔记
2015/06/25 职场文书
2016年党员公开承诺书范文
2016/03/24 职场文书
日本读研:怎样写好一篇日本研究计划书?
2019/07/15 职场文书