BaseCalendar.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. <template>
  2. <!-- 打卡日历页面 -->
  3. <view class="all">
  4. <view class="bar">
  5. <!-- 上一个月 -->
  6. <view class="previous barbtn" @click="changeMonth(-1)">
  7. <view class="iconfont icon-ic_left2"></view>
  8. </view>
  9. <!-- 显示年月 -->
  10. <view class="date">{{ nowYear || '--' }} 年 {{ nowMonth || '--' }} 月</view>
  11. <!-- 下一个月 -->
  12. <view class="next barbtn" @click="changeMonth(1)">
  13. <view class="iconfont icon-ic_right2"></view>
  14. </view>
  15. </view>
  16. <!-- 显示星期 -->
  17. <view class="week-area">
  18. <view class="week-txt" v-for="(item, index) in weeksTxt[langType]" :key="index">{{ item }}</view>
  19. </view>
  20. <view class="myDateTable">
  21. <view v-for="(item, j) in calendarDays" :key="j" class="dateCell">
  22. <view v-if="item.date == undefined || item.date == null" class="cell"></view>
  23. <template v-else>
  24. <!-- 已签到日期 -->
  25. <view v-if="item.isSign == true" class="cell signColor bgWhite">
  26. <!-- {{ item.date }} -->
  27. <image class="sign-today" src="@/pages/users/static/sign-today.png" mode=""></image>
  28. <view class="sign">已签</view>
  29. </view>
  30. <!-- 漏签 -->
  31. <view @click="clickSign(item.date, 0)" class="cell outSignStyle" v-else-if="item.isBeforeToday && item.isThisMonth">
  32. <!-- redColor bgGray -->
  33. {{ item.date }}
  34. <!-- <view class="redDot"></view> -->
  35. </view>
  36. <!-- 今日未签到-->
  37. <view @click="clickSign(item.date, 1)" class="cell whiteColor" v-else-if="item.date == today && nowMonth == toMonth && nowYear == toYear">今</view>
  38. <!-- 当前日期之后 -->
  39. <view class="whiteColor cell" v-else>
  40. {{ item.date }}
  41. </view>
  42. </template>
  43. </view>
  44. </view>
  45. </view>
  46. </template>
  47. <script>
  48. export default {
  49. data() {
  50. return {
  51. calendarDays: [],
  52. SignData: [], // 已经签到的数据
  53. nowYear: 0, //当前选的年
  54. nowMonth: 0, //当前选的月
  55. today: parseInt(new Date().getDate()), //系统本日
  56. toMonth: parseInt(new Date().getMonth() + 1), //系统本月
  57. toYear: parseInt(new Date().getFullYear()), //系统本年
  58. weeksTxt: {
  59. ch: ['日', '一', '二', '三', '四', '五', '六'],
  60. en: ['Sun', 'Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat']
  61. }
  62. };
  63. },
  64. props: {
  65. isReplenishSign: {
  66. // 是否允许过期补签
  67. type: Boolean,
  68. default: false
  69. },
  70. isFullCalendar: {
  71. // 是否需要填满日历,前后空的格子填入上/下个月的日期
  72. type: Boolean,
  73. default: false
  74. },
  75. yearMonth: {
  76. // 2022-01 这种格式,默认当前年月
  77. type: String,
  78. default: new Date().getFullYear() + '-' + new Date().getMonth() + 1
  79. },
  80. dataSource: {
  81. //已签到的数据源,例: 5、6号已签到: ["2022-01-05","2022-01-06"],兼容个位数前可加可不加0
  82. type: Array,
  83. default: () => {
  84. return [];
  85. }
  86. },
  87. langType: {
  88. //只是示例一个翻译而已,要想所有都翻译自己可以再加加
  89. type: String,
  90. default: 'ch' //en
  91. }
  92. },
  93. created() {
  94. if (!/en|ch/g.test(this.langType)) {
  95. this.langType = 'ch'; // 非中英,则固定中文
  96. }
  97. const ymArr = this.yearMonth.split('-');
  98. this.buildCalendar(ymArr[0], ymArr[1]);
  99. this.onSignDataChange(this.dataSource);
  100. },
  101. watch: {
  102. dataSource: 'onSignDataChange'
  103. },
  104. methods: {
  105. clickSign(date, type) {
  106. //type=0补签,type=1当日签到
  107. var strTip = '签到';
  108. if (type == 0) {
  109. if (!this.isReplenishSign) {
  110. // 未开启补签,阻止继续执行
  111. console.log('————补签功能未开启————');
  112. return;
  113. }
  114. strTip = '补签';
  115. }
  116. uni.showToast({
  117. title: date + '号' + strTip + '成功',
  118. icon: 'success',
  119. position: 'bottom'
  120. });
  121. this.$emit('clickChange', this.nowYear + '-' + this.nowMonth + '-' + date); //传给调用模板页面
  122. // 父页面要在clickChange里去修改输入的数据源dataSource,否则视图不更新的!
  123. },
  124. //构建日历数据
  125. buildCalendar(y, m) {
  126. this.nowYear = y;
  127. this.nowMonth = m;
  128. this.calculateEmptyGrids(y, m);
  129. this.calculateDays(y, m);
  130. if (this.isFullCalendar) {
  131. this.fullCell();
  132. }
  133. },
  134. // 监听到签到数据源改变
  135. onSignDataChange(newData, oldData = []) {
  136. this.SignData = newData;
  137. this.matchSign();
  138. },
  139. //匹配标记已经签到的日子
  140. matchSign() {
  141. var signs = this.SignData;
  142. var daysArr = this.calendarDays;
  143. for (var i = 0; i < signs.length; i++) {
  144. var current = new Date(this.toIOSDate(signs[i])).getTime(); // ios只认/格式的日期
  145. for (var j = 0; j < daysArr.length; j++) {
  146. if (current == new Date(this.toIOSDate(daysArr[j].fullDate)).getTime()) {
  147. daysArr[j].isSign = true;
  148. }
  149. }
  150. }
  151. this.calendarDays = daysArr;
  152. // console.log(this.calendarDays );
  153. },
  154. // 计算当月1号前空了几个格子,把它填充在calendarDays数组的前面
  155. calculateEmptyGrids(year, month) {
  156. //计算每个月时要清零
  157. this.calendarDays = [];
  158. const firstDayOfWeek = this.getFirstDayOfWeek(year, month);
  159. if (firstDayOfWeek > 0) {
  160. for (let i = 0; i < firstDayOfWeek; i++) {
  161. this.calendarDays.push({
  162. date: null, // 显示的日期
  163. fullDate: null, // 日期yyyy-mm-dd格式
  164. isBeforeToday: true, // 今日之前
  165. isSign: false, // 是否签到
  166. isThisMonth: false // 是本月
  167. });
  168. }
  169. }
  170. },
  171. // 绘制当月天数占的格子,并把它放到days数组中
  172. calculateDays(year, month) {
  173. const thisMonthDays = this.getMonthDayLength(year, month);
  174. const toDate = new Date(this.toYear + '/' + this.toMonth + '/' + this.today);
  175. for (let i = 1; i <= thisMonthDays; i++) {
  176. const fullDate = year + '-' + month + '-' + i;
  177. const isBeforeToday = new Date(this.toIOSDate(fullDate)) < toDate;
  178. this.calendarDays.push({
  179. date: i,
  180. fullDate,
  181. isBeforeToday,
  182. isSign: false,
  183. isThisMonth: true
  184. });
  185. }
  186. //console.log(this.calendarDays);
  187. },
  188. // 切换控制年月,上一个月,下一个月
  189. changeMonth(type) {
  190. console.log(type)
  191. const nowYear = parseInt(this.nowYear);
  192. const nowMonth = parseInt(this.nowMonth);
  193. const newObj = this.getOperateMonthDate(nowYear, nowMonth, type);
  194. this.buildCalendar(newObj.year, newObj.month); // 重新构建日历数据
  195. this.$emit('dateChange', this.nowYear + '-' + this.nowMonth); //传给调用模板页面去拿新数据
  196. },
  197. // 获取当月共多少天,也就是获取月的最后一天
  198. getMonthDayLength(year, month) {
  199. return new Date(year, month, 0).getDate();
  200. },
  201. // 获取当月第一天星期几
  202. getFirstDayOfWeek(year, month, day = 1) {
  203. return new Date(Date.UTC(year, month - 1, day)).getDay();
  204. },
  205. toIOSDate(strDate) {
  206. // iso不认识"-"拼接的日期,所以转/
  207. return strDate ? strDate.replace(/-/g, '/') : strDate;
  208. },
  209. // 需要填满格子,上/下个月的日期拉出来填满格子
  210. fullCell() {
  211. const endDay = this.getMonthDayLength(this.nowYear, this.nowMonth);
  212. const beforeEmptyLength = this.getFirstDayOfWeek(this.nowYear, this.nowMonth);
  213. const afterEmptyLength = 6 - this.getFirstDayOfWeek(this.nowYear, this.nowMonth, endDay);
  214. const last = this.getOperateMonthDate(this.nowYear, this.nowMonth, -1);
  215. const lastMonthEndDay = this.getMonthDayLength(last.year, last.month);
  216. for (let i = 0; i < beforeEmptyLength; i++) {
  217. const date = lastMonthEndDay - beforeEmptyLength + i + 1;
  218. this.calendarDays[i].date = date;
  219. this.calendarDays[i].fullDate = last.year + '-' + last.month + '-' + date;
  220. }
  221. const next = this.getOperateMonthDate(this.nowYear, this.nowMonth, 1);
  222. for (let i = 1; i <= afterEmptyLength; i++) {
  223. this.calendarDays.push({
  224. date: i, // 显示的日期
  225. fullDate: next.year + '-' + next.month + '-' + i, // 日期yyyy-mm-dd格式
  226. isBeforeToday: false, // 今日之前
  227. isSign: false, // 是否签到
  228. isThisMonth: false // 是本月
  229. });
  230. }
  231. // console.log(beforeEmptyLength,afterEmptyLength,lastMonthEndDay);
  232. // console.log(this.calendarDays);
  233. },
  234. // 获取加/减一个月的日期
  235. getOperateMonthDate(yy, mm, num) {
  236. let month = parseInt(mm) + parseInt(num);
  237. let year = parseInt(yy);
  238. if (month > 12) {
  239. month = 1;
  240. year++;
  241. } else if (month < 1) {
  242. month = 12;
  243. year--;
  244. }
  245. return {
  246. month,
  247. year
  248. };
  249. }
  250. }
  251. };
  252. </script>
  253. <style scoped lang="scss">
  254. .all {
  255. // margin-top: 20rpx;
  256. .cir-bg {
  257. position: absolute;
  258. left: 0;
  259. top: 0;
  260. width: 324rpx;
  261. height: 230rpx;
  262. background: #fee3de;
  263. opacity: 1;
  264. filter: blur(133px);
  265. }
  266. }
  267. .all .bar {
  268. display: flex;
  269. flex-direction: row;
  270. justify-content: space-between;
  271. padding: 60rpx 64rpx 64rpx;
  272. }
  273. .bar .barbtn {
  274. width: 52rpx;
  275. height: 52rpx;
  276. background: rgba(233, 51, 35, 0.1);
  277. border-radius: 50%;
  278. display: flex;
  279. .iconfont {
  280. margin: auto;
  281. color: rgba(233, 51, 35, 0.8);
  282. }
  283. }
  284. .all .week-area {
  285. display: flex;
  286. justify-content: space-between;
  287. padding: 10px 0;
  288. box-sizing: border-box;
  289. width: 91vw;
  290. margin: 10px auto;
  291. border-radius: 10px;
  292. }
  293. .all .week-txt {
  294. text-align: center;
  295. width: 88rpx;
  296. font-size: 24rpx;
  297. line-height: 34rpx;
  298. color: #999999;
  299. }
  300. .myDateTable {
  301. margin: 0 auto;
  302. width: 91vw;
  303. border-radius: 10px;
  304. }
  305. .myDateTable .dateCell {
  306. width: 13vw;
  307. padding: 1vw;
  308. display: inline-block;
  309. text-align: center;
  310. font-size: 36rpx;
  311. box-sizing: border-box;
  312. overflow: hidden;
  313. }
  314. .dateCell .cell {
  315. display: flex;
  316. border-radius: 16rpx;
  317. height: 120rpx;
  318. justify-content: center;
  319. align-items: center;
  320. box-sizing: border-box;
  321. flex-direction: column;
  322. font-family: 'SemiBold';
  323. .sign-today {
  324. width: 40rpx;
  325. height: 40rpx;
  326. }
  327. .sign {
  328. font-size: 18rpx;
  329. font-weight: 500;
  330. color: #e93323;
  331. margin-top: 10rpx;
  332. }
  333. }
  334. .whiteColor {
  335. color: #981007;
  336. }
  337. .signColor {
  338. color: #ffffff;
  339. }
  340. .bgWhite {
  341. background-color: #fceae9;
  342. }
  343. .bgGray {
  344. background-color: rgba(255, 255, 255, 0.42);
  345. }
  346. .bgBlue {
  347. font-size: 14px;
  348. background-color: #4b95e6;
  349. }
  350. .redColor {
  351. color: #ff0000;
  352. }
  353. .outSignStyle {
  354. // border: 1px solid #981007;
  355. color: #981007;
  356. }
  357. .redDot {
  358. width: 3px;
  359. height: 3px;
  360. border-radius: 16rpx;
  361. background-color: red;
  362. }
  363. .date {
  364. font-size: 36rpx;
  365. font-family: PingFang SC-Medium, PingFang SC;
  366. font-weight: 500;
  367. color: #981007;
  368. }
  369. </style>