index.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. /**
  2. * 防抖
  3. * debounce
  4. * @param {Function} fn
  5. * @param {Number} delay
  6. * @returns {Function}
  7. * @example
  8. * const fn = () => console.log('hello world');
  9. * const debounceFn = debounce(fn, 1000);
  10. */
  11. export const debounce = (fn, delay = 10) => {
  12. let timer = null;
  13. return function () {
  14. if (timer) {
  15. clearTimeout(timer);
  16. }
  17. timer = setTimeout(function () {
  18. //模拟触发change事件
  19. // Simulate triggering change event
  20. fn.apply(this, arguments);
  21. // 清空计时器
  22. // Clear timer
  23. timer = null;
  24. }, delay);
  25. };
  26. };
  27. /**
  28. * 节流
  29. * throttle
  30. * @param {Function} fn
  31. * @param {Number} delay
  32. * @returns {Function}
  33. */
  34. export const throttle = (fn, delay = 50) => {
  35. let timer = null;
  36. return function () {
  37. if (timer) {
  38. return;
  39. }
  40. let context = this;
  41. let args = arguments;
  42. timer = setTimeout(function () {
  43. fn.apply(context, args);
  44. timer = null;
  45. }, delay);
  46. };
  47. };
  48. /**
  49. * 节流
  50. * throttle With requestAnimationFrame
  51. * @param {Function} fn
  52. * @param {Number} delay
  53. * @returns {Function}
  54. */
  55. export const throttleWithRAF = (fn, delay = 16) => {
  56. let timeoutId = null;
  57. let rafId = null;
  58. let lastExec = 0;
  59. const throttledFn = function (...args) {
  60. const now = performance.now();
  61. const remainingTime = delay - (now - lastExec);
  62. if (rafId === null && timeoutId === null) {
  63. // 如果没有rafId和timeoutId,说明没有正在进行的动画帧或定时器
  64. rafId = requestAnimationFrame(() => {
  65. // 在动画帧开始时执行函数
  66. fn.apply(this, args);
  67. lastExec = performance.now();
  68. rafId = null;
  69. });
  70. } else if (remainingTime <= 0) {
  71. // 如果超过了延迟时间,取消当前的定时器,然后在下一个动画帧执行
  72. clearTimeout(timeoutId);
  73. timeoutId = null;
  74. rafId = requestAnimationFrame(() => {
  75. fn.apply(this, args);
  76. lastExec = performance.now();
  77. rafId = null;
  78. });
  79. } else {
  80. // 如果还有剩余时间,设置定时器
  81. clearTimeout(timeoutId);
  82. timeoutId = setTimeout(() => {
  83. rafId = requestAnimationFrame(() => {
  84. fn.apply(this, args);
  85. lastExec = performance.now();
  86. rafId = null;
  87. });
  88. }, remainingTime);
  89. }
  90. };
  91. throttledFn.clear = () => {
  92. if (timeoutId !== null) {
  93. clearTimeout(timeoutId);
  94. timeoutId = null;
  95. }
  96. if (rafId !== null) {
  97. cancelAnimationFrame(rafId);
  98. rafId = null;
  99. }
  100. };
  101. return throttledFn;
  102. };
  103. /**
  104. * 将数字按照步长进行四舍五入
  105. * Round the number according to the step length
  106. * @param {Number} num
  107. * @param {Number} step
  108. * @returns {Number}
  109. * @example
  110. * stepNumberFun(18, 5) // 20
  111. */
  112. export const stepNumberFun = (num, step = 1) => {
  113. //将step乘10,转换为整数,避免小数计算精度问题
  114. // Multiply step by 10, convert to integer, avoid decimal calculation precision problem
  115. const stepNum = step * 10;
  116. return (Math.round((num * 10) / stepNum) * stepNum) / 10;
  117. };
  118. /**
  119. * 传入Dom和一段字符串,返回占页面的宽度
  120. * Pass in Dom and a string, return the width occupied by the page
  121. * @param {HTMLElement} dom
  122. * @param {String} str
  123. * @returns {Number}
  124. * @example
  125. * getDomWidth(document.body, 'hello world') // 100
  126. */
  127. export const getDomWidth = (dom, str) => {
  128. let span = document.createElement('span');
  129. span.innerHTML = str;
  130. span.style.visibility = 'hidden';
  131. dom.appendChild(span);
  132. let width = span.getBoundingClientRect().width;
  133. dom.removeChild(span);
  134. return width;
  135. };
  136. /**
  137. * 根据年份和月份获取对应的天数
  138. * Get the number of days corresponding to the year and month according to the year and month
  139. * @param {*} year
  140. * @param {*} month
  141. * @returns {Number}
  142. * @example
  143. * getDayNum(2020, '02') // 29
  144. */
  145. export const getDayNum = (year, month) => {
  146. if (month === '02') {
  147. return isLeapYear(year) ? 29 : 28;
  148. } else if (month === '04' || month === '06' || month === '09' || month === '11') {
  149. return 30;
  150. } else {
  151. return 31;
  152. }
  153. };
  154. /**
  155. * 是否是闰年
  156. * Is it a leap year
  157. * @param {*} year
  158. * @returns {Boolean}
  159. * @example
  160. * isLeapYear(2020) // true
  161. */
  162. export const isLeapYear = year => {
  163. return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
  164. };
  165. /**
  166. * 返回当前日期所处周的开始与结束日期组成的数组,日期格式为 YYYYMMDD
  167. * return an array of start and end date of current week, date format is YYYYMMDD
  168. * @returns {Array}
  169. * @example
  170. * getWeekRange() // ['20200518', '20200524']
  171. */
  172. export const getWeekRange = () => {
  173. const now = new Date();
  174. const year = now.getFullYear();
  175. const month = now.getMonth();
  176. const day = now.getDate();
  177. const week = now.getDay();
  178. const start = new Date(year, month, day - week + 1);
  179. const end = new Date(year, month, day + (7 - week));
  180. return [start, end].map(date => {
  181. const year = date.getFullYear();
  182. const month = date.getMonth() + 1;
  183. const day = date.getDate();
  184. return `${year}${month < 10 ? `0${month}` : month}${day < 10 ? `0${day}` : day}`;
  185. });
  186. };
  187. /**
  188. * 返回当前日期所处月的开始与结束日期组成的数组,日期格式为 YYYYMMDD
  189. * return an array of start and end date of current month, date format is YYYYMMDD
  190. * @returns {Array}
  191. * @example
  192. * getMonthRange() // ['20200501', '20200531']
  193. */
  194. export const getMonthRange = () => {
  195. const now = new Date();
  196. const year = now.getFullYear();
  197. const month = now.getMonth();
  198. const start = new Date(year, month, 1);
  199. const end = new Date(year, month + 1, 0);
  200. return [start, end].map(date => {
  201. const year = date.getFullYear();
  202. const month = date.getMonth() + 1;
  203. const day = date.getDate();
  204. return `${year}${month < 10 ? `0${month}` : month}${day < 10 ? `0${day}` : day}`;
  205. });
  206. };
  207. /**
  208. * 传入年月数据 YYYYMM,返回开始日是周几
  209. * Pass in year and month data YYYYMM, return the start day of the week
  210. * @param {String} yearMonth
  211. * @returns {Number}
  212. * @example
  213. * getStartDay('202005') // 6
  214. */
  215. export const getStartDay = yearMonth => {
  216. const year = yearMonth.slice(0, 4);
  217. const month = yearMonth.slice(4);
  218. return new Date(`${year}-${month}-01`).getDay();
  219. };
  220. /**
  221. * 传入正数或者负数 n,返回当前月往前或者往后的 n 个月的年月数据,格式为 YYYYMM,不足两位的月份前面补 0
  222. * Pass in a positive or negative number n, return the year and month data of the current month forward or backward n months, the format is YYYYMM, and the month less than two digits is filled with 0
  223. * @param {Number} n 正整数或者负整数
  224. * @returns {String} 指定月份的年月数据 YYYYMM
  225. * @example
  226. * getYearMonth(1) // '202006'
  227. */
  228. export const getNowBeforeOrAfterMonth = n => {
  229. const now = new Date();
  230. const year = now.getFullYear();
  231. const month = now.getMonth() + 1;
  232. const newMonth = month + n;
  233. if (newMonth > 12) {
  234. return `${year + 1}${newMonth - 12 < 10 ? `0${newMonth - 12}` : newMonth - 12}`;
  235. } else if (newMonth < 1) {
  236. return `${year - 1}${newMonth + 12 < 10 ? `0${newMonth + 12}` : newMonth + 12}`;
  237. } else {
  238. return `${year}${newMonth < 10 ? `0${newMonth}` : newMonth}`;
  239. }
  240. };
  241. /**
  242. * 传入年月数据 YYYYMM,根据当月 1 日的周几,返回当月的日历数据数组。如果 startSunday 为 true 从周日开始,否则从周一开始,1 日之前的数据为空字符串,之后的数据为空字符串并去除
  243. * Pass in year and month data YYYYMM, return the calendar data array according to the week of the first day of the month. If startSunday is true, start from Sunday, otherwise start from Monday. The data before 1 day is an empty string, and the data after 1 day is an empty string and is removed
  244. * @param {String} yearMonth
  245. * @param {Boolean} startSunday
  246. * @returns {Array}
  247. * @example
  248. * getCalendarData('202005') // ['','','','','','','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31','']
  249. */
  250. export const getCalendarData = (yearMonth, startSunday) => {
  251. const year = yearMonth.slice(0, 4);
  252. const month = yearMonth.slice(4);
  253. const startDay = getStartDay(yearMonth);
  254. const dayNum = getDayNum(year, month);
  255. const data = [];
  256. for (let i = 0; i < (startDay > 0 ? startDay - (startSunday ? 0 : 1) : 7 - (startSunday ? 0 : 1)); i++) {
  257. data.push({ day: '' });
  258. }
  259. for (let i = 1; i <= dayNum; i++) {
  260. // @ts-ignore
  261. data.push({ day: i.toString().padStart(2, '0') });
  262. }
  263. for (let i = 0; i < 42 - startDay - (startSunday ? 0 : 1) - dayNum; i++) {
  264. data.push({ day: '' });
  265. }
  266. // 去除结尾的所有空字符
  267. // Remove all empty characters at the end
  268. while (data[data.length - 1].day === '') {
  269. data.pop();
  270. }
  271. // 如果 day 是 00 或 0 的替换为空字符
  272. // If day is 00, replace it with an empty character
  273. data.forEach(item => {
  274. if (item.day === '00' || item.day === '0') {
  275. item.day = '';
  276. }
  277. });
  278. // 循环 data,如果 day 不为 '',则计算出 day 是周几,放在 week
  279. // Loop data, if day is not '', then calculate that day is what day of the week, put it in week
  280. data.forEach(item => {
  281. if (item.day !== '') {
  282. // @ts-ignore
  283. item.week = new Date(`${year}-${month}-${item.day}`).getDay();
  284. }
  285. });
  286. // 循环 data,如果当天是月末,endDay 为 true,否则为 false
  287. // Loop data, if today is the end of the month, endDay is true, otherwise it is false
  288. data.forEach((item, index) => {
  289. if (item.day !== '') {
  290. // @ts-ignore
  291. item.endDay = index === data.length - 1;
  292. // @ts-ignore
  293. item.startDay = item.day === '01';
  294. }
  295. });
  296. // 根据从周一开始还是周末开始,循环 data,如果 day 不为 '',判断每天是 周第一天 还是 周最后一天,分别放在 weekStartDay 和 weekEndDay
  297. // According to whether it starts from Monday or Sunday, loop data, if day is not '', judge whether each day is the first day of the week or the last day of the week, and put it in weekStartDay and weekEndDay respectively
  298. if (!startSunday) {
  299. data.forEach(item => {
  300. if (item.day !== '') {
  301. // @ts-ignore
  302. item.weekStartDay = item.week === 1;
  303. // @ts-ignore
  304. item.weekEndDay = item.week === 0;
  305. }
  306. });
  307. } else {
  308. data.forEach(item => {
  309. if (item.day !== '') {
  310. // @ts-ignore
  311. item.weekStartDay = item.week === 0;
  312. // @ts-ignore
  313. item.weekEndDay = item.week === 6;
  314. }
  315. });
  316. }
  317. // 根据每一天的 startDay和weekStartDay,endDay和weekEndDay,只要有一项是 true,就是月第一天或周第一天,放在 start 和 end 中
  318. // According to the startDay and weekStartDay of each day, endDay and weekEndDay, as long as one item is true, it is the first day of the month or the first day of the week, put it in start and end
  319. data.forEach(item => {
  320. if (item.day !== '') {
  321. // @ts-ignore
  322. item.start = item.startDay || item.weekStartDay;
  323. // @ts-ignore
  324. item.end = item.endDay || item.weekEndDay;
  325. }
  326. });
  327. return data;
  328. };
  329. /**
  330. * 传入格式为 YYYYMM 的开始与结束月份字符串,返回这两个月份之间的所有月份数据数组,格式为 YYYYMM,不足两位的月份前面补 0
  331. * Pass in the start and end month strings in the format of YYYYMM, return the data array of all months between these two months, the format is YYYYMM, and the month less than two digits is filled with 0
  332. * @param {String} startMonthStr
  333. * @param {String} endMonthStr
  334. * @returns {Array}
  335. * @example
  336. * getMonthRange('202005', '202007') // ['202005', '202006', '202007']
  337. */
  338. export const getMonthListRange = (startMonthStr, endMonthStr) => {
  339. const startYear = startMonthStr.slice(0, 4);
  340. const startMonth = startMonthStr.slice(4);
  341. const endYear = endMonthStr.slice(0, 4);
  342. const endMonth = endMonthStr.slice(4);
  343. const data = [];
  344. if (startYear === endYear) {
  345. for (let i = Number(startMonth); i <= Number(endMonth); i++) {
  346. data.push(`${startYear}${i < 10 ? `0${i}` : i}`);
  347. }
  348. } else {
  349. for (let i = Number(startMonth); i <= 12; i++) {
  350. data.push(`${startYear}${i < 10 ? `0${i}` : i}`);
  351. }
  352. for (let i = 1; i <= Number(endMonth); i++) {
  353. data.push(`${endYear}${i < 10 ? `0${i}` : i}`);
  354. }
  355. }
  356. return data;
  357. };
  358. /**
  359. * 传入开始与结束日期 YYYYMMDD,需要剔除的日期组成的数组 disabledDates。返回中间的所有日期数组,包含开始与结束日期,月与日不足两位的前面补 0,格式为 YYYYMMDD
  360. * Pass in the start and end date YYYYMMDD, the array of disabled dates to be removed. Return the array of all dates in the middle, including the start and end dates, and the month and day less than two digits are filled with 0, in the format of YYYYMMDD
  361. * @param {String} startDate
  362. * @param {String} endDate
  363. * @param {Array} disabledDates
  364. * @returns {Array}
  365. * @example
  366. * getDateRange('20200501', '20200503') // ['20200501', '20200502', '20200503']
  367. */
  368. export const getDateRange = (startDate, endDate, disabledDates) => {
  369. // 将 startDate 与 endDate 转化为 YYYY-MM-DD格式
  370. // Convert startDate and endDate to YYYY-MM-DD format
  371. const startYear = startDate.slice(0, 4);
  372. const startMonth = startDate.slice(4, 6);
  373. const startDay = startDate.slice(6);
  374. const endYear = endDate.slice(0, 4);
  375. const endMonth = endDate.slice(4, 6);
  376. const endDay = endDate.slice(6);
  377. const startTimeStr = new Date(`${startYear}-${startMonth}-${startDay}`).getTime();
  378. const endTimeStr = new Date(`${endYear}-${endMonth}-${endDay}`).getTime();
  379. const startTime = new Date(startTimeStr).getTime();
  380. const endTime = new Date(endTimeStr).getTime();
  381. const data = [];
  382. for (let i = startTime; i <= endTime; i += 24 * 60 * 60 * 1000) {
  383. const date = new Date(i);
  384. const year = date.getFullYear();
  385. const month = date.getMonth() + 1;
  386. const day = date.getDate();
  387. data.push(`${year}${month < 10 ? `0${month}` : month}${day < 10 ? `0${day}` : day}`);
  388. }
  389. // 剔除 disabledDates 中的日期
  390. // Remove dates in disabledDates
  391. if (disabledDates && disabledDates.length) {
  392. disabledDates.forEach(item => {
  393. const index = data.indexOf(item);
  394. if (index > -1) {
  395. data.splice(index, 1);
  396. }
  397. });
  398. }
  399. return data;
  400. };
  401. /**
  402. * 传入 startSunday,返回当天所处周的所有日期数组,日期格式为 YYYYMMDD,月与日不足两位的前面补 0
  403. * Pass in startSunday, return the array of all dates in the week of the current day, the date format is YYYYMMDD, and the month and day less than two digits are filled with 0
  404. * @param {Boolean} startSunday
  405. * @returns {Array}
  406. * @example
  407. * getWeekRange(true) // ['20200531', '20200601', '20200602', '20200603', '20200604', '20200605', '20200606']
  408. */
  409. export const getCurrentWeek = (startSunday = false) => {
  410. const now = new Date();
  411. const year = now.getFullYear();
  412. const month = now.getMonth() + 1;
  413. const date = now.getDate();
  414. const day = now.getDay();
  415. const weekDates = [];
  416. if (startSunday) {
  417. for (let i = 0; i < 7; i++) {
  418. const newDate = new Date(year, month - 1, date - day + i);
  419. const newYear = newDate.getFullYear();
  420. const newMonth = newDate.getMonth() + 1;
  421. const newDateNum = newDate.getDate();
  422. // @ts-ignore
  423. weekDates.push(`${newYear}${newMonth.toString().padStart(2, '0')}${newDateNum.toString().padStart(2, '0')}`);
  424. }
  425. } else {
  426. for (let i = 1; i < 8; i++) {
  427. const newDate = new Date(year, month - 1, date - day + i);
  428. const newYear = newDate.getFullYear();
  429. const newMonth = newDate.getMonth() + 1;
  430. const newDateNum = newDate.getDate();
  431. // @ts-ignore
  432. weekDates.push(`${newYear}${newMonth.toString().padStart(2, '0')}${newDateNum.toString().padStart(2, '0')}`);
  433. }
  434. }
  435. return weekDates;
  436. };
  437. /**
  438. * 返回当天所处月份的所有日期数组,日期格式为 YYYYMMDD,月与日不足两位的前面补 0
  439. * Return the array of all dates in the month of the current day, the date format is YYYYMMDD, and the month and day less than two digits are filled with 0
  440. * @returns {Array}
  441. */
  442. export const getCurrentMonth = () => {
  443. const now = new Date();
  444. const year = now.getFullYear();
  445. const month = now.getMonth() + 1;
  446. const days = new Date(year, month, 0).getDate();
  447. const dates = [];
  448. for (let i = 1; i <= days; i++) {
  449. // @ts-ignore
  450. dates.push(`${year}${month.toString().padStart(2, '0')}${i.toString().padStart(2, '0')}`);
  451. }
  452. return dates;
  453. };
  454. /**
  455. * 返回当天所处季度的所有日期数组,日期格式为 YYYYMMDD,月与日不足两位的前面补 0
  456. * Pass in the date, return the array of all dates in the quarter of the current day, the date format is YYYYMMDD, and the month and day less than two digits are filled with 0
  457. * @returns {Array}
  458. */
  459. export const getCurrentQuarter = () => {
  460. const now = new Date();
  461. const year = now.getFullYear();
  462. const month = now.getMonth() + 1;
  463. const quarter = Math.floor((month - 1) / 3) + 1;
  464. const startMonth = (quarter - 1) * 3 + 1;
  465. const endMonth = quarter * 3;
  466. const days = [];
  467. for (let i = startMonth; i <= endMonth; i++) {
  468. const daysInMonth = new Date(year, i, 0).getDate();
  469. for (let j = 1; j <= daysInMonth; j++) {
  470. // @ts-ignore
  471. days.push(`${year}${i.toString().padStart(2, '0')}${j.toString().padStart(2, '0')}`);
  472. }
  473. }
  474. return days;
  475. };
  476. /**
  477. * 传入正数或负数天数 n 和是否从今天开始计算 today,返回对应的前后日期组成的数组,日期格式为 YYYYMMDD,月与日不足两位的前面补 0
  478. * Pass in a positive or negative number of days n, whether to include today hasToday, return the array of dates before and after the current day, the date format is YYYYMMDD, and the month and day less than two digits are filled with 0
  479. * @param {Number} n
  480. * @param {Boolean} today
  481. * @returns {Array}
  482. */
  483. export const getDaysRangeWithToday = (n = 0, today = true) => {
  484. const now = new Date();
  485. let fakeToday = now;
  486. if (today) {
  487. fakeToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  488. } else {
  489. if (n < 0) {
  490. fakeToday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
  491. } else if (n > 0) {
  492. fakeToday = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
  493. } else {
  494. fakeToday = now;
  495. }
  496. }
  497. const dates = [];
  498. for (let i = 0; i < Math.abs(n); i++) {
  499. const date = new Date(fakeToday.getTime() + (n > 0 ? i : -i) * 24 * 60 * 60 * 1000);
  500. // @ts-ignore
  501. dates.push(`${date.getFullYear()}${(date.getMonth() + 1).toString().padStart(2, '0')}${date.getDate().toString().padStart(2, '0')}`);
  502. }
  503. // 对日期排序
  504. // Sort dates
  505. dates.sort((a, b) => Number(a) - Number(b));
  506. return dates;
  507. };