index.tsx 11 KB

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