日期组件
自定义日期组件
DatePicker.vue
<template> <div class="e-calendar-wrapper"> <div class="e-calendar"> <div class="e-date-select"> <el-button type="primary" style="background: #43BAFE" icon="el-icon-plus" size="small" @click="addMeeting" >添加会议</el-button> </div> <div class="e-calendar-container" v-show="!showYear"> <div class="e-calendar-toolbar"> <div class="e-calendar-svg" @click="prevMonth"> <svg viewBox="0 0 24 24" class="e-calendar-svg-icon"> <path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path> </svg> <transition name="e_calendar_svg_btn"> <div class="e-calendar-svg-cover" v-if="prevMonthClick"></div> </transition> </div> <div class="e-calendar-toolbar-title"> <transition :name="fadeXType"> <div :key="showDate.monthStr" class="e-calendar-toolbar-title-content"> <strong>{{showDate.year}}年</strong> <span>{{ showDate.monthStr }}</span> </div> </transition> </div> <div class="e-calendar-svg" @click="nextMonth"> <svg viewBox="0 0 24 24" class="e-calendar-svg-icon"> <path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path> </svg> <transition name="e_calendar_svg_btn"> <div class="e-calendar-svg-cover" v-if="nextMonthClick"></div> </transition> </div> </div> <div class="e-calendar-week"> <span class="e-calendar-week-day">日</span> <span class="e-calendar-week-day">一</span> <span class="e-calendar-week-day">二</span> <span class="e-calendar-week-day">三</span> <span class="e-calendar-week-day">四</span> <span class="e-calendar-week-day">五</span> <span class="e-calendar-week-day">六</span> </div> <div class="e-calendar-monthday"> <transition :name="fadeXType"> <div :key="showDate.monthStr" class="e-calendar-monthday-wrapper"> <div class="e-calendar-monthday-row" v-for="( row,index) in rows" :key="index"> <span v-for="(day,index) in row" :key="index" class="e-calendar-monthday-row-day" @click="selectDay(day)" :class="{'active': day.selected, 'disabled': day.disabled, 'pointer': day.value !== ''}" > <span v-text="day.value" class="e-calendar-monthday-row-day-value"></span> <transition name="e_calendar_day"> <span class="e-calendar-monthday-row-day-cover" v-if="day.selected"></span> </transition> </span> </div> </div> </transition> </div> </div> <ul class="e-calendar-year" v-show="showYear" ref="yearList"> <li v-for="item in yearList" :key="item" v-text="item" :class="{'active': item === selectDate.year}" @click="selectYear(item)" ></li> </ul> <div class="e-calendar-actions"> <button class="btn" @click="cancel">取消</button> <button class="btn" @click="confirm">确定</button> </div> </div> </div> </template> <script> import Vue from "vue"; import { Button } from "element-ui"; Vue.use(Button); // 阿拉伯数字 转 汉字数字的基本库 const weekJson = { 1: "星期日", 2: "星期一", 3: "星期二", 4: "星期三", 5: "星期四", 6: "星期五", 7: "星期六" }; const monthJson = { 1: "1月", 2: "2月", 3: "3月", 4: "4月", 5: "5月", 6: "6月", 7: "7月", 8: "8月", 9: "9月", 10: "10月", 11: "11月", 12: "12月" }; export default { name: "DatePicker", props: { // 打开date picker的初始值,必传,格式是(2017-08-11) date: { type: String, required: true }, // 日期最小值 minDate: { type: String, default: "1970-01-01" }, // 日期最大值 maxDate: { type: String, default: "2020-12-31" } }, computed: { yearList() { const result = []; for (let i = this.copyMinDate.year; i <= this.copyMaxDate.year; i += 1) { result.push(i); } return result; }, title() { // calendar 上面的所有 title 信息包括二部分 return { year: "", monthday: "" }; }, rows() { const { year, month } = this.showDate; const months = new Date(year, month, 0).getDate(); const result = []; let row = []; let weekValue; // 按照星期分组 for (let i = 1; i <= months; i += 1) { // 根据日期获取星期,并让开头是1,而非0 weekValue = new Date(year, month, i).getDay() + 1; // 判断月第一天在星期几,并填充前面的空白区域 if (i === 1 && weekValue !== 1) { this.addRowEmptyValue(row, weekValue); this.addRowDayValue(row, i); } else { this.addRowDayValue(row, i); // 判断月最后一天在星期几,并填充后面的空白区域 if (i === months && weekValue !== 7) { this.addRowEmptyValue(row, 7 - weekValue + 1); } } // 按照一周分组 if (weekValue % 7 === 0 || i === months) { result.push(row); row = []; } } this.showDate.monthStr = monthJson[this.showDate.month + 1]; console.log("rows", result); return result; } }, data() { return { selectDate: { year: "", month: "", day: "", week: "", date: "", weekStr: "", monthStr: "" }, // 选择的时间,默认是用户传的date时间 showDate: { year: "", month: "", day: "", week: "", date: "", monthStr: "", weekStr: "" }, copyMinDate: { year: "", month: "", day: "" }, copyMaxDate: { year: "", month: "", day: "" }, toolbar: "", fadeXType: "fadeX_Prev", nextMonthClick: false, prevMonthClick: false, showYear: false }; }, created() { this.initDatePicker(); }, methods: { addMeeting() { alert(111); }, initDatePicker() { this.showDate = { ...this.splitDate(this.date, true) }; this.copyMinDate = { ...this.splitDate(this.minDate) }; this.copyMaxDate = { ...this.splitDate(this.maxDate) }; this.selectDate = { ...this.showDate }; }, splitDate(date, addStr) { let result = {}; const splitValue = date.split("-"); try { if (!splitValue || splitValue.length < 3) { throw new Error("时间格式不正确"); } result = { year: Number(splitValue[0]), month: Number(splitValue[1]) - 1, day: Number(splitValue[2]) }; if (addStr) { result.week = new Date(result.year, result.month, result.day).getDay() + 1; result.monthStr = monthJson[result.month + 1]; result.weekStr = weekJson[result.week]; } } catch (error) { console.error(error); } return result; }, addRowEmptyValue(row, count) { for (let w = 1; w < count; w += 1) { row.push({ value: "" }); } }, addRowDayValue(row, i) { const value = { value: i }; const { day, month, year } = this.selectDate; const showDate = this.showDate; // 判断已经选择的 if (year === showDate.year && month === showDate.month && day === i) { value.selected = true; } // 当日期在最小值之外,禁止点击 if (this.isMinLimitMonth() && i < this.copyMinDate.day) { value.disabled = true; } // 当日期在最大值之外,禁止点击 if (this.isMaxLimitMonth() && i > this.copyMinDate.day) { value.disabled = true; } row.push(value); }, /** * 切换到上一个月 */ prevMonth() { if (this.prevMonthClick) { return; } this.prevMonthClick = true; setTimeout(() => { this.prevMonthClick = false; }, 500); this.fadeXType = "fadeX_Prev"; // 如何当前月份已经小于等于minMonth 就不让其在执行 if (this.isMinLimitMonth()) { return; } const { year, month } = this.showDate; // 判断当前月份,如果已经等于1(1就是一月,而不是二月) if (month <= 0) { this.showDate.year = year - 1; this.showDate.month = 11; } else { this.showDate.month -= 1; } }, /** * 切换到下一个月 */ nextMonth() { if (this.nextMonthClick) { return; } this.nextMonthClick = true; setTimeout(() => { this.nextMonthClick = false; }, 500); this.fadeXType = "fadeX_Next"; // 如何当前月份已经大于等于maxMonth 就不让其在执行 if (this.isMaxLimitMonth()) { return; } const { year, month } = this.showDate; // 判断当前月份,如果已经等于12(12就是十二月) if (month >= 11) { this.showDate.year = year + 1; this.showDate.month = 0; } else { this.showDate.month += 1; } }, resetSelectDate(dayValue) { this.selectDate = { ...this.showDate }; this.selectDate.day = dayValue; this.selectDate.week = new Date(this.showDate.year, this.showDate.month, dayValue).getDay() + 1; this.selectDate.weekStr = weekJson[this.selectDate.week]; }, selectDay(day) { if (day.disabled || day.selected || day.value === "") { return; } this.resetSelectDate(day.value); }, selectYear(value) { this.showYear = false; this.showDate.year = value; let type; // 当日期在最小值之外,月份换成最小值月份 或者 当日期在最大值之外,月份换成最大值月份 if (this.isMinLimitMonth()) { type = "copyMinDate"; } else if (this.isMaxLimitMonth()) { // 当日期在最大值之外,月份换成最大值月份 type = "copyMaxDate"; } if (type) { this.showDate.month = this[type].month; this.showDate.day = this[type].day; this.resetSelectDate(this.showDate.day); return; } let dayValue = this.selectDate.day; // 判断日是最大值,防止另一个月没有这个日期 if (this.selectDate.day > 28) { const months = new Date( this.showDate.year, this.showDate.month, 0 ).getDate(); // 当前月份没有这么多天,就把当前月份最大值赋值给day dayValue = months < dayValue ? months : dayValue; } this.resetSelectDate(dayValue); }, isMinLimitMonth() { return ( this.showDate.year <= this.copyMinDate.year && this.showDate.month <= this.copyMinDate.month ); }, isMaxLimitMonth() { return ( this.showDate.year >= this.copyMaxDate.year && this.showDate.month >= this.copyMaxDate.month ); }, openYearList() { if (this.showYear) { this.showYear = false; return; } const index = this.yearList.indexOf(this.selectDate.year); this.showYear = true; setTimeout(() => { this.$refs.yearList.scrollTop = (index - 3) * 40; }); }, openCalendarList() { this.showYear = false; }, // 保持两位数 keepDoubleDigit(number) { return number > 9 ? number : `0${number}`; }, confirm() { const { year, month, day, week, weekStr, monthStr } = this.selectDate; this.$emit("confirm", { date: `${year}-${this.keepDoubleDigit(month)}-${this.keepDoubleDigit( day )}`, year, month, week, monthStr, weekStr, day }); }, cancel() { this.$emit("cancel"); } } }; </script> <style lang="scss"> @import "@/assets/css/basic.scss"; @import "@/assets/css/borderBox.scss"; .e- { &calendar-wrapper { position: fixed; left: 0; top: 0; bottom: 0; right: 0; // background-color: rgba(0, 0, 0, 0.5); background: #333333; border: 1px solid #a5a5a5; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5); border-radius: 5px; z-index: 99999; display: flex; justify-content: center; align-items: center; flex-direction: row; } &calendar { //background-color: #a5a5a5; width: 310px; color: #e6e6e6; opacity: 0.8; } &date-select { //background-color: #00bcd4; padding: 3px 3px; color: #ffffff; } &date-year { font-size: 18px; padding-bottom: 4px; position: relative; width: 66px; height: 25px; overflow: hidden; &-select { position: absolute; opacity: 0.7; &.active { opacity: 1; } } } &date-monthday { font-size: 26px; position: relative; width: 100%; height: 36px; overflow: hidden; &-select { position: absolute; opacity: 0.7; &.active { opacity: 1; } } } &calendar-container { width: auto; } &calendar-toolbar { margin: 5px 10px 5px 10px; height: 40px; display: flex; justify-content: space-between; align-items: center; &-title { position: relative; width: 100px; height: 22px; text-align: center; &-content { position: absolute; width: 100%; font-size: 16px; } } } &calendar-svg { padding: 8px; position: relative; height: 40px; width: 40px; &-icon { display: block; fill: currentColor; height: 24px; width: 24px; user-select: none; position: relative; z-index: 2; } &-cover { position: absolute; left: 0; top: 0; z-index: 1; width: 100%; height: 100%; background-color: #e0e0e0; border-radius: 50%; opacity: 0; display: inline-block; } } &calendar-week { width: 100%; font-size: 12px; //color: rgba(0, 0, 0, 0.87); color: #ffffff; opacity: 0.7; display: flex; justify-content: center; align-items: center; height: 16px; &-day { flex: 1; text-align: center; } } &calendar-monthday { padding-top: 10px; font-size: 14px; position: relative; width: 100%; min-height: 210px; overflow: hidden; &-wrapper { position: absolute; width: 100%; height: 100%; } &-row { display: flex; justify-content: center; align-items: center; &-day { display: flex; justify-content: center; align-items: center; flex: 1; position: relative; height: 35px; &.pointer { cursor: pointer; } &-value { position: relative; z-index: 1; } &-cover { width: 25px; height: 25px; background-color: #00bcd4; position: absolute; left: 10px; top: 5px; transform: translate3d(0, 0, 0); z-index: 0; border-radius: 50%; opacity: 1; display: block; } &.active { color: #ffffff; } &.disabled { opacity: 0.4; cursor: not-allowed; } } } } &calendar-year { height: 276px; overflow: auto; li { padding: 10px; text-align: center; font-size: 16px; &.active { color: #00bcd4; font-size: 20px; font-weight: bold; } } } &calendar-actions { padding: 0 20px 15px; display: flex; justify-content: flex-end; align-items: center; .btn { color: #00bcd4; margin-left: 40px; font-size: 16px; background-color: transparent; } } } .fadeX_Prev-enter-active, .fadeX_Prev-leave-active, .fadeX_Next-enter-active, .fadeX_Next-leave-active, .fadeY-enter-active, .fadeY-leave-active { transition: all 0.3s; } .fadeX_Prev-enter { transform: translateX(-100px); opacity: 0; } .fadeX_Prev-leave-active { transform: translateX(100px); opacity: 0; } .fadeX_Next-enter { transform: translateX(100px); opacity: 0; } .fadeX_Next-leave-active { transform: translateX(-100px); opacity: 0; } .fadeY-enter { transform: translateY(30px); opacity: 0; } .fadeY-leave-active { transform: translateY(-30px); opacity: 0; } .e_calendar_svg_btn-enter-active, .e_calendar_svg_btn-leave-active { transition: all 1s; } .e_calendar_svg_btn-enter { opacity: 1; } .e_calendar_day-enter-active { transition: all 0.2s; } .e_calendar_svg_btn-leave-active, .e_calendar_day-enter { opacity: 0; } .e_calendar_day-enter { width: 0; height: 0; transform: translate3d(12px, 12px, 0); } </style>
basic.scss
@import "./color"; //reset element style html { -ms-overflow-style:none; overflow:-moz-scrollbars-none; } html::-webkit-scrollbar{width:0} body,div,span,header,footer,nav,section,aside,article,ul, li, a, p, textarea, button, input, select { padding: 0; margin: 0; list-style: none; font-weight: normal; font-style: normal; text-decoration: none; border: none; font-family: 'Hiragino Sans GB', 'Hiragino Sans GB W3', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif; -webkit-tap-highlight-color:transparent; &:focus { outline: none; } } html{ -webkit-overflow-scrolling : touch !important; overflow: auto !important; height: 100% !important; } body{ -webkit-overflow-scrolling : touch !important; overflow: auto !important; position: relative; height:100% !important; width:100%; background:$c-little-gray; font-size: 12px; } .clearfix { zoom: 1; &::after { visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; } } .vertical-middle { height: 100%; width: 0; display: inline-block; vertical-align: middle; } .pull-left{ float:left; } .pull-right{ float:right; } .mt6{ margin-top: 6px; } .ml60{ margin-left: 60px; } .pointer{ cursor: pointer; } [v-cloak] { display: none; } .slide-fade-enter-active { transition: all .6s ease; } .slide-fade-leave-active { transition: all .5s ease; } .slide-fade-enter, .slide-fade-leave-active { transform: translateX(60px); opacity: 0; }
color.vue
$c-block : #000; $c-natural-block : #3B3F44; $c-gray : #979797; $c-small-gray: rgb(151, 151, 151); $c-some-gray: rgb(190,190,190); $c-light-gray : #BEBEBE;//提示文字 $c-text-main : #3B3D5B;//提示文字 $c-half-main : lighten($c-text-main, 50%);//提示文字 $c-smoky-gray : #DAD7D9; $c-lightslate-gray: #E0E0E0; //边框颜色 $c-half-lightslate-gray: #F0F0F0; //边框颜色 $c-little-gray: #f9f9f9; //背景 $c-ivory : #f7f7f7; $c-light-white: #EEEEEE; $c-white : #FFF; $c-red : #E34F51; $c-natural-red : #E44F51; $c-light-red : #EF7979 ; $c-pink : #ffa1a1; $c-orange : #f34e19; $c-blue : #1975C3; $c-green : #25C354; $c-yellow : #F6A623; $c-primary : #00bcd4; $c-success : #6AC8D4; $c-light-blue : #6EB8F1; $c-purple-grey : #212340;// 导航栏背景 $b-lightslate-gray : 1px solid $c-lightslate-gray; $b-half-lightslate-gray : 1px solid $c-half-lightslate-gray; $b-main: 1px solid $c-text-main; $b-half-main: 1px solid lighten($c-text-main, 50%); $b-bolder-active-main: 3px solid $c-text-main; $b-mask: rgba(59,61,91,0.60); $b-transparent-block: rgba(0,0,0,0); // app colors $c-start: #6bf3d4; // 渐变起始色 $c-end: #6eb8f1; // 渐变终止色 $c-light-blue : #6EB8F1; $c-number: #D67573; //用于数字提示 $c-red: #E44F51; //用于数字提示 $c-bg-tip: #E44F51; //用于数字提示 $c-bg: #f9f9f9; //背景; $c-bg-white: #ffffff; //白色背景; $c-gray : #979797;//灰色字体 $c-light-gray : #bebebe;//提示文字 $c-lightslate-gray: #E0E0E0; //边框颜色 $c-divider:#e6e6e6; //用于分割线 $c-tabBar: #667980; // 用于默认Tab bar Icon $c-introdution: #667980; // 用于默认Tab bar Icon $c-btnText: #6eb8f1; //适用于高亮按钮文字 $c-title: #4d4d4d; // 适用于标题 $c-text: #4d4d4d; // 适用于正文 $c-title-white: #ffffff; // 适用于标题 $c-text-white: #ffffff; // 适用于正文 $c-text-main : #3B3D5B;//提示文字 $c-text-gold: #FFE98D;// 金字 $c-border-main : #3B3D5B;//提示文字 $c-assistTxt: #808080; //适用于辅助文字 $c-subtitle: #808080; //适用于副标题 $c-tipText: #b3b3b3; //适用于提示性文字 $c-timeText: #b3b3b3; //适用于时间文字 $c-border:#e6e6e6; //适用于边框 $c-light-border:#f0f0f0; //浅颜色边框 $c-bg-authenticate:#F5A623;// 认证边框色 $c-status: #6AC8D4; //选中的状态 $c-border-common:#e0e0e0; $c-border-half-common: rgba(224,224,224,0.5); $b-border-half-common: 1px solid $c-border-common; $b-border-common: 1px solid $c-border-half-common;
borderBox.scss
body,div,span,header,footer,nav,section,aside,article,ul, li, a, p, h1, h2, h3, h4,h5, i, b, textarea, button, input, select { box-sizing: border-box; }
使用
<date-picker v-if="showDatePicker" :date="date" :min-date="minDate" :max-date="maxDate" @confirm="confirm" @cancel="cancel" ></date-picker> <script> import DatePicker from "@/components/DatePicker.vue"; export default { components: { DatePicker }, data() { return { showDatePicker: false, date: "2019-01-28", minDate: "2000-09-11", maxDate: "2020-09-11", selectedDate: "点击选择日期" }; }, computed: {}, methods: { confirm(value) { this.showDatePicker = false; this.selectedDate = value; }, cancel() { this.showDatePicker = false; } } }; </script>