ScheduleCalendarWeek.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. <template>
  2. <div class="l-flex--col c-week-calendar">
  3. <div class="l-flex__none l-flex c-week-calendar__header">
  4. <div class="l-flex__none o-time header" />
  5. <div class="l-flex__fill l-flex">
  6. <div
  7. v-for="item in headers"
  8. :key="item.info"
  9. class="l-flex__auto u-readonly"
  10. >
  11. <div class="c-week-calendar__week">{{ item.day }}</div>
  12. <div class="c-week-calendar__info">{{ item.info }}</div>
  13. </div>
  14. </div>
  15. </div>
  16. <div class="l-flex__auto u-overflow-y--auto">
  17. <div class="l-flex">
  18. <div
  19. v-once
  20. class="l-flex__none l-flex--col c-week-calendar__time"
  21. >
  22. <div
  23. v-for="(val, hour) in 24"
  24. :key="hour"
  25. class="l-flex__none o-time"
  26. >
  27. {{ hour.toString().padStart(2, '0') }}:00
  28. </div>
  29. </div>
  30. <div class="l-flex__fill l-flex c-week-calendar__right">
  31. <div
  32. v-for="day in week"
  33. :key="day.value"
  34. class="l-flex__fill l-flex--col c-week-calendar__day"
  35. :class="day.status"
  36. >
  37. <template v-for="item in day.items">
  38. <div
  39. v-if="item.event"
  40. :key="item.key"
  41. class="l-flex__none c-week-calendar__item"
  42. :class="item.status"
  43. :style="item.style"
  44. @click="onClick(item.event)"
  45. >
  46. <event-item
  47. :item="item"
  48. :editable="editable"
  49. @remove="$listeners.remove"
  50. />
  51. </div>
  52. <div
  53. v-if="!item.event"
  54. :key="item.key"
  55. class="l-flex__none c-week-calendar__item"
  56. :class="item.status"
  57. :style="item.style"
  58. @dblclick="onAdd(item)"
  59. />
  60. </template>
  61. </div>
  62. </div>
  63. </div>
  64. </div>
  65. </div>
  66. </template>
  67. <script>
  68. import { EventFreq } from '@/constant'
  69. import {
  70. ONE_DAY,
  71. toDate,
  72. toDateStr,
  73. toTimeStr,
  74. pickMin,
  75. pickMax,
  76. isOverDay,
  77. isHitOverDayTime,
  78. correctEndTime
  79. } from '@/utils/event'
  80. import EventItem from './EventItemWeek'
  81. const Week = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
  82. export default {
  83. name: 'ScheduleCalendarWeek',
  84. components: {
  85. EventItem
  86. },
  87. props: {
  88. editable: {
  89. type: [Boolean, String],
  90. default: false
  91. },
  92. weeks: {
  93. type: Array,
  94. default: null
  95. },
  96. cursor: {
  97. type: Number,
  98. default: 0
  99. }
  100. },
  101. computed: {
  102. currWeek () {
  103. return this.weeks[this.cursor]
  104. },
  105. headers () {
  106. return this.currWeek.map(({ value }, i) => {
  107. return { day: value, info: Week[i] }
  108. })
  109. },
  110. week () {
  111. return this.currWeek.map(this.transform)
  112. }
  113. },
  114. methods: {
  115. transform (day) {
  116. return { ...day, items: this.createItems(day) }
  117. },
  118. createItems ({ date, events }) {
  119. const maxDate = new Date(date.getTime() + ONE_DAY)
  120. const week = date.getDay()
  121. const arr = []
  122. for (let i = 0; i < events.length; i++) {
  123. const eventProxy = events[i]
  124. switch (eventProxy.origin.freq) {
  125. case EventFreq.ONCE:
  126. arr.push(this.createOnceItem(eventProxy, date, maxDate))
  127. break
  128. case EventFreq.WEEKLY:
  129. if (isHitOverDayTime(eventProxy.origin, date)) {
  130. arr.push(this.createRemainderWeeklyItem(eventProxy, date, maxDate))
  131. }
  132. if (eventProxy.origin.byDay.includes(week)) {
  133. arr.push(this.createWeeklyItem(eventProxy, date, maxDate))
  134. }
  135. break
  136. default:
  137. break
  138. }
  139. }
  140. const items = []
  141. let minDate = toDate(date)
  142. if (arr.length) {
  143. arr.sort((a, b) => a.minDate - b.minDate)
  144. for (let i = 0; i < arr.length; i++) {
  145. if (arr[i].minDate > minDate) {
  146. this.createPlaceholdeItem(items, minDate, arr[i].minDate)
  147. }
  148. items.push(arr[i])
  149. minDate = arr[i].maxDate
  150. }
  151. }
  152. if (this.editable && minDate < maxDate) {
  153. this.createPlaceholdeItem(items, minDate, maxDate)
  154. }
  155. return items
  156. },
  157. createPlaceholdeItem (items, minDate, maxDate) {
  158. const now = new Date()
  159. if (maxDate > now && minDate < now) {
  160. items.push(this.createItem(null, minDate, now))
  161. minDate = now
  162. }
  163. items.push(this.createItem(null, minDate, maxDate))
  164. },
  165. createItem (eventProxy, minDate, maxDate) {
  166. const duration = (maxDate - minDate) / 1000
  167. return {
  168. key: Math.random().toString(16).slice(2),
  169. event: eventProxy,
  170. status: eventProxy
  171. ? duration <= 7200 ? 'hold hidden' : 'hold'
  172. : maxDate > Date.now()
  173. ? this.editable ? 'free' : 'empty'
  174. : 'disabled',
  175. placement: duration <= 7200 ? 'right-start' : 'right-end',
  176. minDate,
  177. maxDate,
  178. startTime: toTimeStr(minDate),
  179. endTime: toTimeStr(maxDate),
  180. duration,
  181. style: { height: `${duration * 0.02}px` }
  182. }
  183. },
  184. createOnceItem (eventProxy, minDate, maxDate) {
  185. const { start, until } = eventProxy.origin
  186. minDate = pickMax(toDate(start), minDate)
  187. maxDate = pickMin(toDate(until), maxDate)
  188. return this.createItem(eventProxy, minDate, maxDate)
  189. },
  190. createRemainderWeeklyItem (eventProxy, minDate, maxDate) {
  191. const { until, endTime } = eventProxy.origin
  192. maxDate = pickMin(toDate(`${toDateStr(minDate)} ${correctEndTime(endTime)}`), pickMin(toDate(until), maxDate))
  193. return this.createItem(eventProxy, minDate, maxDate)
  194. },
  195. createWeeklyItem (eventProxy, minDate, maxDate) {
  196. const { start, until, startTime, endTime } = eventProxy.origin
  197. minDate = pickMax(toDate(`${toDateStr(minDate)} ${startTime}`), pickMax(toDate(start), minDate))
  198. maxDate = pickMin(toDate(`${toDateStr(minDate, isOverDay(eventProxy.origin) ? 1 : 0)} ${correctEndTime(endTime)}`), pickMin(toDate(until), maxDate))
  199. return this.createItem(eventProxy, minDate, maxDate)
  200. },
  201. onClick (event) {
  202. this.$emit('edit', event)
  203. },
  204. onAdd ({ minDate, maxDate }) {
  205. this.$emit('add', {
  206. freq: EventFreq.ONCE,
  207. start: `${toDateStr(minDate)} ${toTimeStr(minDate)}`,
  208. until: `${toDateStr(maxDate)} 00:00:00`
  209. })
  210. }
  211. }
  212. }
  213. </script>
  214. <style lang="scss" scoped>
  215. .c-week-calendar {
  216. position: relative;
  217. color: $black;
  218. font-size: 16px;
  219. border: 1px solid $info;
  220. &__header {
  221. padding: 10px 0;
  222. text-align: center;
  223. border-bottom: 1px solid $info;
  224. }
  225. &__week {
  226. font-size: 20px;
  227. font-weight: bold;
  228. }
  229. &__info {
  230. color: $info;
  231. }
  232. &__time {
  233. align-self: flex-start;
  234. border-right: 1px solid $info;
  235. }
  236. &__right {
  237. background-size: 100% 72px;
  238. background-image: linear-gradient(
  239. to bottom,
  240. transparent 0,
  241. transparent 71px,
  242. $info 71px
  243. );
  244. }
  245. &__day {
  246. user-select: none;
  247. &.enabled {
  248. .c-week-calendar__item.disabled {
  249. background-color: rgba($gray, 0.5);
  250. cursor: not-allowed;
  251. }
  252. }
  253. &.invalid {
  254. background-color: lighten($gray--light, 4%);
  255. cursor: not-allowed;
  256. }
  257. & + & {
  258. border-left: 1px solid $info;
  259. }
  260. }
  261. .c-week-calendar__item {
  262. &.hidden {
  263. overflow: hidden;
  264. }
  265. &.hold {
  266. margin: 0 6px;
  267. color: #fff;
  268. text-align: center;
  269. border-radius: 4px;
  270. background-color: $blue;
  271. transition: background-color 0.4s;
  272. }
  273. &.hold,
  274. &.free {
  275. cursor: pointer;
  276. }
  277. &.free:hover {
  278. margin: 0 6px;
  279. border-radius: 4px;
  280. background-color: rgba($blue, 0.5);
  281. }
  282. }
  283. }
  284. .o-time {
  285. display: inline-block;
  286. width: 70px;
  287. height: 72px;
  288. text-align: center;
  289. &.header {
  290. height: auto;
  291. }
  292. }
  293. </style>