uni-calendar.vue 13 KB

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