[关闭]
@15013890200 2018-08-16T16:15:21.000000Z 字数 14382 阅读 824

vue time 时间插件

vue javascript 插件



  • tips:文章只包括插件部分,注册和引用不再赘述(具体细节可查看 评分插件
  • 从接触前端工作以来,时间控件就经常会被引用。由于HTML自带时间控件样式过于丑陋,用起来和页面整体风格不搭。之前一直都希望能够自己来动手写一套自己的时间控件,由于时间的关系一直拖到现在,终于完成了。
  • 本时间控件由于笔者技术的原因还存在以下不足,待日后流弊了再一一解决:

    1、 该时间控件是当做节点插入所需要的地方,而不是当做节点属性来引用。所以需要通过回调函数来传递组件的返回值;或者麻烦点,通过操作节点也能获得。
    2、该时间控件由于笔者偷懒(其实是没有找到方便的方法)的缘故,暂不支持对控件的皮肤进行设置,要想改颜色,最捷径的方法是编辑器打开源码,整体替换需要替换的颜色 ^_^
    3、改时间控件输出日期暂时只支持:精确到 天、小时、分钟、秒;(其他精确度实在是很少用到,笔者就偷懒忽略了)

  • 笔者自认为的亮点

    1、该时间控件的设计灵感来源于layer date,所以风格差不多,不过在某些细节方面处理的更好(自己去比对)
    2、不同于以往插件,再本次设计过程中,加上了一些动画效果,使控件的呼入不那么生硬。
    3、其他控件拥有的基本功能基本上都有(当然也有可能笔者考虑不周遗忘了,欢迎联系笔者,email: 565831653@qq.com)


1、控件部分

1.1 template 部分

  1. <template>
  2. <div class="j_div_time">
  3. <div class="j_div_time_mask" v-if="onSelect" @click='hide'></div>
  4. <input class="j_ip_time" type="text" v-model="selected" placeholder="请选择时间" readonly @click='show'>
  5. <div class="j_div_time_select_hide" :class="{'j_div_time_select':(onSelect && !position_top),'j_div_time_select_top':(onSelect && position_top)}">
  6. <div class="j_div_time_head">
  7. <div v-if='onSelectType !== "time"'>
  8. <span @click="operate_year(-1)" class="sp_left"><<</span>
  9. <span v-if='onSelectType == "day"' @click="operate_month(-1)" class="sp_left"><</span>
  10. <span @click='set_operate("year")' class="sp_year">{{s_year}}年</span>
  11. <span @click='set_operate("month")' class="sp_month">{{s_month}}月</span>
  12. <span @click="operate_year(1)" class="sp_right">>></span>
  13. <span v-if='onSelectType == "day"' @click="operate_month(1)" class="sp_right">></span>
  14. </div>
  15. <div v-if='onSelectType === "time"'>
  16. <span>选择时间</span>
  17. </div>
  18. </div>
  19. <div class="j_div_time_body" v-if="onSelectType == 'day'">
  20. <div class="j_div_time_nav">
  21. <span v-for='n in nav'>{{n}}</span>
  22. </div>
  23. <div class="j_div_time_date">
  24. <span @click='select_date(d)' :class="{'span_today':d.timestamp == today,'span_disable': (min ? (d.timestamp < min):false) || (max ? (d.timestamp > max):false) || disable,'span_selected':d.timestamp == select_day,'span_current_month':d.current}" v-for='d in date'>{{d.date}}</span>
  25. </div>
  26. </div>
  27. <div class="j_div_time_body_month" v-if='onSelectType == "month"'>
  28. <span @click="select_month(key+1)" :class="{'span_selected':(key+1) == s_month}" v-for="(m,key) in nav_month">{{m}}</span>
  29. </div>
  30. <div class="j_div_time_body_year" v-if='onSelectType == "year"'>
  31. <span @click="select_year(y)" :class="{'span_selected':y == s_year}" v-for="y in nav_year">{{y}}年</span>
  32. </div>
  33. <div class="j_div_time_body_detail" v-if='onSelectType == "time"'>
  34. <div class="title">
  35. <span></span>
  36. <span></span>
  37. <span></span>
  38. </div>
  39. <div class="detail" style="border-right:none;">
  40. <div class="div_detail_container">
  41. <span @click="select_hour(h)" :class="{'span_selected': s_hour == h}" v-for='h in nav_hour'>{{h}}</span>
  42. </div>
  43. </div>
  44. <div class="detail" style="border-right:none;">
  45. <div class="div_detail_container">
  46. <span @click="select_minute(m)" :class="{'span_selected': s_minute == m}" v-for='m in nav_minute'>{{m}}</span>
  47. </div>
  48. </div>
  49. <div class="detail">
  50. <div class="div_detail_container">
  51. <span @click="select_second(s)" :class="{'span_selected': s_second == s}" v-for='s in nav_second'>{{s}}</span>
  52. </div>
  53. </div>
  54. </div>
  55. <div class="j_div_time_footer">
  56. <span class="sp_sure" @click="ensure">确定</span>
  57. <span class="sp_now" :class="{'sp_disable':disable}" @click="now_date">现在</span>
  58. <span class="sp_clear" :class="{'sp_disable':disable}" @click="clear_date">清空</span>
  59. <span @click="select_detail" class="sp_detail" :class="{'sp_disable':disable || (formate != 'hour' && formate != 'minute' && formate != 'second')}">
  60. <label v-if='onSelectType !== "time"' style="cursor:inherit;">选择时分秒</label>
  61. <label v-if='onSelectType === "time"' style="cursor:inherit;color:#3896f8;">返回日期</label>
  62. </span>
  63. </div>
  64. </div>
  65. </div>
  66. </template>

1.2 script 部分

  1. <script>
  2. export default{
  3. name: 'mytime',
  4. data(){
  5. return{
  6. today: null,//存储今天日期时间戳 精确到天 格式:yyyy-mm-dd
  7. select_day: null,//暂存选中的日期
  8. onSelect: false,//记录日期控件的状态,激活or关闭
  9. onSelectType: 'day',//存放日期控件当前选择的状态,值:day、year、month、time
  10. s_year: null,//存放选中的年份
  11. s_month: null,//存放选中的月份
  12. s_day: null,//存放选中的日期
  13. s_hour: null,//存放选中的小时
  14. s_minute: null,//存放选中的分钟
  15. s_second: null,//存放选中的秒数
  16. nav: ['日','一','二','三','四','五','六'],//存放展示的星期
  17. nav_month: ['一月','二月','三月','四月','五月','六月','七月','八月','九月','十月','十一月','十二月'],//存放展示的月份
  18. nav_year: null,//存放展示的年份
  19. nav_minute: [],//存放展示的分钟
  20. nav_hour: [],//存放展示的小时
  21. nav_second: [],//存放展示的秒
  22. date: null,//存放展示的日期
  23. days: 0,//当前月有多少天
  24. position_top: false,
  25. disable: false,//是否禁用
  26. min: '',//日期可选最小日期
  27. max: null,//日期可选最大日期
  28. selected: "",//存放完整的选择日期
  29. formate: 'day'//日期精确到指定,day、hour、minute、second
  30. }
  31. },
  32. props: {
  33. timeObj: {
  34. type: Object,
  35. default: null
  36. }
  37. },
  38. methods: {
  39. init: function(){
  40. /*
  41. *初始化today
  42. *初始化 年月日时分秒
  43. *初始化 展示日期面板
  44. */
  45. for(let key in this.timeObj){
  46. this.$data[key] = this.timeObj[key];
  47. }
  48. if(this.min){
  49. if(typeof this.min == "string") this.min = (new Date(this.min)).valueOf();
  50. }
  51. if(this.max){
  52. if(typeof this.max == "string") this.max = (new Date(this.max)).valueOf();
  53. }
  54. let date = new Date();
  55. let today_str = date.getFullYear()+ '-' +(date.getMonth() > 8 ?(date.getMonth()+1):('0'+(date.getMonth()+1)))+ '-' +(date.getDate() > 9 ?date.getDate():('0'+date.getDate()));
  56. this.today = (new Date(today_str)).valueOf();
  57. if(this.selected){
  58. date = new Date(this.selected);
  59. }
  60. let year = date.getFullYear();
  61. let month = date.getMonth()+1;
  62. month = month > 9 ? month : ('0'+month);
  63. let day = date.getDate();
  64. day = day > 9 ? day : ('0'+day);
  65. let hour = date.getHours();
  66. hour = hour > 9 ? hour : ('0'+hour);
  67. let minute = date.getMinutes();
  68. minute = minute > 9 ? minute : ('0'+minute);
  69. let second = date.getSeconds();
  70. second = second > 9 ? second : ('0'+second);
  71. this.s_hour = hour;
  72. this.s_minute = minute;
  73. this.s_second = second;
  74. this.s_year = year;
  75. this.s_month = month;
  76. this.s_day = day;
  77. let date_str = this.s_year + '-' + this.s_month + '-' + this.s_day;
  78. this.select_day = (new Date(date_str)).valueOf();
  79. this.getDays();
  80. this.setTimeDetail();
  81. },
  82. setTimeDetail: function(){
  83. /** 初始化 时分秒 面板 */
  84. this.nav_hour = [],this.nav_minute = [],this.nav_second = [];
  85. for(let i = 0; i < 24; i++){
  86. i = i > 9 ? i : ('0'+i);
  87. this.nav_hour.push(i.toString());
  88. }
  89. for(let i = 0; i < 60; i++){
  90. i = i > 9 ? i : ('0'+i);
  91. this.nav_minute.push(i.toString());
  92. this.nav_second.push(i.toString());
  93. }
  94. },
  95. getDays: function(){
  96. /** 获取展示日期面板 */
  97. let date = [];
  98. let days = null;
  99. let date_first = this.s_year + '-' + this.s_month + '-01';
  100. date_first = (new Date(date_first)).valueOf();
  101. let week = (new Date(date_first)).getDay() || 7;
  102. if(this.s_month == '01' || this.s_month == '03' || this.s_month == '05' || this.s_month == '07' || this.s_month == '08' || this.s_month == '10' || this.s_month == '12'){
  103. days = 31;
  104. }
  105. else if(this.s_month == '04' || this.s_month == '06' || this.s_month == '09' || this.s_month == '11'){
  106. days = 30;
  107. }
  108. else{
  109. if(parseInt(this.s_year)%4 == 0 && parseInt(this.s_year)%100 != 0){
  110. days = 29;
  111. }
  112. else if (parseInt(this.s_year)%400 == 0) {
  113. days = 29;
  114. }
  115. else{
  116. days = 28;
  117. }
  118. }
  119. this.days = days;
  120. for(let i = week; i > 0 ; i--){
  121. date.push({
  122. timestamp: date_first - i*24*60*60*1000,
  123. date: (new Date(date_first - i*24*60*60*1000)).getDate()
  124. })
  125. }
  126. for(let i = 0; i < days; i++){
  127. date.push({
  128. timestamp: date_first + i*24*60*60*1000,
  129. date: (new Date(date_first + i*24*60*60*1000)).getDate(),
  130. current: true
  131. })
  132. }
  133. let dis = 42 - date.length;
  134. let date_last = date[date.length-1].timestamp;
  135. for(let i = 1; i <= dis; i++){
  136. date.push({
  137. timestamp: date_last + i*24*60*60*1000,
  138. date: (new Date(date_last + i*24*60*60*1000)).getDate()
  139. })
  140. }
  141. this.date = JSON.parse(JSON.stringify(date));
  142. },
  143. show: function(){
  144. //展示控件
  145. this.set_position();
  146. this.init();
  147. this.onSelect = true;
  148. },
  149. hide: function(){
  150. //隐藏控件
  151. this.onSelect = false;
  152. },
  153. select_date: function(d){
  154. //点击日期触发
  155. if(this.disable)return;
  156. if(this.min && this.min > d.timestamp || this.max && this.max < d.timestamp)return;
  157. this.switch_date(d);
  158. },
  159. switch_date: function(d){
  160. //切换日期
  161. let date = new Date(d.timestamp)
  162. this.s_year = date.getFullYear();
  163. let month = date.getMonth()+1;
  164. this.s_month = month > 9 ? month : ('0'+month);
  165. let day = date.getDate();
  166. day = day > 9 ? day : ('0'+day);
  167. this.s_day = day;
  168. if(!this.disable && (!this.min || d.timestamp >= this.min) && (!this.max || this.max >= d.timestamp))this.select_day = d.timestamp;
  169. },
  170. operate_month: function(n){
  171. //切换月份 左右箭头
  172. if(n > 0){
  173. this.switch_date(this.date[this.date.length-1]);
  174. }
  175. else{
  176. this.switch_date(this.date[0]);
  177. }
  178. },
  179. operate_year: function(n){
  180. //切换年份 左右箭头
  181. if(this.onSelectType !== 'year'){
  182. this.s_year = parseInt(this.s_year) + n;
  183. }
  184. else{
  185. if(n > 0){
  186. let last = this.nav_year[this.nav_year.length-1];
  187. this.nav_year = [];
  188. for(let i = 1;i <= 15; i++){
  189. this.nav_year.push(last+i);
  190. }
  191. }
  192. else{
  193. let first = this.nav_year[0];
  194. this.nav_year = [];
  195. for(let i = 15; i >= 1; i-- ){
  196. this.nav_year.push(first-i);
  197. }
  198. }
  199. }
  200. },
  201. set_operate: function(str){
  202. //点击控件头部 年份、月份 切换到对应的年份月份面板
  203. this.onSelectType = str;
  204. if(str == 'year'){
  205. this.nav_year = [];
  206. for(let i = this.s_year - 7; i <= this.s_year + 7; i++){
  207. this.nav_year.push(i);
  208. }
  209. }
  210. },
  211. select_month: function(n){
  212. //选择月份触发
  213. n = n > 9 ? n : ('0'+n);
  214. this.s_month = n;
  215. this.onSelectType = 'day';
  216. },
  217. select_year: function(n){
  218. //选择年份触发
  219. this.s_year = n;
  220. this.onSelectType = 'day';
  221. },
  222. select_detail: function(){
  223. //日期面板与具体时间面板相互切换
  224. if(this.disable)return;
  225. if(this.formate != 'second' && this.formate != 'minute' && this.formate != 'hour')return;
  226. if(this.onSelectType != 'time')this.onSelectType = 'time';
  227. else this.onSelectType = 'day';
  228. },
  229. select_hour: function(h){
  230. //选择小时
  231. this.s_hour = h;
  232. },
  233. select_minute: function(m){
  234. //选择分钟
  235. this.s_minute = m;
  236. },
  237. select_second: function(s){
  238. //选择秒钟
  239. this.s_second = s;
  240. },
  241. ensure: function(){
  242. //确定按钮触发
  243. if(this.disable){this.onSelect = false;return;}
  244. this.out_put();
  245. },
  246. now_date: function(){
  247. //点击现在按钮触发
  248. if(this.disable)return;
  249. let date = new Date();
  250. let year = date.getFullYear();
  251. let month = date.getMonth()+1;
  252. month = month > 9 ? month : ('0'+month);
  253. let day = date.getDate();
  254. day = day > 9 ? day : ('0'+day);
  255. let date_str = year + '-' + month + '-' + day;
  256. if(this.min && (new Date(date_str)).valueOf() < this.min || this.max && (new Date(date_str)).valueOf() > this.max)return;
  257. this.s_year = year;
  258. this.s_month = month;
  259. this.s_day = day;
  260. let hour = date.getHours();
  261. hour = hour > 9 ? hour : ('0'+hour);
  262. let minute = date.getMinutes();
  263. minute = minute > 9 ? minute : ('0'+minute);
  264. let second = date.getSeconds();
  265. second = second > 9 ? second : ('0'+second);
  266. this.s_hour = hour;
  267. this.s_minute = minute;
  268. this.s_second = second;
  269. this.select_day = (new Date(date_str)).valueOf();
  270. this.out_put();
  271. },
  272. out_put: function(){
  273. //输出日期,触发回调函数
  274. if(this.formate == 'second'){
  275. this.selected = this.s_year + '-' + this.s_month + '-' + this.s_day + ' ' + this.s_hour + ':' + this.s_minute + ':' + this.s_second;
  276. }
  277. else if(this.formate == 'minute'){
  278. this.selected = this.s_year + '-' + this.s_month + '-' + this.s_day + ' ' + this.s_hour + ':' + this.s_minute;
  279. }
  280. else if(this.formate == 'hour'){
  281. this.selected = this.s_year + '-' + this.s_month + '-' + this.s_day + ' ' + this.s_hour;
  282. }
  283. else{
  284. this.selected = this.s_year + '-' + this.s_month + '-' + this.s_day;
  285. }
  286. this.$emit('callback',this.selected);
  287. this.onSelect = false;
  288. },
  289. clear_date: function(){
  290. //清空日期
  291. if(this.disable)return;
  292. this.selected = null;
  293. this.onSelect = false;
  294. this.$emit('callback',this.selected);
  295. },
  296. set_position: function(){
  297. //设置控件出现的位置,当控件位于浏览器底端时,控件将出现在文本框上方位置
  298. let clientH = document.documentElement.clientHeight || document.body.clientHeight;
  299. let domB = this.$el.getBoundingClientRect().bottom;
  300. let offset = clientH - domB;
  301. if (offset < 320) {this.position_top = true;}
  302. }
  303. },
  304. mounted(){
  305. this.init();
  306. },
  307. watch: {
  308. 's_month': function(){
  309. this.getDays();
  310. },
  311. 's_year': function(){
  312. this.getDays();
  313. },
  314. 'timeObj.min': function(){
  315. this.init();
  316. }
  317. }
  318. }
  319. </script>

1.3 css 部分

  1. <style scoped>
  2. .j_div_time{font-size:14px;font-family:'微软雅黑';position:relative;user-select:none;}
  3. .j_div_time_mask{position:fixed;top:0;left:0;background-color:#fff;opacity:0;width:100%;height:100%;z-index:1000;}
  4. .j_ip_time{width:95%;outline:none;height: 28px;padding:0;border:1px solid #bbb;padding-left:5%;color:#555;}
  5. .j_ip_hide{width:0;height:0;border:none;opacity:0;margin:0;padding:0;clear:both;float:left;position:absolute;top:0;left:0;}
  6. .j_div_time_select{width:270px!important;position:absolute;height:auto!important;top:32px!important;box-shadow: 0 0 5px #ccc;background-color:#fff;z-index:20000;}
  7. .j_div_time_select_top{width:270px!important;position:absolute;height:auto!important;box-shadow: 0 0 5px #ccc;background-color:#fff;z-index:20000;top:-325px!important;}
  8. .j_div_time_select_hide{width:0;height:0;overflow:hidden;top:50px;transition:top 0.3s;}
  9. .j_div_time_head{width:100%;background-color:#5896f8;height:40px;color:#fff;text-align:center;}
  10. .j_div_time_head span{display:inline-block;height:30px;line-height:30px;margin-top:5px;cursor:pointer;}
  11. .j_div_time_head .sp_left{float:left;margin-left:10px;}
  12. .j_div_time_head .sp_right{float:right;margin-right:10px;}
  13. .j_div_time_head .sp_year{margin-right:5px;}
  14. .j_div_time_body,.j_div_time_body_month,.j_div_time_body_year,.j_div_time_body_detail{padding: 5px 5px 5px 5px;height:223px;}
  15. .j_div_time_nav{color:#111;margin-top:10px;}
  16. .j_div_time_nav span{display:inline-block;height:30px;line-height:30px;text-align:center;width:14.28%;}
  17. .j_div_time_date{color:#d2d2d2;margin-top:3px;}
  18. .j_div_time_date span{display:inline-block;height:30px;line-height:30px;text-align:center;width:14.28%;transition:background-color 0.3s,color 0.3s;}
  19. .j_div_time_date span:hover{background-color:#ddd;cursor:pointer;color:#666;}
  20. .j_div_time_date .span_disable{background-color:#fff!important;color:#d2d2d2!important;cursor:not-allowed!important;}
  21. .j_div_time_date .span_current_month{color:#666;}
  22. .j_div_time_date .span_today{color:#3896f8;background-color:#ddd;}
  23. .j_div_time_date .span_selected{color:#fff!important;background-color:#3896f8!important;}
  24. .j_div_time_body_month span{display:inline-block;width:33.3%;height:25%;line-height:53px;text-align:center;cursor:pointer;color:#666;}
  25. .j_div_time_body_month span:hover{background-color:#ddd;}
  26. .j_div_time_body_month .span_selected{background-color:#3896f8!important;color:#fff;}
  27. .j_div_time_body_year span{display:inline-block;width:33.3%;height:20%;line-height:44.5px;text-align:center;color:#666;cursor:pointer;}
  28. .j_div_time_body_year span:hover{background-color:#ddd;}
  29. .j_div_time_body_year .span_selected{background-color:#3896f8!important;color:#fff;}
  30. .j_div_time_body_detail{color:#666;}
  31. .j_div_time_body_detail .title span{display:inline-block;width:32.2%;height:17%;line-height:38.5px;text-align:center;}
  32. .j_div_time_body_detail .detail{width:32.8%;border:1px solid #ddd;float:left;height:80%;overflow:hidden;}
  33. .j_div_time_body_detail .detail:hover{overflow-y:auto;}
  34. .j_div_time_body_detail .detail span{width:100%;display:inline-block;height:30px;line-height:30px;text-align:center;cursor:pointer;}
  35. .j_div_time_body_detail .detail span:hover{background-color:#ddd;}
  36. .j_div_time_body_detail .div_detail_container{position:relative;}
  37. .j_div_time_body_detail .span_selected{background-color:#3896f8!important;color:#fff;}
  38. .j_div_time_footer{color:#666;height:35px;padding-top:10px;border-top:1px solid #ddd;}
  39. .j_div_time_footer span{display:inline-block;height:24px;line-height:24px;text-align:center;width:46px;float:right;border:1px solid #ddd;cursor:pointer;}
  40. .j_div_time_footer .sp_sure{margin-right:10px;}
  41. .j_div_time_footer .sp_now{border-left:none;border-right:none;}
  42. .j_div_time_footer .sp_detail{width:auto;border:none;margin-right:35px;}
  43. .j_div_time_footer .sp_detail:hover{color:#3896f8;}
  44. .j_div_time_footer .sp_disable{cursor:not-allowed!important;color:#d2d2d2!important;}
  45. </style>

2、引入具体参数配置

tips:具体引入过程忽略

参数名 参数意义 默认值 是否必须 注释
selected 初始化日期控件值 null
disable 日期控件是否可用 false
max 最大可选日期 null
min 最小可选日期 null
formate 输出日期格式 day 值可选day、hour、minute、second

工作场景中经常遇到 开始时间结束时间 相互影响的场景。可以在回调函数里面将 开始/结束 日期 赋值给 对应时间对象的 min/max 属性,从而达到相互影响的效果。(其实还是稍微有点麻烦的)


3、效果图

39282b04a5bf35c2cb65b836c31d0d9.png-8.9kB
56c1b9fcd3381567f6e9c147a3b7611.png-6.9kB
7e72c35b4bdec2f999d1f827fa26e14.png-8.8kB
dee2fc9ea762110ad71367f18a17adf.png-7.6kB
1fbc55b8320b6a16b8ab7fc6615199a.png-8.6kB

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注