calendar.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. <template>
  2. <view class="calendar-wrapper">
  3. <view class="week">
  4. <view class="week-day" v-for="(item, index) in weekDay" :key="index">{{ item }}</view>
  5. </view>
  6. <swiper style="flex: 1;" :vertical="true" :style="{height:height}" :circular="true" @change="swiperChange" :current="swiperCurrent" @animationfinish="animationfinish">
  7. <swiper-item style="height: 100%;" v-for="(items,i) in 3" :key="i">
  8. <view :class="{ hide: !monthOpen }" class="content" >
  9. <view :style="{ top: positionTop + 'rpx' }" class="days">
  10. <view class="item" v-for="(item, index) in dates" :key="index">
  11. <view
  12. class="day"
  13. @click="selectOne(item, $event)"
  14. :class="{
  15. choose: choose == `${item.year}-${item.month}-${item.date}`&&item.isCurM,
  16. nolm: !item.isCurM,
  17. today: isToday(item.year, item.month, item.date),
  18. isWorkDay: isWorkDay(item.year, item.month, item.date)
  19. }"
  20. >
  21. {{ Number(item.date) }}
  22. </view>
  23. <view class="markDay" v-if="isMarkDay(item.year, item.month, item.date)&&item.isCurM"></view>
  24. </view>
  25. </view>
  26. </view>
  27. </swiper-item>
  28. </swiper>
  29. <!-- <image src="https://i.loli.net/2020/07/16/2MmZsucVTlRjSwK.png" mode="scaleToFill" v-if="collapsible" @click="toggle" class="weektoggle" :class="{ down: monthOpen }"></image> -->
  30. </view>
  31. </template>
  32. <script>
  33. export default {
  34. name: 'ren-calendar',
  35. props: {
  36. // 星期几为第一天(0为星期日)
  37. weekstart: {
  38. type: Number,
  39. default: 0
  40. },
  41. // 标记的日期
  42. markDays: {
  43. type: Array,
  44. default: ()=>{
  45. return [];
  46. }
  47. },
  48. //是否展示月份切换按钮
  49. headerBar:{
  50. type: Boolean,
  51. default: true
  52. },
  53. // 是否展开
  54. open: {
  55. type: Boolean,
  56. default: true
  57. },
  58. //是否可收缩
  59. collapsible:{
  60. type: Boolean,
  61. default: true
  62. },
  63. //未来日期是否不可点击
  64. disabledAfter: {
  65. type: Boolean,
  66. default: false
  67. }
  68. },
  69. data() {
  70. return {
  71. weektext: ['日', '一', '二', '三', '四', '五', '六'],
  72. y: new Date().getFullYear(), // 年
  73. m: new Date().getMonth() + 1, // 月
  74. dates: [], // 当前月的日期数据
  75. positionTop: 0,
  76. monthOpen: true,
  77. choose: '',
  78. monthList:[],
  79. swiperCurrent:0
  80. };
  81. },
  82. created() {
  83. // this.monthList=[this.getMonth(-1),this.getMonth(0),this.getMonth(1)]
  84. this.dates=this.monthDay(this.y,this.m)
  85. !this.open && this.toggle();
  86. },
  87. mounted() {
  88. this.choose = this.getToday().date;
  89. },
  90. computed: {
  91. // 顶部星期栏
  92. weekDay() {
  93. return this.weektext.slice(this.weekstart).concat(this.weektext.slice(0, this.weekstart));
  94. },
  95. height() {
  96. return (this.dates.length / 7) * 95+ 'rpx';
  97. }
  98. },
  99. methods: {
  100. animationfinish({ detail: { current } }) {
  101. this.swiperCurrent = current;
  102. },
  103. swiperChange(e){
  104. let startCurrent=this.swiperCurrent
  105. let endCurrent=e.detail.current
  106. this.swiperCurrent=endCurrent
  107. //左滑
  108. if (startCurrent==0&&endCurrent==2) {
  109. this.changeMonth('pre')
  110. return
  111. }
  112. if (startCurrent==2&&endCurrent==1) {
  113. this.changeMonth('pre')
  114. return
  115. }
  116. if (startCurrent==1&&endCurrent==0) {
  117. this.changeMonth('pre')
  118. return
  119. }
  120. //右滑
  121. if (startCurrent==0&&endCurrent==1) {
  122. this.changeMonth('next')
  123. return
  124. }
  125. if (startCurrent==1&&endCurrent==2) {
  126. this.changeMonth('next')
  127. return
  128. }
  129. if (startCurrent==2&&endCurrent==0) {
  130. this.changeMonth('next')
  131. return
  132. }
  133. },
  134. formatNum(num) {
  135. let res = Number(num);
  136. return res < 10 ? '0' + res : res;
  137. },
  138. getToday() {
  139. let date = new Date();
  140. let y = date.getFullYear();
  141. let m = date.getMonth();
  142. let d = date.getDate();
  143. let week = new Date().getDay();
  144. let weekText = ['日', '一', '二', '三', '四', '五', '六'];
  145. let formatWeek = '星期' + weekText[week];
  146. let today = {
  147. date: y + '-' + this.formatNum(m + 1) + '-' + this.formatNum(d),
  148. week: formatWeek
  149. };
  150. return today;
  151. },
  152. // 获取当前月份数据
  153. monthDay(y, month) {
  154. let dates = [];
  155. let m = Number(month);
  156. let firstDayOfMonth = new Date(y, m - 1, 1).getDay(); // 当月第一天星期几
  157. let lastDateOfMonth = new Date(y, m, 0).getDate(); // 当月最后一天
  158. let lastDayOfLastMonth = new Date(y, m - 2, 0).getDate(); // 上一月的最后一天
  159. let weekstart = this.weekstart == 7 ? 0 : this.weekstart;
  160. let startDay = (() => {
  161. // 周初有几天是上个月的
  162. if (firstDayOfMonth == weekstart) {
  163. return 0;
  164. } else if (firstDayOfMonth > weekstart) {
  165. return firstDayOfMonth - weekstart;
  166. } else {
  167. return 7 - weekstart + firstDayOfMonth;
  168. }
  169. })();
  170. let endDay = 7 - ((startDay + lastDateOfMonth) % 7); // 结束还有几天是下个月的
  171. for (let i = 1; i <= startDay; i++) {
  172. dates.push({
  173. date: this.formatNum(lastDayOfLastMonth - startDay + i),
  174. day: weekstart + i - 1 || 7,
  175. month: m - 1 >= 0 ? this.formatNum(m - 1) : 12,
  176. year: m - 1 >= 0 ? y : y - 1
  177. });
  178. }
  179. for (let j = 1; j <= lastDateOfMonth; j++) {
  180. dates.push({
  181. date: this.formatNum(j),
  182. day: (j % 7) + firstDayOfMonth - 1 || 7,
  183. month: this.formatNum(m),
  184. year: y,
  185. isCurM: true //是否当前月份
  186. });
  187. }
  188. for (let k = 1; k <= endDay; k++) {
  189. dates.push({
  190. date: this.formatNum(k),
  191. day: (lastDateOfMonth + startDay + weekstart + k - 1) % 7 || 7,
  192. month: m + 1 <= 11 ? this.formatNum(m + 1) : 0,
  193. year: m + 1 <= 11 ? y : y + 1
  194. });
  195. }
  196. // console.log(dates);
  197. return dates;
  198. },
  199. isWorkDay(y, m, d) {
  200. //是否工作日
  201. let ymd = `${y}/${m}/${d}`;
  202. let formatDY = new Date(ymd.replace(/-/g, '/'));
  203. let week = formatDY.getDay();
  204. if (week == 0 || week == 6) {
  205. return false;
  206. } else {
  207. return true;
  208. }
  209. },
  210. isFutureDay(y, m, d) {
  211. //是否未来日期
  212. let ymd = `${y}/${m}/${d}`;
  213. let formatDY = new Date(ymd.replace(/-/g, '/'));
  214. let showTime = formatDY.getTime();
  215. let curTime = new Date().getTime();
  216. if (showTime > curTime) {
  217. return true;
  218. } else {
  219. return false;
  220. }
  221. },
  222. // 标记日期
  223. isMarkDay(y, m, d) {
  224. let flag = false;
  225. for (let i = 0; i < this.markDays.length; i++) {
  226. let dy = `${y}-${m}-${d}`;
  227. if (this.markDays[i] == dy) {
  228. flag = true;
  229. break;
  230. }
  231. }
  232. return flag;
  233. },
  234. isToday(y, m, d) {
  235. let checkD = y + '-' + m + '-' + d;
  236. let today = this.getToday().date;
  237. if (checkD == today) {
  238. return true;
  239. } else {
  240. return false;
  241. }
  242. },
  243. // 展开收起
  244. toggle() {
  245. this.monthOpen = !this.monthOpen;
  246. if (this.monthOpen) {
  247. this.positionTop = 0;
  248. } else {
  249. let index = -1;
  250. this.dates.forEach((i, x) => {
  251. this.isToday(i.year, i.month, i.date) && (index = x);
  252. });
  253. this.positionTop = -((Math.ceil((index + 1) / 7) || 1) - 1) * 80;
  254. }
  255. },
  256. // 点击回调
  257. selectOne(i, event) {
  258. let date = `${i.year}-${i.month}-${i.date}`;
  259. let selectD = new Date(date).getTime();
  260. let curTime = new Date().getTime();
  261. let week = new Date(date).getDay();
  262. let weekText = ['日', '一', '二', '三', '四', '五', '六'];
  263. let formatWeek = '星期' + weekText[week];
  264. let response = {
  265. date: date,
  266. week: formatWeek
  267. };
  268. if (!i.isCurM) {
  269. // console.log('不在当前月范围内');
  270. return false;
  271. }
  272. if (selectD > curTime) {
  273. if (this.disabledAfter==true) {
  274. this.$emit('onDayClick', false);;
  275. return false;
  276. } else {
  277. this.choose = date;
  278. this.$emit('onDayClick', response);
  279. }
  280. } else {
  281. this.choose = date;
  282. this.$emit('onDayClick', response);
  283. }
  284. },
  285. //改变年月
  286. changYearMonth(y, m) {
  287. this.dates = this.monthDay(y, m);
  288. this.y = y;
  289. this.m = m;
  290. },
  291. changeMonth(type){
  292. if(type=='pre'){
  293. if (this.m + 1 == 2) {
  294. this.m = 12;
  295. this.y = this.y - 1;
  296. } else {
  297. this.m = this.m - 1;
  298. }
  299. }else{
  300. if (this.m + 1 == 13) {
  301. this.m = 1;
  302. this.y = this.y + 1;
  303. } else {
  304. this.m = this.m + 1;
  305. }
  306. }
  307. let m=this.m
  308. if (this.m<10) {
  309. m='0'+this.m
  310. }
  311. let vuex_now_date=this.y+"年"+m+"月"
  312. this.$u.vuex("vuex_now_date",vuex_now_date)
  313. this.dates = this.monthDay(this.y, this.m);
  314. }
  315. }
  316. };
  317. </script>
  318. <style lang="scss" scoped>
  319. .calendar-wrapper {
  320. color: #c5c5c5;
  321. font-size: 26rpx;
  322. text-align: center;
  323. background-color: #5064eb;
  324. padding-bottom: 10rpx;
  325. .header{
  326. display: flex;
  327. align-items: center;
  328. justify-content: center;
  329. height: 88rpx;
  330. color: #42464A;
  331. font-size: 32rpx;
  332. font-weight: bold;
  333. border-bottom: 2rpx solid #f2f2f2;
  334. .pre,.next{
  335. color: #5064eb;
  336. font-size: 28rpx;
  337. font-weight: normal;
  338. padding: 8rpx 15rpx;
  339. border-radius: 10rpx;
  340. border: 2rpx solid #dcdfe6;
  341. }
  342. .pre{
  343. margin-right: 30rpx;
  344. }
  345. .next{
  346. margin-left: 30rpx;
  347. }
  348. }
  349. .week {
  350. display: flex;
  351. align-items: center;
  352. height: 80rpx;
  353. line-height: 80rpx;
  354. border-bottom: 1rpx solid rgba(255, 255, 255, 0.2);
  355. view {
  356. flex: 1;
  357. }
  358. }
  359. .content {
  360. position: relative;
  361. overflow: hidden;
  362. transition: height 0.4s ease;
  363. .days {
  364. transition: top 0.3s;
  365. display: flex;
  366. align-items: center;
  367. flex-wrap: wrap;
  368. position: relative;
  369. .item {
  370. font-size: 30rpx;
  371. position: relative;
  372. display: block;
  373. height: 95rpx;
  374. line-height: 95rpx;
  375. width: calc(100% / 7);
  376. .day {
  377. font-style: normal;
  378. display: inline-block;
  379. vertical-align: middle;
  380. width: 70rpx;
  381. height: 70rpx;
  382. line-height: 70rpx;
  383. overflow: hidden;
  384. border-radius: 60rpx;
  385. &.choose {
  386. background-color: #FFFFFF;
  387. color: #5064eb;
  388. }
  389. &.nolm {
  390. color: #5064eb;
  391. opacity: 0.3;
  392. }
  393. }
  394. .isWorkDay {
  395. color: #fff;
  396. }
  397. .notSigned {
  398. font-style: normal;
  399. width: 8rpx;
  400. height: 8rpx;
  401. background: #fa7268;
  402. border-radius: 10rpx;
  403. position: absolute;
  404. left: 50%;
  405. bottom: 0;
  406. pointer-events: none;
  407. }
  408. .today {
  409. color: #fff;
  410. background-color: #a8c0ff;
  411. }
  412. .workDay {
  413. font-style: normal;
  414. width: 8rpx;
  415. height: 8rpx;
  416. background: #4d7df9;
  417. border-radius: 10rpx;
  418. position: absolute;
  419. left: 50%;
  420. bottom: 0;
  421. pointer-events: none;
  422. }
  423. .markDay{
  424. font-weight: 800;
  425. font-style: normal;
  426. width: 10rpx;
  427. height: 10rpx;
  428. border-radius: 10rpx;
  429. background: #5ebd8a;
  430. position: absolute;
  431. left: calc(50% - 6rpx);
  432. bottom: 15rpx;
  433. pointer-events: none;
  434. }
  435. }
  436. }
  437. }
  438. .hide {
  439. height: 80rpx !important;
  440. }
  441. .weektoggle {
  442. width: 85rpx;
  443. height: 32rpx;
  444. position: relative;
  445. bottom: -42rpx;
  446. &.down {
  447. transform: rotate(180deg);
  448. bottom: 0;
  449. }
  450. }
  451. }
  452. </style>