>웹 프론트엔드 >JS 튜토리얼 >Vue의 날짜 선택기 샘플 코드

Vue의 날짜 선택기 샘플 코드

亚连
亚连원래의
2018-06-09 11:10:331786검색

이 글은 주로 Vue를 활용한 datepicker 작성 예시를 소개하고 있으니 참고용으로 올려보겠습니다.

머리말

플러그인을 작성하는 것은 매우 흥미롭고 훈련되는 일입니다. 그 과정에서 많은 세부 사항을 발견할 수 있기 때문입니다. 프론트엔드 개발 과정에서 jQuery는 의심할 여지 없이 중요한 이정표입니다. 이 훌륭한 프로젝트를 중심으로 직접 사용할 수 있는 우수한 플러그인이 많이 등장하여 개발자의 시간을 크게 절약해 줍니다. jQuery의 가장 중요한 역할은 크로스 브라우저입니다. 현재 브라우저 시장은 완벽하지 않지만 데이터 기반 뷰라는 아이디어가 많이 대중화되고 있습니다. 저는 개인적으로 Vue.js를 선호하므로 Vue.js를 사용하여 컴포넌트를 작성해 보고 싶습니다.

npm에 게시하기 위해 프로젝트 주소 이름을 변경했지만 내부 코드는 변경되지 않았으며 이전보다 사용이 편리해졌습니다.

GitHub 주소: 여기

기능 및 기대

이 날짜 선택기는 현재 몇 가지 일반적인 기능만 구현합니다:

  1. 시간 선택(약간 중복됨)

  2. 최대/최소 시간 제한

  3. 중국어/ 영어 전환 (실제로는 주, 월만 전환하면 됩니다)

  4. .vue 형식으로 사용하거나 브라우저 환경에서 직접 사용할 수 있습니다

  5. 더 이상은 없습니다. . .

디렉터리 구조

모든 작업의 ​​첫 번째 단계는 여전히 프로젝트를 생성하는 것입니다. 이는 단일 구성 요소일 뿐이며 구조는 복잡하지 않습니다. Datepicker.vue는 가장 중요한 구성 요소 파일이며, dist는 webpack의 출력 폴더입니다. , index.js는 webpack입니다. 패키징 항목 파일과 마지막으로 webpack 구성 파일은 라이브러리 파일을 패키징하는 데 사용됩니다. 따라서 프로젝트 구조는 다음과 같습니다.

.
├── Datepicker.vue
├── LICENSE
├── README.md
├── dist
│ └── vue-datepicker.js
├── index.js
├── package.json
└── webpack.config.js

Datepicker.vue로 시작

.vue에 Vue 구성 요소를 작성하는 것은 각 Vue 파일에 템플릿, 스크립트, 스타일의 세 부분이 포함되어 있습니다. 템플릿의 조각 인스턴스가 되므로 가장 바깥쪽 레이어는 먼저 전체 구성 요소의 루트 요소로 사용되는 p 레이어로 덮여 있습니다. 날짜 선택기는 일반적으로 날짜를 표시하는 입력 상자와 날짜를 선택하는 패널의 두 부분으로 구성됩니다. 입력하면 모바일 단말기의 키보드가 자동으로 호출되므로 입력을 사용하지 않고 p를 직접 사용했습니다. 시뮬레이션을 클릭하면 이벤트가 패널의 가시성을 결정합니다. Value는 최종 결과이고 상위 컴포넌트와 통신해야 하므로 값을 prop으로 작성하고 상위 컴포넌트에서는 value.sync="xxx"를 사용합니다. datepicker의 값은 xxx에 양방향으로 바인딩됩니다. 상위 구성 요소.

<template>
 <p class="date-picker">
  <p class="input" v-text="value" @click="panelState = !panelState">
 </p>
 <p class="date-panel" v-show="panelState">
 </p>
</template>
<scrip>
 export default {
  data () {
   return {
    panelState: false //初始值,默认panel关闭
   }
  },
  props: {
   value: String
  }
 }
</script>

렌더링 날짜 목록

한 달은 최소 28일입니다. 일요일이 처음에 정렬되면 최소 4줄이 필요하지만(1번째는 일요일이 됩니다) 매월 일수는 는 30, 31이 가장 많고, 1일이 꼭 일요일일 필요는 없으므로 가장 일반적인 상황에 맞게 간단하게 디자인했으며, 당월 날짜의 채워지지 않은 부분은 총 6줄로 채워 넣었습니다. 이전 달 또는 다음 달의 날짜를 선택하면 계산이 더 쉬워지고 이 기간 동안 패널 높이가 변경되지 않습니다. 날짜 목록의 배열은 동적으로 계산되어야 합니다. Vue는 계산된 속성을 제공하므로 날짜 목록 dateList가 계산된 속성으로 직접 작성됩니다. 내 방법은 날짜 목록을 길이가 42인 배열로 고정한 다음 이번 달, 지난 달, 다음 달의 날짜를 순서대로 채우는 것입니다.

