選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

uni-calendar.vue 13KB

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