calendar.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919
  1. <template>
  2. <view class="lunc-calendar">
  3. <!-- 头部上下月按钮及月份 -->
  4. <view class="header">
  5. <view class="head-icon head-pre-month" v-show="showChangeBtn" @click="changeMonthOrWeek('prev')"></view>
  6. <view class="head-month">{{selDate.year+'年'+(selDate.month<10?'0'+selDate.month:selDate.month)+'月'}}</view>
  7. <view class="head-icon head-next-month" v-show="showChangeBtn" @click="changeMonthOrWeek('next')"></view>
  8. <view class="go-to-today" v-show="showToday" @click="goToday">今</view>
  9. </view>
  10. <!-- 星期 -->
  11. <view class="week-area">
  12. <!-- <view class="week-font" v-for="(item, index) in weekArr" :key="index ">{{ weekType + '' + item }}</view> -->
  13. <view class="week-font" v-for="(item, index) in weekArr" :key="index ">{{ item }}</view>
  14. </view>
  15. <!-- 日历 -->
  16. <swiper class="calendar-data" :current="shrinkType?tranCurrent:tranIndex" circular :duration="tranDuration"
  17. @change="swiperChange" @animationfinish="swiperEndChange" :style="{height:shrinkType?'56px':'266px'}">
  18. <swiper-item class="swiper-item swiper-prev-item" v-for="(a, i) in getAllData" :key="i">
  19. <view class="month-bg" v-if="showMonthBg">{{ getMontBg }}</view>
  20. <view class="month-days" :class="shrinkType?'item-week':''">
  21. <view class="week-days" v-for="(b, j) in a" :key="j">
  22. <view class="day" v-for="(c, k) in b" :key="k" @click="clickDay(c)">
  23. <view class="day-info"
  24. :class="[c.dayClass, getIsSelDay(c)?'is-sel':'', c.dayType!='normal'?'un-month':'']">
  25. <text class="day-solar">{{ c.day }}</text>
  26. <template v-if="showLunar || c.sign && c.sign.length > 0">
  27. <text class="day-tag" v-if="c.sign && c.sign.length > 0"></text>
  28. <text class="day-sign"
  29. v-if="c.sign && c.sign.length > 0">{{ c.sign[0].title }}</text>
  30. <text class="day-lunar" v-else>{{c.dayLunar}}</text>
  31. </template>
  32. </view>
  33. </view>
  34. </view>
  35. </view>
  36. </swiper-item>
  37. </swiper>
  38. <!-- 收缩按钮 -->
  39. <view class="shrink" v-if="showShrink" @click="changeShrink">
  40. <text class="shrink-btn" :class="shrinkType?'shrink-open':'shrink-close'"></text>
  41. </view>
  42. </view>
  43. </template>
  44. <script>
  45. let {
  46. calendar
  47. } = require("./calendar.js");
  48. /**
  49. * @property {Boolean} showLunar = [true|false] 是否显示农历,默认false
  50. * @property {Boolean} showMonthBg = [true|false] 是否显示月份背景,默认true
  51. * @property {Boolean} showChangeBtn = [true|false] 是否显示上月下月箭头按钮,默认true
  52. * @property {String} firstDayOfWeek = [monday|sunday] 周几为每周的第一天,默认monday
  53. * @value monday 每周从周一开始(默认)
  54. * @value sunday 每周从周日开始
  55. * @property {String} weekType = [''|周|星期] 星期的前缀;如周一周二或星期一星期二,为空则只显示一、二等;不用预设值时可自定义前缀,填的值即为星期前缀;默认周
  56. * @value '' 星期显示:只显示一、二等
  57. * @value 周 星期显示:周一、周二等(默认)
  58. * @value 星期 星期显示:星期一、星期二等
  59. * @property {Boolean} weekend = [true|false] 周末标红(周六周日日期用红色字体),默认true
  60. * @property {Boolean} showShrink = [true|false] 是否显示收缩按钮,可显示一周的日期,默认false
  61. * @property {String} shrinkState = [week|month] 收缩状态,默认month
  62. * @value week 默认打开显示周数据(收起状态)
  63. * @value month 默认打开显示月数据(展开状态)
  64. * @property {Array} signList 标记数组,若当前有多个标记,则显示最后一个,期待格式[{date: '2021-09-10', title: '生日', info: '八月初四张三生日'}]
  65. * @event {Function()} dayChange 点击日期触发事件
  66. * @event {Function()} monthChange 切换月份触发事件
  67. * @event {Function()} shrinkClick 收缩和展开时触发事件
  68. */
  69. export default {
  70. name: 'LuncCalendar',
  71. props: {
  72. //是否显示农历
  73. showLunar: {
  74. type: Boolean,
  75. default: false
  76. },
  77. //是否显示月份背景
  78. showMonthBg: {
  79. type: Boolean,
  80. default: true
  81. },
  82. //是否显示上月下月按钮
  83. showChangeBtn: {
  84. type: Boolean,
  85. default: true
  86. },
  87. //每周的周几为第一天
  88. firstDayOfWeek: {
  89. type: String,
  90. default: 'sunday'
  91. },
  92. //每周的周几为第一天
  93. weekType: {
  94. type: String,
  95. default: ''
  96. },
  97. //周末标红
  98. weekend: {
  99. type: Boolean,
  100. default: false
  101. },
  102. //是否可收缩,收起来后以周显示
  103. showShrink: {
  104. type: Boolean,
  105. default: true
  106. },
  107. // 默认打开状态(收起或展开)
  108. shrinkState: {
  109. type: String,
  110. default: 'month'
  111. },
  112. //标记
  113. signList: {
  114. type: Array,
  115. default () {
  116. return []
  117. }
  118. }
  119. },
  120. data() {
  121. return {
  122. weekArr: ['一', '二', '三', '四', '五', '六', '日'], //星期数组
  123. today: {}, //今天日期 -> year, month, day
  124. selDate: {}, //选中日期信息 -> year, month, day
  125. allMonthList: [], // 月份数据 -> [[[周],[周]],[月],[月]]
  126. tranIndex: 1, // 月份轮播所在位置
  127. allWeekList: [], // 周月份数据 -> [[[周]],[月],[月]]
  128. tranCurrent: 1, // 周轮播所在位置
  129. tranDuration: 300, //轮播时间(单位毫秒)
  130. signArr: this.signList, // 标记列表
  131. showToday: false, //显示回到今天(非当月才显示)
  132. shrinkType: false, // 收缩状态,true:收起(显示周),false展开(显示月)
  133. deterChange: true, // 防止切换月份过快
  134. }
  135. },
  136. created() {
  137. let nd = new Date();
  138. this.today = {
  139. year: nd.getFullYear(),
  140. month: nd.getMonth() + 1,
  141. day: nd.getDate()
  142. }
  143. if (this.firstDayOfWeek == "sunday") this.weekArr = ['日', '一', '二', '三', '四', '五', '六'];
  144. this.initDate();
  145. },
  146. watch: {},
  147. computed: {
  148. getAllData() { // 切换周或月时,展示的数据不同
  149. return this.shrinkType ? this.allWeekList : this.allMonthList;
  150. },
  151. getMontBg() { // 获取当前月背景
  152. let monthBg = this.selDate.month;
  153. return !this.shrinkType ? (monthBg < 10 ? '0' + monthBg : monthBg) : '';
  154. },
  155. getIsSelDay() { // 判断是否是选中日期
  156. return (d) => {
  157. let {
  158. year,
  159. month,
  160. day
  161. } = this.selDate;
  162. return year == d.year && month == d.month && day == d.day
  163. }
  164. },
  165. },
  166. methods: {
  167. initDate() { // 初始化日期
  168. this.selDate = JSON.parse(JSON.stringify(this.today));
  169. let monthList = this.getMonthData(this.selDate); // 获取当月数据
  170. let prevMonthList = this.getMonthData(this.getMonthDate(this.selDate, -1)); // 上月数据
  171. let nextMonthList = this.getMonthData(this.getMonthDate(this.selDate)); // 下月数据
  172. this.allMonthList = [prevMonthList, monthList, nextMonthList]
  173. this.tranIndex = 1;
  174. if (this.shrinkState == "week" && !this.shrinkType) this.changeShrink();
  175. },
  176. /**
  177. * 根据指定日期获取当月的数据
  178. * @param {Object} date = { year, month, day } 指定的日期
  179. */
  180. getMonthData(date) {
  181. const {
  182. year,
  183. month,
  184. day
  185. } = date; //指定的日期
  186. let maxDay = new Date(year, month, 0).getDate(); //当前月最大日期
  187. let firstWeek = new Date(year + "/" + month + "/1").getDay(); //月份1号的星期数
  188. if (this.firstDayOfWeek == "monday") firstWeek = firstWeek - 1 < 0 ? 6 : firstWeek - 1;
  189. let list = [];
  190. //每月显示42天,6周,每周7天
  191. for (var i = 0; i < 42; i++) {
  192. let dayInfo = {}; // 每天的详细信息
  193. if (i < firstWeek) { //指定月份上月的最后几天
  194. let {
  195. year,
  196. month
  197. } = this.getMonthDate(date, -1);
  198. let preMaxDay = new Date(year, month, 0).getDate(); //上月最大日期
  199. let day = preMaxDay - firstWeek + i + 1;
  200. dayInfo = this.getDayInfo({
  201. year,
  202. month,
  203. day
  204. }, 'prev');
  205. } else if (i > maxDay + firstWeek - 1) { //指定月份下月的前几天
  206. let {
  207. year,
  208. month
  209. } = this.getMonthDate(date);
  210. let day = i - maxDay - firstWeek + 1;
  211. dayInfo = this.getDayInfo({
  212. year,
  213. month,
  214. day
  215. }, 'next');
  216. } else {
  217. let day = i - firstWeek + 1;
  218. dayInfo = this.getDayInfo({
  219. year,
  220. month,
  221. day
  222. }, 'normal');
  223. }
  224. if (i % 7 == 0) list.push(new Array());
  225. list[list.length - 1].push(dayInfo);
  226. }
  227. return list;
  228. },
  229. /**
  230. * 获取指定日期的详细信息,包括农历节假日等
  231. * @param {Object} date = { year, month, day } 指定的日期
  232. * @param {String} dayType = [prev|next|normal] 日期类型,上月|下月|当前月
  233. */
  234. getDayInfo(date, dayType) {
  235. const {
  236. year,
  237. month,
  238. day
  239. } = date;
  240. let isToday = false; //是否今天
  241. if (year == this.today.year && month == this.today.month && day == this.today.day) isToday = true;
  242. let week = new Date(year + "/" + month + "/" + day).getDay(); //星期数
  243. let lunar = calendar.solar2lunar(year, month, day); //农历
  244. let dayLunar = lunar.IDayCn == '初一' ? lunar.IMonthCn + lunar.IDayCn : lunar.IDayCn;
  245. if (lunar.festival) dayLunar = lunar.festival; // 阳历节日
  246. else if (lunar.lunarFestival) dayLunar = lunar.lunarFestival; // 农历节日
  247. else if (lunar.Term) dayLunar = lunar.Term; // 节气
  248. let holidayArr = ["元旦", "春节", "清明节", "劳动节", "端午节", "中秋节", "国庆节"];
  249. let isHoliday = false;
  250. if (holidayArr.indexOf(dayLunar) != -1) isHoliday = true;
  251. let dayInfo = {
  252. date: year + "-" + month + "-" + day,
  253. year,
  254. month,
  255. day,
  256. week,
  257. lunar, // 农历
  258. dayLunar, // 显示的农历
  259. isToday, // 是否是今日
  260. isHoliday, // 是否是节假日
  261. dayType, // 日期类型,上月、下月或当前月
  262. sign: this.getSignByDate(date)
  263. }
  264. let dayClass = this.getDayClass(dayInfo);
  265. dayInfo["dayClass"] = dayClass;
  266. return dayInfo;
  267. },
  268. /**
  269. * 根据日期详细信息添加对应的class
  270. * @param {Object} dayInfo 日期详情
  271. */
  272. getDayClass(dayInfo) {
  273. let dClass = "";
  274. if (dayInfo.isToday) dClass += ' is-today'; // 今天日期
  275. if (dayInfo.isHoliday) dClass += ' is-holiday'; // 法定假日
  276. if (this.weekend && (dayInfo.week == 0 || dayInfo.week == 6)) dClass += ' week-end'; // 周末标红
  277. return dClass;
  278. },
  279. /**
  280. * 根据日期获取日期对应的事件
  281. * @param {Object} date = { year, month, day } 指定的日期
  282. */
  283. getSignByDate(date) {
  284. const {
  285. year,
  286. month,
  287. day
  288. } = date;
  289. let dayDateS = new Date(year + "/" + month + "/" + day + " 00:00:00").getTime();
  290. let dayDateE = new Date(year + "/" + month + "/" + day + " 23:59:59").getTime();
  291. let daySign = [];
  292. this.signArr.map(sign => {
  293. let signDate = sign.date.replace(/-/g, '/');
  294. let signTimes = new Date(sign.date).getTime();
  295. if (signTimes >= dayDateS && signTimes <= dayDateE) daySign.push(sign)
  296. })
  297. return daySign;
  298. },
  299. /**
  300. * 获取月份数据
  301. * @param {String} type=[pre|next]
  302. */
  303. getOtherData(type) {
  304. let nowMont = this.getMonthDate(this.selDate, type == 'prev' ? -1 : 1); // 获取当前月份
  305. this.selDate = nowMont; // 切换月份后设置选中的日期
  306. let monthData = this.getMonthData(this.getMonthDate(nowMont, type == 'prev' ? -1 : 1));
  307. // 获取上月或下月轮播所在位置
  308. let current = this.getTranIndex().prevNum;
  309. if (type == "next") current = this.getTranIndex().nextNum;
  310. this.allMonthList.splice(current, 1, monthData);
  311. this.judgeShowToday();
  312. this.returnMonthChange(type);
  313. },
  314. /**
  315. * 获取周数据
  316. * @param {String} type=[pre|next]
  317. */
  318. getOtherWeekData(type) {
  319. let oldSel = this.selDate; // 原选中的日期
  320. let newSel = this.getDateByDateAndDay(oldSel, type == 'prev' ? -7 : 7); // 获取7天前或后的日期
  321. if (oldSel.month != newSel.month) { // 跨月,先设置跨月后的月历
  322. // 设置月轮播位置
  323. let current = this.getTranIndex("month").prevNum;
  324. if (type == "next") current = this.getTranIndex("month").nextNum;
  325. this.tranIndex = current;
  326. this.getOtherData(type);
  327. }
  328. this.selDate = newSel;
  329. this.getWeekData(type);
  330. this.judgeShowToday();
  331. },
  332. // 从月历中获取周数据,切换周后获取上周或下周数据
  333. getWeekData(type) {
  334. const {
  335. prevNum: prevIndex,
  336. nowNum: nowIndex,
  337. nextNum: nextIndex
  338. } = this.getTranIndex("month");
  339. const {
  340. prevNum: prevCurrent,
  341. nowNum: nowCurrent,
  342. nextNum: nextCurrent
  343. } = this.getTranIndex("week");
  344. const {
  345. year: selYear,
  346. month: selMonth,
  347. day: selDay
  348. } = this.selDate;
  349. let sDate = selYear + "-" + selMonth + "-" + selDay
  350. let prevMonthList = this.allMonthList[prevIndex];
  351. let nowMonthList = this.allMonthList[nowIndex];
  352. let nextMonthList = this.allMonthList[nextIndex];
  353. for (let i = 0; i < nowMonthList.length; i++) {
  354. for (let j = 0; j < nowMonthList[i].length; j++) {
  355. if (sDate == nowMonthList[i][j].date) {
  356. this.returnDayChange(nowMonthList[i][j]); // 返回选中的日期
  357. if (type == "next") {
  358. this.allWeekList.splice(nextCurrent, 1, [nowMonthList[i + 1]]);
  359. if (i == 5) this.allWeekList.splice(nextCurrent, 1, [nextMonthList[1]]);
  360. } else {
  361. this.allWeekList.splice(prevCurrent, 1, [nowMonthList[i - 1]]);
  362. if (i == 0) {
  363. for (let k = prevMonthList.length - 1; k >= 0; k--) {
  364. if (prevMonthList[k][6].dayType == "normal") {
  365. this.allWeekList.splice(prevCurrent, 1, [prevMonthList[k]]);
  366. break;
  367. }
  368. }
  369. }
  370. }
  371. break;
  372. }
  373. }
  374. }
  375. },
  376. // 根据月份数据获取周数据,相当初始化周数据
  377. getAllWeekData() {
  378. const {
  379. prevNum,
  380. nowNum,
  381. nextNum
  382. } = this.getTranIndex("month");
  383. const {
  384. year: selYear,
  385. month: selMonth,
  386. day: selDay
  387. } = this.selDate;
  388. let sDate = selYear + "-" + selMonth + "-" + selDay; // 选中的日期
  389. let allWeekList = [
  390. [],
  391. [],
  392. []
  393. ];
  394. let prevMonthList = this.allMonthList[prevNum];
  395. let nowMonthList = this.allMonthList[nowNum];
  396. let nextMonthList = this.allMonthList[nextNum];
  397. for (let i = 0; i < nowMonthList.length; i++) {
  398. for (let j = 0; j < nowMonthList[i].length; j++) {
  399. if (sDate == nowMonthList[i][j].date) {
  400. allWeekList[0][0] = nowMonthList[i - 1];
  401. allWeekList[1][0] = nowMonthList[i];
  402. allWeekList[2][0] = nowMonthList[i + 1];
  403. if (i == 5) {
  404. allWeekList[2][0] = nextMonthList[1];
  405. } else if (i == 0) {
  406. for (let k = prevMonthList.length - 1; k >= 0; k--) {
  407. if (prevMonthList[k][6].dayType == "normal") {
  408. allWeekList[0][0] = prevMonthList[k];
  409. break;
  410. }
  411. }
  412. }
  413. break;
  414. }
  415. }
  416. }
  417. this.allWeekList = allWeekList;
  418. },
  419. // 滑动切换结束
  420. swiperEndChange() {
  421. this.tranDuration = 300;
  422. },
  423. // 滑动切换月份或周
  424. swiperChange(e) {
  425. let current = e.detail.current;
  426. let oldIndex = this.shrinkType ? this.tranCurrent : this.tranIndex;
  427. let type = (oldIndex - current == -1 || oldIndex - current == 2) ? 'next' : 'prev';
  428. if (this.shrinkType) {
  429. this.tranCurrent = current;
  430. if (current != oldIndex) this.getOtherWeekData(type);
  431. } else {
  432. this.tranIndex = current;
  433. if (current != oldIndex) this.getOtherData(type);
  434. }
  435. },
  436. // 点击切换月份或周(上月下月切换或上周下周切换)type = [prev|next] 切换类型
  437. changeMonthOrWeek(type) {
  438. if (!this.deterChange) return;
  439. this.deterChange = false;
  440. setTimeout(_ => {
  441. this.deterChange = true;
  442. }, 400); // 防止点击过快
  443. this.tranDuration = 300;
  444. let tranType = this.shrinkType ? 'week' : 'month';
  445. let current = this.getTranIndex(tranType).prevNum;
  446. if (type == "next") current = this.getTranIndex(tranType).nextNum;
  447. if (tranType == "week") {
  448. this.tranCurrent = current;
  449. this.getOtherWeekData(type);
  450. } else {
  451. this.tranIndex = current;
  452. this.getOtherData(type);
  453. }
  454. },
  455. // 点击收缩按钮,切换显示月份或显示周
  456. changeShrink() {
  457. this.shrinkType = !this.shrinkType;
  458. if (this.tranDuration != 0) this.tranDuration = 0;
  459. if (this.shrinkType) {
  460. this.tranCurrent = 1;
  461. this.getAllWeekData();
  462. }
  463. this.returnShrinkChange();
  464. this.judgeShowToday();
  465. },
  466. // 点击回到今天
  467. goToday() {
  468. if (this.tranDuration != 0) this.tranDuration = 0;
  469. let oldDate = JSON.parse(JSON.stringify(this.selDate));
  470. this.initDate();
  471. if (this.shrinkType) {
  472. this.tranCurrent = 1;
  473. this.getAllWeekData();
  474. let today = this.today;
  475. // 判断是否需要触发改变月份事件
  476. if (oldDate.year != today.year || oldDate.month != today.month) {
  477. this.returnMonthChange("today");
  478. } else {
  479. this.returnDayChange(this.today);
  480. }
  481. } else {
  482. this.returnMonthChange("today"); // 事件
  483. }
  484. this.judgeShowToday();
  485. },
  486. // 点击日期
  487. clickDay(dayInfo) {
  488. let {
  489. year,
  490. month,
  491. day
  492. } = this.selDate;
  493. if (day == dayInfo.day && month == dayInfo.month && year == dayInfo.year) return;
  494. let oldSel = JSON.parse(JSON.stringify(this.selDate));
  495. this.selDate.day = dayInfo.day;
  496. if (oldSel.month != dayInfo.month) {
  497. if (!this.shrinkType) {
  498. this.changeMonthOrWeek(dayInfo.dayType);
  499. return;
  500. } else {
  501. this.selDate.year = dayInfo.year;
  502. this.selDate.month = dayInfo.month;
  503. let current = this.getTranIndex("month").prevNum;
  504. if (dayInfo.dayType == "next") current = this.getTranIndex("month").nextNum;
  505. this.tranIndex = current;
  506. let monthData = this.getMonthData(this.getMonthDate(this.selDate, dayInfo.dayType == 'prev' ? -1 :
  507. 1));
  508. let current2 = this.getTranIndex("month").prevNum;
  509. if (dayInfo.dayType == "next") current2 = this.getTranIndex("month").nextNum;
  510. this.allMonthList.splice(current2, 1, monthData); // 设置上月或下月数据
  511. }
  512. this.returnMonthChange(dayInfo.dayType);
  513. } else {
  514. this.returnDayChange(dayInfo);
  515. }
  516. },
  517. // 判断是否需要显示回到今天(非本月或本周时显示)
  518. judgeShowToday() {
  519. const {
  520. year,
  521. month,
  522. day
  523. } = this.today;
  524. const {
  525. year: selYeat,
  526. month: selMonth,
  527. day: selDay
  528. } = this.selDate;
  529. if (this.shrinkType) { // 显示的周
  530. let selTimes = new Date(selYeat, selMonth - 1, selDay).getTime(); // 选中日期的时间戳
  531. let week = new Date(year, month - 1, day).getDay(); // 今天星期
  532. let firstWD = this.getDateByDateAndDay(this.today, -week + (this.firstDayOfWeek == "monday" ? 1 : 0));
  533. let lastWD = this.getDateByDateAndDay(this.today, 6 - week + (this.firstDayOfWeek == "monday" ? 1 :
  534. 0));
  535. let firstTimes = new Date(firstWD.year, firstWD.month - 1, firstWD.day).getTime();
  536. let lastTimes = new Date(lastWD.year, lastWD.month - 1, lastWD.day).getTime();
  537. if (selTimes > lastTimes || selTimes < firstTimes) this.showToday = true;
  538. else this.showToday = false;
  539. } else {
  540. if (year != selYeat || month != selMonth) this.showToday = true;
  541. else this.showToday = false;
  542. }
  543. },
  544. // 重新设置标记
  545. setSignList() {
  546. this.allMonthList.map(month => {
  547. month.map(week => {
  548. week.map(day => {
  549. day.sign = this.getSignByDate({
  550. year: day.year,
  551. month: day.month,
  552. day: day.day
  553. })
  554. })
  555. })
  556. })
  557. },
  558. /**
  559. * 添加标记
  560. * @param {Array} list 需要添加的标记
  561. */
  562. addSignList(list) {
  563. let signArr = this.signArr.concat(list);
  564. this.signArr = signArr;
  565. this.setSignList();
  566. },
  567. /**
  568. * 删除标记
  569. * 根据date和title共同判断是否删除
  570. * @param {Array} list 需要删除的标记
  571. */
  572. deleteSignList(list) {
  573. let signArr = this.signArr;
  574. signArr = signArr.filter(s => {
  575. if (list.find(l => l.date == s.date && l.title == s.title)) return false
  576. else return true;
  577. })
  578. this.signArr = signArr;
  579. this.setSignList();
  580. },
  581. /**
  582. * 事件 - 设置返回日期
  583. * @param {Object} dayInfo 日期详情
  584. */
  585. returnDayChange(dayInfo) {
  586. let {
  587. year,
  588. month,
  589. day
  590. } = dayInfo;
  591. let dayDate = year + "-" + (month < 10 ? '0' + month : month) + "-" + (day < 10 ? '0' + day : day)
  592. let returnData = {
  593. date: dayDate,
  594. year: year,
  595. month: month,
  596. day: day,
  597. week: dayInfo.week,
  598. daySign: dayInfo.sign
  599. }
  600. if (this.showLunar) returnData["lunar"] = dayInfo.lunar;
  601. this.$emit("dayChange", returnData);
  602. },
  603. /**
  604. * 事件 - 设置返回月份
  605. * @param {String} type 类型
  606. */
  607. returnMonthChange(type) {
  608. let selDate = this.selDate.year + "-" + this.selDate.month + "-" + this.selDate.day;
  609. let monthList = this.allMonthList.flat().flat(); // 二维转一维
  610. let dayInfo = monthList.find(day => day.date == selDate);
  611. this.returnDayChange(dayInfo)
  612. this.$emit("monthChange", {
  613. year: dayInfo.year,
  614. month: dayInfo.month,
  615. type: type
  616. });
  617. },
  618. /**
  619. * 事件 - 返回收缩状态
  620. */
  621. returnShrinkChange() {
  622. let type = this.shrinkType ? 'week' : 'month'
  623. this.$emit("shrinkClick", type);
  624. },
  625. /**
  626. * 获取上一个或下一个轮播位置
  627. * @param {String} type = [month|week] 轮播类型,月轮播(tranIndex),周轮播(tranCurrent)
  628. * @returns {Object} {prevNum, nowNum, nextNum}
  629. */
  630. getTranIndex(type = 'month') {
  631. let current = this.tranIndex;
  632. if (type == "week") current = this.tranCurrent;
  633. let prevNum = current - 1 < 0 ? 2 : current - 1;
  634. let nowNum = current;
  635. let nextNum = current + 1 > 2 ? 0 : current + 1;
  636. return {
  637. prevNum,
  638. nowNum,
  639. nextNum
  640. }
  641. },
  642. /**
  643. * 根据日期获取几天后的日期
  644. * @param {Object} date = {year, month, day} 当前日期
  645. * @param {Number} day 当前日期的几天前或几天后(负数)
  646. * @returns {Object} {year, month, day}
  647. */
  648. getDateByDateAndDay(date, num) {
  649. let dTime = new Date(date.year + "/" + date.month + "/" + date.day).getTime() + num * 24 * 60 * 60 * 1000;
  650. let nd = new Date(dTime);
  651. return {
  652. year: nd.getFullYear(),
  653. month: nd.getMonth() + 1,
  654. day: nd.getDate()
  655. }
  656. },
  657. /**
  658. * 获取几个月前或后的日期
  659. * @param {Object} date = {year, month, day} 当前日期
  660. * @param {Number} num 当前日期的num月前或后,默认1月后(下月)
  661. * @returns {Object} {year, month, day}
  662. */
  663. getMonthDate(date, num = 1) {
  664. let nextMonth = date.month + num;
  665. let diffYear = parseInt(Math.abs(nextMonth) / 12);
  666. let year = date.year;
  667. let month = nextMonth;
  668. if (nextMonth > 12) {
  669. year = date.year + diffYear;
  670. month = nextMonth % 12;
  671. } else if (nextMonth < 1) {
  672. year = date.year - (diffYear + 1);
  673. month = nextMonth + 12 * (diffYear + 1);
  674. }
  675. let monthMaxDay = new Date(year, month, 0).getDate(); // 月份最大日期
  676. let day = date.day > monthMaxDay ? monthMaxDay : date.day;
  677. return {
  678. year,
  679. month,
  680. day
  681. }
  682. },
  683. }
  684. }
  685. </script>
  686. <style lang="scss">
  687. .lunc-calendar {
  688. background-color: #FFF;
  689. // 头部
  690. .header {
  691. display: flex;
  692. flex-direction: row;
  693. justify-content: center;
  694. position: relative;
  695. height: 90rpx;
  696. line-height: 90rpx;
  697. border-bottom: 1px solid #DDD;
  698. .head-month {
  699. font-size: 36rpx;
  700. padding: 0 40rpx;
  701. }
  702. .go-to-today {
  703. position: absolute;
  704. right: 0;
  705. top: 26rpx;
  706. padding: 8rpx 12rpx 8rpx 22rpx;
  707. background-color: rgba(255, 184, 0, 1);
  708. border-radius: 22rpx 0 0 22rpx;
  709. font-size: 24rpx;
  710. line-height: 24rpx;
  711. color: #FFF;
  712. }
  713. .head-icon {
  714. width: 20rpx;
  715. height: 20rpx;
  716. margin-top: 36rpx;
  717. }
  718. .head-icon::after {
  719. content: '';
  720. display: block;
  721. width: 18rpx;
  722. height: 18rpx;
  723. border-top: 2rpx solid #606266;
  724. border-left: 2rpx solid #606266;
  725. }
  726. .head-icon.head-pre-month {
  727. transform: rotate(-45deg);
  728. }
  729. .head-icon.head-next-month {
  730. transform: rotate(135deg);
  731. }
  732. }
  733. // 星期
  734. .week-area {
  735. display: flex;
  736. flex-direction: row;
  737. align-items: center;
  738. border-bottom: 1px solid #EEE;
  739. padding: 16rpx 0;
  740. // margin: 0 10rpx;
  741. .week-font {
  742. flex: 1;
  743. text-align: center;
  744. color: #666;
  745. font-size: 28rpx;
  746. padding: 30rpx;
  747. }
  748. }
  749. // 日历
  750. .calendar-data {
  751. // transition: all 300ms;
  752. .swiper-item {
  753. position: relative;
  754. display: -webkit-box;
  755. .month-bg {
  756. position: absolute;
  757. font-size: 240px;
  758. font-weight: bold;
  759. top: 50%;
  760. left: 50%;
  761. transform: translate(-50%, -55%);
  762. color: #EEE;
  763. opacity: 0.4;
  764. z-index: -1;
  765. word-break: initial;
  766. }
  767. .month-days {
  768. width: 100%;
  769. margin-top: 14rpx;
  770. position: relative;
  771. // transition: all 300ms;
  772. .week-days {
  773. display: flex;
  774. flex-direction: row;
  775. .day {
  776. flex: 1;
  777. width: 14.28%;
  778. text-align: center;
  779. height: 84rpx;
  780. color: #000;
  781. padding: 0 6rpx;
  782. box-sizing: border-box;
  783. .day-info {
  784. height: 100%;
  785. display: flex;
  786. flex-direction: column;
  787. justify-content: flex-start;
  788. align-items: center;
  789. position: relative;
  790. .day-solar {
  791. display: block;
  792. font-size: 34rpx;
  793. line-height: 34rpx;
  794. margin-top: 18rpx;
  795. }
  796. .day-lunar,
  797. .day-sign {
  798. color: #909399;
  799. font-size: 24rpx;
  800. line-height: 24rpx;
  801. transform: scale(0.8);
  802. white-space: nowrap;
  803. }
  804. .day-sign {
  805. color: #F75858 !important;
  806. }
  807. .day-tag {
  808. content: "";
  809. position: absolute;
  810. top: 8rpx;
  811. right: 8rpx;
  812. width: 10rpx;
  813. height: 10rpx;
  814. border-radius: 50%;
  815. display: inline-block;
  816. background-color: #F75858;
  817. }
  818. }
  819. // 非当月日期
  820. .day-info.un-month {
  821. opacity: 0.25;
  822. transition: all 300ms;
  823. }
  824. .is-today {
  825. background-color: #b8b8b8a3 ;
  826. border-radius: 6rpx;
  827. color: #4CB2B6;
  828. }
  829. // 今天日期
  830. .is-today .day-solar,
  831. .is-today .day-sign,
  832. .is-today .day-lunar {
  833. color: #4CB2B6;
  834. }
  835. // 周末
  836. .week-end .day-solar {
  837. color: #FF9595;
  838. }
  839. // 法定假日
  840. .is-holiday .day-solar,
  841. .is-holiday .day-sign,
  842. .is-holiday .day-lunar {
  843. color: #F75858 !important;
  844. }
  845. // 当前选中的日期
  846. .is-sel {
  847. background-color: #4CB2B6;
  848. border-radius: 6rpx;
  849. color: #fff;
  850. }
  851. // 今天日期
  852. .is-sel .day-solar,
  853. .is-sel .day-sign,
  854. .is-sel .day-lunar {
  855. color: #FFFFFF;
  856. }
  857. }
  858. }
  859. .week-days.week-hide {
  860. display: none;
  861. }
  862. }
  863. .item-week {
  864. .un-month {
  865. opacity: 1 !important;
  866. }
  867. }
  868. }
  869. }
  870. // 收缩按钮
  871. .shrink {
  872. display: flex;
  873. justify-content: center;
  874. align-items: center;
  875. height: 60rpx;
  876. border-top: 1px solid #DDD;
  877. .shrink-btn {
  878. width: 32rpx;
  879. height: 32rpx;
  880. background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAMhJREFUOE/dky8LAkEQxd9w0WjV72KxWXfggmAyWWzXxHbNYjIJhoOZarvid9FqNB4jCyes3h9OLghu3fceb36zS+h5qKcf/xYgIisAayJKnHMa8lFVZ2YpgB0z7193FQYisgCwAXA3s8QLicgbhwC2zHwMgxshBkFeXzE2Nvh2rW8NsiybRFE0ZuZTXZCIzIuiuMZxfKltICJTAAciegDwIM9eqKozAKmZDQAsmTlvHSGY/1YKR00cWl9iGYRP8p220BXm7//CE8WyQBHBXZb9AAAAAElFTkSuQmCC);
  881. // transition: all 300ms;
  882. }
  883. .shrink-close {
  884. transform: rotate(-180deg);
  885. }
  886. }
  887. }
  888. </style>