自定义日期组件
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>