index.tsx 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. import { Button, Calendar, Icon, Image, Popup, Tag, Toast } from 'vant'
  2. import { defineComponent } from 'vue'
  3. import dayjs from 'dayjs'
  4. import styles from './index.module.less'
  5. import IconArrow from '@/common/images/icon_arrow.png'
  6. import IconClock from '@/common/images/icon_clock.png'
  7. export default defineComponent({
  8. name: 'calendar',
  9. props: {
  10. calendarDate: {
  11. type: Date,
  12. default: () => new Date()
  13. },
  14. // 选中的数据
  15. selectList: {
  16. type: Array,
  17. default: []
  18. },
  19. // 接口数据
  20. list: {
  21. type: Object,
  22. default: {}
  23. },
  24. /**
  25. * 每天选择课程最大数
  26. */
  27. maxDays: {
  28. type: [Number, String],
  29. default: 0
  30. },
  31. /**
  32. * 点击并选中任意日期时触发
  33. */
  34. onSelect: {
  35. type: Function,
  36. default: (date: Date) => {}
  37. },
  38. /**
  39. * 上一月,不能小于当月
  40. */
  41. prevMonth: {
  42. type: Function,
  43. default: (date: Date) => {}
  44. },
  45. /**
  46. * 下一月,暂无限制
  47. */
  48. nextMonth: {
  49. type: Function,
  50. default: (date: Date) => {}
  51. },
  52. /**
  53. * 日期选择结束时触发
  54. */
  55. selectDay: {
  56. type: Function,
  57. default: (obj: any) => {}
  58. }
  59. },
  60. data() {
  61. return {
  62. minDate: new Date(),
  63. maxDate: new Date(),
  64. currentDate: new Date(), // 当前日历日期
  65. subtitle: '',
  66. show: false,
  67. dayList: [],
  68. selectDays: [] as any
  69. }
  70. },
  71. computed: {
  72. arrowStatus() {
  73. // 上月箭头状态
  74. return !dayjs().isBefore(dayjs(this.currentDate), 'month')
  75. },
  76. selectDayTitle() {
  77. // 选中日期标题
  78. return dayjs(this.currentDate).format('YYYY-MM-DD')
  79. },
  80. isPrevDay() {
  81. // 是否可以点击上一天
  82. return dayjs(this.currentDate)
  83. .subtract(1, 'day')
  84. .isBefore(dayjs(this.minDate), 'day')
  85. },
  86. isNextDay() {
  87. // 是否可以点击下一天
  88. return dayjs(this.currentDate)
  89. .add(1, 'day')
  90. .isAfter(dayjs(this.maxDate), 'day')
  91. }
  92. },
  93. mounted() {
  94. // 初始化标题和最大显示日期
  95. this.subtitle = dayjs().format('YYYY年MM月')
  96. this.maxDate = dayjs().endOf('month').toDate()
  97. this.minDate = dayjs().add(1, 'day').toDate()
  98. // 初始化日历
  99. // console.log(this.list, 323, this.maxDays)
  100. },
  101. methods: {
  102. formatter(date: any) {
  103. const dateStr = dayjs(date.date).format('YYYY-MM-DD')
  104. const dateObj = this.list[dateStr]
  105. if (dateObj && dateObj.fullCourse) {
  106. date.bottomInfo = '满'
  107. date.className = 'full'
  108. date.type = 'disabled'
  109. }
  110. date.type = date.type === 'selected' ? '' : date.type
  111. return date
  112. },
  113. onPrevMonth() {
  114. // 上一月
  115. if (this.arrowStatus) return
  116. const tempDate = dayjs(this.currentDate).subtract(1, 'month')
  117. this._monthChange(tempDate)
  118. this.prevMonth && this.prevMonth(this.minDate)
  119. },
  120. onNextMonth() {
  121. // 下一月
  122. const tempDate = dayjs(this.currentDate).add(1, 'month')
  123. this._monthChange(tempDate)
  124. this.nextMonth && this.nextMonth(this.minDate)
  125. },
  126. _monthChange(date: any) {
  127. // 月份改变
  128. // 需要判断是否是当月,需要单独处理最小时间
  129. const currentMinDate = dayjs().add(1, 'day').toDate()
  130. const monthMinDate = date.startOf('month').toDate()
  131. this.minDate = dayjs(currentMinDate).isAfter(monthMinDate)
  132. ? currentMinDate
  133. : monthMinDate
  134. // this.minDate = date.startOf('month').toDate()
  135. this.maxDate = date.endOf('month').toDate()
  136. this.currentDate = date.toDate()
  137. this.$emit('update:calendarDate', date.toDate())
  138. this.subtitle = date.format('YYYY年MM月')
  139. },
  140. onSelectDay(item: any) {
  141. // 选择某个时间段
  142. const index = this.selectDays.findIndex((days: any) => {
  143. return days.startTime === item.startTime
  144. })
  145. if (this.selectDays.length < this.maxDays || index !== -1) {
  146. const index = this.selectDays.findIndex(
  147. (days: any) => days.startTime === item.startTime
  148. )
  149. item.checked = !item.checked
  150. if (index === -1) {
  151. this.selectDays.push({ ...item })
  152. } else {
  153. this.selectDays.splice(index, 1)
  154. }
  155. } else {
  156. Toast('最多选择' + this.maxDays + '个时间段')
  157. }
  158. },
  159. onPrevDay() {
  160. // 获取上一天的数据
  161. const tempDate = dayjs(this.currentDate).subtract(1, 'day')
  162. this._dayChange(tempDate.toDate())
  163. },
  164. onNextDay() {
  165. // 获取下一天的数据
  166. const tempDate = dayjs(this.currentDate).add(1, 'day')
  167. this._dayChange(tempDate.toDate())
  168. },
  169. onDateSelect(date: any) {
  170. console.log(date)
  171. // 选择日历上某一个日期
  172. this.selectDays = [...this.selectList] // 初始化用户选中的值
  173. this._dayChange(date)
  174. this.onSelect && this.onSelect(date)
  175. },
  176. _dayChange(date: Date) {
  177. const dateStr = dayjs(date).format('YYYY-MM-DD')
  178. let dataList = (this.list[dateStr] && this.list[dateStr].courseTime) || []
  179. dataList.forEach((item: any) => {
  180. item.start = dayjs(item.startTime).format('HH:mm')
  181. item.end = dayjs(item.endTime).format('HH:mm')
  182. const isExist = this.selectDays?.some(
  183. (course: any) => course.startTime === item.startTime
  184. )
  185. item.checked = isExist
  186. })
  187. this.dayList = dataList
  188. this.currentDate = date // 更新当前日期
  189. this.$emit('update:calendarDate', date)
  190. this.show = true
  191. }
  192. },
  193. render() {
  194. return (
  195. <>
  196. <Calendar
  197. class={styles.calendar}
  198. showTitle={false}
  199. poppable={false}
  200. showConfirm={false}
  201. showMark={false}
  202. firstDayOfWeek={1}
  203. rowHeight={56}
  204. minDate={this.minDate}
  205. maxDate={this.maxDate}
  206. color="var(--van-primary)"
  207. formatter={this.formatter}
  208. onSelect={this.onDateSelect}
  209. v-slots={{
  210. subtitle: () => (
  211. <div class={styles.subtitle}>
  212. <Icon
  213. name={IconArrow}
  214. size={22}
  215. class={this.arrowStatus && styles.disabled}
  216. onClick={this.onPrevMonth}
  217. />
  218. <span>{this.subtitle}</span>
  219. <Icon
  220. name={IconArrow}
  221. size={22}
  222. class={styles.right}
  223. onClick={this.onNextMonth}
  224. />
  225. </div>
  226. )
  227. // 'bottom-info': (date: any) => <span>{date.type}</span>
  228. }}
  229. />
  230. <Popup show={this.show} class={styles.calenderPopup}>
  231. <div class={styles.popup}>
  232. <div class={styles.title}>
  233. {/* <Button
  234. type="primary"
  235. plain
  236. style={{ border: 0 }}
  237. size="small"
  238. disabled={this.isPrevDay}
  239. onClick={this.onPrevDay}
  240. >
  241. 上一日
  242. </Button> */}
  243. <span>{this.selectDayTitle}</span>
  244. {/* <Button
  245. type="primary"
  246. plain
  247. style={{ border: 0 }}
  248. size="small"
  249. disabled={this.isNextDay}
  250. onClick={this.onNextDay}
  251. >
  252. 下一日
  253. </Button> */}
  254. </div>
  255. <div class={styles.container}>
  256. {this.dayList.map((item: any) => (
  257. <div>
  258. <Tag
  259. round
  260. class={[styles.tag, item.checked ? styles.active : '']}
  261. size="large"
  262. plain
  263. onClick={() => this.onSelectDay(item)}
  264. >
  265. {item.start}~{item.end}
  266. </Tag>
  267. </div>
  268. ))}
  269. {this.dayList.length <= 0 && (
  270. <div class={styles.noDay}>
  271. <Image src={IconClock} class={styles.clock} fit="cover" />
  272. <span>今日已约满</span>
  273. </div>
  274. )}
  275. </div>
  276. <div class={styles.dayBtn}>
  277. <Button
  278. round
  279. plain
  280. style={{ width: '33.33%', marginRight: '10px' }}
  281. onClick={() => {
  282. this.show = false
  283. this.selectDays = []
  284. }}
  285. >
  286. 取消
  287. </Button>
  288. <Button
  289. type="primary"
  290. block
  291. round
  292. disabled={!(this.selectDays.length > 0)}
  293. onClick={() => {
  294. this.selectDay && this.selectDay(this.selectDays)
  295. this.show = false
  296. }}
  297. >
  298. 确认
  299. </Button>
  300. </div>
  301. </div>
  302. </Popup>
  303. </>
  304. )
  305. }
  306. })