├── .babelrc ├── .gitignore ├── README.md ├── dist ├── rgutil.amd.min.js ├── rgutil.cjs.min.js ├── rgutil.esm.min.js ├── rgutil.min.js └── rgutil.umd.min.js ├── docs ├── favicon.ico ├── index.89f4cd0b.css ├── index.a6aa89c1.js ├── index.html └── vendor.1bc0a0dd.js ├── index.html ├── index.js ├── jest.config.js ├── package-lock.json ├── package.json ├── public └── favicon.ico ├── rollup.config.js ├── src ├── App.vue ├── components │ ├── KittyTop.vue │ └── bookdoc.vue ├── doc │ ├── array.js │ ├── base.js │ ├── cache.js │ ├── datetime.js │ ├── encryption.js │ ├── function.js │ ├── index.js │ ├── struct.js │ └── window.js ├── index.css └── main.js ├── test ├── array.test.js ├── base.test.js ├── cache.test.js ├── datetime.test.js ├── encryption.test.js ├── function.test.js ├── struct.test.js └── window.test.js ├── utils ├── array.js ├── base.js ├── cache.js ├── cache │ ├── LFUCache.js │ └── LRUCache.js ├── datetime.js ├── encryption.js ├── encryption │ ├── base64.js │ └── md5.js ├── function.js ├── struct.js ├── struct │ ├── eventEmitter.js │ ├── eventEmitterMap.js │ ├── sensitive-words.js │ └── trie.js └── window.js ├── vite.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": { 7 | "node": "current" 8 | } 9 | } 10 | ] 11 | ] 12 | } 13 | /**下面注释是参考部分,不用考虑*/ 14 | // { 15 | // "presets": ["@babel/env"] 16 | // } 17 | 18 | // { 19 | // "presets": [ 20 | // [ 21 | // "@babel/env", 22 | // { 23 | // "targets": { 24 | // "ie": 11, 25 | // "esmodules": true 26 | // }, 27 | // // 启用更符合规范的转换,但速度会更慢,默认为 `false`,从目前来看,是更严格的转化,包括一些代码检查。 28 | // "spec": false, 29 | 30 | // // 有两种模式:normal, loose。其中 normal 更接近 es6 loose 更接近 es5 31 | // "loose": false, 32 | 33 | // // "amd" | "umd" | "systemjs" | "commonjs" | "cjs" | false, defaults to "commonjs" 34 | // "modules": false, 35 | 36 | // // 打印插件使用情况 37 | // "debug": true, 38 | 39 | // // 按需增加移除一些功能 40 | // // include: [], 41 | // // exclude: [], 42 | 43 | // // 增加 polyfills 44 | // // 按需使用 45 | // "useBuiltIns": "usage" 46 | // // 引用一次 47 | // // useBuiltIns: 'entry', 48 | // // 不引用,独自使用 49 | // // useBuiltIns: false, 50 | 51 | // // 强制使用所有的插件 52 | // // forceAllTransforms: false, 53 | 54 | // // 配置 browerslist 的位置 55 | // // configPath: , 56 | // // 配置是否忽略 browerslist 文件 57 | // // ignoreBrowserslistConfig: false, 58 | 59 | // // 是否跳过 proposal 的文件 60 | // // shippedProposals: false, 61 | // } 62 | // ] 63 | // ] 64 | // } 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | demo/* 3 | *.local 4 | *.bat -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rgutil前端ES6工具库 2 | 3 | github仓库地址: https://github.com/GuCangRan/rgutil 4 | 5 | 码云仓库地址: https://gitee.com/gucangran/rgutil 6 | # 文档地址 (支持Chrome和edge浏览器,内核>80才行,低版本空白) 7 | https://gucangran.github.io/rgutil/ 8 | 9 | 或 10 | 11 | http://gucangran.gitee.io/rgutil 12 | 13 | 14 | >1. $R是基于ES6函数库,易懂简洁到能用一行就一行的强迫症设计,依然考虑阅读性,毕竟给人类看的,顺便机器运行 15 | >2. $R函数基本保持独立,让您满足单独可复制自己所需方法,嫌弃套娃地狱写法 16 | >3. $R支持npm下载,Tree-shaking按需加载函数,只给你想要的 17 | >4. $R支持单页面直接脚本引用,立即执行函数方式,umd方式,es6方式任您选 18 | >5. $R文档支持在线大多数方法直接测试结果,就是图个方便,不香么 19 | >6. $R默认打包es6语法,毕竟大势所趋,可更改rollup.config.js中plugins的babel注释内容去打包支持7. $R采纳jest进行单元测试,可看代码测试覆盖报告 21 | >8. $R是默认命名,可修改package.json中utilName再打包,用你喜欢的 22 | 23 | 24 | # npm下载 25 | ```javascript 26 | npm install rgutil 27 | ``` 28 | 29 | ```javascript 30 | //1. 函数全部导入 31 | import * as $R from "rgutil"; 32 | 33 | //写法: 34 | $R.isNumber(8) 35 | 36 | 37 | //2. 按需导入 38 | import { isNumber } from "rgutil"; 39 | 40 | //写法 41 | isNumber(8) 42 | 43 | ``` 44 | 45 | # 页面使用 46 | 47 | >打包后脚本在dist文件夹,自行下载 48 | 49 | ```javascript 50 | //纯html页面直接引用 51 | //1. 立即执行函数直接引用 52 | // 53 | //或引用 54 | // 55 | 56 | //在线直接引用 57 | 58 | 64 | 65 | //2. ES6模块引用方式 66 | 74 | 75 | ``` 76 | 77 | **特别说明:** 本函数库借鉴了很多大神的开源文章和部分面试题,以及自己平时所用,汇总合成。非常感谢这些大神的付出,致敬!这也是自己的一个学习过程,为开源贡献一份微薄之力。 78 | 79 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuCangRan/rgutil/6170cf47fcfd3508bb6f669a10e15b5d5c1f4797/docs/favicon.ico -------------------------------------------------------------------------------- /docs/index.89f4cd0b.css: -------------------------------------------------------------------------------- 1 | code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.kitty-top[data-v-40937d4c]{display:block;position:absolute;margin:auto;bottom:0;right:0}.wrapper[data-v-40937d4c]{height:100px;width:60px;margin:auto;font-family:"Clicker Script",cursive}.moon[data-v-40937d4c]{height:60px;width:60px;background:#666;border-radius:30px;box-shadow:0 0 30px 1px #444}.moon[data-v-40937d4c]:before{border-radius:30px;box-shadow:5px -25px 0 0 #444,25px 0 0 0 #444,25px -18px 0 5px #444;content:"";position:absolute;width:10px;height:10px;left:15px;bottom:5px;background:#444}.cat[data-v-40937d4c]{background:#f95;height:30px;top:auto;right:15px;bottom:30px;width:30px;z-index:0;-webkit-transition:all linear .2s}.cat[data-v-40937d4c]:after,.cat[data-v-40937d4c]:before{width:0;height:0;top:-5px;position:absolute;content:""}.cat[data-v-40937d4c]:before{border-left:0 solid transparent;border-right:8px solid transparent;border-bottom:8px solid #f95;left:0}.cat[data-v-40937d4c]:after{border-right:0px solid transparent;border-left:8px solid transparent;border-bottom:8px solid #f95;right:0}.cat .eyes[data-v-40937d4c]{position:absolute;height:4px;width:4px;background:#333;border-radius:100%;top:8px}.cat .eyes.left[data-v-40937d4c]{left:6px}.cat .eyes.right[data-v-40937d4c]{right:6px}.cat .mouth[data-v-40937d4c]{position:absolute;height:2px;width:60%;background:#333;border-radius:0 0 30% 30%;top:14px;margin:auto;left:0;right:0}.wrapper:hover .cat[data-v-40937d4c]{bottom:50px}.doc-main{height:100%;display:grid;grid-template-columns:410px 1fr}.doc-main .doc-nav{overflow:auto;background:#fff;padding:0 5px;border:5px solid #ecececc4}.doc-main .doc-container{overflow:auto;background:#ecececc4}.pre-desc{font-size:18px;line-height:1.5}.search-wrapper{width:100%;margin:0 auto;text-align:center}.search-input{width:90%;height:36px;border:1px solid #ccc;border-radius:4px;margin:5px 0}.serarch-position{top:45px;position:fixed;left:15px;width:380px}.search-input:focus{border-color:#66afe9;outline:0}.doc-title{font-size:18px;margin-top:15px;padding:5px 5px 5px 5px;font-weight:700;border-left:4px solid #66afe9;border-radius:5px;color:#66afe9}.doc-child{font-size:16px;height:23px;padding:4px 15px;margin:3px 0;display:grid;grid-template-columns:180px 1fr;cursor:Pointer;background:#fff}.doc-child div{border:1px solid #f1f1f1;padding:2px 0}.doc-child:hover{color:#078dfc}.doc-desc{position:relative;margin:20px 10px;padding:10px 10px;background-color:#fff;box-shadow:0 3px 12px 0 rgba(198,203,222,.8);border-radius:5px;border-width:0 0 0 7px;border-color:#75bbe5;border-style:solid}.doc-desc::after,.doc-desc::before{content:"";z-index:-1;position:absolute;left:10px;bottom:10px;width:45%;height:55%;max-height:100px;box-shadow:0 8px 16px rgba(0,0,0,.3)}.doc-desc::after{left:auto;right:10px}.doc-desc .doc-desc-name{padding:10px 0;font-size:20px;font-weight:700}.test-demo{margin:29px auto;padding:8px 16px;border-radius:8px;background:#fff;outline:6px solid #eeeeee;box-shadow:0 0 0 5px #eee}.money-img{display:grid;grid-template-columns:1fr 1fr;padding:10px 30px}.money-img div img{width:500px;height:550px}.heart-desc{padding:20px 30px;color:transparent;font-size:30px;font-weight:700;color:#078dfc}.top-title{position:fixed;z-index:1000;right:40px;bottom:60px;border:1px solid #dbdbdbc2;height:40px;width:40px;border-radius:50%;background:#dbdbdbc2}.test-button{margin:5px;cursor:pointer;text-align:center;line-height:1;font-size:14px;padding:10px 18px;color:#5d5e61;background:#fff;border:1px solid #cfd1d4;border-radius:20px;outline:0}.test-button:hover{background:#ccc}pre[class*=language-]{background:#efeeeea6}.container{height:100vh;width:100vw;display:grid;grid-auto-rows:50px 1fr 30px}header{display:grid;grid-template-columns:auto 1fr 30px 30px 100px;grid-gap:15px;background:linear-gradient(180deg,#0b8df6,#2196f3),#2196f3}header::before{content:"";position:absolute;left:0;top:40px;right:0;background-repeat:repeat-x;height:10px;background-size:20px 20px;background-image:radial-gradient(circle at 10px -5px,transparent 12px,#fff 13px,#fff 20px)}header::after{content:"";position:absolute;left:0;top:35px;right:0;background-repeat:repeat-x;height:15px;background-size:40px 20px;background-image:radial-gradient(circle at 10px 15px,#fff 12px,transparent 13px)}header .header-title{font-size:28px;padding:5px 10px}header .header-tips{height:100%;position:relative}header .header-tips-content{position:absolute;bottom:15px;padding:3px 3px;color:#fff}header .header-item{height:35px;padding:4px 0}main{background-color:#f5f7f9;overflow:auto;padding:0 5px}footer{background-color:#f5f7f9;line-height:30px;text-align:center;color:#cfd1d4}body{margin:0;padding:0;height:100vh;width:100vw;display:flex;overflow:hidden}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background-color:#fff;border-radius:3px}::-webkit-scrollbar-thumb{border-radius:7px;background-color:#aaa}::-webkit-scrollbar-button{background-color:#fff}::-webkit-scrollbar-corner{background:#fff} -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | $̚R̚ ES6函数库 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | $̚R̚ ES6函数库 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | export { 2 | version 3 | } 4 | from "./package.json"; 5 | 6 | export * from './utils/base.js'; 7 | export * from './utils/array.js'; 8 | export * from './utils/function.js'; 9 | export * from './utils/window.js'; 10 | export * from './utils/datetime.js'; 11 | export * from './utils/encryption.js'; 12 | export * from './utils/cache.js'; 13 | export * from './utils/struct.js'; -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property, visit: 3 | * https://jestjs.io/docs/en/configuration.html 4 | */ 5 | 6 | module.exports = { 7 | // All imported modules in your tests should be mocked automatically 8 | // automock: false, 9 | 10 | // Stop running tests after `n` failures 11 | // bail: 0, 12 | 13 | // The directory where Jest should store its cached dependency information 14 | // cacheDirectory: "C:\\Users\\绝版小书包\\AppData\\Local\\Temp\\jest", 15 | 16 | // Automatically clear mock calls and instances between every test 17 | // clearMocks: false, 18 | 19 | // Indicates whether the coverage information should be collected while executing the test 20 | // collectCoverage: false, 21 | 22 | // An array of glob patterns indicating a set of files for which coverage information should be collected 23 | // collectCoverageFrom: undefined, 24 | 25 | // The directory where Jest should output its coverage files 26 | coverageDirectory: "testcoverage", 27 | 28 | // An array of regexp pattern strings used to skip coverage collection 29 | // coveragePathIgnorePatterns: [ 30 | // "\\\\node_modules\\\\" 31 | // ], 32 | 33 | // Indicates which provider should be used to instrument code for coverage 34 | coverageProvider: "v8", 35 | 36 | // A list of reporter names that Jest uses when writing coverage reports 37 | // coverageReporters: [ 38 | // "json", 39 | // "text", 40 | // "lcov", 41 | // "clover" 42 | // ], 43 | 44 | // An object that configures minimum threshold enforcement for coverage results 45 | // coverageThreshold: undefined, 46 | 47 | // A path to a custom dependency extractor 48 | // dependencyExtractor: undefined, 49 | 50 | // Make calling deprecated APIs throw helpful error messages 51 | // errorOnDeprecated: false, 52 | 53 | // Force coverage collection from ignored files using an array of glob patterns 54 | // forceCoverageMatch: [], 55 | 56 | // A path to a module which exports an async function that is triggered once before all test suites 57 | // globalSetup: undefined, 58 | 59 | // A path to a module which exports an async function that is triggered once after all test suites 60 | // globalTeardown: undefined, 61 | 62 | // A set of global variables that need to be available in all test environments 63 | // globals: {}, 64 | 65 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 66 | // maxWorkers: "50%", 67 | 68 | // An array of directory names to be searched recursively up from the requiring module's location 69 | // moduleDirectories: [ 70 | // "node_modules" 71 | // ], 72 | 73 | // An array of file extensions your modules use 74 | // moduleFileExtensions: [ 75 | // "js", 76 | // "json", 77 | // "jsx", 78 | // "ts", 79 | // "tsx", 80 | // "node" 81 | // ], 82 | 83 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module 84 | // moduleNameMapper: {}, 85 | 86 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 87 | // modulePathIgnorePatterns: [], 88 | 89 | // Activates notifications for test results 90 | // notify: false, 91 | 92 | // An enum that specifies notification mode. Requires { notify: true } 93 | // notifyMode: "failure-change", 94 | 95 | // A preset that is used as a base for Jest's configuration 96 | // preset: undefined, 97 | 98 | // Run tests from one or more projects 99 | // projects: undefined, 100 | 101 | // Use this configuration option to add custom reporters to Jest 102 | // reporters: undefined, 103 | 104 | // Automatically reset mock state between every test 105 | // resetMocks: false, 106 | 107 | // Reset the module registry before running each individual test 108 | // resetModules: false, 109 | 110 | // A path to a custom resolver 111 | // resolver: undefined, 112 | 113 | // Automatically restore mock state between every test 114 | // restoreMocks: false, 115 | 116 | // The root directory that Jest should scan for tests and modules within 117 | // rootDir: undefined, 118 | 119 | // A list of paths to directories that Jest should use to search for files in 120 | // roots: [ 121 | // "" 122 | // ], 123 | 124 | // Allows you to use a custom runner instead of Jest's default test runner 125 | // runner: "jest-runner", 126 | 127 | // The paths to modules that run some code to configure or set up the testing environment before each test 128 | // setupFiles: [], 129 | 130 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 131 | // setupFilesAfterEnv: [], 132 | 133 | // The number of seconds after which a test is considered as slow and reported as such in the results. 134 | // slowTestThreshold: 5, 135 | 136 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 137 | // snapshotSerializers: [], 138 | 139 | // The test environment that will be used for testing 140 | testEnvironment: "node", 141 | 142 | // Options that will be passed to the testEnvironment 143 | // testEnvironmentOptions: {}, 144 | 145 | // Adds a location field to test results 146 | // testLocationInResults: false, 147 | 148 | // The glob patterns Jest uses to detect test files 149 | testMatch: [ 150 | "**/__tests__/**/*.[jt]s?(x)", 151 | "**/test/**/*.[jt]s?(x)", 152 | "**/?(*.)+(spec|test).[tj]s?(x)" 153 | ], 154 | 155 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 156 | // testPathIgnorePatterns: [ 157 | // "\\\\node_modules\\\\" 158 | // ], 159 | 160 | // The regexp pattern or array of patterns that Jest uses to detect test files 161 | // testRegex: [], 162 | 163 | // This option allows the use of a custom results processor 164 | // testResultsProcessor: undefined, 165 | 166 | // This option allows use of a custom test runner 167 | // testRunner: "jasmine2", 168 | 169 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 170 | // testURL: "http://localhost", 171 | 172 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 173 | // timers: "real", 174 | 175 | // A map from regular expressions to paths to transformers 176 | // transform: undefined, 177 | 178 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 179 | // transformIgnorePatterns: [ 180 | // "\\\\node_modules\\\\", 181 | // "\\.pnp\\.[^\\\\]+$" 182 | // ], 183 | 184 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 185 | // unmockedModulePathPatterns: undefined, 186 | 187 | // Indicates whether each individual test should be reported during the run 188 | // verbose: undefined, 189 | 190 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 191 | // watchPathIgnorePatterns: [], 192 | 193 | // Whether to use watchman for file crawling 194 | // watchman: true, 195 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rgutil", 3 | "version": "2.0.1", 4 | "description": "$R是基于ES6的JavaScript函数库", 5 | "main": "index.js", 6 | "utilName": "$R", 7 | "scripts": { 8 | "rbuild": "rollup -c", 9 | "db": "npm run dev:build", 10 | "pb": "npm run pro:build", 11 | "dev:build": "rimraf dist && cross-env NODE_ENV=development rollup -c", 12 | "pro:build": "rimraf dist && cross-env NODE_ENV=production rollup -c ", 13 | "watch": "rollup -c -w", 14 | "dev": "vite", 15 | "build": "vite build", 16 | "build:all": "vite build && npm run pro:build", 17 | "test": "jest --coverage" 18 | }, 19 | "dependencies": { 20 | "@babel/polyfill": "^7.12.1", 21 | "prismjs": "^1.24.1", 22 | "vue": "3.1.4" 23 | }, 24 | "devDependencies": { 25 | "@babel/cli": "^7.14.5", 26 | "@babel/core": "^7.14.6", 27 | "@babel/preset-env": "^7.14.7", 28 | "@babel/register": "^7.14.5", 29 | "@babel/types": "^7.14.5", 30 | "@rollup/plugin-babel": "^5.3.0", 31 | "@vitejs/plugin-vue": "^1.2.5", 32 | "@vue/compiler-sfc": "^3.1.4", 33 | "babel-plugin-annotate-pure-calls": "^0.4.0", 34 | "babel-plugin-import-export-rename": "^1.0.1", 35 | "core-js": "^3.15.2", 36 | "cross-env": "^7.0.3", 37 | "jest": "^27.0.6", 38 | "rimraf": "^3.0.2", 39 | "rollup-plugin-babel": "^4.4.0", 40 | "rollup-plugin-commonjs": "^10.1.0", 41 | "rollup-plugin-json": "^4.0.0", 42 | "rollup-plugin-node-resolve": "^5.2.0", 43 | "rollup-plugin-terser": "^7.0.2", 44 | "rollup-plugin-uglify": "^6.0.4", 45 | "rollup-watch": "^4.3.1", 46 | "sass": "^1.35.2", 47 | "vite": "^2.4.2" 48 | }, 49 | "keywords": [ 50 | "ES6函数库", 51 | "vue3", 52 | "vite" 53 | ], 54 | "author": "GuCangRan", 55 | "license": "MIT" 56 | } 57 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GuCangRan/rgutil/6170cf47fcfd3508bb6f669a10e15b5d5c1f4797/public/favicon.ico -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import json from 'rollup-plugin-json'; 2 | import babel from 'rollup-plugin-babel'; 3 | import resolve from 'rollup-plugin-node-resolve'; 4 | import { 5 | terser 6 | } from "rollup-plugin-terser"; 7 | import commonjs from 'rollup-plugin-commonjs'; 8 | import { 9 | utilName 10 | } from './package.json'; 11 | import '@babel/polyfill'; 12 | 13 | let config = { 14 | input: 'index.js', 15 | output: [], 16 | plugins: [ 17 | resolve(), //Rollup 能找到 `ms` 18 | commonjs(), //Rollup 能转换 `ms` 为一个ES模块 19 | json(), 20 | babel({ 21 | exclude: ['node_modules/**'], 22 | //>=es6版本 23 | presets: [ 24 | [ 25 | "@babel/preset-env", 26 | { 27 | "targets": "> 5%", 28 | "useBuiltIns": "usage", 29 | "corejs": 3 30 | } 31 | ] 32 | ] 33 | //开启兼容ie11等版本直接打包处理,自行配置 34 | // presets: [ 35 | // [ 36 | // '@babel/preset-env', 37 | // { 38 | // modules: false, 39 | // targets: { 40 | // browsers: '> 1%, IE 11, not op_mini all, not dead', 41 | // // node: "current" 42 | // }, 43 | // corejs: 3, 44 | // useBuiltIns: 'usage' 45 | // } 46 | // ] 47 | // ] 48 | }) 49 | ], 50 | external: [''], // 指出应将哪些模块视为外部模块 51 | watch: { 52 | chokidar: {}, 53 | exclude: ['node_modules/**'] 54 | } 55 | }; 56 | 57 | 58 | let isMin = ""; //如果生产环境打包为min版本 59 | if (process.env.NODE_ENV === 'production') { 60 | config.plugins.push( 61 | terser() 62 | ); 63 | isMin = ".min"; 64 | } 65 | 66 | //打包输出文件 67 | config.output = [{ 68 | file: `dist/rgutil${isMin}.js`, 69 | format: 'iife', // iife 表示立即执行函数 70 | name: utilName, 71 | sourcemap: false 72 | }, 73 | { 74 | file: `dist/rgutil.esm${isMin}.js`, 75 | format: 'esm', 76 | name: utilName, 77 | sourcemap: false 78 | }, 79 | { 80 | file: `dist/rgutil.umd${isMin}.js`, 81 | format: 'umd', 82 | name: utilName, 83 | sourcemap: false 84 | }, 85 | { 86 | file: `dist/rgutil.cjs${isMin}.js`, 87 | format: 'cjs', 88 | name: utilName, 89 | sourcemap: false 90 | }, 91 | { 92 | file: `dist/rgutil.amd${isMin}.js`, 93 | format: 'amd', 94 | amd: { 95 | define: 'define' 96 | }, 97 | name: utilName, 98 | sourcemap: false 99 | } 100 | ]; 101 | 102 | module.exports = config; 103 | 104 | 105 | // if (process.env.NODE_ENV === 'production') { 106 | // config.plugins.push( 107 | // terser({ 108 | // output: { 109 | // comments: "all", 110 | // }, 111 | // }) 112 | // ); 113 | // } -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 81 | 164 | -------------------------------------------------------------------------------- /src/components/KittyTop.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | 21 | 123 | -------------------------------------------------------------------------------- /src/components/bookdoc.vue: -------------------------------------------------------------------------------- 1 | 116 | 117 | 304 | -------------------------------------------------------------------------------- /src/doc/array.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 数组函数文档 3 | */ 4 | 5 | const children = [{ 6 | base: { 7 | name: "arrDistinct", 8 | title: "数组去重", 9 | desc: "去除数组重复元素,返回新数组", 10 | parmInfo: "(array)" 11 | }, 12 | demo: [ 13 | [2, 3, 3, 3], 14 | null, 15 | undefined, 16 | ["red", "green", "blue", "red"], 17 | [ 18 | [1, 2], 19 | [1, 2, 3], 20 | [1, 2, 3], 21 | ] 22 | 23 | ] 24 | }, { 25 | base: { 26 | name: "arrMax", 27 | title: "数组最大值", 28 | desc: "返回数组最大值", 29 | parmInfo: "(array)" 30 | }, 31 | demo: [ 32 | [0, 9, 18, 6] 33 | ] 34 | }, { 35 | base: { 36 | name: "arrMin", 37 | title: "数组最小值", 38 | desc: "返回数组最小值", 39 | parmInfo: "(array)" 40 | }, 41 | demo: [ 42 | [0, 9, 18, 6] 43 | ] 44 | }, , { 45 | base: { 46 | name: "arrCountOcc", 47 | title: "出现次数", 48 | desc: "计算数组中值的出现次数", 49 | parmInfo: "(arr, value)" 50 | }, 51 | demo: [{ 52 | parms: [ 53 | [0, 9, 18, 6, 3, 6], 6 54 | ], 55 | desc: ["[0,9,18,6,3,6], 6"], 56 | }, { 57 | parms: [ 58 | ["red", "red", "green"], "red" 59 | ], 60 | desc: ["[\"red\",\"red\",\"green\"], \"red\""], 61 | }] 62 | }, { 63 | base: { 64 | name: "arrDeepFlatten", 65 | title: "数组扁平", 66 | desc: "将多维数组组合为一维", 67 | parmInfo: "(array)" 68 | }, 69 | demo: [ 70 | [ 71 | [1, 2, [1, 2, [1, 2, [2, 3]]]] 72 | ], 73 | ["red", "green", ["blue", "red"]] 74 | ] 75 | }, { 76 | base: { 77 | name: "arrDiff", 78 | title: "数组差异对比", 79 | desc: "对比两个数组之间的差异,返回arr1在arr2中不包含的数组", 80 | parmInfo: "(arr1,arr2)" 81 | }, 82 | demo: [{ 83 | parms: [ 84 | [0, 9, 18, 6, 3, 6], 85 | [33, 44, 5] 86 | ], 87 | desc: ["[0, 9, 18, 6, 3, 6], [33,44,5]"], 88 | }, { 89 | parms: [ 90 | ["red", "red", "green"], 91 | ["red"] 92 | ], 93 | desc: ["[\"red\",\"red\",\"green\"], [\"red\"]"], 94 | }] 95 | }, { 96 | base: { 97 | name: "arrNonUnique", 98 | title: "去除出现一次值", 99 | desc: "去除数组中只出现一次的值,返回具备多次的值", 100 | parmInfo: "(arr1,arr2)" 101 | }, 102 | demo: [ 103 | [0, 9, 18, 6, 9, 6], 104 | ["red", "red", "green"] 105 | ] 106 | }, 107 | { 108 | base: { 109 | name: "arrWithValues", 110 | title: "构建默认数组", 111 | desc: "创建默认长度默认值的数组", 112 | parmInfo: "(count, value=0)" 113 | }, 114 | demo: [{ 115 | parmMore: [6, 8] 116 | }, { 117 | parmMore: [6, "red"] 118 | }] 119 | }, 120 | { 121 | base: { 122 | name: "arrIntersection", 123 | title: "数组交集", 124 | desc: "获取两个数组中公共元素", 125 | parmInfo: "(arr1, arr2)" 126 | }, 127 | demo: [{ 128 | parms: [ 129 | [5, 6, 7, 8], 130 | [6, 7, 8, 9, 10] 131 | ], 132 | desc: ["[5, 6, 7, 8], [6, 7, 8, 9, 10]"], 133 | }, { 134 | parms: [ 135 | ["red"], 136 | ["green"] 137 | ], 138 | desc: ["\"red\", [\"green\"]"], 139 | }] 140 | }, 141 | { 142 | base: { 143 | name: "arrNthElement", 144 | title: "获取第n个元素", 145 | desc: "获取数组第n个元素(n是数组索引)", 146 | parmInfo: "(arr, n = 0)" 147 | }, 148 | demo: [{ 149 | parms: [ 150 | [5, 6, 7, 8], 2 151 | ], 152 | desc: ["[5, 6, 7, 8], 2"], 153 | }, { 154 | parms: [ 155 | ["red"], 1 156 | ], 157 | desc: ["\"red\", 1"], 158 | }] 159 | }, 160 | { 161 | base: { 162 | name: "arrAverage", 163 | title: "求平均数", 164 | desc: "求取平均数", 165 | parmInfo: "(any)" 166 | }, 167 | demo: [{ 168 | parms: [ 169 | ...[1, 2, 3] 170 | ], 171 | desc: ["...[1, 2, 3]"], 172 | }, { 173 | parms: [ 174 | 1, 2, 7 175 | ], 176 | desc: ["1, 2, 7"], 177 | }] 178 | }, 179 | { 180 | base: { 181 | name: "arrAverageBy", 182 | title: "数组对象某属性平均数", 183 | desc: "求取数组对象某属性平均数", 184 | parmInfo: "(arr,fn)" 185 | }, 186 | demo: [{ 187 | parms: [ 188 | [{ 189 | n: 1 190 | }, { 191 | n: 3 192 | }, { 193 | n: 5 194 | }], o => o.n 195 | ], 196 | desc: ["[{ n: 1 }, { n: 3 }, { n: 5 }],o => o.n"], 197 | }, { 198 | parms: [ 199 | [{ 200 | n: 1 201 | }, { 202 | n: 3 203 | }, { 204 | n: 5 205 | }], 'n' 206 | ], 207 | desc: ["[{ n: 1 }, { n: 3 }, { n: 5 }],'n'"], 208 | }] 209 | }, 210 | { 211 | base: { 212 | name: "arrAsList", 213 | title: "转换为数组", 214 | desc: "其他类型转换为数组", 215 | parmInfo: "(arr,fn)" 216 | }, 217 | demo: ['abc', 8, [8]] 218 | }, 219 | { 220 | base: { 221 | name: "arrIndexOfAll", 222 | title: "获取值所在索引", 223 | desc: "获取数组指定值的所有索引", 224 | parmInfo: "(arr, val)" 225 | }, 226 | demo: [{ 227 | parms: [ 228 | [3, 2, 3, 3, 5, 3], 3 229 | ], 230 | desc: ["[1, 2, 3, 1, 2, 3],3"], 231 | }, { 232 | parms: [ 233 | [1, 2, 7], 6 234 | ], 235 | desc: ["[1, 2, 7], 6"], 236 | }] 237 | }, 238 | { 239 | base: { 240 | name: "arrMinN", 241 | title: "指定长度升序数组", 242 | desc: "返回指定长度的升序数组", 243 | parmInfo: "(arr, n = 1)" 244 | }, 245 | demo: [{ 246 | parms: [ 247 | [3, 2, 3, 3, 5, 3], 3 248 | ], 249 | desc: ["[1, 2, 3, 1, 2, 3],3"], 250 | }, { 251 | parms: [ 252 | [1, 2, 7] 253 | ], 254 | desc: ["[1, 2, 7]"], 255 | }] 256 | }, 257 | { 258 | base: { 259 | name: "arrRandomInRange", 260 | title: "指定长度随机数组", 261 | desc: "生成两数之间指定长度的随机数组", 262 | parmInfo: "(min, max, n = 1)" 263 | }, 264 | demo: [{ 265 | parmMore: [10, 16, 3] 266 | }, { 267 | parmMore: [10, 16, 10] 268 | } 269 | 270 | ] 271 | }, 272 | { 273 | base: { 274 | name: "arrRandomSample", 275 | title: "数组随机抽取一个", 276 | desc: "在指定数组中获取随机数,返回一个值", 277 | parmInfo: "(arr)" 278 | }, 279 | demo: [ 280 | [10, 16, 3], 281 | ["red", "green", "blue"], 282 | ] 283 | }, 284 | { 285 | base: { 286 | name: "arrRandomN", 287 | title: "数组随机抽取多个", 288 | desc: "在指定数组中获取指定长度的数组,返回一个数组", 289 | parmInfo: "([...arr], n = 1)" 290 | }, 291 | demo: [{ 292 | parms: [ 293 | [3, 2, 3, 3, 5, 3], 3 294 | ], 295 | desc: ["[1, 2, 3, 1, 2, 3],3"], 296 | }, { 297 | parms: [ 298 | [1, 2, 7], 5 299 | ], 300 | desc: ["[1, 2, 7],5"], 301 | }] 302 | }, 303 | { 304 | base: { 305 | name: "arrShuffle", 306 | title: "数组随机打乱", 307 | desc: "数组随机打乱产生新的数组,返回一个值", 308 | parmInfo: "(arr)" 309 | }, 310 | demo: [ 311 | [10, 16, 3], 312 | ["red", "green", "blue"], 313 | ] 314 | }, 315 | { 316 | base: { 317 | name: "arrToTree", 318 | title: "数组转换树", 319 | desc: "将数组转换为树形结构的对象", 320 | parmInfo: "(items, id = null, link = 'parent_id')" 321 | }, 322 | demo: [ 323 | [{ 324 | id: 1, 325 | parent_id: null 326 | }, 327 | { 328 | id: 2, 329 | parent_id: 1 330 | }, 331 | { 332 | id: 3, 333 | parent_id: 1 334 | }, 335 | { 336 | id: 4, 337 | parent_id: 2 338 | }, 339 | { 340 | id: 5, 341 | parent_id: 4 342 | } 343 | ] 344 | ] 345 | }, 346 | { 347 | base: { 348 | name: "arrTranspose", 349 | title: "交换矩阵的行和列", 350 | desc: `交换矩阵的行和列 351 | 注意: 矩阵是指行和列 数量相等哦,我没嘲笑你不懂,主要我怕有不懂的`, 352 | parmInfo: "(array)" 353 | }, 354 | demo: [ 355 | [ 356 | [1, 2, 3], 357 | [4, 5, 6], 358 | [7, 8, 9], 359 | ] 360 | ] 361 | }, 362 | { 363 | base: { 364 | name: "arrZip", 365 | title: "压缩数组", 366 | desc: `压缩数组 367 | 参数: 传递多个数组`, 368 | parmInfo: "(...arr)" 369 | }, 370 | demo: [{ 371 | parms: [ 372 | ['a', 'b', 'c'], 373 | [1, 2, 3] 374 | ], 375 | desc: ["['a', 'b', 'c'],[1, 2, 3]"], 376 | }] 377 | }, 378 | { 379 | base: { 380 | name: "arrUnZip", 381 | title: "解压数组", 382 | desc: "解压数组", 383 | parmInfo: `(arr)` 384 | }, 385 | demo: [{ 386 | parms: [ 387 | [ 388 | ["a", 1], 389 | ["b", 2], 390 | ["c", 3] 391 | ] 392 | ], 393 | desc: [`[["a",1],["b",2],["c",3]]`], 394 | }] 395 | }, 396 | { 397 | base: { 398 | name: "bubbleSort", 399 | title: "冒泡排序", 400 | desc: ` 401 | 相邻两个数逐个比较,如果前一个数比后一个数小则交换位置。 402 | 403 | 参数说明: 404 | arr:输入数组 405 | isNumTimes: 默认false,是否显示时间复杂度O(次数) 如果为ture,返回数据格式: [排序数组,次数] 406 | `, 407 | parmInfo: `(arr, isNumTimes = false)` 408 | }, 409 | demo: [{ 410 | parms: [ 411 | [4, 2, 5, 1, 3] 412 | ], 413 | desc: [`[4, 2, 5, 1, 3]`], 414 | }, 415 | { 416 | parms: [ 417 | [4, 2, 5, 1, 3], true 418 | ], 419 | desc: [`[4, 2, 5, 1, 3],true`], 420 | } 421 | ] 422 | }, 423 | { 424 | base: { 425 | name: "insertSort", 426 | title: "插入排序", 427 | desc: ` 428 | 假设前面 n-1 的元素已经排好序,将第n个元素插入到前面已经排好的序列中。 429 | 430 | 参数说明: 431 | arr:输入数组 432 | isNumTimes: 默认false,是否显示时间复杂度O(次数) 如果为ture,返回数据格式: [排序数组,次数] 433 | `, 434 | parmInfo: `(arr, isNumTimes = false)` 435 | }, 436 | demo: [{ 437 | parms: [ 438 | [4, 2, 5, 1, 3] 439 | ], 440 | desc: [`[4, 2, 5, 1, 3]`], 441 | }, 442 | { 443 | parms: [ 444 | [4, 2, 5, 1, 3], true 445 | ], 446 | desc: [`[4, 2, 5, 1, 3],true`], 447 | } 448 | ] 449 | }, 450 | { 451 | base: { 452 | name: "quickSort", 453 | title: "快速排序", 454 | desc: ` 455 | 冒泡排序的改进算法。通过多次的比较和交换来实现排序。 456 | 457 | 参数说明: 458 | arr:输入数组 459 | isNumTimes: 默认false,是否显示时间复杂度O(次数) 如果为ture,返回数据格式: [排序数组,次数] 460 | `, 461 | parmInfo: `(arr, isNumTimes = false)` 462 | }, 463 | demo: [{ 464 | parms: [ 465 | [4, 2, 5, 1, 3] 466 | ], 467 | desc: [`[4, 2, 5, 1, 3]`], 468 | }, 469 | { 470 | parms: [ 471 | [4, 2, 5, 1, 3], true 472 | ], 473 | desc: [`[4, 2, 5, 1, 3],true`], 474 | } 475 | ] 476 | }, 477 | { 478 | base: { 479 | name: "selectionSort", 480 | title: "选择排序", 481 | desc: ` 482 | 从待排序的数据元素中选出最小/最大的一个元素,存放在序列的起始位置, 483 | 然后再从剩余的未排序元素中寻找到最小/最大元素,然后放到已排序的序列的末尾。 484 | 以此类推,直到全部待排序的数据元素的个数为零。 485 | 486 | 参数说明: 487 | arr:输入数组 488 | isNumTimes: 默认false,是否显示时间复杂度O(次数) 如果为ture,返回数据格式: [排序数组,次数] 489 | `, 490 | parmInfo: `(arr, isNumTimes = false)` 491 | }, 492 | demo: [{ 493 | parms: [ 494 | [4, 2, 5, 1, 3] 495 | ], 496 | desc: [`[4, 2, 5, 1, 3]`], 497 | }, 498 | { 499 | parms: [ 500 | [4, 2, 5, 1, 3], true 501 | ], 502 | desc: [`[4, 2, 5, 1, 3],true`], 503 | } 504 | ] 505 | }, 506 | { 507 | base: { 508 | name: "shellSort", 509 | title: "希尔排序", 510 | desc: ` 511 | 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序。 512 | 513 | 参数说明: 514 | arr:输入数组 515 | isNumTimes: 默认false,是否显示时间复杂度O(次数) 如果为ture,返回数据格式: [排序数组,次数] 516 | `, 517 | parmInfo: `(arr, isNumTimes = false)` 518 | }, 519 | demo: [{ 520 | parms: [ 521 | [4, 2, 5, 1, 3] 522 | ], 523 | desc: [`[4, 2, 5, 1, 3]`], 524 | }, 525 | { 526 | parms: [ 527 | [4, 2, 5, 1, 3], true 528 | ], 529 | desc: [`[4, 2, 5, 1, 3],true`], 530 | } 531 | ] 532 | } 533 | ]; 534 | 535 | const docData = { 536 | title: { 537 | label: "数组", 538 | value: "array", 539 | expand: true, 540 | testValue: "", 541 | testResult: "", 542 | children: [] 543 | }, 544 | children: children 545 | } 546 | 547 | export default docData; -------------------------------------------------------------------------------- /src/doc/base.js: -------------------------------------------------------------------------------- 1 | const children = [{ 2 | base: { 3 | name: "is", 4 | title: "判断数据类型", 5 | desc: "判断数据是否为指定的数据类型,如果是则返回true", 6 | parmInfo: "(type, val)" 7 | }, 8 | demo: [{ 9 | parms: [ArrayBuffer, new ArrayBuffer()], 10 | desc: ["ArrayBuffer, new ArrayBuffer()"], 11 | }, 12 | { 13 | parms: [Array, [1]], 14 | desc: ["Array,[1]"], 15 | }, { 16 | parms: [Map, new Map()], 17 | desc: ["Map, new Map()"], 18 | }, { 19 | parms: [Set, new Set()], 20 | desc: ["Set, new Set()"], 21 | }, 22 | { 23 | parms: [WeakMap, new WeakMap()], 24 | desc: ["WeakMap, new WeakMap()"], 25 | }, 26 | { 27 | parms: [WeakSet, new WeakSet()], 28 | desc: ["WeakSet, new WeakSet()"], 29 | }, 30 | { 31 | parms: [String, ''], 32 | desc: ["String, '测试'"], 33 | }, 34 | { 35 | parms: [Number, 1], 36 | desc: ["Number, 123"], 37 | }, 38 | { 39 | parms: [Number, new Number(1)], 40 | desc: ["Number, new Number(1)"], 41 | }, 42 | { 43 | parms: [Boolean, true], 44 | desc: ["Boolean, true"], 45 | }, 46 | { 47 | parms: [Boolean, new Boolean(true)], 48 | desc: ["Boolean, new Boolean(true)"], 49 | }, 50 | 51 | ] 52 | }, 53 | { 54 | base: { 55 | name: "isNil", 56 | title: "是否null或undefined", 57 | desc: "判断当前变量的值是否为 null 或 undefined,如果是则返回true", 58 | parmInfo: "(val)" 59 | }, 60 | demo: [8, null, undefined] 61 | }, 62 | { 63 | base: { 64 | name: "isNull", 65 | title: "是否为null", 66 | desc: "判断当前变量的值是否为 null,如果是则返回true", 67 | parmInfo: "(val)" 68 | }, 69 | demo: [8, null, undefined] 70 | }, 71 | { 72 | base: { 73 | name: "isNumber", 74 | title: "是否数值类型", 75 | desc: "判断是否数值类型,如果是则返回true", 76 | parmInfo: "(val)" 77 | }, 78 | demo: [8, null, undefined, NaN] 79 | }, 80 | { 81 | base: { 82 | name: "isArray", 83 | title: "是否数组类型", 84 | desc: "判断是否数组类型,如果是则返回true", 85 | parmInfo: "(obj))" 86 | }, 87 | demo: [ 88 | [8], { 89 | A: "test" 90 | }, 91 | undefined 92 | ] 93 | }, 94 | { 95 | base: { 96 | name: "isPositiveNum", 97 | title: "是否正整数", 98 | desc: "是否正整数,如果是则返回true", 99 | parmInfo: "(val)" 100 | }, 101 | demo: [8, 1.8, -1] 102 | }, { 103 | base: { 104 | name: "isNegativeNum", 105 | title: "是否负整数", 106 | desc: "是否负整数,如果是则返回true", 107 | parmInfo: "(val)" 108 | }, 109 | demo: [8, 1.8, -1] 110 | }, 111 | { 112 | base: { 113 | name: "isInteger", 114 | title: "是否整数", 115 | desc: "是否整数,如果是则返回true", 116 | parmInfo: "(val)" 117 | }, 118 | demo: [8, 1.8, -2] 119 | }, 120 | { 121 | base: { 122 | name: "isPostiveFloat", 123 | title: "是否正浮点数", 124 | desc: "是否正浮点数,如果是则返回true", 125 | parmInfo: "(val)" 126 | }, 127 | demo: [1.1] 128 | }, 129 | { 130 | base: { 131 | name: "isNegativeFloat", 132 | title: "是否负浮点数", 133 | desc: "是否负浮点数,如果是则返回true", 134 | parmInfo: "(val)" 135 | }, 136 | demo: [-1.1] 137 | }, 138 | { 139 | base: { 140 | name: "isFloat", 141 | title: "是否浮点数", 142 | desc: "是否浮点数,如果是则返回true", 143 | parmInfo: "(val)" 144 | }, 145 | demo: [1.1] 146 | }, 147 | { 148 | base: { 149 | name: "isBrowser", 150 | title: "是否浏览器环境", 151 | desc: "用于判断程序运行环境是否在浏览器,这有助于避免在node环境运行前端模块时出错。", 152 | parmInfo: "()" 153 | }, 154 | demo: [""] 155 | }, 156 | { 157 | base: { 158 | name: "isPrime", 159 | title: "是否质数", 160 | desc: "是否质数", 161 | parmInfo: "(num)" 162 | }, 163 | demo: [2, 10] 164 | }, 165 | { 166 | base: { 167 | name: "isAscii", 168 | title: "是否ASCII字符", 169 | desc: "检查字符串是否仅包含ASCII字符", 170 | parmInfo: "(str)" 171 | }, 172 | demo: [{ 173 | parms: ["dz-&8855"], 174 | desc: ["\"dz-&8855\""], 175 | }, { 176 | parms: ["年轻人不讲武德"], 177 | desc: ["\"年轻人不讲武德\""], 178 | } 179 | 180 | ] 181 | }, 182 | { 183 | base: { 184 | name: "isHasWhitespace", 185 | title: "是否包含空格", 186 | desc: "检查字符串是否包含空格", 187 | parmInfo: "(str)" 188 | }, 189 | demo: [{ 190 | parms: ["dz-&8855"], 191 | desc: ["\"dz-&8855\""], 192 | }, { 193 | parms: ["年轻人 不讲武德"], 194 | desc: ["\"年轻人 不讲武德\""], 195 | } 196 | 197 | ] 198 | }, 199 | { 200 | base: { 201 | name: "isHexColor", 202 | title: "是否16进制颜色", 203 | desc: "检查字符串是否为十六进制颜色", 204 | parmInfo: "(color)" 205 | }, 206 | demo: [{ 207 | parms: ["#D1B2C3"], 208 | desc: ["\"#D1B2C3\""], 209 | }, { 210 | parms: ["f00"], 211 | desc: ["\"f00\""], 212 | } 213 | 214 | ] 215 | }, 216 | { 217 | base: { 218 | name: "isHexadecimal", 219 | title: "是否16进制数字", 220 | desc: "检查字符串是否为十六进制颜色", 221 | parmInfo: "(str)" 222 | }, 223 | demo: [{ 224 | parms: ["#D1B2C3"], 225 | desc: ["\"#D1B2C3\""], 226 | }, { 227 | parms: ["A00"], 228 | desc: ["\"A00\""], 229 | } 230 | 231 | ] 232 | }, 233 | { 234 | base: { 235 | name: "isPlainObject", 236 | title: "是否普通对象", 237 | desc: "检查值是否为普通对象", 238 | parmInfo: "(any)" 239 | }, 240 | demo: [ 241 | null, "'haha'", [], {}, () => {}, { 242 | a: '1', 243 | b: '2' 244 | } 245 | ] 246 | }, 247 | { 248 | base: { 249 | name: "isRegExp", 250 | title: "是否正则", 251 | desc: "是否正则表达式", 252 | parmInfo: "(value)" 253 | }, 254 | demo: [{ 255 | parms: /s+/g, 256 | desc: ["/s+/g"], 257 | } 258 | 259 | ] 260 | }, 261 | { 262 | base: { 263 | name: "isString", 264 | title: "是否字符串", 265 | desc: "检查值是否为字符串", 266 | parmInfo: "(value)" 267 | }, 268 | demo: [{ 269 | parms: ["哈哈测试"], 270 | desc: ["\"哈哈测试\""], 271 | }, 272 | { 273 | parms: [123], 274 | desc: ["123"], 275 | } 276 | 277 | ] 278 | }, 279 | { 280 | base: { 281 | name: "isObject", 282 | title: "是否对象", 283 | desc: "检查值是否对象", 284 | parmInfo: "(value)" 285 | }, 286 | demo: [null, [], {}] 287 | }, 288 | { 289 | base: { 290 | name: "isFunction", 291 | title: "是否是函数", 292 | desc: "检查值是否是函数", 293 | parmInfo: "(function)" 294 | }, 295 | demo: [function () {}, 296 | function* () {}, 297 | async function () {}, 298 | () => {} 299 | ] 300 | }, 301 | { 302 | base: { 303 | name: "isWeChat", 304 | title: "是否微信号", 305 | desc: "验证微信号,6至20位,以字母开头,字母,数字,减号,下划线", 306 | parmInfo: "(string)" 307 | }, 308 | demo: [ 309 | "abc2232332" 310 | ] 311 | }, 312 | { 313 | base: { 314 | name: "isChineseName", 315 | title: "是否中文姓名", 316 | desc: "是否中文姓名", 317 | parmInfo: "(string)" 318 | }, 319 | demo: [ 320 | "张三", 321 | "zhang三" 322 | ] 323 | }, 324 | { 325 | base: { 326 | name: "isEnglishName", 327 | title: "是否英文姓名", 328 | desc: "是否英文姓名", 329 | parmInfo: "(string)" 330 | }, 331 | demo: [ 332 | "lily", 333 | "shily 杨" 334 | ] 335 | }, 336 | { 337 | base: { 338 | name: "isSubnetMask", 339 | title: "是否子网掩码", 340 | desc: "验证子网掩码", 341 | parmInfo: "(string)" 342 | }, 343 | demo: [{ 344 | parms: ["255.255.255.0"], 345 | desc: [`"255.255.255.0"`], 346 | }, 347 | { 348 | parms: ["257.255.255.0"], 349 | desc: [`"257.255.255.0"`], 350 | } 351 | ] 352 | }, 353 | { 354 | base: { 355 | name: "isPhone", 356 | title: "是否手机号码", 357 | desc: "是否手机号码.验证大陆", 358 | parmInfo: "(string)" 359 | }, 360 | demo: ["18310221234"] 361 | }, 362 | { 363 | base: { 364 | name: "isEmail", 365 | title: "是否邮箱", 366 | desc: "是否邮箱", 367 | parmInfo: "(string)" 368 | }, 369 | demo: ["123@test.com"] 370 | }, 371 | { 372 | base: { 373 | name: "isHTMLTag", 374 | title: "是否html标签", 375 | desc: "是否html标签", 376 | parmInfo: "(string)" 377 | }, 378 | demo: ["div"] 379 | }, 380 | { 381 | base: { 382 | name: "isSvgTag", 383 | title: "是否svg标签", 384 | desc: "是否svg标签", 385 | parmInfo: "(string)" 386 | }, 387 | demo: ["svg"] 388 | }, 389 | { 390 | base: { 391 | name: "isDate", 392 | title: "是否日期", 393 | desc: "是否日期", 394 | parmInfo: "(val)" 395 | }, 396 | demo: [{ 397 | parms: [new Date()], 398 | desc: [`new Date()`], 399 | }] 400 | }, 401 | { 402 | base: { 403 | name: "isEmoji", 404 | title: "是否emoji字符", 405 | desc: "是否emoji字符", 406 | parmInfo: "(val)" 407 | }, 408 | demo: [{ 409 | parms: ["☺"], 410 | desc: [`"☺"`], 411 | }] 412 | }, 413 | { 414 | base: { 415 | name: "isLicensePlate", 416 | title: "是否车牌号", 417 | desc: "是否车牌号", 418 | parmInfo: "(val)" 419 | }, 420 | demo: ["京A2345D"] 421 | }, 422 | { 423 | base: { 424 | name: "isPwdStrength", 425 | title: "是否强密码", 426 | desc: "是否强密码,至少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符", 427 | parmInfo: "(val)" 428 | }, 429 | demo: ["U#3p09sd"] 430 | }, 431 | { 432 | base: { 433 | name: "isIPV4", 434 | title: "是否ipv4", 435 | desc: "是否ipv4", 436 | parmInfo: "(val)" 437 | }, 438 | demo: ["127.0.0.1", "234.345.123.3"] 439 | }, 440 | { 441 | base: { 442 | name: "isIPV6", 443 | title: "是否ipv6", 444 | desc: "是否ipv4", 445 | parmInfo: "(val)" 446 | }, 447 | demo: ["fe80::1457:990b:fd57:d88c%23","127.0.0.1"] 448 | }, 449 | { 450 | base: { 451 | name: "isMacAddress", 452 | title: "是否mac地址", 453 | desc: "是否mac地址", 454 | parmInfo: "(val)" 455 | }, 456 | demo: ["28-D2-44-A6-05-55", "24:f9:a3:7b:d5:51"] 457 | }, 458 | { 459 | base: { 460 | name: "isUrl", 461 | title: "是否URL地址", 462 | desc: "是否URL地址,支持https,http", 463 | parmInfo: "(val)" 464 | }, 465 | demo: ["https://www.baidu.com", "http://www.baidu.com"] 466 | }, 467 | { 468 | base: { 469 | name: "isVersion", 470 | title: "是否X.Y.Z格式版本号", 471 | desc: "是否X.Y.Z格式版本号,严格模式", 472 | parmInfo: "(val)" 473 | }, 474 | demo: ["1.0.2", "1.3"] 475 | }, 476 | { 477 | base: { 478 | name: "isLeapYear", 479 | title: "是否闰年", 480 | desc: "是否闰年", 481 | parmInfo: "(year)" 482 | }, 483 | demo: [2020, 2021] 484 | } 485 | ]; 486 | 487 | const docData = { 488 | title: { 489 | label: "常规函数", 490 | value: "base", 491 | expand: true, 492 | testValue: "", 493 | testResult: "", 494 | children: [] 495 | }, 496 | children: children 497 | } 498 | 499 | export default docData; -------------------------------------------------------------------------------- /src/doc/cache.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 缓存函数说明 3 | */ 4 | const children = [{ 5 | base: { 6 | name: "LRUCache", 7 | title: "LRU缓存结构", 8 | desc: `LRU缓存结构,超出容量范围时,删除一个'最近最少使用'缓存; 9 | 如果键存在于缓存中,则获取键的值,否则返回 -1。 10 | 参数: capacity 容量大小,Number类型,不存在返回-1 11 | 示例: 12 | let cache = $R.LRUCache(2); // 2代表缓存容量; $R.LRUCache(capacity)每调用一次就产生一个新的缓存空间 13 | 方法: 14 | cache.put(key,value); //key:主键, value:对象值 15 | cache.get(key); //获取返回值,key:主键 16 | cache.clear(); //清除内置缓存所有数据 17 | 18 | cache.put(1, 1); 19 | cache.put(2, 2); 20 | cache.get(1); // 返回 1 21 | cache.put(3, 3); // 该操作移除key=2,同时加入key=3缓存 22 | cache.get(2); // 返回 -1 (未找到) 23 | `, 24 | parmInfo: "(capacity)", 25 | showDemo: false 26 | }, 27 | demo: [] 28 | }, 29 | { 30 | base: { 31 | name: "LFUCache", 32 | title: "LFU缓存结构", 33 | desc: `LFUCache缓存结构,当缓存达到其容量时,则应该在插入新项之前,使最不经常使用的项无效。在此问题中,当两个或更多个键具有相同使用频率时,去除'最久未使用'的键。 34 | 如果键存在于缓存中,则获取键的值,否则返回 -1。 35 | 参数: capacity 容量大小,Number类型 36 | 示例: 37 | let cache = $R.LFUCache(2); // 2代表缓存容量 $R.LFUCache(capacity)每调用一次就产生一个新的缓存空间 38 | 方法: 39 | cache.put(key,value); //key:主键, value:对象值 40 | cache.get(key); //获取返回值,key:主键;不存在返回-1 41 | cache.clear(); //清除内置缓存所有数据 42 | 43 | cache.put(1, 1); 44 | cache.put(2, 2); 45 | cache.get(1); // 返回 1 46 | cache.put(3, 3); // 去除键 2,并将key为3加入缓存 47 | cache.get(2); // 返回 -1(未找到) 48 | cache.get(3); // 返回 3 49 | cache.put(4, 4); // 去除键 1 50 | cache.get(1); // 返回 -1(未找到) 51 | cache.get(3); // 返回 3 52 | cache.get(4); // 返回 4 53 | `, 54 | parmInfo: "(capacity)", 55 | showDemo: false 56 | }, 57 | demo: [] 58 | } 59 | 60 | ] 61 | 62 | const docData = { 63 | title: { 64 | label: "缓存", 65 | value: "cache", 66 | expand: true, 67 | testValue: "", 68 | testResult: "", 69 | children: [] 70 | }, 71 | children: children 72 | } 73 | 74 | export default docData; -------------------------------------------------------------------------------- /src/doc/datetime.js: -------------------------------------------------------------------------------- 1 | const children = [{ 2 | base: { 3 | name: "dateFormat", 4 | title: "格式化日期", 5 | desc: `格式化日期 6 | valueFormat: yyyy-MM-dd HH:mm:fff 7 | yy M d H m f 格式任意组合\n 8 | dateTime: 可不传值, 默认当前日期new Date() 9 | timeDiff=8:默认增加8小时,国内不用处理,如果有时区差异地区,可调整此时区来获取当地时间`, 10 | parmInfo: "(valueFormat, dateTime = new Date(),timeDiff=8)" 11 | }, 12 | demo: [{ 13 | parms: ["yyyy-MM-dd HH:mm:fff"], 14 | desc: [`"yyyy-MM-dd HH:mm:fff"`], 15 | }, { 16 | parms: ["yyyy-MM-dd HH:mm:fff", new Date(2020, 10, 1, 10, 10, 10)], 17 | desc: [`"yyyy-MM-dd HH:mm:fff",new Date(2020,10,1,10,10,10)`], 18 | }] 19 | }, 20 | { 21 | base: { 22 | name: "daysDiff", 23 | title: "日期之差天数", 24 | desc: "计算两个日期之间的差异天数", 25 | parmInfo: "(date,date)" 26 | }, 27 | demo: [{ 28 | parms: [new Date("2020-09-18"), new Date("2021-01-18")], 29 | desc: ["new Date(\"2020-09-18\"), new Date(\"2021-01-18\")"], 30 | }, ] 31 | }, 32 | { 33 | base: { 34 | name: "monthDiff", 35 | title: "日期之差月数", 36 | desc: "计算两个日期之间的月数", 37 | parmInfo: "(date,date)" 38 | }, 39 | demo: [{ 40 | parms: [new Date("2020-09-18"), new Date("2021-01-18")], 41 | desc: ["new Date(\"2020-09-18\"), new Date(\"2021-01-18\")"], 42 | }] 43 | }, 44 | { 45 | base: { 46 | name: "dayOfYear", 47 | title: "日期一年中哪一天", 48 | desc: "日期获取一年中的哪一天", 49 | parmInfo: "(date)" 50 | }, 51 | demo: [{ 52 | parms: [new Date("2020-09-16")], 53 | desc: [`new Date("2020-09-16")`], 54 | } 55 | 56 | ] 57 | }, 58 | { 59 | base: { 60 | name: "daysInMonth", 61 | title: "某年某月的天数", 62 | desc: `获取某年某月的天数 63 | 参数: 64 | month: 1-12月 65 | `, 66 | parmInfo: "(year, month)" 67 | }, 68 | demo: [{ 69 | parms: [2020, 9], 70 | desc: ["2020,9"], 71 | } 72 | 73 | ] 74 | }, 75 | { 76 | base: { 77 | name: "weekDayEnglish", 78 | title: "英语-指定日期的周几", 79 | desc: "获取指定日期的周几,英语", 80 | parmInfo: "(date)" 81 | }, 82 | demo: [{ 83 | parms: [new Date()], 84 | desc: ["new Date()"], 85 | } 86 | 87 | ] 88 | }, 89 | { 90 | base: { 91 | name: "weekDayChinese", 92 | title: "汉语-指定日期的周几", 93 | desc: "获取指定日期的周几,汉语显示", 94 | parmInfo: "(date)" 95 | }, 96 | demo: [{ 97 | parms: [new Date()], 98 | desc: ["new Date()"], 99 | } 100 | 101 | ] 102 | }, 103 | { 104 | base: { 105 | name: "monthNameEnglish", 106 | title: "英语-指定日期的几月", 107 | desc: "获取指定日期是几月,英语显示", 108 | parmInfo: "(date)" 109 | }, 110 | demo: [{ 111 | parms: [new Date()], 112 | desc: ["new Date()"], 113 | } 114 | 115 | ] 116 | }, 117 | { 118 | base: { 119 | name: "monthNameChinese", 120 | title: "汉语-指定日期的几月", 121 | desc: "获取指定日期是几月,汉语显示", 122 | parmInfo: "(date, isNum = true)" 123 | }, 124 | demo: [{ 125 | parms: [new Date()], 126 | desc: ["new Date()"], 127 | }, 128 | { 129 | parms: [new Date(), false], 130 | desc: ["new Date(),false"], 131 | } 132 | ] 133 | }, 134 | { 135 | base: { 136 | name: "timeDistance", 137 | title: "时间距离", 138 | desc: `传入时间与当前时间的距离描述,比如10天前,此刻,10秒前 139 | 具体分为: 年, 个月, 星期, 天, 小时, 分钟, 秒`, 140 | parmInfo: "(startDate)" 141 | }, 142 | demo: [{ 143 | parms: [new Date(2020, 11, 11)], 144 | desc: ["new Date(2020,11,11)"], 145 | }] 146 | } 147 | ] 148 | const docData = { 149 | title: { 150 | label: "日期", 151 | value: "array", 152 | expand: true, 153 | testValue: "", 154 | testResult: "", 155 | children: [] 156 | }, 157 | children: children 158 | } 159 | 160 | export default docData; -------------------------------------------------------------------------------- /src/doc/encryption.js: -------------------------------------------------------------------------------- 1 | const children = [ 2 | { 3 | base: { 4 | name: "base64Encode", 5 | title: "base64编码", 6 | desc: "base64编码", 7 | parmInfo: "(string)" 8 | }, 9 | demo: ["Hello Moon"] 10 | }, 11 | { 12 | base: { 13 | name: "base64Decode", 14 | title: "base64解码", 15 | desc: "base64解码", 16 | parmInfo: "(string)" 17 | }, 18 | demo: ["SGVsbG8gTW9vbg=="] 19 | }, 20 | { 21 | base: { 22 | name: "md5", 23 | title: "md5加密", 24 | desc: "md5加密", 25 | parmInfo: "(string)" 26 | }, 27 | demo: ["qwer123"] 28 | } 29 | ] 30 | 31 | const docData = { 32 | title: { 33 | label: "加密", 34 | value: "encryption", 35 | expand: true, 36 | testValue: "", 37 | testResult: "", 38 | children: [] 39 | }, 40 | children: children 41 | } 42 | 43 | export default docData; -------------------------------------------------------------------------------- /src/doc/function.js: -------------------------------------------------------------------------------- 1 | const children = [{ 2 | base: { 3 | name: "objType", 4 | title: "获取对象类型", 5 | desc: "获取对象类型", 6 | parmInfo: "(object)" 7 | }, 8 | demo: [ 9 | [], {}, 10 | 2, "abc" 11 | ] 12 | }, 13 | { 14 | base: { 15 | name: "byteSize", 16 | title: "获取字节", 17 | desc: "获取字节长度", 18 | parmInfo: "(string)" 19 | }, 20 | demo: [{ 21 | parms: [ 22 | ["微笑"] 23 | ], 24 | desc: ["\"微笑\""], 25 | }, 26 | { 27 | parms: [ 28 | ["wu de"] 29 | ], 30 | desc: ["\"wu de\""], 31 | 32 | } 33 | ] 34 | }, 35 | { 36 | base: { 37 | name: "splitLines", 38 | title: "换行字符串转数组", 39 | desc: "正则表达式匹配换行符并创建一个数组", 40 | parmInfo: "(string)" 41 | }, 42 | demo: [{ 43 | parms: ["wu\nde\nha\nqiu"], 44 | desc: ["\"wu\\nde\\nha\\nqiu\""] 45 | }] 46 | }, 47 | { 48 | base: { 49 | name: "removeHTMLTags", 50 | title: "移除html标签", 51 | desc: "删除字符串中的html标签", 52 | parmInfo: "(string)" 53 | }, 54 | demo: [{ 55 | parms: ["
123
"], 56 | desc: ["\"
123
\""] 57 | }] 58 | }, 59 | { 60 | base: { 61 | name: "escapeHTML", 62 | title: "转义html", 63 | desc: "将html标签进行转移", 64 | parmInfo: "(string)" 65 | }, 66 | demo: [{ 67 | parms: ["
123
"], 68 | desc: ["\"
123
\""] 69 | }] 70 | }, 71 | { 72 | base: { 73 | name: "unescapeHTML", 74 | title: "还原html", 75 | desc: "将转义后html进行还原", 76 | parmInfo: "(string)" 77 | }, 78 | demo: ["<div v-for='(group, index) in listDoc' :key='index'>123</div>"] 79 | }, 80 | { 81 | base: { 82 | name: "formatNumber", 83 | title: "格式化数值", 84 | desc: "将数字进行分割", 85 | parmInfo: "(num, cnt =3, split = ',')" 86 | }, 87 | demo: [{ 88 | parms: [123456789], 89 | desc: [`123456789`] 90 | }, { 91 | parms: [123456789, 4, '-'], 92 | desc: [`123456789,4,'-'`] 93 | }] 94 | }, 95 | { 96 | base: { 97 | name: "formatString", 98 | title: "格式化字符串", 99 | desc: "将字符串进行分割", 100 | parmInfo: "(string, cnt =3, split = ',')" 101 | }, 102 | demo: [{ 103 | parms: ["absdfsfsdfasdfassad"], 104 | desc: [`"absdfsfsdfasdfassad"`] 105 | }, { 106 | parms: ["absdfsfsdfasdfassad", 4, "-"], 107 | desc: [`"absdfsfsdfasdfassad", 4, "-"`] 108 | }] 109 | }, 110 | { 111 | base: { 112 | name: "throttle", 113 | title: "节流", 114 | desc: `节流操作使回调函数在每隔一段时间定期执行一次,时间间隔内再触发,不会重新执行。 115 | 用例: 116 | function test(val) { 117 | console.log("test throttle" + val); 118 | } 119 | 120 | var myThrottle = $R.throttle(test, 1000); 121 | 122 | myThrottle("测试传参"); //执行 123 | myThrottle("测试传参"); //节流时间内不执行 124 | 125 | `, 126 | parmInfo: "(func, wait = 100)", 127 | showDemo: false 128 | }, 129 | demo: [] 130 | }, 131 | { 132 | base: { 133 | name: "debounce", 134 | title: "防抖", 135 | desc: `短时间内多次触发一个函数,只执行最后一次,或在开始时执行 136 | 用例: 137 | function test(val) { 138 | console.log("test throttle" + val); 139 | } 140 | 141 | var myDebounce = $R.debounce(test, 1000); 142 | 143 | myDebounce("测试传参"); //防抖未执行 144 | myDebounce("测试传参"); //延迟1秒执 145 | 146 | `, 147 | parmInfo: "(func, wait = 100)", 148 | showDemo: false 149 | }, 150 | demo: [] 151 | }, 152 | { 153 | base: { 154 | name: "urlParam", 155 | title: "获取URL参数", 156 | desc: `获取URL参数 157 | 参数说明: 158 | 159 | nullDefault = null: 参数不存在默认返回null,可自定义修改不存在默认参数 160 | 161 | url = window.location.href:默认当前URL,也可以手动传入url http://test.com?a=123&b=234 162 | 163 | `, 164 | parmInfo: "(param, nullDefault = null,url = window.location.href)", 165 | showDemo: true 166 | }, 167 | demo: [{ 168 | parms: ["a", null, "http://test.com?a=123&b=234"], 169 | desc: [`"a",null,"http://test.com?a=123&b=234"`] 170 | }, { 171 | parms: ["c", "测试空", "http://test.com?a=123&b=234"], 172 | desc: [`"c","测试空","http://test.com?a=123&b=234"`] 173 | }, { 174 | parms: ["c"], 175 | desc: [`"c"`] 176 | }] 177 | }, 178 | { 179 | base: { 180 | name: "randomNum", 181 | title: "获取随机数", 182 | desc: "获取随机数", 183 | parmInfo: "(min, max)" 184 | }, 185 | demo: [{ 186 | parms: [1, 5], 187 | desc: [`1,5`] 188 | }] 189 | }, 190 | { 191 | base: { 192 | name: "randomCode", 193 | title: "获取随机码", 194 | desc: "获取指定位数的随机码(数字+字母(大写)),默认4位", 195 | parmInfo: "(codeLength = 4)" 196 | }, 197 | demo: [4, 6] 198 | }, 199 | { 200 | base: { 201 | name: "randomIP", 202 | title: "生成随机ip-v4", 203 | desc: "生成随机ip-v4", 204 | parmInfo: "()", 205 | showDemo: false 206 | }, 207 | 208 | demo: [ 209 | "" 210 | ] 211 | }, 212 | { 213 | base: { 214 | name: "randomHexColor", 215 | title: "随机16进制颜色", 216 | desc: `随机16进制颜色`, 217 | parmInfo: "()" 218 | }, 219 | demo: [""] 220 | }, 221 | { 222 | base: { 223 | name: "randomBoolean", 224 | title: "随机生成布尔值", 225 | desc: `随机生成布尔值`, 226 | parmInfo: "()" 227 | }, 228 | demo: [""] 229 | }, 230 | { 231 | base: { 232 | name: "randomFloat", 233 | title: "随机生成浮点数据", 234 | desc: `随机生成浮点数据`, 235 | parmInfo: "(min = 0, max = 1)" 236 | }, 237 | demo: [{ 238 | parmMore: [0, 1] 239 | }] 240 | }, 241 | { 242 | base: { 243 | name: "capitalize", 244 | title: "首字母大写", 245 | desc: "首字母大写", 246 | parmInfo: "(string)" 247 | }, 248 | demo: ["hello", "hello moon"] 249 | }, 250 | { 251 | base: { 252 | name: "capitalizeEveryWord", 253 | title: "每个首字母都大写", 254 | desc: "每个首字母都大写", 255 | parmInfo: "(string)" 256 | }, 257 | demo: ["hello 哈哈 moon", "hello moon"] 258 | }, 259 | { 260 | base: { 261 | name: "decapitalize", 262 | title: "首字母小写", 263 | desc: "首字母小写", 264 | parmInfo: "(string)" 265 | }, 266 | demo: ["Hello", "Hello Moon"] 267 | }, 268 | { 269 | base: { 270 | name: "decapitalizeEveryWord", 271 | title: "每个首字母都小写", 272 | desc: "每个首字母都小写", 273 | parmInfo: "(string)" 274 | }, 275 | demo: ["Hello 哈哈 Moon", "Hello Moon"] 276 | }, 277 | { 278 | base: { 279 | name: "camelCase", 280 | title: "转为驼峰命名", 281 | desc: "转为驼峰命名,以下划线和中间分隔先进行转换处理", 282 | parmInfo: "(string)" 283 | }, 284 | demo: ["my-name-desc", "Hello-Ni_Hao"] 285 | }, 286 | { 287 | base: { 288 | name: "hyphenate", 289 | title: "驼峰转为连字符", 290 | desc: "驼峰转为连字符,全都为小写", 291 | parmInfo: "(string)" 292 | }, 293 | demo: ["myNameZhangSan"] 294 | }, 295 | { 296 | base: { 297 | name: "privacyPhone", 298 | title: "脱敏电话号码", 299 | desc: "脱敏电话保留前三位,后四位,中间默认变为*号", 300 | parmInfo: "(phone, split = '****')" 301 | }, 302 | demo: [{ 303 | parms: ["18312349876"], 304 | desc: [`"18312349876"`] 305 | }, 306 | { 307 | parms: ["18312349876", "^^^^"], 308 | desc: [`"18312349876","^^^^"`] 309 | } 310 | ] 311 | }, 312 | { 313 | base: { 314 | name: "privacyName", 315 | title: "脱敏姓名", 316 | desc: "脱敏姓名保留前一位,其余默认变为*号", 317 | parmInfo: "(name, split = '**')" 318 | }, 319 | demo: [{ 320 | parms: ["张三丰"], 321 | desc: [`"张三丰"`] 322 | }, 323 | { 324 | parms: ["张三丰", "^^"], 325 | desc: [`"张三丰","^^"`] 326 | } 327 | ] 328 | }, 329 | { 330 | base: { 331 | name: "bytesToSize", 332 | title: "比特单位转换", 333 | desc: `比特单位转换 334 | 单位: B, KB, MB, GB, TB, PB, EB, ZB, YB 335 | `, 336 | parmInfo: "(bytes)" 337 | }, 338 | demo: [0, 15, 2323, 5678999] 339 | }, 340 | { 341 | base: { 342 | name: "getUUID", 343 | title: "获取uuid", 344 | desc: "获取uuid", 345 | parmInfo: "(spit = '-')" 346 | }, 347 | demo: [ 348 | "-", "(*)" 349 | ] 350 | }, 351 | { 352 | base: { 353 | name: "deepClone", 354 | title: "深度克隆对象", 355 | desc: `深度克隆对象 356 | 支持正则克隆对象,当前页面测试正则因JSON.stringify转换正则无法正常显示,无法测试 357 | `, 358 | parmInfo: "(obj, hash = new WeakMap())" 359 | }, 360 | demo: [{ 361 | parms: [{ 362 | name: "张三", 363 | age: 12, 364 | hobby: ["打篮球", "吹牛"] 365 | 366 | }], 367 | desc: [`{ 368 | name: "张三", 369 | age: 12, 370 | hobby: ["打篮球", "吹牛"] 371 | }`] 372 | }] 373 | }, 374 | { 375 | base: { 376 | name: "deepFreeze", 377 | title: "深度冻结对象", 378 | desc: `深度冻结对象,只能冻结对象`, 379 | parmInfo: "(obj)", 380 | showDemo: false 381 | }, 382 | demo: [] 383 | }, 384 | { 385 | base: { 386 | name: "splitPath", 387 | title: "分割路径", 388 | desc: `分割路径`, 389 | parmInfo: "(path)" 390 | }, 391 | demo: ["D:\\Downloads\\src\\chibang.wav", "/Downloads/src/chibang.wav"] 392 | }, 393 | { 394 | base: { 395 | name: "digitUpperCase", 396 | title: "数字转大写金额", 397 | desc: `数字转大写金额`, 398 | parmInfo: "(number)" 399 | }, 400 | demo: [12345.67, -100] 401 | }, 402 | { 403 | base: { 404 | name: "subText", 405 | title: "截取字符串", 406 | desc: `截取字符串,多出部分默认为... 可自定义改为其他`, 407 | parmInfo: "(str, length = 0, rep = '...')" 408 | }, 409 | demo: [{ 410 | parms: ["上山打老虎", 2], 411 | desc: [`"上山打老虎",2`] 412 | }, 413 | { 414 | parms: ["上山打老虎", 2, "***"], 415 | desc: [`"上山打老虎",2,"***"`] 416 | } 417 | ] 418 | }, 419 | { 420 | base: { 421 | name: "formatPath", 422 | title: "格式化路径", 423 | desc: `格式化路径,斜线正常化`, 424 | parmInfo: "(str, length = 0, rep = '...')" 425 | }, 426 | demo: [ 427 | ".\\ni\\wo\\ta\\", 428 | ".//ni//wo///////ta/" 429 | ] 430 | }, 431 | { 432 | base: { 433 | name: "characterCount", 434 | title: "字符出现次数", 435 | desc: `字符在字符串中出现次数`, 436 | parmInfo: "(str = '', char = '')" 437 | }, 438 | demo: [{ 439 | parms: ["abcdabcabca", "a"], 440 | desc: [`"abcdabcabca", "a"`] 441 | }, 442 | { 443 | parms: ["年轻人不讲武德,武德如风", "武德"], 444 | desc: [`"年轻人不讲武德,武德如风", "武德"`] 445 | } 446 | 447 | ] 448 | }, 449 | 450 | { 451 | base: { 452 | name: "starScore", 453 | title: "获取星标评分", 454 | desc: `获取星标评分,只支持五颗星, 评分区间: 0-5 不在范围会强制加入区间最近边界`, 455 | parmInfo: "(rate)" 456 | }, 457 | demo: [3] 458 | }, 459 | { 460 | base: { 461 | name: "once", 462 | title: "执行一次函数", 463 | desc: `只执行一次函数 464 | 465 | 传入参数:fn是一个函数 466 | 示例: 467 | let count = 1; 468 | const testFn = () => { 469 | count++; 470 | return count; 471 | } 472 | const myOnce = $R.once(testFn); 473 | myOnce(); //执行结果: 2 474 | myOnce(); //执行结果: 2 475 | myOnce(); //执行结果: 2 476 | `, 477 | parmInfo: "(fn)", 478 | showDemo: false 479 | }, 480 | demo: [] 481 | }, 482 | { 483 | base: { 484 | name: "csvToArray", 485 | title: "csv转换为数组", 486 | desc: `csv转换为数组 487 | 参数说明: 488 | data : csv数据 489 | delimiter = ',' :分割符,默认逗号 490 | omitFirstRow = false :是否标题行 默认否`, 491 | parmInfo: "(data, delimiter = ',', omitFirstRow = false)" 492 | }, 493 | demo: [{ 494 | parms: ['a,b\nc,d'], 495 | desc: [`'a,b\\nc,d'`] 496 | }, 497 | { 498 | parms: ['a;b\nc;d', ';'], 499 | desc: [`'a;b\\nc;d', ';'`] 500 | }, 501 | { 502 | parms: ['col1,col2\na,b\nc,d', ',', true], 503 | desc: [`'col1,col2\\na,b\\nc,d', ',', true`] 504 | } 505 | ] 506 | }, 507 | { 508 | base: { 509 | name: "csvToJSON", 510 | title: "csv转换为json", 511 | desc: `csv转换为json 512 | 参数说明: 513 | data : csv数据 514 | delimiter = ',' :分割符,默认逗号`, 515 | parmInfo: "(data, delimiter = ',')" 516 | }, 517 | demo: [{ 518 | parms: ['col1,col2\na,b\nc,d'], 519 | desc: [`col1,col2\\na,b\\nc,d`] 520 | }, 521 | { 522 | parms: ['col1;col2\na;b\nc;d', ';'], 523 | desc: [`'col1;col2\\na;b\\nc;d', ';'`] 524 | } 525 | ] 526 | }, 527 | { 528 | base: { 529 | name: "timeTaken", 530 | title: "计算函数执行时间", 531 | desc: ` 532 | 参数说明: 533 | 传入函数,例如: () => Math.pow(2, 10) 534 | 返回函数结果 535 | 时间计算是借助console处理,控制台可看到运行时间 536 | console.time('timeTaken'); 537 | console.timeEnd('timeTaken'); 538 | `, 539 | parmInfo: "(callback)" 540 | }, 541 | demo: [{ 542 | parms: [() => Math.pow(2, 10)], 543 | desc: [`() => Math.pow(2, 10)`] 544 | }] 545 | } 546 | ] 547 | const docData = { 548 | title: { 549 | label: "函数", 550 | value: "function", 551 | expand: true, 552 | testValue: "", 553 | testResult: "", 554 | children: [] 555 | }, 556 | children: children 557 | } 558 | 559 | export default docData; -------------------------------------------------------------------------------- /src/doc/index.js: -------------------------------------------------------------------------------- 1 | import base from './base.js' 2 | import array from './array.js' 3 | import func from './function.js' 4 | import datetime from './datetime.js' 5 | import encryption from './encryption.js' 6 | import window from './window.js' 7 | import cache from './cache.js' 8 | import struct from './struct.js' 9 | //将文档说明都集成此数组 10 | const dataList = [ 11 | base, 12 | array, 13 | func, 14 | datetime, 15 | encryption, 16 | window, 17 | cache, 18 | struct 19 | ] 20 | 21 | //合成文档json结构,界面调用 22 | export default () => { 23 | let docTree = [] 24 | let docMapDemo = new Map() 25 | dataList.map(item => { 26 | let titleData = item.title; 27 | titleData.children = []; 28 | //创建demo示例Map组合 29 | item.children.map(itemchild => { 30 | titleData.children.push(itemchild.base); 31 | docMapDemo.set(itemchild.base.name, itemchild.demo); 32 | }) 33 | //构建菜单组织树 34 | docTree.push(titleData); 35 | }) 36 | 37 | return { 38 | docTree, 39 | docMapDemo 40 | }; 41 | } 42 | 43 | 44 | 45 | /** 模板函数 46 | const children = [{ 47 | base: { 48 | name: "isBrowser", 49 | title: "是否浏览器环境", 50 | desc: "用于判断程序运行环境是否在浏览器,这有助于避免在node环境运行前端模块时出错。", 51 | parmInfo: "()" 52 | }, 53 | demo: [""] 54 | }] 55 | 56 | const docData = { 57 | title: { 58 | label: "数组", 59 | value: "array", 60 | expand: true, 61 | testValue: "", 62 | testResult: "", 63 | children: [] 64 | }, 65 | children: children 66 | } 67 | 68 | export default docData; 69 | 70 | */ -------------------------------------------------------------------------------- /src/doc/struct.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 结构函数说明 3 | */ 4 | const children = [{ 5 | base: { 6 | name: "eventEmitter", 7 | title: "发布订阅对象方式", 8 | desc: ` 9 | 发布订阅,缓存基于对象方式,即:{} 10 | 发布-订阅模式其实是一种对象间一对多的依赖关系, 11 | 当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。 12 | 13 | 函数使用,测试如下: 14 | let ret = []; //临时存储数组 15 | function user1(content) { 16 | ret.push("user1订阅:" + content) 17 | } 18 | 19 | function user2(content) { 20 | ret.push("user2订阅:" + content) 21 | } 22 | 23 | function user3(content) { 24 | ret.push("user3订阅:" + content) 25 | } 26 | 27 | function user4(content) { 28 | ret.push("user4订阅:" + content) 29 | } 30 | 31 | //1.常规写法 32 | let em = $R.eventEmitter(); 33 | // 订阅 34 | em.on('event1', user1); 35 | em.on('event1', user2); 36 | em.on('event1', user3); 37 | // 取消user2方法的订阅 38 | em.off('event1', user2); 39 | //执行一次 40 | em.once('event2', user4) 41 | 42 | // 发布 43 | em.emit('event1', '发布-订阅模式'); 44 | em.emit('event1', '发布-订阅模式'); 45 | em.emit('event2', '执行一次'); 46 | em.emit('event2', '执行一次'); 47 | 48 | //2.链式写法 49 | ret = []; //清空数组 50 | em = $R.eventEmitter(); 51 | 52 | em.on('event1', user1) // 订阅 53 | .on('event1', user2) // 订阅 54 | .on('event1', user3) // 订阅 55 | .off('event1', user2) // 取消user2方法的订阅 56 | .once('event2', user4) //执行一次 57 | .emit('event1', '发布-订阅模式') // 发布 58 | .emit('event1', '发布-订阅模式') // 发布 59 | .emit('event2', '执行一次') // 发布 60 | .emit('event2', '执行一次'); // 发布 61 | 62 | //清除全部注册内容 63 | em.clear(); 64 | 65 | //两种方式,执行结果 66 | ret=[ 67 | "user1订阅:发布-订阅模式", 68 | "user3订阅:发布-订阅模式", 69 | "user1订阅:发布-订阅模式", 70 | "user3订阅:发布-订阅模式", 71 | "user4订阅:执行一次" 72 | ] 73 | `, 74 | parmInfo: "()", 75 | showDemo: false 76 | }, 77 | demo: [] 78 | }, 79 | { 80 | base: { 81 | name: "eventEmitterMap", 82 | title: "发布订阅Map方式", 83 | desc: ` 84 | 发布订阅,缓存基于Map结构存储 85 | 用法同eventEmitter,记得复制上面代码改掉函数名字哦 86 | `, 87 | parmInfo: "()", 88 | showDemo: false 89 | }, 90 | demo: [] 91 | }, { 92 | base: { 93 | name: "sensitiveWords", 94 | title: "敏感词过滤", 95 | desc: ` 96 | 基于DFA算法,过滤敏感词 97 | 测试用例如下: 98 | const sw = $R.sensitiveWords(); 99 | //追加敏感词 100 | sw.addWords(['唐僧', '孙悟空', '猪八戒']); 101 | sw.addWord('玉帝哥哥'); 102 | 103 | let value = "我爱唐僧哥哥"; 104 | //检测敏感词 105 | sw.containsDfa(value)//true); 106 | sw.wordsDfa(value)//['唐僧']; 107 | 108 | value = "唐僧念紧箍咒,孙悟空要打唐僧,猪八戒看不懂"; 109 | sw.containsDfa(value)//true; 110 | sw.wordsDfa(value)//['唐僧', '孙悟空', '唐僧', '猪八戒']; 111 | 112 | value = "我爱嫦娥姐姐" 113 | sw.containsDfa(value)//false; 114 | sw.wordsDfa(value)//[] 115 | 116 | //检测敏感词替换 117 | value = "我不是玉帝哥哥,就是个干滴滴的弟弟" 118 | sw.replaceDfa(value, '*', true)//"我不是*,就是个干滴滴的弟弟" 119 | sw.replaceDfa(value, '*', false)//"我不是****,就是个干滴滴的弟弟" 120 | 121 | `, 122 | parmInfo: "()", 123 | showDemo: false 124 | }, 125 | demo: [] 126 | }, { 127 | base: { 128 | name: "trie", 129 | title: "trie树算法", 130 | desc: ` 131 | let trie = $R.trie(); 132 | //追加敏感词 133 | trie.inserts(['唐僧', '孙悟空', '猪八戒']); 134 | trie.insert('玉帝哥哥'); 135 | //检测词语 136 | trie.search("唐僧")//true; 137 | trie.search("唐僧你")//false; 138 | trie.startsWith("唐")//true; 139 | trie.startsWith("唐长老")//false; 140 | `, 141 | parmInfo: "()", 142 | showDemo: false 143 | }, 144 | demo: [] 145 | } 146 | ] 147 | 148 | const docData = { 149 | title: { 150 | label: "结构", 151 | value: "struct", 152 | expand: true, 153 | testValue: "", 154 | testResult: "", 155 | children: [] 156 | }, 157 | children: children 158 | } 159 | 160 | export default docData; -------------------------------------------------------------------------------- /src/doc/window.js: -------------------------------------------------------------------------------- 1 | const children = [{ 2 | base: { 3 | name: "goToTop", 4 | title: "滚动页面顶部", 5 | desc: `滚动页面顶部 6 | isAnimation = flase :直接滚动到顶部 7 | isAnimation = true :滚动到顶部,具备滚动效果 8 | `, 9 | parmInfo: "(isAnimation = false)", 10 | showDemo: false 11 | }, 12 | demo: [] 13 | }, 14 | { 15 | base: { 16 | name: "goToTopClassName", 17 | title: "滚动class元素顶部", 18 | desc: "某个具备class元素内置滚动轴回到顶部,相同class多个只处理第一个", 19 | parmInfo: "(className)", 20 | showDemo: false 21 | }, 22 | demo: [] 23 | }, 24 | { 25 | base: { 26 | name: "goToTopId", 27 | title: "滚动ID顶部", 28 | desc: "某个具备id元素内置滚动轴回到顶部", 29 | parmInfo: "(id, isAnimation = false)", 30 | showDemo: false 31 | }, 32 | demo: [] 33 | }, 34 | { 35 | base: { 36 | name: "getSelectedText", 37 | title: "获取所选元素", 38 | desc: "获取所选元素", 39 | parmInfo: "()", 40 | showDemo: false 41 | }, 42 | demo: [] 43 | }, 44 | { 45 | base: { 46 | name: "setLocalStorage", 47 | title: "存储到localStorage", 48 | desc: "存储到localStorage", 49 | parmInfo: "(key, value)", 50 | testmode: "button" 51 | }, 52 | demo: [] 53 | }, 54 | { 55 | base: { 56 | name: "getLocalStorage", 57 | title: "读取localStorage", 58 | desc: "读取localStorage", 59 | parmInfo: "(key)", 60 | testmode: "button" 61 | }, 62 | demo: [] 63 | }, 64 | { 65 | base: { 66 | name: "delLocalStorage", 67 | title: "删除localStorage", 68 | desc: "删除localStorage", 69 | parmInfo: "(key)", 70 | testmode: "button" 71 | }, 72 | demo: [] 73 | }, 74 | { 75 | base: { 76 | name: "setSessionStorage", 77 | title: "存储到sessionStorage", 78 | desc: "存储到sessionStorage", 79 | parmInfo: "(key, value)", 80 | testmode: "button" 81 | }, 82 | demo: [] 83 | }, 84 | { 85 | base: { 86 | name: "getSessionStorage", 87 | title: "读取sessionStorage", 88 | desc: "读取sessionStorage", 89 | parmInfo: "(key)", 90 | testmode: "button" 91 | }, 92 | demo: [] 93 | }, 94 | { 95 | base: { 96 | name: "delSessionStorage", 97 | title: "删除sessionStorage", 98 | desc: "删除sessionStorage", 99 | parmInfo: "(key)", 100 | testmode: "button" 101 | }, 102 | demo: [] 103 | }, 104 | { 105 | base: { 106 | name: "setCookie", 107 | title: "创建cookie", 108 | desc: "创建cookie 默认,24小时", 109 | parmInfo: "(key, value, expireHours = 24)", 110 | testmode: "button" 111 | }, 112 | demo: [] 113 | }, 114 | { 115 | base: { 116 | name: "getCookie", 117 | title: "读取cookie", 118 | desc: "读取cookie", 119 | parmInfo: "(key)", 120 | testmode: "button" 121 | }, 122 | demo: [] 123 | }, 124 | { 125 | base: { 126 | name: "delCookie", 127 | title: "删除cookie", 128 | desc: "删除cookie", 129 | parmInfo: "(key)", 130 | testmode: "button" 131 | }, 132 | demo: [] 133 | }, 134 | { 135 | base: { 136 | name: "pageViewHeight", 137 | title: "页面可视高度", 138 | desc: "获取页面可视高度", 139 | parmInfo: "()", 140 | showDemo: false 141 | }, 142 | demo: [""] 143 | }, 144 | { 145 | base: { 146 | name: "pageViewWidth", 147 | title: "页面可视宽度", 148 | desc: "获取页面可视宽度", 149 | parmInfo: "()", 150 | showDemo: false 151 | }, 152 | demo: [""] 153 | }, 154 | { 155 | base: { 156 | name: "scrollPostion", 157 | title: "获取滚动轴位置", 158 | desc: "获取滚动轴位置", 159 | parmInfo: "(el = window)", 160 | showDemo: false 161 | }, 162 | demo: [""] 163 | }, 164 | { 165 | base: { 166 | name: "copyText", 167 | title: "复制文本", 168 | desc: "复制文本", 169 | parmInfo: "(str)", 170 | testmode: "button" 171 | }, 172 | demo: [] 173 | }, 174 | { 175 | base: { 176 | name: "fullScreen", 177 | title: "进入全屏", 178 | desc: "进入全屏", 179 | parmInfo: "(selector = document.body)", 180 | isInput: false, 181 | testmode: "button" 182 | }, 183 | demo: [] 184 | }, 185 | { 186 | base: { 187 | name: "exitFullscreen", 188 | title: "退出全屏", 189 | desc: "退出全屏", 190 | parmInfo: "()", 191 | isInput: false, 192 | testmode: "button" 193 | }, 194 | demo: [] 195 | }, 196 | { 197 | base: { 198 | name: "isFullScreen", 199 | title: "是否全屏状态", 200 | desc: "是否全屏状态", 201 | parmInfo: "()", 202 | isInput: false, 203 | testmode: "button" 204 | }, 205 | demo: [] 206 | } 207 | ] 208 | 209 | const docData = { 210 | title: { 211 | label: "系统", 212 | value: "window", 213 | expand: true, 214 | testValue: "", 215 | testResult: "", 216 | children: [] 217 | }, 218 | children: children 219 | } 220 | 221 | export default docData; -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | /* * { 2 | padding: 0; 3 | margin: 0; 4 | border: 0; 5 | outline: 0; 6 | box-sizing: border-box; 7 | } */ 8 | body { 9 | margin: 0; 10 | padding: 0; 11 | height: 100vh; 12 | width: 100vw; 13 | display: flex; 14 | overflow: hidden; 15 | } 16 | 17 | ::-webkit-scrollbar { 18 | width: 6px; 19 | height: 6px; 20 | } 21 | 22 | ::-webkit-scrollbar-track { 23 | /* -webkit-box-shadow: inset 0 0 6px #ccc; */ 24 | background-color: #fff; 25 | border-radius: 3px; 26 | } 27 | 28 | ::-webkit-scrollbar-thumb { 29 | border-radius: 7px; 30 | /* -webkit-box-shadow: inset 0 0 6px #ccc; */ 31 | background-color: #aaa; 32 | } 33 | 34 | ::-webkit-scrollbar-button { 35 | background-color: #fff; 36 | } 37 | 38 | ::-webkit-scrollbar-corner { 39 | background: #fff; 40 | } 41 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { 2 | createApp 3 | } from 'vue' 4 | import App from './App.vue' 5 | import './index.css' 6 | const app = createApp(App); 7 | //app.config.globalProperties.$R = $R; 8 | app.mount('#app') -------------------------------------------------------------------------------- /test/array.test.js: -------------------------------------------------------------------------------- 1 | import * as fun from '../utils/array' 2 | 3 | 4 | //数组去重 5 | test('arrDistinct', () => { 6 | expect(fun.arrDistinct([2, 3, 3, 3])).toEqual([2, 3]) 7 | expect(fun.arrDistinct(["red", "green", "blue", "red"])).toEqual(["red", "green", "blue"]) 8 | expect(fun.arrDistinct([ 9 | [1, 2], 10 | [1, 2, 3], 11 | [1, 2, 3] 12 | ])).toEqual([ 13 | [1, 2], 14 | [1, 2, 3], 15 | [1, 2, 3] 16 | ]) 17 | }) 18 | 19 | 20 | //数组最大值 21 | test('arrMax', () => { 22 | expect(fun.arrMax([0, 9, 18, 6])).toEqual(18) 23 | }) 24 | 25 | 26 | //数组最小值 27 | test('arrMin', () => { 28 | expect(fun.arrMin([0, 9, 18, 6])).toEqual(0) 29 | }) 30 | 31 | 32 | //出现次数 33 | test('arrCountOcc', () => { 34 | expect(fun.arrCountOcc([0, 9, 18, 6, 3, 6], 6)).toEqual(2) 35 | 36 | expect(fun.arrCountOcc(["red", "red", "green"], "red")).toEqual(2) 37 | }) 38 | 39 | 40 | //数组扁平 41 | test('arrDeepFlatten', () => { 42 | expect(fun.arrDeepFlatten([ 43 | [1, 2, [1, 2, [1, 2, [2, 3]]]] 44 | ])).toEqual([1, 2, 1, 2, 1, 2, 2, 3]) 45 | expect(fun.arrDeepFlatten(["red", "green", ["blue", "red"]])).toEqual(["red", "green", "blue", "red"]) 46 | }) 47 | 48 | 49 | //数组差异对比 50 | test('arrDiff', () => { 51 | expect(fun.arrDiff([0, 9, 18, 6, 3, 6], [33, 44, 5])).toEqual([0, 9, 18, 6, 3, 6]) 52 | 53 | expect(fun.arrDiff(["red", "red", "green"], ["red"])).toEqual(["green"]) 54 | }) 55 | 56 | 57 | //去除出现一次值 58 | test('arrNonUnique', () => { 59 | expect(fun.arrNonUnique([0, 9, 18, 6, 9, 6])).toEqual([9, 6, 9, 6]) 60 | expect(fun.arrNonUnique(["red", "red", "green"])).toEqual(["red", "red"]) 61 | }) 62 | 63 | 64 | //构建默认数组 65 | test('arrWithValues', () => { 66 | expect(fun.arrWithValues({ 67 | "parmMore": [6, 8] 68 | })).toEqual([0]) 69 | expect(fun.arrWithValues({ 70 | "parmMore": [6, "red"] 71 | })).toEqual([0]) 72 | }) 73 | 74 | 75 | //数组交集 76 | test('arrIntersection', () => { 77 | expect(fun.arrIntersection( 78 | [5, 6, 7, 8], 79 | [6, 7, 8, 9, 10] 80 | )).toEqual([6, 7, 8]) 81 | 82 | expect(fun.arrIntersection( 83 | ["red"], 84 | ["green"] 85 | )).toEqual([]) 86 | }) 87 | 88 | 89 | //获取第n个元素 90 | test('arrNthElement', () => { 91 | expect(fun.arrNthElement([5, 6, 7, 8], 2)).toEqual(7) 92 | 93 | expect(fun.arrNthElement(["red"], 1)).toEqual([]) 94 | }) 95 | 96 | 97 | //求平均数 98 | test('arrAverage', () => { 99 | expect(fun.arrAverage(1, 2, 3)).toEqual(2) 100 | expect(fun.arrAverage(1, 2, 7)).toEqual(3.3333333333333335) 101 | }) 102 | 103 | 104 | //数组对象某属性平均数 105 | test('arrAverageBy', () => { 106 | expect(fun.arrAverageBy([{ 107 | "n": 1 108 | }, { 109 | "n": 3 110 | }, { 111 | "n": 5 112 | }], o => o.n)).toEqual(3) 113 | 114 | expect(fun.arrAverageBy( 115 | [{ 116 | "n": 1 117 | }, { 118 | "n": 3 119 | }, { 120 | "n": 5 121 | }], "n" 122 | )).toEqual(3) 123 | }) 124 | 125 | 126 | //转换为数组 127 | test('arrAsList', () => { 128 | expect(fun.arrAsList("abc")).toEqual(["abc"]) 129 | expect(fun.arrAsList(8)).toEqual([8]) 130 | expect(fun.arrAsList([8])).toEqual([8]) 131 | }) 132 | 133 | 134 | //获取值所在索引 135 | test('arrIndexOfAll', () => { 136 | expect(fun.arrIndexOfAll( 137 | [3, 2, 3, 3, 5, 3], 3 138 | )).toEqual([0, 2, 3, 5]) 139 | 140 | expect(fun.arrIndexOfAll( 141 | [1, 2, 7], 6 142 | )).toEqual([]) 143 | }) 144 | 145 | 146 | //指定长度升序数组 147 | test('arrMinN', () => { 148 | expect(fun.arrMinN([3, 2, 3, 3, 5, 3], 3)).toEqual([2, 3, 3]) 149 | 150 | expect(fun.arrMinN([1, 2, 7])).toEqual([1]) 151 | }) 152 | 153 | 154 | //指定长度随机数组 155 | test('arrRandomInRange', () => { 156 | let lnum = fun.arrRandomInRange(10, 16, 3); 157 | let isExist = true; 158 | for (let index = 0; index < lnum.length; index++) { 159 | const el = lnum[index]; 160 | if (el < 10 || el > 16) { 161 | isExist = false; 162 | } 163 | } 164 | 165 | expect(lnum.length == 3 && isExist).toEqual(true) 166 | }) 167 | 168 | 169 | //数组随机抽取一个 170 | test('arrRandomSample', () => { 171 | expect([10, 16, 3].includes(fun.arrRandomSample([10, 16, 3]))).toEqual(true) 172 | expect(["red", "green", "blue"].includes(fun.arrRandomSample(["red", "green", "blue"]))).toEqual(true) 173 | }) 174 | 175 | 176 | //数组随机抽取多个 177 | test('arrRandomN', () => { 178 | expect(fun.arrRandomN([3, 2, 3, 3, 5, 3], 3).length == 3).toEqual(true) 179 | expect(fun.arrRandomN([1, 2, 7], 5).length == 3).toEqual(true) 180 | }) 181 | 182 | 183 | //数组随机打乱 184 | test('arrShuffle', () => { 185 | let testArr = fun.arrShuffle([10, 16, 3]); 186 | let isEist = true; 187 | testArr.forEach(element => { 188 | if ([10, 16, 3].includes(element) == false) { 189 | isEist = false; 190 | } 191 | }); 192 | 193 | expect(isEist).toEqual(true) 194 | 195 | }) 196 | 197 | 198 | //数组转换树 199 | test('arrToTree', () => { 200 | expect(fun.arrToTree([{ 201 | "id": 1, 202 | "parent_id": null 203 | }, { 204 | "id": 2, 205 | "parent_id": 1 206 | }, { 207 | "id": 3, 208 | "parent_id": 1 209 | }, { 210 | "id": 4, 211 | "parent_id": 2 212 | }, { 213 | "id": 5, 214 | "parent_id": 4 215 | }])).toEqual([{ 216 | "id": 1, 217 | "parent_id": null, 218 | "children": [{ 219 | "id": 2, 220 | "parent_id": 1, 221 | "children": [{ 222 | "id": 4, 223 | "parent_id": 2, 224 | "children": [{ 225 | "id": 5, 226 | "parent_id": 4, 227 | "children": [] 228 | }] 229 | }] 230 | }, { 231 | "id": 3, 232 | "parent_id": 1, 233 | "children": [] 234 | }] 235 | }]) 236 | }) 237 | 238 | 239 | //交换矩阵的行和列 240 | test('arrTranspose', () => { 241 | expect(fun.arrTranspose([ 242 | [1, 2, 3], 243 | [4, 5, 6], 244 | [7, 8, 9] 245 | ])).toEqual([ 246 | [1, 4, 7], 247 | [2, 5, 8], 248 | [3, 6, 9] 249 | ]) 250 | }) 251 | 252 | test('arrZip', () => { 253 | expect(fun.arrZip(['a', 'b', 'c'], [1, 2, 3])).toEqual([ 254 | ['a', 1], 255 | ['b', 2], 256 | ['c', 3] 257 | ]) 258 | 259 | }) 260 | 261 | test('arrUnZip', () => { 262 | expect(fun.arrUnZip([ 263 | ['a', 1], 264 | ['b', 2], 265 | ['c', 3] 266 | ])).toEqual([ 267 | ['a', 'b', 'c'], 268 | [1, 2, 3] 269 | ]) 270 | 271 | }) 272 | 273 | 274 | test('bubbleSort', () => { 275 | expect(fun.bubbleSort([4, 2, 5, 1, 3])).toEqual([1, 2, 3, 4, 5]); 276 | expect(fun.bubbleSort([4, 2, 5, 1, 3], true)).toEqual([ 277 | [1, 2, 3, 4, 5], 16 278 | ]); 279 | }) 280 | 281 | test('insertSort', () => { 282 | expect(fun.insertSort([4, 2, 5, 1, 3])).toEqual([1, 2, 3, 4, 5]); 283 | expect(fun.insertSort([4, 2, 5, 1, 3], true)).toEqual([ 284 | [1, 2, 3, 4, 5], 6 285 | ]); 286 | }) 287 | 288 | test('quickSort', () => { 289 | expect(fun.quickSort([4, 2, 5, 1, 3])).toEqual([1, 2, 3, 4, 5]); 290 | expect(fun.quickSort([4, 2, 5, 1, 3], true)).toEqual([ 291 | [1, 2, 3, 4, 5], 10 292 | ]); 293 | }) 294 | 295 | test('selectionSort', () => { 296 | expect(fun.selectionSort([4, 2, 5, 1, 3])).toEqual([1, 2, 3, 4, 5]); 297 | expect(fun.selectionSort([4, 2, 5, 1, 3], true)).toEqual([ 298 | [1, 2, 3, 4, 5], 10 299 | ]); 300 | }) 301 | 302 | test('shellSort ', () => { 303 | expect(fun.shellSort([4, 2, 5, 1, 3])).toEqual([1, 2, 3, 4, 5]); 304 | expect(fun.shellSort([4, 2, 5, 1, 3], true)).toEqual([ 305 | [1, 2, 3, 4, 5], 6 306 | ]); 307 | }) -------------------------------------------------------------------------------- /test/base.test.js: -------------------------------------------------------------------------------- 1 | import * as fun from '../utils/base' 2 | 3 | //判断数据类型 4 | test('is', () => { 5 | expect(fun.is(ArrayBuffer, new ArrayBuffer())).toEqual(true) 6 | 7 | expect(fun.is(Array, [1])).toEqual(true) 8 | 9 | expect(fun.is(Map, new Map())).toEqual(true) 10 | 11 | expect(fun.is(Set, new Set())).toEqual(true) 12 | 13 | expect(fun.is(WeakMap, new WeakMap())).toEqual(true) 14 | 15 | expect(fun.is(WeakSet, new WeakSet())).toEqual(true) 16 | 17 | expect(fun.is(String, "")).toEqual(true) 18 | 19 | expect(fun.is(Number, 1)).toEqual(true) 20 | 21 | expect(fun.is(Number, new Number(1))).toEqual(true) 22 | 23 | expect(fun.is(Boolean, true)).toEqual(true) 24 | 25 | expect(fun.is(Boolean, new Boolean(true))).toEqual(true) 26 | 27 | }) 28 | 29 | 30 | //是否null或undefined 31 | test('isNil', () => { 32 | expect(fun.isNil(8)).toEqual(false) 33 | 34 | }) 35 | 36 | 37 | //是否为null 38 | test('isNull', () => { 39 | expect(fun.isNull(8)).toEqual(false) 40 | 41 | }) 42 | 43 | 44 | //是否数值类型 45 | test('isNumber', () => { 46 | expect(fun.isNumber(8)).toEqual(true) 47 | 48 | expect(fun.isNumber(NaN)).toEqual(false) 49 | 50 | }) 51 | 52 | 53 | //是否数组类型 54 | test('isArray', () => { 55 | expect(fun.isArray([8])).toEqual(true) 56 | 57 | expect(fun.isArray({ 58 | "A": "test" 59 | })).toEqual(false) 60 | 61 | }) 62 | 63 | 64 | //是否正整数 65 | test('isPositiveNum', () => { 66 | expect(fun.isPositiveNum(8)).toEqual(true) 67 | 68 | expect(fun.isPositiveNum(1.8)).toEqual(false) 69 | 70 | expect(fun.isPositiveNum(-1)).toEqual(false) 71 | 72 | }) 73 | 74 | 75 | //是否负整数 76 | test('isNegativeNum', () => { 77 | expect(fun.isNegativeNum(8)).toEqual(false) 78 | 79 | expect(fun.isNegativeNum(1.8)).toEqual(false) 80 | 81 | expect(fun.isNegativeNum(-1)).toEqual(true) 82 | 83 | }) 84 | 85 | 86 | //是否整数 87 | test('isInteger', () => { 88 | expect(fun.isInteger(8)).toEqual(true) 89 | 90 | expect(fun.isInteger(1.8)).toEqual(false) 91 | 92 | expect(fun.isInteger(-2)).toEqual(true) 93 | 94 | }) 95 | 96 | 97 | //是否正浮点数 98 | test('isPostiveFloat', () => { 99 | expect(fun.isPostiveFloat(1.1)).toEqual(true) 100 | 101 | }) 102 | 103 | 104 | //是否负浮点数 105 | test('isNegativeFloat', () => { 106 | expect(fun.isNegativeFloat(-1.1)).toEqual(true) 107 | 108 | }) 109 | 110 | 111 | //是否浮点数 112 | test('isFloat', () => { 113 | expect(fun.isFloat(1.1)).toEqual(true) 114 | 115 | }) 116 | 117 | 118 | //是否浏览器环境 119 | test('isBrowser', () => { 120 | expect(fun.isBrowser()).toEqual(false) 121 | 122 | }) 123 | 124 | 125 | //是否质数 126 | test('isPrime', () => { 127 | expect(fun.isPrime(2)).toEqual(true) 128 | 129 | expect(fun.isPrime(10)).toEqual(false) 130 | 131 | }) 132 | 133 | 134 | //是否ASCII字符 135 | test('isAscii', () => { 136 | expect(fun.isAscii("dz-&8855")).toEqual(true) 137 | 138 | expect(fun.isAscii("年轻人不讲武德")).toEqual(false) 139 | 140 | }) 141 | 142 | 143 | //是否包含空格 144 | test('isHasWhitespace', () => { 145 | expect(fun.isHasWhitespace("dz-&8855")).toEqual(false) 146 | 147 | expect(fun.isHasWhitespace("年轻人 不讲武德")).toEqual(true) 148 | 149 | }) 150 | 151 | 152 | //是否16进制颜色 153 | test('isHexColor', () => { 154 | expect(fun.isHexColor("#D1B2C3")).toEqual(true) 155 | 156 | expect(fun.isHexColor("f00")).toEqual(false) 157 | 158 | }) 159 | 160 | 161 | //是否16进制数字 162 | test('isHexadecimal', () => { 163 | expect(fun.isHexadecimal("#D1B2C3")).toEqual(false) 164 | 165 | expect(fun.isHexadecimal("A00")).toEqual(true) 166 | 167 | }) 168 | 169 | 170 | //是否普通对象 171 | test('isPlainObject', () => { 172 | expect(fun.isPlainObject("'haha'")).toEqual(false) 173 | 174 | expect(fun.isPlainObject()).toEqual(false) 175 | 176 | expect(fun.isPlainObject({})).toEqual(true) 177 | 178 | expect(fun.isPlainObject("() => {}")).toEqual(false) 179 | 180 | expect(fun.isPlainObject({ 181 | "a": "1", 182 | "b": "2" 183 | })).toEqual(true) 184 | 185 | }) 186 | 187 | 188 | //是否正则 189 | test('isRegExp', () => { 190 | expect(fun.isRegExp({})).toEqual(false) 191 | 192 | }) 193 | 194 | 195 | //是否字符串 196 | test('isString', () => { 197 | expect(fun.isString("哈哈测试")).toEqual(true) 198 | 199 | expect(fun.isString(123)).toEqual(false) 200 | 201 | }) 202 | 203 | 204 | //是否对象 205 | test('isObject', () => { 206 | expect(fun.isObject([])).toEqual(true) 207 | 208 | expect(fun.isObject({})).toEqual(true) 209 | 210 | }) 211 | 212 | 213 | //是否是函数 214 | test('isFunction', () => { 215 | expect(fun.isFunction(function () {})).toEqual(true) 216 | 217 | expect(fun.isFunction(function* () {})).toEqual(true) 218 | 219 | expect(fun.isFunction(async function () {})).toEqual(true) 220 | 221 | expect(fun.isFunction(() => {})).toEqual(true) 222 | 223 | }) 224 | 225 | 226 | //是否微信号 227 | test('isWeChat', () => { 228 | expect(fun.isWeChat("abc2232332")).toEqual(true) 229 | 230 | }) 231 | 232 | 233 | //是否中文姓名 234 | test('isChineseName', () => { 235 | expect(fun.isChineseName("张三")).toEqual(true) 236 | 237 | expect(fun.isChineseName("zhang三")).toEqual(false) 238 | 239 | }) 240 | 241 | 242 | //是否英文姓名 243 | test('isEnglishName', () => { 244 | expect(fun.isEnglishName("lily")).toEqual(true) 245 | 246 | expect(fun.isEnglishName("shily 杨")).toEqual(false) 247 | 248 | }) 249 | 250 | 251 | //是否子网掩码 252 | test('isSubnetMask', () => { 253 | expect(fun.isSubnetMask("255.255.255.0")).toEqual(true) 254 | 255 | expect(fun.isSubnetMask("257.255.255.0")).toEqual(false) 256 | 257 | }) 258 | 259 | 260 | //是否手机号码 261 | test('isPhone', () => { 262 | expect(fun.isPhone("18310221234")).toEqual(true) 263 | 264 | }) 265 | 266 | 267 | //是否邮箱 268 | test('isEmail', () => { 269 | expect(fun.isEmail("123@test.com")).toEqual(true) 270 | 271 | }) 272 | 273 | 274 | //是否html标签 275 | test('isHTMLTag', () => { 276 | expect(fun.isHTMLTag("div")).toEqual(true) 277 | 278 | }) 279 | 280 | 281 | //是否svg标签 282 | test('isSvgTag', () => { 283 | expect(fun.isSvgTag("svg")).toEqual(true) 284 | 285 | }) 286 | 287 | 288 | //是否日期 289 | test('isDate', () => { 290 | expect(fun.isDate(new Date())).toEqual(true) 291 | 292 | }) 293 | 294 | 295 | //是否emoji字符 296 | test('isEmoji', () => { 297 | expect(fun.isEmoji("☺")).toEqual(true) 298 | 299 | }) 300 | 301 | test('isLicensePlate', () => { 302 | expect(fun.isLicensePlate("京A2345D")).toEqual(true) 303 | }) 304 | 305 | test('isPwdStrength', () => { 306 | expect(fun.isPwdStrength("U#3p09sd")).toEqual(true) 307 | 308 | expect(fun.isPwdStrength("qwer122323")).toEqual(false) 309 | }) 310 | 311 | test('isIPV4', () => { 312 | expect(fun.isIPV4("127.0.0.1")).toEqual(true) 313 | 314 | expect(fun.isIPV4("234.345.123.3")).toEqual(false) 315 | }) 316 | 317 | test('isIPV6', () => { 318 | expect(fun.isIPV6("fe80::1457:990b:fd57:d88c%23")).toEqual(true) 319 | expect(fun.isIPV6("127.0.0.1")).toEqual(false) 320 | }) 321 | 322 | 323 | test('isMacAddress', () => { 324 | expect(fun.isMacAddress("28-D2-44-A6-05-55")).toEqual(true) 325 | 326 | expect(fun.isMacAddress("24:f9:a3:7b:d5:51")).toEqual(true) 327 | }) 328 | 329 | test('isUrl', () => { 330 | expect(fun.isUrl("https://www.baidu.com")).toEqual(true) 331 | expect(fun.isUrl("http://www.baidu.com")).toEqual(true) 332 | expect(fun.isUrl("http://2232")).toEqual(false) 333 | }) 334 | 335 | test('isVersion', () => { 336 | expect(fun.isVersion("1.0.2")).toEqual(true) 337 | expect(fun.isVersion("1.3")).toEqual(false) 338 | 339 | }) 340 | 341 | 342 | test('isLeapYear', () => { 343 | expect(fun.isLeapYear(2020)).toEqual(true) 344 | expect(fun.isLeapYear(2021)).toEqual(false) 345 | 346 | }) -------------------------------------------------------------------------------- /test/cache.test.js: -------------------------------------------------------------------------------- 1 | import * as fun from '../utils/cache' 2 | 3 | test('LRUCache', () => { 4 | 5 | const cache = fun.LRUCache(2); 6 | cache.put(1, 1); 7 | cache.put(2, 2); 8 | 9 | expect(cache.get(1)).toEqual(1) 10 | cache.put(3, 3); // 该操作移除key=2,同时加入key=3缓存 11 | expect(cache.get(2)).toEqual(-1) //获取2找不到为-1 12 | }) 13 | 14 | test('LFUCache', () => { 15 | 16 | const cache = fun.LFUCache(2); 17 | cache.put(1, 1); 18 | cache.put(2, 2); 19 | expect(cache.get(1)).toEqual(1) // 返回 1 20 | cache.put(3, 3); // 去除键 2,并将key为3加入缓存 21 | expect(cache.get(2)).toEqual(-1) // 返回 -1(未找到) 22 | expect(cache.get(3)).toEqual(3) // 返回 3 23 | cache.put(4, 4); // 去除键 1 24 | expect(cache.get(1)).toEqual(-1); // 返回 -1(未找到) 25 | expect(cache.get(3)).toEqual(3); // 返回 3 26 | expect(cache.get(4)).toEqual(4); // 返回 4 27 | 28 | }) -------------------------------------------------------------------------------- /test/datetime.test.js: -------------------------------------------------------------------------------- 1 | import * as fun from '../utils/datetime' 2 | 3 | //格式化日期 4 | test('dateFormat', () => { 5 | //expect(fun.dateFormat("yyyy-MM-dd HH:mm:fff")).toEqual("2020-12-09 21:21:809") 6 | expect(fun.dateFormat("yyyy-MM-dd HH:mm:fff", new Date(2020, 10, 1, 10, 10, 10))).toEqual("2020-11-01 10:10:000") 7 | 8 | }) 9 | 10 | 11 | //日期之差天数 12 | test('daysDiff', () => { 13 | expect(fun.daysDiff(new Date("2020-09-18"), new Date("2021-01-18"))).toEqual(122) 14 | 15 | }) 16 | 17 | 18 | //日期之差月数 19 | test('monthDiff', () => { 20 | expect(fun.monthDiff(new Date("2020-09-18"), new Date("2021-01-18"))).toEqual(4) 21 | 22 | }) 23 | 24 | 25 | //日期一年中哪一天 26 | test('dayOfYear', () => { 27 | expect(fun.dayOfYear(new Date("2020-09-16"))).toEqual(260) 28 | 29 | }) 30 | 31 | 32 | //某年某月的天数 33 | test('daysInMonth', () => { 34 | expect(fun.daysInMonth(2020, 9)).toEqual(30) 35 | 36 | }) 37 | 38 | 39 | //英语-指定日期的周几 40 | test('weekDayEnglish', () => { 41 | expect(fun.weekDayEnglish(new Date(2020, 11, 9))).toEqual("Wednesday") 42 | 43 | }) 44 | 45 | 46 | //汉语-指定日期的周几 47 | test('weekDayChinese', () => { 48 | expect(fun.weekDayChinese(new Date(2020, 11, 9))).toEqual("周三") 49 | 50 | }) 51 | 52 | 53 | //英语-指定日期的几月 54 | test('monthNameEnglish', () => { 55 | expect(fun.monthNameEnglish(new Date(2020, 11, 9))).toEqual("December") 56 | expect(fun.monthNameEnglish(new Date(2020, 11, 9), true)).toEqual("Dec") 57 | 58 | }) 59 | 60 | 61 | //汉语-指定日期的几月 62 | test('monthNameChinese', () => { 63 | expect(fun.monthNameChinese(new Date(2020, 11, 9))).toEqual("12月") 64 | 65 | expect(fun.monthNameChinese(new Date(2020, 11, 9), false)).toEqual("十二月") 66 | 67 | }) 68 | 69 | test('timeDistance', () => { 70 | expect(fun.timeDistance(new Date())).toEqual("此刻") 71 | 72 | let startDate = new Date(); 73 | startDate.setHours(startDate.getHours() - 24); 74 | expect(fun.timeDistance(startDate)).toEqual("1天前") 75 | 76 | }) -------------------------------------------------------------------------------- /test/encryption.test.js: -------------------------------------------------------------------------------- 1 | import * as fun from '../utils/encryption' 2 | 3 | test('base64Encode', () => { 4 | expect(fun.base64Encode("Hello Moon")).toEqual("SGVsbG8gTW9vbg==") 5 | }) 6 | test('base64Decode', () => { 7 | expect(fun.base64Decode("SGVsbG8gTW9vbg==")).toEqual("Hello Moon") 8 | }) 9 | test('md5', () => { 10 | expect(fun.md5("qwer123")).toEqual("553e83ca69693b33ef73958c04b7a315") 11 | }) -------------------------------------------------------------------------------- /test/function.test.js: -------------------------------------------------------------------------------- 1 | import * as fun from '../utils/function' 2 | 3 | test('objType', () => { 4 | expect(fun.objType([])).toEqual("Array") 5 | expect(fun.objType({})).toEqual("Object") 6 | expect(fun.objType(2)).toEqual("Number") 7 | expect(fun.objType("abc")).toEqual("String") 8 | }) 9 | 10 | test('byteSize', () => { 11 | try { 12 | //jest 暂时不支持Blob类型 13 | expect(fun.byteSize("微笑")).toEqual(6) 14 | expect(fun.byteSize("wu de")).toEqual(5) 15 | } catch (error) {} 16 | }) 17 | test('splitLines', () => { 18 | expect(fun.splitLines("wu\nde\nha\nqiu")).toEqual(["wu", "de", "ha", "qiu"]) 19 | }) 20 | test('removeHTMLTags', () => { 21 | expect(fun.removeHTMLTags("
123
")).toEqual("123") 22 | }) 23 | 24 | 25 | test('escapeHTML', () => { 26 | expect(fun.escapeHTML("
123
")) 27 | .toEqual("<div v-for='(group, index) in listDoc' :key='index'>123</div>") 28 | }) 29 | 30 | test('unescapeHTML', () => { 31 | expect(fun.unescapeHTML("<div v-for='(group, index) in listDoc' :key='index'>123</div>")) 32 | .toEqual("
123
") 33 | }) 34 | 35 | 36 | test('formatNumber', () => { 37 | expect(fun.formatNumber(123456789)).toEqual("123,456,789") 38 | expect(fun.formatNumber(123456789, 4, "-")).toEqual("1-2345-6789") 39 | }) 40 | test('formatString', () => { 41 | expect(fun.formatString("absdfsfsdfasdfassad")).toEqual("a,bsd,fsf,sdf,asd,fas,sad") 42 | expect(fun.formatString("absdfsfsdfasdfassad", 4, "-")).toEqual("abs-dfsf-sdfa-sdfa-ssad") 43 | }) 44 | test('throttle', () => { 45 | function test(val) { 46 | //console.log("test throttle" + val); 47 | return val; 48 | } 49 | 50 | var myThrottle = fun.throttle(test, 1000); 51 | 52 | let parm = "测试传参"; 53 | myThrottle(parm); //执行 54 | expect(myThrottle(parm)).toEqual(undefined); 55 | 56 | }) 57 | test('debounce', () => { 58 | 59 | function test(val) { 60 | //console.log("test throttle" + val); 61 | return val; 62 | } 63 | const myDebounce = fun.debounce(test, 1000); 64 | let parm = "测试传参"; 65 | expect(myDebounce(parm)).toEqual(undefined) 66 | }) 67 | test('urlParam', () => { 68 | expect(fun.urlParam("a", null, "http://test.com?a=123&b=234")).toEqual("123") 69 | expect(fun.urlParam("c", "测试空", "http://test.com?a=123&b=234")).toEqual("测试空") 70 | }) 71 | test('randomNum', () => { 72 | let num = fun.randomNum(1, 5); 73 | expect(num >= 1 && num <= 5).toEqual(true) 74 | }) 75 | test('randomCode', () => { 76 | expect(fun.randomCode(4).length).toEqual(4) 77 | expect(fun.randomCode(6).length).toEqual(6) 78 | }) 79 | test('randomIP', () => { 80 | let ip = fun.randomIP(); 81 | expect(/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ip)).toEqual(true) 82 | }) 83 | test('capitalize', () => { 84 | expect(fun.capitalize("hello")).toEqual("Hello") 85 | expect(fun.capitalize("hello moon")).toEqual("Hello moon") 86 | }) 87 | test('capitalizeEveryWord', () => { 88 | expect(fun.capitalizeEveryWord("hello 哈哈 moon")).toEqual("Hello 哈哈 Moon") 89 | expect(fun.capitalizeEveryWord("hello moon")).toEqual("Hello Moon") 90 | }) 91 | test('decapitalize', () => { 92 | expect(fun.decapitalize("Hello")).toEqual("hello") 93 | expect(fun.decapitalize("Hello Moon")).toEqual("hello Moon") 94 | }) 95 | test('decapitalizeEveryWord', () => { 96 | expect(fun.decapitalizeEveryWord("Hello 哈哈 Moon")).toEqual("hello 哈哈 moon") 97 | expect(fun.decapitalizeEveryWord("Hello Moon")).toEqual("hello moon") 98 | }) 99 | test('camelCase', () => { 100 | expect(fun.camelCase("my-name-desc")).toEqual("myNameDesc") 101 | expect(fun.camelCase("Hello-Ni_Hao")).toEqual("helloNiHao") 102 | }) 103 | test('hyphenate', () => { 104 | expect(fun.hyphenate("myNameZhangSan")).toEqual("my-name-zhang-san") 105 | }) 106 | test('privacyPhone', () => { 107 | expect(fun.privacyPhone("18312349876")).toEqual("183****9876") 108 | expect(fun.privacyPhone("18312349876", "^^^^")).toEqual("183^^^^9876") 109 | }) 110 | test('privacyName', () => { 111 | expect(fun.privacyName("张三丰")).toEqual("张**") 112 | expect(fun.privacyName("张三丰", "^^")).toEqual("张^^") 113 | }) 114 | test('bytesToSize', () => { 115 | expect(fun.bytesToSize(0)).toEqual("0 B") 116 | expect(fun.bytesToSize(15)).toEqual("15.0 B") 117 | expect(fun.bytesToSize(2323)).toEqual("2.27 KB") 118 | expect(fun.bytesToSize(5678999)).toEqual("5.42 MB") 119 | }) 120 | 121 | test('getUUID', () => { 122 | expect(fun.getUUID("-").includes("-")).toEqual(true) 123 | expect(fun.getUUID("(*)").includes("(*)")).toEqual(true) 124 | }) 125 | test('deepClone', () => { 126 | let obj = { 127 | "name": "张三", 128 | "age": 12, 129 | "hobby": ["打篮球", "吹牛"] 130 | }; 131 | expect(fun.deepClone(obj) == obj).toEqual(false) 132 | }) 133 | test('splitPath', () => { 134 | 135 | expect(fun.splitPath("D:\\Downloads\\src\\chibang.wav")).toEqual({ 136 | "dir": "D:\\Downloads\\src\\", 137 | "name": "chibang.wav", 138 | "ext": ".wav" 139 | }) 140 | 141 | expect(fun.splitPath("/Downloads/src/chibang.wav")).toEqual({ 142 | "dir": "/Downloads/src/", 143 | "name": "chibang.wav", 144 | "ext": ".wav" 145 | }) 146 | }) 147 | test('digitUpperCase', () => { 148 | expect(fun.digitUpperCase(12345.67)).toEqual("壹万贰仟叁佰肆拾伍元陆角柒分") 149 | expect(fun.digitUpperCase(-100)).toEqual("欠壹佰元整") 150 | }) 151 | 152 | test('subText', () => { 153 | expect(fun.subText("上山打老虎", 2)).toEqual("上山...") 154 | expect(fun.subText("上山打老虎", 2, "***")).toEqual("上山***") 155 | 156 | expect(fun.subText("", 2, "***")).toEqual("") 157 | }) 158 | 159 | test('randomHexColor', () => { 160 | expect(fun.randomHexColor().length).toEqual(7) 161 | }) 162 | 163 | 164 | test('randomBoolean', () => { 165 | let lboolen = fun.randomBoolean(); 166 | expect(lboolen == true || lboolen == false).toEqual(true) 167 | }) 168 | 169 | 170 | test('randomFloat', () => { 171 | let float = fun.randomFloat(0, 1); 172 | expect(float >= 0 && float <= 1).toEqual(true) 173 | }) 174 | 175 | test('formatPath', () => { 176 | 177 | expect(fun.formatPath("\\ni\\wo\\ta\\")).toEqual("/ni/wo/ta/") 178 | expect(fun.formatPath(".//ni//wo///////ta/")).toEqual("./ni/wo/ta/") 179 | }) 180 | 181 | test('characterCount', () => { 182 | 183 | expect(fun.characterCount("abcdabcabca", "a")).toEqual(4) 184 | expect(fun.characterCount("年轻人不讲武德,武德如风", "武德")).toEqual(2) 185 | }) 186 | 187 | test('deepFreeze', () => { 188 | let obj = { 189 | a: 1, 190 | b: { 191 | c: 2 192 | } 193 | } 194 | fun.deepFreeze(obj); 195 | try { 196 | obj.a = 10; 197 | obj.b.c = 20; 198 | } catch (error) {} 199 | 200 | expect(obj.a).toEqual(1) 201 | expect(obj.b.c).toEqual(2) 202 | 203 | }) 204 | 205 | 206 | test('starScore', () => { 207 | expect(fun.starScore(3)).toEqual("★★★☆☆") 208 | expect(fun.starScore(10)).toEqual("★★★★★") 209 | }) 210 | 211 | 212 | test('once', () => { 213 | let count = 1; 214 | const testFn = () => { 215 | count++; 216 | return count; 217 | } 218 | const myOnce = fun.once(testFn); 219 | expect(myOnce()).toEqual(2) 220 | expect(myOnce()).toEqual(2) 221 | expect(myOnce()).toEqual(2) 222 | 223 | }) 224 | 225 | test('csvToArray', () => { 226 | expect(fun.csvToArray('a,b\nc,d')).toEqual([ 227 | ['a', 'b'], 228 | ['c', 'd'] 229 | ]) 230 | 231 | expect(fun.csvToArray('a;b\nc;d', ';')).toEqual([ 232 | ['a', 'b'], 233 | ['c', 'd'] 234 | ]) 235 | expect(fun.csvToArray('col1,col2\na,b\nc,d', ',', true)).toEqual([ 236 | ['a', 'b'], 237 | ['c', 'd'] 238 | ]) 239 | 240 | }) 241 | 242 | test('csvToJSON ', () => { 243 | expect(fun.csvToJSON('col1,col2\na,b\nc,d')).toEqual([{ 244 | 'col1': 'a', 245 | 'col2': 'b' 246 | }, { 247 | 'col1': 'c', 248 | 'col2': 'd' 249 | }]) 250 | expect(fun.csvToJSON('col1;col2\na;b\nc;d', ';')).toEqual([{ 251 | 'col1': 'a', 252 | 'col2': 'b' 253 | }, { 254 | 'col1': 'c', 255 | 'col2': 'd' 256 | }]) 257 | }) 258 | 259 | test('timeTaken', () => { 260 | expect(fun.timeTaken(() => Math.pow(2, 10))).toEqual(1024); 261 | 262 | }) -------------------------------------------------------------------------------- /test/struct.test.js: -------------------------------------------------------------------------------- 1 | import * as fun from '../utils/struct' 2 | 3 | test('eventEmitter', () => { 4 | 5 | let ret = []; //临时存储数组 6 | function user1(content) { 7 | ret.push("user1订阅:" + content) 8 | } 9 | 10 | function user2(content) { 11 | ret.push("user2订阅:" + content) 12 | } 13 | 14 | function user3(content) { 15 | ret.push("user3订阅:" + content) 16 | } 17 | 18 | function user4(content) { 19 | ret.push("user4订阅:" + content) 20 | } 21 | 22 | //1.常规写法,下面还有链式写法 23 | let em = fun.eventEmitter(); 24 | // 订阅 25 | em.on('event1', user1); 26 | em.on('event1', user2); 27 | em.on('event1', user3); 28 | 29 | // 取消user2方法的订阅 30 | em.off('event1', user2); 31 | //执行一次 32 | em.once('event2', user4) 33 | 34 | // 发布 35 | em.emit('event1', '发布-订阅模式'); 36 | em.emit('event1', '发布-订阅模式'); 37 | em.emit('event2', '执行一次'); 38 | em.emit('event2', '执行一次'); 39 | 40 | expect(ret).toEqual([ 41 | "user1订阅:发布-订阅模式", 42 | "user3订阅:发布-订阅模式", 43 | "user1订阅:发布-订阅模式", 44 | "user3订阅:发布-订阅模式", 45 | "user4订阅:执行一次" 46 | ]) 47 | 48 | 49 | //2.链式写法 50 | ret = []; //清空数组 51 | em = fun.eventEmitter(); 52 | 53 | em.on('event1', user1) // 订阅 54 | .on('event1', user2) // 订阅 55 | .on('event1', user3) // 订阅 56 | .off('event1', user2) // 取消user2方法的订阅 57 | .once('event2', user4) //执行一次 58 | .emit('event1', '发布-订阅模式') // 发布 59 | .emit('event1', '发布-订阅模式') // 发布 60 | .emit('event2', '执行一次') // 发布 61 | .emit('event2', '执行一次'); // 发布 62 | 63 | expect(ret).toEqual([ 64 | "user1订阅:发布-订阅模式", 65 | "user3订阅:发布-订阅模式", 66 | "user1订阅:发布-订阅模式", 67 | "user3订阅:发布-订阅模式", 68 | "user4订阅:执行一次" 69 | ]) 70 | 71 | }) 72 | 73 | 74 | test('eventEmitterMap', () => { 75 | 76 | let ret = []; //临时存储数组 77 | function user1(content) { 78 | ret.push("user1订阅:" + content) 79 | } 80 | 81 | function user2(content) { 82 | ret.push("user2订阅:" + content) 83 | } 84 | 85 | function user3(content) { 86 | ret.push("user3订阅:" + content) 87 | } 88 | 89 | function user4(content) { 90 | ret.push("user4订阅:" + content) 91 | } 92 | 93 | //1.常规写法,下面还有链式写法 94 | let em = fun.eventEmitterMap(); 95 | // 订阅 96 | em.on('event1', user1); 97 | em.on('event1', user2); 98 | em.on('event1', user3); 99 | 100 | // 取消user2方法的订阅 101 | em.off('event1', user2); 102 | //执行一次 103 | em.once('event2', user4) 104 | 105 | // 发布 106 | em.emit('event1', '发布-订阅模式'); 107 | em.emit('event1', '发布-订阅模式'); 108 | em.emit('event2', '执行一次'); 109 | em.emit('event2', '执行一次'); 110 | 111 | expect(ret).toEqual([ 112 | "user1订阅:发布-订阅模式", 113 | "user3订阅:发布-订阅模式", 114 | "user1订阅:发布-订阅模式", 115 | "user3订阅:发布-订阅模式", 116 | "user4订阅:执行一次" 117 | ]) 118 | 119 | 120 | //2.链式写法 121 | ret = []; //清空数组 122 | em = fun.eventEmitterMap(); 123 | 124 | em.on('event1', user1) // 订阅 125 | .on('event1', user2) // 订阅 126 | .on('event1', user3) // 订阅 127 | .off('event1', user2) // 取消user2方法的订阅 128 | .once('event2', user4) //执行一次 129 | .emit('event1', '发布-订阅模式') // 发布 130 | .emit('event1', '发布-订阅模式') // 发布 131 | .emit('event2', '执行一次') // 发布 132 | .emit('event2', '执行一次'); // 发布 133 | 134 | expect(ret).toEqual([ 135 | "user1订阅:发布-订阅模式", 136 | "user3订阅:发布-订阅模式", 137 | "user1订阅:发布-订阅模式", 138 | "user3订阅:发布-订阅模式", 139 | "user4订阅:执行一次" 140 | ]) 141 | 142 | }) 143 | 144 | 145 | 146 | test('sensitiveWords', () => { 147 | const sw = fun.sensitiveWords(); 148 | //追加敏感词 149 | sw.addWords(['唐僧', '孙悟空', '猪八戒']); 150 | sw.addWord('玉帝哥哥'); 151 | 152 | let value = "我爱唐僧哥哥"; 153 | //检测敏感词 154 | expect(sw.containsDfa(value)).toEqual(true); 155 | expect(sw.wordsDfa(value)).toEqual(['唐僧']); 156 | 157 | value = "唐僧念紧箍咒,孙悟空要打唐僧,猪八戒看不懂"; 158 | expect(sw.containsDfa(value)).toEqual(true); 159 | expect(sw.wordsDfa(value)).toEqual(['唐僧', '孙悟空', '唐僧', '猪八戒']); 160 | 161 | value = "我爱嫦娥姐姐" 162 | expect(sw.containsDfa(value)).toEqual(false); 163 | expect(sw.wordsDfa(value)).toEqual([]) 164 | 165 | //检测敏感词替换 166 | value = "我不是玉帝哥哥,就是个干滴滴的弟弟" 167 | expect(sw.replaceDfa(value, '*', true)).toEqual("我不是*,就是个干滴滴的弟弟") 168 | expect(sw.replaceDfa(value, '*', false)).toEqual( 169 | "我不是****,就是个干滴滴的弟弟" 170 | ) 171 | }) 172 | 173 | 174 | test('trie', () => { 175 | let trie = fun.trie(); 176 | //追加敏感词 177 | trie.inserts(['唐僧', '孙悟空', '猪八戒']); 178 | trie.insert('玉帝哥哥'); 179 | //检测词语 180 | expect(trie.search("唐僧")).toEqual(true); 181 | expect(trie.search("唐僧你")).toEqual(false); 182 | expect(trie.startsWith("唐")).toEqual(true); 183 | expect(trie.startsWith("唐长老")).toEqual(false); 184 | 185 | }) -------------------------------------------------------------------------------- /test/window.test.js: -------------------------------------------------------------------------------- 1 | import * as fun from '../utils/window' 2 | //滚动页面顶部 3 | test('goToTop', () => {}) 4 | 5 | 6 | //滚动class元素顶部 7 | test('goToTopClassName', () => {}) 8 | 9 | 10 | //滚动ID顶部 11 | test('goToTopId', () => {}) 12 | 13 | 14 | //获取所选元素 15 | test('getSelectedText', () => {}) 16 | 17 | 18 | //存储到localStorage 19 | test('setLocalStorage', () => {}) 20 | 21 | 22 | //读取localStorage 23 | test('getLocalStorage', () => {}) 24 | 25 | 26 | //创建cookie 27 | test('setCookie', () => {}) 28 | 29 | 30 | //读取cookie 31 | test('getCookie', () => {}) 32 | 33 | 34 | //删除cookie 35 | test('delCookie', () => {}) 36 | 37 | test('copyText', () => {}) -------------------------------------------------------------------------------- /utils/array.js: -------------------------------------------------------------------------------- 1 | /**数组相关函数 */ 2 | 3 | /** 4 | * 去除重复数组 5 | * @param {array} arr 数组 6 | */ 7 | export const arrDistinct = arr => [...new Set(arr)]; 8 | 9 | 10 | /** 11 | * 返回数组最大值 12 | * @param {array} arr 13 | */ 14 | export const arrMax = (arr) => Math.max(...arr); 15 | 16 | /** 17 | * 返回数组最小值 18 | * @param {array} arr 19 | */ 20 | export const arrMin = (arr) => Math.min(...arr); 21 | 22 | /** 23 | * 计算数组中值的出现次数 24 | * @param {*} arr 25 | * @param {*} value 26 | */ 27 | export const arrCountOcc = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0); 28 | 29 | /** 30 | * 数组扁平化,支持深度 31 | * @param {*} arr 32 | */ 33 | export const arrDeepFlatten = (arr, depth = Infinity) => arr.flat(depth); 34 | 35 | /** 36 | * 对比两个数组之间的差异,返回数组 37 | * @param {*} arr1 38 | * @param {*} arr2 39 | */ 40 | export const arrDiff = (arr1, arr2) => { 41 | const s = new Set(arr2); 42 | return arr1.filter(x => !s.has(x)); 43 | }; 44 | 45 | /** 46 | * 去除数组中只出现一次的值,返回具备多次的值 47 | * @param {*} arr 48 | */ 49 | export const arrNonUnique = arr => arr.filter(i => arr.indexOf(i) !== arr.lastIndexOf(i)); 50 | 51 | /** 52 | * 创建默认长度默认值的数组 53 | * @param {*} count 54 | * @param {*} value 55 | */ 56 | export const arrWithValues = (count, value = 0) => Array(count).fill(value); 57 | 58 | /** 59 | * 获取两个数组中公共元素 60 | * @param {*} arr1 61 | * @param {*} arr2 62 | */ 63 | export const arrIntersection = (arr1, arr2) => { 64 | const s = new Set(arr2); 65 | return arr1.filter(x => s.has(x)); 66 | }; 67 | 68 | /** 69 | * 获取数组第n个元素 70 | * @param {*} arr 71 | * @param {*} n 72 | */ 73 | export const arrNthElement = (arr, n = 0) => n >= arr.length ? [] : (n > 0 ? arr.slice(n, n + 1) : arr.slice(n))[0]; 74 | 75 | /** 76 | * 求取平均数 77 | * @param {...any} nums 78 | */ 79 | export const arrAverage = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length; 80 | 81 | /** 82 | * 求取数组对象某变量的平均数 83 | * @param {*} arr 84 | * @param {*} fn 85 | */ 86 | export const arrAverageBy = (arr, fn) => 87 | arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0) / 88 | arr.length; 89 | 90 | /** 91 | * 其他类型转换为数组 92 | * @param {*} val 93 | */ 94 | export const arrAsList = val => (Array.isArray(val) ? val : [val]); 95 | 96 | /** 97 | * 获取数组指定值的所有索引 98 | * @param {*} arr 99 | * @param {*} val 100 | */ 101 | export const arrIndexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []); 102 | 103 | /** 104 | * 返回指定长度的升序数组 105 | * @param {*} arr 106 | * @param {*} n 107 | */ 108 | export const arrMinN = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n); 109 | 110 | /** 111 | * 生成两数之间指定长度的随机数组 112 | * @param {*} min 113 | * @param {*} max 114 | * @param {*} n 115 | */ 116 | export const arrRandomInRange = (min, max, n = 1) => 117 | Array.from({ 118 | length: n 119 | }, () => Math.floor(Math.random() * (max - min + 1)) + min); 120 | 121 | /** 122 | * 在指定数组中获取随机数,返回一个值 123 | * @param {*} arr 124 | */ 125 | export const arrRandomSample = arr => arr[Math.floor(Math.random() * arr.length)]; 126 | 127 | /** 128 | * 在指定数组中获取指定长度的随机数,返回一个数组 129 | * @param {*} arr 130 | */ 131 | export const arrRandomN = ([...arr], n = 1) => { 132 | let m = arr.length; 133 | while (m) { 134 | const i = Math.floor(Math.random() * m--); 135 | [arr[m], arr[i]] = [arr[i], arr[m]]; 136 | } 137 | return arr.slice(0, n); 138 | }; 139 | 140 | /** 141 | * 数组随机打乱产生新的数组 142 | * @param {*} array 143 | */ 144 | export const arrShuffle = ([...arr]) => { 145 | let m = arr.length; 146 | while (m) { 147 | const i = Math.floor(Math.random() * m--); 148 | [arr[m], arr[i]] = [arr[i], arr[m]]; 149 | } 150 | return arr; 151 | }; 152 | 153 | /** 154 | * 将数组转换为树形结构的对象 155 | * @param {*} items 156 | * @param {*} id 157 | * @param {*} link 158 | */ 159 | export const arrToTree = (items, id = null, link = 'parent_id') => 160 | items 161 | .filter(item => item[link] === id) 162 | .map(item => ({ 163 | ...item, 164 | children: arrToTree(items, item.id) 165 | })); 166 | 167 | 168 | /** 169 | * 交换多维数组行和列 170 | * @param {*} matrix 171 | */ 172 | export const arrTranspose = matrix => matrix[0].map((col, i) => matrix.map(row => row[i])); 173 | 174 | /** 175 | * 压缩数组 176 | * @param {...any} arr 177 | */ 178 | export const arrZip = (...arr) => Array.from({ 179 | length: Math.max(...arr.map(a => a.length)) 180 | }, (_, i) => arr.map(a => a[i])); 181 | 182 | 183 | /** 184 | * 解压缩数组 185 | * @param {*} arr 186 | */ 187 | export const arrUnZip = arr => arr.reduce((acc, c) => (c.forEach((v, i) => acc[i].push(v)), acc), Array.from({ 188 | length: Math.max(...arr.map(a => a.length)) 189 | }, (_) => [])); 190 | 191 | /** 192 | * 冒泡排序 193 | * @param {*} arr 194 | * @param {*} isNumTimes 195 | */ 196 | export const bubbleSort = (arr, isNumTimes = false) => { 197 | let temp = '', 198 | sum = 0; 199 | for (let i = 0; i < arr.length; i++) { 200 | let flag = false; 201 | for (let j = 0; j < arr.length - 1; j++) { 202 | if (arr[j] > arr[j + 1]) { 203 | temp = arr[j + 1]; 204 | arr[j + 1] = arr[j]; 205 | arr[j] = temp; 206 | flag = true; 207 | } 208 | sum++; //计算次数 209 | } 210 | if (!flag) { 211 | break; 212 | } 213 | } 214 | return isNumTimes ? [arr, sum] : arr; 215 | }; 216 | 217 | 218 | /** 219 | * 插入排序 220 | * @param {*} arr 221 | * @param {*} isNumTimes 222 | */ 223 | export const insertSort = (arr, isNumTimes = false) => { 224 | let sum = 0; 225 | 226 | for (let i = 1; i < arr.length; i++) { 227 | if (arr[i] < arr[i - 1]) { 228 | let temp = arr[i]; 229 | let j = i - 1; 230 | arr[i] = arr[j]; 231 | 232 | while (j >= 0 && temp < arr[j]) { 233 | arr[j + 1] = arr[j]; 234 | j--; 235 | sum++; //统计次数 236 | }; 237 | arr[j + 1] = temp; 238 | } 239 | } 240 | return isNumTimes ? [arr, sum] : arr; 241 | } 242 | 243 | /** 244 | * 快速排序 245 | * @param {*} arr 246 | * @param {*} isNumTimes 247 | */ 248 | export const quickSort = (arr, isNumTimes = false) => { 249 | quickSortObj.sum = 0; 250 | arr = quickSortObj.quickSort(arr); 251 | return isNumTimes ? [arr, quickSortObj.sum] : arr; 252 | } 253 | 254 | const quickSortObj = { 255 | sum: 0, 256 | quickSort: function (arr) { 257 | if (arr.length <= 1) { 258 | return arr; 259 | } 260 | var medianIndex = Math.floor(arr.length / 2); // 分解值索引 261 | var medianValue = arr.splice(medianIndex, 1); // 分界值 262 | var left = []; 263 | var right = []; 264 | for (let i = 0; i < arr.length; i++) { 265 | if (arr[i] < medianValue) { 266 | left.push(arr[i]) 267 | } else { 268 | right.push(arr[i]) 269 | } 270 | this.sum++; 271 | } 272 | //console.log(medianIndex, medianValue, left, right) 273 | return this.quickSort(left).concat(medianValue, this.quickSort(right)); 274 | } 275 | }; 276 | 277 | /** 278 | * 选择排序 279 | * @param {*} arr 280 | */ 281 | export const selectionSort = (arr, isNumTimes = false) => { 282 | let sum = 0; 283 | if (arr == null || arr.length < 2) { 284 | return isNumTimes ? [arr, sum] : arr; 285 | } 286 | for (var i = 0; i < (arr.length - 1); i++) { 287 | let minIndex = i; 288 | for (let j = i + 1; j < arr.length; j++) { 289 | minIndex = arr[j] < arr[minIndex] ? j : minIndex; 290 | sum++; 291 | } 292 | let temp = arr[i]; 293 | arr[i] = arr[minIndex]; 294 | arr[minIndex] = temp; 295 | } 296 | return isNumTimes ? [arr, sum] : arr; 297 | } 298 | 299 | /** 300 | * 希尔排序 301 | * @param {*} arr 302 | * @param {*} isNumTimes 303 | */ 304 | export const shellSort = (arr, isNumTimes = false) => { 305 | var len = arr.length, 306 | sum = 0; 307 | var fraction = Math.floor(len / 2); // 定义间隔区间 308 | for (fraction; fraction > 0; fraction = Math.floor(fraction / 2)) { 309 | for (var i = fraction; i < len; i++) { 310 | for (var j = i - fraction; j >= 0 && arr[j] > arr[fraction + j]; j -= fraction) { 311 | var temp = arr[j]; 312 | arr[j] = arr[fraction + j]; // 后移 313 | arr[fraction + j] = temp; // 填补 314 | sum++; 315 | } 316 | } 317 | } 318 | return isNumTimes ? [arr, sum] : arr; 319 | } -------------------------------------------------------------------------------- /utils/base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 常规函数 3 | */ 4 | 5 | /** 6 | * 判断数据是否为指定的数据类型,如果是则返回true。 7 | * @param {*} type 源对象类型 8 | * @param {*} val 目标对象 9 | */ 10 | export const is = (type, val) => ![, null].includes(val) && val.constructor === type; 11 | 12 | /** 13 | * 判断当前变量的值是否为 null 或 undefined 14 | * @param {*} val 15 | */ 16 | export const isNil = val => val === undefined || val === null; 17 | 18 | /** 19 | * 是否为null 20 | * @param {*} val 21 | */ 22 | export const isNull = val => val === null; 23 | 24 | /** 25 | * 是否数值类型 26 | * @param {*} val 27 | */ 28 | export const isNumber = val => !isNaN(parseFloat(val)) && isFinite(val); 29 | 30 | /** 31 | * 是否数组类型 32 | * @param {*} obj 33 | */ 34 | export const isArray = obj => Array.isArray(obj); 35 | 36 | 37 | /** 38 | * 是否正整数 39 | * @param {*} val 40 | */ 41 | export const isPositiveNum = val => /^[1-9]\d*$/.test(val); 42 | 43 | /** 44 | * 是否负整数 45 | * @param {*}} val 46 | */ 47 | export const isNegativeNum = val => /^-[1-9]\d*$/.test(val); 48 | 49 | /** 50 | * 匹配整数 51 | * @param {*} val 52 | */ 53 | export const isInteger = val => /^(-|\+)?\d+$/.test(val); 54 | 55 | /** 56 | * 匹配正浮点数 57 | */ 58 | export const isPostiveFloat = val => /^[1-9]\d*.\d*|0.\d*[1-9]\d*$/.test(val) 59 | 60 | 61 | /** 62 | * 匹配负浮点数 63 | */ 64 | export const isNegativeFloat = val => /^-([1-9]\d*.\d*|0.\d*[1-9]\d*)$/.test(val) 65 | 66 | /** 67 | * 匹配浮点数 68 | */ 69 | export const isFloat = val => /^-?([1-9]\d*.\d*|0.\d*[1-9]\d*|0?.0+|0)$/.test(val) 70 | 71 | 72 | /** 73 | * 用于判断程序运行环境是否在浏览器,这有助于避免在node环境运行前端模块时出错。 74 | */ 75 | export const isBrowser = () => ![typeof window, typeof document].includes('undefined'); 76 | 77 | /** 78 | * 是否质数 79 | * @param {*} num 80 | */ 81 | export const isPrime = num => (num > 1) && Array(Math.floor(Math.sqrt(num)) - 1).fill(0).map((_, i) => i + 2).every(i => num % i !== 0); 82 | 83 | /** 84 | * 检查字符串是否仅包含ASCII字符 85 | * @param {*} str 86 | */ 87 | export const isAscii = str => /^[\x00-\x7F]+$/.test(str); 88 | 89 | /** 90 | * 检查字符串是否包含空格 91 | * @param {*} str 92 | */ 93 | export const isHasWhitespace = str => /\s/.test(str); 94 | 95 | /** 96 | * 检查字符串是否为十六进制颜色 97 | * @param {*} color 98 | */ 99 | export const isHexColor = color => /^#([0-9A-F]{3}|[0-9A-F]{4}|[0-9A-F]{6}|[0-9A-F]{8})$/i.test(color); 100 | 101 | /** 102 | * 检查字符串是否为十六进制数字 103 | * @param {*} str 104 | */ 105 | export const isHexadecimal = str => /^[A-F0-9]+$/i.test(str); 106 | 107 | /** 108 | * 检查值是否为普通对象 109 | * @param {*} v 110 | */ 111 | export const isPlainObject = v => (!!v && typeof v === 'object' && (v.__proto__ === null || v.__proto__ === Object.prototype)); 112 | 113 | /** 114 | * 是否正则表达式 115 | * @param {*} value 116 | */ 117 | export const isRegExp = value => Object.prototype.toString.call(value) === '[object RegExp]'; 118 | 119 | /** 120 | * 检查值是否为字符串 121 | * @param {*} value 122 | */ 123 | export const isString = value => Object.prototype.toString.call(value) === '[object String]'; 124 | 125 | /** 126 | * 是否对象 127 | * @param {*} v 128 | */ 129 | export const isObject = v => (v !== null && typeof v === 'object'); 130 | 131 | /** 132 | * 是否是函数 133 | * @param {*} v 134 | */ 135 | export const isFunction = v => ['[object Function]', '[object GeneratorFunction]', '[object AsyncFunction]', '[object Promise]'].includes(Object.prototype.toString.call(v)); 136 | 137 | /** 138 | * 是否是微信 139 | * @param {*} value 140 | */ 141 | export const isWeChat = value => /^[a-zA-Z][-_a-zA-Z0-9]{5,19}$/g.test(value); 142 | 143 | /** 144 | * 验证中文姓名 145 | * @param {string} value 146 | */ 147 | export const isChineseName = value => /^(?:[\u4e00-\u9fa5·]{2,16})$/g.test(value); 148 | 149 | 150 | /** 151 | * 验证英文姓名 152 | *@param { string } value 153 | */ 154 | export const isEnglishName = value => /(^[a-zA-Z]{1}[a-zA-Z\s]{0,20}[a-zA-Z]{1}$)/g.test(value); 155 | 156 | /** 157 | * 验证子网掩码 158 | * @param {*} value 159 | */ 160 | export const isSubnetMask = value => /^(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/g.test(value); 161 | 162 | /** 163 | * 是否手机号码 164 | * @param {*} val 165 | */ 166 | export const isPhone = (val) => /^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(val); 167 | // /^1[3456789]\d{9}$/.test(val); 168 | 169 | /** 170 | * 是否邮箱 171 | * @param {*} val 172 | */ 173 | export const isEmail = (val) => /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(val); 174 | 175 | /** 176 | * 是否html标签 177 | * @param {*} str 178 | */ 179 | let cacheHTMLTag = []; 180 | export const isHTMLTag = (str) => { 181 | if (cacheHTMLTag.length == 0) { 182 | cacheHTMLTag = `html,body,base,head,link,meta,style,title, 183 | address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section, 184 | div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul, 185 | a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby, 186 | s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video, 187 | embed,object,param,source,canvas,script,noscript,del,ins, 188 | caption,col,colgroup,table,thead,tbody,td,th,tr, 189 | button,datalist,fieldset,form,input,label,legend,meter,optgroup,option, 190 | output,progress,select,textarea, 191 | details,dialog,menu,menuitem,summary, 192 | content,element,shadow,template,blockquote,iframe,tfoot`.replace(/\s+/g, '').split(","); 193 | } 194 | return cacheHTMLTag.includes(str); 195 | } 196 | /** 197 | * 是否svg标签 198 | * @param {*} str 199 | */ 200 | let cacheSvgTag = []; 201 | export const isSvgTag = (str) => { 202 | if (cacheSvgTag.length == 0) { 203 | cacheSvgTag = `svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face, 204 | foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern, 205 | polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view`.replace(/\s+/g, '').split(","); 206 | } 207 | return cacheSvgTag.includes(str); 208 | } 209 | 210 | /** 211 | * 是否日期 212 | * @param {*} val 213 | */ 214 | export const isDate = (val) => Object.prototype.toString.call(val) === '[object Date]'; 215 | 216 | /** 217 | * 是否emoji字符 218 | */ 219 | export const isEmoji = (val) => /(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\ufe0f|\u200d|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|\ud83c[\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|\ud83c[\ude32-\ude3a]|\ud83c[\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])/g.test(val); 220 | 221 | /** 222 | * 是否车牌号 223 | * @param {*} val 224 | */ 225 | export const isLicensePlate = val => /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领]{1}[A-HJ-NP-Z]{1}(?:(([0-9]{5}[DF])|([DF][A-HJ-NP-Z0-9][0-9]{4}))|[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1})$/.test(val); 226 | 227 | /** 228 | * 是否强密码,至少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符 229 | * @param {*} val 230 | */ 231 | export const isPwdStrength = val => /^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*? ])\S*$/.test(val); 232 | 233 | 234 | /** 235 | * 是否ipv4 236 | * @param {*} val 237 | */ 238 | export const isIPV4 = val => /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(val); 239 | 240 | /** 241 | 是否ipv6 242 | */ 243 | export const isIPV6 = (val) => /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/.test(val) 244 | 245 | 246 | /** 247 | * 是否mac地址 248 | * @param {*} val 249 | */ 250 | export const isMacAddress = val => /^((([a-f0-9]{2}:){5})|(([a-f0-9]{2}-){5}))[a-f0-9]{2}$/i.test(val); 251 | 252 | /** 253 | * 是否URL地址 254 | * @param {*} val 255 | */ 256 | export const isUrl = val => /^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?$/.test(val); 257 | 258 | /** 259 | * 是否X.Y.Z格式版本号 260 | * @param {*} val 261 | */ 262 | export const isVersion = val => /^\d+(?:\.\d+){2}$/.test(val) 263 | 264 | /** 265 | * 是否闰年 266 | * @param {*} year 267 | */ 268 | export const isLeapYear = (year) => 0 === year % 4 && (year % 100 !== 0 || year % 400 === 0); -------------------------------------------------------------------------------- /utils/cache.js: -------------------------------------------------------------------------------- 1 | /**缓存类 */ 2 | 3 | import classLRUCache from './cache/LRUCache' 4 | import classLFUCache from './cache/LFUCache' 5 | 6 | /** 7 | * LRU缓存 8 | * @param {*} capacity 9 | */ 10 | export const LRUCache = (capacity) => { 11 | return new classLRUCache(capacity); 12 | } 13 | 14 | /** 15 | * LFU缓存 16 | * @param {*}} capacity 17 | */ 18 | export const LFUCache = (capacity) => { 19 | return new classLFUCache(capacity); 20 | } -------------------------------------------------------------------------------- /utils/cache/LFUCache.js: -------------------------------------------------------------------------------- 1 | /** 2 | * LFU缓存算法 3 | */ 4 | export default class LFUCache { 5 | constructor(capacity) { 6 | this.size = capacity 7 | this.values = new Map() 8 | this.times = new Map() 9 | } 10 | 11 | get(key) { 12 | if (this.values.has(key)) { 13 | let val = this.values.get(key) || 0 14 | let time = this.times.get(key) || 0 15 | this.values.delete(key) 16 | this.times.delete(key) 17 | this.values.set(key, val) 18 | this.times.set(key, time + 1) 19 | return val 20 | } 21 | return -1 22 | } 23 | 24 | put(key, value) { 25 | if (this.size <= 0) return; 26 | let time = 1 27 | let min = Math.min(...this.times.values()) 28 | if (this.values.has(key)) { 29 | time = (this.times.get(key) || 0) + 1 30 | this.values.delete(key) 31 | this.times.delete(key) 32 | } 33 | this.values.set(key, value) 34 | this.times.set(key, time) 35 | 36 | if (this.size < this.values.size) { 37 | let it = this.values.keys() 38 | let delKey = it.next().value 39 | while (delKey && this.times.get(delKey) !== min) { 40 | delKey = it.next().value 41 | } 42 | this.values.delete(delKey) 43 | this.times.delete(delKey) 44 | } 45 | } 46 | clear() { 47 | this.values.clear(); 48 | this.times.clear(); 49 | } 50 | } -------------------------------------------------------------------------------- /utils/cache/LRUCache.js: -------------------------------------------------------------------------------- 1 | /** 2 | * LRU缓存算法 3 | */ 4 | export default class LRUCache { 5 | constructor(capacity) { 6 | this.cache = new Map() 7 | this.capacity = capacity 8 | } 9 | get(k) { 10 | if (!this.cache.has(k)) return -1 11 | 12 | const v = this.cache.get(k) 13 | 14 | this.cache.delete(k) 15 | this.cache.set(k, v) 16 | 17 | return v 18 | } 19 | put(k, v) { 20 | if (this.size <= 0) return; 21 | if (this.cache.has(k)) this.cache.delete(k) 22 | 23 | this.cache.set(k, v) 24 | 25 | if (this.cache.size > this.capacity) { 26 | this.cache.delete(this.cache.keys().next().value) 27 | } 28 | } 29 | 30 | clear() { 31 | this.cache.clear(); 32 | } 33 | } -------------------------------------------------------------------------------- /utils/datetime.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 日期函数 3 | */ 4 | 5 | /** 6 | * 获取格式化日期 7 | * @param {*} valueFormat 8 | * @param {Date} dateTime 9 | * @param {*} timeDiff 默认时区差值,便于拓展 10 | */ 11 | export const dateFormat = (valueFormat, dateTime, timeDiff = 8) => { 12 | let dt = dateTime ? new Date(dateTime.getTime()) : new Date(); //重置日期对象,不改变传入日期对象 13 | 14 | dt.setHours(dt.getHours() + timeDiff); //增加八个小时,否则会差8个小时时区 15 | let [y4, M2, d2, H2, m2, s2, f3] = dt.toISOString().split(/[^0-9]/).slice(0, -1); 16 | let dateObj = { 17 | "yyyy": y4, 18 | "yyy": y4.substring(1, 4), 19 | "yy": y4.substring(2, 4), 20 | "MM": M2, 21 | "M": parseInt(M2), 22 | "dd": d2, 23 | "d": parseInt(d2), 24 | "HH": H2, 25 | "H": parseInt(H2), 26 | "mm": m2, 27 | "m": parseInt(m2), 28 | "ss": s2, 29 | "s": parseInt(s2), 30 | "fff": f3, 31 | "ff": parseInt(f3), 32 | "f": parseInt(f3) 33 | } 34 | return valueFormat.replace(/y{2,4}|M{1,2}|d{1,2}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|f{1,3}/g, (key) => { 35 | return dateObj[key]; 36 | }) 37 | 38 | } 39 | 40 | /** 41 | * 计算两个日期之间的差异天数 42 | * @param {*} date 43 | * @param {*} otherDate 44 | */ 45 | export const daysDiff = (date, otherDate) => Math.ceil(Math.abs(date - otherDate) / (1000 * 60 * 60 * 24)); 46 | 47 | /** 48 | * 计算两个日期之间的月数 49 | * @param {*} startDate 50 | * @param {*} endDate 51 | */ 52 | export const monthDiff = (startDate, endDate) => Math.max(0, (endDate.getFullYear() - startDate.getFullYear()) * 12 - startDate.getMonth() + endDate.getMonth()); 53 | 54 | /** 55 | * 日期获取一年中的哪一天 56 | * @param {*} date 2 57 | */ 58 | export const dayOfYear = date => Math.floor((date - new Date(date.getFullYear(), 0, 0)) / (1000 * 60 * 60 * 24)); 59 | 60 | /** 61 | * 获取某年某月的天数 62 | * @param {*} year 63 | * @param {*} month 64 | */ 65 | export const daysInMonth = (year, month) => new Date(year, month, 0).getDate(); 66 | 67 | /** 68 | * 获取指定日期的周几-英文 69 | * @param {*} date 70 | */ 71 | export const weekDayEnglish = date => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][date.getDay()]; 72 | 73 | /** 74 | * 获取指定日期的周几-汉语 75 | * @param {*} date 76 | */ 77 | export const weekDayChinese = date => ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][date.getDay()]; 78 | 79 | 80 | /** 81 | * 获取指定日期是几月-英文 82 | * @param {*} date 83 | * @param {*} isAbbreviation 是否简写方式,默认不简写 84 | */ 85 | export const monthNameEnglish = (date, isAbbreviation = false) => { 86 | return isAbbreviation ? ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][date.getMonth()] : ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', ' November', 'December'][date.getMonth()]; 87 | } 88 | /** 89 | * 获取指定日期是几月-汉语 90 | * @param {*} date 91 | * @param {*} isNum 92 | */ 93 | export const monthNameChinese = (date, isNum = true) => { 94 | return isNum ? ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'][date.getMonth()] : ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'][date.getMonth()]; 95 | } 96 | 97 | 98 | /** 99 | * 时间距离 100 | * 传入时间与当前时间的距离描述,比如10天前,此刻,10秒前 101 | * @param startDate 时间类型 102 | */ 103 | export const timeDistance = (startDate) => { 104 | let timeSpan = Math.round((Date.now() - startDate.getTime()) / 1000); 105 | if (timeSpan == 0) { 106 | return "此刻"; 107 | } 108 | 109 | //处理传递时间大于当前时间情况 110 | let timeBA = timeSpan > 0 ? "前" : "后"; 111 | timeSpan = Math.abs(timeSpan); 112 | 113 | let timeDesc = ['年', '个月', '星期', '天', '小时', '分钟', '秒']; 114 | let timeValue = [31536000, 2592000, 604800, 86400, 3600, 60, 1]; 115 | for (let i = 0; i < timeValue.length; i++) { 116 | let inm = Math.floor(timeSpan / timeValue[i]); 117 | if (inm != 0) { 118 | return inm + timeDesc[i] + timeBA; 119 | } 120 | } 121 | } -------------------------------------------------------------------------------- /utils/encryption.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 加密相关 3 | */ 4 | 5 | import Md5Encrypt from './encryption/md5' 6 | import Base64 from './encryption/base64' 7 | 8 | /** 9 | * md5加密 10 | */ 11 | export const md5 = Md5Encrypt; 12 | 13 | /** 14 | * base64编码 15 | */ 16 | export const base64Encode = Base64.encode; 17 | /** 18 | * base64解码 19 | */ 20 | export const base64Decode = Base64.decode; -------------------------------------------------------------------------------- /utils/encryption/base64.js: -------------------------------------------------------------------------------- 1 | const Base64 = { 2 | _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 3 | encode: function (e) { 4 | e = String(e); 5 | let t = ""; 6 | let n, r, i, s, o, u, a; 7 | let f = 0; 8 | e = Base64._utf8_encode(e); 9 | while (f < e.length) { 10 | n = e.charCodeAt(f++); 11 | r = e.charCodeAt(f++); 12 | i = e.charCodeAt(f++); 13 | s = n >> 2; 14 | o = (n & 3) << 4 | r >> 4; 15 | u = (r & 15) << 2 | i >> 6; 16 | a = i & 63; 17 | if (isNaN(r)) { 18 | u = a = 64 19 | } else if (isNaN(i)) { 20 | a = 64 21 | } 22 | t = t + Base64._keyStr.charAt(s) + Base64._keyStr.charAt(o) + Base64._keyStr.charAt(u) + Base64._keyStr.charAt(a) 23 | } 24 | return t 25 | }, 26 | decode: function (e) { 27 | let t = ""; 28 | let n, r, i; 29 | let s, o, u, a; 30 | let f = 0; 31 | e = e.replace(/[^A-Za-z0-9+/=]/g, ""); 32 | while (f < e.length) { 33 | s = Base64._keyStr.indexOf(e.charAt(f++)); 34 | o = Base64._keyStr.indexOf(e.charAt(f++)); 35 | u = Base64._keyStr.indexOf(e.charAt(f++)); 36 | a = Base64._keyStr.indexOf(e.charAt(f++)); 37 | n = s << 2 | o >> 4; 38 | r = (o & 15) << 4 | u >> 2; 39 | i = (u & 3) << 6 | a; 40 | t = t + String.fromCharCode(n); 41 | if (u != 64) { 42 | t = t + String.fromCharCode(r) 43 | } 44 | if (a != 64) { 45 | t = t + String.fromCharCode(i) 46 | } 47 | } 48 | t = Base64._utf8_decode(t); 49 | return t 50 | }, 51 | _utf8_encode: function (e) { 52 | e = e.replace(/rn/g, "n"); 53 | let t = ""; 54 | for (let n = 0; n < e.length; n++) { 55 | let r = e.charCodeAt(n); 56 | if (r < 128) { 57 | t += String.fromCharCode(r) 58 | } else if (r > 127 && r < 2048) { 59 | t += String.fromCharCode(r >> 6 | 192); 60 | t += String.fromCharCode(r & 63 | 128) 61 | } else { 62 | t += String.fromCharCode(r >> 12 | 224); 63 | t += String.fromCharCode(r >> 6 & 63 | 128); 64 | t += String.fromCharCode(r & 63 | 128) 65 | } 66 | } 67 | return t 68 | }, 69 | _utf8_decode: function (e) { 70 | let t = ""; 71 | let n = 0; 72 | let r = 0, 73 | c2 = 0; 74 | while (n < e.length) { 75 | r = e.charCodeAt(n); 76 | if (r < 128) { 77 | t += String.fromCharCode(r); 78 | n++ 79 | } else if (r > 191 && r < 224) { 80 | c2 = e.charCodeAt(n + 1); 81 | t += String.fromCharCode((r & 31) << 6 | c2 & 63); 82 | n += 2 83 | } else { 84 | c2 = e.charCodeAt(n + 1); 85 | c3 = e.charCodeAt(n + 2); 86 | t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c3 & 63); 87 | n += 3 88 | } 89 | } 90 | return t 91 | } 92 | }; 93 | 94 | export default Base64; -------------------------------------------------------------------------------- /utils/encryption/md5.js: -------------------------------------------------------------------------------- 1 | /** 2 | * md5加密 3 | * @param {*} string 4 | */ 5 | export default function md5(string) { 6 | function md5_RotateLeft(lValue, iShiftBits) { 7 | return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits)); 8 | } 9 | 10 | function md5_AddUnsigned(lX, lY) { 11 | let lX4, lY4, lX8, lY8, lResult; 12 | lX8 = (lX & 0x80000000); 13 | lY8 = (lY & 0x80000000); 14 | lX4 = (lX & 0x40000000); 15 | lY4 = (lY & 0x40000000); 16 | lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF); 17 | if (lX4 & lY4) { 18 | return (lResult ^ 0x80000000 ^ lX8 ^ lY8); 19 | } 20 | if (lX4 | lY4) { 21 | if (lResult & 0x40000000) { 22 | return (lResult ^ 0xC0000000 ^ lX8 ^ lY8); 23 | } else { 24 | return (lResult ^ 0x40000000 ^ lX8 ^ lY8); 25 | } 26 | } else { 27 | return (lResult ^ lX8 ^ lY8); 28 | } 29 | } 30 | 31 | function md5_F(x, y, z) { 32 | return (x & y) | ((~x) & z); 33 | } 34 | 35 | function md5_G(x, y, z) { 36 | return (x & z) | (y & (~z)); 37 | } 38 | 39 | function md5_H(x, y, z) { 40 | return (x ^ y ^ z); 41 | } 42 | 43 | function md5_I(x, y, z) { 44 | return (y ^ (x | (~z))); 45 | } 46 | 47 | function md5_FF(a, b, c, d, x, s, ac) { 48 | a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_F(b, c, d), x), ac)); 49 | return md5_AddUnsigned(md5_RotateLeft(a, s), b); 50 | }; 51 | 52 | function md5_GG(a, b, c, d, x, s, ac) { 53 | a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_G(b, c, d), x), ac)); 54 | return md5_AddUnsigned(md5_RotateLeft(a, s), b); 55 | }; 56 | 57 | function md5_HH(a, b, c, d, x, s, ac) { 58 | a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_H(b, c, d), x), ac)); 59 | return md5_AddUnsigned(md5_RotateLeft(a, s), b); 60 | }; 61 | 62 | function md5_II(a, b, c, d, x, s, ac) { 63 | a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_I(b, c, d), x), ac)); 64 | return md5_AddUnsigned(md5_RotateLeft(a, s), b); 65 | }; 66 | 67 | function md5_ConvertToWordArray(string) { 68 | let lWordCount; 69 | let lMessageLength = string.length; 70 | let lNumberOfWords_temp1 = lMessageLength + 8; 71 | let lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64; 72 | let lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16; 73 | let lWordArray = Array(lNumberOfWords - 1); 74 | let lBytePosition = 0; 75 | let lByteCount = 0; 76 | while (lByteCount < lMessageLength) { 77 | lWordCount = (lByteCount - (lByteCount % 4)) / 4; 78 | lBytePosition = (lByteCount % 4) * 8; 79 | lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition)); 80 | lByteCount++; 81 | } 82 | lWordCount = (lByteCount - (lByteCount % 4)) / 4; 83 | lBytePosition = (lByteCount % 4) * 8; 84 | lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition); 85 | lWordArray[lNumberOfWords - 2] = lMessageLength << 3; 86 | lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29; 87 | return lWordArray; 88 | }; 89 | 90 | function md5_WordToHex(lValue) { 91 | let WordToHexValue = "", 92 | WordToHexValue_temp = "", 93 | lByte, lCount; 94 | for (lCount = 0; lCount <= 3; lCount++) { 95 | lByte = (lValue >>> (lCount * 8)) & 255; 96 | WordToHexValue_temp = "0" + lByte.toString(16); 97 | WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2); 98 | } 99 | return WordToHexValue; 100 | }; 101 | 102 | function md5_Utf8Encode(string) { 103 | string = string.replace(/\r\n/g, "\n"); 104 | let utftext = ""; 105 | for (let n = 0; n < string.length; n++) { 106 | let c = string.charCodeAt(n); 107 | if (c < 128) { 108 | utftext += String.fromCharCode(c); 109 | } else if ((c > 127) && (c < 2048)) { 110 | utftext += String.fromCharCode((c >> 6) | 192); 111 | utftext += String.fromCharCode((c & 63) | 128); 112 | } else { 113 | utftext += String.fromCharCode((c >> 12) | 224); 114 | utftext += String.fromCharCode(((c >> 6) & 63) | 128); 115 | utftext += String.fromCharCode((c & 63) | 128); 116 | } 117 | } 118 | return utftext; 119 | }; 120 | let x = Array(); 121 | let k, AA, BB, CC, DD, a, b, c, d; 122 | let S11 = 7, 123 | S12 = 12, 124 | S13 = 17, 125 | S14 = 22; 126 | let S21 = 5, 127 | S22 = 9, 128 | S23 = 14, 129 | S24 = 20; 130 | let S31 = 4, 131 | S32 = 11, 132 | S33 = 16, 133 | S34 = 23; 134 | let S41 = 6, 135 | S42 = 10, 136 | S43 = 15, 137 | S44 = 21; 138 | string = md5_Utf8Encode(string); 139 | x = md5_ConvertToWordArray(string); 140 | a = 0x67452301; 141 | b = 0xEFCDAB89; 142 | c = 0x98BADCFE; 143 | d = 0x10325476; 144 | for (k = 0; k < x.length; k += 16) { 145 | AA = a; 146 | BB = b; 147 | CC = c; 148 | DD = d; 149 | a = md5_FF(a, b, c, d, x[k + 0], S11, 0xD76AA478); 150 | d = md5_FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756); 151 | c = md5_FF(c, d, a, b, x[k + 2], S13, 0x242070DB); 152 | b = md5_FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE); 153 | a = md5_FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF); 154 | d = md5_FF(d, a, b, c, x[k + 5], S12, 0x4787C62A); 155 | c = md5_FF(c, d, a, b, x[k + 6], S13, 0xA8304613); 156 | b = md5_FF(b, c, d, a, x[k + 7], S14, 0xFD469501); 157 | a = md5_FF(a, b, c, d, x[k + 8], S11, 0x698098D8); 158 | d = md5_FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF); 159 | c = md5_FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1); 160 | b = md5_FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE); 161 | a = md5_FF(a, b, c, d, x[k + 12], S11, 0x6B901122); 162 | d = md5_FF(d, a, b, c, x[k + 13], S12, 0xFD987193); 163 | c = md5_FF(c, d, a, b, x[k + 14], S13, 0xA679438E); 164 | b = md5_FF(b, c, d, a, x[k + 15], S14, 0x49B40821); 165 | a = md5_GG(a, b, c, d, x[k + 1], S21, 0xF61E2562); 166 | d = md5_GG(d, a, b, c, x[k + 6], S22, 0xC040B340); 167 | c = md5_GG(c, d, a, b, x[k + 11], S23, 0x265E5A51); 168 | b = md5_GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA); 169 | a = md5_GG(a, b, c, d, x[k + 5], S21, 0xD62F105D); 170 | d = md5_GG(d, a, b, c, x[k + 10], S22, 0x2441453); 171 | c = md5_GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681); 172 | b = md5_GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8); 173 | a = md5_GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6); 174 | d = md5_GG(d, a, b, c, x[k + 14], S22, 0xC33707D6); 175 | c = md5_GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87); 176 | b = md5_GG(b, c, d, a, x[k + 8], S24, 0x455A14ED); 177 | a = md5_GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905); 178 | d = md5_GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8); 179 | c = md5_GG(c, d, a, b, x[k + 7], S23, 0x676F02D9); 180 | b = md5_GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A); 181 | a = md5_HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942); 182 | d = md5_HH(d, a, b, c, x[k + 8], S32, 0x8771F681); 183 | c = md5_HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122); 184 | b = md5_HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C); 185 | a = md5_HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44); 186 | d = md5_HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9); 187 | c = md5_HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60); 188 | b = md5_HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70); 189 | a = md5_HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6); 190 | d = md5_HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA); 191 | c = md5_HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085); 192 | b = md5_HH(b, c, d, a, x[k + 6], S34, 0x4881D05); 193 | a = md5_HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039); 194 | d = md5_HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5); 195 | c = md5_HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8); 196 | b = md5_HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665); 197 | a = md5_II(a, b, c, d, x[k + 0], S41, 0xF4292244); 198 | d = md5_II(d, a, b, c, x[k + 7], S42, 0x432AFF97); 199 | c = md5_II(c, d, a, b, x[k + 14], S43, 0xAB9423A7); 200 | b = md5_II(b, c, d, a, x[k + 5], S44, 0xFC93A039); 201 | a = md5_II(a, b, c, d, x[k + 12], S41, 0x655B59C3); 202 | d = md5_II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92); 203 | c = md5_II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D); 204 | b = md5_II(b, c, d, a, x[k + 1], S44, 0x85845DD1); 205 | a = md5_II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F); 206 | d = md5_II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0); 207 | c = md5_II(c, d, a, b, x[k + 6], S43, 0xA3014314); 208 | b = md5_II(b, c, d, a, x[k + 13], S44, 0x4E0811A1); 209 | a = md5_II(a, b, c, d, x[k + 4], S41, 0xF7537E82); 210 | d = md5_II(d, a, b, c, x[k + 11], S42, 0xBD3AF235); 211 | c = md5_II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB); 212 | b = md5_II(b, c, d, a, x[k + 9], S44, 0xEB86D391); 213 | a = md5_AddUnsigned(a, AA); 214 | b = md5_AddUnsigned(b, BB); 215 | c = md5_AddUnsigned(c, CC); 216 | d = md5_AddUnsigned(d, DD); 217 | } 218 | return (md5_WordToHex(a) + md5_WordToHex(b) + md5_WordToHex(c) + md5_WordToHex(d)).toLowerCase(); 219 | } -------------------------------------------------------------------------------- /utils/function.js: -------------------------------------------------------------------------------- 1 | /**通用函数类 */ 2 | 3 | /** 4 | * 类型检测 5 | * @param {*} obj 6 | */ 7 | export const objType = (obj) => Object.prototype.toString.call(obj).slice(8, -1); 8 | /** 9 | * 获取当前对象类型 10 | * @param {*} v 11 | */ 12 | //export const getType = v => v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase(); 13 | 14 | 15 | /** 16 | * 获取字节 17 | * @param {*} str 18 | */ 19 | export const byteSize = str => new Blob([str]).size; 20 | 21 | /** 22 | * 将多行字符串换行并转换为数组 23 | * @param {*} str 24 | */ 25 | export const splitLines = str => str.split(/\r?\n/); 26 | 27 | /** 28 | * 删除字符串中的HTMl标签 29 | * @param {*} str 30 | */ 31 | export const removeHTMLTags = str => str.replace(/<[^>]*>/g, ''); 32 | 33 | 34 | 35 | /** 36 | * 转义HTML标签 37 | * @param {*} str 38 | */ 39 | export const escapeHTML = str => 40 | str.replace( 41 | /[&<>'"]/g, 42 | tag => 43 | ({ 44 | '&': '&', 45 | '<': '<', 46 | '>': '>', 47 | "'": ''', 48 | '"': '"' 49 | } [tag] || tag) 50 | ); 51 | /** 52 | * 还原HTML标签 53 | * @param {*} str 54 | */ 55 | export const unescapeHTML = str => 56 | str.replace( 57 | /(&|<|>|'|")/g, 58 | tag => 59 | ({ 60 | '&': '&', 61 | '<': '<', 62 | '>': '>', 63 | ''': "'", 64 | '"': '"' 65 | } [tag] || tag) 66 | ); 67 | 68 | 69 | /** 70 | * 将数字进行分割 71 | * @param {*} num 数字 72 | * @param {*} cnt 要分割的数量 73 | * @param {*} split 分隔符,默认, 74 | */ 75 | export const formatNumber = (num, cnt = 3, split = ',') => { 76 | //let reg = /\B(?=(\d{3})+(?!\d))/g; 77 | let reg = new RegExp(`\\B(?=(\\d{${cnt}})+(?!\\d))`, "g") 78 | // num.toString().replace(reg, ($, $1) => { 79 | // console.log("22", $, $1); 80 | // }) 81 | return num.toString().replace(reg, split); 82 | } 83 | 84 | /** 85 | * 将字符串进行分割 86 | * @param {*} val string 87 | * @param {*} cnt 要分割的数量 88 | * @param {*} split 分隔符,默认, 89 | */ 90 | export const formatString = (val, cnt = 3, split = ',') => { 91 | let reg = new RegExp(`\\B(?=(\\w{${cnt}})+(?!\\w))`, "g") 92 | return val.replace(reg, split); 93 | } 94 | 95 | /** 96 | * 节流 97 | * 节流操作使回调函数在每隔一段时间定期执行一次,时间间隔内再触发,不会重新执行。 98 | * @param {*} func 99 | * @param {*} timerNumber 100 | */ 101 | export const throttle = (func, wait = 100) => { 102 | let timer = null; 103 | return (...args) => { 104 | if (timer) { 105 | return; 106 | } 107 | 108 | timer = setTimeout(() => { 109 | func(...args) 110 | timer = null; 111 | }, wait) 112 | } 113 | } 114 | 115 | /** 116 | * 防抖 117 | * 短时间内多次触发一个函数,只执行最后一次,或在开始时执行 118 | * @param {*} fn 119 | * @param {*} delay 120 | */ 121 | export const debounce = (fn, delay) => { 122 | let timer = null; 123 | return (...args) => { 124 | clearTimeout(timer); 125 | timer = setTimeout(() => { 126 | fn(...args); 127 | }, delay); 128 | } 129 | } 130 | 131 | /** 132 | * 获取URL参数 133 | * @param {*} param 134 | * @param {*} url 135 | * @param {*} nullDefault 136 | */ 137 | export const urlParam = (param, nullDefault = null, url = window.location.href) => { 138 | let p = new URLSearchParams(new URL(url).search).get(param); 139 | return p || nullDefault; 140 | } 141 | 142 | /** 143 | * 获取随机数 144 | * @param {*} min 145 | * @param {*} max 146 | */ 147 | export const randomNum = (min = 0, max = 0) => parseInt(Math.random() * (max - min + 1) + min, 10); 148 | 149 | /** 150 | * 获取指定位数的随机码(数字+字母(大写)) 151 | * @param {*} codeLength 152 | */ 153 | export const randomCode = (codeLength = 4) => { 154 | let code = '' 155 | let randomCode = [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"]; 156 | while (codeLength--) { 157 | code += randomCode[randomNum(0, randomCode.length - 1)] 158 | } 159 | return code 160 | } 161 | 162 | /** 163 | * 生成随机ip-v4 164 | */ 165 | export const randomIP = () => Array(4).fill(0).map((_, i) => Math.floor(Math.random() * 255) + (i === 0 ? 1 : 0)).join('.'); 166 | 167 | /** 168 | * 随机16进制颜色 169 | */ 170 | export const randomHexColor = () => '#' + (Math.random() * 0xfffff * 1000000).toString(16).slice(0, 6); 171 | 172 | /** 173 | * 随机生成布尔值 174 | */ 175 | export const randomBoolean = () => Math.random() >= 0.5; 176 | 177 | /** 178 | * 随机生成浮点数据 179 | * @param {*} min 180 | * @param {*} max 181 | */ 182 | export const randomFloat = (min = 0, max = 1) => Math.random() * (max - min) + min; 183 | 184 | /** 185 | * 首字母大写 186 | * @param {*} param0 187 | */ 188 | export const capitalize = ([first, ...rest]) => first.toUpperCase() + rest.join(''); 189 | 190 | /** 191 | * 每个首字母都大写 192 | * @param {*} str 193 | */ 194 | export const capitalizeEveryWord = str => str.replace(/\b[a-z]/g, char => char.toUpperCase()); 195 | 196 | /** 197 | * 首字母小写 198 | * @param {*} param0 199 | */ 200 | export const decapitalize = ([first, ...rest]) => first.toLowerCase() + rest.join('') 201 | 202 | /** 203 | * 每个首字母都小写 204 | * @param {*} str 205 | */ 206 | export const decapitalizeEveryWord = str => str.replace(/\b[A-Z]/g, char => char.toLowerCase()); 207 | 208 | /** 209 | * 转为驼峰命名,下划线和中间分隔先进行转换处理 210 | * @param {*} str 211 | */ 212 | export const camelCase = (str) => { 213 | const arr = str 214 | .replace(/([A-Z])/g, '-$1') 215 | .toLowerCase() 216 | .replace(/[_.\- ]+/g, '-') 217 | .replace(/(^-)|(-$)/g, '') 218 | .split('-'); 219 | 220 | //移除第一位 221 | let ret = arr[0]; 222 | arr.shift(); 223 | 224 | return ret + arr.map(item => item.replace(/\w/, ($1) => $1.toUpperCase())).join(''); 225 | } 226 | /** 227 | * 驼峰转为连字符 228 | * @param {*} str 229 | */ 230 | export const hyphenate = (str) => str.replace(/\B([A-Z])/g, '-$1').toLowerCase() 231 | 232 | 233 | /** 234 | * 脱敏电话号码 235 | * @param {*} phone 236 | * @param {*} split 237 | */ 238 | export const privacyPhone = (phone, split = "****") => (phone + '').replace(/(\d{3})\d{1,}(\d{4})/, `$1${split}$2`); 239 | 240 | /** 241 | * 脱敏姓名 242 | * @param {*} name 243 | */ 244 | export const privacyName = (name, split = "**") => name.replace(/^(\S)(\S|\s)*$/, `$1${split}`); 245 | 246 | /** 247 | * 比特单位转换 248 | * @param {*} bytes 249 | */ 250 | export const bytesToSize = (bytes) => { 251 | if (bytes === 0) return '0 B' 252 | let k = 1024; 253 | let sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] 254 | let i = Math.floor(Math.log(bytes) / Math.log(k)) 255 | return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i] 256 | } 257 | 258 | 259 | 260 | /** 261 | * 获取uuid 262 | * @param {*} spit 默认替换指定符号 263 | */ 264 | export const getUUID = (spit = "-") => { 265 | let s = []; 266 | let hexDigits = "0123456789abcdef"; 267 | for (let i = 0; i < 36; i++) { 268 | s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); 269 | } 270 | s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010 271 | s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01 272 | s[8] = s[13] = s[18] = s[23] = spit; 273 | return s.join(""); 274 | } 275 | 276 | /** 277 | * 深度克隆对象 278 | * @param {*} obj 279 | */ 280 | export const deepClone = (obj, hash = new WeakMap()) => { 281 | if (obj instanceof RegExp) return new RegExp(obj) 282 | if (obj instanceof Date) return new Date(obj) 283 | if (obj === null || typeof obj !== 'object') { //如果不是复杂数据类型,直接返回 284 | return obj 285 | } 286 | if (hash.has(obj)) { 287 | return hash.get(obj) 288 | } 289 | 290 | let t = new obj.constructor() 291 | hash.set(obj, t) 292 | for (let key in obj) { 293 | if (obj.hasOwnProperty(key)) { 294 | t[key] = deepClone(obj[key], hash) 295 | } 296 | } 297 | return t 298 | } 299 | //去除之前写法 300 | // export const deepClone = (obj) => { 301 | // if (obj instanceof RegExp) return new RegExp(obj) 302 | // if (obj == null || typeof obj != 'object') return obj 303 | 304 | // let res = Object.prototype.toString.call(obj) === '[object Array]' ? [] : {}; 305 | // for (let key in obj) { 306 | // if (obj.hasOwnProperty(key)) { 307 | // res[key] = deepClone(obj[key]) 308 | // } 309 | // } 310 | // return res 311 | // } 312 | 313 | /** 314 | * 深度冻结对象 315 | * @param {*} obj 316 | */ 317 | export const deepFreeze = (obj) => { 318 | var prop, propKey 319 | Object.freeze(obj) 320 | for (propKey in obj) { 321 | prop = obj[propKey]; 322 | if (!obj.hasOwnProperty(propKey) || !(typeof prop === "object") || Object.isFrozen(prop)) { 323 | continue; 324 | } 325 | deepFreeze(prop); 326 | } 327 | } 328 | 329 | /** 330 | * 分割路径 331 | * @param {*} path 332 | */ 333 | export const splitPath = (path = "") => { 334 | const match = path.match(/^([\s\S]*?)((?:\.{1,2}|[^\\/]+?|)(\.[^./\\]*|))(?:[\\/]*)$/); 335 | return { 336 | dir: match[1], 337 | name: match[2], 338 | ext: match[3] 339 | }; 340 | } 341 | 342 | /** 343 | * 数字转大写金额 344 | * @param {*} n 345 | */ 346 | export const digitUpperCase = (n = 0) => { 347 | let fraction = ['角', '分']; 348 | let digit = [ 349 | '零', '壹', '贰', '叁', '肆', 350 | '伍', '陆', '柒', '捌', '玖' 351 | ]; 352 | let unit = [ 353 | ['元', '万', '亿'], 354 | ['', '拾', '佰', '仟'] 355 | ]; 356 | let head = n < 0 ? '欠' : ''; 357 | n = Math.abs(n); 358 | let s = ''; 359 | for (let i = 0; i < fraction.length; i++) { 360 | s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, ''); 361 | } 362 | s = s || '整'; 363 | n = Math.floor(n); 364 | for (let i = 0; i < unit[0].length && n > 0; i++) { 365 | let p = ''; 366 | for (let j = 0; j < unit[1].length && n > 0; j++) { 367 | p = digit[n % 10] + unit[1][j] + p; 368 | n = Math.floor(n / 10); 369 | } 370 | s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s; 371 | } 372 | return head + s.replace(/(零.)*零元/, '元') 373 | .replace(/(零.)+/g, '零') 374 | .replace(/^整$/, '零元整'); 375 | } 376 | 377 | /** 378 | * 截取字符串 379 | * @param {*} str 380 | * @param {*} length 381 | * @param {*} rep 382 | */ 383 | export const subText = (str, length = 0, rep = "...") => str.length > length ? (str.substr(0, length) + rep) : str; 384 | 385 | 386 | /** 387 | * 格式化路径 388 | * @param {*} path 389 | */ 390 | export const formatPath = path => path.replace(/[\\/]+/g, '/'); 391 | 392 | /** 393 | * 字符在字符串中出现次数 394 | * @param {*} str 395 | * @param {*} char 396 | */ 397 | export const characterCount = (str = '', char = '') => str.split(char).length - 1; 398 | 399 | //html = document.documentElement.outerHTML 400 | /** 401 | * 解析内容中http地址 402 | * @param {*} html 403 | */ 404 | //export const parseUrl = (html) => html.match(/(url\(|src=|href=)[\"\']*([^\"\'\(\)\<\>\[\] ]+)[\"\'\)]*|(http:\/\/[\w\-\.]+[^\"\'\(\)\<\>\[\] ]+)/gi) || []; 405 | 406 | 407 | 408 | /** 409 | * 获取星标评分 410 | * @param {*} rate 411 | */ 412 | export const starScore = rate => { 413 | if (rate < 0) rate = 0; 414 | else if (rate > 5) rate = 5; 415 | return "★★★★★☆☆☆☆☆".slice(5 - rate, 10 - rate); 416 | } 417 | 418 | /** 419 | * 只执行一次函数 420 | * @param {*} fn 421 | */ 422 | export const once = (fn) => { 423 | let called = false, 424 | result; 425 | 426 | return (...rest) => { 427 | if (called) return result; 428 | called = true; 429 | result = fn.apply(null, rest); 430 | fn = null; 431 | return result; 432 | } 433 | 434 | } 435 | 436 | /** 437 | * csv转换为数组 438 | * @param {*} data csv数据 439 | * @param {*} delimiter 分割符,默认逗号 440 | * @param {*} omitFirstRow 是否标题行 默认否 441 | */ 442 | export const csvToArray = (data, delimiter = ',', omitFirstRow = false) => 443 | data.slice(omitFirstRow ? data.indexOf('\n') + 1 : 0) 444 | .split('\n') 445 | .map(v => v.split(delimiter)); 446 | 447 | /** 448 | * 将csv数据转为json 449 | * @param {*} data csv数据 450 | * @param {*} delimiter 分割符,默认逗号 451 | */ 452 | export const csvToJSON = (data, delimiter = ',') => { 453 | const titles = data.slice(0, data.indexOf('\n')).split(delimiter); 454 | return data 455 | .slice(data.indexOf('\n') + 1) 456 | .split('\n') 457 | .map(v => { 458 | const values = v.split(delimiter); 459 | return titles.reduce( 460 | (obj, title, index) => ((obj[title] = values[index]), obj), {} 461 | ); 462 | }); 463 | }; 464 | 465 | /** 466 | * 计算函数执行时间 467 | * @param {*}} callback 468 | */ 469 | export const timeTaken = callback => { 470 | console.time('timeTaken'); 471 | const r = callback(); 472 | console.timeEnd('timeTaken'); 473 | return r; 474 | }; -------------------------------------------------------------------------------- /utils/struct.js: -------------------------------------------------------------------------------- 1 | /**结构类 */ 2 | 3 | import classEventEmitter from './struct/eventEmitter' 4 | import classEventEmitterMap from './struct/eventEmitterMap' 5 | import classSensitiveWords from './struct/sensitive-words' 6 | import classTrie from './struct/trie' 7 | 8 | /** 9 | * 发布订阅,对象缓存模式 10 | */ 11 | export const eventEmitter = () => { 12 | return new classEventEmitter(); 13 | } 14 | 15 | /** 16 | * 发布订阅,Map缓存方式 17 | */ 18 | export const eventEmitterMap = () => { 19 | return new classEventEmitterMap(); 20 | } 21 | 22 | /** 23 | * 敏感词处理,DFA算法 24 | */ 25 | export const sensitiveWords = () => { 26 | return new classSensitiveWords(); 27 | } 28 | 29 | /** 30 | * trie算法 31 | */ 32 | export const trie = () => { 33 | return new classTrie(); 34 | } -------------------------------------------------------------------------------- /utils/struct/eventEmitter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 发布订阅,基于对象实现 3 | */ 4 | export default class eventEmitter { 5 | 6 | constructor() { 7 | /** 8 | * 缓存结构,构建结构如下 9 | * list={ 10 | * event1:[fn1,fn2...], 11 | * event2:[fn1,fn2...] 12 | * } 13 | */ 14 | this.list = {}; 15 | } 16 | 17 | /** 18 | * 订阅 19 | * @param {*} event 事件名称 20 | * @param {*} fn 订阅函数 21 | */ 22 | on(event, fn) { 23 | //- !(fn instanceof Function) 24 | if (Object.prototype.toString.call(fn).slice(8, -1) != 'Function') { 25 | throw new Error("注册fn必须是一个函数") 26 | } 27 | 28 | (this.list[event] || (this.list[event] = [])).push(fn); 29 | 30 | return this; 31 | } 32 | 33 | /** 34 | * 监听一次 35 | * @param {*} event 36 | * @param {*} fn 37 | */ 38 | once(event, fn) { 39 | 40 | const on = (...rest) => { 41 | this.off(event, on); 42 | fn.apply(this, rest); 43 | } 44 | on.fn = fn; 45 | this.on(event, on); 46 | 47 | return this; 48 | } 49 | 50 | /** 51 | * 取消订阅 52 | * @param {*} event 事件名称 53 | * @param {*} fn 订阅函数 54 | */ 55 | off(event, fn) { 56 | //如果缓存中不存在对应事件,直接返回 57 | if (!this.list[event]) return this; 58 | 59 | //如果不传递fn,说明要清空此事件中全部函数 60 | if (!fn) { 61 | this.list[event] = []; 62 | return this; 63 | } 64 | //如果传递fn,过滤所有不为fn (一个事件可以注册多个fn,所以全都过滤) 65 | this.list[event] = this.list[event].filter(item => item !== fn && item.fn !== fn); 66 | 67 | return this; 68 | } 69 | 70 | /** 71 | * 发布 72 | */ 73 | emit(...rest) { 74 | let event = rest.shift() || ""; //获取第一个元素,并移除第一个 75 | let fns = this.list[event]; 76 | if (!fns || fns.length === 0) { 77 | return this; 78 | } 79 | 80 | //遍历函数 81 | fns.forEach(fn => { 82 | fn.apply(this, rest); 83 | }); 84 | return this; 85 | } 86 | /** 87 | * 清除所有注册内容 88 | */ 89 | clear() { 90 | this.list = {}; 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /utils/struct/eventEmitterMap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 发布订阅,基于Map实现 3 | */ 4 | export default class eventEmitterMap { 5 | 6 | constructor() { 7 | this.map = new Map(); 8 | } 9 | 10 | /** 11 | * 订阅 12 | * @param {*} event 事件名称 13 | * @param {*} fn 订阅函数 14 | */ 15 | on(event, fn) { 16 | //- !(fn instanceof Function) 17 | if (Object.prototype.toString.call(fn).slice(8, -1) != 'Function') { 18 | throw new Error("注册fn必须是一个函数") 19 | } 20 | 21 | if (!this.map.get(event)) { 22 | this.map.set(event, []); 23 | } 24 | this.map.get(event).push(fn); 25 | return this; 26 | } 27 | 28 | /** 29 | * 监听一次 30 | * @param {*} event 31 | * @param {*} fn 32 | */ 33 | once(event, fn) { 34 | const on = (...rest) => { 35 | this.off(event, on); 36 | fn.apply(this, rest); 37 | } 38 | on.fn = fn; 39 | this.on(event, on); 40 | 41 | return this; 42 | } 43 | 44 | /** 45 | * 取消订阅 46 | * @param {*} event 事件名称 47 | * @param {*} fn 订阅函数 48 | */ 49 | off(event, fn) { 50 | //如果缓存中不存在对应事件,直接返回 51 | if (!this.map.get(event)) return this; 52 | 53 | //如果不传递fn,说明要清空此事件中全部函数 54 | if (!fn) { 55 | this.map.set(event, []); 56 | return this; 57 | } 58 | //如果传递fn,过滤所有不为fn (一个事件可以注册多个fn,所以全都过滤) 59 | this.map.set(event, this.map.get(event).filter(item => item !== fn && item.fn !== fn)); 60 | 61 | return this; 62 | } 63 | 64 | /** 65 | * 发布 66 | */ 67 | emit(...rest) { 68 | let event = rest.shift() || ""; //获取第一个元素,并移除第一个 69 | let fns = this.map.get(event); 70 | if (!fns || fns.length === 0) { 71 | return this; 72 | } 73 | 74 | //遍历函数 75 | fns.forEach(fn => { 76 | fn.apply(this, rest); 77 | }); 78 | return this; 79 | } 80 | 81 | /** 82 | * 清除所有注册内容 83 | */ 84 | clear() { 85 | this.map.clear(); 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /utils/struct/sensitive-words.js: -------------------------------------------------------------------------------- 1 | //参考代码: https://github.com/alex-my/egg-full-sensitivewords 感谢作者的源码 2 | 3 | /** 4 | * DFA 算法 5 | * 敏感词库 6 | */ 7 | 8 | class DfaMap { 9 | constructor() { 10 | this._maps = {}; 11 | } 12 | 13 | get words() { 14 | return this._maps; 15 | } 16 | 17 | /** 18 | * 构建敏感词词库 19 | * @param {Array} words : ['word', ...] 20 | */ 21 | addToHashMap(words) { 22 | /** 23 | * words: ["王八蛋", "王八羔子"] 构成以下内容 24 | * 25 | * 王 -> 八 -> 蛋 26 | * -> 羔 -> 子 27 | * 28 | * JSON解析: https://www.keylala.cn 29 | * { 30 | * "王": { 31 | * "isEnd": 0, 32 | * "八": { 33 | * "isEnd": 0, 34 | * "蛋": { 35 | * "isEnd": 1 36 | * }, 37 | * "羔": { 38 | * "isEnd": 0, 39 | * "子": { 40 | * "isEnd": 1 41 | * } 42 | * } 43 | * } 44 | * } 45 | * } 46 | */ 47 | let key = null; 48 | let keyChar = null; 49 | let wordMap = null; 50 | let newMap = null; 51 | let nowMap = null; 52 | for (let i = 0; i < words.length; i++) { 53 | nowMap = this._maps; 54 | // key 敏感字 (eg: 王八蛋) 55 | key = words[i]; 56 | // 给每个敏感词语构造一棵树 57 | for (let j = 0; j < key.length; j++) { 58 | // 取出该敏感词语的每个词 (eg: 王) 59 | keyChar = key[j]; 60 | // 敏感词库中是否已经有该词,(eg: 以'王'为起始点的树) 61 | wordMap = nowMap[keyChar]; 62 | // 如果词库中存在该词,存在以它为首的这棵树,则接下来,用这颗树继续往下编 63 | if (wordMap) { 64 | nowMap = wordMap; 65 | } else { 66 | // 没有,则以它为起点,构造一颗新树 67 | newMap = {}; 68 | // 不是最后一个 69 | newMap.isEnd = 0; 70 | // 添加新树 (实际添加到了 this._maps) 中 71 | nowMap[keyChar] = newMap; 72 | // 后续使用这颗新树 73 | nowMap = newMap; 74 | } 75 | if (j === key.length - 1) { 76 | nowMap.isEnd = 1; 77 | } 78 | } 79 | } 80 | } 81 | } 82 | 83 | /** 84 | * 敏感词处理算法逻辑 85 | */ 86 | export default class SensitiveWords { 87 | constructor() { 88 | this.dfaMap = new DfaMap(); 89 | } 90 | /** 91 | * 加载敏感字集合,一般用于服务器启动时添加 92 | * @param {Array} words : ['word', ...] 93 | */ 94 | addWords(words) { 95 | this.dfaMap.addToHashMap(words); 96 | } 97 | 98 | /** 99 | * 新增敏感字 100 | * @param {string} word 101 | */ 102 | addWord(word) { 103 | this.addWords([word]); 104 | } 105 | 106 | /** 107 | * 给定的字符串是否包含敏感字, DFA 算法 108 | * @param {string} content 109 | */ 110 | containsDfa(content) { 111 | let result = false; 112 | for (let i = 0; i < content.length; i++) { 113 | if (this._checkByDfa(content, i) > 0) { 114 | result = true; 115 | break; 116 | } 117 | } 118 | return result; 119 | } 120 | 121 | /** 122 | * 获取给定的字符串包含的敏感词语, DFA 算法 123 | * @param {string} content 124 | */ 125 | wordsDfa(content) { 126 | const l = []; 127 | for (let i = 0; i < content.length; i++) { 128 | const index = this._checkByDfa(content, i); 129 | if (index > 0) { 130 | l.push(content.substring(i, i + index)); 131 | i = i + index - 1; // i++ 跳过已检查的内容 132 | } 133 | } 134 | return l; 135 | } 136 | 137 | /** 138 | * 用 separator 替换 content 139 | * @param {string} content 140 | * @param {string} separator 141 | * @param {bool} once: 142 | * true: 对敏感字值替换一次,比如 王八蛋 -> *, 143 | * false: 敏感字中含有多少字就替换多少次 王八蛋 -> *** 144 | */ 145 | replaceDfa(content, separator = '*', once = false) { 146 | let newContent = content; 147 | const l = this.wordsDfa(content); 148 | for (let i = 0; i < l.length; i++) { 149 | const words = l[i]; 150 | // 获取替换的文本 151 | const replaceWords = this._replaceWords(separator, words.length, once); 152 | newContent = newContent.replace(new RegExp(words, 'g'), replaceWords); 153 | } 154 | return newContent; 155 | } 156 | 157 | /** 158 | * 检测是否包含敏感字,DFA 159 | * @param {string} content 160 | * @param {int} begin 161 | * @return {int} 162 | */ 163 | _checkByDfa(content, begin) { 164 | let matchFlag = 0; 165 | let isEnd = false; 166 | let nowMap = this.dfaMap.words; 167 | let word = null; 168 | for (let i = begin; i < content.length; i++) { 169 | word = content[i]; 170 | // 查找 nowMap 中是否有以 word 为起点的树 171 | nowMap = nowMap[word]; 172 | if (nowMap !== undefined) { 173 | matchFlag++; 174 | // 为 1 表示该树到了终点 175 | if (nowMap.isEnd === 1) { 176 | isEnd = true; 177 | break; 178 | } 179 | } else { 180 | // 没找到 (eg: 没有找到 王 开头的树),直接返回了 181 | break; 182 | } 183 | } 184 | 185 | if (!isEnd) { 186 | // isEnd = false, 表示只匹配了部分,比如 他是王八,只匹配到了 王八,不符合条件 187 | matchFlag = 0; 188 | } 189 | 190 | return matchFlag; 191 | } 192 | 193 | _replaceWords(separator, length, once) { 194 | if (once) { 195 | return separator; 196 | } 197 | return new Array(length + 1).join(separator); 198 | } 199 | } -------------------------------------------------------------------------------- /utils/struct/trie.js: -------------------------------------------------------------------------------- 1 | /** 2 | * trie树结构 3 | */ 4 | 5 | /** 6 | * 结构类型 7 | */ 8 | class TrieNode { 9 | constructor() { 10 | this.next = Object.create(null); 11 | this.isEnd = false; 12 | } 13 | } 14 | 15 | /** 16 | * trie树 17 | */ 18 | export default class Trie { 19 | constructor() { 20 | this.root = new TrieNode(); 21 | }; 22 | 23 | insert(word) { 24 | if (!word) return false; 25 | let node = this.root; 26 | for (let i = 0; i < word.length; ++i) { 27 | if (!node.next[word[i]]) { 28 | node.next[word[i]] = new TrieNode(); 29 | } 30 | node = node.next[word[i]]; 31 | } 32 | node.isEnd = true; 33 | return true; 34 | }; 35 | 36 | inserts(wordsArray) { 37 | wordsArray.map(word => this.insert(word)) 38 | } 39 | 40 | search(word) { 41 | if (!word) return false; 42 | let node = this.root; 43 | for (let i = 0; i < word.length; ++i) { 44 | if (node.next[word[i]]) { 45 | node = node.next[word[i]]; 46 | } else { 47 | return false; 48 | } 49 | } 50 | return node.isEnd; 51 | }; 52 | 53 | startsWith(prefix) { 54 | if (!prefix) return true; 55 | let node = this.root; 56 | for (let i = 0; i < prefix.length; ++i) { 57 | if (node.next[prefix[i]]) { 58 | node = node.next[prefix[i]]; 59 | } else { 60 | return false; 61 | } 62 | } 63 | return true; 64 | }; 65 | } -------------------------------------------------------------------------------- /utils/window.js: -------------------------------------------------------------------------------- 1 | /** 2 | * window函数 3 | */ 4 | 5 | /** 6 | * 回到顶部,针对window整个页面的 7 | */ 8 | export const goToTop = (isAnimation = false) => { 9 | //直接到顶部 10 | if (isAnimation) { 11 | window.scrollTo(0, 0); 12 | return true; 13 | } 14 | //滚动到顶部 15 | scrollToTop(); 16 | return true; 17 | } 18 | 19 | //滚动到顶部 20 | const scrollToTop = () => { 21 | const c = document.documentElement.scrollTop || document.body.scrollTop; 22 | if (c > 0) { 23 | window.requestAnimationFrame(scrollToTop); 24 | window.scrollTo(0, c - c / 8); 25 | } 26 | } 27 | 28 | /** 29 | * 某个具备class元素内置滚动轴回到顶部,只处理第一个 30 | */ 31 | export const goToTopClassName = (className) => { 32 | let el = document.getElementsByClassName(className); 33 | if (!el[0]) { 34 | throw (`未能找到class为 ${className} 的节点,请确认`) 35 | } 36 | el[0].scrollTo(0, 0); 37 | }; 38 | 39 | /** 40 | * 某个具备id元素内置滚动轴回到顶部 41 | */ 42 | export const goToTopId = (id, isAnimation = false) => { 43 | let el = document.getElementById(id); 44 | if (!el) { 45 | throw (`未能找到id为 ${ID} 的节点,请确认`) 46 | } 47 | 48 | if (isAnimation) { 49 | el.scrollIntoView({ 50 | behavior: 'smooth' 51 | }) 52 | return; 53 | } 54 | 55 | if (el.scrollIntoView) { 56 | el.scrollIntoView(); 57 | } else if (el.scrollIntoViewIfNeeded) { 58 | el.scrollIntoViewIfNeeded(); 59 | } 60 | }; 61 | 62 | /** 63 | * 获取所选元素 64 | */ 65 | export const getSelectedText = () => window.getSelection().toString(); 66 | 67 | 68 | /** 69 | * 存储到localStorage 70 | * 说明:对象值如果是函数 、RegExp等特殊对象存贮会被忽略 71 | * @param { String } key 属性 72 | * @param { string } value 值 73 | */ 74 | export const setLocalStorage = (key, value) => { 75 | if (typeof (value) === 'object') value = JSON.stringify(value); 76 | localStorage.setItem(key, value) 77 | return true; 78 | }; 79 | 80 | /** 81 | * 读取localStorage存储值 82 | * @param {String} key 属性 83 | */ 84 | export const getLocalStorage = (key) => localStorage.getItem(key); 85 | 86 | 87 | /** 88 | * 删除localStorage存储值 89 | * @param {String} key 属性 90 | */ 91 | export const delLocalStorage = (key) => localStorage.removeItem(key); 92 | 93 | /** 94 | * 增加sessionStorage值 95 | * @param {*} key 96 | * @param {*} value 97 | */ 98 | export const setSessionStorage = (key, value) => sessionStorage.setItem(key, value); 99 | 100 | /** 101 | * 读取sessionStorage值 102 | * @param {*} key 103 | */ 104 | export const getSessionStorage = (key) => sessionStorage.getItem(key); 105 | /** 106 | * 删除sessionStorage值 107 | * @param {*} key 108 | */ 109 | export const delSessionStorage = (key) => sessionStorage.removeItem(key); 110 | 111 | 112 | /** 113 | * 创建cookie 114 | * @param {*} key 115 | * @param {*} value 116 | * @param {*} expireHours 过期时间 默认24小时 117 | */ 118 | export const setCookie = (key, value, expireHours = 24) => { 119 | let path = "/"; 120 | let date = Date.now() + expireHours * 60 * 60 * 1000; // cookie过期时间 121 | date = new Date(date).toUTCString(); 122 | document.cookie = key + "=" + encodeURIComponent(value) + ((!expireHours) ? "" : ("; expires=" + date)) + ";path=" + path + ";"; 123 | return true; 124 | } 125 | 126 | /** 127 | * 获取cookie 128 | * @param {*} key 129 | */ 130 | export const getCookie = (key) => { 131 | let reg = new RegExp("(^| )" + key + "=([^;]*)(;|$)"); 132 | let arr = document.cookie.match(reg); 133 | return arr ? arr[2] : null; 134 | } 135 | 136 | /** 137 | * 删除cookie 138 | * @param {*} key 139 | */ 140 | export const delCookie = (key) => getCookie(key) ? setCookie(key, "", -1) : false; 141 | 142 | /** 143 | * 获取页面可视高度 144 | */ 145 | export const pageViewHeight = () => { 146 | let d = document, 147 | a = d.compatMode == "BackCompat" ? d.body : d.documentElement; 148 | return a.clientHeight; 149 | } 150 | 151 | /** 152 | * 获取页面可视宽度 153 | */ 154 | export const pageViewWidth = () => { 155 | let d = document, 156 | a = d.compatMode == "BackCompat" ? d.body : d.documentElement; 157 | return a.clientWidth; 158 | } 159 | 160 | 161 | /** 162 | * 获取滚动轴位置 163 | * @param {*} el 164 | */ 165 | export const scrollPostion = (el = window) => 166 | ({ 167 | x: (el.pageXOffset !== undefined) ? el.pageXOffset : el.scrollLeft, 168 | y: (el.pageYOffset !== undefined) ? el.pageYOffset : el.scrollTop 169 | }); 170 | 171 | /** 172 | * 复制文本 173 | * @param {*} str 174 | */ 175 | export const copyText = (str) => { 176 | const el = document.createElement("textarea"); 177 | el.value = str; 178 | el.setAttribute("readonly", ""); 179 | el.style.position = "absolute"; 180 | el.style.left = "-9999px"; 181 | el.style.top = "-9999px"; 182 | document.body.appendChild(el); 183 | const selected = 184 | document.getSelection().rangeCount > 0 ? 185 | document.getSelection().getRangeAt(0) : 186 | false; 187 | el.select(); 188 | document.execCommand("copy"); 189 | document.body.removeChild(el); 190 | if (selected) { 191 | document.getSelection().removeAllRanges(); 192 | document.getSelection().addRange(selected); 193 | } 194 | return true; 195 | } 196 | 197 | /** 198 | * 进入全屏 199 | * @param {*} selector 200 | */ 201 | export const fullScreen = (selector = document.body) => { 202 | if (selector.requestFullscreen) { 203 | selector.requestFullscreen(); 204 | } else if (selector.mozRequestFullScreen) { 205 | selector.mozRequestFullScreen(); 206 | } else if (selector.webkitRequestFullscreen) { 207 | selector.webkitRequestFullscreen(); 208 | } else if (selector.msRequestFullscreen) { 209 | selector.msRequestFullscreen(); 210 | } 211 | //return true; 212 | }; 213 | 214 | /** 215 | * 退出全屏 216 | */ 217 | export const exitFullscreen = () => { 218 | if (document.exitFullScreen) { 219 | document.exitFullScreen(); 220 | } else if (document.mozCancelFullScreen) { 221 | document.mozCancelFullScreen(); 222 | } else if (document.webkitExitFullscreen) { 223 | document.webkitExitFullscreen(); 224 | } else if (document.msExitFullscreen) { 225 | document.msExitFullscreen(); 226 | } 227 | //return true; 228 | } 229 | 230 | /** 231 | * 是否全屏状态 232 | */ 233 | export const isFullScreen = () => { 234 | return !!( 235 | //document.fullscreen || 236 | document.mozFullScreen || 237 | document.webkitIsFullScreen || 238 | document.webkitFullScreen || 239 | document.msFullScreen 240 | ); 241 | } -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { 2 | defineConfig 3 | } from 'vite' 4 | import vue from '@vitejs/plugin-vue' 5 | import path from 'path' 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | resolve: { 10 | alias: [{ 11 | find: '@', 12 | replacement: path.resolve(__dirname, 'src') 13 | }] 14 | }, 15 | base: './', 16 | build: { 17 | outDir: 'docs', // 将构建好的文件输出到哪里(或者说将编译的文件) 18 | assetsDir: './', // 放置静态资源的地方 (js/css/img/font/...) 19 | }, 20 | //entry: 'public/index.html', 21 | sourcemap: false, 22 | plugins: [vue()] 23 | }) --------------------------------------------------------------------------------