Vue文件封装日历组件

2023-05-24,,

封装就是要具有灵活性,样式自适应,调用的时候传入props就可以变成自己想要的样式。

效果展示网址:https://1963331542.github.io/

源代码:

 <template>
<div :style="mainBoxStyle" @mouseenter="setEnterFn()" @mouseleave="setLeaveFn()">
<div :style="topLineStyle">
<div
@click.stop="timeBoxShow=false;setNowTime(true)"
:style="[iptStyle,{float:'left'}]"
>{{curIptDate}}</div>
<div
@click="timeBoxShow=true;setTimeIpt(true)"
:style="[iptStyle,{float:'right'}]"
>{{curIptTime}}</div>
</div>
<div :style="timeBoxStyle" v-show="timeBoxShow">
<div
:style="[timeAListStyle,
{paddingLeft:setWidth(12),width:setWidth(70)}]"
@mousewheel.stop="setIndex($event,1)"
v-on:DOMMouseScroll.stop="setIndex($event,1)"
>
<div
:style="[{zIndex:99,width:'100%',
height:setHeight(30),position:'absolute',
top:setHeight(60),left:setWidth(12),borderBottom:border,
borderTop:border}]"
></div>
<div
v-for="(item,i) in hourArr"
@click="curShowHourIndex=(i-2)"
:style="[timeOneStyle,i==0?
{marginTop:setHeight((i-curShowHourIndex)*30),
transition:'margin-top 0.2s'}:{},(i-curShowHourIndex)==2?
{display:'flex',alignItems:'center'}:{},
(i-curShowHourIndex)==5||(i-curShowHourIndex)==-1?
{color:'rgba(0,0,0,0)'}:{}]"
:key="i"
>{{item}}</div>
</div>
<div
:style="timeAListStyle"
@mousewheel.stop="setIndex($event,2)"
v-on:DOMMouseScroll.stop="setIndex($event,2)"
>
<div
:style="[{zIndex:99,width:'100%',height:setHeight(30),
position:'absolute',top:setHeight(60),left:0,
borderBottom:border,borderTop:border}]"
></div>
<div
v-for="(item,i) in minArr"
@click="curShowMinIndex=(i-2)"
:style="[timeOneStyle,i==0?
{marginTop:setHeight((i-curShowMinIndex)*30),transition:'margin-top 0.2s'}:{},
(i-curShowMinIndex)==2?{display:'flex',alignItems:'center'}:{},
(i-curShowMinIndex)==5||(i-curShowMinIndex)==-1?{color:'rgba(0,0,0,0)'}:{}]"
:key="i"
>{{item}}</div>
</div>
<div
@mousewheel.stop="setIndex($event,3)"
v-on:DOMMouseScroll.stop="setIndex($event,3)"
:style="[timeAListStyle,{width:setWidth(30),
paddingRight:setWidth(12)}]"
>
<div
:style="[{zIndex:99,width:setWidth(30),paddingRight:setWidth(12),
height:setHeight(30),position:'absolute',top:setHeight(60),left:0,
borderBottom:border,borderTop:border}]"
></div>
<div
v-for="(item,i) in secArr"
@click="curShowSecIndex=(i-2)"
:style="[timeOneStyle,i==0?{marginTop:setHeight((i-curShowSecIndex)*30),
transition:'margin-top 0.2s'}:{},
(i-curShowSecIndex)==2?{display:'flex',alignItems:'center'}:{},
(i-curShowSecIndex)==5||(i-curShowSecIndex)==-1?{color:'rgba(0,0,0,0)'}:{}]"
:key="i"
>{{item}}</div>
</div>
<div :style="{height:setHeight(33),float:'left',width:'100%'}">
<div @click="timeBoxShow=false" :style="[bottomTimeBtnStyle,{color:'#1e90ff'}]">确认</div>
<div @click="timeBoxShow=false;curIptTime=''" :style="bottomTimeBtnStyle">取消</div>
</div>
</div>
<div :style="[onelineStyle,{height:setHeight(37)}]">
<div
:style="[
oneBtnStyle,
i<2?{float:'left'}:{float:'right'},
{
height:setHeight(34)
}
]"
v-for="(item,i) in [0,1,2,3]"
:key="i"
>
<div
:style="[
sanjiaoStyle,
i<2?{float:'left'}:
{float:'right'}
]"
:class="{'iconfont':true,'icon-zuoshuangjiantou':i==0,
'icon-icon-copy':i==1,'icon-icon':i==3,'icon-zuoshuangjiantou-copy':i==2}"
@click="i==0?setDate(curYear-1,curMonth):
i==1?setDate(curYear,curMonth-1):
i==3?setDate(curYear,curMonth+1):setDate(curYear+1,curMonth)"
></div>
</div>
<div :style="titleStyle">
<span>{{curYear}}</span>年
<span>{{curMonth}}</span>月
</div>
</div>
<div :style="[onelineStyle,{height:setHeight(34)}]">
<div
v-for="(item,j) in [0,1,2,3,4,5,6]"
:key="j"
:style="[oneBtnStyle,{height:setHeight(34),fontSize:setHeight(16)},
j==0?{clear:'both'}:{}]"
>{{weekList[j]}}</div>
</div>
<div
@mouseleave="curEnterDay=0"
:style="[onelineStyle,{height:setHeight(30*arr.length/7)}]"
>
<div
@mouseenter="i>=arr.indexOf(1)&&i<arr.lastIndexOf(1)?curEnterDay=item:''"
@click.stop="i>=arr.indexOf(1)&&i<arr.lastIndexOf(1)
?clickFn(item):i<arr.indexOf(1)
?switchMonthClick('-1',item):i>=arr.lastIndexOf(1)
?switchMonthClick('+1',item):()=>{}"
:style="[
oneBtnStyle,
{color:'#999999'},
i>=arr.indexOf(1)&&i<arr.lastIndexOf(1)?
(item==curClickDay&&curMonth==curClickMonth&&curYear==curClickYear)?
selectStyle:
(item==curClickDay+1&&curMonth==curClickMonth&&curYear==curClickYear&&(i+1)%7==1)
?{color:'#333333',clear:'both'}:
curEnterDay==item||(demoDate.getFullYear()==curYear
&&demoDate.getMonth()==curMonth-1
&&demoDate.getDate()==item)?
{color:'#1e90ff'}:{color:'#333333'}
:{},
]"
v-for="(item,i) in arr"
:key="i"
>{{item}}</div>
</div>
<div :style="[onelineStyle,{padding:0,paddingTop:setHeight(14),height:setHeight(54)}]">
<div :style="bottomLineStyle">
<div @click="emitDate()" :style="bottomBtnStyle">确定</div>
<div
@click="setNowTime()"
:style="[bottomBtnStyle,{border:'none',color:'#1e90ff'}]"
>此刻</div>
</div>
</div>
</div>
</template> <script>
import Vue from '@/main.js'
export default {
name: "calendar",
data() {
return {
mainBoxStyle: {},
sanjiaoStyle: {},
oneBtnStyle: {},
onelineStyle: {},
titleStyle: {},
topLineStyle: {},
bottomLineStyle: {},
bottomBtnStyle: {},
selectStyle: {},
timeBoxStyle: {},
bottomTimeBtnStyle: {},
iptStyle: {},
timeAListStyle: {},
curYear: 0,
curMonth: 0,
curFirstWeek: "",
weekList: ["日", "一", "二", "三", "四", "五", "六"],
dateNumList: [],
arr: [],
curDate: {},
curIptDate: "",
curIptTime: "",
curEnterDay: 0,
curClickDay: 0,
curClickMonth: 0,
curClickYear: 0,
curClickHour: "00",
curClickMin: "00",
curClickSec: "00",
demoDate: {},
hourArr: [],
minArr: [],
secArr: [],
timeOneStyle: {},
curShowHourIndex: 0,
curShowMinIndex: 0,
curShowSecIndex: 0,
timeBoxShow: false,
isLeaveCount:0,
isEnterCount:0,
};
},
created() {
for (var i = 0; i < 60; i++) {
if (i < 24) {
i < 10 ? this.hourArr.push("0" + i) : this.hourArr.push(i + "");
}
i < 10 ? this.minArr.push("0" + i) : this.minArr.push(i + "");
i < 10 ? this.secArr.push("0" + i) : this.secArr.push(i + "");
}
this.curDate = new Date();
if (this.value) {
this.setDate(
this.value.slice(0, 4),
this.value.slice(5, 7),
this.value.slice(
this.value.lastIndexOf(this.format[1]) + 1,
this.value.lastIndexOf(this.format[1]) + 3
),
this.value.slice(
this.value.lastIndexOf(this.format[1]) + 4,
this.value.lastIndexOf(this.format[1]) + 6
),
this.value.slice(
this.value.lastIndexOf(this.format[1]) + 7,
this.value.lastIndexOf(this.format[1]) + 9
)
);
this.clickFn(this.value.slice(8, 10) * 1);
}
var cw = (this.setWidth(33, true) - this.setHeight(28, true)).toFixed(
2
);
var dw = (cw / 2).toFixed(2);
this.selectStyle = {
background: "#1e90ff",
width: this.setHeight(28),
height: this.setHeight(28),
margin: this.setHeight(1) + " 0",
marginLeft: dw + this.unit,
marginRight: cw - dw + this.unit,
float: "left",
color: "#fff",
borderRadius: "50%"
};
this.demoDate = new Date();
this.timeBoxStyle = {
width: this.setWidth(160),
height: this.setHeight(208),
background: this.background,
fontSize: this.fontSize + this.unit,
color: this.color,
border: this.border,
position: "absolute",
top: this.setHeight(46),
left: this.setWidth(130),
boxShadow: `0 0 ${this.setHeight(6)} rgba(0,0,0,0.3)`
};
this.timeAListStyle = {
width: this.setWidth(58),
height: this.setHeight(161),
borderBottom: this.border,
overflow: "hidden",
// display:'flex',
// flexDirection:'column',
float: "left",
position: "relative",
marginTop: this.setHeight(10)
};
this.timeOneStyle = {
width: "100%",
height: this.setHeight(30),
lineHeight: this.setHeight(30),
cursor: "pointer"
};
this.bottomBtnStyle = {
width: this.setWidth(45),
height: this.setHeight(23),
border: this.border,
borderRadius: this.setHeight(3),
textAlign: "center",
lineHeight: this.setHeight(21),
float: "right",
marginRight: this.setHeight(4),
marginTop: this.setHeight(8),
marginBottom: this.setHeight(8),
cursor: "pointer"
};
this.bottomTimeBtnStyle = {
width: this.setWidth(42),
height: this.setHeight(33),
textAlign: "center",
lineHeight: this.setHeight(33),
float: "right",
cursor: "pointer"
};
this.bottomLineStyle = {
width: "100%",
height: this.setHeight(40),
borderTop: this.border
};
this.iptStyle = {
width: this.setWidth(116),
height: this.setHeight(27),
border: this.border,
borderRadius: this.setHeight(3),
textAlign: "center",
lineHeight: this.setHeight(27),
marginTop: this.setHeight(9),
cursor: "pointer"
};
this.topLineStyle = {
width: "100%",
height: this.setHeight(47),
borderBottom: this.border,
padding: "0 " + this.setWidth(7)
};
this.mainBoxStyle = {
width: this.w + this.unit,
height: this.h,
float: "left",
background: this.background,
fontSize: this.fontSize + this.unit,
color: this.color,
border: this.border,
position: "relative",
boxShadow: `0 0 ${this.setHeight(6)} rgba(0,0,0,0.3)`
};
this.oneBtnStyle = {
width: this.setWidth(33),
height: this.setHeight(30),
fontSize: this.fontSize + this.unit,
float: "left",
background: this.background,
display: "flex",
alignItems: "center",
justifyContent: "center",
cursor: "pointer"
};
this.sanjiaoStyle = {
fontSize: this.setHeight(22)
};
this.titleStyle = {
width: this.setWidth(96),
height: this.setHeight(32),
fontSize: this.setHeight(16),
lineHeight: this.setHeight(32),
float: "left",
textAlign: "center",
marginBottom: this.setHeight(5)
};
this.onelineStyle = {
width: "100%",
paddingLeft: this.setWidth(12)
};
this.init();
},
props: {
format: {
type: Array,
default: function() {
return ["-", " ", ":"];
}
},
fn: {
type: Function,
required: true
},
value: {
type: String
},
w: {
type: [Number, String],
default: 2.57
},
h: {
type: [Number, String],
default: "auto"
},
unit: {
type: String,
default: "rem"
},
fontSize: {
type: [Number, String],
default: 0.14
},
color: {
type: String,
default: "#333"
},
border: {
type: String,
default: "0.01rem solid #cccccc"
},
background: {
type: String,
default: "#fff"
}
},
methods: {
setHeight(h, flag) {
if (flag) {
var height =
(this.unit == "rem"
? (this.fontSize * ((h * 0.01) / 0.14)).toFixed(2)
: Math.round(this.fontSize * (h / 14))) * 1;
} else {
var height =
(this.unit == "rem"
? (this.fontSize * ((h * 0.01) / 0.14)).toFixed(2)
: Math.round(this.fontSize * (h / 14))) + this.unit;
}
return height;
},
setWidth(w, flag) {
if (flag) {
var Width =
(this.unit == "rem"
? (this.w * ((w * 0.01) / 2.57)).toFixed(2)
: Math.round(this.w * (w / 257))) * 1;
} else {
var Width =
(this.unit == "rem"
? (this.w * ((w * 0.01) / 2.57)).toFixed(2)
: Math.round(this.w * (w / 257))) + this.unit;
}
return Width;
},
getMonthDayCount(year, month) {
var isLeapYear = false;
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
isLeapYear = true;
}
var dayCount = 0;
switch (month) {
case 0:
case 2:
case 4:
case 6:
case 7:
case 9:
case 11:
dayCount = 31;
break;
case 1:
if (isLeapYear) {
dayCount = 29;
} else {
dayCount = 28;
}
break;
default:
dayCount = 30;
break;
}
return dayCount;
},
setDate(year, month, hour, min, sec) {
if (year) {
this.curDate.setFullYear(year * 1);
}
if (month) {
this.curDate.setMonth(month - 1);
}
if (hour) {
this.curDate.setHours(hour * 1);
}
if (min) {
this.curDate.setMinutes(min * 1);
}
if (sec) {
this.curDate.setSeconds(sec * 1);
}
this.init();
},
init() {
var activeDate = new Date();
activeDate.setFullYear(this.curDate.getFullYear());
activeDate.setMonth(this.curDate.getMonth());
activeDate.setDate(this.curDate.getDate());
activeDate.setHours(this.curDate.getHours());
activeDate.setMinutes(this.curDate.getMinutes());
activeDate.setSeconds(this.curDate.getSeconds());
this.arr = [];
this.curYear = activeDate.getFullYear();
this.curMonth = activeDate.getMonth() + 1;
if (this.value) {
this.curShowHourIndex =
this.hourArr.indexOf(
activeDate.getHours() < 10
? "0" + activeDate.getHours()
: "" + activeDate.getHours()
) - 2;
this.curShowMinIndex =
this.minArr.indexOf(
activeDate.getMinutes() < 10
? "0" + activeDate.getMinutes()
: "" + activeDate.getMinutes()
) - 2;
this.curShowSecIndex =
this.secArr.indexOf(
activeDate.getSeconds() < 10
? "0" + activeDate.getSeconds()
: "" + activeDate.getSeconds()
) - 2;
}
var len = this.getMonthDayCount(
activeDate.getFullYear(),
activeDate.getMonth()
);
for (var i = 0; i < len; i++) {
this.arr.push(i + 1);
}
activeDate.setDate(0);
var lastMonthDayCount = this.getMonthDayCount(
activeDate.getFullYear(),
activeDate.getMonth()
);
var weekIndex = activeDate.getDay(); for (var i = 0; i < weekIndex + 1; i++) {
this.arr.unshift(lastMonthDayCount - i);
}
var len2 = this.arr.length;
for (var i = 0; i < 42 - len2; i++) {
this.arr.push(i + 1);
if (this.arr.length % 7 == 0) {
break;
}
}
},
clickFn(item) {
this.timeBoxShow = false;
this.curClickDay = item;
this.curClickMonth = this.curMonth;
this.curClickYear = this.curYear;
this.curIptDate =
this.curClickYear +
this.format[0] +
(this.curClickMonth < 10
? "0" + this.curClickMonth
: this.curClickMonth) +
this.format[0] +
(this.curClickDay < 10
? "0" + this.curClickDay
: this.curClickDay);
},
switchMonthClick(num, item) {
this.setDate(this.curYear, this.curMonth * 1 + num * 1);
setTimeout(() => {
this.clickFn(item);
}, 1);
},
setNowTime(flag) {
if (!flag) {
this.setTimeIpt(true);
}
var nowDate = new Date();
this.curClickYear = nowDate.getFullYear();
this.curClickMonth = nowDate.getMonth() + 1;
this.curClickDay = nowDate.getDate();
this.curIptDate =
this.curClickYear +
this.format[0] +
(this.curClickMonth < 10
? "0" + this.curClickMonth
: this.curClickMonth) +
this.format[0] +
(this.curClickDay < 10
? "0" + this.curClickDay
: this.curClickDay);
this.setDate(this.curClickYear, this.curClickMonth);
},
setIndex(evt, num) {
if (evt.wheelDelta) {
if (evt.wheelDelta > 0) {
if (num == 1) {
if (this.curShowHourIndex < this.hourArr.length - 3) {
this.curShowHourIndex += 1;
}
}
if (num == 2) {
if (this.curShowMinIndex < this.minArr.length - 3) {
this.curShowMinIndex += 1;
}
}
if (num == 3) {
if (this.curShowSecIndex < this.secArr.length - 3) {
this.curShowSecIndex += 1;
}
}
}
if (evt.wheelDelta < 0) {
if (num == 1) {
if (this.curShowHourIndex > -2) {
this.curShowHourIndex -= 1;
}
}
if (num == 2) {
if (this.curShowMinIndex > -2) {
this.curShowMinIndex -= 1;
}
}
if (num == 3) {
if (this.curShowSecIndex > -2) {
this.curShowSecIndex -= 1;
}
}
}
} else if (evt.detail) {
if (evt.detail > 0) {
if (num == 1) {
if (this.curShowHourIndex < this.hourArr.length - 3) {
this.curShowHourIndex += 1;
}
}
if (num == 2) {
if (this.curShowMinIndex < this.minArr.length - 3) {
this.curShowMinIndex += 1;
}
}
if (num == 3) {
if (this.curShowSecIndex < this.secArr.length - 3) {
this.curShowSecIndex += 1;
}
}
}
if (evt.detail < 0) {
if (num == 1) {
if (this.curShowHourIndex > -2) {
this.curShowHourIndex -= 1;
}
}
if (num == 2) {
if (this.curShowMinIndex > -2) {
this.curShowMinIndex -= 1;
}
}
if (num == 3) {
if (this.curShowSecIndex > -2) {
this.curShowSecIndex -= 1;
}
}
}
}
},
setTimeIpt(flag) {
this.timeBoxShow = true;
if (flag) {
var newDateTime = new Date();
this.curShowHourIndex = newDateTime.getHours() - 2;
this.curShowMinIndex = newDateTime.getMinutes() - 2;
this.curShowSecIndex = newDateTime.getSeconds() - 2;
}
this.curClickHour = this.hourArr[this.curShowHourIndex * 1 + 2];
this.curClickMin = this.minArr[this.curShowMinIndex * 1 + 2];
this.curClickSec = this.secArr[this.curShowSecIndex * 1 + 2];
this.curIptTime =
this.curClickHour +
this.format[2] +
this.curClickMin +
this.format[2] +
this.curClickSec;
},
emitDate() {
if (this.curIptDate && this.curIptTime) {
var valueFB =
this.curIptDate + this.format[1] + this.curIptTime;
this.fn(false);
this.$emit("input", valueFB);
}
},
setLeaveFn() {
console.log('LeaveAdd')
this.addEvent(document.body,'click',this.abc)
},
setEnterFn(){
console.log('enterRemove')
this.removeEvent(document.body,'click',this.abc)
},
abc(){
this.fn(false)
this.removeEvent(document.body,'click',this.abc)
},
addEvent(element, type, callback) {
if (element.addEventListener) {
element.addEventListener(type, callback, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, callback);
} else {
element["on" + type] = callback;
}
},
removeEvent(ele,str,fn) {
if(ele.removeEventListener){
ele.removeEventListener(str,fn);
}else if(ele.detachEvent){
ele.detachEvent("on"+str,fn);
}else{
ele["on"+str] = null;
}
}
},
watch: {
curShowHourIndex(newVal, oldVal) {
this.setTimeIpt();
},
curShowMinIndex(newVal, oldVal) {
this.setTimeIpt();
},
curShowSecIndex(newVal, oldVal) {
this.setTimeIpt();
},
},
mounted() {
this.$nextTick(function(){
this.setFont()
})
}
};
</script> <style scoped>
@import "https://at.alicdn.com/t/font_1043142_ucomoekqib.css";
span {
display: inline-block;
}
.clear-fix:after {
content: ".";
clear: both;
display: block;
height: 0;
overflow: hidden;
visibility: hidden;
}
div {
-moz-user-select: none;
-o-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
h1 {
flex-direction: column;
justify-content: flex-end;
}
</style>

日历组件源代码

--------------------------------------------------------------------------------------------------------------------------------------------------------

1.建一个vue文件,打出一个 <  然后按tab键  自动创建vue的模板  首先在script标签里写好name和props属性

props写成校验写法,这里我先定义了宽度w,高度h,单位rem,字体大小fontSize,字体颜色color,边框border,背景颜色background,这些都是基本的组件封装需要的props。如图:

这些props我都给它设置了一个默认值,当调用时不传其中的某个参数就是默认的值,传的话就是指定值;

然后定义交互的props:   v-model需要的props:value   v-model绑定的是显示在父组件某个容器里的值   显示的日期格式化单位format  控制显隐的点击函数fn。如图:

类型为Array或者Object的props设置默认值需要用回调函数返回。

-------------------------------------------------------------------------------------------------------------------------------------------------

2.props定义好后就开始写结构和样式,这里的样式不能写在style标签里面,需要用:style动态添加到标签上面。

先写data定义好几个基础的样式变量。如图:

接着在methods里面写两个设置宽度样式setWidth和高度样式setHeight的方法。如图:

setHeight与setWidth这两个方法的第一个参数为设置的像素大小(即设计图量的多少px就传多少,不带单位),第二个参数可选,传入true时返回一个纯数字,不传时返回一个带单位的值

这里我用了三元运算符,后面将会大量用到三元运算符(有嵌套用法)。this.w与this.fontSize即传入的props。

this.w*(w/257)与this.fontSize*(h/14)  这里的w / 257就是你要设置的宽相对于设计图的最外层大盒子的宽的占比。h/14就是设置的高相对于字体大小的占比。

这里不推荐用h / this.h 因为这里的组件高度需要自适应。

-------------------------------------------------------------------------------------------------------------------------------

3.template里面创建好基本结构 。如图:

接着在created里面初始化样式 。如图:

上面的代码可能就是最后的this.selectStyle有点不好理解

这里的cw就是用按钮的实际的宽度去减选中时的高度。因为选中的时候宽度需要写成和高度一样的才能成为一个圆形。

这时就需要设置选中的按钮左右的margin。因为cw 有可能是奇数,所以除以2的时候再四舍五入就不是平均值了。

所以margin-right就是用cw减去margin-left的值,避免盒子被挤到下一行。

-------------------------------------------------------------------------------------------------------

4.逻辑部分=>创建结构里需要显示的数据。如图:

在template里将数据渲染到标签上。如图:

接着在methods里面写一个初始化方法 init  :

这个方法用来渲染页面的日期数据,这里需要写一个获取指定年份和月份所对应的天数。

在methods里面再定义一个方法getMonthDayCount。如图:

渲染日历init方法,如图:

init方法写好后在created钩子函数里最后调用一下 this.init()

调用这个方法写完后页面的初始数据就算完成了,这时页面还是有问题的,有许多地方部分样式需要重写,

所以下面就是如何重写部分样式。如图:

class的动态添加也可以给指定的项添加对应的class类名。如图:

后面的一些样式微调就不一一截图了,当样式微调写好后,组件应该已经可以看到效果了,接下来写年份月份的切换效果,

这里需要再在methods里面定义一个setDate方法,用于设置当前时间对象,重新渲染日历。如图:

next:给切换的图标写上点击事件。如图:

这时鼠标点击切换年月份已经没有问题了。

然后给每一个日期写一个点击事件函数clickFn。如图:

------------------------------------------------------------------------未完待续------------------------------------------------------------------

Vue文件封装日历组件的相关教程结束。

《Vue文件封装日历组件.doc》

下载本文的Word格式文档,以方便收藏与打印。