ren-calendar.vue 13 KB

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