uv-calendars.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. <template>
  2. <view class="uv-calendar">
  3. <view class="uv-calendar__content" v-if="insert">
  4. <calendar-body
  5. :date="date"
  6. :nowDate="nowDate"
  7. :weeks="weeks"
  8. :calendar="calendar"
  9. :selected="selected"
  10. :lunar="lunar"
  11. :showMonth="showMonth"
  12. :color="color"
  13. :startText="startText"
  14. :endText="endText"
  15. :range="range"
  16. :multiple="multiple"
  17. :allowSameDay="allowSameDay"
  18. @bindDateChange="bindDateChange"
  19. @pre="pre"
  20. @next="next"
  21. @backToday="backToday"
  22. @choiceDate="choiceDate"
  23. ></calendar-body>
  24. </view>
  25. <uv-popup ref="popup" mode="bottom" v-else :round="round" z-index="998" :close-on-click-overlay="closeOnClickOverlay" @maskClick="maskClick">
  26. <view style="min-height: 100px;">
  27. <uv-toolbar
  28. :show="true"
  29. :cancelColor="cancelColor"
  30. :confirmColor="getConfirmColor"
  31. :cancelText="cancelText"
  32. :confirmText="confirmText"
  33. :title="title"
  34. @cancel="close"
  35. @confirm="confirm"></uv-toolbar>
  36. <view class="line"></view>
  37. <calendar-body
  38. :nowDate="nowDate"
  39. :weeks="weeks"
  40. :calendar="calendar"
  41. :selected="selected"
  42. :lunar="lunar"
  43. :showMonth="showMonth"
  44. :color="color"
  45. :startText="startText"
  46. :endText="endText"
  47. :range="range"
  48. :multiple="multiple"
  49. :allowSameDay="allowSameDay"
  50. @bindDateChange="bindDateChange"
  51. @pre="pre"
  52. @next="next"
  53. @backToday="backToday"
  54. @choiceDate="choiceDate"
  55. ></calendar-body>
  56. </view>
  57. </uv-popup>
  58. </view>
  59. </template>
  60. <script>
  61. /**
  62. * Calendar 日历
  63. * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
  64. * @tutorial https://ext.dcloud.net.cn/plugin?name=uv-calendar
  65. * @property {String} date 自定义当前时间,默认为今天
  66. * @property {Boolean} lunar 显示农历
  67. * @property {String} startDate 日期选择范围-开始日期
  68. * @property {String} endDate 日期选择范围-结束日期
  69. * @property {String} mode = [不传 | multiple | range ] 多个日期 | 选择日期范围 默认单日期
  70. * @property {Boolean} insert = [true|false] 插入模式,默认为false
  71. * @value true 弹窗模式
  72. * @value false 插入模式
  73. * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
  74. * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
  75. * @property {String} cancelColor 取消按钮颜色
  76. * @property {String} confirmColor 确认按钮颜色,默认#3c9cff
  77. * @property {String} title 头部工具条中间的标题文字
  78. * @property {String} color 主题色,默认#3c9cff
  79. * @property {Number} round :insert="false"时的圆角
  80. * @property {Boolean} closeOnClickOverlay 点击遮罩是否关闭
  81. * @property {String} startText range为true时,第一个日期底部的提示文字
  82. * @property {String} endText range为true时,最后一个日期底部的提示文字
  83. * @property {String} readonly 是否为只读状态,只读状态下禁止选择日期,默认false
  84. *
  85. * @event {Function} change 日期改变,`insert :ture` 时生效
  86. * @event {Function} confirm 确认选择`insert :false` 时生效
  87. * @event {Function} monthSwitch 切换月份时触发
  88. *
  89. * @example <uv-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
  90. */
  91. import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js';
  92. import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js';
  93. import Calendar from './util.js';
  94. import calendarBody from './calendar-body.vue';
  95. import { initVueI18n } from '@dcloudio/uni-i18n';
  96. import i18nMessages from './i18n/index.js';
  97. const { t } = initVueI18n(i18nMessages);
  98. export default {
  99. components: {
  100. calendarBody
  101. },
  102. mixins: [mpMixin, mixin],
  103. emits: ['close', 'confirm', 'change', 'monthSwitch'],
  104. props: {
  105. // 取消按钮颜色
  106. cancelColor: {
  107. type: String,
  108. default: ''
  109. },
  110. // 确认按钮颜色,range模式下未选全显示灰色
  111. confirmColor: {
  112. type: String,
  113. default: '#3c9cff'
  114. },
  115. // 标题
  116. title: {
  117. type: String,
  118. default: ''
  119. },
  120. // 主题色
  121. color: {
  122. type: String,
  123. default: '#3c9cff'
  124. },
  125. // 默认显示日期
  126. date: {
  127. type: [String,Array],
  128. default: ''
  129. },
  130. // 打点等设置
  131. selected: {
  132. type: Array,
  133. default () {
  134. return []
  135. }
  136. },
  137. // 是否显示农历
  138. lunar: {
  139. type: Boolean,
  140. default: false
  141. },
  142. // 可选择的起始日期
  143. startDate: {
  144. type: String,
  145. default: ''
  146. },
  147. // 可选择的结束日期
  148. endDate: {
  149. type: String,
  150. default: ''
  151. },
  152. // multiple - 选择多日期 range - 选择日期范围
  153. mode: {
  154. type: String,
  155. default: ''
  156. },
  157. // 是否插入模式
  158. insert: {
  159. type: Boolean,
  160. default: false
  161. },
  162. // 是否显示月份为背景
  163. showMonth: {
  164. type: Boolean,
  165. default: true
  166. },
  167. // 弹窗模式是否清空上次选择内容
  168. clearDate: {
  169. type: Boolean,
  170. default: true
  171. },
  172. // 弹窗圆角
  173. round: {
  174. type: [Number,String],
  175. default: 8
  176. },
  177. // 点击遮罩是否关闭弹窗
  178. closeOnClickOverlay: {
  179. type: Boolean,
  180. default: true
  181. },
  182. // range为true时,第一个日期底部的提示文字
  183. startText: {
  184. type: String,
  185. default: '开始'
  186. },
  187. // range为true时,最后一个日期底部的提示文字
  188. endText: {
  189. type: String,
  190. default: '结束'
  191. },
  192. // 是否允许日期范围的起止时间为同一天,mode = range时有效
  193. allowSameDay: {
  194. type: Boolean,
  195. default: false
  196. },
  197. // 是否禁用
  198. readonly: {
  199. type: Boolean,
  200. default: false
  201. },
  202. ...uni.$uv?.props?.calendars
  203. },
  204. data(){
  205. return {
  206. weeks: [],
  207. calendar: {},
  208. nowDate: '',
  209. allowConfirm: false,
  210. multiple: false,
  211. range: false
  212. }
  213. },
  214. computed:{
  215. /**
  216. * for i18n
  217. */
  218. confirmText() {
  219. return t("uv-calender.ok")
  220. },
  221. cancelText() {
  222. return t("uv-calender.cancel")
  223. },
  224. getConfirmColor() {
  225. if(this.range || this.multiple || this.readonly) {
  226. return this.allowConfirm? this.confirmColor: '#999'
  227. }else {
  228. return this.confirmColor;
  229. }
  230. }
  231. },
  232. watch: {
  233. date(newVal) {
  234. this.init(newVal)
  235. },
  236. startDate(val) {
  237. this.cale.resetSatrtDate(val)
  238. this.cale.setDate(this.nowDate.fullDate)
  239. this.weeks = this.cale.weeks
  240. },
  241. endDate(val) {
  242. this.cale.resetEndDate(val)
  243. this.cale.setDate(this.nowDate.fullDate)
  244. this.weeks = this.cale.weeks
  245. },
  246. selected(newVal) {
  247. this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
  248. this.weeks = this.cale.weeks
  249. }
  250. },
  251. created() {
  252. this.setMode();
  253. this.cale = new Calendar({
  254. selected: this.selected,
  255. startDate: this.startDate,
  256. endDate: this.endDate,
  257. range: this.range,
  258. multiple: this.multiple,
  259. allowSameDay: this.allowSameDay
  260. })
  261. this.init(this.date)
  262. },
  263. methods: {
  264. setMode() {
  265. switch (this.mode){
  266. case 'range':
  267. this.range = true;
  268. break;
  269. case 'multiple':
  270. this.multiple = true;
  271. default:
  272. break;
  273. }
  274. },
  275. async open() {
  276. if (this.clearDate && !this.insert) {
  277. this.cale.cleanRangeStatus()
  278. this.init(this.date)
  279. }
  280. if(!this.insert){
  281. this.$refs.popup.open();
  282. }
  283. },
  284. close() {
  285. this.$refs.popup.close();
  286. this.$emit('close');
  287. },
  288. confirm() {
  289. if(this.readonly) {
  290. return;
  291. } else if(this.range && !this.cale.rangeStatus.after) {
  292. return;
  293. } else if(this.multiple && this.cale.multipleStatus.data.length == 0){
  294. return;
  295. }
  296. this.setEmit('confirm');
  297. this.close()
  298. },
  299. maskClick() {
  300. if(this.closeOnClickOverlay) {
  301. this.$emit('close');
  302. }
  303. },
  304. bindDateChange(e) {
  305. const value = e.detail.value + '-1'
  306. this.setDate(value)
  307. const { year, month } = this.cale.getDate(value)
  308. this.$emit('monthSwitch', {
  309. year,
  310. month
  311. })
  312. },
  313. /**
  314. * 初始化日期显示
  315. * @param {Object} date
  316. */
  317. init(date) {
  318. if(this.range) {
  319. // 重置范围选择状态
  320. this.cale.cleanRangeStatus();
  321. }else if(this.multiple){
  322. // 重置多选状态
  323. this.cale.cleanMultipleStatus();
  324. }
  325. this.cale.setDate(date,'init')
  326. this.weeks = this.cale.weeks
  327. this.nowDate = this.calendar = this.cale.getInfo(date)
  328. this.changeConfirmStatus();
  329. },
  330. /**
  331. * 变化触发
  332. */
  333. change() {
  334. this.changeConfirmStatus();
  335. if (!this.insert) return
  336. this.setEmit('change')
  337. },
  338. changeConfirmStatus() {
  339. if(this.readonly) {
  340. this.allowConfirm = false;
  341. } else if (this.range) {
  342. this.allowConfirm = this.cale.rangeStatus.after ? true : false;
  343. } else if(this.multiple) {
  344. this.allowConfirm = this.cale.multipleStatus.data.length > 0 ? true : false;
  345. }
  346. },
  347. /**
  348. * 选择月份触发
  349. */
  350. monthSwitch() {
  351. let {
  352. year,
  353. month
  354. } = this.nowDate
  355. this.$emit('monthSwitch', {
  356. year,
  357. month: Number(month)
  358. })
  359. },
  360. /**
  361. * 派发事件
  362. * @param {Object} name
  363. */
  364. setEmit(name) {
  365. let {
  366. year,
  367. month,
  368. date,
  369. fullDate,
  370. lunar,
  371. extraInfo
  372. } = this.calendar
  373. this.$emit(name, {
  374. range: this.cale.rangeStatus,
  375. multiple: this.cale.multipleStatus,
  376. year,
  377. month,
  378. date,
  379. fulldate: fullDate,
  380. lunar,
  381. extraInfo: extraInfo || {}
  382. })
  383. },
  384. /**
  385. * 选择天触发
  386. * @param {Object} weeks
  387. */
  388. choiceDate(weeks) {
  389. if (weeks.disable || this.readonly) return
  390. this.calendar = weeks
  391. // 设置范围选择
  392. this.cale.setRange(this.calendar.fullDate)
  393. // 设置多选
  394. this.cale.setMultiple(this.calendar.fullDate);
  395. this.weeks = this.cale.weeks
  396. this.change()
  397. },
  398. /**
  399. * 回到今天
  400. */
  401. backToday() {
  402. const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}`
  403. const date = this.cale.getDate(new Date())
  404. const todayYearMonth = `${date.year}-${date.month}`
  405. this.init(date.fullDate)
  406. if (nowYearMonth !== todayYearMonth) {
  407. this.monthSwitch()
  408. }
  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. this.cale.setDate(date)
  433. this.weeks = this.cale.weeks
  434. this.nowDate = this.cale.getInfo(date)
  435. }
  436. }
  437. }
  438. </script>
  439. <style scoped lang="scss">
  440. $uv-border-color: #EDEDED !default;
  441. .uv-calendar__content {
  442. background-color: #fff;
  443. }
  444. .line {
  445. width: 750rpx;
  446. height: 1px;
  447. border-bottom-color: $uv-border-color;
  448. border-bottom-style: solid;
  449. border-bottom-width: 1px;
  450. }
  451. </style>