wu-calendar.vue 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  1. <template>
  2. <view class="wu-calendar" @click.stop>
  3. <view v-if="!insert && show" class="wu-calendar__mask" :class="{'wu-calendar--mask-show': aniMaskShow}"
  4. @click="clean"></view>
  5. <view v-if="insert || show" class="wu-calendar__content"
  6. :class="{'wu-calendar--fixed': !insert, 'wu-calendar--ani-show': aniMaskShow}">
  7. <!-- 弹窗日历取消与确认按钮位置 -->
  8. <slot name="operation" v-if="operationPosition == 'top'">
  9. <view v-if="!insert" class="wu-calendar__header wu-calendar--fixed-top">
  10. <view class="wu-calendar__header-btn-box" @click="cancel">
  11. <text class="wu-calendar__header-text wu-calendar--fixed-width" :style="[{color: cancelColor}]">{{cancelText}}</text>
  12. </view>
  13. <view class="wu-calendar__header-btn-box" @click="confirm">
  14. <text class="wu-calendar__header-text wu-calendar--fixed-width" :style="[{color: confirmColor}]">{{okText}}</text>
  15. </view>
  16. </view>
  17. </slot>
  18. <!-- 日历头部 -->
  19. <slot name="header" :nowDate="nowDate">
  20. <view class="wu-calendar__header">
  21. <!-- 纵向滑动切换展示内容 -->
  22. <template v-if="slideSwitchMode == 'vertical'">
  23. <view class="wu-calendar__header-btn-box vertical">
  24. <view class="wu-calendar__header-btn wu-calendar--top" @click.stop="pre"></view>
  25. <picker mode="date" :value="nowDate.fullDate" fields="month" @change="bindDateChange">
  26. <text
  27. class="wu-calendar__header-text">{{ (nowDate.year||'') + YearText + ( nowDate.month||'') + MonthText }}</text>
  28. </picker>
  29. <view class="wu-calendar__header-btn wu-calendar--bottom" @click.stop="next"></view>
  30. </view>
  31. <text class="wu-calendar__backtoday vertical" @click="backToday">{{todayText}}</text>
  32. </template>
  33. <!-- 横向滑动与无滑动展示内容 -->
  34. <template v-else>
  35. <view class="wu-calendar__header-btn-box horizontal" @click.stop="pre">
  36. <view class="wu-calendar__header-btn wu-calendar--left"></view>
  37. </view>
  38. <picker mode="date" :value="nowDate.fullDate" fields="month" @change="bindDateChange">
  39. <text
  40. class="wu-calendar__header-text">{{ (nowDate.year||'') + YearText + ( nowDate.month||'') + MonthText }}</text>
  41. </picker>
  42. <view class="wu-calendar__header-btn-box horizontal" @click.stop="next">
  43. <view class="wu-calendar__header-btn wu-calendar--right"></view>
  44. </view>
  45. <text class="wu-calendar__backtoday" @click="backToday">{{todayText}}</text>
  46. </template>
  47. </view>
  48. </slot>
  49. <view class="wu-calendar__box">
  50. <view class="wu-calendar__weeks">
  51. <view class="wu-calendar__weeks-day" v-if="startWeek === 'sun'">
  52. <text class="wu-calendar__weeks-day-text">{{SUNText}}</text>
  53. </view>
  54. <view class="wu-calendar__weeks-day">
  55. <text class="wu-calendar__weeks-day-text">{{monText}}</text>
  56. </view>
  57. <view class="wu-calendar__weeks-day">
  58. <text class="wu-calendar__weeks-day-text">{{TUEText}}</text>
  59. </view>
  60. <view class="wu-calendar__weeks-day">
  61. <text class="wu-calendar__weeks-day-text">{{WEDText}}</text>
  62. </view>
  63. <view class="wu-calendar__weeks-day">
  64. <text class="wu-calendar__weeks-day-text">{{THUText}}</text>
  65. </view>
  66. <view class="wu-calendar__weeks-day">
  67. <text class="wu-calendar__weeks-day-text">{{FRIText}}</text>
  68. </view>
  69. <view class="wu-calendar__weeks-day">
  70. <text class="wu-calendar__weeks-day-text">{{SATText}}</text>
  71. </view>
  72. <view class="wu-calendar__weeks-day" v-if="startWeek === 'mon'">
  73. <text class="wu-calendar__weeks-day-text">{{SUNText}}</text>
  74. </view>
  75. </view>
  76. <!-- 滑动切换 -->
  77. <swiper v-if="slideSwitchMode !== 'none'" :class="{'wu-calendar__weeks_container': initStatus}"
  78. :style="[calendarContentStyle]" :duration="500" :vertical="slideSwitchMode == 'vertical'" circular
  79. :current="swiperCurrent" @change="swiperChange" skip-hidden-item-layout>
  80. <!-- 月或周日历 -->
  81. <template v-if="type === 'month' || type === 'week'">
  82. <swiper-item>
  83. <wu-calendar-block :weeks="preWeeks" :calendar="calendar" :selected="selected"
  84. :lunar="lunar" @change="choiceDate" :color="color" :actBadgeColor="actBadgeColor" :startText="startText"
  85. :endText="endText" :month="preWeeksMonth" :FoldStatus="FoldStatus"
  86. :monthShowCurrentMonth="monthShowCurrentMonth" :showMonth="showMonth"
  87. :itemHeight="itemHeight" :defaultMargin="defaultMargin" :todayDefaultStyle="todayDefaultStyle"></wu-calendar-block>
  88. </swiper-item>
  89. <swiper-item>
  90. <wu-calendar-block :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar"
  91. @change="choiceDate" :color="color" :actBadgeColor="actBadgeColor" :startText="startText" :endText="endText"
  92. :monthShowCurrentMonth="monthShowCurrentMonth" :month="weeksMonth"
  93. :FoldStatus="FoldStatus" :showMonth="showMonth"
  94. :itemHeight="itemHeight" :defaultMargin="defaultMargin" :todayDefaultStyle="todayDefaultStyle"></wu-calendar-block>
  95. </swiper-item>
  96. <swiper-item>
  97. <wu-calendar-block :weeks="nextWeeks" :calendar="calendar" :selected="selected"
  98. :lunar="lunar" @change="choiceDate" :color="color" :actBadgeColor="actBadgeColor" :startText="startText"
  99. :endText="endText" :month="nextWeeksMonth" :FoldStatus="FoldStatus"
  100. :monthShowCurrentMonth="monthShowCurrentMonth" :showMonth="showMonth"
  101. :itemHeight="itemHeight" :defaultMargin="defaultMargin" :todayDefaultStyle="todayDefaultStyle"></wu-calendar-block>
  102. </swiper-item>
  103. </template>
  104. </swiper>
  105. <!-- 无滑动切换 -->
  106. <template v-else>
  107. <!-- 月或周日历 -->
  108. <wu-calendar-block class="wu-calendar__weeks_container" :style="[calendarContentStyle]"
  109. :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"
  110. :color="color" :actBadgeColor="actBadgeColor" :startText="startText" :endText="endText" :month="nowDate.month"
  111. :FoldStatus="FoldStatus" :monthShowCurrentMonth="monthShowCurrentMonth" :showMonth="showMonth"
  112. :itemHeight="itemHeight" :defaultMargin="defaultMargin" :todayDefaultStyle="todayDefaultStyle"></wu-calendar-block>
  113. </template>
  114. </view>
  115. <view class="wu-calendar__fold" v-if="type !== 'year' && Fold" @click="FoldClick">
  116. <wu-icon v-if="FoldStatus == 'open'" name="arrow-up" bold size="18"></wu-icon>
  117. <wu-icon v-else-if="FoldStatus == 'close'" name="arrow-down" bold size="18"></wu-icon>
  118. </view>
  119. <!-- 弹窗日历取消与确认按钮位置 -->
  120. <slot name="operation" v-if="operationPosition == 'bottom'">
  121. <view v-if="!insert" class="wu-calendar__header wu-calendar--fixed-top">
  122. <view class="wu-calendar__header-btn-box" @click="cancel">
  123. <text class="wu-calendar__header-text wu-calendar--fixed-width" :style="[{color: cancelColor}]">{{cancelText}}</text>
  124. </view>
  125. <view class="wu-calendar__header-btn-box" @click="confirm">
  126. <text class="wu-calendar__header-text wu-calendar--fixed-width" :style="[{color: confirmColor}]">{{okText}}</text>
  127. </view>
  128. </view>
  129. </slot>
  130. <wu-safe-bottom v-if="!insert && show"></wu-safe-bottom>
  131. </view>
  132. </view>
  133. </template>
  134. <script>
  135. import Calendar from './util.js';
  136. import mpMixin from '@/uni_modules/wu-ui-tools/libs/mixin/mpMixin.js';
  137. import mixin from '@/uni_modules/wu-ui-tools/libs/mixin/mixin.js';
  138. import props from './props.js';
  139. import {
  140. initVueI18n
  141. } from '@dcloudio/uni-i18n'
  142. import i18nMessages from '../i18n/index.js'
  143. const {
  144. t
  145. } = initVueI18n(i18nMessages)
  146. /**
  147. * Calendar 日历
  148. * @description 日历组件,多模式选择(单日期、多日期、范围日期选择),多日历类型(周、月日历),动态计算滑动。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
  149. * @tutorial https://wuui.cn/zh-CN/components/calendar.html
  150. * @property {String} date 自定义当前时间,默认为今天
  151. * @property {String} type 日历类型(默认为month)
  152. * @value month 月日历
  153. * @value week 周日历
  154. * @property {Boolean} fold 是否支持折叠(默认值 month: true, week: false)
  155. * @property {Boolean} useToday 是否使用默认日期(今天,默认为true)
  156. * @property {Boolean} todayDefaultStyle 是否显示今日默认样式(默认为true)
  157. * @property {String} color 主题色(默认#3c9cff)
  158. * @property {String} mode = [single|multiple|range] 日期选择类型(默认single(单日期选择))
  159. * @value single 单日期选择
  160. * @value multiple 多日期选择
  161. * @value range 范围选择
  162. * @property {String} color 主题色(默认#3c9cff)
  163. * @property {Number} itemHeight 日历中每一项日期的高度(默认60),单位px
  164. * @property {String} cancelColor 取消文字的颜色(默认#333333)
  165. * @property {String} confirmColor 确认文字的颜色(默认#333333)
  166. * @property {String} startText mode=range时,第一个日期底部的提示文字
  167. * @property {String} endText mode=range时,最后一个日期底部的提示文字
  168. * @property {String} startWeek 日历以周几开始,默认为周日(sun),`type: month | week`时生效
  169. * @value sun 周日
  170. * @value mon 周一
  171. * @property {Boolean} lunar 显示农历
  172. * @property {String} startDate 日期选择范围-开始日期
  173. * @property {String} endDate 日期选择范围-结束日期
  174. * @property {Boolean} rangeEndRepick 允许范围内重选结束日期(默认false)
  175. * @property {Boolean} rangeSameDay 允许日期选择范围起始日期为同一天(默认false)
  176. * @property {Boolean} rangeHaveDisableTruncation 允许日期选择范围内遇到打点禁用日期进行截断
  177. * @property {Boolean} monthShowCurrentMonth 当月是否仅展示当月数据
  178. * @property {Boolean} insert = [true|false] 插入模式,默认为true
  179. * @value true 插入模式
  180. * @value false 弹窗模式
  181. * @property {String} slideSwitchMode 滑动切换模式,默认为horizontal(横向滑动切换)
  182. * @value horizontal 横向滑动切换
  183. * @value vertical 纵向滑动切换
  184. * @value none 不使用滑动切换
  185. * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
  186. * @property {Array[Object]} selected 自定义打点,期待格式 [{date: '2023-11-18', info: '¥888', infoColor: '#6ac695', topInfo: '机票打折', topInfoColor: '#6ac695', badge: true, badgePosition: 'top-right', disable: false}, ...]
  187. * @property {Boolean} showMonth 是否选择月份为背景(默认true)
  188. * @property {Boolean} maskClick 是否点击遮罩层关闭(默认false)
  189. * @property {Boolean} disabledChoice 是否禁止点击日历(默认false)
  190. * @property {String} actBadgeColor 当通过 `selected` 属性设置某个日期 `badgeColor`后,如果该日期被选择且主题色与 `badgeColor` 一致时,徽标会显示本颜色
  191. * @property {String} operationPosition 弹窗日历取消和确认按钮的显示位置
  192. * @property {Boolean} confirmFullDate 弹窗日历点击确认时是否需要选择完整日期
  193. * @event {Function} close 日历弹窗点击遮罩层关闭,`insert :false` 时生效
  194. * @event {Function} change 日期改变,`insert :ture` 时生效
  195. * @event {Function} confirm 确认选择,`insert :false` 时生效
  196. * @event {Function} cancel 点击取消按钮,`insert :false` 时生效
  197. * @event {Function} monthSwitch 切换月份时触发
  198. * @event {Function} foldSwitch 切换折叠状态时触发,`type: month | week` & `fold: true` 时生效
  199. * @example <wu-calendar :insert="true":lunar="true" start-date="2022-5-20" end-date="2023-5-20"@change="change" />
  200. */
  201. export default {
  202. mixins: [mpMixin, mixin, props],
  203. emits: ['close', 'cancel', 'confirm', 'change', 'monthSwitch', 'foldSwitch'],
  204. data() {
  205. return {
  206. show: false,
  207. weeks: [],
  208. preWeeks: [],
  209. nextWeeks: [],
  210. weeksMonth: null,
  211. preWeeksMonth: null,
  212. nextWeeksMonth: null,
  213. calendar: {},
  214. nowDate: '',
  215. aniMaskShow: false,
  216. swiperCurrent: 1,
  217. swiperChangeDirection: '',
  218. pickerDate: '',
  219. Fold: null,
  220. FoldStatus: null,
  221. weekContentStyle: {},
  222. initStatus: false,
  223. defaultMargin: 8
  224. }
  225. },
  226. computed: {
  227. /**
  228. * for i18n
  229. */
  230. okText() {
  231. return t("wu-calender.ok")
  232. },
  233. cancelText() {
  234. return t("wu-calender.cancel")
  235. },
  236. YearText() {
  237. return t("wu-calender.year")
  238. },
  239. MonthText() {
  240. return t("wu-calender.month")
  241. },
  242. todayText() {
  243. return t("wu-calender.today")
  244. },
  245. monText() {
  246. return t("wu-calender.MON")
  247. },
  248. TUEText() {
  249. return t("wu-calender.TUE")
  250. },
  251. WEDText() {
  252. return t("wu-calender.WED")
  253. },
  254. THUText() {
  255. return t("wu-calender.THU")
  256. },
  257. FRIText() {
  258. return t("wu-calender.FRI")
  259. },
  260. SATText() {
  261. return t("wu-calender.SAT")
  262. },
  263. SUNText() {
  264. return t("wu-calender.SUN")
  265. },
  266. calendarContentStyle() {
  267. return {
  268. height: (this.FoldStatus === 'open' ? this.itemHeight * 6 : this.itemHeight) + 'px'
  269. }
  270. },
  271. getDateType() {
  272. if (this.type === 'year') return this.type
  273. return this.FoldStatus === 'open' ? 'month' : 'week'
  274. }
  275. },
  276. watch: {
  277. date(newVal) {
  278. this.cale.cleanRange();
  279. this.init(newVal)
  280. },
  281. mode(newVal) {
  282. this.cale.cleanRange();
  283. this.cale.resetMode(newVal)
  284. this.init(this.date)
  285. },
  286. startDate(val) {
  287. this.cale.resetSatrtDate(val)
  288. this.cale.setDate(this.nowDate.fullDate)
  289. this.assignmentWeeks();
  290. },
  291. endDate(val) {
  292. this.cale.resetEndDate(val)
  293. this.cale.setDate(this.nowDate.fullDate)
  294. this.assignmentWeeks();
  295. },
  296. monthShowCurrentMonth(val) {
  297. this.cale.resetMonthShowCurrentMonth(val)
  298. this.setDate(this.nowDate.fullDate)
  299. },
  300. rangeEndRepick(val) {
  301. this.cale.resetRangeEndRepick(val)
  302. },
  303. rangeSameDay(val) {
  304. this.cale.resetRangeSameDay(val)
  305. },
  306. rangeHaveDisableTruncation(val) {
  307. this.cale.resetRangeHaveDisableTruncation(val)
  308. this.cale.cleanRange()
  309. this.init(this.date)
  310. },
  311. selected: {
  312. handler(newVal) {
  313. this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
  314. this.assignmentWeeks()
  315. // 找出目前的信息weeks 并 将 this.calendar重新赋值
  316. let nowDateInfo = this.cale.canlender.filter(item => item.fullDate && this.cale.dateEqual(item
  317. .fullDate, this.calendar.fullDate))
  318. if (nowDateInfo.length) this.calendar = nowDateInfo[0]
  319. },
  320. deep: true
  321. },
  322. fold(newVal) {
  323. this.Fold = newVal
  324. },
  325. type(newVal) {
  326. this.initFold();
  327. this.cale.resetFoldStatus(this.FoldStatus);
  328. this.init(this.date)
  329. },
  330. startWeek(newVal) {
  331. this.cale.cleanRange();
  332. this.cale.resetStartWeek(newVal)
  333. this.init(this.date)
  334. }
  335. },
  336. created() {
  337. this.initFold();
  338. this.cale = new Calendar({
  339. selected: this.selected,
  340. startDate: this.startDate,
  341. endDate: this.endDate,
  342. mode: this.mode,
  343. type: this.type,
  344. startWeek: this.startWeek,
  345. foldStatus: this.FoldStatus,
  346. monthShowCurrentMonth: this.monthShowCurrentMonth,
  347. rangeEndRepick: this.rangeEndRepick,
  348. rangeSameDay: this.rangeSameDay,
  349. rangeHaveDisableTruncation: this.rangeHaveDisableTruncation
  350. })
  351. this.init(this.date);
  352. },
  353. methods: {
  354. // 取消穿透
  355. clean() {
  356. if (this.maskClick) {
  357. this.$emit('close')
  358. this.close()
  359. }
  360. },
  361. bindDateChange(e) {
  362. const value = e.detail.value + '-1'
  363. this.setDate(value)
  364. this.swiperCurrentChangeWeeks();
  365. const {
  366. year,
  367. month
  368. } = this.cale.getDate(value)
  369. this.$emit('monthSwitch', {
  370. year,
  371. month: Number(month),
  372. fullDate: `${year}-${`${month}`.padStart(2, '0')}`
  373. })
  374. },
  375. /**
  376. * 初始化日期显示
  377. * @param {Object} date
  378. */
  379. init(date) {
  380. this.$nextTick(()=>{
  381. // 初始化
  382. this.initStatus = false;
  383. let firstDate = this.mode == 'single' ? date : date[0];
  384. // 如果填写默认值
  385. if (date) {
  386. // 当前数据类型
  387. let dateType = Object.prototype.toString.call(date);
  388. // 验证类型
  389. if (this.mode == 'single' && dateType != '[object String]') {
  390. return console.error(`类型错误,mode=${this.mode}时,date=String`)
  391. } else if (this.mode != 'single' && dateType != '[object Array]') {
  392. return console.error(`类型错误,mode=${this.mode}时,date=Array`)
  393. }
  394. // 根据类型默认选中不同的值
  395. if (this.mode == 'multiple') {
  396. this.cale.multiple = date.map(item=>item);
  397. this.cale._getWeek(this.cale.multiple[this.cale.multiple.length - 1]);
  398. } else if (this.mode == 'range') {
  399. date[0] ? this.cale.setRange(date[0]) : ''
  400. date[1] ? this.cale.setRange(date[1]) : ''
  401. }
  402. }
  403. // 如果不填写默认值 且 使用今日作为默认值 并且 还没有在打点中禁用今天的日期
  404. else if (this.useToday && !this.selected.filter(item => item.disable && this.cale.dateEqual(item.date, this
  405. .cale.date.fullDate)).length) {
  406. if (this.mode == 'multiple') {
  407. this.cale.multiple = [this.cale.date.fullDate];
  408. this.cale._getWeek(this.cale.multiple[this.cale.multiple.length - 1]);
  409. } else if (this.mode == 'range') {
  410. this.cale.setRange(this.cale.date.fullDate)
  411. }
  412. }
  413. // 设置日期
  414. this.cale.setDate(firstDate);
  415. // 现在的日期
  416. this.nowDate = this.cale.getInfo(firstDate);
  417. // 设置当前月份
  418. this.weeksMonth = this.nowDate.month;
  419. // 如果不填写默认值 且 使用今日作为默认值
  420. if ((this.useToday && !this.date) || this.date) {
  421. this.calendar = this.nowDate;
  422. }
  423. // 渲染
  424. this.updateWeeks(false, true);
  425. // 初始化成功
  426. this.initStatus = true;
  427. })
  428. },
  429. /**
  430. * 打开日历弹窗
  431. */
  432. open() {
  433. // #ifdef APP-NVUE
  434. // 为弹窗模式且需要清理数据
  435. if (this.clearDate && !this.insert) {
  436. this.reset(this.date);
  437. }
  438. // #endif
  439. this.show = true;
  440. // #ifdef H5
  441. if (!this.insert) document.body.style = 'overflow: hidden'
  442. // #endif
  443. this.$nextTick(() => {
  444. setTimeout(() => {
  445. this.aniMaskShow = true;
  446. }, 50)
  447. })
  448. },
  449. /**
  450. * 关闭日历弹窗
  451. */
  452. close() {
  453. this.aniMaskShow = false;
  454. this.$nextTick(() => {
  455. setTimeout(() => {
  456. this.show = false
  457. // #ifdef H5
  458. if (!this.insert) document.body.style = 'overflow: visible'
  459. // #endif
  460. // #ifndef APP-NVUE
  461. // 为弹窗模式且需要清理数据
  462. if (this.clearDate && !this.insert) {
  463. this.reset()
  464. }
  465. // #endif
  466. }, 300)
  467. })
  468. },
  469. /**
  470. * 重置
  471. */
  472. reset() {
  473. this.cale.cleanRange();
  474. this.cale.cleanMultiple();
  475. this.swiperCurrent = 1;
  476. this.init(this.date);
  477. },
  478. /**
  479. * 清空选中
  480. */
  481. clearSelect() {
  482. this.cale.cleanRange();
  483. this.cale.cleanMultiple();
  484. this.calendar = {};
  485. this.updateWeeks(false, true);
  486. },
  487. /**
  488. * 确认按钮
  489. */
  490. confirm() {
  491. if(this.confirmFullDate) {
  492. if(this.mode == 'single' && !this.calendar.fullDate) {
  493. return uni.showToast({
  494. icon: 'none',
  495. title: '请选择日期',
  496. duration: 600
  497. });
  498. } else if(this.mode == 'multiple' && !this.cale.multiple.length) {
  499. return uni.showToast({
  500. icon: 'none',
  501. title: '请至少选择一个日期',
  502. duration: 600
  503. });
  504. } else if(this.mode == 'range') {
  505. if(!this.cale.rangeStatus.before) {
  506. return uni.showToast({
  507. icon: 'none',
  508. title: '请选择开始日期',
  509. duration: 600
  510. });
  511. } else if(!this.cale.rangeStatus.after) {
  512. return uni.showToast({
  513. icon: 'none',
  514. title: '请选择结束日期',
  515. duration: 600
  516. });
  517. }
  518. }
  519. }
  520. this.setEmit('confirm')
  521. this.close()
  522. },
  523. /**
  524. * 取消按钮
  525. */
  526. cancel() {
  527. this.$emit('cancel')
  528. this.close()
  529. },
  530. /**
  531. * 变化触发
  532. */
  533. change() {
  534. if (!this.insert) return
  535. this.setEmit('change')
  536. },
  537. /**
  538. * 选择月份触发
  539. */
  540. monthSwitch() {
  541. let {
  542. year,
  543. month
  544. } = this.nowDate;
  545. this.$emit('monthSwitch', {
  546. year,
  547. month: Number(month),
  548. fullDate: `${year}-${`${month}`.padStart(2, '0')}`
  549. })
  550. },
  551. /**
  552. * 派发事件
  553. * @param {Object} name
  554. */
  555. setEmit(name) {
  556. let {
  557. year,
  558. month,
  559. date,
  560. fullDate,
  561. lunar,
  562. extraInfo,
  563. type,
  564. mode
  565. } = this.calendar;
  566. let params = {
  567. range: this.cale.rangeStatus,
  568. multiple: this.cale.multiple,
  569. mode,
  570. type,
  571. year,
  572. month: Number(month),
  573. date,
  574. fulldate: fullDate,
  575. lunar,
  576. extraInfo: extraInfo || {}
  577. }
  578. if (this.type === 'month' || this.type === 'week') {
  579. params.foldStatus = this.FoldStatus
  580. }
  581. this.$emit(name, params)
  582. },
  583. /**
  584. * 选择天触发
  585. * @param {Object} weeks
  586. */
  587. choiceDate(weeks) {
  588. // 如果为禁用 或者 空数据 或者 禁止点击日期
  589. if (weeks.disable || weeks.empty || this.disabledChoice) return;
  590. this.calendar = weeks;
  591. // 保存操作的日历信息
  592. this.nowDate = this.calendar;
  593. // 设置选择范围
  594. this.cale.setRange(this.calendar.fullDate);
  595. // 设置多选
  596. this.cale.setMultiple(this.calendar.fullDate);
  597. // 如果启用滑动切换 且当前模式为范围选择时则重新计算上月与下月
  598. if (this.slideSwitchMode !== 'none') {
  599. let weekName = '';
  600. switch (this.swiperCurrent) {
  601. case 0:
  602. weekName = 'preWeeks'
  603. if (this.mode == 'range') {
  604. this.weeks = this.cale._getWeek(this.weeks[0].find(item => item.fullDate).fullDate, false)
  605. this.nextWeeks = this.cale._getWeek(this.nextWeeks[0].find(item => item.fullDate).fullDate,
  606. false)
  607. }
  608. break;
  609. case 1:
  610. weekName = 'weeks'
  611. if (this.mode == 'range') {
  612. this.preWeeks = this.cale._getWeek(this.preWeeks[0].find(item => item.fullDate).fullDate,
  613. false)
  614. this.nextWeeks = this.cale._getWeek(this.nextWeeks[0].find(item => item.fullDate).fullDate,
  615. false)
  616. }
  617. break;
  618. case 2:
  619. weekName = 'nextWeeks'
  620. if (this.mode == 'range') {
  621. this.weeks = this.cale._getWeek(this.weeks[0].find(item => item.fullDate).fullDate, false)
  622. this.preWeeks = this.cale._getWeek(this.preWeeks[0].find(item => item.fullDate).fullDate,
  623. false)
  624. }
  625. break;
  626. }
  627. this[weekName] = this.cale.weeks;
  628. } else {
  629. this.weeks = this.cale.weeks;
  630. }
  631. this.change();
  632. },
  633. /**
  634. * 回到今天
  635. */
  636. backToday() {
  637. // 获取目前的年月
  638. const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}`
  639. if (this.cale.rangeStatus.before && !this.cale.rangeStatus.after) {
  640. this.cale.rangeStatus.before = '';
  641. }
  642. // 设置日期
  643. this.setDate(this.cale.date.fullDate);
  644. let date = this.nowDate;
  645. this.calendar = date;
  646. // 设置选中的日期
  647. this.cale.setRange(date.fullDate);
  648. // 今天的日期
  649. const todayYearMonth = `${date.year}-${date.month}`
  650. // 如果当前日期 与 今天的日期不符
  651. if (nowYearMonth !== todayYearMonth) {
  652. // 触发月份切换事件
  653. this.monthSwitch()
  654. }
  655. // 设置日期
  656. this.setDate(this.cale.date.fullDate);
  657. // swiperCurrent改变需要改动的weeks
  658. this.swiperCurrentChangeWeeks();
  659. // 改变事件
  660. this.change()
  661. },
  662. /**
  663. * 上个月
  664. */
  665. pre() {
  666. this.swiperChangeDirection = 'pre'
  667. this.updateWeeks();
  668. },
  669. /**
  670. * 下个月
  671. */
  672. next() {
  673. this.swiperChangeDirection = 'next'
  674. this.updateWeeks();
  675. },
  676. /**
  677. * 设置日期
  678. * @param {Object} date
  679. */
  680. setDate(date) {
  681. this.cale.setDate(date)
  682. this.nowDate = this.cale.getInfo(date)
  683. this.assignmentWeeks()
  684. },
  685. /**
  686. * 用来将cale.weeks 赋值到 weeks
  687. */
  688. assignmentWeeks() {
  689. let weekName = '';
  690. let weekMonthName = '';
  691. switch (this.swiperCurrent) {
  692. case 0:
  693. weekName = 'preWeeks'
  694. weekMonthName = 'preWeeksMonth'
  695. break;
  696. case 1:
  697. weekName = 'weeks'
  698. weekMonthName = 'weeksMonth'
  699. break;
  700. case 2:
  701. weekName = 'nextWeeks'
  702. weekMonthName = 'nextWeeksMonth'
  703. break;
  704. }
  705. this[weekName] = this.cale.weeks;
  706. this[weekMonthName] = this.cale.selectDate.month;
  707. },
  708. /**
  709. * 滑动切换日期
  710. */
  711. swiperChange(e) {
  712. // 非用户滑动不执行
  713. if (e.detail.source !== 'touch' && e.detail.source !== 'autoplay') return;
  714. let curr = e.detail.current;
  715. if (curr - this.swiperCurrent == 1 || curr - this.swiperCurrent == -2) {
  716. this.swiperChangeDirection = 'next'
  717. } else {
  718. this.swiperChangeDirection = 'pre'
  719. }
  720. this.swiperCurrent = curr;
  721. this.updateWeeks();
  722. },
  723. /**
  724. * 更新weeks
  725. * @param {Boolean} isChange 是否使当前的weeks发生变化
  726. */
  727. updateWeeks(isChange = true, isInt = false) {
  728. let newFullDate = ''
  729. // 是否变动日期信息
  730. if (isChange) {
  731. // 如果目前处于打开状态也就是月日历时,将记录月份改为一号(这样可以在用户切换到任意月份时并折叠自动选中1号)
  732. let fullDate = this.FoldStatus === 'close' ? this.nowDate.fullDate : `${this.nowDate.year}-${this.nowDate.month}-${1}`
  733. newFullDate = this.cale.getDate(fullDate, this.swiperChangeDirection === 'next' ? +1 : -1, this.getDateType).fullDate;
  734. } else {
  735. newFullDate = this.cale.getDate(this.nowDate.fullDate, 0, this.getDateType).fullDate;
  736. }
  737. this.setDate(newFullDate)
  738. this.swiperCurrentChangeWeeks();
  739. if(!isInt) {
  740. this.monthSwitch();
  741. }
  742. },
  743. /**
  744. * swiperCurrent改变需要改动的weeks
  745. */
  746. swiperCurrentChangeWeeks() {
  747. if (this.slideSwitchMode === 'none') return;
  748. // 防止一次渲染过多数据,造成对nvue的卡顿
  749. this.$nextTick(() => {
  750. let nextDate = this.cale.getDate(this.nowDate.fullDate, +1, this.getDateType);
  751. let nextWeeks = this.cale._getWeek(nextDate.fullDate, false);
  752. let nextWeeksMonth = nextDate.month
  753. let preDate = this.cale.getDate(this.nowDate.fullDate, -1, this.getDateType);
  754. let preWeeks = this.cale._getWeek(preDate.fullDate, false);
  755. let preWeeksMonth = preDate.month
  756. if (this.swiperCurrent == 0) {
  757. this.weeks = nextWeeks;
  758. this.weeksMonth = nextWeeksMonth;
  759. this.nextWeeks = preWeeks;
  760. this.nextWeeksMonth = preWeeksMonth;
  761. } else if (this.swiperCurrent == 1) {
  762. this.nextWeeks = nextWeeks;
  763. this.nextWeeksMonth = nextWeeksMonth;
  764. this.preWeeks = preWeeks;
  765. this.preWeeksMonth = preWeeksMonth;
  766. } else {
  767. this.preWeeks = nextWeeks;
  768. this.preWeeksMonth = nextWeeksMonth;
  769. this.weeks = preWeeks;
  770. this.weeksMonth = preWeeksMonth;
  771. }
  772. })
  773. },
  774. // 点击折叠
  775. FoldClick() {
  776. this.FoldStatus = this.FoldStatus === 'open' ? 'close' : 'open';
  777. this.cale.resetFoldStatus(this.FoldStatus);
  778. // 重置当前weeks
  779. this.setDate(this.nowDate.fullDate);
  780. this.$nextTick(() => {
  781. // 重置左右切换的上下weeks
  782. if (this.slideSwitchMode !== 'none') {
  783. let nextDate = this.cale.getDate(this.nowDate.fullDate, +1, this.getDateType);
  784. let nextWeeks = this.cale._getWeek(nextDate.fullDate, false);
  785. let nextWeeksMonth = nextDate.month;
  786. let preDate = this.cale.getDate(this.nowDate.fullDate, -1, this.getDateType);
  787. let preWeeks = this.cale._getWeek(preDate.fullDate, false);
  788. let preWeeksMonth = preDate.month;
  789. if (this.swiperChangeDirection == 'next') {
  790. if (this.swiperCurrent == 0) {
  791. this.weeks = nextWeeks;
  792. this.weeksMonth = nextWeeksMonth;
  793. this.nextWeeks = preWeeks;
  794. this.nextWeeksMonth = preWeeksMonth;
  795. } else if (this.swiperCurrent == 1) {
  796. this.nextWeeks = nextWeeks;
  797. this.nextWeeksMonth = nextWeeksMonth;
  798. this.preWeeks = preWeeks;
  799. this.preWeeksMonth = preWeeksMonth;
  800. } else {
  801. this.preWeeks = nextWeeks;
  802. this.preWeeksMonth = nextWeeksMonth;
  803. this.weeks = preWeeks;
  804. this.weeksMonth = preWeeksMonth;
  805. }
  806. } else {
  807. if (this.swiperCurrent == 0) {
  808. this.nextWeeks = preWeeks;
  809. this.nextWeeksMonth = preWeeksMonth;
  810. this.weeks = nextWeeks;
  811. this.weeksMonth = nextWeeksMonth;
  812. } else if (this.swiperCurrent == 1) {
  813. this.preWeeks = preWeeks;
  814. this.preWeeksMonth = preWeeksMonth;
  815. this.nextWeeks = nextWeeks;
  816. this.nextWeeksMonth = nextWeeksMonth;
  817. } else {
  818. this.weeks = preWeeks;
  819. this.weeksMonth = preWeeksMonth;
  820. this.preWeeks = nextWeeks;
  821. this.preWeeksMonth = nextWeeksMonth;
  822. }
  823. }
  824. }
  825. })
  826. // fold切换事件
  827. this.$emit('foldSwitch', {
  828. type: this.type,
  829. status: this.FoldStatus
  830. })
  831. },
  832. // 初始化折叠
  833. initFold() {
  834. if (this.type === 'month' || this.type === 'week') {
  835. this.Fold = this.fold === null ? this.type !== 'month' : this.fold;
  836. this.FoldStatus = this.type !== 'month' ? 'close' : 'open';
  837. }
  838. }
  839. }
  840. }
  841. </script>
  842. <style lang="scss" scoped>
  843. $wu-bg-color-mask: rgba($color: #000000, $alpha: 0.4);
  844. $wu-border-color: #EDEDED;
  845. $wu-text-color: #333;
  846. $wu-bg-color-hover: #f1f1f1;
  847. $wu-font-size-base: 32rpx;
  848. $wu-text-color-placeholder: #808080;
  849. $wu-color-subtitle: #555555;
  850. $wu-text-color-grey: #999;
  851. .wu-calendar {
  852. /* #ifndef APP-NVUE */
  853. display: flex;
  854. /* #endif */
  855. flex-direction: column;
  856. }
  857. .wu-calendar__mask {
  858. position: fixed;
  859. bottom: 0;
  860. top: 0;
  861. left: 0;
  862. right: 0;
  863. background-color: $wu-bg-color-mask;
  864. transition-property: opacity;
  865. transition-duration: 0.3s;
  866. opacity: 0;
  867. /* #ifndef APP-NVUE */
  868. z-index: 99;
  869. /* #endif */
  870. }
  871. .wu-calendar--mask-show {
  872. opacity: 1;
  873. }
  874. .wu-calendar--fixed {
  875. position: fixed;
  876. /* #ifdef APP-NVUE */
  877. bottom: 0;
  878. /* #endif */
  879. left: 0;
  880. right: 0;
  881. transition-property: transform;
  882. transition-duration: 0.3s;
  883. transform: translateY(1080rpx);
  884. /* #ifndef APP-NVUE */
  885. bottom: calc(var(--window-bottom));
  886. z-index: 99;
  887. /* #endif */
  888. }
  889. .wu-calendar--ani-show {
  890. transform: translateY(0);
  891. }
  892. .wu-calendar__content {
  893. background-color: #fff;
  894. }
  895. .wu-calendar__header {
  896. position: relative;
  897. /* #ifndef APP-NVUE */
  898. display: flex;
  899. /* #endif */
  900. flex-direction: row;
  901. justify-content: center;
  902. align-items: center;
  903. border-bottom-color: $wu-border-color;
  904. border-bottom-style: solid;
  905. border-bottom-width: 2rpx;
  906. }
  907. .wu-calendar--fixed-top {
  908. /* #ifndef APP-NVUE */
  909. display: flex;
  910. /* #endif */
  911. height: 90rpx;
  912. flex-direction: row;
  913. justify-content: space-between;
  914. border-top-color: $wu-border-color;
  915. border-top-style: solid;
  916. border-top-width: 2rpx;
  917. }
  918. .wu-calendar--fixed-width {
  919. width: 100rpx;
  920. }
  921. .wu-calendar__backtoday {
  922. position: absolute;
  923. right: 0;
  924. top: 25rpx;
  925. padding: 0 10rpx;
  926. padding-left: 20rpx;
  927. height: 50rpx;
  928. line-height: 50rpx;
  929. font-size: 24rpx;
  930. border-top-left-radius: 50rpx;
  931. border-bottom-left-radius: 50rpx;
  932. color: $wu-text-color;
  933. background-color: $wu-bg-color-hover;
  934. &.vertical {
  935. top: 38rpx;
  936. }
  937. }
  938. .wu-calendar__header-text {
  939. text-align: center;
  940. width: 200rpx;
  941. font-size: $wu-font-size-base;
  942. color: $wu-text-color;
  943. }
  944. .wu-calendar__header-btn-box {
  945. /* #ifndef APP-NVUE */
  946. display: flex;
  947. /* #endif */
  948. flex-direction: row;
  949. align-items: center;
  950. justify-content: center;
  951. .wu-calendar__header-btn {
  952. width: 20rpx;
  953. height: 20rpx;
  954. }
  955. &.horizontal {
  956. width: 100rpx;
  957. height: 100rpx;
  958. }
  959. &.vertical {
  960. flex-direction: column;
  961. padding: 20rpx 0;
  962. }
  963. }
  964. .wu-calendar__header-btn {
  965. border-left-color: $wu-text-color-placeholder;
  966. border-left-style: solid;
  967. border-left-width: 4rpx;
  968. border-top-color: $wu-color-subtitle;
  969. border-top-style: solid;
  970. border-top-width: 4rpx;
  971. }
  972. .wu-calendar--left {
  973. transform: rotate(-45deg);
  974. }
  975. .wu-calendar--right {
  976. transform: rotate(135deg);
  977. }
  978. .wu-calendar--top {
  979. transform: rotate(45deg);
  980. }
  981. .wu-calendar--bottom {
  982. transform: rotate(225deg);
  983. }
  984. .wu-calendar__weeks {
  985. position: relative;
  986. /* #ifndef APP-NVUE */
  987. display: flex;
  988. /* #endif */
  989. flex-direction: row;
  990. padding: 0 8rpx;
  991. }
  992. .wu-calendar__weeks-day {
  993. flex: 1;
  994. /* #ifndef APP-NVUE */
  995. display: flex;
  996. /* #endif */
  997. flex-direction: column;
  998. justify-content: center;
  999. align-items: center;
  1000. height: 90rpx;
  1001. border-bottom-color: #F5F5F5;
  1002. border-bottom-style: solid;
  1003. border-bottom-width: 2rpx;
  1004. }
  1005. .wu-calendar__weeks-day-text {
  1006. font-size: 28rpx;
  1007. }
  1008. .wu-calendar__box {
  1009. position: relative;
  1010. }
  1011. .wu-calendar__weeks_container {
  1012. transition: height 0.2s linear;
  1013. }
  1014. .wu-calendar__fold {
  1015. /* #ifndef APP-NVUE */
  1016. display: flex;
  1017. /* #endif */
  1018. flex-direction: row;
  1019. justify-content: center;
  1020. }
  1021. </style>