computed: {
 dateList () {
  //获取当月的天数
  let currentMonthLength = new Date(this.tmpMonth, this.tmpMonth + 1, 0).getDate()
  //先将当月的日期塞入dateList
  let dateList = Array.from({length: currentMonthLength}, (val, index) => {
   return {
    currentMonth: true,
    value: index + 1
   }
  })
  //获取当月1号的星期是为了确定在1号前需要插多少天
  let startDay = new Date(this.year, this.tmpMonth, 1).getDay()
  //确认上个月一共多少天
  let previousMongthLength = new Date(this.year, this.tmpMonth, 0).getDate()
 }
 //在1号前插入上个月日期
 for(let i = 0, len = startDay; i < len; i++){
  dateList = [{previousMonth: true, value: previousMongthLength - i}].concat(dateList)
 }
 //补全剩余位置
 for(let i = 0, item = 1; i < 42; i++, item++){
  dateList[dateList.length] = {nextMonth: true, value: i}
 }
 return dateList
}

여기에서는 Array.from을 사용하여 배열을 초기화하고 Array Like를 전달한 후 이를 배열로 변환합니다. 문자열을 연결할 때 arr[arr.length] 및 [{}].concat(arr)을 사용합니다. , JsTips를 통해 이 방법이 더 나은 성능을 발휘한다는 것을 배웠기 때문에 관련 링크는 기사 마지막 부분에 게시됩니다.
이런 식으로 날짜 목록이 구성되고 템플릿에서 v-for 루프를 사용하여 렌더링됩니다.

<ul class="date-list">
 <li v-for="item in dateList"
  v-text="item.value" 
  :class="{preMonth: item.previousMonth, nextMonth: item.nextMonth,
   selected: date === item.value && month === tmpMonth && item.currentMonth, invalid: validateDate(item)}"
  @click="selectDate(item)">
 </li>
</ul>

스타일을 직접 사용하고 원하는 대로 작성할 수 있습니다. 주기 날짜는 지난 달이나 이번 달에 나타날 수 있다는 점에 유의해야 합니다. 다른 기능에 대한 판단 조건을 제공하기 위해 previuosMonth, currentMonth 및 nextMonth를 통해 각각 표시했습니다.
연도와 월 목록은 비슷합니다. 연도 목록의 초기 값을 데이터에 직접 썼고, 월과의 일관성을 유지하기 위해 v-for를 통해 매번 12개가 표시됩니다. 세우다.

data () {
 return {
  yearList: Array.from({length: 12}, (value, index) => new Date().getFullYear() + index)
 }
}

날짜 선택 기능

선택 순서는 연 -> 월 -> 일이므로 상태 변수를 통해 패널에 표시되는 내용을 제어하고 적절한 기능을 바인딩하여 표시 상태를 전환할 수 있습니다.

<p>
 <p class="type-year" v-show="panelType === &#39;year&#39;">
  <ul class="year-list">
   <li v-for="item in yearList"
    v-text="item"
    :class="{selected: item === tmpYear, invalid: validateYear(item)}" 
    @click="selectYear(item)"
   >
   </li>
  </ul>
 </p>
 <p class="type-month" v-show="panelType === &#39;month&#39;">
  <ul class="month-list">
   <li v-for="item in monthList"
    v-text="item | month language"
    :class="{selected: $index === tmpMonth && year === tmpYear, invalid: validateMonth($index)}" 
    @click="selectMonth($index)"
   >
   </li>
  </ul>
 </p>
 <p class="type-date" v-show="panelType === &#39;date&#39;">
  <ul class="date-list">
   <li v-for="item in dateList"
    v-text="item.value" 
    track-by="$index" 
    :class="{preMonth: item.previousMonth, nextMonth: item.nextMonth,
     selected: date === item.value && month === tmpMonth && item.currentMonth, invalid: validateDate(item)}"
    @click="selectDate(item)">
   </li>
  </ul>
 </p>
</p>

날짜 선택 방법에 대해서는 자세히 설명하지 않겠습니다. selectYear 및 selectMonth에서 연도 및 월 변수에 값을 할당한 후 각각 panelType을 다음 단계로 푸시하면 날짜 선택 기능이 구현됩니다.

