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 相关文章推荐
js中根据字数截取字符串,不能截断url
Jan 12 Javascript
解析img图片没找到onerror事件 Stack overflow at line: 0
Dec 23 Javascript
详细解密jsonp跨域请求
Apr 15 Javascript
JavaScript让网页出现渐隐渐显背景颜色的方法
Apr 21 Javascript
详细谈谈javascript的对象
Jul 31 Javascript
ASP.NET jquery ajax传递参数的实例
Nov 02 Javascript
提高JavaScript执行效率的23个实用技巧
Mar 01 Javascript
使用JavaScript实现点击循环切换图片效果
Sep 03 Javascript
深入浅析JSONAPI在PHP中的应用
Dec 24 Javascript
vuex + keep-alive实现tab标签页面缓存功能
Oct 17 Javascript
vue中使用elementUI组件手动上传图片功能
Dec 13 Javascript
js实现移动端轮播图滑动切换
Dec 21 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中通过ADO调用Asscess数据库和COM程序
2006/10/09 PHP
初级的用php写的采集程序
2007/03/16 PHP
Zend Studio去除编辑器的语法警告设置方法
2012/10/24 PHP
php简单定时执行任务的实现方法
2015/02/23 PHP
php使用变量动态创建类的对象用法示例
2017/02/06 PHP
Yii2框架自定义验证规则操作示例
2019/02/08 PHP
laravel5.2表单验证,并显示错误信息的实例
2019/09/29 PHP
jQuery Ajax使用 全解析
2010/12/15 Javascript
jQuery :first选择器使用介绍
2013/08/09 Javascript
jQuery插件jFade实现鼠标经过的图片高亮其它变暗
2015/03/14 Javascript
JS实现灵巧的下拉导航效果代码
2015/08/25 Javascript
jquery实现可点击伸缩与展开的菜单效果代码
2015/08/31 Javascript
基于jQuery实现自动轮播旋转木马特效
2015/11/02 Javascript
jQuery限制图片大小的方法
2016/05/25 Javascript
对Js OOP编程 创建对象的一些全面理解
2016/07/26 Javascript
基于JavaScript实现下拉列表左右移动代码
2017/02/07 Javascript
详解Angularjs 如何自定义Img的ng-load 事件
2017/02/15 Javascript
超级简易的JS计算器实例讲解(实现加减乘除)
2017/08/08 Javascript
Python实现将SQLite中的数据直接输出为CVS的方法示例
2017/07/13 Python
python实现学生管理系统
2018/01/11 Python
Python使用matplotlib绘制三维图形示例
2018/08/25 Python
python循环定时中断执行某一段程序的实例
2019/06/29 Python
pymysql模块的使用(增删改查)详解
2019/09/09 Python
python求质数列表的例子
2019/11/24 Python
python使用docx模块读写docx文件的方法与docx模块常用方法详解
2020/02/17 Python
手机端用rem+scss做适配的详解
2017/11/15 HTML / CSS
欧洲最大的预定车位市场:JustPark
2020/01/06 全球购物
应付会计岗位职责
2013/12/12 职场文书
证婚人经典证婚词
2014/01/09 职场文书
校园环保建议书
2014/05/14 职场文书
车辆委托书范本
2014/10/05 职场文书
中秋节慰问信
2015/02/15 职场文书
第一节英语课开场白
2015/06/01 职场文书
运动会加油稿50字
2015/07/21 职场文书
建筑工程挂靠协议书
2016/03/23 职场文书
SpringBoot整合MongoDB的实现步骤
2021/06/23 MongoDB