timeSever2.vue 16 KB

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