├── .vscode ├── settings.json └── launch.json ├── src ├── lib │ ├── log │ │ ├── rule.md │ │ └── index.ts │ ├── isObject.ts │ ├── base64Encode.ts │ ├── base64Decode.ts │ ├── isEmptyStr.ts │ ├── union.ts │ ├── diffset.ts │ ├── sleep.ts │ ├── intersection.ts │ ├── isAndroid.ts │ ├── uniqueArray.ts │ ├── hasClass.ts │ ├── isJson.ts │ ├── isWeixin.ts │ ├── promiseWrap.ts │ ├── global.d.ts │ ├── isIOS.ts │ ├── extend.ts │ ├── isEmail.ts │ ├── isChinese.ts │ ├── randomColor.ts │ ├── parseUrl.ts │ ├── stringifyUrl.ts │ ├── isPhoneNum.ts │ ├── isMobile.ts │ ├── fileToBlob.ts │ ├── isEmptyObject.ts │ ├── checkType.ts │ ├── copyText.ts │ ├── setCookie.ts │ ├── computedStyle.ts │ ├── calcSameEleLength.ts │ ├── memo.ts │ ├── loadCss.ts │ ├── addClass.ts │ ├── removeClass.ts │ ├── omitStr.ts │ ├── calcStrLength.ts │ ├── removeCookie.ts │ ├── loadScript.ts │ ├── deleteUrlParam.ts │ ├── wait.ts │ ├── getCookie.ts │ ├── compose.ts │ ├── deepClone.ts │ ├── curry.ts │ ├── throttle.ts │ ├── formatDate.ts │ ├── strTrim.ts │ ├── cssFilter.ts │ ├── type.ts │ ├── decorator │ │ └── index.ts │ ├── debounce.ts │ ├── versionCompare.ts │ ├── wx │ │ ├── types.ts │ │ └── index.ts │ ├── event │ │ └── index.ts │ ├── http │ │ └── index.ts │ ├── performance │ │ └── index.ts │ ├── device │ │ └── index.ts │ ├── index.ts │ └── synthesis │ │ └── index.ts ├── index.ts ├── collect.js └── style.less ├── tea.yaml ├── .gitignore ├── rollup.config.js ├── babel.config.js ├── jest.config.js ├── README.md ├── test ├── calcStrLength.test.js ├── base64Encode.test.js ├── base64Decode.test.js ├── checkType.test.js ├── calcSameEleLength.test.js └── addClass.test.js ├── .eslintrc.js ├── index.html ├── tsconfig.json ├── config ├── webpack.dev.js ├── webpack.prod.js └── webpack.common.js ├── createConfig.js └── package.json /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5501 3 | } -------------------------------------------------------------------------------- /src/lib/log/rule.md: -------------------------------------------------------------------------------- 1 | // 规定打印的格式类型 2 | ```code 3 | [d-utils] 功能 其他参数信息 => 4 | ``` 5 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0x18fb62c5c736405ece7E8e7a2F01D084fFa77B6c' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | # lib/ 5 | typescript/ 6 | types/ 7 | coverage/ 8 | es/ 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { moduleLists, createConfig } from './createConfig' 4 | 5 | const config = moduleLists.map(item => createConfig(item.input, item.moduleType)) 6 | 7 | module.exports = config 8 | -------------------------------------------------------------------------------- /src/lib/isObject.ts: -------------------------------------------------------------------------------- 1 | import checkType from './checkType' 2 | 3 | /** 4 | * 判断是否是 Object 5 | */ 6 | function isObject (o: T): boolean { 7 | return checkType(o) === 'object' 8 | } 9 | 10 | export default isObject 11 | -------------------------------------------------------------------------------- /src/lib/base64Encode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 字符串转成base64编码 3 | * @param str 字符串 4 | * @return str base64 字符串 5 | */ 6 | function base64Encode (str: string): string { 7 | return window.btoa(str) 8 | } 9 | 10 | export default base64Encode 11 | -------------------------------------------------------------------------------- /src/lib/base64Decode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description base64解码成字符串 3 | * @param str base64字符串 4 | * @return 返回str字符串 5 | */ 6 | function base64Decode (str: string): string { 7 | return window.atob(decodeURIComponent(str)) 8 | } 9 | 10 | export default base64Decode 11 | -------------------------------------------------------------------------------- /src/lib/isEmptyStr.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断是否是空字符串 多个空格也视为空字符 3 | * @param str 需要校验的字符串 4 | * @return Boolean 是否是空字符串 5 | */ 6 | function isEmptyStr(str): boolean { 7 | return str.replace(/(^\s*)|(\s*$)/g, '').length === 0 8 | } 9 | 10 | export default isEmptyStr 11 | -------------------------------------------------------------------------------- /src/lib/union.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 返回数组之间的并集 3 | * @param { Array } args 可以是多个数组,数量不限制 4 | * @return { Array } 返回数组 5 | */ 6 | function union (...args: any[]): any[] { 7 | return Array.from(new Set([].concat(...args))) 8 | } 9 | 10 | export default union 11 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | "@babel/preset-env", { 5 | modules: false 6 | } 7 | ], 8 | // ["@babel/preset-react"] 9 | ], 10 | plugins: [ 11 | ["@babel/plugin-syntax-dynamic-import"] 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: [ 3 | "/test" 4 | ], 5 | testRegex: 'test/(.+)\\.test\\.(jsx?|tsx?)$', 6 | transform: { 7 | "^.+\\.(tsx|ts)?$": "ts-jest", 8 | "^.+\\.(jsx|js)?$": "babel-jest" 9 | }, 10 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 11 | }; 12 | -------------------------------------------------------------------------------- /src/lib/diffset.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 返回两个数组之间的差集 3 | * @param { Array } args 可以是多个数组,两个数组 4 | * @return { Array } 返回数组 5 | */ 6 | function diffset (a: any[], b: any[]): any[] { 7 | const setB = new Set(b) 8 | return a.filter((item) => { 9 | return !setB.has(item) 10 | }) 11 | } 12 | 13 | export default diffset -------------------------------------------------------------------------------- /src/lib/sleep.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 睡眠, 阻塞代码 timer毫秒 3 | * @param { number } timer 睡眠时长 执行后续的操作 4 | * @return promise 5 | */ 6 | function sleep (timer: number): Promise { 7 | return new Promise((resolve) => { 8 | setTimeout(() => { 9 | resolve() 10 | }, timer) 11 | }) 12 | } 13 | 14 | export default sleep 15 | -------------------------------------------------------------------------------- /src/lib/intersection.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 返回两个数组之间的交集 3 | * @param { Array } args 可以是多个数组,两个数组 4 | * @return { Array } 返回数组 5 | */ 6 | function intersection (a: any[], b: any[]): any[] { 7 | const setB = new Set(b) 8 | return a.filter((item) => { 9 | return setB.has(item) 10 | }) 11 | } 12 | 13 | export default intersection 14 | -------------------------------------------------------------------------------- /src/lib/isAndroid.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 判断是否是Android操作系统 3 | * @return { Boolean } 返回是否是Android的布尔值 4 | * @example 5 | * isAndroid() // false 6 | */ 7 | function isAndroid (): boolean { 8 | const ua = window.navigator.userAgent 9 | return (!!~ua.indexOf('Android')) || 10 | (!!~ua.indexOf('Adr')) 11 | } 12 | 13 | export default isAndroid 14 | -------------------------------------------------------------------------------- /src/lib/uniqueArray.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 数组去重 3 | * @param { Arrary } arr 要去重的arr 4 | * @return { Array } 返回一个新的数组,不改变原来的数组 5 | * @example 6 | * // [1, 2, 3, undefined, "4"] 7 | * uniqueArray([1,2,3,3,,3,3,'4',"4",'4',]) 8 | */ 9 | function uniqueArray (arr: any[]): any[] { 10 | return [...new Set(arr)] 11 | } 12 | 13 | export default uniqueArray 14 | -------------------------------------------------------------------------------- /src/lib/hasClass.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 判断元素是否存在某个class类 3 | * @param { HTMLElement } el dom元素 4 | * @param { String } className class名称 5 | * @example 6 | * hasClass(document.body, 'd-utils') 7 | */ 8 | function hasClass(el: HTMLElement, className: string): boolean { 9 | return el.classList.contains(className) 10 | } 11 | 12 | export default hasClass 13 | -------------------------------------------------------------------------------- /src/lib/isJson.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断是否是JSON字符串 3 | * @param str 需要校验的字符串 4 | * @return Boolean 是否是JSON格式的字符串 5 | */ 6 | function isJson (str: string): boolean { 7 | try { 8 | if (typeof JSON.parse(str) == "object") { 9 | return true; 10 | } 11 | return false 12 | } catch (e) { 13 | return false 14 | } 15 | } 16 | 17 | export default isJson 18 | -------------------------------------------------------------------------------- /src/lib/isWeixin.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 判断当前是否是微信浏览器 3 | * @return Boolean 4 | * @example 5 | * isWeiXin() // true 6 | */ 7 | function isWeiXin (): boolean { 8 | const ua = window.navigator.userAgent 9 | const uaLower = ua.toLowerCase() 10 | return String(uaLower.match(/MicroMessenger/i)) === 'micromessenger' 11 | } 12 | 13 | export default isWeiXin 14 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "启动程序", 11 | "program": "${workspaceFolder}/webpack.config.js" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /src/lib/promiseWrap.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description async / await 的错误处理 通过err的条件判断或者!res 的条件判断来走后续流程 3 | * @param { Promise } promise 4 | * @return { Array } array [0] 为err, array[1] 为data 5 | */ 6 | function wrap (promise: Promise) { 7 | return promise 8 | .then((res: any) => [null, res]) 9 | .catch((err: any) => [err, null]) 10 | } 11 | 12 | export default wrap -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { omitStr, memo, loadScript, addClass, calcSameEleLength, Log, loadCss } from './lib/index' 2 | 3 | const add = memo((a) => { 4 | console.log(100 + a) 5 | return 100 + a 6 | }) 7 | 8 | add(2) 9 | add(2) 10 | loadCss('https://daiwei.site/web_next/_next/static/css/styles.f02299a1.chunk.css') 11 | addClass(document.body, 'd-utils') 12 | Log.info('d-utils') 13 | -------------------------------------------------------------------------------- /src/lib/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'weixin-js-sdk' 2 | declare module 'axios' 3 | 4 | interface Window { 5 | Notification: any 6 | __D_UTILS_WX_FIRST_URL_HOOK__: any 7 | Dutils: any 8 | } 9 | 10 | interface Document { 11 | mozCancelFullScreen: any 12 | msExitFullscreen: any 13 | webkitCancelFullScreen: any 14 | msExiFullscreen: any 15 | webkitExitFullscreen: any 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/isIOS.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 正则表达式 判断是否为IOS设备 3 | */ 4 | export const EXP_IOS: RegExp = /(iPhone|iPad|iPod|iOS)/i 5 | 6 | /** 7 | * @description 判断是否是IOS操作系统 8 | * @return { Boolean } 返回是否是IOS的布尔值 9 | * @example 10 | * isIOS() // false 11 | */ 12 | export function isIOS(): boolean { 13 | const ua = window.navigator.userAgent 14 | return EXP_IOS.test(ua) 15 | } 16 | 17 | export default isIOS 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## d-utils 2 | 3 | v4版本将所有方法拆分 4 | 5 | # 快速使用 6 | #### 安装 (4.0) 7 | 使用npm安装 `d-utils` 依赖 8 | ```bash 9 | npm i d-utils 10 | ``` 11 | yarn 12 | ```hash 13 | yarn add d-utils 14 | ``` 15 | #### 使用 16 | 获取所有方法 17 | ```js 18 | import * as Dutils from 'd-utils' 19 | Dutils.addClass(document.body, 'd-utils') 20 | ``` 21 | 按需引入 22 | ```js 23 | import { addClass, Log } from 'd-utils' 24 | addClass(document.body, 'd-utils') 25 | Log.info('d-utils') 26 | ``` 27 | -------------------------------------------------------------------------------- /src/lib/extend.ts: -------------------------------------------------------------------------------- 1 | import deepClone from "./deepClone"; 2 | 3 | /** 4 | * @description extend继承方法 Object.assign(...arg)的包装 5 | * @param { Any } 参数为object对象 6 | * @returns { Object } 返回一个新的对象 7 | * @example 8 | * extend({a: 1}, {a: 2}) // {a: 1} 9 | * ⚠️ Object.assign属于浅拷贝,为了后续的操作不影响到之前的数据,最好在extend的第一个参数设置为{} 10 | */ 11 | function extend (...arg: any): any { 12 | return deepClone(Object.assign({}, ...arg)) 13 | } 14 | 15 | export default extend 16 | -------------------------------------------------------------------------------- /src/lib/isEmail.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 正则表达式 邮箱是否合法 3 | */ 4 | export const EXP_EMAIL: RegExp = /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/ 5 | 6 | /** 7 | * @description 判断email格式是否正确 8 | * @param { String } email 邮箱名称 字符串 9 | * @return { Boolean } true是有效 false无效 10 | * @example 11 | * isEmail('185098535@qq.com') // true 12 | */ 13 | function isEmail (email: string): boolean { 14 | return EXP_EMAIL.test(email) 15 | } 16 | 17 | export default isEmail 18 | -------------------------------------------------------------------------------- /src/lib/isChinese.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 正则表达式是否全部是 3 | */ 4 | export const EXP_CHINESE: RegExp = /^[\u3220-\uFA29]+$/ 5 | 6 | /** 7 | * @description 判断字符串是否都是中文 8 | * @param { String } str 9 | * @return Boolean 10 | * @example 11 | * isChinese('你好,世界') // false 12 | * isChinese('你好') // true 13 | * isChinese('world') // false 14 | */ 15 | function isChinese (str: string): boolean { 16 | return EXP_CHINESE.test(str) 17 | } 18 | 19 | export default isChinese 20 | -------------------------------------------------------------------------------- /src/lib/randomColor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 返回rgba随机色 3 | * @param { Number } opacity 透明度 0~1之间 4 | * @return { String } rgba色值 5 | * @example 6 | * const color = randomColor(1) 7 | * console(color) 8 | */ 9 | function randomColor (opacity: number = 1): string { 10 | const r = ~~(Math.random() * 256) 11 | const g = ~~(Math.random() * 256) 12 | const b = ~~(Math.random() * 256) 13 | return `rgba(${r},${g},${b},${opacity})` 14 | } 15 | 16 | export default randomColor 17 | -------------------------------------------------------------------------------- /src/lib/parseUrl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取url地址的参数信转化成键值对的对象格式 3 | * @param { string } url 解析的url地址 4 | * @example 5 | * UrlUtils.parseUrl('http://www.daiwei.org/?a=1&b=2') 6 | */ 7 | function parseUrl (url: string = window.location.search): any { 8 | const newUrl: string = url.slice(url.indexOf('?')) 9 | const sp: any = new URLSearchParams(newUrl) 10 | const obj = {} 11 | for (let [k, v] of sp.entries()) { 12 | obj[k] = v 13 | } 14 | return obj 15 | } 16 | 17 | export default parseUrl 18 | -------------------------------------------------------------------------------- /src/lib/stringifyUrl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description object对象转化成get请求的字符串形式 3 | * @param { Object } obj 需要操作的对象 4 | * @return { String } 返回一个字符串 a=1&b=2 5 | * @example 6 | * // 'a=1&b=2' 7 | * UrlUtils.stringifyUrl({a: 1, b: 2}) 8 | */ 9 | function stringifyUrl (obj: object): string { 10 | const arr = Object.entries(obj) 11 | return arr.map(item => { 12 | let [k, v] = [...item] 13 | return `${k}=${encodeURIComponent(v)}` 14 | }).join('&') 15 | } 16 | 17 | export default stringifyUrl 18 | -------------------------------------------------------------------------------- /src/lib/isPhoneNum.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 正则表达式 手机的合法校验 /^1[3-9]\d{9}$/ 3 | */ 4 | export const EXP_PHONE_NUM: RegExp = /^1[3-9]\d{9}$/ 5 | 6 | /** 7 | * @description 判断手机格式是否正确 8 | * @param { String } num 手机号 字符串 9 | * @return { Boolean } true是有效 false无效 10 | * @example 11 | * isPhoneNum('13651971940') // true 12 | */ 13 | function isPhoneNum (num: string): boolean { 14 | if (typeof num !== 'string') { 15 | return false 16 | } 17 | return EXP_PHONE_NUM.test(num) 18 | } 19 | 20 | export default isPhoneNum 21 | -------------------------------------------------------------------------------- /src/lib/isMobile.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 正则表达式 判断是否为移动设备 3 | */ 4 | export const EXP_MOBILE: RegExp = /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i 5 | 6 | /** 7 | * @description 判断是否是移动端 8 | * @return { Boolean } 返回是否是移动端的布尔值 9 | * @example 10 | * isMobile() // false 11 | */ 12 | function isMobile(): boolean { 13 | const ua = window.navigator.userAgent 14 | return EXP_MOBILE.test(ua) 15 | } 16 | 17 | export default isMobile 18 | -------------------------------------------------------------------------------- /src/lib/fileToBlob.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 文件转成blob流 3 | * @param { File } dataUrl 单个file 4 | * @return { Blob } 返回新的文件流 可以append到formdata中 5 | */ 6 | export function fileToBolb (dataUrl: any): Blob { 7 | const arr = dataUrl.split(',') 8 | const mime = arr[0].match(/:(.*?);/)[1] 9 | const bstr = atob(arr[1]) 10 | let n = bstr.length 11 | const u8arr = new Uint8Array(n) 12 | while (n--) { 13 | u8arr[n] = bstr.charCodeAt(n) 14 | } 15 | return new Blob([u8arr], { type: mime }) 16 | } 17 | 18 | export default fileToBolb 19 | -------------------------------------------------------------------------------- /src/lib/isEmptyObject.ts: -------------------------------------------------------------------------------- 1 | import isObject from './isObject' 2 | /** 3 | * @description 判断对象是否是空对象 4 | * @param { Object } 传入的对象 5 | * @return Boolean 是否是空对象 6 | * @example 7 | * let obj = { 8 | * a: 1, 9 | * b: 2 10 | * } 11 | * let obj1 = {} 12 | * isEmptyObject(obj) // false 13 | * isEmptyObject(obj1) // true 14 | */ 15 | function isEmptyObject (obj: any): boolean { 16 | if (!isObject(obj)) { 17 | return false 18 | } 19 | return Object.keys(obj).length === 0 20 | } 21 | 22 | export default isEmptyObject 23 | -------------------------------------------------------------------------------- /src/lib/checkType.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 检索数据类型并返回数据类型名称 object array string undefined bool number null 等等... 3 | * @param { Any } data 要判断的数据 4 | * @example 5 | * checkType('1') // string 6 | * @example 7 | * checkType({}) // object 8 | * @example 9 | * checkType([]) // array 10 | * @example 11 | * checkType(localStorage) // storage 12 | */ 13 | function checkType (data: T): string { 14 | let str = Object.prototype.toString.call(data) 15 | return str.match(/\[object (.*?)\]/)[1].toLowerCase() 16 | } 17 | 18 | export default checkType 19 | -------------------------------------------------------------------------------- /test/calcStrLength.test.js: -------------------------------------------------------------------------------- 1 | const calcStrLength = require('./../src/lib/calcStrLength').default 2 | 3 | describe('calcStrLength', function() { 4 | test('显示字符串长度', function() { 5 | const len = calcStrLength('test-calcStrLength') 6 | expect(len).toBe('test-calcStrLength'.length) 7 | }) 8 | 9 | test('中文按照两个字符长度计算', function() { 10 | const len = calcStrLength('hello中国', true) 11 | expect(len).toBe(9) 12 | }) 13 | 14 | test('特殊字符串测试 🌞', function() { 15 | const len = calcStrLength('🌞', true) 16 | expect(len).toBe(2) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /src/lib/copyText.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 复制网页文字到剪切板,之后可以粘贴在任何可粘贴的地方 3 | * @param { String } str 拷贝的内容 4 | * @example 5 | * copyText('hello world') 6 | */ 7 | function copyText (str: string): void { 8 | const textArea = document.createElement('textarea') 9 | textArea.style.cssText = 'position: absolute; top: -1000px; right: -1000px; z-index: -1000;' 10 | document.body.appendChild(textArea) 11 | textArea.value = str 12 | textArea.select() 13 | document.execCommand('copy') 14 | document.body.removeChild(textArea) 15 | } 16 | 17 | export default copyText 18 | -------------------------------------------------------------------------------- /src/lib/setCookie.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 设置Cookie 3 | * @param { String } name cookie名称 4 | * @param { String } value cooke的值 5 | * @param { Number } exp 过期时间 默认2小时 单位毫秒 6 | * @example 7 | * // 设置name为test的值为12345,设置过期时间为1小时 8 | * setCookie('test', '12345', 60 * 60 * 1000) 9 | */ 10 | function setCookie (name: string, value: string, exp: number = 60 * 60 * 2 * 1000): void { 11 | const date = new Date() 12 | date.setTime(date.getTime() + exp) 13 | document.cookie = `${name}=${escape(value)};expires=${date.toUTCString()}` 14 | } 15 | 16 | export default setCookie 17 | -------------------------------------------------------------------------------- /src/lib/computedStyle.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 获取元素的css属性内容 3 | * @param { HTMLElement } el dom元素 4 | * @param { String } cssProp css的属性名称 5 | * @return { String } css对应的属性的值 6 | * @example 7 | * computedStyle(document.body, 'width') 8 | */ 9 | 10 | function computedStyle (el: any, cssProp: string): void { 11 | if (!el) { 12 | return 13 | } 14 | if (!cssProp) { 15 | return 16 | } 17 | return document.defaultView.getComputedStyle ? document.defaultView.getComputedStyle(el, '')[cssProp] : el.currentStyle[cssProp] 18 | } 19 | 20 | export default computedStyle 21 | -------------------------------------------------------------------------------- /src/lib/calcSameEleLength.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 判断元素在数组或者字符串里存在的次数 3 | * @param { Array | String } target 存在的数组或字符串 4 | * @param { String | Number | ... } s 目标元素 值类型的元素 5 | * @return { Number } 数量 6 | */ 7 | function calcSameEleLength (target: T[] | string, s: T): number { 8 | if (typeof s == 'string' && s.length > 1) throw Error('元素只支持长度为1的字符查询') 9 | let newTarget = typeof target === 'string' ? target.split('') : target 10 | return (newTarget as []).reduce((t: number, c: T | string) => { 11 | return s === c ? t + 1 : t 12 | }, 0) 13 | } 14 | 15 | export default calcSameEleLength 16 | -------------------------------------------------------------------------------- /src/lib/memo.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 缓存函数 3 | * @param { Function } fn 需要被缓存的函数 4 | * @example 5 | * const memoFn = memo((a) => { 6 | * console.log('fn 被执行') 7 | * return a * 1000 8 | * }) 9 | * 10 | * memoFn(10) 11 | * 结果: console.log('fn 被执行') 12 | * memoFn(10) 13 | * 结果: 无console的打印,返回对象中存储的结果 14 | */ 15 | function memo(fn: Function) { 16 | let cache = {} 17 | return function (str: string | number | boolean | null | undefined) { 18 | let newStr = str.toString() 19 | let res = cache[newStr] 20 | return res || (cache[newStr] = fn(str)) 21 | } 22 | } 23 | 24 | export default memo 25 | -------------------------------------------------------------------------------- /test/base64Encode.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const base64Encode = require('./../src/lib/base64Encode').default 3 | 4 | describe('base64Encode', function() { 5 | test('正常字符串转码', function() { 6 | const str = base64Encode('hello d-utils') 7 | assert.equal(str, 'aGVsbG8gZC11dGlscw==') 8 | }) 9 | 10 | test('空格字符转base64', function() { 11 | const str = base64Encode(' ') 12 | assert.equal(str, 'IA==') 13 | }) 14 | 15 | test('base64字符转码', function() { 16 | const str = base64Encode('aGVsbG8gZC11dGlscw==') 17 | assert.equal(str, 'YUdWc2JHOGdaQzExZEdsc2N3PT0=') 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /test/base64Decode.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const base64Decode = require('./../src/lib/base64Decode').default 3 | 4 | describe('base64Decode', function() { 5 | test('base64转正常字符串', function() { 6 | const str = base64Decode('aGVsbG8gZC11dGlscw==') 7 | assert.equal(str, 'hello d-utils') 8 | }) 9 | 10 | test('base64转空格字符', function() { 11 | const str = base64Decode('IA==') 12 | assert.equal(str, ' ') 13 | }) 14 | 15 | test('base64字符转码', function() { 16 | const str = base64Decode('YUdWc2JHOGdaQzExZEdsc2N3PT0=') 17 | assert.equal(str, 'aGVsbG8gZC11dGlscw==') 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /test/checkType.test.js: -------------------------------------------------------------------------------- 1 | const checkType = require('./../src/lib/checkType').default 2 | 3 | describe('checkType', function() { 4 | test('判断字符串类型', function() { 5 | expect(checkType('hello world')).toBe('string') 6 | }) 7 | 8 | test('判断数字', function() { 9 | expect(checkType(1111)).toBe('number') 10 | }) 11 | 12 | test('判断数组', function() { 13 | expect(checkType([])).toBe('array') 14 | }) 15 | 16 | test('判断本地存储', function() { 17 | expect(checkType(window.localStorage)).toBe('storage') 18 | }) 19 | 20 | test('判断 null', function() { 21 | expect(checkType(null)).toBe('null') 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /src/lib/loadCss.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 动态加载css样式 3 | * @param url link的地址 4 | * @param config link属性配置 5 | * @returns Promise 6 | */ 7 | function loadCss (url: string, config?: Partial>): Promise { 8 | return new Promise((resolve, reject) => { 9 | try { 10 | const link = document.createElement('link'); 11 | link.href = url; 12 | for (let k in config) { 13 | link[k] = config[k] 14 | } 15 | document.getElementsByTagName('head')[0].appendChild(link); 16 | resolve() 17 | } catch (e) { 18 | reject(e) 19 | } 20 | }) 21 | } 22 | 23 | export default loadCss 24 | -------------------------------------------------------------------------------- /src/lib/addClass.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 判断元素是否存在某个class类 3 | * @param { HTMLElement } el dom元素 4 | * @param { String } className class名称 5 | * @example 6 | * hasClass(document.body, 'd-utils') 7 | */ 8 | import hasClass from './hasClass' 9 | 10 | function addClass(el: HTMLElement, className: string | string[]): void { 11 | if (Array.isArray(className)) { 12 | className.forEach((item: string) => { 13 | if (!hasClass(el, item)) { 14 | el.classList.add(item) 15 | } 16 | }) 17 | return 18 | } 19 | if (!hasClass(el, className)) { 20 | el.classList.add(className) 21 | } 22 | } 23 | 24 | export default addClass 25 | -------------------------------------------------------------------------------- /src/lib/removeClass.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 元素删除class 3 | * @param { HTMLElement } el dom元素 4 | * @param { (String | Array) } className class名称,可以是多个 5 | * @example 6 | * removeClass(document.body, 'd-utils') 7 | */ 8 | import hasClass from './hasClass' 9 | 10 | function removeClass(el: HTMLElement, className: string | string[]): void { 11 | if (Array.isArray(className)) { 12 | className.forEach((item: string) => { 13 | if (hasClass(el, item)) { 14 | el.classList.remove(item) 15 | } 16 | }) 17 | return 18 | } 19 | if (hasClass(el, className)) { 20 | el.classList.remove(className) 21 | } 22 | } 23 | 24 | export default removeClass 25 | -------------------------------------------------------------------------------- /src/lib/omitStr.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 格式化替换字符串 3 | * @param { string } str 需要操作的字符串 4 | * @param { number } start 开始位置 5 | * @param { number } end 结束位置 3 的话就是倒数第三个 倒数第三个不被替换 6 | * @param { string } replaceStr 替换的值 默认 '*' 7 | * @example 8 | * console.log(omitStr('185098535', 2, 3, '*')) 9 | * // 18****535 10 | */ 11 | function omitStr (str: string, start?: number, end?: number, replaceStr: string = '*'): string { 12 | const startPosition = start || 0; 13 | const endPosition = end || 0; 14 | const count = str.length - startPosition - endPosition 15 | return str.substr(0, startPosition) + replaceStr.repeat(count) + str.substr(count + startPosition, end) 16 | } 17 | 18 | export default omitStr 19 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "es6": true, 6 | "node": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "parserOptions": { 10 | "ecmaVersion": 2015, 11 | "sourceType": "module" 12 | }, 13 | "rules": { 14 | "indent": [ 15 | "error", 16 | 2 17 | ], 18 | "quotes": [ 19 | "error", 20 | "single" 21 | ], 22 | "no-console": 0, 23 | "semi": ["error", "never"], 24 | "no-mixed-spaces-and-tabs": [2, false], 25 | 'generator-star-spacing': 'off', 26 | "no-useless-escape": 'off' 27 | } 28 | }; -------------------------------------------------------------------------------- /src/lib/calcStrLength.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 计算字符串长度 isStrict为true的时候 返回一个字符串的长度,汉字算2个字符长度 3 | * @param { String } str 要计算的字符串 4 | * @param { Boolean } isStrict true 返回一个字符串的长度,汉字算2个字符长度; false 直接返回长度 5 | * @return { Number } 返回字符串长度 6 | * @example 7 | * const str = 'd-utils库' 8 | * console(calcStrLength(str)) 9 | * console(calcStrLength(str, true)) 10 | */ 11 | function calcStrLength (str: string, isStrict?: boolean): number { 12 | if (typeof str !== 'string') { 13 | return 0 14 | } 15 | 16 | if (!isStrict) return str.length 17 | 18 | return Array.from(str).reduce((total, current) => { 19 | return total += current.charCodeAt(0) > 255 ? 2 : 1 20 | }, 0) 21 | } 22 | 23 | export default calcStrLength 24 | -------------------------------------------------------------------------------- /test/calcSameEleLength.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const calcSameEleLength = require('./../src/lib/calcSameEleLength').default 3 | describe('calcSameEleLength', function() { 4 | test('数字在数组中的数量为3', function() { 5 | const count = calcSameEleLength([1, 2, 3, 2, 11, 2, 3, 44, 3], 3) 6 | expect(count).toBe(3) 7 | }) 8 | 9 | test('字符串数字数组中的数量应该为0', function() { 10 | const count = calcSameEleLength([1, 2, 3, 2, 11, 2, 3, 44, 3], '3') 11 | expect(count).toBe(0) 12 | }) 13 | 14 | test('字母在字符串中存在的数量', function() { 15 | const count = calcSameEleLength('1231231412314', '3') 16 | expect(count).toBe(3) 17 | }) 18 | 19 | test('空字符串判断', function() { 20 | const count = calcSameEleLength(' ', ' ') 21 | expect(count).toBe(6) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | d-utils 7 | 8 | 9 |

