calendar.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <template>
  2. <view class="content">
  3. <view>
  4. <view class="flex year-month">
  5. <view @click="prevMonth"><</view>
  6. <view>
  7. <picker mode="date" fields="month" @change="monthChange">
  8. <text class="bold">{{time.year}} . {{time.month + 1}}</text>
  9. </picker>
  10. </view>
  11. <view @click="nextMonth">></view>
  12. </view>
  13. <view class="flex">
  14. <view class="flex-item flex" v-for="item in weeks" >
  15. <text class="week">{{item}}</text>
  16. </view>
  17. </view>
  18. <view class="flex-wrap">
  19. <template v-for="item in visibleDays" :Key="item.day">
  20. <view class="day-box flex-column">
  21. <text
  22. class="day"
  23. @click="clickDate(item.day)"
  24. :style="[
  25. isToday(item.day) && todayObj,
  26. isClick(item.day) && selectedObj,
  27. ]"
  28. :class="[
  29. {selected: isClick(item.day)},
  30. {notCurrentMonth: !isCurrentMonth(item.day)}
  31. ]"
  32. >{{item.day | dayFilter}}</text>
  33. <template v-if="showText">
  34. <text
  35. v-if="isCurrentMonth(item.day)"
  36. class="day-text"
  37. :style="{color: textColor}"
  38. >
  39. {{item.data.value || ''}}
  40. </text>
  41. </template>
  42. <template v-if="showDot">
  43. <text
  44. v-if="isCurrentMonth(item.day) && item.data.dot && item.data.active"
  45. class="day-dot"
  46. ></text>
  47. <text
  48. v-if="isCurrentMonth(item.day) && item.data.dot && !item.data.active"
  49. class="day-dot dot-gray"
  50. ></text>
  51. </template>
  52. </view>
  53. </template>
  54. </view>
  55. </view>
  56. </view>
  57. </template>
  58. <script>
  59. const getYearMonthDay = (date) => {
  60. let year = date.getFullYear();
  61. let month = date.getMonth();
  62. let day = date.getDate();
  63. return {
  64. year,
  65. month,
  66. day
  67. }
  68. }
  69. const getDate = (year, month, day) => {
  70. return new Date(year, month, day)
  71. }
  72. export default {
  73. data() {
  74. return {
  75. iArr: [1,2,3,4,5,6],
  76. jArr: [1,2,3,4,5,6,7],
  77. value: new Date(),
  78. weeks: ['日', '一', '二', '三', '四', '五', '六'],
  79. click_time: {},
  80. month_data: this.extraData,
  81. time: this.defaultTime,
  82. todayObj: {
  83. background: this.bgColor,
  84. color: '#ffffff'
  85. },
  86. selectedObj: {
  87. background: this.selColor,
  88. color: '#ffffff'
  89. }
  90. }
  91. },
  92. props: {
  93. bgColor: {
  94. type: String,
  95. default: '#4198f8'
  96. },
  97. selColor: {
  98. type: String,
  99. default: '#4198f8'
  100. },
  101. textColor: {
  102. type: String,
  103. default: '#4198f8'
  104. },
  105. defaultTime: {
  106. type: Object,
  107. default: ()=> {
  108. return {
  109. year: getYearMonthDay(new Date()).year,
  110. month: getYearMonthDay(new Date()).month
  111. }
  112. }
  113. },
  114. extraData: {
  115. type: Array,
  116. default: ()=> {
  117. return [] // {date: '2020-6-3', value: '签到', dot: true, active: true}
  118. }
  119. },
  120. showText: {
  121. type: Boolean,
  122. default: true
  123. },
  124. showDot: {
  125. type: Boolean,
  126. default: false
  127. }
  128. },
  129. filters: {
  130. dayFilter(val) {
  131. return val.getDate();
  132. }
  133. },
  134. watch: {
  135. extraData:{
  136. handler(newV, oldV) {
  137. if (newV !== oldV) {
  138. this.month_data = newV
  139. }
  140. },
  141. deep:true
  142. }
  143. },
  144. computed: {
  145. visibleDays() { // 计算当月展示日期
  146. let {time: {year, month}, month_data} = this;
  147. let currentFirstDay = getDate(year, month, 1);
  148. let week = currentFirstDay.getDay();
  149. let startDay = currentFirstDay - week * 60 * 60 * 1000 * 24;
  150. let arr = [];
  151. for(let i = 0; i < 42; i++) {
  152. let day = new Date(startDay + i * 60 * 60 * 1000 * 24);
  153. let {year: dayY, month: dayM, day: dayD} = getYearMonthDay(day);
  154. let data = {};
  155. for (let item of month_data) {
  156. let dateString = item.date;
  157. let dateArr = dateString.indexOf('-') !== -1
  158. ? dateString.split('-')
  159. : dateString.indexOf('/') !== -1
  160. ? dateString.split('/')
  161. : [];
  162. if (dateArr.length === 3
  163. && Number(dateArr[0]) === Number(dayY)
  164. && Number(dateArr[1]) === (Number(dayM) + 1)
  165. && Number(dateArr[2]) === Number(dayD)) {
  166. data = item
  167. }
  168. }
  169. let obj = {
  170. day,
  171. data
  172. }
  173. arr.push(obj)
  174. }
  175. return arr
  176. }
  177. },
  178. mounted() {
  179. },
  180. methods: {
  181. isCurrentMonth(date) { // 是否当月
  182. let {year, month} = getYearMonthDay(getDate(this.time.year, this.time.month, 1));
  183. let {year: y, month:m} = getYearMonthDay(date);
  184. return year === y && month === m;
  185. },
  186. isToday(date) { // 是否当天
  187. let {year, month, day} = getYearMonthDay(new Date());
  188. let {year: y, month: m, day: d} = getYearMonthDay(date);
  189. return year === y && month === m && day === d;
  190. },
  191. isClick(date) { // 是否是点击日期
  192. let {click_time} = this;
  193. if (!click_time.day) return false;
  194. let {year, month, day} = getYearMonthDay(getDate(click_time.year, click_time.month, click_time.day));
  195. let {year: y, month: m, day: d} = getYearMonthDay(date);
  196. return year === y && month === m && day === d;
  197. },
  198. clickDate(date) { // 点击日期
  199. let {year, month, day} = getYearMonthDay(date);
  200. this.click_time = {year, month, day};
  201. this.$emit('calendarTap', {year, month, day})
  202. },
  203. prevMonth() { // 上一月
  204. let { time: { year, month} } = this;
  205. let d = getDate(year, month, 1);
  206. d.setMonth(d.getMonth() - 1);
  207. this.time = getYearMonthDay(d);
  208. // this.click_time = {};
  209. this.$emit('monthTap', getYearMonthDay(d))
  210. },
  211. nextMonth() { // 下一月
  212. // 获取当前的年月的日期
  213. let { time: { year, month} } = this;
  214. let d = getDate(year, month, 1);
  215. d.setMonth(d.getMonth() + 1);
  216. this.time = getYearMonthDay(d);
  217. // this.click_time = {};
  218. this.$emit('monthTap', getYearMonthDay(d))
  219. },
  220. monthChange(e) {
  221. let {value} = e.detail;
  222. let timeArr = value.split('-');
  223. this.time = {year: timeArr[0], month: timeArr[1] - 1, day: 1};
  224. this.$emit('monthTap',{year: timeArr[0], month: timeArr[1] - 1, day: 1})
  225. }
  226. }
  227. }
  228. </script>
  229. <style scoped lang="scss">
  230. .content {
  231. width: 750rpx;
  232. margin: 0 auto;
  233. }
  234. .flex {
  235. width: 100%;
  236. display: flex;
  237. align-items: center;
  238. justify-content: space-between;
  239. flex-direction: row;
  240. }
  241. .flex-wrap {
  242. width: 100%;
  243. display: flex;
  244. flex-wrap: wrap;
  245. align-items: center;
  246. justify-content: space-between;
  247. flex-direction: row;
  248. }
  249. .flex-column {
  250. width: 100%;
  251. height: 106rpx;
  252. display: flex;
  253. flex-direction: column;
  254. justify-content: flex-start;
  255. align-items: center;
  256. }
  257. .flex-item {
  258. flex: 1;
  259. }
  260. .bold {
  261. font-weight: bold;
  262. font-size: 28rpx;
  263. }
  264. .year-month {
  265. width: 400rpx;
  266. margin: 0 auto 20rpx;
  267. padding-top: 20rpx;
  268. }
  269. .week {
  270. margin: 20rpx 20rpx 40rpx;
  271. width: 60rpx;
  272. text-align: center;
  273. color: #999999;
  274. }
  275. .day-box {
  276. width: 100rpx;
  277. text-align: center;
  278. display: flex;
  279. flex-direction: column;
  280. }
  281. .day {
  282. width: 60rpx;
  283. height: 60rpx;
  284. line-height: 60rpx;
  285. border-radius: 50%;
  286. text-align: center;
  287. font-weight: 600;
  288. }
  289. .day-text {
  290. font-size: 22rpx;
  291. }
  292. .day-dot {
  293. width: 12rpx;
  294. height: 12rpx;
  295. border-radius: 50%;
  296. background: #4cd964;
  297. &.dot-gray {
  298. background: gray;
  299. }
  300. }
  301. .today, .selected {
  302. background: #4198f8;
  303. color: #ffffff;
  304. }
  305. .notCurrentMonth {
  306. color: #999999;
  307. pointer-events: none;
  308. background: none;
  309. }
  310. </style>