index.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <template>
  2. <view class="page adffc">
  3. <div class="week adf">
  4. <div class="week-pre adfacjc" v-for="(w, i) in weeks" :class="{ active: i === 0 || i === 6 }" :key="i">{{ w }}</div>
  5. </div>
  6. <div class="list">
  7. <div class="list-item" v-for="(c,i) in calendarsCopy" :key="i">
  8. <div class="title">{{c.monthTitle}}</div>
  9. <div class="days">
  10. <div class="days-pre"
  11. :class="{'active':d.selected&&d.isCurrentMonth,'in':d.inrange&&d.isCurrentMonth}"
  12. v-for="(d,di) in c.days" :key="di">
  13. <div class="today" v-if="d.isToday">今天</div>
  14. <div class="day adfacjc" :class="{'hui':d.isPast}">{{d.isCurrentMonth?d.day:''}}</div>
  15. <div class="se" v-if="d.start&&d.isCurrentMonth">开始</div>
  16. <div class="se" v-if="d.end&&d.isCurrentMonth">结束</div>
  17. </div>
  18. </div>
  19. </div>
  20. </div>
  21. </view>
  22. </template>
  23. <script setup name="ActivityCalendar">
  24. import { ref, onMounted } from 'vue';
  25. const weeks = ['日', '一', '二', '三', '四', '五', '六'];
  26. const calendars = ref([]);
  27. const calendarsCopy = ref([]);
  28. const createCalendar = (months) => {
  29. return new Promise((resolve,reject)=>{
  30. const calendarList = [];
  31. const today = new Date();
  32. today.setHours(0, 0, 0, 0); // 标准化日期
  33. for (let i = 0; i < months; i++) {
  34. // --- 1. 计算目标月份 ---
  35. const targetDate = new Date();
  36. targetDate.setHours(0, 0, 0, 0);
  37. targetDate.setMonth(targetDate.getMonth() + i, 1);
  38. const year = targetDate.getFullYear();
  39. const month = targetDate.getMonth(); // 0-11
  40. const monthData = {
  41. monthTitle: `${year}/${String(month + 1).padStart(2, '0')}`,
  42. days: []
  43. };
  44. // --- 2. 计算日历网格的起始和结束日期 ---
  45. let startDate;
  46. const firstDayOfMonth = new Date(year, month, 1);
  47. const lastDayOfMonth = new Date(year, month + 1, 0);
  48. // **核心逻辑:根据是否为第一个月,决定网格的起始日期**
  49. if (i === 0) {
  50. // **第一个月(当前月):** 网格从今天所在周的周日开始
  51. startDate = new Date(today);
  52. startDate.setDate(today.getDate() - today.getDay());
  53. } else {
  54. // **后续月份:** 网格从该月第一天所在周的周日开始,保证月份的完整性
  55. startDate = new Date(firstDayOfMonth);
  56. startDate.setDate(firstDayOfMonth.getDate() - firstDayOfMonth.getDay());
  57. }
  58. // **网格的结束日期总是该月最后一天所在周的周六**
  59. const endDate = new Date(lastDayOfMonth);
  60. endDate.setDate(lastDayOfMonth.getDate() + (6 - lastDayOfMonth.getDay()));
  61. // --- 3. 循环填充日历网格中的每一天 ---
  62. for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {
  63. const currentYear = d.getFullYear();
  64. const currentMonth = d.getMonth() + 1;
  65. const currentDay = d.getDate();
  66. monthData.days.push({
  67. day: currentDay,
  68. dateStr: `${currentYear}-${String(currentMonth).padStart(2, '0')}-${String(currentDay).padStart(2, '0')}`,
  69. isToday: d.getTime() === today.getTime(),
  70. // 标识是否属于当前正在生成的月份 (e.g., 在7月数据中,8月1日这项为 false)
  71. isCurrentMonth: d.getMonth() === month,
  72. // 标识是否为过去日期 (仅在第一个月生效)
  73. isPast: i === 0 && d.getTime() < today.getTime()
  74. });
  75. }
  76. calendarList.push(monthData);
  77. }
  78. resolve(calendarList)
  79. })
  80. };
  81. const setStartEndDay = type => {//type 0:全部时间 1:一周内 2:一月内 3:本周末
  82. let d = new Date();
  83. let s = new Date().Format('yyyy-MM-dd');
  84. let e = null;
  85. switch(type){
  86. case 0:
  87. s = e = '';
  88. break;
  89. case 1:
  90. e = new Date(d.setDate(d.getDate(s)+6)).Format('yyyy-MM-dd');
  91. break;
  92. case 2:
  93. e = new Date(d.setDate(d.getDate(s)+30)).Format('yyyy-MM-dd');
  94. break;
  95. case 3:
  96. let sw = new Date(s).getDay();
  97. if(sw===0){//当天是周日
  98. s = new Date(d.setDate(d.getDate(s)-1)).Format('yyyy-MM-dd');
  99. e = new Date(s).Format('yyyy-MM-dd');
  100. }else{
  101. s = new Date(d.setDate(d.getDate(s)+(6-sw))).Format('yyyy-MM-dd');
  102. e = new Date(d.setDate(d.getDate(s)+1)).Format('yyyy-MM-dd');
  103. }
  104. break;
  105. }
  106. calendarsCopy.value = JSON.parse(JSON.stringify(calendars.value));
  107. if(!s||!e) return
  108. calendarsCopy.value.forEach(c=>{
  109. c?.days.forEach(d=>{
  110. if(d.dateStr===s||d.dateStr===e){
  111. d.selected = true;
  112. d.start = d.dateStr===s;
  113. d.end = d.dateStr===e;
  114. }
  115. if(Date.parse(new Date(d.dateStr))>Date.parse(new Date(s))&&Date.parse(new Date(d.dateStr))<Date.parse(new Date(e))){
  116. d.inrange = true;
  117. }
  118. })
  119. })
  120. }
  121. onMounted(async () => {
  122. calendars.value = await createCalendar(2);
  123. calendarsCopy.value = JSON.parse(JSON.stringify(calendars.value));
  124. });
  125. defineExpose({
  126. setStartEndDay
  127. })
  128. </script>
  129. <style scoped lang="scss">
  130. .page {
  131. width: 100%;
  132. height: 100%;
  133. background: #f7f7f7;
  134. .week {
  135. width: 100%;
  136. height: 81rpx;
  137. background: #ffffff;
  138. &-pre {
  139. width: calc(100% / 7);
  140. height: 100%;
  141. font-family: PingFangSC, PingFang SC;
  142. font-weight: 400;
  143. font-size: 24rpx;
  144. color: #6b7280;
  145. line-height: 33rpx;
  146. &.active {
  147. font-weight: bold;
  148. color: #70cf52;
  149. }
  150. }
  151. }
  152. .list {
  153. flex: 1;
  154. overflow-y: auto;
  155. &-item{
  156. .title{
  157. font-family: PingFang-SC, PingFang-SC;
  158. font-weight: bold;
  159. font-size: 30rpx;
  160. color: #252525;
  161. line-height: 30rpx;
  162. text-align: center;
  163. margin-top: 30rpx;
  164. }
  165. .days{
  166. display: flex;
  167. flex-wrap: wrap;
  168. margin-top: 40rpx;
  169. &-pre{
  170. width: calc(100% / 7);
  171. height: 120rpx;
  172. position: relative;
  173. &.active{
  174. background: #B7F358;
  175. }
  176. &.in{
  177. background: rgba(183, 243,88, .4);
  178. }
  179. .day{
  180. width: 100%;
  181. height: 100%;
  182. font-family: PingFang-SC, PingFang-SC;
  183. font-weight: bold;
  184. font-size: 30rpx;
  185. color: #252525;
  186. line-height: 42rpx;
  187. &.hui{
  188. color: #989998;
  189. }
  190. }
  191. .today{
  192. width: 100%;
  193. font-family: PingFangSC, PingFang SC;
  194. font-weight: 400;
  195. font-size: 20rpx;
  196. color: #989998;
  197. line-height: 28rpx;
  198. text-align: center;
  199. position: absolute;
  200. top: 10rpx;
  201. }
  202. .se{
  203. width: 100%;
  204. font-family: PingFangSC, PingFang SC;
  205. font-weight: bold;
  206. font-size: 20rpx;
  207. color: #989998;
  208. line-height: 28rpx;
  209. text-align: center;
  210. position: absolute;
  211. bottom: 15rpx;
  212. }
  213. }
  214. }
  215. }
  216. }
  217. }
  218. </style>