d-utils

10 | 通用js 11 | 查看文档 12 |
13 | 14 | 未曾遗忘的青春 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /src/lib/removeCookie.ts: -------------------------------------------------------------------------------- 1 | import getCookie from "./getCookie" 2 | 3 | /** 4 | * @description 删除Cookie 5 | * @param { String } name cookie名称 如果不传参数则设置所有cookie过期 6 | * @returns { Array } 是一个伪数组 7 | * @example 8 | * removeCookie('test') 9 | */ 10 | function removeCookie (name: string): any { 11 | const date = new Date() 12 | date.setTime(date.getTime() - 1) 13 | if (name) { 14 | const cookieInfo = getCookie(name) 15 | if (cookieInfo !== null) { 16 | document.cookie = `${name}=${cookieInfo};expires=${date.toUTCString()}` 17 | } 18 | return 19 | } 20 | const allCookies = getCookie() 21 | for (let k in allCookies) { 22 | document.cookie = `${allCookies[k].name}=${allCookies[k].value};expires=${date.toUTCString()}` 23 | } 24 | } 25 | 26 | export default removeCookie 27 | -------------------------------------------------------------------------------- /src/lib/loadScript.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 动态加载script标签 3 | * @param url script 的地址 4 | * @param config script配置 5 | * @returns Promise onload的 e 6 | */ 7 | function loadScript (url: string, config?: Partial>): Promise { 8 | return new Promise((resolve, reject) => { 9 | try { 10 | const body = document.body || document.getElementsByTagName('body')[0] 11 | const script = document.createElement('script') 12 | script.src = url 13 | script.onload = resolve 14 | script.onerror = reject 15 | for (let k in config) { 16 | script[k] = config[k] 17 | } 18 | body.appendChild(script) 19 | } catch (e) { 20 | reject(e) 21 | } 22 | }) 23 | } 24 | 25 | export default loadScript 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "ESNext", 5 | "removeComments": false, 6 | "declaration": true, 7 | "outDir": "./es", 8 | "declarationDir": "./es", 9 | "baseUrl": "./", 10 | "allowSyntheticDefaultImports": true, 11 | "jsx": "react", 12 | "experimentalDecorators": true, 13 | "downlevelIteration": true, 14 | "allowJs": false, 15 | "skipLibCheck": true, 16 | "typeRoots": [ 17 | "node_modules/@types", 18 | "global.d.ts", 19 | "typings" 20 | ], 21 | "lib": [ 22 | "dom", 23 | "es2015" 24 | ], 25 | "sourceMap": false, 26 | "noImplicitAny": false 27 | }, 28 | "files": [ 29 | "./src/lib/index.ts" 30 | ], 31 | "include": [ 32 | "src/lib/*.ts" 33 | ], 34 | "exclude": [ 35 | "node_modules" 36 | ] 37 | } -------------------------------------------------------------------------------- /src/lib/deleteUrlParam.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 移除url的某一个参数 3 | * @since 3.0.1 4 | * @param { Array } paramNames 参数名称的数组 5 | * @param { URL } url url地址 6 | * @return { String } 返回一个新地址 7 | * @example 8 | * UrlUtils.deleteUrlParam(['code', 'name'], 'http://localhost:2008/#a?a=22&b=2&code=3') 9 | * // 'http://localhost:2008/#a?a=22&b=2' 10 | */ 11 | export function deleteUrlParam (paramNames: string[], url: string = location.href) { 12 | const newSearch: string = url.split('?')[1] 13 | if (!newSearch) return url 14 | 15 | const hostAndPath: string = url.split('?')[0] 16 | const urlSearch = new URLSearchParams(newSearch) 17 | paramNames.forEach((param: string) => { 18 | urlSearch.delete(param) 19 | }) 20 | return urlSearch.toString() ? `${hostAndPath}?${urlSearch.toString()}` : hostAndPath 21 | } 22 | 23 | export default deleteUrlParam 24 | -------------------------------------------------------------------------------- /src/lib/wait.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 等待加载 3 | * @param { funciton } callback 一个停止轮训的while事件 返回值为boolean 返回true的时候则停止阻塞 开始执行后续的代码 4 | * @param { number } loopTime 单次轮训的时长 默认100毫秒 5 | * @param { number } timeout 超时的时间 默认10000毫秒 10秒 6 | * @return Promise 7 | */ 8 | function wait (callback: () => boolean, loopTime: number = 100, timeout: number = 10000): Promise { 9 | return new Promise((resolve, reject) => { 10 | if (typeof callback === 'function' && typeof callback() === 'boolean') { 11 | const t = setInterval(() => { 12 | if (callback()) { 13 | clearTimeout(t) 14 | clearTimeout(out) 15 | resolve() 16 | } 17 | }, loopTime) 18 | 19 | const out = setTimeout(() => { 20 | clearTimeout(t) 21 | clearTimeout(out) 22 | reject() 23 | }, timeout) 24 | } 25 | }) 26 | } 27 | 28 | export default wait 29 | -------------------------------------------------------------------------------- /src/lib/getCookie.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 获取Cookie 3 | * @param { String } name cookie名称 4 | * @returns { (Array | Null) } 返回数据 5 | * @example 6 | * getCookie('test') 7 | */ 8 | function getCookie (name?: string): string | any { 9 | if (name) { 10 | const reg = new RegExp(`(^| )${name}=([^;]*)(;|$)`) 11 | const arr = document.cookie.match(reg) 12 | return arr&&arr[2] ? arr[2] : null 13 | } 14 | const getAllCookies = [] 15 | if (document.cookie.length) { 16 | const arrCookie = document 17 | .cookie 18 | .split('; ') 19 | for (let k in arrCookie) { 20 | getAllCookies.push({ 21 | name: `${unescape(arrCookie[k].split('=')[0])}`, 22 | value: `${unescape(arrCookie[k].split('=')[1])}` 23 | }) 24 | } 25 | return getAllCookies 26 | } else { 27 | return null 28 | } 29 | } 30 | 31 | export default getCookie 32 | -------------------------------------------------------------------------------- /src/lib/compose.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 方法的从右往左执行 3 | * @param { Function } fns 各种方法 4 | * @example 5 | * compose(a, b, c)(...query) // a, b, c 皆为方法 6 | * 7 | * const testCompose = (name, age) => { 8 | * console.log('this name is: ', name) 9 | * console.log('this age is: ', age) 10 | * // 如果方法内部返回多个值作为后一个方法的参数,使用数组返回 11 | * return [name, age] 12 | * } 13 | * const full = (name, age) => { 14 | * console.log(`this is full: ${name} & ${age}`) 15 | * } 16 | * 17 | * compose(full, testCompose)('d-utils', 1) 18 | * 19 | * // this name is: d-utils 20 | * // this age is: 1 21 | * // this is full: d-utils & 1 22 | */ 23 | function compose (...fn: Function[]) { 24 | return function (...args: any) { 25 | return fn.reduceRight((prevResult, currentFn) => { 26 | return currentFn.call(this, ...prevResult) 27 | }, args) 28 | } 29 | } 30 | 31 | export default compose 32 | -------------------------------------------------------------------------------- /src/collect.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const logStr = 4 | `/** 5 | * name: d-utils 6 | * version: v4 7 | * author: ifmiss 8 | */\n 9 | ` 10 | const ignoreName = [ 11 | 'index.ts', 12 | 'type.ts', 13 | 'global.d.ts' 14 | ] 15 | const url = path.join(__dirname, 'lib') 16 | const a = fs.readdirSync(url) 17 | let str = logStr 18 | 19 | for (v of a) { 20 | if (ignoreName.includes(v)) continue 21 | const state = fs.statSync(path.resolve(url, v)) 22 | nameArr = v.split('.') 23 | const n = nameArr[0] 24 | const name = state.isDirectory() ? `./${v}/index` : `./${n}` 25 | const defineName = state.isDirectory() ? `${n.charAt(0).toUpperCase() + n.slice(1) 26 | }` : `${n}` 27 | const l = nameArr.length 28 | if (l) { 29 | str += `export { default as ${defineName} } from '${name}';\n \n` 30 | } 31 | } 32 | 33 | const indexUrl = path.resolve(url, 'index.ts') 34 | fs.writeFileSync(indexUrl, str) 35 | -------------------------------------------------------------------------------- /test/addClass.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const jsdom = require("jsdom"); 3 | const addClass = require('./../src/lib/addClass').default 4 | 5 | const { JSDOM } = jsdom; 6 | const dom = new JSDOM(``, { 7 | url: "https://daiwei.site/", 8 | referrer: "https://daiwei.site/", 9 | contentType: "text/html", 10 | includeNodeLocations: true, 11 | storageQuota: 10000000 12 | }); 13 | 14 | const body = dom.window.document.body 15 | 16 | describe('addClass', function() { 17 | test('测试添加一个class 名称', function() { 18 | addClass(body, 'test-class') 19 | assert.equal(body.className, 'test-class') 20 | }) 21 | 22 | test('添加一个相同的class', function() { 23 | addClass(body, 'test-class') 24 | assert.equal(body.className, 'test-class') 25 | }) 26 | 27 | test('参数使用 class数组', function() { 28 | const prevLenth = body.className.split(' ').length 29 | addClass(body, ['hello', 'world', 'd-utils']) 30 | const afterLenth = body.className.split(' ').length 31 | assert.equal(afterLenth - prevLenth, 3) 32 | }) 33 | }) -------------------------------------------------------------------------------- /src/lib/deepClone.ts: -------------------------------------------------------------------------------- 1 | import checkType from "./checkType" 2 | 3 | /** 4 | * @description 深拷贝 5 | * @param { Object } obj 被拷贝的对象 6 | * @return { Object } 返回新的对象 7 | * @example 8 | * let a = { 9 | * a: 1, 10 | * b: 2, 11 | * c: 3, 12 | * d: [1, 2] 13 | * } 14 | * let b = deepClone(a) 15 | * a.d[0] = 3 16 | * console.log(a) 17 | * // a: {a: 1, b: 2, c: 3, d: [3, 2]} 18 | * console.log(b) 19 | * // b: {a: 1, b: 2, c: 3, d: [1, 2]} 20 | * // 此时修改a.d[0]的值, a对象变化了,b对象没有随之改变 21 | */ 22 | function deepClone (obj: any): any { 23 | console.warn('deepClone 方法暂时没有做对象引用的优化,可食用 lodash 的 cloneDeep 方法') 24 | const result: any = {} 25 | const keys: any = Object.keys(obj) 26 | let type 27 | for (let k of keys) { 28 | type = checkType(obj[k]) 29 | switch (type) { 30 | case 'object': 31 | result[k] = deepClone(obj[k]) 32 | break 33 | case 'array': 34 | result[k] = [].concat(obj[k]) 35 | break 36 | default: 37 | result[k] = obj[k] 38 | } 39 | } 40 | return result 41 | } 42 | 43 | export default deepClone 44 | -------------------------------------------------------------------------------- /src/lib/curry.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 函数柯里化工具 3 | * @param { Function } fn 方法 4 | * @param { Any } agrs 参数,可选 5 | * @example 6 | * const addCur = function (a, b, c) { 7 | * console.log('a + b + c', a + b + c) 8 | * } 9 | * const reduceCur = function (a, b, c) { 10 | * console.log('a - b - c', a - b - c) 11 | * } 12 | * const add = curry(addCur, 2) 13 | * s(1)(2) // a + b + c 6 14 | * s(1, 3) // a + b + c 6 15 | * 16 | * const reduce = curry(reduceCur) 17 | * const reduce1 = curry(reduceCur) 18 | * reduce(1)(2)(3) // a - b - c -1 19 | * reduce1(1, 2, 3) // a - b - c -3 20 | */ 21 | function curry (fn: Function, ...arg: any): any { 22 | const _this = this 23 | const args = Array.from(arguments).slice(1) 24 | // fn.length 属性指明函数的形参个数。 25 | const len = fn.length 26 | 27 | return function () { 28 | const _args = Array.from(arguments) 29 | args.push(..._args) 30 | if (args.length < len) { 31 | return curry.call(_this, fn, ...args) 32 | } 33 | 34 | return fn.apply(_this, args) 35 | } 36 | } 37 | 38 | export default curry 39 | -------------------------------------------------------------------------------- /src/lib/throttle.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 函数节流 3 | * @param { Function } fn 需要节流的函数 4 | * @param { Number } t 节流时间,多久以后执行一次方法 单位ms 5 | * @example 6 | * // 在鼠标resize的过程中,1秒触发一次,如果resize了10秒相当于console.log('resize')只执行了10次 7 | * window.onresize = throttle(function () { 8 | * // es5 获取参数 9 | * let arg = Array.prototype.slice.call(arguments) 10 | * // es6 获取参数 11 | * let arg1 = Array.from(arguments) 12 | * console.log('resize-throttle', arg) 13 | * console.log('resize-throttle', arg1) 14 | * }, 1000) 15 | */ 16 | 17 | function throttle (fn: Function, t: number = 1000): Function { 18 | if (typeof fn !== 'function') { 19 | console.error(`第一个参数必须是方法`, '[d-utils] GenericUtils throttle error => ') 20 | return 21 | } 22 | const _fn = fn 23 | let time: any = null 24 | let first = true 25 | return function () { 26 | let arg = arguments 27 | let _this = this 28 | if (first) { 29 | _fn.apply(_this, arg) 30 | first = false 31 | return 32 | } 33 | if (time) return 34 | time = setTimeout(function () { 35 | setTimeout(time) 36 | time = null 37 | _fn.apply(_this, arg) 38 | }, t) 39 | } 40 | } 41 | 42 | export default throttle 43 | -------------------------------------------------------------------------------- /src/lib/formatDate.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 日期格式化 可转换成自己想要的格式 3 | * @param { Date } date 日期内容 如 当前日期 new Date() 4 | * @param { String } fmt 格式模板 'yyyy-MM-dd hh:mm:ss' 5 | * @return { String } '2018-08-15 01:46:22' 6 | * @example 7 | * formatDate(new Date(), `yyyy-MM-dd hh:mm:ss`) 8 | * @example 9 | * formatDate(new Date(), `yyyy-MM-dd`) 10 | */ 11 | function formatDate (date: Date = new Date(), fmt: string = 'yyyy-MM-dd hh:mm:ss'): any { // author: meizz 12 | const newDate = new Date(date) 13 | let o: object = { 14 | 'M+': newDate.getMonth() + 1, // 月份 15 | 'd+': newDate.getDate(), // 日 16 | 'h+': newDate.getHours(), // 小时 17 | 'm+': newDate.getMinutes(), // 分 18 | 's+': newDate.getSeconds(), // 秒 19 | 'q+': ~~((newDate.getMonth() + 3) / 3), // 季度 20 | 'S': newDate.getMilliseconds() // 毫秒 21 | } 22 | if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (newDate.getFullYear() + '').substr(4 - RegExp.$1.length)) } 23 | for (let k in o) { 24 | if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) } 25 | } 26 | return fmt 27 | } 28 | 29 | export default formatDate 30 | -------------------------------------------------------------------------------- /src/lib/strTrim.ts: -------------------------------------------------------------------------------- 1 | import { GenericType } from './type' 2 | /** 3 | * @description 字符串的去除空格 4 | * @param { String } str 操作的字符串 5 | * @param { Number } type 类型 0: 去除首位空格;1: 去除所有空格; 2: 去除左边空格; 3: 去除右边空格; 默认为去除首位空格 6 | * @return { String } 返回操作之后的字符串 7 | * @example 8 | * const str = ' d -js- ut ils ' 9 | * // 0: 去除首尾空格 默认为0 10 | * strTrim(str) 11 | * strTrim(str, 0) 12 | * @example 13 | * // 1: 去除所有空格 14 | * strTrim(str, 1) 15 | * @example 16 | * // 2: 去除左边空格 17 | * strTrim(str, 2) 18 | * @example 19 | * // 3: 去除右边空格 20 | * strTrim(str, 3) 21 | */ 22 | function strTrim (str: string, type: GenericType.StrTrimType = GenericType.StrTrimType.LEFT_RIGHT): string { 23 | if (typeof str !== 'string') { 24 | console.error(`str must be string but found ${typeof str}`, '[d-utils] GenericUtils strTrim error => ') 25 | return 26 | } 27 | switch (type) { 28 | case 0: 29 | return str.replace(/(^\s*)|(\s*$)/g, '') 30 | case 1: 31 | return str.replace(/\s/g, '') 32 | case 2: 33 | return str.replace(/(^\s*)/g, '') 34 | case 3: 35 | return str.replace(/(\s*$)/g, '') 36 | default: 37 | return str.replace(/(^\s*)|(\s*$)/g, '') 38 | } 39 | } 40 | 41 | export default strTrim 42 | -------------------------------------------------------------------------------- /src/lib/cssFilter.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description js设置元素的filter样式 3 | * @param { HTMLElement } el dom元素 4 | * @param { (String | Object) } type filter类型 blur、opacity、grayscale、sepia、saturate、hue-rotate、invert、brightness、contrast、drop-shadow, 当type为Object的时候就是显示一系列键值对,设置多个filter属性 `blur、opacity、grayscale、sepia、saturate、hue-rotate、invert、brightness、contrast、drop-shadow 5 | * @param { (String | Number) } option 参数 10px 10% 等等,根据不同type的类型设定不同的参数配置 6 | * @example 7 | * // 单个filter属性传参数 8 | * cssFilter(document.body, 'grayscale', 1) 9 | * // 多个filter属性传参数 10 | * cssFilter(document.body, { 11 | * grayscale: 0.5, 12 | * opacity: 0.7, 13 | * 'hue-rotate': '90deg' 14 | * }) 15 | */ 16 | function cssFilter(el: HTMLElement, type: any, option: string | number): void { 17 | if (typeof type === 'object' && !option) { 18 | let cssText = '' 19 | for (let k in type) { 20 | if (type.hasOwnProperty(k)) { 21 | cssText+= `${k}(${type[k]})` 22 | } 23 | } 24 | el.style.filter = cssText 25 | el.style.webkitFilter = cssText 26 | return 27 | } 28 | el.style.filter = `${type}(${option})` 29 | el.style.webkitFilter = `${type}(${option})` 30 | } 31 | 32 | export default cssFilter 33 | -------------------------------------------------------------------------------- /config/webpack.dev.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | const path = require('path'); 4 | const webpackPromptPlugin = require('webpack-prompt-plugin') 5 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 6 | 7 | const resolve = function (dir) { 8 | return path.resolve(__dirname, dir); 9 | } 10 | 11 | module.exports = merge(common, { 12 | mode: 'development', 13 | entry: { 14 | index: './src/index.ts' 15 | }, 16 | output: { 17 | path: resolve('dist'), 18 | publicPath: '/', 19 | filename: 'd-utils.js', 20 | libraryTarget: 'umd' 21 | }, 22 | 23 | module: { 24 | rules: [ 25 | ] 26 | }, 27 | 28 | plugins: [ 29 | new webpackPromptPlugin(), 30 | new HtmlWebpackPlugin({ 31 | filename: 'index.html', 32 | template: 'index.html', 33 | inject: true, 34 | minify: { 35 | removeComments: true 36 | } 37 | }), 38 | ], 39 | 40 | devServer: { 41 | // 当使用 HTML5 History API 时,任意的 404 响应都可能需要被替代为 index.html。通过传入以下启用: 42 | // contentBase: "./", 43 | host: '0.0.0.0', 44 | // 端口号 45 | port: 2001, 46 | //当有编译器错误或警告时,在浏览器中显示全屏覆盖。默认禁用。如果您只想显示编译器错误: 47 | noInfo: true, 48 | // 配置端口号 49 | overlay: true, 50 | historyApiFallback: true 51 | } 52 | }) 53 | -------------------------------------------------------------------------------- /src/lib/type.ts: -------------------------------------------------------------------------------- 1 | export namespace GenericType { 2 | export enum StrTrimType { 3 | /** 去除首尾字符 */ 4 | LEFT_RIGHT, 5 | 6 | /** 去除所有空格 */ 7 | ALL, 8 | 9 | /** 去除左边的空格 */ 10 | LEFT, 11 | 12 | /** 去除右边课空格 */ 13 | RIGHT 14 | } 15 | 16 | export interface INotification { 17 | title: string; 18 | body: string; 19 | icon: string; 20 | show: () => void; 21 | click: () => void; 22 | } 23 | } 24 | 25 | export namespace ImageUtilsType { 26 | export interface Resourse { 27 | content: string; 28 | left: number; 29 | top: number; 30 | needRound: boolean; 31 | type: string; 32 | fanmily?: string; 33 | color?: string; 34 | width?: number; 35 | maxWidth?: number; 36 | height?: number; 37 | } 38 | 39 | export enum FontStyle { 40 | fanmily = '14px Arial', 41 | color = '#d4546f' 42 | } 43 | 44 | export enum TextType { 45 | Text = 'text', 46 | Image = 'image' 47 | } 48 | } 49 | 50 | export namespace LogUtilsType { 51 | export interface ILogBeautyOptions { 52 | /** 53 | * 是否是较大显示console的高度,如果console的内容较多建议设置为false 默认为小格式 54 | */ 55 | isMax?: boolean; 56 | /** 57 | * 背景色列表,是一个从左向右渐变的过程 58 | */ 59 | colors?: string[]; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/lib/decorator/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 通用的装饰器 3 | */ 4 | import Log from './../log/index' 5 | 6 | class Decorator { 7 | /** 8 | * 装饰器,作用在类的方法上 9 | * 方法的 log 信息 10 | */ 11 | static log (target, name, descriptor): void { 12 | const fn = descriptor.value; 13 | descriptor.value = (...args) => { 14 | let result 15 | Log.groupCollapsed(`[d-utils] DecoratorUtils ${name}方法的执行信息`, Log.defaultColor) 16 | Log.default(`${name}(${args})`, `方法准备执行:`) 17 | Log.info(args, '详细的参数值: ') 18 | try { 19 | result = fn.apply(target, args) 20 | Log.success(result, `执行成功结果:`) 21 | } catch (err) { 22 | Log.error(err, `执行失败结果:`) 23 | } 24 | Log.groupEnd() 25 | } 26 | } 27 | 28 | /** 29 | * 装饰器,作用在类的方法上 30 | * 方法执行时间 31 | */ 32 | static fnTime (target, name, descriptor): object | void { 33 | const fn = descriptor.value 34 | if (typeof fn !== 'function') { 35 | Log.error(`${name}必须为方法`, `[d-utils] fnTime 执行失败结果: `) 36 | return 37 | } 38 | 39 | return { 40 | ...descriptor, 41 | value () { 42 | console.time(`[d-utils] ${name}方法执行时间: `); 43 | try { 44 | return fn.apply(target, arguments) 45 | } finally { 46 | console.timeEnd(`[d-utils] ${name}方法执行时间: `) 47 | } 48 | } 49 | } 50 | } 51 | } 52 | 53 | export default Decorator 54 | -------------------------------------------------------------------------------- /src/lib/debounce.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 函数防抖 3 | * @param { Function } fn 需要防抖的函数 4 | * @param { Number } t 防抖时间,多久以后才能再执行 单位ms 5 | * @param { Boolean } immediate true: 立刻执行方法且最后一次时间不执行, false: 等t时间之后再执行方法,如果t时间内执行,则在最后一次的t时间之后执行方法,类似动态搜索效果 6 | * @example 7 | * // 在鼠标resize的过程中,1秒以后可以被执行,如果在1秒内触发resize,则从新计算下一个一秒再允许执行 8 | * window.onresize = debounce(function () { 9 | * // es5 获取参数 10 | * let arg = Array.prototype.slice.call(arguments) 11 | * // es6 获取参数 12 | * let arg1 = Array.from(arguments) 13 | * console.log('resize-debounce', arg) 14 | * console.log('resize-debounce', arg1) 15 | * }, 1000) 16 | */ 17 | export function debounce (fn: Function, t: number, immediate: boolean = true): any { 18 | if (typeof fn !== 'function') { 19 | console.error(`第一个参数必须是方法`, '[d-utils] GenericUtils debounce error => ') 20 | return 21 | } 22 | let time: any 23 | // 立刻执行第一次该方法 24 | if (immediate) { 25 | return function () { 26 | clearTimeout(time) 27 | if (!time) { 28 | fn.apply(this, arguments) 29 | } 30 | time = setTimeout(function () { 31 | setTimeout(time) 32 | time = null 33 | }, t) 34 | } 35 | } else { 36 | // 满足 time 时间结束之后自动执行一次该方法 37 | return function () { 38 | clearTimeout(time) 39 | time = setTimeout(function () { 40 | setTimeout(time) 41 | fn.apply(this, arguments) 42 | time = null 43 | }, t) 44 | } 45 | } 46 | } 47 | 48 | export default debounce 49 | -------------------------------------------------------------------------------- /src/lib/versionCompare.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 支持相同版本号格式比较 3 | * @param current 当前版本 4 | * @param compare 比对的版本 5 | * @return { boolean } LESS_THAN 小于 EQUAL 等于 MORE_THAN 大于 6 | * @example 7 | * versionCompare("1.2.3", "1.2.4") // LESS_THAN 8 | * versionCompare("1.2.3", "1.2.3") // EQUAL 9 | * versionCompare("2.2.3", "1.2.3") // MORE_THAN 10 | */ 11 | 12 | export type RESULT_STATE = "UN_DEFINED" | "LESS_THAN" | "EQUAL" | "MORE_THAN"; 13 | 14 | 15 | function versionCompare(v1: string, v2: string): RESULT_STATE { 16 | let arrV1 = v1.split('.'); 17 | let arrV2 = v2.split('.'); 18 | let newLength; 19 | let result: RESULT_STATE = "UN_DEFINED"; 20 | 21 | // 记录初始化的length 22 | const baseV1l = newLength = arrV1.length; 23 | const baseV2l = arrV2.length; 24 | 25 | if (baseV1l !== baseV2l) { 26 | if (baseV1l > baseV2l) { 27 | arrV2 = arrV2.concat(Array(baseV1l - baseV2l).fill('0')); 28 | newLength = baseV1l; 29 | } else { 30 | arrV1 = arrV1.concat(Array(baseV2l - baseV1l).fill('0')); 31 | newLength = baseV2l; 32 | } 33 | } 34 | 35 | let i = 0; 36 | while (i < newLength && result == "UN_DEFINED") { 37 | if (Number(arrV1[i]) > Number(arrV2[i])) { 38 | result = "MORE_THAN"; 39 | } 40 | if (Number(arrV1[i]) < Number(arrV2[i])) { 41 | result = "LESS_THAN"; 42 | } 43 | if (Number(arrV1[i]) == Number(arrV2[i]) && i == newLength - 1) { 44 | result = "EQUAL"; 45 | } 46 | i++; 47 | } 48 | return result; 49 | } 50 | 51 | export default versionCompare; 52 | -------------------------------------------------------------------------------- /src/lib/wx/types.ts: -------------------------------------------------------------------------------- 1 | export interface IWeixinUtils { 2 | wx: any; 3 | plantSdkUrlIosOrAndorid (): void; 4 | wxSign (ticket: string): IWxSign; 5 | routerAuthorized (appId: string): void; 6 | initWxConfig (config: IWxConfig): void; 7 | wxShareToFriend (sharInfo: IWxShareToFriend): Promise 8 | wxShareToFriendCircle (sharInfo: IWxShareToFriendsCircle): Promise 9 | hideAllNonBaseMenuItem (): Promise 10 | hideMenuItems (arr: string[]): Promise 11 | wxShare (sharInfo: any): Promise 12 | } 13 | 14 | /** 验签拿到的三个字段 */ 15 | export interface IWxSign { 16 | timestamp: any; 17 | nonceStr: string; 18 | signature: string; 19 | } 20 | 21 | /** 微信config的配置信息 */ 22 | export interface IWxConfig { 23 | debug?: boolean; 24 | appId: string; 25 | timestamp: string | number; 26 | nonceStr: string; 27 | signature: string; 28 | jsApiList: string[]; 29 | } 30 | 31 | /** 微信分享回调的方法类型 */ 32 | export interface IWxCallBackType { 33 | // 微信的方法的类型 34 | type: string; 35 | // 返回的参数数据 36 | data: any; 37 | } 38 | 39 | /** 分享给朋友的分享字段 */ 40 | export interface IWxShareToFriend { 41 | title: string; 42 | desc: string; 43 | link: string; 44 | imgUrl: string; 45 | success?: (res: IWxCallBackType) => void; 46 | cancel?: (res: IWxCallBackType) => void; 47 | complete?: (res: IWxCallBackType) => void; 48 | } 49 | 50 | /** 分享到朋友圈的分享字段 */ 51 | export interface IWxShareToFriendsCircle { 52 | title: string; 53 | link: string; 54 | imgUrl: string; 55 | success?: (res: IWxCallBackType) => void; 56 | cancel?: (res: IWxCallBackType) => void; 57 | complete?: (res: IWxCallBackType) => void; 58 | } 59 | -------------------------------------------------------------------------------- /src/style.less: -------------------------------------------------------------------------------- 1 | .colortext-lt (@color1, @color2) { 2 | position: relative; 3 | background: -webkit-linear-gradient(left top, @color1 , @color2); /* Safari 5.1 - 6.0 */ 4 | background: -o-linear-gradient(bottom right, @color1, @color2); /* Opera 11.1 - 12.0 */ 5 | background: -moz-linear-gradient(bottom right, @color1, @color2); /* Firefox 3.6 - 15 */ 6 | background: linear-gradient(to bottom right, @color1 , @color2); /* 标准的语法 */ 7 | -webkit-background-clip: text; 8 | -webkit-text-fill-color: transparent; 9 | } 10 | 11 | body,html{ 12 | margin: 0; 13 | padding: 0; 14 | height:100%; 15 | background: #fff; 16 | } 17 | 18 | body{ 19 | // background: red; 20 | display: flex; 21 | width: 100%; 22 | height: 100%; 23 | align-items: center; 24 | flex-direction: column; 25 | justify-content: center; 26 | .title{ 27 | font-size: 0.82rem; 28 | display: inline-block; 29 | .colortext-lt(#CF8BF3, #FDB99B); 30 | margin-top: 0; 31 | } 32 | .disc{ 33 | font-size: 0.34rem; 34 | color: #333; 35 | } 36 | 37 | .href{ 38 | margin-top: 20px; 39 | color: #333; 40 | font-size: 0.32rem; 41 | transition: all 0.3s; 42 | cursor: pointer; 43 | &:link, &:active, &:visited{ 44 | color: #333; 45 | } 46 | } 47 | 48 | .github-info{ 49 | margin-top: 40px; 50 | text-align: center; 51 | display: flex; 52 | justify-content: center; 53 | span{ 54 | color: #666; 55 | font: Arial,"Helvetica Neue",Helvetica,"Microsoft Yahei","Hiragino Sans GB","Heiti SC","WenQuanYi Micro Hei",sans-serif; 56 | font-size: 0.28rem; 57 | } 58 | } 59 | .btn{ 60 | width: 320px; 61 | line-height: 40px; 62 | background: #999; 63 | cursor: pointer; 64 | color: #fff; 65 | text-align: center; 66 | margin-top: 30px; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/lib/event/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 一个简单的发布订阅者模式 3 | */ 4 | 5 | interface IHandler { 6 | fn: Function, 7 | type: string, 8 | name: string 9 | } 10 | 11 | class Event { 12 | /** 13 | * 键值对 对应事件名称以及数组的值 14 | */ 15 | static handler = {} 16 | 17 | /** 18 | * on 方法 添加监听事件 19 | */ 20 | static on (name: string, handler: Function): Event { 21 | const i:IHandler = { 22 | fn: handler, 23 | type: 'on', 24 | name: name 25 | } 26 | if (Object.keys(Event.handler).includes(name)) { 27 | Event.handler[name].push(i) 28 | return Event 29 | } 30 | Event.handler[name] = [].concat(i) 31 | return Event 32 | } 33 | 34 | /** 35 | * off 方法 移除监听事件 36 | */ 37 | static off (name: string, handler: Function): Event { 38 | const event: any[] = Event.handler[name] 39 | if (event) { 40 | for (let i = event.length - 1; i >= 0; i--) { 41 | if (event[i].fn === handler) { 42 | event.splice(i, 1) 43 | } 44 | } 45 | } 46 | return Event 47 | } 48 | 49 | /** 50 | * emit 方法 触发监听的事件 51 | */ 52 | static emit (name: string, ...args: any): Event { 53 | const event = Event.handler[name] 54 | let newEvent = [] 55 | event && event.length && event.forEach((item: IHandler, index: number) => { 56 | item.fn.call(this, ...args) 57 | 58 | // 如果有只监听一次的事件 59 | if (item.type !== 'once') { 60 | newEvent.push(event.slice(index, index + 1)) 61 | } 62 | }) 63 | 64 | const hasOnce = event && event.length && event.some((item: IHandler) => { 65 | return item.type === 'once' 66 | }) 67 | 68 | if (hasOnce) { 69 | Event.handler[name] = newEvent 70 | } 71 | 72 | // 这里做一个执行完成之后的 once代码 off 的操作 73 | return Event 74 | } 75 | 76 | /** 77 | * once 方法 添加事件 只会被执行一次 78 | */ 79 | static once (name: string, handler: Function): void { 80 | Event.on(name, handler) 81 | Event.handler[name][0]['type'] = 'once' 82 | } 83 | } 84 | 85 | export default Event 86 | -------------------------------------------------------------------------------- /src/lib/http/index.ts: -------------------------------------------------------------------------------- 1 | import stringifyUrl from './../stringifyUrl' 2 | import Log from './../log/index' 3 | 4 | export interface RequestInitWrapper extends RequestInit { 5 | timeOut?: number 6 | showTip?: boolean 7 | query?: object 8 | } 9 | 10 | export type HttpMethod = 11 | 'GET' | 12 | 'POST' | 13 | 'PUT' | 14 | 'PATCH' | 15 | 'DELETE' 16 | 17 | export type HttpContentType = 18 | 'application/json;charset=UTF-8' | 19 | 'application/x-www-form-urlencoded; charset=UTF-8' 20 | 21 | export interface InterfaceType { 22 | status: number; 23 | msg: string; 24 | result: T; 25 | } 26 | 27 | function timeout(ms: number = 12 * 1000) { 28 | return new Promise((resolve, reject) => { 29 | setTimeout(function () { 30 | resolve({ 31 | msg: '可能由于网络状态等原因,暂无数据', 32 | status: 0, 33 | result: null 34 | }); 35 | }, ms); 36 | }); 37 | } 38 | 39 | async function http(url: RequestInfo, c?: RequestInitWrapper): Promise> { 40 | let newUrl = url 41 | console.log('url', url) 42 | if (c && c.query && Object.keys(c.query).length > 0) { 43 | newUrl = `${url}?${stringifyUrl(c.query)}` 44 | } 45 | const config = Object.assign({}, { 46 | credentials: 'include', 47 | headers: { 48 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 49 | }, 50 | showTip: true 51 | }, c); 52 | 53 | const requestQueue = [ 54 | timeout(config.timeOut), 55 | window.fetch(`${newUrl}`, config) 56 | ]; 57 | return new Promise((resolve, reject) => { 58 | Promise.race(requestQueue).then((response: Response | any) => { 59 | if (response.ok || 60 | response.status >= 200 && response.status < 300) { 61 | response.json().then((res: InterfaceType) => { 62 | if (res.status && res.status === 200) { 63 | resolve(res); 64 | return; 65 | } 66 | if (config.showTip && res.msg) { 67 | Log.error(res.msg); 68 | } 69 | reject(res); 70 | }); 71 | } else { 72 | response && response.msg && Log.error(response.msg); 73 | reject(response); 74 | } 75 | }).catch((err: any) => { 76 | reject(err); 77 | }); 78 | }); 79 | }; 80 | 81 | interface StringObj { 82 | [props: string]: any 83 | } 84 | 85 | export function formatRequestBody(params: StringObj) { 86 | const newParamas = new URLSearchParams(); 87 | for (let k in params) { 88 | newParamas.append(k, params[k]); 89 | } 90 | return newParamas; 91 | } 92 | 93 | export default http; 94 | -------------------------------------------------------------------------------- /config/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge'); 2 | const path = require('path'); 3 | const TerserPlugin = require('terser-webpack-plugin'); 4 | const common = require('./webpack.common.js'); 5 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 6 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; 7 | 8 | // css压缩打包相关 9 | var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 10 | 11 | // 打包清除dist目录 12 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 13 | 14 | module.exports = merge(common, { 15 | mode: 'production', 16 | entry: { 17 | "index": './src/lib/index.ts' 18 | }, 19 | output: { 20 | path: path.resolve(__dirname, './../dist'), 21 | publicPath: '', 22 | filename: '[name].js', 23 | libraryTarget: 'umd', 24 | // library: "Dutils" 25 | }, 26 | module: { 27 | rules: [ 28 | { 29 | test: /\.js$/, 30 | use: { 31 | loader: 'babel-loader', 32 | } 33 | }, 34 | ] 35 | }, 36 | plugins: [ 37 | // 清除 38 | new CleanWebpackPlugin({ 39 | cleanOnceBeforeBuildPatterns: path.resolve(__dirname, 'dist') 40 | }), 41 | 42 | // css 压缩 43 | new OptimizeCssAssetsPlugin({}), 44 | 45 | // new BundleAnalyzerPlugin() 46 | ], 47 | optimization: { 48 | namedModules: true, 49 | minimizer: [ 50 | new TerserPlugin({ 51 | cache: true, 52 | parallel: true, 53 | sourceMap: false, // Must be set to true if using source-maps in production 54 | terserOptions: { 55 | // https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions 56 | } 57 | }), 58 | ], 59 | // splitChunks: { 60 | // chunks: "all", 61 | // minSize: 30000, 62 | // minChunks: 1, 63 | // maxAsyncRequests: 5, 64 | // maxInitialRequests: 3, 65 | // name: true, 66 | // cacheGroups: { 67 | // default: { 68 | // minChunks: 2, 69 | // priority: -20, 70 | // reuseExistingChunk: true, 71 | // }, 72 | // vendors: { 73 | // test: /[\\/]node_modules[\\/]/, 74 | // chunks: "initial", 75 | // name: "vendor", 76 | // priority: 10, 77 | // enforce: true, 78 | // }, 79 | // commons: { 80 | // name: 'vendors', 81 | // chunks: 'all', 82 | // minChunks: 2, 83 | // maxInitialRequests: 5, // The default limit is too small to showcase the effect 84 | // minSize: 0 // This is example is too small to create commons chunks 85 | // } 86 | // } 87 | // }, 88 | minimizer: [ 89 | new UglifyJsPlugin({ 90 | test: /\.js(\?.*)?$/i 91 | }), 92 | ] 93 | } 94 | }); -------------------------------------------------------------------------------- /createConfig.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import path from 'path'; 4 | import json from 'rollup-plugin-json'; 5 | import commonjs from 'rollup-plugin-commonjs'; 6 | import resolve from 'rollup-plugin-node-resolve'; 7 | import babel from 'rollup-plugin-babel'; 8 | import typescript2 from 'rollup-plugin-typescript2'; 9 | import clear from 'rollup-plugin-clear'; 10 | import pkg from './package.json'; 11 | import { terser } from 'rollup-plugin-terser'; 12 | import fs from 'fs'; 13 | 14 | export const LIB_DIR = path.join(__dirname, './', 'src/lib') 15 | 16 | export const ENTRY_MODULE_MAP = fs.readdirSync(path.resolve(LIB_DIR)).reduce((prev, name) => { 17 | const stat = fs.statSync(`${LIB_DIR}/${name}`) 18 | if (name.includes('.d.ts')) return prev 19 | if (stat.isDirectory()) { 20 | prev[`${name}/index`] = `${LIB_DIR}/${name}/index.ts`; 21 | } else { 22 | prev[`${name.replace('.ts', '')}`] = `${LIB_DIR}/${name}`; 23 | } 24 | return prev; 25 | }, {}); 26 | 27 | const HEADER_TEXT = ` 28 | /** 29 | * d-utils version: ${pkg.version} 30 | * by ifmiss 31 | */ 32 | ` 33 | 34 | const plugins = [ 35 | clear({ 36 | targets: ['dist'] 37 | }), 38 | json(), 39 | typescript2(), 40 | babel({ 41 | exclude: 'node_modules/**', 42 | runtimeHelpers: true 43 | }), 44 | resolve({ 45 | mainFields: 'main', 46 | modulesOnly: true 47 | }), 48 | commonjs({ 49 | include: 'node_modules/**', 50 | sourceMap: true, 51 | namedExports: { 52 | react: [ 53 | 'useState', 54 | 'useEffect', 55 | 'useMemo', 56 | 'useCallBack', 57 | 'useRef' 58 | ], 59 | 'react-router-dom': [ 60 | 'useLocation' 61 | ] 62 | } 63 | }), 64 | terser({ 65 | output: { 66 | comments: true 67 | } 68 | }) 69 | ] 70 | 71 | export const moduleLists = [ 72 | { 73 | input: { 74 | index: 'src/lib/index.ts', 75 | ...ENTRY_MODULE_MAP 76 | }, 77 | moduleType: 'es' 78 | }, 79 | { 80 | input: { 81 | index: 'src/lib/index.ts', 82 | }, 83 | moduleType: 'umd' 84 | }, 85 | { 86 | input: { 87 | index: 'src/lib/index.ts', 88 | ...ENTRY_MODULE_MAP 89 | }, 90 | moduleType: 'cjs' 91 | } 92 | ] 93 | 94 | const external = [ 95 | 'sha1', 96 | 'weixin-js-sdk' 97 | ] 98 | 99 | export const createConfig = (input, moduleType) => { 100 | return { 101 | input, 102 | output: { 103 | dir: path.resolve(__dirname, `dist/${moduleType}`), 104 | format: moduleType, 105 | banner: HEADER_TEXT, 106 | exports: 'auto', 107 | name: '[name].js' 108 | }, 109 | external, 110 | plugins 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /config/webpack.common.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 4 | 5 | const resolve = (dir) => { 6 | return path.resolve(__dirname, dir) 7 | } 8 | 9 | const devMode = process.env.NODE_ENV === "development" 10 | 11 | module.exports = { 12 | plugins: [ 13 | new MiniCssExtractPlugin ({ 14 | filename: "css/[name]-[hash].css", 15 | chunkFilename: "css/[name]-[hash].css" 16 | }), 17 | ], 18 | 19 | module: { 20 | rules: [ 21 | { 22 | test: /\.(js|jsx)$/, 23 | loader: 'babel-loader', 24 | query: { 25 | compact: false 26 | } 27 | }, 28 | { 29 | test: /\.(ts|tsx)$/, 30 | use: [ 31 | {loader: 'babel-loader',}, 32 | { 33 | loader: 'ts-loader', 34 | options: { 35 | // 加快编译速度 36 | transpileOnly: true, 37 | // 指定特定的ts编译配置,为了区分脚本的ts配置 38 | configFile: path.resolve(__dirname, './../tsconfig.json') 39 | } 40 | } 41 | ] 42 | }, 43 | { 44 | test: /\.(c)ss$/, 45 | use: [ 46 | devMode ? 'style-loader' : MiniCssExtractPlugin.loader, 47 | { 48 | loader: 'css-loader', 49 | options: { 50 | modules: true 51 | } 52 | }, 53 | { 54 | loader:"postcss-loader", 55 | options: { 56 | plugins: () => [ 57 | require('autoprefixer')() 58 | ] 59 | } 60 | }, 61 | ] 62 | }, 63 | { 64 | test: /\.less$/, 65 | use: [ 66 | devMode ? 'style-loader' : MiniCssExtractPlugin.loader, 67 | "css-loader", 68 | "less-loader" 69 | ], 70 | }, 71 | { 72 | test: /\.(ttf|eot|woff|woff2)$/, 73 | use: [ 74 | { 75 | loader: 'url-loader' 76 | } 77 | ] 78 | }, 79 | { 80 | test: /\.(png|jpg|gif)$/, 81 | use: [ 82 | { 83 | loader: 'url-loader', 84 | options: { 85 | limit: 8192 86 | } 87 | } 88 | ] 89 | }, 90 | { 91 | test: /\.svg$/, 92 | use: '@svgr/webpack' 93 | } 94 | ] 95 | }, 96 | 97 | resolve: { 98 | alias: { 99 | '@api': resolve('./../src/api'), 100 | '@assets': resolve('./../src/assets'), 101 | '@components': resolve('./../src/components'), 102 | '@constance': resolve('./../src/constance'), 103 | '@store': resolve('./../src/store'), 104 | '@styles': resolve('./../src/styles'), 105 | '@views': resolve('./../src/views'), 106 | '@utils': resolve('./../src/utils'), 107 | '@router': resolve('./../src/router') 108 | }, 109 | extensions: ['.tsx', '.ts', '.jsx', '.js'] 110 | }, 111 | } 112 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d-utils", 3 | "version": "4.0.3", 4 | "description": "d-utils", 5 | "main": "dist/umd/index.js", 6 | "module": "dist/es/index.js", 7 | "commonjs": "dist/cjs/index.js", 8 | "sideEffects": false, 9 | "types": "dist/es/index.d.ts", 10 | "scripts": { 11 | "test": "jest", 12 | "test:c": "jest --coverage", 13 | "collect": "node src/collect.js", 14 | "dev": "webpack-dev-server --config ./config/webpack.dev.js --progress", 15 | "build": "yarn collect && webpack --progress --config ./config/webpack.prod.js && tsc", 16 | "beta": "npm publish --tag=beta", 17 | "build:c": "yarn collect && rollup -c" 18 | }, 19 | "files": [ 20 | "dist/", 21 | "README.md" 22 | ], 23 | "keywords": [ 24 | "js", 25 | "utils", 26 | "js-tools" 27 | ], 28 | "author": "daiwei", 29 | "license": "ISC", 30 | "devDependencies": { 31 | "@babel/core": "^7.4.3", 32 | "@babel/plugin-proposal-class-properties": "^7.4.0", 33 | "@babel/plugin-proposal-decorators": "^7.4.0", 34 | "@babel/plugin-transform-runtime": "^7.4.3", 35 | "@babel/preset-env": "^7.4.3", 36 | "@babel/preset-react": "^7.9.4", 37 | "@babel/runtime": "^7.4.3", 38 | "@dw/webpack-prompt-plugin": "^1.0.8", 39 | "@rollup/pluginutils": "^3.1.0", 40 | "@types/jest": "^26.0.7", 41 | "@types/node": "^11.13.22", 42 | "babel-eslint": "^10.0.1", 43 | "babel-jest": "^26.2.1", 44 | "babel-loader": "^8.0.5", 45 | "babel-plugin-transform-decorators-legacy": "^1.3.5", 46 | "babel-polyfill": "^6.26.0", 47 | "babel-preset-stage-2": "^6.24.1", 48 | "clean-webpack-plugin": "^3.0.0", 49 | "css-loader": "^0.28.10", 50 | "eslint": "^6.6.0", 51 | "eslint-loader": "^2.1.0", 52 | "eslint-utils": "1.4.1", 53 | "extract-text-webpack-plugin": "^4.0.0-beta.0", 54 | "html-webpack-plugin": "^3.0.6", 55 | "jest": "^26.2.0", 56 | "jsdom": "^16.3.0", 57 | "less": "^3.9.0", 58 | "less-loader": "^4.1.0", 59 | "lodash": "^4.17.14", 60 | "loglevel": "^1.6.3", 61 | "macaddress": ">=0.2.9", 62 | "mini-css-extract-plugin": "^0.9.0", 63 | "npm": "^6.14.7", 64 | "optimize-css-assets-webpack-plugin": "^5.0.3", 65 | "randomatic": ">=3.0.0", 66 | "rollup": "^2.23.0", 67 | "rollup-plugin-babel": "^4.4.0", 68 | "rollup-plugin-clear": "^2.0.7", 69 | "rollup-plugin-commonjs": "^10.1.0", 70 | "rollup-plugin-json": "^4.0.0", 71 | "rollup-plugin-node-resolve": "^5.2.0", 72 | "rollup-plugin-terser": "^6.1.0", 73 | "rollup-plugin-typescript2": "^0.27.1", 74 | "style-loader": "^0.20.3", 75 | "ts-jest": "^26.1.4", 76 | "ts-loader": "^5.3.3", 77 | "typescript": "^3.3.3333", 78 | "uglifyjs-webpack-plugin": "^2.2.0", 79 | "url-loader": "^1.0.1", 80 | "url-parse": ">=1.4.3", 81 | "webpack": "^4.41.5", 82 | "webpack-bundle-analyzer": "^3.7.0", 83 | "webpack-cli": "^3.3.10", 84 | "webpack-dev-server": "^3.2.1", 85 | "webpack-merge": "^4.2.2", 86 | "webpack-node-externals": "^1.7.2", 87 | "webpack-prompt-plugin": "^1.1.3" 88 | }, 89 | "dependencies": { 90 | "sha1": "^1.1.1", 91 | "weixin-js-sdk": "^1.4.0" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/lib/performance/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 网页性能监测 3 | */ 4 | import Log from './../log/index' 5 | 6 | export default class PerformanceUtils { 7 | /** 8 | * @description window.performance对象 9 | */ 10 | static performance = window.performance 11 | 12 | /** 13 | * @description window.performance.timing对象 14 | */ 15 | static timing = window.performance.timing 16 | 17 | /** 18 | * @description DNS查询耗时 19 | * @description timing.domainLookupEnd - timing.domainLookupStart 20 | * @returns { number } 时差 单位:ms 21 | */ 22 | static dnsTime (): number { 23 | return PerformanceUtils.timing.domainLookupEnd - PerformanceUtils.timing.domainLookupStart 24 | } 25 | 26 | /** 27 | * @description 白屏时间 28 | * @description timing.domLoading - timing.navigationStart 29 | * @returns { number } 时差 单位:ms 30 | */ 31 | static loadTime (): number { 32 | return PerformanceUtils.timing.domLoading - PerformanceUtils.timing.navigationStart 33 | } 34 | 35 | /** 36 | * @description request请求耗时 37 | * @description timing.responseEnd - timing.responseStart 38 | * @returns { number } 时差 单位:ms 39 | */ 40 | static requestTime (): number { 41 | return PerformanceUtils.timing.responseEnd - PerformanceUtils.timing.responseStart 42 | } 43 | 44 | /** 45 | * @description TCP链接耗时 46 | * @description timing.connectEnd - timing.connectStart 47 | * @returns { number } 时差 单位:ms 48 | */ 49 | static tcpTime (): number { 50 | return PerformanceUtils.timing.connectEnd - PerformanceUtils.timing.connectStart 51 | } 52 | 53 | /** 54 | * @description 解析dom树耗时 55 | * @description timing.domComplete - timing.domInteractive 56 | * @returns { number } 时差 单位:ms 57 | */ 58 | static renderDomTime (): number { 59 | return PerformanceUtils.timing.domComplete - PerformanceUtils.timing.domInteractive 60 | } 61 | 62 | /** 63 | * @description domready时间(用户可操作时间节点) 64 | * @description timing.domContentLoadedEventEnd - timing.navigationStart 65 | * @returns { number } 时差 单位:ms 66 | */ 67 | static readyDomTime (): number { 68 | return PerformanceUtils.timing.domContentLoadedEventEnd - PerformanceUtils.timing.navigationStart 69 | } 70 | 71 | /** 72 | * @description onload时间(总下载时间) 73 | * @description timing.loadEventEnd - timing.navigationStart 74 | * @returns { number } 时差 单位:ms 75 | */ 76 | static loadFullTime (): number { 77 | return PerformanceUtils.timing.loadEventEnd - PerformanceUtils.timing.navigationStart 78 | } 79 | 80 | /** 81 | * @description 打印已知的所有数据信息 82 | */ 83 | static logger (): void { 84 | window.addEventListener('load', () => { 85 | setTimeout(() => { 86 | Log.group('[d-utils] PerformanceUtils logger - list: ', Log.infoColor) 87 | Log.default(PerformanceUtils.dnsTime(), 'DNS查询耗时') 88 | Log.default(PerformanceUtils.loadTime(), '白屏时间') 89 | Log.default(PerformanceUtils.requestTime(), 'request请求耗时') 90 | Log.default(PerformanceUtils.tcpTime(), 'TCP链接耗时') 91 | Log.default(PerformanceUtils.renderDomTime(), '解析dom树耗时') 92 | Log.default(PerformanceUtils.readyDomTime(), '用户可操作时间节点') 93 | Log.default(PerformanceUtils.loadFullTime(), 'onload时间') 94 | Log.groupEnd() 95 | }, 300) 96 | }) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/lib/device/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 设备相关 3 | */ 4 | 5 | class Device { 6 | /** 7 | * @description 横竖屏的判断,如果是横屏幕显示,显示dom提示竖屏显示 8 | * @param { String } 提示内容 9 | * @example 10 | * DeviceUtils.checkLayoutOrientation() // 横屏时候提示 请旋转屏幕,以达到更好的浏览效果 11 | * @example 12 | * DeviceUtils.checkLayoutOrientation('请竖直使用手机') // 横屏时候提示 请竖直使用手机 13 | */ 14 | static checkLayoutOrientation (text: string = '请旋转屏幕,以达到更好的浏览效果'): void { 15 | if (!window.hasOwnProperty('orientation')) return 16 | let ele: any = null 17 | // 0 和 360 的时候是竖屏 18 | function initOrientation () { 19 | const ori = window.orientation 20 | if (ori === 0 || ori === 360) { 21 | if (ele) { 22 | document.body.removeChild(ele) 23 | ele = null 24 | } 25 | } else { 26 | if (ele) return 27 | initTipInfo() 28 | } 29 | } 30 | function initTipInfo () { 31 | ele = document.createElement('div') 32 | ele.style.cssText = `position: fixed; 33 | top: 0; 34 | left: 0; 35 | right:0; 36 | bottom:0; 37 | display:flex; 38 | align-items:center; 39 | justify-content:center; 40 | font-size: 20px; 41 | background:#fff; 42 | z-index: 19940320; 43 | padding: 40px;` 44 | ele.innerText = text 45 | document.body.appendChild(ele) 46 | } 47 | function initEvent () { 48 | window.addEventListener('orientationchange', () => { 49 | initOrientation() 50 | }) 51 | } 52 | initOrientation() 53 | initEvent() 54 | } 55 | 56 | /** 57 | * @description 移动端REM的初始化js的方法,默认基于750的设计稿,可以限制最大显示宽度, 超出需要isFullOverMax 判断是否全屏幕显示, 不全屏则是body居中 58 | * @param { number } BaseWidth 基础的设计稿宽度 默认750 59 | * @param { number } MaxWidth 移动端最大的比例宽度点 默认document.body.clientWidth 60 | * @param { boolean } isFullOverMax 超出{MaxWidth}最大宽度的时候是否居中显示(body居中的前提是超出设定的宽度以及isFullOverMax=false) 默认false 61 | * @example 62 | * DeviceUtils.initRem() 63 | */ 64 | static initRem (BaseWidth: number = 750, MaxWidth: number = document.body.clientWidth, isFullOverMax: boolean = true): void { 65 | const r:any = {} 66 | const MaxWidthP = MaxWidth / BaseWidth 67 | 68 | r.Html = document.getElementsByTagName('html')[0] 69 | 70 | r.intiFontSize = function () { 71 | const baseOrientation = Math.min(document.body.clientWidth, document.body.clientHeight) 72 | let p = parseFloat((baseOrientation / BaseWidth).toFixed(4)) 73 | let s = p > MaxWidthP ? MaxWidthP : p 74 | if (isFullOverMax) s = p 75 | return s 76 | } 77 | 78 | r.updateFontSize = function () { 79 | r.Html.setAttribute('style', 'font-size:' + r.intiFontSize() * 100 + 'px') 80 | if (!isFullOverMax && document.body.clientWidth >= MaxWidth) { 81 | document.body.setAttribute('style', 'margin: 0 auto; width: 7.5rem') 82 | } 83 | } 84 | 85 | if (!document.addEventListener) return 86 | 87 | window.addEventListener('resize', r.updateFontSize, false) 88 | document.addEventListener('DOMContentLoaded', r.updateFontSize, false) 89 | } 90 | } 91 | 92 | export default Device 93 | -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * name: d-utils 3 | * version: v4 4 | * author: ifmiss 5 | */ 6 | 7 | export { default as addClass } from './addClass'; 8 | 9 | export { default as base64Decode } from './base64Decode'; 10 | 11 | export { default as base64Encode } from './base64Encode'; 12 | 13 | export { default as calcSameEleLength } from './calcSameEleLength'; 14 | 15 | export { default as calcStrLength } from './calcStrLength'; 16 | 17 | export { default as checkType } from './checkType'; 18 | 19 | export { default as compose } from './compose'; 20 | 21 | export { default as computedStyle } from './computedStyle'; 22 | 23 | export { default as copyText } from './copyText'; 24 | 25 | export { default as cssFilter } from './cssFilter'; 26 | 27 | export { default as curry } from './curry'; 28 | 29 | export { default as debounce } from './debounce'; 30 | 31 | export { default as Decorator } from './decorator/index'; 32 | 33 | export { default as deepClone } from './deepClone'; 34 | 35 | export { default as deleteUrlParam } from './deleteUrlParam'; 36 | 37 | export { default as Device } from './device/index'; 38 | 39 | export { default as diffset } from './diffset'; 40 | 41 | export { default as Event } from './event/index'; 42 | 43 | export { default as extend } from './extend'; 44 | 45 | export { default as fileToBlob } from './fileToBlob'; 46 | 47 | export { default as formatDate } from './formatDate'; 48 | 49 | export { default as getCookie } from './getCookie'; 50 | 51 | export { default as hasClass } from './hasClass'; 52 | 53 | export { default as Http } from './http/index'; 54 | 55 | export { default as intersection } from './intersection'; 56 | 57 | export { default as isAndroid } from './isAndroid'; 58 | 59 | export { default as isChinese } from './isChinese'; 60 | 61 | export { default as isEmail } from './isEmail'; 62 | 63 | export { default as isEmptyObject } from './isEmptyObject'; 64 | 65 | export { default as isEmptyStr } from './isEmptyStr'; 66 | 67 | export { default as isIOS } from './isIOS'; 68 | 69 | export { default as isJson } from './isJson'; 70 | 71 | export { default as isMobile } from './isMobile'; 72 | 73 | export { default as isObject } from './isObject'; 74 | 75 | export { default as isPhoneNum } from './isPhoneNum'; 76 | 77 | export { default as isWeixin } from './isWeixin'; 78 | 79 | export { default as loadCss } from './loadCss'; 80 | 81 | export { default as loadScript } from './loadScript'; 82 | 83 | export { default as Log } from './log/index'; 84 | 85 | export { default as memo } from './memo'; 86 | 87 | export { default as omitStr } from './omitStr'; 88 | 89 | export { default as parseUrl } from './parseUrl'; 90 | 91 | export { default as Performance } from './performance/index'; 92 | 93 | export { default as promiseWrap } from './promiseWrap'; 94 | 95 | export { default as randomColor } from './randomColor'; 96 | 97 | export { default as removeClass } from './removeClass'; 98 | 99 | export { default as removeCookie } from './removeCookie'; 100 | 101 | export { default as setCookie } from './setCookie'; 102 | 103 | export { default as sleep } from './sleep'; 104 | 105 | export { default as strTrim } from './strTrim'; 106 | 107 | export { default as stringifyUrl } from './stringifyUrl'; 108 | 109 | export { default as Synthesis } from './synthesis/index'; 110 | 111 | export { default as throttle } from './throttle'; 112 | 113 | export { default as union } from './union'; 114 | 115 | export { default as uniqueArray } from './uniqueArray'; 116 | 117 | export { default as wait } from './wait'; 118 | 119 | export { default as Wx } from './wx/index'; 120 | 121 | -------------------------------------------------------------------------------- /src/lib/log/index.ts: -------------------------------------------------------------------------------- 1 | import isObject from './../isObject' 2 | import { LogUtilsType } from './../type' 3 | /** 4 | * 日志的打印封装 5 | */ 6 | class Log { 7 | /** 8 | * 提示色 '#9E9E9E' 默认灰色 9 | */ 10 | static defaultColor: string = '#9E9E9E' 11 | 12 | /** 13 | * 提示色 '#0099FF' 蓝色 14 | */ 15 | static infoColor: string = '#0099FF' 16 | 17 | /** 18 | * 提示色 '#00CC99' 绿色 19 | */ 20 | static successColor: string = '#00CC99' 21 | 22 | /** 23 | * 提示色 '#CC3366' 红色 24 | */ 25 | static errorColor: string = '#CC0000' 26 | 27 | /** 28 | * 提示色 '#CC9966' 黄色 29 | */ 30 | static warningColor: string = '#FF9966' 31 | 32 | /** 33 | * console提示信息 34 | * @param { any } data 打印的数据信息 35 | * @param { string } dataTitile 提示文案 36 | * @param { string } color 颜色 37 | * @example 38 | * Log.console(window.screen, 'window:', 'red') 39 | */ 40 | static console (data: any, dataTitile: string = '数据信息', color: string = Log.defaultColor): void { 41 | if (isObject(data)) { 42 | if (Array.isArray(data)) { 43 | console.log(`%c${dataTitile}`, `color: ${color}; font-weight: bold`, data) 44 | } else { 45 | console.log(`%c${dataTitile}`, `color: ${color}; font-weight: bold`, {...data}) 46 | } 47 | return 48 | } 49 | console.log(`%c${dataTitile}`, `color: ${color}; font-weight: bold`, data) 50 | } 51 | 52 | /** 53 | * default提示信息 54 | * @param { any } data 打印的数据信息 55 | * @param { string } dataTitile 提示文案 56 | * @example 57 | * Log.default('date', 'default') 58 | */ 59 | static default (data: any, dataTitile: string = '[d-utils] log_utils default => '): void { 60 | Log.console(data, dataTitile, Log.defaultColor) 61 | } 62 | 63 | /** 64 | * info提示信息 65 | * @param { any } data 打印的数据信息 66 | * @param { string } dataTitile 提示文案 67 | * @example 68 | * Log.info('date', 'info') 69 | */ 70 | static info (data: any, dataTitile: string = '[d-utils] log_utils info => '): void { 71 | Log.console(data, dataTitile, Log.infoColor) 72 | } 73 | 74 | /** 75 | * success成功信息 76 | * @param { any } data 打印的数据信息 77 | * @param { string } dataTitile 提示文案 78 | * @example 79 | * Log.info('date', 'success') 80 | */ 81 | static success (data: any, dataTitile: string = '[d-utils] log_utils success => '): void { 82 | Log.console(data, dataTitile, Log.successColor) 83 | } 84 | 85 | /** 86 | * error失败信息 87 | * @param { any } data 打印的数据信息 88 | * @param { string } dataTitile 提示文案 89 | * @example 90 | * Log.info('date', 'error') 91 | */ 92 | static error (data: any, dataTitile: string = '[d-utils] log_utils error => '): void { 93 | Log.console(data, dataTitile, Log.errorColor) 94 | } 95 | 96 | /** 97 | * warn警告信息 98 | * @param { any } data 打印的数据信息 99 | * @param { string } dataTitile 提示文案 100 | * @example 101 | * Log.info('date', 'warn') 102 | */ 103 | static warn (data: any, dataTitile: string = '[d-utils] log_utils warning => '): void { 104 | Log.console(data, dataTitile, Log.warningColor) 105 | } 106 | 107 | /** 108 | * @description console的美化样式 109 | * @param { String } text 内容 110 | * @param { Object } options 配置项,对象,大小背景,和背景颜色设置 111 | * @property { Boolean } isMax 是否是较大显示console的高度,如果console的内容较多建议设置为false 默认为小格式 112 | * @property { Array } colors 背景色列表,是一个从左向右渐变的过程 113 | * @example 114 | * Log.beauty('hello world') 115 | * @example 116 | * Log.beauty('这是一个console的方法,可以设置背景色的哦', { 117 | * isMax: false, 118 | * colors: ['#fa709a', '#fee140', '#ffb199'] 119 | * }) 120 | */ 121 | static beauty (text: string = '未曾遗忘的青春', options?: LogUtilsType.ILogBeautyOptions): void { 122 | if (options && typeof options !== 'object') throw new TypeError(`options is an object, but found ${typeof options}`) 123 | let data = { 124 | isMax: false, 125 | colors: ['#a18cd1', '#fbc2eb', '#8ec5fc'] 126 | } 127 | let opt = Object.assign({}, data, options) 128 | if (opt.isMax) { 129 | console.log(`%c${text}`, `background-size: 100%;background-image: -moz-linear-gradient(left, ${opt.colors.toString()});background-image: -webkit-linear-gradient(left, ${opt.colors.toString()});background-image: linear-gradient(to right, ${opt.colors.toString()});padding:20px 40px;color:#fff;font-size:18px;`) 130 | } else { 131 | console.log(`%c${text}`, `background-size: 100%;background-image: -moz-linear-gradient(left, ${opt.colors.toString()});background-image: -webkit-linear-gradient(left, ${opt.colors.toString()});background-image: linear-gradient(to right, ${opt.colors.toString()});padding:2px 5px;color:#fff;font-size:12px;`) 132 | } 133 | } 134 | 135 | /** 136 | * log打印一个group组 默认全部展示折叠 137 | */ 138 | static group (dataTitile: string = '[d-utils] log_utils group => ', color: string = Log.defaultColor): void { 139 | console.group(`%c${dataTitile}`, `color: ${color}; font-weight: bold`) 140 | } 141 | 142 | /** 143 | * log打印一个group组 折叠的 144 | */ 145 | static groupCollapsed (dataTitile: string = '[d-utils] log_utils group_collapsed => ', color: string = Log.defaultColor): void { 146 | console.groupCollapsed(`%c${dataTitile}`, `color: ${color}; font-weight: bold`) 147 | } 148 | 149 | /** 150 | * 关闭一个console.group 151 | */ 152 | static groupEnd (): void { 153 | console.groupEnd() 154 | } 155 | 156 | /** 157 | * 打印一个table的表格数据 158 | * @param data 数组对象数据 159 | */ 160 | static table (data: any[]): void { 161 | console.table(data) 162 | } 163 | } 164 | 165 | export default Log 166 | -------------------------------------------------------------------------------- /src/lib/synthesis/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 图片合成等操作 3 | */ 4 | import Log from './../log/index' 5 | import { ImageUtilsType } from './../type' 6 | 7 | interface IImageUtils { 8 | addSourse: (resourse: ImageUtilsType.Resourse) => ImageUtils; 9 | compose: () => Promise; 10 | convertCanvasToImage: () => any; 11 | } 12 | 13 | export default class ImageUtils implements IImageUtils { 14 | /** 15 | * 基于当前屏幕的比例 16 | */ 17 | public persent: number = 1 18 | 19 | /** 20 | * canvas的元素 21 | */ 22 | private canvas: any = null 23 | 24 | /** 25 | * context canvas 上下文 26 | */ 27 | private context: any = null 28 | 29 | /** 30 | * 合并的背景地址 31 | */ 32 | private mainResource: string = '' 33 | 34 | /** 35 | * canvas的宽度 实际上数合并背景的宽度 36 | */ 37 | private canvasWidth: number = 0 38 | 39 | /** 40 | * canvas的高度 实际上数合并背景的高度 41 | */ 42 | private canvasHeight: number = 0 43 | 44 | public constructor (backgroud: string, persent?: number) { 45 | this.mainResource = backgroud 46 | if (persent) { 47 | this.persent = persent 48 | } 49 | } 50 | 51 | /** 52 | * 资源列表 53 | */ 54 | private resourceList: ImageUtilsType.Resourse[] = [] 55 | 56 | public addSourse (resourse: ImageUtilsType.Resourse): ImageUtils { 57 | this.resourceList.push(resourse) 58 | return this 59 | } 60 | 61 | /** 62 | * 加载图片 63 | */ 64 | private async loadResourse (src: string, resourse?: ImageUtilsType.Resourse): Promise { 65 | const image = new Image() 66 | image.crossOrigin = 'anonymous' 67 | image.src = src 68 | return new Promise((resolve, reject) => { 69 | image.onload = () => { 70 | const result = resourse ? { 71 | ref: image, 72 | ...resourse 73 | } : image 74 | resolve(result) 75 | } 76 | 77 | image.onerror = () => { 78 | Log.error(src, '[d-utils] ImageUtils loadResourse 裁剪图片加载错误') 79 | reject() 80 | } 81 | }) 82 | } 83 | 84 | private composeMainResource (image: any) { 85 | const radio = image.width / image.height 86 | this.canvasWidth = image.width * this.persent 87 | this.canvasHeight = this.canvasWidth / radio 88 | // 设置canvas的宽高 89 | this.canvas.width = this.canvasWidth 90 | this.canvas.height = this.canvasHeight 91 | this.context.drawImage(image, 92 | 0, 93 | 0, 94 | this.canvasWidth, 95 | this.canvasHeight) 96 | this.context.restore() 97 | } 98 | 99 | /** 100 | * @description cavans绘制效果 101 | * @param image addSourse 添加的数据信息 102 | */ 103 | private renderResource (image: any) { 104 | const newImageInfo = { 105 | left: image.left * this.canvasWidth, 106 | top: image.top * this.canvasHeight, 107 | width: image.width * this.canvasWidth, 108 | height: image.height * this.canvasHeight, 109 | } 110 | 111 | this.context.save() 112 | if (image.needRound) { 113 | // 走圆形绘制图片 此时都视为正方形 114 | this.context.arc(newImageInfo.width / 2 + newImageInfo.left, 115 | newImageInfo.width / 2 + newImageInfo.top, 116 | newImageInfo.width / 2, 117 | 0, 118 | Math.PI * 2, 119 | false) 120 | this.context.clip() 121 | this.context.drawImage(image.ref, 122 | newImageInfo.left, 123 | newImageInfo.top, 124 | newImageInfo.width, 125 | newImageInfo.width) 126 | this.context.restore() 127 | } else { 128 | // 走正常绘制 129 | this.context.drawImage(image.ref, 130 | newImageInfo.left, 131 | newImageInfo.top, 132 | newImageInfo.width, 133 | newImageInfo.height) 134 | this.context.restore() 135 | } 136 | } 137 | 138 | /** 139 | * @description 初始化canvas的设置 140 | * @return { Promise } 返回合成成功的image对象信息 141 | */ 142 | public async compose (): Promise { 143 | this.canvas = document.createElement('canvas') 144 | this.context = this.canvas.getContext('2d') 145 | 146 | const mainResource = await this.loadResourse(this.mainResource) 147 | this.composeMainResource(mainResource) 148 | 149 | // 开始执行 150 | const composeQueue: any[] = [] 151 | const resourceL = this.resourceList.reduce((total: any[], item: ImageUtilsType.Resourse, index: number): any[] => { 152 | if (item.type === ImageUtilsType.TextType.Image) { 153 | const data = this.loadResourse(item.content, item) 154 | total.push(data) 155 | } else { 156 | this.context.font = item.fanmily || ImageUtilsType.FontStyle.fanmily 157 | this.context.fillStyle = item.color || ImageUtilsType.FontStyle.color 158 | this.context.fillText(item.content, 159 | item.left * this.canvasWidth, 160 | item.top * this.canvasHeight) 161 | } 162 | return total 163 | }, composeQueue) 164 | 165 | const resolveQueue: any = await Promise.all(resourceL) 166 | 167 | // 再次绘制 168 | await resolveQueue.forEach((item: ImageUtilsType.Resourse) => { 169 | this.renderResource(item) 170 | }) 171 | 172 | return Promise.resolve(this.convertCanvasToImage()) 173 | } 174 | 175 | /** 176 | * @description canvase转换成图片 177 | * @return { Image } 返回一个new Image的实例 178 | */ 179 | public convertCanvasToImage (): any { 180 | const image = new Image() 181 | image.src = this.canvas.toDataURL('image/png', 1) 182 | Log.success(image, '[d-utils] ImageUtils convertCanvasToImage 图片对象创建成功') 183 | return image 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/lib/wx/index.ts: -------------------------------------------------------------------------------- 1 | import isIOS from '../isIOS' 2 | import isAndroid from './../isAndroid' 3 | import LogUtils from './../log/index' 4 | import parseUrl from './../parseUrl' 5 | 6 | import { 7 | IWxSign, 8 | IWxShareToFriend, 9 | IWxCallBackType, 10 | IWxShareToFriendsCircle, 11 | IWxConfig 12 | } from './types' 13 | 14 | const wx = require('weixin-js-sdk') 15 | const sha1 = require('sha1') 16 | 17 | /** 18 | * 微信相关的工具 19 | * 微信jssdk的操作 20 | */ 21 | export default class WeixinUtils { 22 | static wx: any = wx 23 | 24 | /** 25 | * @description 初始化微信请求 js-sdk 的url地址 需要区分两种情况 26 | * IOS 或者 Android 微信版本小于6.3.31, Android 微信版本大于6.3.31 27 | * 当前这种只支持与VUE单页面模式 28 | * @returns 返回获取jssdk的url参数值 29 | */ 30 | private static defaultShareInfo = { 31 | title: '这是一个微信分享的title', 32 | desc: '这是一个微信分享的desc', 33 | link: '这是一个微信分享的link', 34 | imgUrl: '这是一个微信分享的imgUrl', 35 | success: (): void => {}, 36 | cancel: (): void => {}, 37 | complete: (): void => {} 38 | } 39 | 40 | /** 41 | * ios 安卓需要验签的地址 42 | * @returns { string } 浏览器url 43 | */ 44 | static sdkUrlIosOrAndorid (): string { 45 | if (isIOS() || 46 | isAndroid() && !WeixinUtils.isUpThanWxVersion('6.3.31')) { 47 | if (window.__D_UTILS_WX_FIRST_URL_HOOK__) { 48 | return window.__D_UTILS_WX_FIRST_URL_HOOK__ 49 | } 50 | } 51 | return window.location.href.split('#')[0] 52 | } 53 | 54 | /** 55 | * @description IOS 或者 Android 微信版本小于6.3.31 需要种植首次进入页面的URL,用于解决微信签名错误 56 | */ 57 | static plantSdkUrlIosOrAndorid (): void { 58 | if (!window.__D_UTILS_WX_FIRST_URL_HOOK__) { 59 | window.__D_UTILS_WX_FIRST_URL_HOOK__ = window 60 | .location 61 | .href 62 | .split('#')[0] 63 | } 64 | } 65 | 66 | /** 67 | * @description wxSign 微信验签的动作 68 | * @param { String } jsapi_ticket 公众号用于调用微信JS接口的临时票据 69 | * @return { IWxSign } 返回 timestamp, nonceStr, signature 70 | */ 71 | 72 | static wxSign (ticket: string): IWxSign { 73 | const nonceStr = WeixinUtils.randomWord(16) 74 | const timestamp = (Date.now() + '').substr(0, 10) 75 | const url = WeixinUtils.sdkUrlIosOrAndorid() 76 | const str = `jsapi_ticket=${ticket}&noncestr=${nonceStr}×tamp=${timestamp}&url=${url}` 77 | const signature = sha1(str) 78 | return { timestamp, nonceStr, signature } 79 | } 80 | 81 | /** 82 | * 跳转微信oauth2授权登录 非静默授权 83 | * @param { String } appId 84 | */ 85 | static routerAuthorized (appId: string): void { 86 | let redirectUrl = window.location.href 87 | redirectUrl = encodeURIComponent(redirectUrl) 88 | window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirectUrl}&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect` 89 | } 90 | 91 | /** 92 | * randomWord 产生任意长度随机字母数字组合 93 | * min-任意长度最小位[固定位数] 94 | * max-任意长度最大位 95 | */ 96 | private static randomWord (min: number, max?: number): string { 97 | let str = '' 98 | let range = min 99 | const arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; 100 | 101 | // 随机产生 102 | if (max && max > min) { 103 | range = Math.round(Math.random() * (max - min)) + min 104 | } else { 105 | for (let i = 0; i < range; i++) { 106 | const pos: number = Math.round(Math.random() * (arr.length - 1)) 107 | str += arr[pos]; 108 | } 109 | } 110 | return str; 111 | } 112 | 113 | /** 114 | * @description 是否高于微信某一个版本 115 | * @param { String } version 116 | * @returns { Boolean } 返回是否满足条件 117 | */ 118 | private static isUpThanWxVersion (version: string = '6.3.31'): boolean { 119 | const str = window.navigator.userAgent 120 | const v0 = version.split('.').map((v) => { 121 | return parseInt(v, 10); 122 | }) 123 | const regExp = /MicroMessenger\/([\d|\.]+)/ 124 | if (regExp.exec(str) === null) { 125 | return false 126 | } 127 | const vv: any[] = regExp.exec(str) || [] 128 | let v1 = vv[1].split('.') 129 | 130 | if (v1.length >= 4) v1 = v1.slice(0, 3) 131 | 132 | v1 = v1.map((v: any) => { 133 | return parseInt(v, 10) 134 | }) 135 | if (v1[0] > v0[0]) return true 136 | if (v1[0] === v0[0] && v1[1] > v0[1]) return true 137 | if (v1[0] === v0[0] && v1[1] === v0[1] && v1[2] >= v0[2]) return true 138 | return false 139 | } 140 | 141 | /** 142 | * @description 初始化微信配置签名 143 | * @param { Object } data 微信的签名配置 144 | * @props { Boolean } data.debug 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 145 | * @props { String } data.appId 必填,公众号的唯一标识 146 | * @props { Number } data.timestamp 必填,生成签名的时间戳 147 | * @props { String } data.nonceStr 必填,生成签名的随机串 148 | * @props { String } data.signature 必填,签名 149 | * @props { Array } data.jsApiList 必填,需要使用的JS接口列表 150 | * @link 接口列表地址 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115 151 | */ 152 | static initWxConfig (config: IWxConfig): void { 153 | wx.config(Object.assign({}, { 154 | debug: false 155 | }, config)) 156 | 157 | wx.error((res: any) => { 158 | LogUtils.error(res, '[d-utils] wx.config error => ') 159 | }) 160 | } 161 | 162 | /** 163 | * 分享给朋友 164 | * @param {Object} sharInfo 165 | * @props { String } sharInfo.title 分享的title 166 | * @props { String } sharInfo.desc 分享描述 167 | * @props { String } sharInfo.link 分享链接 168 | * @props { String } sharInfo.imgUrl 分享图标 169 | * @props { Function } sharInfo.success 成功的回调 170 | * @props { Function } sharInfo.cancel 取消的回调 171 | * @props { Function } sharInfo.complete 完成的回调 172 | * @return { Promise } 返回一个promise 173 | */ 174 | static wxShareToFriend (sharInfo: IWxShareToFriend): Promise { 175 | const selfShareInfo = Object.assign({}, this.defaultShareInfo, sharInfo) 176 | return new Promise ((resolve, reject) => { 177 | try { 178 | wx.ready(() => { 179 | wx.onMenuShareAppMessage({ 180 | title: selfShareInfo.title, 181 | desc: selfShareInfo.desc, 182 | link: selfShareInfo.link, 183 | imgUrl: selfShareInfo.imgUrl, 184 | success: function (res) { 185 | const data: IWxCallBackType = { 186 | type: 'onMenuShareAppMessage', 187 | data: res 188 | } 189 | selfShareInfo.success(data) 190 | resolve(data) 191 | }, 192 | cancel: function (res) { 193 | const data: IWxCallBackType = { 194 | type: 'onMenuShareAppMessage', 195 | data: res 196 | } 197 | selfShareInfo.cancel(data) 198 | resolve(data) 199 | }, 200 | complete: function (res) { 201 | const data: IWxCallBackType = { 202 | type: 'onMenuShareAppMessage', 203 | data: res 204 | } 205 | selfShareInfo.complete(data) 206 | resolve(data) 207 | } 208 | }) 209 | }) 210 | } catch (e) { 211 | const data: IWxCallBackType = { 212 | type: 'onMenuShareAppMessage', 213 | data: e 214 | } 215 | reject(data) 216 | } 217 | }) 218 | } 219 | 220 | /** 221 | * 分享到朋友圈 222 | * @param {Object} sharInfo 223 | * @props { String } sharInfo.title 分享的title 224 | * @props { String } sharInfo.link 分享链接 225 | * @props { String } sharInfo.imgUrl 分享图标 226 | * @props { Function } sharInfo.success 成功的回调 227 | * @props { Function } sharInfo.cancel 取消的回调 228 | * @props { Function } sharInfo.complete 完成的回调 229 | * @return { Promise } 返回一个promise 230 | */ 231 | static wxShareToFriendCircle (sharInfo: IWxShareToFriendsCircle): Promise { 232 | const selfShareInfo = Object.assign({}, this.defaultShareInfo, sharInfo) 233 | return new Promise ((resolve, reject) => { 234 | try { 235 | wx.ready(() => { 236 | wx.onMenuShareTimeline({ 237 | title: selfShareInfo.title, 238 | link: selfShareInfo.link, 239 | imgUrl: selfShareInfo.imgUrl, 240 | success: function (res) { 241 | const data: IWxCallBackType = { 242 | type: 'onMenuShareTimeline', 243 | data: res 244 | } 245 | selfShareInfo.success(data) 246 | resolve(data) 247 | }, 248 | cancel: function (res) { 249 | const data: IWxCallBackType = { 250 | type: 'onMenuShareTimeline', 251 | data: res 252 | } 253 | selfShareInfo.cancel(data) 254 | resolve(data) 255 | }, 256 | complete: function (res) { 257 | const data: IWxCallBackType = { 258 | type: 'onMenuShareTimeline', 259 | data: res 260 | } 261 | selfShareInfo.complete(data) 262 | resolve(data) 263 | } 264 | }) 265 | }) 266 | } catch (e) { 267 | const data: IWxCallBackType = { 268 | type: 'onMenuShareTimeline', 269 | data: e 270 | } 271 | reject(data) 272 | } 273 | }) 274 | } 275 | 276 | /** 277 | * 隐藏所有非基础按钮接口 278 | * @return { Promise } 返回一个promise 279 | */ 280 | static hideAllNonBaseMenuItem (): Promise { 281 | return new Promise((resolve, reject) => { 282 | wx.ready(() => { 283 | try { 284 | wx.hideAllNonBaseMenuItem() 285 | const data: IWxCallBackType = { 286 | type: 'hideAllNonBaseMenuItem', 287 | data: '成功' 288 | } 289 | resolve(data) 290 | } catch (e) { 291 | const data: IWxCallBackType = { 292 | type: 'hideAllNonBaseMenuItem', 293 | data: e 294 | } 295 | reject(data) 296 | } 297 | }); 298 | }) 299 | } 300 | 301 | /** 302 | * 批量隐藏功能按钮接口 303 | * @param { array } arr // 要隐藏的菜单项,只能隐藏“传播类”和“保护类”按钮,所有menu项见附录3 304 | * @return { Promise } 返回一个promise 305 | */ 306 | static hideMenuItems (arr: string[] = []): Promise { 307 | return new Promise((resolve, reject) => { 308 | wx.ready(() => { 309 | try { 310 | wx.hideMenuItems({ 311 | menuList: arr 312 | }) 313 | const data: IWxCallBackType = { 314 | type: 'hideMenuItems', 315 | data: `成功, 隐藏的数组名称: ${arr}` 316 | } 317 | resolve(data) 318 | } catch (e) { 319 | const data: IWxCallBackType = { 320 | type: 'hideMenuItems', 321 | data: e 322 | } 323 | reject(data) 324 | } 325 | }) 326 | }) 327 | } 328 | 329 | /** 330 | * ios 手机在code过期之后会重新静默授权,会导致分享失败,通过url中是否存在code,针对ios用户执行reload的操作 331 | * @since 3.0.1 332 | */ 333 | static plantIosReloadShim = () => { 334 | const query = parseUrl() 335 | if (Object.keys(query).includes('code') && isIOS()) { 336 | localStorage.setItem('weixin-utils-reload', 'true') 337 | } 338 | } 339 | 340 | /** 341 | * 在其他页面都需要添加改方法,用户在页面加载之后重新reload,已保证微信分享正常 342 | * @since 3.0.1 343 | */ 344 | static reloadIosWhenCode = () => { 345 | const hostAndPath = window.location.href.split('?')[0] 346 | const reload = localStorage.getItem('weixin-utils-reload') 347 | const urlSearch = new URLSearchParams(window.location.search) 348 | urlSearch.delete('code') 349 | const newUrl = urlSearch.toString() ? `${hostAndPath}?${urlSearch.toString()}` : hostAndPath 350 | if (reload === 'true') { 351 | localStorage.removeItem('weixin-utils-reload') 352 | setTimeout(() => { 353 | location.replace(newUrl) 354 | }, 88) 355 | } 356 | } 357 | } 358 | --------------------------------------------------------------------------------