luanqing-calendar.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. <template>
  2. <view class="uni-calendar" @touchstart="onCalendarTouchStart" @touchend="onCalendarTouchEnd">
  3. <view v-if="!insert && show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view>
  4. <view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}">
  5. <view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
  6. <view class="uni-calendar__header-btn-box" @click="close">
  7. <text class="uni-calendar__header-text uni-calendar--fixed-width">{{cancelText}}</text>
  8. </view>
  9. <view class="uni-calendar__header-btn-box" @click="confirm">
  10. <text class="uni-calendar__header-text uni-calendar--fixed-width">{{okText}}</text>
  11. </view>
  12. </view>
  13. <!-- 头部 START -->
  14. <view v-if="isShowHeader" class="uni-calendar__header">
  15. <view class="uni-calendar__header-btn-box" @click.stop="pre">
  16. <view class="uni-calendar__header-btn uni-calendar--left"></view>
  17. </view>
  18. <picker mode="date" :value="date" fields="month" @change="bindDateChange">
  19. <text class="uni-calendar__header-text">{{ (nowDate.year||'') +' / '+( nowDate.month||'')}}</text>
  20. </picker>
  21. <view class="uni-calendar__header-btn-box" @click.stop="next">
  22. <view class="uni-calendar__header-btn uni-calendar--right"></view>
  23. </view>
  24. <text class="uni-calendar__backtoday" @click="backtoday">{{todayText}}</text>
  25. </view>
  26. <!-- 头部 END -->
  27. <view class="uni-calendar__box">
  28. <view v-if="showMonth" class="uni-calendar__box-bg">
  29. <text class="uni-calendar__box-bg-text">{{nowDate && nowDate.month}}</text>
  30. </view>
  31. <!-- 头部 周一 到 周日 START -->
  32. <view class="uni-calendar__weeks luanqing-calendar-title-bar">
  33. <view class="uni-calendar__weeks-day">
  34. <text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
  35. </view>
  36. <view class="uni-calendar__weeks-day">
  37. <text class="uni-calendar__weeks-day-text">{{monText}}</text>
  38. </view>
  39. <view class="uni-calendar__weeks-day">
  40. <text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
  41. </view>
  42. <view class="uni-calendar__weeks-day">
  43. <text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
  44. </view>
  45. <view class="uni-calendar__weeks-day">
  46. <text class="uni-calendar__weeks-day-text">{{THUText}}</text>
  47. </view>
  48. <view class="uni-calendar__weeks-day">
  49. <text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
  50. </view>
  51. <view class="uni-calendar__weeks-day">
  52. <text class="uni-calendar__weeks-day-text">{{SATText}}</text>
  53. </view>
  54. </view>
  55. <!-- 头部 周一 到 周日 END -->
  56. <!-- 具体的日期日历 START -->
  57. <view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
  58. <view class="uni-calendar__weeks-item" v-for="(week,weeksIndex) in item" :key="weeksIndex">
  59. <!-- 多选日期 -->
  60. <multiple-selector-item v-if="mode ==='multiple'" :themeColor="themeColor" :isAbleSelectFutureDate="isAbleSelectFutureDate" class="uni-calendar-item--hook" :curYear="curYear" :curMonth="curMonth" :curDate="curDate" :weeks="week" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></multiple-selector-item>
  61. <!-- 单选日期 -->
  62. <single-selector-item v-else :themeColor="themeColor" :isAbleSelectFutureDate="isAbleSelectFutureDate" class="uni-calendar-item--hook" :curYear="curYear" :curMonth="curMonth" :curDate="curDate" :weeks="week" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></single-selector-item>
  63. </view>
  64. </view>
  65. <!-- 具体的日期日历 END -->
  66. </view>
  67. </view>
  68. </view>
  69. </template>
  70. <script>
  71. import Calendar from './util.js';
  72. import multipleSelectorItem from './luanqing-multiple-selector-item.vue'
  73. import singleSelectorItem from './luanqing-single-selector-item.vue'
  74. import {
  75. initVueI18n
  76. } from '@dcloudio/uni-i18n'
  77. import messages from './i18n/index.js'
  78. const { t } = initVueI18n(messages)
  79. /**
  80. * Calendar 日历
  81. * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
  82. * @tutorial https://ext.dcloud.net.cn/plugin?id=56
  83. * @property {String} date 自定义当前时间,默认为今天
  84. * @property {Boolean} lunar 显示农历
  85. * @property {String} startDate 日期选择范围-开始日期
  86. * @property {String} endDate 日期选择范围-结束日期
  87. * @property {Boolean} range 范围选择
  88. * @property {Boolean} insert = [true|false] 插入模式,默认为false
  89. * @value true 弹窗模式
  90. * @value false 插入模式
  91. * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
  92. * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
  93. * @property {Boolean} showMonth 是否选择月份为背景
  94. * @event {Function} change 日期改变,`insert :ture` 时生效
  95. * @event {Function} confirm 确认选择`insert :false` 时生效
  96. * @event {Function} monthSwitch 切换月份时触发
  97. * @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
  98. */
  99. export default {
  100. components: {
  101. multipleSelectorItem,
  102. singleSelectorItem
  103. },
  104. emits:['close','confirm','change','monthSwitch'],
  105. props: {
  106. themeColor:{
  107. type: String,
  108. default: '#E00300'
  109. },
  110. isAbleSelectFutureDate:{
  111. type: Boolean,
  112. default: true
  113. },
  114. isShowHeader:{
  115. type: Boolean,
  116. default: false
  117. },
  118. date: {
  119. type: String,
  120. default: ''
  121. },
  122. mode: {
  123. type: String,
  124. default: 'multiple'
  125. },
  126. selected: {
  127. type: Array,
  128. default () {
  129. return []
  130. }
  131. },
  132. lunar: {
  133. type: Boolean,
  134. default: false
  135. },
  136. startDate: {
  137. type: String,
  138. default: ''
  139. },
  140. endDate: {
  141. type: String,
  142. default: ''
  143. },
  144. range: {
  145. type: Boolean,
  146. default: false
  147. },
  148. insert: {
  149. type: Boolean,
  150. default: true
  151. },
  152. showMonth: {
  153. type: Boolean,
  154. default: true
  155. },
  156. clearDate: {
  157. type: Boolean,
  158. default: true
  159. },
  160. },
  161. data() {
  162. return {
  163. touchStartX:0,
  164. touchStartY:0,
  165. todayDate:1,
  166. todayMonth:1,
  167. todayYear:1970,
  168. show: false,
  169. weeks: [],
  170. calendar: {},
  171. nowDate: '',
  172. aniMaskShow: false,
  173. curYear:2022,
  174. curMonth:7,
  175. curDate:12,
  176. recordUnit: 0,
  177. }
  178. },
  179. computed:{
  180. /**
  181. * for i18n
  182. */
  183. okText() {
  184. return t("uni-calender.ok")
  185. },
  186. cancelText() {
  187. return t("uni-calender.cancel")
  188. },
  189. todayText() {
  190. return t("uni-calender.today")
  191. },
  192. monText() {
  193. return t("uni-calender.MON")
  194. },
  195. TUEText() {
  196. return t("uni-calender.TUE")
  197. },
  198. WEDText() {
  199. return t("uni-calender.WED")
  200. },
  201. THUText() {
  202. return t("uni-calender.THU")
  203. },
  204. FRIText() {
  205. return t("uni-calender.FRI")
  206. },
  207. SATText() {
  208. return t("uni-calender.SAT")
  209. },
  210. SUNText() {
  211. return t("uni-calender.SUN")
  212. },
  213. },
  214. watch: {
  215. date(newVal) {
  216. this.init(newVal)
  217. },
  218. startDate(val){
  219. this.cale.resetSatrtDate(val);
  220. this.cale.setDate(this.nowDate.fullDate)
  221. this.weeks = this.cale.weeks
  222. },
  223. endDate(val){
  224. this.cale.resetEndDate(val)
  225. this.cale.setDate(this.nowDate.fullDate)
  226. this.weeks = this.cale.weeks
  227. },
  228. selected(newVal) {
  229. this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
  230. this.weeks = this.cale.weeks
  231. }
  232. },
  233. created() {
  234. // 获取日历方法实例
  235. this.cale = new Calendar({
  236. // date: new Date(),
  237. selected: this.selected,
  238. startDate: this.startDate,
  239. endDate: this.endDate,
  240. range: this.range,
  241. })
  242. // 选中某一天
  243. // this.cale.setDate(this.date)
  244. this.init(this.date)
  245. // this.setDay
  246. if(!this.$props.isAbleSelectFutureDate){
  247. let date = new Date();
  248. this.curMonth = date.getMonth() + 1;
  249. this.curYear = date.getFullYear();
  250. this.curDate = date.getDate();
  251. }
  252. },
  253. methods: {
  254. onCalendarTouchStart(e){
  255. //console.log("触摸状态:",e);
  256. this.touchStartX = e.changedTouches[0].pageX;
  257. this.touchStartY = e.changedTouches[0].pageY;
  258. },
  259. onCalendarTouchEnd(e){
  260. //console.log("触摸状态2:",e);
  261. let tempX = e.changedTouches[0].pageX;
  262. let tempY = e.changedTouches[0].pageY;
  263. // 1. X轴移动距离大于等于 100
  264. // 2. Y轴偏移不能大于 30
  265. // ① <----> ② <-----> ③
  266. // Y轴条件,不能偏离超过80
  267. let diff = Math.abs(tempY - this.touchStartY);
  268. if(diff <= 80){
  269. //console.log("Y轴偏离值:",diff);
  270. // 左滑
  271. if(this.touchStartX - tempX >= 70) {
  272. //console.log("左滑:",this.touchStartX - tempX);
  273. this.$emit('scrollLeft');
  274. }
  275. // 右滑
  276. else if(tempX - this.touchStartX >= 70){
  277. //console.log("右滑:",tempX - this.touchStartX);
  278. this.$emit('scrollRight');
  279. }
  280. }else{
  281. //console.log("Y轴偏离了:",diff);
  282. }
  283. },
  284. // 取消穿透
  285. clean() {},
  286. bindDateChange(e) {
  287. const value = e.detail.value + '-1'
  288. //console.log(this.cale.getDate(value));
  289. this.init(value)
  290. },
  291. /**
  292. * 初始化日期显示
  293. * @param {Object} date
  294. */
  295. init(date) {
  296. this.cale.setDate(date);
  297. this.weeks = this.cale.weeks;
  298. this.nowDate = this.calendar = this.cale.getInfo(date);
  299. this.todayDate = this.nowDate.lunar.cDay;
  300. this.todayMonth = this.nowDate.lunar.cMonth;
  301. this.todayYear = this.nowDate.lunar.cYear;
  302. },
  303. /**
  304. * 打开日历弹窗
  305. */
  306. open() {
  307. // 弹窗模式并且清理数据
  308. if (this.clearDate && !this.insert) {
  309. this.cale.cleanMultipleStatus()
  310. this.init(this.date)
  311. }
  312. this.show = true
  313. this.$nextTick(() => {
  314. setTimeout(() => {
  315. this.aniMaskShow = true
  316. }, 50)
  317. })
  318. },
  319. /**
  320. * 关闭日历弹窗
  321. */
  322. close() {
  323. this.aniMaskShow = false
  324. this.$nextTick(() => {
  325. setTimeout(() => {
  326. this.show = false
  327. this.$emit('close')
  328. }, 300)
  329. })
  330. },
  331. /**
  332. * 确认按钮
  333. */
  334. confirm() {
  335. this.setEmit('confirm')
  336. this.close()
  337. },
  338. /**
  339. * 变化触发
  340. */
  341. change() {
  342. if (!this.insert) return;
  343. this.setEmit('change');
  344. },
  345. /**
  346. * 选择月份触发
  347. */
  348. monthSwitch() {
  349. let {
  350. year,
  351. month
  352. } = this.nowDate
  353. this.$emit('monthSwitch', {
  354. year,
  355. month: Number(month)
  356. })
  357. },
  358. /**
  359. * 派发事件
  360. * @param {Object} name
  361. */
  362. setEmit(name) {
  363. let {
  364. year,
  365. month,
  366. date,
  367. fullDate,
  368. // lunar,
  369. // extraInfo,
  370. recordData
  371. } = this.calendar
  372. this.$emit(name, {
  373. range: this.cale.multipleStatus,
  374. recordData,
  375. year,
  376. month,
  377. date,
  378. fulldate: fullDate,
  379. // lunar,
  380. // extraInfo: extraInfo || {}
  381. })
  382. },
  383. /**
  384. * 选择天触发
  385. * @param {Object} weeks
  386. */
  387. choiceDate(weeks) {
  388. if (weeks.disable) return
  389. weeks.checked = true;
  390. this.calendar = weeks;
  391. // 设置多选
  392. this.cale.setMultiple(this.calendar.fullDate);
  393. this.weeks = this.cale.weeks;
  394. // this.weeks['1'][3].checked = true;
  395. // this.weeks.forEach(item=>{
  396. // item.checked = true;
  397. // })
  398. //console.log("打印所有数据:",this.weeks);
  399. this.change(weeks);
  400. },
  401. /**
  402. * 回到今天
  403. */
  404. backtoday() {
  405. //console.log(this.cale.getDate(new Date()).fullDate);
  406. let date = this.cale.getDate(new Date()).fullDate
  407. // this.cale.setDate(date)
  408. this.init(date)
  409. this.change()
  410. },
  411. /**
  412. * 上个月
  413. */
  414. pre() {
  415. const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
  416. this.setDate(preDate)
  417. this.monthSwitch()
  418. },
  419. /**
  420. * 下个月
  421. */
  422. next() {
  423. const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
  424. this.setDate(nextDate)
  425. this.monthSwitch()
  426. },
  427. /**
  428. * 设置日期
  429. * @param {Object} date
  430. */
  431. setDate(date) {
  432. //console.log("设置日期:",date);
  433. this.cale.setDate(date)
  434. this.weeks = this.cale.weeks
  435. this.nowDate = this.cale.getInfo(date)
  436. }
  437. }
  438. }
  439. </script>
  440. <style lang="scss" scoped>
  441. .uni-calendar {
  442. /* #ifndef APP-NVUE */
  443. display: flex;
  444. /* #endif */
  445. flex-direction: column;
  446. }
  447. .uni-calendar__mask {
  448. position: fixed;
  449. bottom: 0;
  450. top: 0;
  451. left: 0;
  452. right: 0;
  453. background-color: $uni-bg-color-mask;
  454. transition-property: opacity;
  455. transition-duration: 0.3s;
  456. opacity: 0;
  457. /* #ifndef APP-NVUE */
  458. z-index: 99;
  459. /* #endif */
  460. }
  461. .uni-calendar--mask-show {
  462. opacity: 1
  463. }
  464. .uni-calendar--fixed {
  465. position: fixed;
  466. /* #ifdef APP-NVUE */
  467. bottom: 0;
  468. /* #endif */
  469. left: 0;
  470. right: 0;
  471. transition-property: transform;
  472. transition-duration: 0.3s;
  473. transform: translateY(460px);
  474. /* #ifndef APP-NVUE */
  475. bottom: calc(var(--window-bottom));
  476. z-index: 99;
  477. /* #endif */
  478. }
  479. .uni-calendar--ani-show {
  480. transform: translateY(0);
  481. }
  482. .uni-calendar__content {
  483. background-color: #fff;
  484. }
  485. .uni-calendar__header {
  486. position: relative;
  487. /* #ifndef APP-NVUE */
  488. display: flex;
  489. /* #endif */
  490. flex-direction: row;
  491. justify-content: center;
  492. align-items: center;
  493. height: 50px;
  494. border-bottom-color: $uni-border-color;
  495. border-bottom-style: solid;
  496. border-bottom-width: 1px;
  497. }
  498. .uni-calendar--fixed-top {
  499. /* #ifndef APP-NVUE */
  500. display: flex;
  501. /* #endif */
  502. flex-direction: row;
  503. justify-content: space-between;
  504. border-top-color: $uni-border-color;
  505. border-top-style: solid;
  506. border-top-width: 1px;
  507. }
  508. .uni-calendar--fixed-width {
  509. width: 50px;
  510. // padding: 0 15px;
  511. }
  512. .uni-calendar__backtoday {
  513. position: absolute;
  514. right: 0;
  515. top: 25rpx;
  516. padding: 0 5px;
  517. padding-left: 10px;
  518. height: 25px;
  519. line-height: 25px;
  520. font-size: 12px;
  521. border-top-left-radius: 25px;
  522. border-bottom-left-radius: 25px;
  523. color: $uni-text-color;
  524. background-color: $uni-bg-color-hover;
  525. }
  526. .uni-calendar__header-text {
  527. text-align: center;
  528. width: 100px;
  529. font-size: $uni-font-size-base;
  530. color: $uni-text-color;
  531. }
  532. .uni-calendar__header-btn-box {
  533. /* #ifndef APP-NVUE */
  534. display: flex;
  535. /* #endif */
  536. flex-direction: row;
  537. align-items: center;
  538. justify-content: center;
  539. width: 50px;
  540. height: 50px;
  541. }
  542. .uni-calendar__header-btn {
  543. width: 10px;
  544. height: 10px;
  545. border-left-color: $uni-text-color-placeholder;
  546. border-left-style: solid;
  547. border-left-width: 2px;
  548. border-top-color: $uni-color-subtitle;
  549. border-top-style: solid;
  550. border-top-width: 2px;
  551. }
  552. .uni-calendar--left {
  553. transform: rotate(-45deg);
  554. }
  555. .uni-calendar--right {
  556. transform: rotate(135deg);
  557. }
  558. .luanqing-calendar-title-bar{
  559. background-color: #F4F6F8;
  560. }
  561. .uni-calendar__weeks {
  562. position: relative;
  563. /* #ifndef APP-NVUE */
  564. display: flex;
  565. /* #endif */
  566. flex-direction: row;
  567. }
  568. .uni-calendar__weeks-item {
  569. flex: 1;
  570. }
  571. .uni-calendar__weeks-day {
  572. flex: 1;
  573. /* #ifndef APP-NVUE */
  574. display: flex;
  575. /* #endif */
  576. flex-direction: column;
  577. justify-content: center;
  578. align-items: center;
  579. height: 48rpx;
  580. border-bottom-color: #F5F5F5;
  581. border-bottom-style: solid;
  582. border-bottom-width: 1px;
  583. }
  584. .uni-calendar__weeks-day-text {
  585. font-size: 20rpx;
  586. font-weight: 400;
  587. color: #666666;
  588. }
  589. .uni-calendar__box {
  590. position: relative;
  591. }
  592. .uni-calendar__box-bg {
  593. /* #ifndef APP-NVUE */
  594. display: flex;
  595. /* #endif */
  596. justify-content: center;
  597. align-items: center;
  598. position: absolute;
  599. top: 0;
  600. left: 0;
  601. right: 0;
  602. bottom: 0;
  603. }
  604. .uni-calendar__box-bg-text {
  605. font-size: 200px;
  606. font-weight: bold;
  607. color: $uni-text-color-grey;
  608. opacity: 0.1;
  609. text-align: center;
  610. /* #ifndef APP-NVUE */
  611. line-height: 1;
  612. /* #endif */
  613. }
  614. </style>