그러나 날짜를 선택하기 전에 현재 연도와 월의 실제 값이 변경되는 것을 원하지 않을 수 있으므로 이러한 방법에서는 먼저 선택한 값을 임시 변수에 할당한 다음 모든 값을 한 번에 할당할 수 있습니다. seletDate가 호출될 때.

selectMonth (month) {
 if(this.validateMonth(month)){
  return
 }else{
  //临时变量
  this.tmpMonth = month
  //切换panel状态
  this.panelType = &#39;date&#39;
 }
},
selectDate (date) {
 //validate logic above...
 //一次性全部赋值
 this.year = tmpYear
 this.month = tmpMonth
 this.date = date.value
 this.value = `${this.tmpYear}-${(&#39;0&#39; + (this.month + 1)).slice(-2)}-${(&#39;0&#39; + this.date).slice(-2)}`
 //选择完日期后,panel自动隐藏
 this.panelState = false
}

최대/최소 시간 제한

최대/최소 값은 상위 구성 요소에서 전달되어야 하므로 props를 사용해야 합니다. 또한 이 값은 문자열이거나 변수일 수 있습니다(예: 날짜 선택기가 두 개 있습니다. 동시에 두 번째 날짜는 첫 번째 날짜보다 클 수 없으므로(이 논리) 값을 전달하려면 동적으로 바인딩을 사용해야 합니다.

<datepicker :value.sync="start"></datepicker>
<!-- 现在min的值会随着start的变化而变化 -->
<datepicker :value.sync="end" :min="start" ></datepicker>

增加了限制条件,对于不合法的日期,其按钮应该变为置灰状态,我用了比较时间戳的方式来判断日期是否合法,因为就算当前panel中的日期是跨年或是跨月的,通过日期构造函数创建时都会帮你转换成对应的合法值,省去很多判断的麻烦:

new Date(2015, 0, 0).getTime() === new Date(2014, 11, 31).getTime() //true
new Date(2015, 12, 0).getTime() === new Date(2016, 0, 0).getTime() //true

因此验证日期是否合法的函数是这样的:

validateDate (date) {
 let mon = this.tmpMonth
 if(date.previousMonth){
  mon -= 1
 }else if(date.nextMonth){
  mon += 1
 }
 if(new Date(this.tmpYear, mon, date.value).getTime() >= new Date(this.minYear, this.minMonth - 1, this.minDate).getTime()
  && new Date(this.tmpYear, mon, date.value).getTime() <= new Date(this.maxYear, this.maxMonth - 1, this.maxDate).getTime()){
  return false
 }
 return true
}

动态计算位置

当页面右侧有足够的空间显示时,datepicker的panel会定位为相对于父元素left: 0的位置,如果没有足够的空间,则应该置于right: 0的位置,这一点可以通过Vue提供的动态样式和样式对象来实现(动态class和动态style其实只是动态props的特例),而计算位置的时刻,我放在了组件声明周期的ready周期中,因为这时组件已经插入到DOM树中,可以获取style进行动态计算:

ready () {
 if(this.$el.parentNode.offsetWidth + this.$el.parentNode.offsetLeft - this.$el.offsetLeft <= 300){
  this.coordinates = {right: &#39;0&#39;, top: `${window.getComputedStyle(this.$el.children[0]).offsetHeight + 4}px`}
 }else{
  this.coordinates = {left: &#39;0&#39;, top: `${window.getComputedStyle(this.$el.children[0]).offsetHeight + 4}px`}
 }
}
<!-- template中对应的动态style -->
<p :style="coordinates"></p>

为了panel的显隐可以平滑过渡,可以使用transition做过渡动画,这里我简单地通过一个0.2秒的透明度过渡让显隐更平滑。

<p :style="this.coordinates" v-show="panelState" transition="toggle"></p>
//less syntax
.toggle{
 &-transition{
  transition: all ease .2s;
 }
 &-enter, &-leave{
  opacity: 0;
 }
}

中英文切换

这里其实也很简单,这种多语言切换实质就是一个key根据不同的type而输出不同的value,所以使用filter可以很容易的实现它!比如渲染星期的列表:

<ul class="weeks">
  <li v-for="item in weekList" v-text="item | week language"></li>
 </ul>
 
filters : {
 week (item, lang){
  switch (lang) {
   case &#39;en&#39;:
    return {0: &#39;Su&#39;, 1: &#39;Mo&#39;, 2: &#39;Tu&#39;, 3: &#39;We&#39;, 4: &#39;Th&#39;, 5: &#39;Fr&#39;, 6: &#39;Sa&#39;}[item]
   case &#39;ch&#39;:
    return {0: &#39;日&#39;, 1: &#39;一&#39;, 2: &#39;二&#39;, 3: &#39;三&#39;, 4: &#39;四&#39;, 5: &#39;五&#39;, 6: &#39;六&#39;}[item]
   default:
    return item
  }
 }
}

多种使用方式

对于一个Vue组件,如果是使用webpack + vue-loader的.vue单文件写法,我希望这样使用:

//App.vue
<script>
 import datepicker from &#39;path/to/datepicker.vue&#39;
 export default {
  components: { datepicker}
 }
</script>

如果是直接在浏览器中使用,那么我希望datepicker这个组件是暴露在全局下的,可以这么使用:

//index.html
<html>
 <script src="path/to/vue.js"></script>
 <script src="path/to/datepicker.js"></script>
 <body>
  <p id="app"></p>
  <script>
   new Vue({
    el: &#39;#app&#39;,
    components: { datepicker }
   })
  </script>
 </body>
</html>

这里我选择了webpack作为打包工具,使用webpack的output.library和output.linraryTarget这两个属性就可以把你的bundle文件作为库文件打包。library定义了库的名字,libraryTarget定义了你想要打包的格式,具体可以看文档。我希望自己的库可以通过datepicker加载到,并且打包成umd格式,因此我的webpack.config.js是这样的:

module.exports = {
 entry: &#39;./index.js&#39;,
 output: {
  path: &#39;./dist&#39;,
  library: &#39;datepicker&#39;,
  filename: &#39;vue-datepicker.js&#39;,
  libraryTarget: &#39;umd&#39;
 },
 module: {
  loaders: [
   {test: /\.vue$/, loaders: [&#39;vue&#39;]},
   {test: /\.js$/, exclude: /node_modules/, loaders: [&#39;babel&#39;]}
  ]
 }
}

打包完成的模块就是一个umd格式的模块啦,可以在浏览器中直接使用,也可以配合require.js等模块加载器使用!

适配 Vue 2.x

Vue 2.0已经发布有段时间了,现在把之前的组件适配到Vue 2.0。迁移过程还是很顺利的,核心API改动不大,可以借助vue-migration-helper来找出废弃的API再逐步修改。这里只列举一些我需要修改的API。

filter

2.0中的filter只能在mustache绑定中使用,如果想在指令式绑定中绑定过滤后的值,可以选择计算属性。我在月份和星期的显示中使用到了过滤器来过滤语言类型,但我之前是在指令式绑定中使用的filter,所以需要如下修改,:

//修改前
<p class="month-box" @click="chType(&#39;month&#39;)" v-text="tmpMonth + 1 | month language"></p>
//修改后,filter传参的方式也变了,变成了函数调用的风格
<p class="month-box" @click="chType(&#39;month&#39;)">{{tmpMonth + 1 | month(language)}}</p>

移除$index和$key

这两个属性不会在v-for中被自动创建了,如需使用,要在v-for中自行声明:

<li v-for="item in monthList" @click="selectMonth($index)"></li>
//
<li v-for="(item, index) in monthList" @click="selectMonth(index)"></li>

ready 生命周期移除

ready从生命周期钩子中移除了,迁移方法很简单,使用mounted和this.$nextTick来替换。

prop.sync弃用

prop的sync弃用了,迁移方案是使用自定义事件,而且Datepicker这种input类型组件,可以使用表单输入组件的自定义事件作为替换方案。自定义组件也可以使用v-model指令了,但是必须满足两个条件:

  1. 接收一个value的prop

  2. 值发生变化时,触发一个input事件,传入新值。

所以Datepicker的使用方式也不是f2711184ba1ee7ec6aa7b9dc5d2771bd736fc7563f791c0444464598257dec0b了,而是bad5218a9f314e0b4262fb54ca286ace736fc7563f791c0444464598257dec0b。组件自身向父级传值的方式也不一样了:

//1.x版本,设置了value的值会同步到父级
this.value = `${this.tmpYear}-${(&#39;0&#39; + (this.month + 1)).slice(-2)}-${(&#39;0&#39; + this.date).slice(-2)}`
//2.x版本,需要自己触发input事件,将新值作为参数传递回去
let value = `${this.tmpYear}-${(&#39;0&#39; + (this.month + 1)).slice(-2)}-${(&#39;0&#39; + this.date).slice(-2)}`
this.$emit(&#39;input&#39;, value)

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

详细解读Vue.js中的组件使用方法以及作用?

AngularJS1.x应用迁移至React(详细教程)

在vue 2.0中如何实现购物车小球抛物线

위 내용은 Vue의 날짜 선택기 샘플 코드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.