calendar.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. <template>
  2. <view class="zzx-calendar">
  3. <view class="calendar-heander">
  4. <text @click="daysPre()" class="text-xl text-gray" style="margin-right: 30px;"> < </text>
  5. {{timeStr}}
  6. <text class="text-xl text-gray" @click="daysNext()" style="margin-left: 30px;"> > </text>
  7. <view class="back-today" @click="goback" v-if="showBack" style="height: 48rpx;line-height: 48rpx;width: 70rpx;margin-top: 1.5rpx;font-size:24rpx;">
  8. 今日
  9. </view>
  10. </view>
  11. <view class="calendar-weeks margin-top-sm">
  12. <view class="calendar-week border-bottom-dashed border-top-dashed" v-for="(week, index) in weeks" :key="index">
  13. {{week}}
  14. </view>
  15. </view>
  16. <view class="calendar-content margin-top-sm">
  17. <swiper class="calendar-swiper" :style="{
  18. width: '100%',
  19. height: sheight
  20. }" :indicator-dots="false" :autoplay="false" :duration="duration" :current="current" @change="changeSwp" :circular="true">
  21. <swiper-item class="calendar-item" v-for="sitem in swiper" :key="sitem">
  22. <view class="calendar-days">
  23. <template v-if="sitem === current">
  24. <view class="calendar-day" v-for="(item,index) in days" :key="index"
  25. :class="{
  26. 'day-hidden': !item.show
  27. }" @click="clickItem(item)">
  28. <!-- 显示打卡的时间并设置样式 -->
  29. <view v-if="item.info" class="date solid" :class="[
  30. item.isToday ? todayClass : '',
  31. item.fullDate === selectedDate ? 'color-text color-bg' : '',
  32. item.info.id != null ? 'round line-blue ' : 'line-white text-black',
  33. ]">
  34. {{item.info.id != null || item.fullDate === selectedDate ? item.time.getDate() : item.time.getDate()}}
  35. </view>
  36. <!-- 显示其他的时间和当前时间 -->
  37. <view v-else
  38. class="date"
  39. :class="[item.isToday ? todayClass : '',item.fullDate === selectedDate ? 'color-text color-bg' : 'text-gray']"
  40. >
  41. {{item.time.getDate()}}
  42. </view>
  43. </view>
  44. </template>
  45. <template v-else>
  46. <template v-if="current - sitem === 1 || current-sitem ===-2">
  47. <view class="calendar-day" v-for="(item,index) in predays" :key="index"
  48. :class="{
  49. 'day-hidden': !item.show
  50. }">
  51. <view
  52. class="date"
  53. :class="[
  54. item.isToday ? todayClass : ''
  55. ]"
  56. >
  57. {{item.time.getDate()}}
  58. </view>
  59. </view>
  60. </template>
  61. <template v-else>
  62. <view class="calendar-day" v-for="(item,index) in nextdays" :key="index"
  63. :class="{
  64. 'day-hidden': !item.show
  65. }">
  66. <view
  67. class="date"
  68. :class="[
  69. item.isToday ? todayClass : ''
  70. ]"
  71. >
  72. {{item.time.getDate()}}
  73. </view>
  74. </view>
  75. </template>
  76. </template>
  77. </view>
  78. </swiper-item>
  79. </swiper>
  80. <view class="mode-change" @click="changeMode" style="position: relative;">
  81. <view style="position: absolute;right: -1px;bottom: -4px;">
  82. <image v-if="showImage" src="../../static/other/calendar.png" style="width: 40rpx;height: 40rpx;border-radius: 20rpx 0 20rpx 0;"></image>
  83. <image v-else="showImage" src="../../static/other/calendarShow.png" style="width: 40rpx;height: 40rpx;border-radius: 20rpx 0 20rpx 0;"></image>
  84. </view>
  85. </view>
  86. </view>
  87. </view>
  88. </template>
  89. <script>
  90. import {gegerateDates, dateEqual,formatDate} from './generateDates.js';
  91. export default {
  92. props: {
  93. duration: {
  94. type: Number,
  95. default: 500
  96. },
  97. dotList: {
  98. type: Array, /// 打点日期列表
  99. default() {
  100. return [
  101. ]
  102. }
  103. },
  104. showBack: {
  105. type: Boolean, // 是否返回今日
  106. default: false
  107. },
  108. todayClass: {
  109. type: String, // 今日的自定义样式class
  110. default: 'is-today'
  111. },
  112. checkedClass: {
  113. type: String, // 选中日期的样式class
  114. default: 'is-checked'
  115. },
  116. dotStyle: {
  117. type: Object, // 打点日期的自定义样式
  118. default() {
  119. return {
  120. background: '#c6c6c6'
  121. }
  122. }
  123. }
  124. },
  125. watch:{
  126. dotList: function(newvalue){
  127. const days = this.days.slice(0);
  128. newvalue.forEach(item => {
  129. const index = days.findIndex(ditem => ditem.fullDate === item.date);
  130. if (index > 0) {
  131. days[index].info = item;
  132. }
  133. });
  134. this.days = days;
  135. let isweek = false;
  136. isweek = !!this.days.find(item => item.fullDate === this.selectedDate);
  137. let d = new Date(this.currentYear, this.currentMonth - 1, this.currentDate)
  138. const sel = new Date(this.selectedDate.replace('-', '/').replace('-', '/'));
  139. const isMonth = sel.getFullYear() === this.currentYear && (sel.getMonth() + 1) === this.currentMonth;
  140. if ((this.selectedDate && isMonth) || isweek) {
  141. d = new Date(this.selectedDate.replace('-', '/').replace('-', '/'))
  142. }
  143. this.initDate(d)
  144. }
  145. },
  146. computed: {
  147. sheight() {
  148. // 根据年月判断有多少行
  149. // 判断该月有多少天
  150. let h = '70rpx';
  151. if (!this.weekMode) {
  152. const d = new Date(this.currentYear, this.currentMonth, 0);
  153. const days = d.getDate(); // 判断本月有多少天
  154. let day = new Date(d.setDate(1)).getDay();
  155. if (day === 0) {
  156. day = 7;
  157. }
  158. const pre = 8 - day;
  159. const rows = Math.ceil((days-pre) / 7) + 1;
  160. h = 70 * rows + 'rpx'
  161. }
  162. return h
  163. },
  164. timeStr() {
  165. let str = '';
  166. const d = new Date(this.currentYear, this.currentMonth - 1, this.currentDate);
  167. const y = d.getFullYear();
  168. const m = (d.getMonth()+1) <=9 ? `0${d.getMonth()+1}` : d.getMonth()+1;
  169. str = `${y}年${m}月`;
  170. return str;
  171. },
  172. predays() {
  173. let pres = [];
  174. if (this.weekMode) {
  175. const d = new Date(this.currentYear, this.currentMonth - 1,this.currentDate)
  176. d.setDate(d.getDate() - 7);
  177. pres = gegerateDates(d, 'week')
  178. } else {
  179. const d = new Date(this.currentYear, this.currentMonth - 2,1)
  180. pres = gegerateDates(d, 'month')
  181. }
  182. return pres;
  183. },
  184. nextdays() {
  185. let nexts = [];
  186. if (this.weekMode) {
  187. const d = new Date(this.currentYear, this.currentMonth - 1,this.currentDate)
  188. d.setDate(d.getDate() + 7);
  189. nexts = gegerateDates(d, 'week')
  190. } else {
  191. const d = new Date(this.currentYear, this.currentMonth,1)
  192. nexts = gegerateDates(d, 'month')
  193. }
  194. return nexts;
  195. }
  196. },
  197. data() {
  198. return {
  199. weeks: ['一', '二', '三', '四', '五', '六', '日'],
  200. current: 1,
  201. currentYear: '',
  202. currentMonth: '',
  203. currentDate: '',
  204. days: [],
  205. weekMode: true,
  206. showImage:true,
  207. swiper: [0,1,2],
  208. // dotList: [], // 打点的日期列表
  209. selectedDate: formatDate(new Date(), 'yyyy-MM-dd'),
  210. selectedItem:null,
  211. };
  212. },
  213. methods: {
  214. changeSwp(e) {
  215. // console.log(e);
  216. const pre = this.current;
  217. const current = e.target.current;
  218. /* 根据前一个减去目前的值我们可以判断是下一个月/周还是上一个月/周
  219. *current - pre === 1, -2时是下一个月/周
  220. *current -pre === -1, 2时是上一个月或者上一周
  221. */
  222. this.current = current;
  223. if (current - pre === 1 || current - pre === -2) {
  224. this.daysNext();
  225. } else {
  226. this.daysPre();
  227. }
  228. },
  229. // 初始化日历的方法
  230. initDate(cur) {
  231. let date = ''
  232. if (cur) {
  233. date = new Date(cur)
  234. } else {
  235. date = new Date()
  236. }
  237. this.currentDate = date.getDate() // 今日日期 几号
  238. this.currentYear = date.getFullYear() // 当前年份
  239. this.currentMonth = date.getMonth() + 1 // 当前月份
  240. this.currentWeek = date.getDay() === 0 ? 7 : date.getDay() // 1...6,0 // 星期几
  241. const nowY = new Date().getFullYear() // 当前年份
  242. const nowM = new Date().getMonth() + 1
  243. const nowD = new Date().getDate() // 今日日期 几号
  244. const nowW = new Date().getDay();
  245. // this.selectedDate = formatDate(new Date(), 'yyyy-MM-dd')
  246. this.days = [];
  247. let days = [];
  248. if (this.weekMode) {
  249. days = gegerateDates(date, 'week');
  250. // this.selectedDate = days[0].fullDate;
  251. } else {
  252. days = gegerateDates(date, 'month');
  253. // const sel = new Date(this.selectedDate.replace('-', '/').replace('-', '/'));
  254. // const isMonth = sel.getFullYear() === this.currentYear && (sel.getMonth() + 1) === this.currentMonth;
  255. // if(!isMonth) {
  256. // this.selectedDate = formatDate(new Date(this.currentYear, this.currentMonth-1,1), 'yyyy-MM-dd')
  257. // }
  258. }
  259. days.forEach(day => {
  260. const dot = this.dotList.find(item => {
  261. return dateEqual(item.date, day.fullDate);
  262. })
  263. if (dot) {
  264. day.info = dot;
  265. }
  266. })
  267. this.days = days;
  268. },
  269. // 上一个
  270. daysPre () {
  271. if (this.weekMode) {
  272. const d = new Date(this.currentYear, this.currentMonth - 1,this.currentDate);
  273. d.setDate(d.getDate() - 7);
  274. this.initDate(d);
  275. } else {
  276. const d = new Date(this.currentYear, this.currentMonth -2, 1);
  277. this.initDate(d);
  278. }
  279. },
  280. // 下一个
  281. daysNext () {
  282. if (this.weekMode) {
  283. const d = new Date(this.currentYear, this.currentMonth - 1,this.currentDate);
  284. d.setDate(d.getDate() + 7);
  285. this.initDate(d);
  286. } else {
  287. const d = new Date(this.currentYear, this.currentMonth, 1);
  288. this.initDate(d);
  289. }
  290. },
  291. changeMode() {
  292. const premode = this.weekMode;
  293. let isweek = false;
  294. this.showImage = !this.showImage;
  295. if (premode) {
  296. isweek = !!this.days.find(item => item.fullDate === this.selectedDate);
  297. }
  298. this.weekMode =!this.weekMode;
  299. let d = new Date(this.currentYear, this.currentMonth - 1, this.currentDate)
  300. const sel = new Date(this.selectedDate.replace('-', '/').replace('-', '/'));
  301. const isMonth = sel.getFullYear() === this.currentYear && (sel.getMonth() + 1) === this.currentMonth;
  302. if ((this.selectedDate && isMonth) || isweek) {
  303. d = new Date(this.selectedDate.replace('-', '/').replace('-', '/'))
  304. }
  305. this.initDate(d)
  306. },
  307. // 点击日期
  308. clickItem(e) {
  309. this.selectedDate = e.fullDate;
  310. this.selectedItem = e;
  311. this.$emit('selected-change', e);
  312. },
  313. goback() {
  314. const d = new Date();
  315. this.initDate(d);
  316. uni.$emit('clickToday');
  317. var time =this.intervalTime.getTime();
  318. var data ={"fullDate":time,isToday:true,show:true,"info":undefined};
  319. this.clickItem(data);
  320. }
  321. },
  322. created() {
  323. this.initDate();
  324. },
  325. mounted() {
  326. }
  327. }
  328. </script>
  329. <style lang="scss" scoped>
  330. .zzx-calendar {
  331. width: 100%;
  332. height: auto;
  333. .calendar-heander {
  334. text-align: center;
  335. height: 60upx;
  336. line-height: 60upx;
  337. position: relative;
  338. font-size: 30upx;
  339. padding-top: 10rpx;
  340. .back-today {
  341. position: absolute;
  342. right: 0;
  343. width: 120upx;
  344. height: 35upx;
  345. line-height: 35upx;
  346. font-size: 20upx;
  347. top: 15upx;
  348. border-radius: 15upx 0 0 15upx;
  349. color: #ffffff;
  350. background-color: #0A8CD5;
  351. }
  352. }
  353. .calendar-weeks {
  354. width: 100%;
  355. display: flex;
  356. flex-flow:row nowrap;
  357. height: 60upx;
  358. line-height: 60upx;
  359. justify-content: center;
  360. align-items: center;
  361. font-size: 30upx;
  362. .calendar-week {
  363. width: calc(100% / 7);
  364. height: 100%;
  365. text-align: center;
  366. }
  367. .border-bottom-dashed{
  368. border-bottom: 0.5px dashed #ddd;
  369. }
  370. .border-top-dashed{
  371. border-top: 0.5px dashed #ddd;
  372. }
  373. }
  374. swiper {
  375. width: 100%;
  376. height: 60upx;
  377. }
  378. .calendar-content {
  379. min-height: 60upx;
  380. }
  381. .calendar-swiper {
  382. min-height: 70upx;
  383. transition: height ease-out 0.3s;
  384. }
  385. .calendar-item {
  386. margin: 0;
  387. padding: 0;
  388. height: 100%;
  389. }
  390. .calendar-days {
  391. display: flex;
  392. flex-flow: row wrap;
  393. width: 100%;
  394. height: 100%;
  395. overflow: hidden;
  396. font-size: 28upx;
  397. .calendar-day {
  398. width: calc(100% / 7);
  399. height: 70upx;
  400. text-align: center;
  401. display: flex;
  402. flex-flow: column nowrap;
  403. justify-content: flex-start;
  404. align-items: center;
  405. }
  406. }
  407. .day-hidden {
  408. visibility: hidden;
  409. }
  410. .mode-change {
  411. display: flex;
  412. justify-content: center;
  413. margin-top:10upx;
  414. .mode-arrow-top {
  415. width: 0;
  416. height:0;
  417. border-left: 12upx solid transparent;
  418. border-right: 12upx solid transparent;
  419. border-bottom: 10upx solid #0A8CD5;
  420. }
  421. .mode-arrow-bottom {
  422. width: 0;
  423. height:0;
  424. border-left: 12upx solid transparent;
  425. border-right: 12upx solid transparent;
  426. border-top: 10upx solid #0A8CD5;
  427. }
  428. }
  429. .is-today {
  430. background: #0A8CD5 !important;
  431. // border: 1upx solid #FF6633;
  432. border-radius: 50%;
  433. color: white !important;
  434. }
  435. .is-checked {
  436. background: #FF6633;
  437. color: #ffffff;
  438. }
  439. .date {
  440. width: 50upx;
  441. height: 50upx;
  442. line-height: 50upx;
  443. margin: 0 auto;
  444. border-radius: 50upx;
  445. }
  446. .dot-show {
  447. margin-top:4upx;
  448. width: 10upx;
  449. height: 10upx;
  450. background: #c6c6c6;
  451. border-radius: 10upx;
  452. }
  453. .color-text{
  454. color: #0A8CD5;
  455. }
  456. .color-bg{
  457. background-color: #C6E6FF;
  458. }
  459. .text-gray{
  460. color:#9e9e9e;
  461. }
  462. .line-blue::after, .lines-blue::after {
  463. border-color: #C6E6FF;
  464. }
  465. .text-black{
  466. color: #101010;
  467. }
  468. }
  469. </style>