├── .gitignore ├── LICENSE ├── README.md ├── commit.md ├── log.ts ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── animation │ ├── animation.js │ ├── imageloader.js │ └── timeline.js ├── index.ts └── utils │ ├── assets.ts │ ├── async │ └── task.ts │ ├── base.ts │ ├── cache │ ├── BaseCache.ts │ ├── BaseLocalStorage.ts │ ├── LocalStorageBrowser.ts │ └── LocalStorageMiniApp.ts │ ├── car.ts │ ├── city │ └── cityList.ts │ ├── client.ts │ ├── common.ts │ ├── css.ts │ ├── date.ts │ ├── event.ts │ ├── file.ts │ ├── filterdata │ └── index.ts │ ├── idcard.ts │ ├── locate.ts │ ├── math.ts │ ├── miniprogram.ts │ ├── random │ └── number.ts │ ├── string.ts │ ├── tree.ts │ ├── url.ts │ └── verification.ts ├── tsconfig.json └── types └── index.d.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 惊蛰 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | toa-tools, not just an js utils library!
3 | 一套移动端营销页常用的高质量高可持续的工具函数库 4 |
5 | 6 | ## 目录 7 | 8 | - [目录](#目录) 9 | - [简介](#简介) 10 | - [快速上手](#快速上手) 11 | - [安装](#安装) 12 | - [引入](#引入) 13 | - [讨论交流](#讨论交流) 14 | - [贡献代码](#贡献代码) 15 | - [开源协议](#开源协议) 16 | 17 | ## 简介 18 | 19 | toa-tools 是一套移动端营销页常用的高质量高可持续工具函数库。遵循简洁、易用、持续更新的开发原则。 20 | 21 | ## 快速上手 22 | 23 | ### 安装 24 | 25 | 1. 为你的项目添加 toa-tools 工具 26 | 27 | ``` 28 | npm i toa-tools 29 | ``` 30 | 31 | > **若你本地没有 Node.js 环境,无法使用 NPM 安装,则可以采用[CDN 安装](https://github.com/codeluosiyu/toa-tools/build/index.js)** 32 | 33 | ### 引入 34 | 35 | 2. 在页面中使用 36 | ```js 37 | import toa from "toa-tools"; 38 | ``` 39 | 或者 40 | ```js 41 | import { isInApp } from "toa-tools"; 42 | ``` 43 | **至此,Lin UI 已成功引入至你的项目中了!** 44 | 45 | ## 讨论交流 46 | 47 | ## 贡献代码 48 | 49 | 我们的代码基于 develop 分支开发,欢迎提交 Pull Request 进行代码贡献。 50 | 51 | 在提交 Pull Request 之前,请详细阅读我们的[开发规范](https://github.com/codeluosiyu/toa-tools/wiki),否则可能因为 Commit 信息不规范等原因被关闭 Pull Request。 52 | 53 | ## 开源协议 54 | 55 | [MIT](LICENSE) © 2018 codeluosiyu 56 | 57 | -------------------------------------------------------------------------------- /commit.md: -------------------------------------------------------------------------------- 1 | 2022-12-03T08:16:00+08:00 2 | 随机数:50236 3 | 提交次数:1 -------------------------------------------------------------------------------- /log.ts: -------------------------------------------------------------------------------- 1 | const log = "" -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "toa-tools", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@trysound/sax": { 8 | "version": "0.2.0", 9 | "resolved": "http://registry.npm.release.ctripcorp.com/@trysound/sax/download/@trysound/sax-0.2.0.tgz", 10 | "integrity": "sha1-zMqrdYr1Z2Hre/N69vA/Mm3XmK0=" 11 | }, 12 | "@types/estree": { 13 | "version": "0.0.51", 14 | "resolved": "http://registry.npm.release.ctripcorp.com/@types/estree/download/@types/estree-0.0.51.tgz", 15 | "integrity": "sha1-z9cJJKJaP9MrIY5eQg5ol+GsT0A=", 16 | "dev": true 17 | }, 18 | "@types/node": { 19 | "version": "17.0.21", 20 | "resolved": "http://registry.npm.release.ctripcorp.com/@types/node/download/@types/node-17.0.21.tgz", 21 | "integrity": "sha1-hkuYfAxo0HtDRYRcPmO3Xt0UNkQ=", 22 | "dev": true 23 | }, 24 | "@types/resolve": { 25 | "version": "0.0.8", 26 | "resolved": "http://registry.npm.release.ctripcorp.com/@types/resolve/download/@types/resolve-0.0.8.tgz", 27 | "integrity": "sha1-8mB00jjgJlnjI84aE9BB7uKA4ZQ=", 28 | "dev": true, 29 | "requires": { 30 | "@types/node": "*" 31 | } 32 | }, 33 | "ansi-styles": { 34 | "version": "4.3.0", 35 | "resolved": "http://registry.npm.release.ctripcorp.com/ansi-styles/download/ansi-styles-4.3.0.tgz", 36 | "integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=", 37 | "requires": { 38 | "color-convert": "^2.0.1" 39 | } 40 | }, 41 | "boolbase": { 42 | "version": "1.0.0", 43 | "resolved": "http://registry.npm.release.ctripcorp.com/boolbase/download/boolbase-1.0.0.tgz", 44 | "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" 45 | }, 46 | "browserslist": { 47 | "version": "4.20.0", 48 | "resolved": "http://registry.npm.release.ctripcorp.com/browserslist/download/browserslist-4.20.0.tgz", 49 | "integrity": "sha1-NZUeNUEHjBJdNt92BW6Uc4pS6+k=", 50 | "requires": { 51 | "caniuse-lite": "^1.0.30001313", 52 | "electron-to-chromium": "^1.4.76", 53 | "escalade": "^3.1.1", 54 | "node-releases": "^2.0.2", 55 | "picocolors": "^1.0.0" 56 | } 57 | }, 58 | "builtin-modules": { 59 | "version": "3.2.0", 60 | "resolved": "http://registry.npm.release.ctripcorp.com/builtin-modules/download/builtin-modules-3.2.0.tgz", 61 | "integrity": "sha1-RdXbmefuXmvE82LgCL+RerUEmIc=", 62 | "dev": true 63 | }, 64 | "caniuse-api": { 65 | "version": "3.0.0", 66 | "resolved": "http://registry.npm.release.ctripcorp.com/caniuse-api/download/caniuse-api-3.0.0.tgz", 67 | "integrity": "sha1-Xk2Q4idJYdRikZl99Znj7QCO5MA=", 68 | "requires": { 69 | "browserslist": "^4.0.0", 70 | "caniuse-lite": "^1.0.0", 71 | "lodash.memoize": "^4.1.2", 72 | "lodash.uniq": "^4.5.0" 73 | } 74 | }, 75 | "caniuse-lite": { 76 | "version": "1.0.30001314", 77 | "resolved": "http://registry.npm.release.ctripcorp.com/caniuse-lite/download/caniuse-lite-1.0.30001314.tgz", 78 | "integrity": "sha1-Zcf5+35FlPygozO+wdiTlmI3dZY=" 79 | }, 80 | "chalk": { 81 | "version": "4.1.2", 82 | "resolved": "http://registry.npm.release.ctripcorp.com/chalk/download/chalk-4.1.2.tgz", 83 | "integrity": "sha1-qsTit3NKdAhnrrFr8CqtVWoeegE=", 84 | "requires": { 85 | "ansi-styles": "^4.1.0", 86 | "supports-color": "^7.1.0" 87 | } 88 | }, 89 | "color-convert": { 90 | "version": "2.0.1", 91 | "resolved": "http://registry.npm.release.ctripcorp.com/color-convert/download/color-convert-2.0.1.tgz", 92 | "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", 93 | "requires": { 94 | "color-name": "~1.1.4" 95 | } 96 | }, 97 | "color-name": { 98 | "version": "1.1.4", 99 | "resolved": "http://registry.npm.release.ctripcorp.com/color-name/download/color-name-1.1.4.tgz", 100 | "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=" 101 | }, 102 | "colord": { 103 | "version": "2.9.2", 104 | "resolved": "http://registry.npm.release.ctripcorp.com/colord/download/colord-2.9.2.tgz", 105 | "integrity": "sha1-JeK6y7qmWZFCLAfqIJ4giUKO/7E=" 106 | }, 107 | "commander": { 108 | "version": "7.2.0", 109 | "resolved": "http://registry.npm.release.ctripcorp.com/commander/download/commander-7.2.0.tgz", 110 | "integrity": "sha1-o2y1fQtQHOEI5NIFWaFQo5HZerc=" 111 | }, 112 | "concat-with-sourcemaps": { 113 | "version": "1.1.0", 114 | "resolved": "http://registry.npm.release.ctripcorp.com/concat-with-sourcemaps/download/concat-with-sourcemaps-1.1.0.tgz", 115 | "integrity": "sha1-1OqT8FriV5CVG5nns7CeOQikCC4=", 116 | "requires": { 117 | "source-map": "^0.6.1" 118 | } 119 | }, 120 | "css-declaration-sorter": { 121 | "version": "6.1.4", 122 | "resolved": "http://registry.npm.release.ctripcorp.com/css-declaration-sorter/download/css-declaration-sorter-6.1.4.tgz", 123 | "integrity": "sha1-ub+07ZpB+NzKm/cYTYSeqUqClLQ=", 124 | "requires": { 125 | "timsort": "^0.3.0" 126 | } 127 | }, 128 | "css-select": { 129 | "version": "4.2.1", 130 | "resolved": "http://registry.npm.release.ctripcorp.com/css-select/download/css-select-4.2.1.tgz", 131 | "integrity": "sha1-nmZdauTH+dZdvmnQMW4yIfsnTN0=", 132 | "requires": { 133 | "boolbase": "^1.0.0", 134 | "css-what": "^5.1.0", 135 | "domhandler": "^4.3.0", 136 | "domutils": "^2.8.0", 137 | "nth-check": "^2.0.1" 138 | } 139 | }, 140 | "css-tree": { 141 | "version": "1.1.3", 142 | "resolved": "http://registry.npm.release.ctripcorp.com/css-tree/download/css-tree-1.1.3.tgz", 143 | "integrity": "sha1-60hw+2/XcHMn7JXC/yqwm16NuR0=", 144 | "requires": { 145 | "mdn-data": "2.0.14", 146 | "source-map": "^0.6.1" 147 | } 148 | }, 149 | "css-what": { 150 | "version": "5.1.0", 151 | "resolved": "http://registry.npm.release.ctripcorp.com/css-what/download/css-what-5.1.0.tgz", 152 | "integrity": "sha1-P3tweq32M7r2LCzrhXm1RbtA9/4=" 153 | }, 154 | "cssesc": { 155 | "version": "3.0.0", 156 | "resolved": "http://registry.npm.release.ctripcorp.com/cssesc/download/cssesc-3.0.0.tgz", 157 | "integrity": "sha1-N3QZGZA7hoVl4cCep0dEXNGJg+4=" 158 | }, 159 | "cssnano": { 160 | "version": "5.1.3", 161 | "resolved": "http://registry.npm.release.ctripcorp.com/cssnano/download/cssnano-5.1.3.tgz", 162 | "integrity": "sha1-zqF5+RgyuXE1pRUIUO6Lu+fo3Jk=", 163 | "requires": { 164 | "cssnano-preset-default": "^5.2.3", 165 | "lilconfig": "^2.0.3", 166 | "yaml": "^1.10.2" 167 | } 168 | }, 169 | "cssnano-preset-default": { 170 | "version": "5.2.3", 171 | "resolved": "http://registry.npm.release.ctripcorp.com/cssnano-preset-default/download/cssnano-preset-default-5.2.3.tgz", 172 | "integrity": "sha1-dPa+S0O2oKRfDv6jPjCoB0Zgoqo=", 173 | "requires": { 174 | "css-declaration-sorter": "^6.0.3", 175 | "cssnano-utils": "^3.1.0", 176 | "postcss-calc": "^8.2.3", 177 | "postcss-colormin": "^5.3.0", 178 | "postcss-convert-values": "^5.1.0", 179 | "postcss-discard-comments": "^5.1.1", 180 | "postcss-discard-duplicates": "^5.1.0", 181 | "postcss-discard-empty": "^5.1.1", 182 | "postcss-discard-overridden": "^5.1.0", 183 | "postcss-merge-longhand": "^5.1.1", 184 | "postcss-merge-rules": "^5.1.0", 185 | "postcss-minify-font-values": "^5.1.0", 186 | "postcss-minify-gradients": "^5.1.0", 187 | "postcss-minify-params": "^5.1.1", 188 | "postcss-minify-selectors": "^5.2.0", 189 | "postcss-normalize-charset": "^5.1.0", 190 | "postcss-normalize-display-values": "^5.1.0", 191 | "postcss-normalize-positions": "^5.1.0", 192 | "postcss-normalize-repeat-style": "^5.1.0", 193 | "postcss-normalize-string": "^5.1.0", 194 | "postcss-normalize-timing-functions": "^5.1.0", 195 | "postcss-normalize-unicode": "^5.1.0", 196 | "postcss-normalize-url": "^5.1.0", 197 | "postcss-normalize-whitespace": "^5.1.1", 198 | "postcss-ordered-values": "^5.1.0", 199 | "postcss-reduce-initial": "^5.1.0", 200 | "postcss-reduce-transforms": "^5.1.0", 201 | "postcss-svgo": "^5.1.0", 202 | "postcss-unique-selectors": "^5.1.1" 203 | } 204 | }, 205 | "cssnano-utils": { 206 | "version": "3.1.0", 207 | "resolved": "http://registry.npm.release.ctripcorp.com/cssnano-utils/download/cssnano-utils-3.1.0.tgz", 208 | "integrity": "sha1-lWhNCMkVEe38cNJjYzjKN+86aGE=" 209 | }, 210 | "csso": { 211 | "version": "4.2.0", 212 | "resolved": "http://registry.npm.release.ctripcorp.com/csso/download/csso-4.2.0.tgz", 213 | "integrity": "sha1-6jpWE0bo3J9UbW/r7dUBh884lSk=", 214 | "requires": { 215 | "css-tree": "^1.1.2" 216 | } 217 | }, 218 | "dom-serializer": { 219 | "version": "1.3.2", 220 | "resolved": "http://registry.npm.release.ctripcorp.com/dom-serializer/download/dom-serializer-1.3.2.tgz", 221 | "integrity": "sha1-YgZDfTLO767HFhgDIwx6ILwbTZE=", 222 | "requires": { 223 | "domelementtype": "^2.0.1", 224 | "domhandler": "^4.2.0", 225 | "entities": "^2.0.0" 226 | } 227 | }, 228 | "domelementtype": { 229 | "version": "2.2.0", 230 | "resolved": "http://registry.npm.release.ctripcorp.com/domelementtype/download/domelementtype-2.2.0.tgz", 231 | "integrity": "sha1-mgtsJ4LtahxzI9QiZxg9+b2LHVc=" 232 | }, 233 | "domhandler": { 234 | "version": "4.3.0", 235 | "resolved": "http://registry.npm.release.ctripcorp.com/domhandler/download/domhandler-4.3.0.tgz", 236 | "integrity": "sha1-FsZYxibPlmln4wb5ZrQx931KViY=", 237 | "requires": { 238 | "domelementtype": "^2.2.0" 239 | } 240 | }, 241 | "domutils": { 242 | "version": "2.8.0", 243 | "resolved": "http://registry.npm.release.ctripcorp.com/domutils/download/domutils-2.8.0.tgz", 244 | "integrity": "sha1-RDfe9dtuLR9dbuhZvZXKfQIEgTU=", 245 | "requires": { 246 | "dom-serializer": "^1.0.1", 247 | "domelementtype": "^2.2.0", 248 | "domhandler": "^4.2.0" 249 | } 250 | }, 251 | "electron-to-chromium": { 252 | "version": "1.4.82", 253 | "resolved": "http://registry.npm.release.ctripcorp.com/electron-to-chromium/download/electron-to-chromium-1.4.82.tgz", 254 | "integrity": "sha1-UeEjykNLHrqMQ07OK1TwlbMEplE=" 255 | }, 256 | "entities": { 257 | "version": "2.2.0", 258 | "resolved": "http://registry.npm.release.ctripcorp.com/entities/download/entities-2.2.0.tgz", 259 | "integrity": "sha1-CY3JDruD2N/6CJ1VJWs1HTTE2lU=" 260 | }, 261 | "escalade": { 262 | "version": "3.1.1", 263 | "resolved": "http://registry.npm.release.ctripcorp.com/escalade/download/escalade-3.1.1.tgz", 264 | "integrity": "sha1-2M/ccACWXFoBdLSoLqpcBVJ0LkA=" 265 | }, 266 | "estree-walker": { 267 | "version": "0.6.1", 268 | "resolved": "http://registry.npm.release.ctripcorp.com/estree-walker/download/estree-walker-0.6.1.tgz", 269 | "integrity": "sha1-UwSRQ/QMbrkYsjZx0f4yGfOhs2I=" 270 | }, 271 | "eventemitter3": { 272 | "version": "4.0.7", 273 | "resolved": "http://registry.npm.release.ctripcorp.com/eventemitter3/download/eventemitter3-4.0.7.tgz", 274 | "integrity": "sha1-Lem2j2Uo1WRO9cWVJqG0oHMGFp8=" 275 | }, 276 | "fsevents": { 277 | "version": "2.3.2", 278 | "resolved": "http://registry.npm.release.ctripcorp.com/fsevents/download/fsevents-2.3.2.tgz", 279 | "integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=", 280 | "dev": true, 281 | "optional": true 282 | }, 283 | "function-bind": { 284 | "version": "1.1.1", 285 | "resolved": "http://registry.npm.release.ctripcorp.com/function-bind/download/function-bind-1.1.1.tgz", 286 | "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=" 287 | }, 288 | "generic-names": { 289 | "version": "4.0.0", 290 | "resolved": "http://registry.npm.release.ctripcorp.com/generic-names/download/generic-names-4.0.0.tgz", 291 | "integrity": "sha1-C9ii/SP+jqFsvQonms1pwGkz2aM=", 292 | "requires": { 293 | "loader-utils": "^3.2.0" 294 | } 295 | }, 296 | "has": { 297 | "version": "1.0.3", 298 | "resolved": "http://registry.npm.release.ctripcorp.com/has/download/has-1.0.3.tgz", 299 | "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", 300 | "requires": { 301 | "function-bind": "^1.1.1" 302 | } 303 | }, 304 | "has-flag": { 305 | "version": "4.0.0", 306 | "resolved": "http://registry.npm.release.ctripcorp.com/has-flag/download/has-flag-4.0.0.tgz", 307 | "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=" 308 | }, 309 | "icss-replace-symbols": { 310 | "version": "1.1.0", 311 | "resolved": "http://registry.npm.release.ctripcorp.com/icss-replace-symbols/download/icss-replace-symbols-1.1.0.tgz", 312 | "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=" 313 | }, 314 | "icss-utils": { 315 | "version": "5.1.0", 316 | "resolved": "http://registry.npm.release.ctripcorp.com/icss-utils/download/icss-utils-5.1.0.tgz", 317 | "integrity": "sha1-xr5oWKvQE9do6YNmrkfiXViHsa4=" 318 | }, 319 | "import-cwd": { 320 | "version": "3.0.0", 321 | "resolved": "http://registry.npm.release.ctripcorp.com/import-cwd/download/import-cwd-3.0.0.tgz", 322 | "integrity": "sha1-IIRVR3GAFRJuqbNna3WS+4vUz5I=", 323 | "requires": { 324 | "import-from": "^3.0.0" 325 | } 326 | }, 327 | "import-from": { 328 | "version": "3.0.0", 329 | "resolved": "http://registry.npm.release.ctripcorp.com/import-from/download/import-from-3.0.0.tgz", 330 | "integrity": "sha1-BVz+w4zVon2AV8pRN219O/CJGWY=", 331 | "requires": { 332 | "resolve-from": "^5.0.0" 333 | } 334 | }, 335 | "is-core-module": { 336 | "version": "2.8.1", 337 | "resolved": "http://registry.npm.release.ctripcorp.com/is-core-module/download/is-core-module-2.8.1.tgz", 338 | "integrity": "sha1-9Z/fynAdWHnQprEApAqhVgzichE=", 339 | "requires": { 340 | "has": "^1.0.3" 341 | } 342 | }, 343 | "is-module": { 344 | "version": "1.0.0", 345 | "resolved": "http://registry.npm.release.ctripcorp.com/is-module/download/is-module-1.0.0.tgz", 346 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", 347 | "dev": true 348 | }, 349 | "is-reference": { 350 | "version": "1.2.1", 351 | "resolved": "http://registry.npm.release.ctripcorp.com/is-reference/download/is-reference-1.2.1.tgz", 352 | "integrity": "sha1-iy2sCzcfS8mU/eq6nrVC0DAC0Lc=", 353 | "dev": true, 354 | "requires": { 355 | "@types/estree": "*" 356 | } 357 | }, 358 | "lilconfig": { 359 | "version": "2.0.4", 360 | "resolved": "http://registry.npm.release.ctripcorp.com/lilconfig/download/lilconfig-2.0.4.tgz", 361 | "integrity": "sha1-9FB9BD1wWLOAtqj1y3vNSzTO4II=" 362 | }, 363 | "loader-utils": { 364 | "version": "3.2.0", 365 | "resolved": "http://registry.npm.release.ctripcorp.com/loader-utils/download/loader-utils-3.2.0.tgz", 366 | "integrity": "sha1-vOzFGniYvudHPUvGuEWyOvgwTU8=" 367 | }, 368 | "lodash.camelcase": { 369 | "version": "4.3.0", 370 | "resolved": "http://registry.npm.release.ctripcorp.com/lodash.camelcase/download/lodash.camelcase-4.3.0.tgz", 371 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" 372 | }, 373 | "lodash.memoize": { 374 | "version": "4.1.2", 375 | "resolved": "http://registry.npm.release.ctripcorp.com/lodash.memoize/download/lodash.memoize-4.1.2.tgz", 376 | "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" 377 | }, 378 | "lodash.uniq": { 379 | "version": "4.5.0", 380 | "resolved": "http://registry.npm.release.ctripcorp.com/lodash.uniq/download/lodash.uniq-4.5.0.tgz", 381 | "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" 382 | }, 383 | "magic-string": { 384 | "version": "0.25.9", 385 | "resolved": "http://registry.npm.release.ctripcorp.com/magic-string/download/magic-string-0.25.9.tgz", 386 | "integrity": "sha1-3n+fr5HvihyR0CwuUxTIJ3283Rw=", 387 | "dev": true, 388 | "requires": { 389 | "sourcemap-codec": "^1.4.8" 390 | } 391 | }, 392 | "mdn-data": { 393 | "version": "2.0.14", 394 | "resolved": "http://registry.npm.release.ctripcorp.com/mdn-data/download/mdn-data-2.0.14.tgz", 395 | "integrity": "sha1-cRP8QoGRfWPOKbQ0RvcB5owlulA=" 396 | }, 397 | "node-releases": { 398 | "version": "2.0.2", 399 | "resolved": "http://registry.npm.release.ctripcorp.com/node-releases/download/node-releases-2.0.2.tgz", 400 | "integrity": "sha1-cTn+ceL08RtH1NKYaq+MSGmeDAE=" 401 | }, 402 | "normalize-url": { 403 | "version": "6.1.0", 404 | "resolved": "http://registry.npm.release.ctripcorp.com/normalize-url/download/normalize-url-6.1.0.tgz", 405 | "integrity": "sha1-QNCIW1Nd7/4/MUe+yHfQX+TFZoo=" 406 | }, 407 | "nth-check": { 408 | "version": "2.0.1", 409 | "resolved": "http://registry.npm.release.ctripcorp.com/nth-check/download/nth-check-2.0.1.tgz", 410 | "integrity": "sha1-Lv4WL1w9oGoolZ+9PbddvuqfD8I=", 411 | "requires": { 412 | "boolbase": "^1.0.0" 413 | } 414 | }, 415 | "p-finally": { 416 | "version": "1.0.0", 417 | "resolved": "http://registry.npm.release.ctripcorp.com/p-finally/download/p-finally-1.0.0.tgz", 418 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" 419 | }, 420 | "p-queue": { 421 | "version": "6.6.2", 422 | "resolved": "http://registry.npm.release.ctripcorp.com/p-queue/download/p-queue-6.6.2.tgz", 423 | "integrity": "sha1-IGip3PjmfdDsPnory3aBD6qF5CY=", 424 | "requires": { 425 | "eventemitter3": "^4.0.4", 426 | "p-timeout": "^3.2.0" 427 | } 428 | }, 429 | "p-timeout": { 430 | "version": "3.2.0", 431 | "resolved": "http://registry.npm.release.ctripcorp.com/p-timeout/download/p-timeout-3.2.0.tgz", 432 | "integrity": "sha1-x+F6vJcdKnli74NiazXWNazyPf4=", 433 | "requires": { 434 | "p-finally": "^1.0.0" 435 | } 436 | }, 437 | "path-parse": { 438 | "version": "1.0.7", 439 | "resolved": "http://registry.npm.release.ctripcorp.com/path-parse/download/path-parse-1.0.7.tgz", 440 | "integrity": "sha1-+8EUtgykKzDZ2vWFjkvWi77bZzU=" 441 | }, 442 | "picocolors": { 443 | "version": "1.0.0", 444 | "resolved": "http://registry.npm.release.ctripcorp.com/picocolors/download/picocolors-1.0.0.tgz", 445 | "integrity": "sha1-y1vcdP8/UYkiNur3nWi8RFZKuBw=" 446 | }, 447 | "pify": { 448 | "version": "5.0.0", 449 | "resolved": "http://registry.npm.release.ctripcorp.com/pify/download/pify-5.0.0.tgz", 450 | "integrity": "sha1-H17KP16H6+wozG1UoOSq8ArMEn8=" 451 | }, 452 | "postcss-calc": { 453 | "version": "8.2.4", 454 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-calc/download/postcss-calc-8.2.4.tgz", 455 | "integrity": "sha1-d7nCm/y+igf/ZpPchwUIKIiXOaU=", 456 | "requires": { 457 | "postcss-selector-parser": "^6.0.9", 458 | "postcss-value-parser": "^4.2.0" 459 | } 460 | }, 461 | "postcss-colormin": { 462 | "version": "5.3.0", 463 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-colormin/download/postcss-colormin-5.3.0.tgz", 464 | "integrity": "sha1-PO6eXKYrLCfoT85jr/wM+1kBlWo=", 465 | "requires": { 466 | "browserslist": "^4.16.6", 467 | "caniuse-api": "^3.0.0", 468 | "colord": "^2.9.1", 469 | "postcss-value-parser": "^4.2.0" 470 | } 471 | }, 472 | "postcss-convert-values": { 473 | "version": "5.1.0", 474 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-convert-values/download/postcss-convert-values-5.1.0.tgz", 475 | "integrity": "sha1-+NOr5AtM5LFHBwKgcGND6sF+fBA=", 476 | "requires": { 477 | "postcss-value-parser": "^4.2.0" 478 | } 479 | }, 480 | "postcss-discard-comments": { 481 | "version": "5.1.1", 482 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-discard-comments/download/postcss-discard-comments-5.1.1.tgz", 483 | "integrity": "sha1-6QAZ4aDluZ3gX2NRbOZAvQ3z02k=" 484 | }, 485 | "postcss-discard-duplicates": { 486 | "version": "5.1.0", 487 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-discard-duplicates/download/postcss-discard-duplicates-5.1.0.tgz", 488 | "integrity": "sha1-nrT+hFZwak7r1tO3t3fQe60D6Eg=" 489 | }, 490 | "postcss-discard-empty": { 491 | "version": "5.1.1", 492 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-discard-empty/download/postcss-discard-empty-5.1.1.tgz", 493 | "integrity": "sha1-5XdiND/39QP+U/ylU9GNfww2nGw=" 494 | }, 495 | "postcss-discard-overridden": { 496 | "version": "5.1.0", 497 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-discard-overridden/download/postcss-discard-overridden-5.1.0.tgz", 498 | "integrity": "sha1-foxbUzJXR+nZATG7iGNSgvtKJ24=" 499 | }, 500 | "postcss-load-config": { 501 | "version": "3.1.3", 502 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-load-config/download/postcss-load-config-3.1.3.tgz", 503 | "integrity": "sha1-IZNbLEO5qG5lgaV2yn7hveK9HSM=", 504 | "requires": { 505 | "lilconfig": "^2.0.4", 506 | "yaml": "^1.10.2" 507 | } 508 | }, 509 | "postcss-merge-longhand": { 510 | "version": "5.1.1", 511 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-merge-longhand/download/postcss-merge-longhand-5.1.1.tgz", 512 | "integrity": "sha1-hHs9uUbwXXzqx/N7YN5RTwv2J1g=", 513 | "requires": { 514 | "postcss-value-parser": "^4.2.0", 515 | "stylehacks": "^5.1.0" 516 | } 517 | }, 518 | "postcss-merge-rules": { 519 | "version": "5.1.0", 520 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-merge-rules/download/postcss-merge-rules-5.1.0.tgz", 521 | "integrity": "sha1-otURfroJyGhqVHHZe9mvzzDRtB8=", 522 | "requires": { 523 | "browserslist": "^4.16.6", 524 | "caniuse-api": "^3.0.0", 525 | "cssnano-utils": "^3.1.0", 526 | "postcss-selector-parser": "^6.0.5" 527 | } 528 | }, 529 | "postcss-minify-font-values": { 530 | "version": "5.1.0", 531 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-minify-font-values/download/postcss-minify-font-values-5.1.0.tgz", 532 | "integrity": "sha1-8d8AFKcmCD0mDTvYXXOF+4nR8Bs=", 533 | "requires": { 534 | "postcss-value-parser": "^4.2.0" 535 | } 536 | }, 537 | "postcss-minify-gradients": { 538 | "version": "5.1.0", 539 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-minify-gradients/download/postcss-minify-gradients-5.1.0.tgz", 540 | "integrity": "sha1-3gJgpnoTt7MhqK3DFQcl8sZhI3c=", 541 | "requires": { 542 | "colord": "^2.9.1", 543 | "cssnano-utils": "^3.1.0", 544 | "postcss-value-parser": "^4.2.0" 545 | } 546 | }, 547 | "postcss-minify-params": { 548 | "version": "5.1.1", 549 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-minify-params/download/postcss-minify-params-5.1.1.tgz", 550 | "integrity": "sha1-xfjn2sVl5XfdmZBHh/vsV2y9v7I=", 551 | "requires": { 552 | "browserslist": "^4.16.6", 553 | "cssnano-utils": "^3.1.0", 554 | "postcss-value-parser": "^4.2.0" 555 | } 556 | }, 557 | "postcss-minify-selectors": { 558 | "version": "5.2.0", 559 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-minify-selectors/download/postcss-minify-selectors-5.2.0.tgz", 560 | "integrity": "sha1-F8K+Iz4Sso/6ikIaAvyLg5glU2w=", 561 | "requires": { 562 | "postcss-selector-parser": "^6.0.5" 563 | } 564 | }, 565 | "postcss-modules": { 566 | "version": "4.3.1", 567 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-modules/download/postcss-modules-4.3.1.tgz", 568 | "integrity": "sha1-UXwGwJ6rB9Ezrg7/yixRCruhgEg=", 569 | "requires": { 570 | "generic-names": "^4.0.0", 571 | "icss-replace-symbols": "^1.1.0", 572 | "lodash.camelcase": "^4.3.0", 573 | "postcss-modules-extract-imports": "^3.0.0", 574 | "postcss-modules-local-by-default": "^4.0.0", 575 | "postcss-modules-scope": "^3.0.0", 576 | "postcss-modules-values": "^4.0.0", 577 | "string-hash": "^1.1.1" 578 | } 579 | }, 580 | "postcss-modules-extract-imports": { 581 | "version": "3.0.0", 582 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-modules-extract-imports/download/postcss-modules-extract-imports-3.0.0.tgz", 583 | "integrity": "sha1-zaHwR8CugMl9vijD52pDuIAldB0=" 584 | }, 585 | "postcss-modules-local-by-default": { 586 | "version": "4.0.0", 587 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-modules-local-by-default/download/postcss-modules-local-by-default-4.0.0.tgz", 588 | "integrity": "sha1-67tU+uFZjuz99pGgKz/zs5ClpRw=", 589 | "requires": { 590 | "icss-utils": "^5.0.0", 591 | "postcss-selector-parser": "^6.0.2", 592 | "postcss-value-parser": "^4.1.0" 593 | } 594 | }, 595 | "postcss-modules-scope": { 596 | "version": "3.0.0", 597 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-modules-scope/download/postcss-modules-scope-3.0.0.tgz", 598 | "integrity": "sha1-nvMVFFbTu/oSDKRImN/Kby+gHwY=", 599 | "requires": { 600 | "postcss-selector-parser": "^6.0.4" 601 | } 602 | }, 603 | "postcss-modules-values": { 604 | "version": "4.0.0", 605 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-modules-values/download/postcss-modules-values-4.0.0.tgz", 606 | "integrity": "sha1-18Xn5ow7s8myfL9Iyguz/7RgLJw=", 607 | "requires": { 608 | "icss-utils": "^5.0.0" 609 | } 610 | }, 611 | "postcss-normalize-charset": { 612 | "version": "5.1.0", 613 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-normalize-charset/download/postcss-normalize-charset-5.1.0.tgz", 614 | "integrity": "sha1-kwLeCykJS1LCWemyz43Ah5h58O0=" 615 | }, 616 | "postcss-normalize-display-values": { 617 | "version": "5.1.0", 618 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-normalize-display-values/download/postcss-normalize-display-values-5.1.0.tgz", 619 | "integrity": "sha1-cqu65YCBlg6e3XIA/PIauDJcPag=", 620 | "requires": { 621 | "postcss-value-parser": "^4.2.0" 622 | } 623 | }, 624 | "postcss-normalize-positions": { 625 | "version": "5.1.0", 626 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-normalize-positions/download/postcss-normalize-positions-5.1.0.tgz", 627 | "integrity": "sha1-kCp8uXzwueixtlTUpD1FHkiWZFg=", 628 | "requires": { 629 | "postcss-value-parser": "^4.2.0" 630 | } 631 | }, 632 | "postcss-normalize-repeat-style": { 633 | "version": "5.1.0", 634 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-normalize-repeat-style/download/postcss-normalize-repeat-style-5.1.0.tgz", 635 | "integrity": "sha1-9tb9WlT1GnQcyEo390WeYO96Y5g=", 636 | "requires": { 637 | "postcss-value-parser": "^4.2.0" 638 | } 639 | }, 640 | "postcss-normalize-string": { 641 | "version": "5.1.0", 642 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-normalize-string/download/postcss-normalize-string-5.1.0.tgz", 643 | "integrity": "sha1-QRlhFp4HMIyCwfjFXz6KM3dX4ig=", 644 | "requires": { 645 | "postcss-value-parser": "^4.2.0" 646 | } 647 | }, 648 | "postcss-normalize-timing-functions": { 649 | "version": "5.1.0", 650 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-normalize-timing-functions/download/postcss-normalize-timing-functions-5.1.0.tgz", 651 | "integrity": "sha1-1WFEEPjwsjiOnyQKpgEbpvUtr7s=", 652 | "requires": { 653 | "postcss-value-parser": "^4.2.0" 654 | } 655 | }, 656 | "postcss-normalize-unicode": { 657 | "version": "5.1.0", 658 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-normalize-unicode/download/postcss-normalize-unicode-5.1.0.tgz", 659 | "integrity": "sha1-PSOu3jXhYAiaKF4nv3Fd4R3J23U=", 660 | "requires": { 661 | "browserslist": "^4.16.6", 662 | "postcss-value-parser": "^4.2.0" 663 | } 664 | }, 665 | "postcss-normalize-url": { 666 | "version": "5.1.0", 667 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-normalize-url/download/postcss-normalize-url-5.1.0.tgz", 668 | "integrity": "sha1-7Z2IyoLiGr75n3Q0V9NymgQq3Nw=", 669 | "requires": { 670 | "normalize-url": "^6.0.1", 671 | "postcss-value-parser": "^4.2.0" 672 | } 673 | }, 674 | "postcss-normalize-whitespace": { 675 | "version": "5.1.1", 676 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-normalize-whitespace/download/postcss-normalize-whitespace-5.1.1.tgz", 677 | "integrity": "sha1-CKGg0f+henzG7+HmydqWnMRJPPo=", 678 | "requires": { 679 | "postcss-value-parser": "^4.2.0" 680 | } 681 | }, 682 | "postcss-ordered-values": { 683 | "version": "5.1.0", 684 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-ordered-values/download/postcss-ordered-values-5.1.0.tgz", 685 | "integrity": "sha1-BO9CngmRsCkryRixNc1MA497iJ8=", 686 | "requires": { 687 | "cssnano-utils": "^3.1.0", 688 | "postcss-value-parser": "^4.2.0" 689 | } 690 | }, 691 | "postcss-reduce-initial": { 692 | "version": "5.1.0", 693 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-reduce-initial/download/postcss-reduce-initial-5.1.0.tgz", 694 | "integrity": "sha1-/DFlnqboXEkvsqe1RTcMIVgixdY=", 695 | "requires": { 696 | "browserslist": "^4.16.6", 697 | "caniuse-api": "^3.0.0" 698 | } 699 | }, 700 | "postcss-reduce-transforms": { 701 | "version": "5.1.0", 702 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-reduce-transforms/download/postcss-reduce-transforms-5.1.0.tgz", 703 | "integrity": "sha1-Mztw53WLgC890N3+mLscz++Wtuk=", 704 | "requires": { 705 | "postcss-value-parser": "^4.2.0" 706 | } 707 | }, 708 | "postcss-selector-parser": { 709 | "version": "6.0.9", 710 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-selector-parser/download/postcss-selector-parser-6.0.9.tgz", 711 | "integrity": "sha1-7nHDuf9j2c0TCDiHbBOi7BqZKy8=", 712 | "requires": { 713 | "cssesc": "^3.0.0", 714 | "util-deprecate": "^1.0.2" 715 | } 716 | }, 717 | "postcss-svgo": { 718 | "version": "5.1.0", 719 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-svgo/download/postcss-svgo-5.1.0.tgz", 720 | "integrity": "sha1-CjF0AM7XifIzoogm53Uj8VhX2A0=", 721 | "requires": { 722 | "postcss-value-parser": "^4.2.0", 723 | "svgo": "^2.7.0" 724 | } 725 | }, 726 | "postcss-unique-selectors": { 727 | "version": "5.1.1", 728 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-unique-selectors/download/postcss-unique-selectors-5.1.1.tgz", 729 | "integrity": "sha1-qfJz0erNCemqYIj0sFB7GLG1QbY=", 730 | "requires": { 731 | "postcss-selector-parser": "^6.0.5" 732 | } 733 | }, 734 | "postcss-value-parser": { 735 | "version": "4.2.0", 736 | "resolved": "http://registry.npm.release.ctripcorp.com/postcss-value-parser/download/postcss-value-parser-4.2.0.tgz", 737 | "integrity": "sha1-cjwJkgg2um0+WvAZ+SvAlxwC5RQ=" 738 | }, 739 | "promise.series": { 740 | "version": "0.2.0", 741 | "resolved": "http://registry.npm.release.ctripcorp.com/promise.series/download/promise.series-0.2.0.tgz", 742 | "integrity": "sha1-LMfr6Vn8OmYZwEq029yeRS2GS70=" 743 | }, 744 | "resolve": { 745 | "version": "1.22.0", 746 | "resolved": "http://registry.npm.release.ctripcorp.com/resolve/download/resolve-1.22.0.tgz", 747 | "integrity": "sha1-XguMZ8Fd9XqJvbq+YDoALyFzEZg=", 748 | "requires": { 749 | "is-core-module": "^2.8.1", 750 | "path-parse": "^1.0.7", 751 | "supports-preserve-symlinks-flag": "^1.0.0" 752 | } 753 | }, 754 | "resolve-from": { 755 | "version": "5.0.0", 756 | "resolved": "http://registry.npm.release.ctripcorp.com/resolve-from/download/resolve-from-5.0.0.tgz", 757 | "integrity": "sha1-w1IlhD3493bfIcV1V7wIfp39/Gk=" 758 | }, 759 | "rollup": { 760 | "version": "2.70.0", 761 | "resolved": "http://registry.npm.release.ctripcorp.com/rollup/download/rollup-2.70.0.tgz", 762 | "integrity": "sha1-F6kuWTjpKiUbliNS6QTJ9VgjDsc=", 763 | "dev": true, 764 | "requires": { 765 | "fsevents": "~2.3.2" 766 | } 767 | }, 768 | "rollup-plugin-commonjs": { 769 | "version": "10.1.0", 770 | "resolved": "http://registry.npm.release.ctripcorp.com/rollup-plugin-commonjs/download/rollup-plugin-commonjs-10.1.0.tgz", 771 | "integrity": "sha1-QXrztUUDh44ITRJ6300cr4vrhvs=", 772 | "dev": true, 773 | "requires": { 774 | "estree-walker": "^0.6.1", 775 | "is-reference": "^1.1.2", 776 | "magic-string": "^0.25.2", 777 | "resolve": "^1.11.0", 778 | "rollup-pluginutils": "^2.8.1" 779 | } 780 | }, 781 | "rollup-plugin-node-resolve": { 782 | "version": "5.2.0", 783 | "resolved": "http://registry.npm.release.ctripcorp.com/rollup-plugin-node-resolve/download/rollup-plugin-node-resolve-5.2.0.tgz", 784 | "integrity": "sha1-cw+T0Q7SAkc7H7VKWZen24xthSM=", 785 | "dev": true, 786 | "requires": { 787 | "@types/resolve": "0.0.8", 788 | "builtin-modules": "^3.1.0", 789 | "is-module": "^1.0.0", 790 | "resolve": "^1.11.1", 791 | "rollup-pluginutils": "^2.8.1" 792 | } 793 | }, 794 | "rollup-plugin-postcss": { 795 | "version": "4.0.2", 796 | "resolved": "http://registry.npm.release.ctripcorp.com/rollup-plugin-postcss/download/rollup-plugin-postcss-4.0.2.tgz", 797 | "integrity": "sha1-FelGLzlHUFmzaM4OScgA+ksfcFA=", 798 | "requires": { 799 | "chalk": "^4.1.0", 800 | "concat-with-sourcemaps": "^1.1.0", 801 | "cssnano": "^5.0.1", 802 | "import-cwd": "^3.0.0", 803 | "p-queue": "^6.6.2", 804 | "pify": "^5.0.0", 805 | "postcss-load-config": "^3.0.0", 806 | "postcss-modules": "^4.0.0", 807 | "promise.series": "^0.2.0", 808 | "resolve": "^1.19.0", 809 | "rollup-pluginutils": "^2.8.2", 810 | "safe-identifier": "^0.4.2", 811 | "style-inject": "^0.3.0" 812 | } 813 | }, 814 | "rollup-plugin-typescript": { 815 | "version": "1.0.1", 816 | "resolved": "http://registry.npm.release.ctripcorp.com/rollup-plugin-typescript/download/rollup-plugin-typescript-1.0.1.tgz", 817 | "integrity": "sha1-hlZQM7cUw9Hzq6UQqtPcUZ9wkek=", 818 | "dev": true, 819 | "requires": { 820 | "resolve": "^1.10.0", 821 | "rollup-pluginutils": "^2.5.0" 822 | } 823 | }, 824 | "rollup-pluginutils": { 825 | "version": "2.8.2", 826 | "resolved": "http://registry.npm.release.ctripcorp.com/rollup-pluginutils/download/rollup-pluginutils-2.8.2.tgz", 827 | "integrity": "sha1-cvKvB0i1kjZNvTOJ5gDlqURKNR4=", 828 | "requires": { 829 | "estree-walker": "^0.6.1" 830 | } 831 | }, 832 | "safe-identifier": { 833 | "version": "0.4.2", 834 | "resolved": "http://registry.npm.release.ctripcorp.com/safe-identifier/download/safe-identifier-0.4.2.tgz", 835 | "integrity": "sha1-z2v8oxwol8WICS0XUNMO9QHVn8s=" 836 | }, 837 | "source-map": { 838 | "version": "0.6.1", 839 | "resolved": "http://registry.npm.release.ctripcorp.com/source-map/download/source-map-0.6.1.tgz", 840 | "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=" 841 | }, 842 | "sourcemap-codec": { 843 | "version": "1.4.8", 844 | "resolved": "http://registry.npm.release.ctripcorp.com/sourcemap-codec/download/sourcemap-codec-1.4.8.tgz", 845 | "integrity": "sha1-6oBL2UhXQC5pktBaOO8a41qatMQ=", 846 | "dev": true 847 | }, 848 | "stable": { 849 | "version": "0.1.8", 850 | "resolved": "http://registry.npm.release.ctripcorp.com/stable/download/stable-0.1.8.tgz", 851 | "integrity": "sha1-g26zyDgv4pNv6vVEYxAXzn1Ho88=" 852 | }, 853 | "string-hash": { 854 | "version": "1.1.3", 855 | "resolved": "http://registry.npm.release.ctripcorp.com/string-hash/download/string-hash-1.1.3.tgz", 856 | "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=" 857 | }, 858 | "style-inject": { 859 | "version": "0.3.0", 860 | "resolved": "http://registry.npm.release.ctripcorp.com/style-inject/download/style-inject-0.3.0.tgz", 861 | "integrity": "sha1-0hxHev/skYEcyCNVgypwDSK/jdM=" 862 | }, 863 | "stylehacks": { 864 | "version": "5.1.0", 865 | "resolved": "http://registry.npm.release.ctripcorp.com/stylehacks/download/stylehacks-5.1.0.tgz", 866 | "integrity": "sha1-pABmSQygysoE6WxrAhU93DmRNSA=", 867 | "requires": { 868 | "browserslist": "^4.16.6", 869 | "postcss-selector-parser": "^6.0.4" 870 | } 871 | }, 872 | "supports-color": { 873 | "version": "7.2.0", 874 | "resolved": "http://registry.npm.release.ctripcorp.com/supports-color/download/supports-color-7.2.0.tgz", 875 | "integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=", 876 | "requires": { 877 | "has-flag": "^4.0.0" 878 | } 879 | }, 880 | "supports-preserve-symlinks-flag": { 881 | "version": "1.0.0", 882 | "resolved": "http://registry.npm.release.ctripcorp.com/supports-preserve-symlinks-flag/download/supports-preserve-symlinks-flag-1.0.0.tgz", 883 | "integrity": "sha1-btpL00SjyUrqN21MwxvHcxEDngk=" 884 | }, 885 | "svgo": { 886 | "version": "2.8.0", 887 | "resolved": "http://registry.npm.release.ctripcorp.com/svgo/download/svgo-2.8.0.tgz", 888 | "integrity": "sha1-T/gMzmcQ3CeV8MfHQQHmdkz8zSQ=", 889 | "requires": { 890 | "@trysound/sax": "0.2.0", 891 | "commander": "^7.2.0", 892 | "css-select": "^4.1.3", 893 | "css-tree": "^1.1.3", 894 | "csso": "^4.2.0", 895 | "picocolors": "^1.0.0", 896 | "stable": "^0.1.8" 897 | } 898 | }, 899 | "timsort": { 900 | "version": "0.3.0", 901 | "resolved": "http://registry.npm.release.ctripcorp.com/timsort/download/timsort-0.3.0.tgz", 902 | "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" 903 | }, 904 | "tslib": { 905 | "version": "2.3.1", 906 | "resolved": "http://registry.npm.release.ctripcorp.com/tslib/download/tslib-2.3.1.tgz", 907 | "integrity": "sha1-6KM1rdXOrlGqJh0ypJAVjvBC7wE=", 908 | "dev": true 909 | }, 910 | "typescript": { 911 | "version": "4.6.2", 912 | "resolved": "http://registry.npm.release.ctripcorp.com/typescript/download/typescript-4.6.2.tgz", 913 | "integrity": "sha1-/hLScntwj07vQPUVmLM5i6qWEdQ=", 914 | "dev": true 915 | }, 916 | "util-deprecate": { 917 | "version": "1.0.2", 918 | "resolved": "http://registry.npm.release.ctripcorp.com/util-deprecate/download/util-deprecate-1.0.2.tgz", 919 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 920 | }, 921 | "yaml": { 922 | "version": "1.10.2", 923 | "resolved": "http://registry.npm.release.ctripcorp.com/yaml/download/yaml-1.10.2.tgz", 924 | "integrity": "sha1-IwHF/78StGfejaIzOkWeKeeSDks=" 925 | } 926 | } 927 | } 928 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "toa-tools", 3 | "version": "0.0.1", 4 | "description": "javascript tools", 5 | "author": "", 6 | "license": "ISC", 7 | "bugs": { 8 | "url": "https://github.com/codeluosiyu/toa-tools/issues" 9 | }, 10 | "homepage": "https://github.com/codeluosiyu/toa-tools#readme", 11 | "main": "index.js", 12 | "scripts": { 13 | "build": "rollup -c", 14 | "build:dev": "npm run clean && env_type=development npm run build", 15 | "build:prod": "npm run clean && env_type=production npm run build", 16 | "clean": "rimraf umd", 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/codeluosiyu/toa-tools.git" 22 | }, 23 | "keywords": [ 24 | "toolkit", 25 | "rollup", 26 | "typescript" 27 | ], 28 | "browser": "dist/index.ts", 29 | "devDependencies": { 30 | "rollup": "^2.70.0", 31 | "rollup-plugin-commonjs": "^10.1.0", 32 | "rollup-plugin-node-resolve": "^5.2.0", 33 | "rollup-plugin-typescript": "^1.0.1", 34 | "tslib": "^2.3.1", 35 | "typescript": "^4.6.2" 36 | }, 37 | "dependencies": { 38 | "rollup-plugin-postcss": "^4.0.2" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import typescript from 'rollup-plugin-typescript'; 4 | import pkg from './package.json'; 5 | 6 | export default { 7 | input: 'src/index.ts', // 打包入口 8 | output: { // 打包出口 9 | file: pkg.browser, // 最终打包出来的文件路径和文件名,这里是在package.json的browser: 'dist/index.js'字段中配置的 10 | format: 'umd', // umd是兼容amd/cjs/iife的通用打包格式,适合浏览器 11 | sourcemap: true // 生成源码映射文件 12 | }, 13 | plugins: [ // 打包插件 14 | resolve(), // 查找和打包node_modules中的第三方模块 15 | commonjs(), // 将 CommonJS 转换成 ES2015 模块供 Rollup 处理 16 | typescript() // 解析TypeScript 17 | ] 18 | }; 19 | -------------------------------------------------------------------------------- /src/animation/animation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Timeline = require('./timeline'); 4 | var loadImage = require('./imageloader'); 5 | 6 | //初始化状态 7 | var STATE_INITIAL = 0; 8 | //开始状态 9 | var STATE_START = 1; 10 | //停止状态 11 | var STATE_STOP = 2; 12 | 13 | //同步任务 14 | var TASK_SYNC = 0; 15 | //异步任务 16 | var TASK_ASYNC = 1; 17 | 18 | 19 | /** 20 | * 简单的函数封装,执行callback 21 | * @param callback 执行的函数 22 | */ 23 | function next(callback) { 24 | callback && callback(); 25 | } 26 | 27 | /** 28 | * 帧动画库类 29 | * @constructor 30 | */ 31 | function Animation() { 32 | this.taskQueue = []; 33 | this.timeline = new Timeline(); 34 | this.state = STATE_INITIAL; 35 | this.index = 0; 36 | } 37 | 38 | /** 39 | * 添加一个同步任务,预加载图片 40 | * @param imglist 图片数组 41 | */ 42 | Animation.prototype.loadImage = function (imglist) { 43 | 44 | var taskFn = function (next) { 45 | loadImage(imglist.slice(), next); 46 | }; 47 | var type = TASK_SYNC; 48 | 49 | return this._add(taskFn, type); 50 | }; 51 | 52 | /** 53 | * 添加一个异步定时任务,通过定时改变图片背景位置,实现帧动画 54 | * @param ele dom对象 55 | * @param positions 背景位置数组 56 | * @param imageUrl 图片地址 57 | */ 58 | Animation.prototype.changePosition = function (ele, positions, imageUrl) { 59 | var len = positions.length; 60 | var taskFn; 61 | var type; 62 | if (len) { 63 | var me = this; 64 | taskFn = function (next, time) { 65 | //如果指定图片,则设置dom对象的背景图片地址 66 | if (imageUrl) { 67 | ele.style.backgroundImage = 'url(' + imageUrl + ')'; 68 | } 69 | //获得当前背景图片位置索引 70 | var index = Math.min(time / me.interval | 0, len); 71 | var position = positions[index - 1].split(' '); 72 | //改变dom对象的背景图片位置 73 | ele.style.backgroundPosition = position[0] + 'px ' + position[1] + 'px'; 74 | //当前任务执行完毕 75 | if (index === len) { 76 | next(); 77 | } 78 | }; 79 | type = TASK_ASYNC; 80 | } else { 81 | taskFn = next; 82 | type = TASK_SYNC; 83 | } 84 | 85 | return this._add(taskFn, type); 86 | }; 87 | 88 | /** 89 | * 添加一个异步定时任务,通过定时改变背景图片地址,实现帧动画 90 | * @param ele dom(Image对象) 91 | * @param imglist 图片地址数组 92 | */ 93 | Animation.prototype.changeSrc = function (ele, imglist) { 94 | var len = imglist.length; 95 | var taskFn; 96 | var type; 97 | if (len) { 98 | var me = this; 99 | taskFn = function (next, time) { 100 | //获得当前的图片索引 101 | var index = Math.min(time / me.interval | 0, len); 102 | //改变image对象的图片地址 103 | ele.src = imglist[index - 1]; 104 | //当前任务执行完毕 105 | if (index === len) { 106 | next(); 107 | } 108 | }; 109 | type = TASK_ASYNC; 110 | } else { 111 | taskFn = next; 112 | type = TASK_SYNC; 113 | } 114 | 115 | return this._add(taskFn, type); 116 | }; 117 | 118 | /** 119 | * 高级用法,添加一个异步定时执行的任务, 120 | * 该任务自定义动画每帧执行的任务函数 121 | * @param taskFn 每帧执行的任务函数 122 | */ 123 | Animation.prototype.enterFrame = function (taskFn) { 124 | return this._add(taskFn, TASK_ASYNC); 125 | }; 126 | 127 | /** 128 | * 添加一个同步任务,可在上一个任务完成执行回调函数 129 | * @param callback 回调函数 130 | */ 131 | Animation.prototype.then = function (callback) { 132 | var taskFn = function (next) { 133 | callback(this); 134 | next(); 135 | }; 136 | var type = TASK_SYNC; 137 | 138 | return this._add(taskFn, type); 139 | }; 140 | 141 | /** 142 | * 开始执行任务 143 | * @param interval 异步定时任务执行的间隔 144 | */ 145 | Animation.prototype.start = function (interval) { 146 | //如果任务已经开始,则返回 147 | if (this.state === STATE_START) 148 | return this; 149 | //如果任务链中没有任务,则返回 150 | if (!this.taskQueue.length) 151 | return this; 152 | this.state = STATE_START; 153 | 154 | this.interval = interval; 155 | this._runTask(); 156 | return this; 157 | }; 158 | 159 | /** 160 | * 添加一个同步任务,该任务就是回退到上一个任务中, 161 | * 实现重复上一个任务的效果,可定义重复的次数。 162 | * @param times 重复次数 163 | */ 164 | Animation.prototype.repeat = function (times) { 165 | var me = this; 166 | var taskFn = function () { 167 | if (typeof times === 'undefined') { 168 | //无限回退到上一个任务 169 | me.index--; 170 | me._runTask(); 171 | return; 172 | } 173 | if (times) { 174 | times--; 175 | //回退到上一个任务 176 | me.index--; 177 | me._runTask(); 178 | } else { 179 | //达到重复执行次数,则跳转到下一个任务 180 | var task = me.taskQueue[me.index]; 181 | me._next(task); 182 | } 183 | }; 184 | var type = TASK_SYNC; 185 | 186 | return this._add(taskFn, type); 187 | }; 188 | 189 | /** 190 | * 添加一个同步任务,该任务就是无线循环上一次任务 191 | */ 192 | Animation.prototype.repeatForever = function () { 193 | return this.repeat(); 194 | }; 195 | 196 | /** 197 | * 设置当前任务结束后下一个任务开始前的等待时间 198 | * @param time 等待的时长 199 | */ 200 | Animation.prototype.wait = function (time) { 201 | if (this.taskQueue && this.taskQueue.length > 0) { 202 | this.taskQueue[this.taskQueue.length - 1].wait = time; 203 | } 204 | return this; 205 | }; 206 | 207 | /** 208 | * 暂停当前执行的异步定时任务 209 | */ 210 | Animation.prototype.pause = function () { 211 | if (this.state === STATE_START) { 212 | this.state = STATE_STOP; 213 | this.timeline.stop(); 214 | return this; 215 | } 216 | return this; 217 | }; 218 | 219 | /** 220 | * 重新开始执行当前异步定时任务 221 | */ 222 | Animation.prototype.restart = function () { 223 | if (this.state === STATE_STOP) { 224 | this.state = STATE_START; 225 | this.timeline.restart(); 226 | return this; 227 | } 228 | return this; 229 | }; 230 | 231 | /** 232 | * 释放资源 233 | */ 234 | Animation.prototype.dispose = function () { 235 | if (this.state !== STATE_INITIAL) { 236 | this.state = STATE_INITIAL; 237 | this.taskQueue = null; 238 | this.timeline.stop(); 239 | this.timeline = null; 240 | return this; 241 | } 242 | return this; 243 | }; 244 | 245 | /** 246 | * 添加一个任务到任务队列中 247 | * @param taskFn 任务方法 248 | * @param type 任务类型 249 | * @returns {Animation} 250 | * @private 251 | */ 252 | Animation.prototype._add = function (taskFn, type) { 253 | this.taskQueue.push({ 254 | taskFn: taskFn, 255 | type: type 256 | }); 257 | return this; 258 | }; 259 | 260 | /** 261 | * 执行任务 262 | * @private 263 | */ 264 | Animation.prototype._runTask = function () { 265 | if (!this.taskQueue || this.state !== STATE_START) 266 | return; 267 | //如果任务链任务执行完则释放资源 268 | if (this.index === this.taskQueue.length) { 269 | this.dispose(); 270 | return; 271 | } 272 | //获得任务链上的一个任务 273 | var task = this.taskQueue[this.index]; 274 | if (task.type === TASK_SYNC) { 275 | this._syncTask(task); 276 | } else { 277 | this._asyncTask(task); 278 | } 279 | }; 280 | 281 | /** 282 | * 同步任务 283 | * @param task 执行任务的函数 284 | * @private 285 | */ 286 | Animation.prototype._syncTask = function (task) { 287 | var me = this; 288 | var next = function () { 289 | //切换到下一个任务 290 | me._next(task); 291 | }; 292 | var taskFn = task.taskFn; 293 | taskFn(next); 294 | }; 295 | 296 | /** 297 | * 异步任务 298 | * @param task 执行异步的函数 299 | * @private 300 | */ 301 | Animation.prototype._asyncTask = function (task) { 302 | var me = this; 303 | //定义每一帧执行的回调函数 304 | var enterframe = function (time) { 305 | var taskFn = task.taskFn; 306 | var next = function () { 307 | //停止执行当前任务 308 | me.timeline.stop(); 309 | //执行下一个任务 310 | me._next(task); 311 | }; 312 | taskFn(next, time); 313 | }; 314 | 315 | this.timeline.onenterframe = enterframe; 316 | this.timeline.start(this.interval); 317 | }; 318 | 319 | /** 320 | * 切换到下一个任务,如果当前任务需要等待,则延时执行 321 | * @param task 下一个任务 322 | * @private 323 | */ 324 | Animation.prototype._next = function (task) { 325 | var me = this; 326 | this.index++; 327 | task.wait ? setTimeout(function () { 328 | me._runTask(); 329 | }, task.wait) : this._runTask(); 330 | }; 331 | 332 | 333 | function createAnimation() { 334 | return new Animation(); 335 | } 336 | 337 | createAnimation.version = __VERSION__; 338 | 339 | module.exports = createAnimation; 340 | 341 | -------------------------------------------------------------------------------- /src/animation/imageloader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var __id = 0; 4 | 5 | /** 6 | * 动态创建id 7 | * @returns {number} 8 | */ 9 | function getId() { 10 | return ++__id; 11 | } 12 | 13 | /** 14 | * 预加载图片函数 15 | * @param images 加载的图片数组或对象 16 | * @param callback 全部图片加载完毕后调用的回调函数 17 | * @param timeout 加载超时的时长 18 | */ 19 | function loadImage(images, callback, timeout) { 20 | //加载完成图片的计数器 21 | var count = 0; 22 | //全部图片成功加载完图片的标志位 23 | var success = true; 24 | //超时timer的id 25 | var timeoutId = 0; 26 | //是否加载超时的标志位 27 | var isTimeout = false; 28 | //对图片数组(或对象)进行遍历 29 | for (var key in images) { 30 | //过滤掉prototype的属性 31 | if (!images.hasOwnProperty(key)) 32 | continue; 33 | //获得每个图片元素 34 | //期望格式是个object: {src:xxx} 35 | var item = images[key]; 36 | 37 | // 如果item是个字符串,则构造object 38 | if (typeof item === 'string') { 39 | item = images[key] = { 40 | src: item 41 | }; 42 | } 43 | 44 | //如果格式不满足期望,则丢弃此条数据进行下一次遍历 45 | if (!item || !item.src) 46 | continue; 47 | 48 | //计数+1 49 | count++; 50 | //设置图片元素的id 51 | item.id = "__img_" + key + getId(); 52 | //设置图片元素的img,是一个Image对象 53 | item.img = window[item.id] = new Image(); 54 | 55 | doLoad(item); 56 | } 57 | 58 | //遍历完成如果计数为0,则直接调用 59 | if (!count) { 60 | callback(success); 61 | } 62 | //如果设置了加载时长,则设置超时函数计时器 63 | else if (timeout) { 64 | timeoutId = setTimeout(onTimeout, timeout); 65 | } 66 | 67 | /** 68 | * 真正进行图片加载的函数 69 | * @param item 图片元素对象 70 | */ 71 | function doLoad(item) { 72 | item.status = "loading"; 73 | 74 | var img = item.img; 75 | //定义图片加载成功的回调函数 76 | img.onload = function () { 77 | //如果每张图片都成功才算成功 78 | success = success && true; 79 | item.status = "loaded"; 80 | done(); 81 | }; 82 | img.onerror = function () { 83 | //若有一张图片加载失败,则为失败 84 | success = false; 85 | item.status = "error"; 86 | done(); 87 | }; 88 | //发起一个http(s)请求加载图片 89 | img.src = item.src; 90 | 91 | /** 92 | * 每张图片加载完成的回调函数 93 | */ 94 | function done() { 95 | //事件清理 96 | img.onload = img.onerror = null; 97 | 98 | try { 99 | //删除window上注册的属性 100 | delete window[item.id]; 101 | } 102 | catch (e) { 103 | 104 | } 105 | //每张图片加载完成,计数器减一,当所有图片加载完毕且没有超时的情况下, 106 | //清除超时计时器,且执行回调函数 107 | if (!--count && !isTimeout) { 108 | clearTimeout(timeoutId); 109 | callback(success); 110 | } 111 | } 112 | } 113 | 114 | /** 115 | * 超时函数 116 | */ 117 | function onTimeout() { 118 | isTimeout = true; 119 | callback(false); 120 | } 121 | } 122 | 123 | module.exports = loadImage; 124 | -------------------------------------------------------------------------------- /src/animation/timeline.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var DEFAULT_INTERVAL = 1000 / 60; 4 | 5 | //初始化状态 6 | var STATE_INITIAL = 0; 7 | //开始状态 8 | var STATE_START = 1; 9 | //停止状态 10 | var STATE_STOP = 2; 11 | 12 | /** 13 | * Timline时间轴类 14 | * @constructor 15 | */ 16 | function Timeline() { 17 | this.animationHandler = 0; 18 | this.state = STATE_INITIAL; 19 | } 20 | 21 | /** 22 | * 时间轴上每一次回调执行的函数 23 | * @param time 从动画开始到当前执行的时间 24 | */ 25 | Timeline.prototype.onenterframe = function (time) { 26 | }; 27 | 28 | /** 29 | * 动画开始 30 | * @param interval 每一次回调的间隔时间 31 | */ 32 | Timeline.prototype.start = function (interval) { 33 | if (this.state === STATE_START) 34 | return; 35 | this.state = STATE_START; 36 | 37 | this.interval = interval || DEFAULT_INTERVAL; 38 | startTimeline(this, +new Date()); 39 | }; 40 | 41 | /** 42 | * 重新开始动画 43 | */ 44 | Timeline.prototype.restart = function () { 45 | if (this.state === STATE_START) 46 | return; 47 | if (!this.dur || !this.interval) 48 | return; 49 | 50 | this.state = STATE_START; 51 | 52 | //无缝连接停止动画的状态 53 | startTimeline(this, +new Date() - this.dur); 54 | }; 55 | 56 | /** 57 | * 动画停止 58 | */ 59 | Timeline.prototype.stop = function () { 60 | if (this.state !== STATE_START) 61 | return; 62 | this.state = STATE_STOP; 63 | 64 | //如果动画开始过,则记录动画从开始到当前所经历的时间 65 | if (this.startTime) { 66 | this.dur = +new Date() - this.startTime; 67 | } 68 | cancelAnimationFrame(this.animationHandler); 69 | }; 70 | 71 | /** 72 | * 时间轴动画启动函数 73 | * @param timeline 时间轴实例 74 | * @param startTime 动画开始时间戳 75 | */ 76 | function startTimeline(timeline, startTime) { 77 | //记录上一次回调的时间戳 78 | var lastTick = +new Date(); 79 | 80 | timeline.startTime = startTime; 81 | nextTick.interval = timeline.interval; 82 | nextTick(); 83 | 84 | /** 85 | * 每一帧执行的函数 86 | */ 87 | function nextTick() { 88 | var now = +new Date(); 89 | 90 | timeline.animationHandler = requestAnimationFrame(nextTick); 91 | 92 | //如果当前时间与上一次回调的时间戳相差大于我们设置的间隔时间,表示可以执行一次回调函数。 93 | if (now - lastTick >= timeline.interval) { 94 | timeline.onenterframe(now - startTime); 95 | lastTick = now; 96 | } 97 | } 98 | } 99 | 100 | /** 101 | * raf 102 | */ 103 | var requestAnimationFrame = (function () { 104 | return window.requestAnimationFrame || 105 | window.webkitRequestAnimationFrame || 106 | window.mozRequestAnimationFrame || 107 | window.oRequestAnimationFrame || 108 | //所有都不支持,用setTimeout兼容 109 | function (callback) { 110 | return window.setTimeout(callback, (callback.interval || DEFAULT_INTERVAL)); // make interval as precise as possible. 111 | }; 112 | })(); 113 | 114 | /** 115 | * cancel raf 116 | */ 117 | var cancelAnimationFrame = (function () { 118 | return window.cancelAnimationFrame || 119 | window.webkitCancelAnimationFrame || 120 | window.mozCancelAnimationFrame || 121 | window.oCancelAnimationFrame || 122 | function (id) { 123 | window.clearTimeout(id); 124 | }; 125 | })(); 126 | 127 | module.exports = Timeline; -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { randomUniqueArr } from "./utils/base.ts" 2 | -------------------------------------------------------------------------------- /src/utils/assets.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取资源后缀 3 | * @param obj 4 | * @returns 5 | */ 6 | export const getAssetsType = async (obj: string) => { 7 | // 获取字符串中.之后的所有字符 8 | var index = obj.lastIndexOf("."); 9 | obj = obj.substring(index + 1, obj.length); 10 | return obj; 11 | }; 12 | 13 | /** 14 | * 已知宽高获取图片相对于屏幕的高度使得自适应 15 | * @param obj 16 | * @param targetWidth 17 | * @returns 18 | */ 19 | export const getMediaHeight = async (obj: any, targetWidth = 750) => { 20 | let ratio = obj.width / obj.height; 21 | let targetHeight = targetWidth / ratio; 22 | return (targetHeight + 2).toFixed(2); 23 | }; 24 | 25 | 26 | /** 27 | * 获取资源后缀 28 | * @param {*} file 29 | * @returns 30 | */ 31 | export const getType = (file) => { 32 | var index1 = filename.lastIndexOf("."); 33 | var index2 = file.length; 34 | var type = file.substring(index1, index2).toUpperCase(); 35 | return type; 36 | } 37 | -------------------------------------------------------------------------------- /src/utils/async/task.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 写一个函数,可以控制最大并发数 3 | */ 4 | function concurrentPoll() { 5 | this.tasks = []; 6 | this.max = 10; 7 | setTimeout(() => { 8 | this.run(); 9 | }, 0); 10 | } 11 | concurrentPoll.prototype.addTask = function (task) { 12 | this.tasks.push(task); 13 | }; 14 | concurrentPoll.prototype.run = function () { 15 | if (this.tasks.length == 0) { 16 | return; 17 | } 18 | var min = Math.min(this.tasks.length, max); 19 | for (var i = 0; i < min; i++) { 20 | this.max--; 21 | var task = this.tasks.shift(); 22 | task() 23 | .then((res) => { 24 | console.log(res); 25 | }) 26 | .catch((err) => { 27 | console.log(err); 28 | }) 29 | .finally(() => { 30 | this.max++; 31 | this.run(); 32 | }); 33 | } 34 | }; 35 | 36 | class PromisePool { 37 | constructor(max, fn) { 38 | this.max = max; //最大并发量 39 | this.fn = fn; //自定义的请求函数 40 | this.pool = []; //并发池 41 | this.urls = []; //剩余的请求地址 42 | } 43 | start(urls) { 44 | this.urls = urls; //先循环把并发池塞满 45 | while (this.pool.length < this.max) { 46 | let url = this.urls.shift(); 47 | this.setTask(url); 48 | } 49 | //利用Promise.race方法来获得并发池中某任务完成的信号 50 | let race = Promise.race(this.pool); 51 | return this.run(race); 52 | } 53 | run(race) { 54 | race 55 | .then(res => { 56 | //每当并发池跑完一个任务,就再塞入一个任务 57 | let url = this.urls.shift(); 58 | this.setTask(url); 59 | return this.run(Promise.race(this.pool)); 60 | }) 61 | } 62 | setTask(url) { 63 | if (!url) return 64 | let task = this.fn(url); 65 | this.pool.push(task); //将该任务推入pool并发池中 66 | console.log(`\x1B[43m ${url} 开始,当前并发数:${this.pool.length}`) 67 | task.then(res => { 68 | //请求结束后将该Promise任务从并发池中移除 69 | this.pool.splice(this.pool.indexOf(task), 1); 70 | console.log(`\x1B[43m ${url} 结束,当前并发数:${this.pool.length}`); 71 | }) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/utils/base.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 数组合并 3 | * @returns 4 | */ 5 | export const arrayConcat = function () { 6 | var tmpArr = []; 7 | for (let i = 0; i < arguments.length; i++) { 8 | tmpArr = tmpArr.concat(arguments[i]); 9 | } 10 | return tmpArr; 11 | }; 12 | 13 | /** 14 | * 删除数组指定部分 15 | * @returns 16 | */ 17 | export const arrayDrop = function (array, index, howmany) { 18 | if (!index) { 19 | index = 0; 20 | } 21 | if (!howmany) { 22 | howmany = 1; 23 | } 24 | array.splice(index, howmany); 25 | return array; 26 | }; 27 | 28 | /** 29 | * 检查数组是否包含 30 | * @returns 31 | */ 32 | export const arrayIndexOf = function (arr: Array, needFind: String | Number | Boolean | unknown) { 33 | var index = -1; 34 | for (let i = 0; i < arr.length; i++) { 35 | if (arr[i] == needFind) { 36 | index = i; 37 | return i; 38 | } 39 | } 40 | return index; 41 | }; 42 | 43 | /** 44 | * 检查数组不同 45 | * @returns 46 | */ 47 | export const arrayDifference = function (a, b) { 48 | const set = new Set(b); 49 | return a.filter((x) => !set.has(x)); 50 | }; 51 | 52 | export const arrayShuffle = function (arr) { 53 | let l = arr.length; 54 | while (l) { 55 | const i = Math.floor(Math.random() * l--); 56 | [arr[l], arr[i]] = [arr[i], arr[l]]; 57 | console.log(i); 58 | } 59 | return arr; 60 | }; 61 | 62 | export const arraySum = function (arr) { 63 | return arr.reduce((acc, val) => acc + val, 0); 64 | }; 65 | 66 | export const arrayAvg = function (arr) { 67 | return arr.reduce((acc, val) => acc + val, 0) / arr.length; 68 | }; 69 | 70 | export const arrayEach = function (arr, fun) { 71 | for (let i = 0; i < arr.length; i++) { 72 | fun(arr[i], i); 73 | } 74 | }; 75 | 76 | /** 77 | * 2数之间的随机数 78 | * @param min 79 | * @param max 80 | * @returns 81 | */ 82 | export const random = function (min: number, max: number): number { 83 | switch (arguments.length) { 84 | case 1: 85 | return parseInt(Math.random() * min + 1 + "", 10); 86 | break; 87 | case 2: 88 | return parseInt(Math.random() * (max - min + 1) + min + "", 10); 89 | break; 90 | default: 91 | return 0; 92 | } 93 | }; 94 | 95 | /** 96 | * 判断是不是一个空对象 97 | * @param obj 98 | * @returns 99 | */ 100 | export const isEmptyObj = function (obj) { 101 | return JSON.stringify(obj) === "{}"; 102 | }; 103 | 104 | /** 105 | * 判断是不是一个空对象 106 | * @param obj 107 | * @returns 108 | */ 109 | export const isPlainObject = (obj) => { 110 | return typeof obj == "object" && Object.getPrototypeOf(obj) === Object.prototype 111 | } 112 | 113 | /** 114 | * 生成一个不重复的随机数组 115 | * @param {number} len 数组长度 116 | * @param {number} min 最小随机数 117 | * @param {number} max 最大随机数 118 | * @return {array} 不重复的随机数组 119 | */ 120 | export const randomUniqueArr = (len, min, max) => { 121 | if (max - min < len) { 122 | return null; 123 | } 124 | const hash = []; 125 | 126 | while (hash.length < len) { 127 | const num = Math.floor(Math.random() * 100); 128 | 129 | if (hash.indexOf(num) === -1) { 130 | hash.push(num); 131 | } 132 | } 133 | return hash; 134 | }; 135 | 136 | 137 | /** 138 | * 判断以X开头 139 | * @param target 140 | * @param str 141 | * @param ignorecase 142 | * @returns 143 | */ 144 | export const startsWith = (target: String, str: string, ignorecase: boolean) => { 145 | var start_str = target.substr(0, str.length); 146 | return ignorecase ? start_str.toLowerCase() === str.toLowerCase() : start_str === str; 147 | } 148 | 149 | /** 150 | * 判断以X结尾 151 | * @param target 152 | * @param str 153 | * @param ignorecase 154 | * @returns 155 | */ 156 | export const endsWith = (target: String, str: string, ignorecase: boolean) => { 157 | var end_str = target.substring(target.length - str.length); 158 | return ignorecase ? end_str.toLowerCase() === str.toLowerCase() : end_str === str; 159 | } 160 | 161 | /** 162 | * limit方法,超出区间外,则取最近的区间边界值 163 | * @param target 164 | * @param n1 165 | * @param n2 166 | * @returns 167 | */ 168 | export const limitNumber = (target, n1, n2) => { 169 | var a = [n1, n2].sort() 170 | if (target < a[0]) return target = a[0] 171 | if (target > a[1]) return target = a[1] 172 | return target 173 | } 174 | 175 | /** 176 | * 分割数组 177 | * @param nums 178 | * @returns 179 | */ 180 | export const partitionDisjoint = (nums: string | any[]) => { 181 | let n = nums.length; 182 | let maxLeft = new Array(n).fill(Number.MIN_SAFE_INTEGER); 183 | let minRight = new Array(n).fill(Number.MAX_SAFE_INTEGER); 184 | maxLeft[0] = nums[0]; 185 | minRight[n - 1] = nums[n - 1]; 186 | for (let i = 1, j = n - 2; i < n; i++, j--) { 187 | maxLeft[i] = Math.max(nums[i], maxLeft[i - 1]); 188 | minRight[j] = Math.min(nums[j], minRight[j + 1]); 189 | } 190 | for (let i = 1; i < n; i++) { 191 | if (maxLeft[i - 1] <= minRight[i]) return i; 192 | } 193 | return -1; 194 | } 195 | 196 | /** 197 | * 判断是不是伪数组 198 | * @param obj 199 | * @returns 200 | */ 201 | export const isArrayList = (obj) => { 202 | var toString = Object.toString 203 | var rarraylike = /(ArraylLi stlColl ectionlMaplArguments)\]$/ 204 | var rfunction = /^\s*\bfunction\b/ 205 | if (!obj) { 206 | return false 207 | } 208 | 209 | const n = obj.length 210 | if (n === n >>> 0) { 211 | const type = toString.call(obj).slice(8, -1) 212 | if (rarraylike.test(type)) { 213 | return false 214 | } 215 | if (type === "Array") { 216 | return true 217 | } 218 | try { 219 | if (obj.property.isEnumerable.call(obj, 'length') === false) { 220 | return rfunction.test(obj.item || obj.callee) 221 | } 222 | } catch (e) { 223 | return !obj.window 224 | } 225 | } 226 | return false 227 | 228 | } 229 | 230 | /** 231 | * 比较2个NPM包的版本号 232 | * @param version1 233 | * @param version2 234 | * @returns 235 | */ 236 | export const compareVersion = function (version1, version2) { 237 | let a1 = version1.split("."); 238 | let a2 = version2.split("."); 239 | let i = 0; 240 | let result = 0; 241 | while (i < a1.length || i < a2.length) { 242 | a1[i] = a1[i] ? a1[i] / 1 : 0; 243 | a2[i] = a2[i] ? a2[i] / 1 : 0; 244 | if (a1[i] > a2[i]) { 245 | result = 1; 246 | break; 247 | } else if (a1[i] < a2[i]) { 248 | result = -1; 249 | break; 250 | } 251 | i++; 252 | } 253 | return result; 254 | }; 255 | 256 | /** 257 | * 手写trim方法 258 | * @param str 259 | */ 260 | export const trim = (str) => { 261 | var whitespace = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\n\ 262 | \u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000'; 263 | for (var i = 0; i < str.length; i++) { 264 | if (whitespace.indexOf(str.charAt(i)) === -1) { 265 | str = str.substring(i) 266 | break; 267 | } 268 | } 269 | 270 | for (i = str.length - 1; i >= 0; i--) { 271 | if (whitespace.indexOf(str.charAt(i)) === -1) { 272 | str = str.substring(0, i + 1) 273 | break; 274 | } 275 | } 276 | return whitespace.indexOf(str.charAt(0)) === -1 ? str : '' 277 | } 278 | 279 | 280 | /** 281 | * 连续数组 282 | * @param nums 283 | * @returns 284 | */ 285 | export const findMaxLengthFromArr = (nums) => { 286 | const n = nums.length; 287 | const map = new Map(); 288 | map.set(0, -1); 289 | let pre = 0; 290 | let res = 0; 291 | for (let i = 0; i < n; i++) { 292 | pre += nums[i] == 0 ? -1 : 1; 293 | if (map.has(pre)) { 294 | res = Math.max(res, i - map.get(pre)); 295 | } else { 296 | map.set(pre, i); 297 | } 298 | } 299 | return res; 300 | }; 301 | 302 | /** 303 | * 数组嵌套 304 | * @param nums 305 | * @returns 306 | */ 307 | export const arrayNesting = (nums: number[]): number => { 308 | const dfs = (idx: number): number => { 309 | if (nums[idx] == -1) { 310 | return 0 311 | } 312 | let nxt = nums[idx] 313 | nums[idx] = -1 314 | return 1 + dfs(nxt) 315 | } 316 | let ans = 0 317 | for (let i = 0; i < nums.length; i++) { 318 | ans = Math.max(ans, dfs(i)) 319 | } 320 | return ans 321 | }; 322 | 323 | /** 324 | * 替换数组中的元素 325 | * @param {number[]} nums 326 | * @param {number[][]} operations 327 | * @return {number[]} 328 | */ 329 | export const arrayChange = (nums, operations) => { 330 | const pos = new Map() 331 | for (let i = 0; i < nums.length; i++) { 332 | pos.set(nums[i], i) 333 | } 334 | for (const [prev, next] of operations) { 335 | const prePos = pos.get(prev) 336 | pos.delete(prev) 337 | pos.set(next, prePos) 338 | nums[prePos] = next 339 | } 340 | return nums 341 | }; 342 | 343 | /** 检查是不是数组 */ 344 | export const isArray = (arg) => { 345 | return Object.prototype.toString.call(arg) === '[object Array]' 346 | } 347 | 348 | export const browserType = () => { 349 | // 权重:系统 + 系统版本 > 平台 > 内核 + 载体 + 内核版本 + 载体版本 > 外壳 + 外壳版本 350 | const ua = navigator.userAgent.toLowerCase(); 351 | const testUa = regexp => regexp.test(ua); 352 | const testVs = regexp => (ua.match(regexp) + "") 353 | .replace(/[^0-9|_.]/ig, "") 354 | .replace(/_/ig, "."); 355 | 356 | // 系统 357 | let system = "unknown"; 358 | if (testUa(/windows|win32|win64|wow32|wow64/ig)) { 359 | system = "windows"; // window系统 360 | } else if (testUa(/macintosh|macintel/ig)) { 361 | system = "osx"; // osx系统 362 | } else if (testUa(/x11/ig)) { 363 | system = "linux"; // linux系统 364 | } else if (testUa(/android|adr/ig)) { 365 | system = "android"; // android系统 366 | } else if (testUa(/ios|iphone|ipad|ipod|iwatch/ig)) { 367 | system = "ios"; // ios系统 368 | } 369 | 370 | // 系统版本 371 | let systemVs = "unknown"; 372 | if (system === "windows") { 373 | if (testUa(/windows nt 5.0|windows 2000/ig)) { 374 | systemVs = "2000"; 375 | } else if (testUa(/windows nt 5.1|windows xp/ig)) { 376 | systemVs = "xp"; 377 | } else if (testUa(/windows nt 5.2|windows 2003/ig)) { 378 | systemVs = "2003"; 379 | } else if (testUa(/windows nt 6.0|windows vista/ig)) { 380 | systemVs = "vista"; 381 | } else if (testUa(/windows nt 6.1|windows 7/ig)) { 382 | systemVs = "7"; 383 | } else if (testUa(/windows nt 6.2|windows 8/ig)) { 384 | systemVs = "8"; 385 | } else if (testUa(/windows nt 6.3|windows 8.1/ig)) { 386 | systemVs = "8.1"; 387 | } else if (testUa(/windows nt 10.0|windows 10/ig)) { 388 | systemVs = "10"; 389 | } 390 | } else if (system === "osx") { 391 | systemVs = testVs(/os x [\d._]+/ig); 392 | } else if (system === "android") { 393 | systemVs = testVs(/android [\d._]+/ig); 394 | } else if (system === "ios") { 395 | systemVs = testVs(/os [\d._]+/ig); 396 | } 397 | 398 | // 平台 399 | let platform = "unknow"; 400 | if (system === "windows" || system === "osx" || system === "linux") { 401 | platform = "desktop"; // 桌面端 402 | } else if (system === "android" || system === "ios" || testUa(/mobile/ig)) { 403 | platform = "mobile"; // 移动端 404 | } 405 | 406 | // 内核和载体 407 | let engine = "unknow"; 408 | let supporter = "unknow"; 409 | if (testUa(/applewebkit/ig) && testUa(/safari/ig)) { 410 | engine = "webkit"; // webkit内核 411 | if (testUa(/edge/ig)) { 412 | supporter = "edge"; // edge浏览器 413 | } else if (testUa(/opr/ig)) { 414 | supporter = "opera"; // opera浏览器 415 | } else if (testUa(/chrome/ig)) { 416 | supporter = "chrome"; // chrome浏览器 417 | } else { 418 | supporter = "safari"; // safari浏览器 419 | } 420 | } else if (testUa(/gecko/ig) && testUa(/firefox/ig)) { 421 | engine = "gecko"; // gecko内核 422 | supporter = "firefox"; // firefox浏览器 423 | } else if (testUa(/presto/ig)) { 424 | engine = "presto"; // presto内核 425 | supporter = "opera"; // opera浏览器 426 | } else if (testUa(/trident|compatible|msie/ig)) { 427 | engine = "trident"; // trident内核 428 | supporter = "iexplore"; // iexplore浏览器 429 | } 430 | 431 | // 内核版本 432 | let engineVs = "unknow"; 433 | if (engine === "webkit") { 434 | engineVs = testVs(/applewebkit\/[\d.]+/ig); 435 | } else if (engine === "gecko") { 436 | engineVs = testVs(/gecko\/[\d.]+/ig); 437 | } else if (engine === "presto") { 438 | engineVs = testVs(/presto\/[\d.]+/ig); 439 | } else if (engine === "trident") { 440 | engineVs = testVs(/trident\/[\d.]+/ig); 441 | } 442 | 443 | // 载体版本 444 | let supporterVs = "unknow"; 445 | if (supporter === "chrome") { 446 | supporterVs = testVs(/chrome\/[\d.]+/ig); 447 | } else if (supporter === "safari") { 448 | supporterVs = testVs(/version\/[\d.]+/ig); 449 | } else if (supporter === "firefox") { 450 | supporterVs = testVs(/firefox\/[\d.]+/ig); 451 | } else if (supporter === "opera") { 452 | supporterVs = testVs(/opr\/[\d.]+/ig); 453 | } else if (supporter === "iexplore") { 454 | supporterVs = testVs(/(msie [\d.]+)|(rv:[\d.]+)/ig); 455 | } else if (supporter === "edge") { 456 | supporterVs = testVs(/edge\/[\d.]+/ig); 457 | } 458 | 459 | // 外壳和外壳版本 460 | let shell = "none"; 461 | let shellVs = "unknow"; 462 | if (testUa(/micromessenger/ig)) { 463 | shell = "wechat"; // 微信浏览器 464 | shellVs = testVs(/micromessenger\/[\d.]+/ig); 465 | } else if (testUa(/qqbrowser/ig)) { 466 | shell = "qq"; // QQ浏览器 467 | shellVs = testVs(/qqbrowser\/[\d.]+/ig); 468 | } else if (testUa(/ubrowser/ig)) { 469 | shell = "uc"; // UC浏览器 470 | shellVs = testVs(/ubrowser\/[\d.]+/ig); 471 | } else if (testUa(/2345explorer/ig)) { 472 | shell = "2345"; // 2345浏览器 473 | shellVs = testVs(/2345explorer\/[\d.]+/ig); 474 | } else if (testUa(/metasr/ig)) { 475 | shell = "sougou"; // 搜狗浏览器 476 | } else if (testUa(/lbbrowser/ig)) { 477 | shell = "liebao"; // 猎豹浏览器 478 | } else if (testUa(/maxthon/ig)) { 479 | shell = "maxthon"; // 遨游浏览器 480 | shellVs = testVs(/maxthon\/[\d.]+/ig); 481 | } else if (testUa(/bidubrowser/ig)) { 482 | shell = "baidu"; // 百度浏览器 483 | shellVs = testVs(/bidubrowser [\d.]+/ig); 484 | } 485 | 486 | return Object.assign({ 487 | engine, // webkit gecko presto trident 488 | engineVs, 489 | platform, // desktop mobile 490 | supporter, // chrome safari firefox opera iexplore edge 491 | supporterVs, 492 | system, // windows osx linux android ios 493 | systemVs 494 | }, shell === "none" ? {} : { 495 | shell, // wechat qq uc 2345 sougou liebao maxthon baidu 496 | shellVs 497 | }); 498 | } 499 | 500 | 501 | 502 | -------------------------------------------------------------------------------- /src/utils/cache/BaseCache.ts: -------------------------------------------------------------------------------- 1 | export const setSession = (name, val) => { 2 | return sessionStorage.setItem(name, JSON.stringify({ v: val })); 3 | }; 4 | 5 | export const getSession = (name) => { 6 | if (!sessionStorage.getItem(name)) { 7 | return ""; 8 | } else { 9 | let v = ""; 10 | try { 11 | v = JSON.parse(sessionStorage.getItem(name)).v; 12 | if (v === undefined) { 13 | v = JSON.parse(sessionStorage.getItem(name)); 14 | } 15 | } catch (e) { 16 | v = sessionStorage.getItem(name); 17 | } 18 | return v; 19 | } 20 | }; 21 | 22 | export const setLocal = (name, val) => { 23 | return localStorage.setItem(name, JSON.stringify({ v: val })); 24 | }; 25 | 26 | export const getLocal = (name) => { 27 | if (!JSON.parse(localStorage.getItem(name))) { 28 | return ""; 29 | } else { 30 | let v = ""; 31 | try { 32 | v = JSON.parse(localStorage.getItem(name)).v; 33 | if (v === undefined) { 34 | v = JSON.parse(localStorage.getItem(name)); 35 | } 36 | } catch (e) { 37 | v = localStorage.getItem(name); 38 | } 39 | return v; 40 | } 41 | }; 42 | 43 | export const sessionClear = () => { 44 | return sessionStorage.clear(); 45 | }; 46 | 47 | export const localClear = () => { 48 | return localStorage.clear(); 49 | }; 50 | 51 | export const removeSession = (name) => { 52 | return sessionStorage.removeItem(name); 53 | }; 54 | 55 | export const getCookit = (key) => { 56 | if (document.cookie.length > 0) { 57 | let c_start = document.cookie.indexOf(key + "="); 58 | if (c_start != -1) { 59 | c_start = c_start + key.length + 1; 60 | let c_end = document.cookie.indexOf(";", c_start); 61 | if (c_end == -1) { 62 | c_end = document.cookie.length; 63 | } 64 | return unescape(document.cookie.substring(c_start, c_end)); 65 | } 66 | } 67 | return ""; 68 | }; 69 | 70 | export const getCookie = (key, cookies) => { 71 | var value = ""; 72 | var isInBrowser = typeof window !== "undefined"; 73 | if (!cookies) { 74 | if (isInBrowser) { 75 | cookies = document.cookie; 76 | } else { 77 | return value; 78 | } 79 | } 80 | var cookieArr = cookies.split("; "); 81 | for (var i = 0, cookie = void 0, index = void 0; i < cookieArr.length; i++) { 82 | cookie = cookieArr[i]; 83 | index = cookie.indexOf("="); 84 | if (cookie.substr(0, index) === key) { 85 | value = cookie.substr(index + 1); 86 | } 87 | } 88 | return value; 89 | }; 90 | -------------------------------------------------------------------------------- /src/utils/cache/BaseLocalStorage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 兼容浏览器和小程序的缓存方法 3 | */ 4 | export abstract class BaseLocalStorage { 5 | keyPrefix = ""; 6 | abstract getItem(key: string): T | undefined; 7 | abstract setItem(key: string, value: any): void; 8 | abstract removeItem(key: string): void; 9 | abstract clear(): void; 10 | abstract keys(): string[]; 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/cache/LocalStorageBrowser.ts: -------------------------------------------------------------------------------- 1 | import { BaseLocalStorage } from "./BaseLocalStorage"; 2 | 3 | export class LocalStorageBrowser extends BaseLocalStorage { 4 | setItem(key: string, value: unknown) { 5 | localStorage.setItem(this.keyPrefix + key, JSON.stringify(value)); 6 | } 7 | 8 | getItem(key: string) { 9 | let raw = localStorage.getItem(this.keyPrefix + key); 10 | try { 11 | if (raw == undefined) { 12 | return undefined; 13 | } else { 14 | return JSON.parse(raw); 15 | } 16 | } catch (e) { 17 | console.warn( 18 | "Parse JSON Error when getItem from localStorage: " + key, 19 | raw, 20 | e 21 | ); 22 | return undefined; 23 | } 24 | } 25 | 26 | removeItem(key: string): void { 27 | localStorage.removeItem(this.keyPrefix + key); 28 | } 29 | 30 | clear(): void { 31 | localStorage.clear(); 32 | } 33 | 34 | keys(): string[] { 35 | return Object.keys(localStorage) 36 | .filter((v) => v.indexOf(this.keyPrefix) === 0) 37 | .map((v) => v.substr(this.keyPrefix.length)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/utils/cache/LocalStorageMiniApp.ts: -------------------------------------------------------------------------------- 1 | import { BaseLocalStorage } from "./BaseLocalStorage"; 2 | 3 | export class LocalStorageMiniapp extends BaseLocalStorage { 4 | private _miniAppObj: any; 5 | 6 | constructor(miniAppObj: any) { 7 | super(); 8 | this._miniAppObj = miniAppObj; 9 | } 10 | 11 | // 同步 12 | setItem(key: string, value: unknown) { 13 | return this._miniAppObj.setStorageSync( 14 | this.keyPrefix + key, 15 | JSON.stringify(value) 16 | ); 17 | } 18 | 19 | getItem(key: string) { 20 | let raw = this._miniAppObj.getStorageSync(this.keyPrefix + key); 21 | try { 22 | if (raw == undefined || raw == "") { 23 | return undefined; 24 | } else { 25 | return JSON.parse(raw); 26 | } 27 | } catch (e) { 28 | console.warn( 29 | "Parse JSON Error when getItem from localStorage: " + key, 30 | raw, 31 | e 32 | ); 33 | return undefined; 34 | } 35 | } 36 | 37 | removeItem(key: string) { 38 | this._miniAppObj.removeStorageSync(this.keyPrefix + key); 39 | } 40 | 41 | clear() { 42 | this._miniAppObj.clearStorageSync(); 43 | } 44 | 45 | keys() { 46 | return (this._miniAppObj.getStorageInfoSync().keys as string[]) 47 | .filter((v) => v.indexOf(this.keyPrefix) === 0) 48 | .map((v) => v.substr(this.keyPrefix.length)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/utils/car.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 验证车牌号 3 | * @param val 4 | * @returns 5 | */ 6 | export const isCarNum = (val: string) => { 7 | var patrn = /^([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1})$/ 8 | var patrn2 = /^([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF])|([DF]([A-HJ-NP-Z0-9])[0-9]{4})))$/ 9 | if (!patrn.test(val) && !patrn2.test(val)) { 10 | return false 11 | } else { 12 | return true 13 | } 14 | } 15 | 16 | /** 17 | * 校验车架号 18 | * @param val 19 | * @returns 20 | */ 21 | export const isVehicle = (val: string) => { 22 | var patrn = /^[A-HJ-NP-Za-hj-np-z0-9]+$/ 23 | if (!patrn.test(val) || val === '') { 24 | return false 25 | } else { 26 | return true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/utils/client.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断是否在服务端 3 | * @type {Boolean} 4 | */ 5 | export const isServer = (() => { 6 | const ret = typeof window === "undefined"; 7 | return () => ret; 8 | })(); 9 | 10 | /** 11 | * 判断是否在客户端 12 | * @type {Boolean} 13 | */ 14 | export const isClient = () => { 15 | return !isServer(); 16 | }; 17 | 18 | /** 19 | * 取url参数 20 | * @param {String} name url参数 21 | * @param {Boolean} decodeSearch 是否对location.search整体解码一次, 默认true 22 | * @return {String} url值 23 | */ 24 | export const getQuery = (name = "", decodeSearch = true) => { 25 | const reg = new RegExp("(?:^|&)" + name + "=([^&]*)(?:&|$)", "i"); 26 | let search = location.search; 27 | search = decodeSearch ? decodeURIComponent(search) : search; 28 | const ret = search.substr(1).match(reg); 29 | if (ret) return decodeURIComponent(ret[1]); 30 | return ""; 31 | }; 32 | 33 | /** 34 | * 获取所有url参数 35 | * @param {Array} 需要排除的参数数组 36 | * @return {Object} 参数键值对 37 | */ 38 | export const getQueryAll = (exclude = []) => { 39 | if (typeof window !== "undefined") { 40 | const search: any = window.location.search; 41 | const paramArr = 42 | search !== "" ? search.split("?").pop().split("&") : []; 43 | const paramObj = {}; 44 | paramArr.forEach(entry => { 45 | const [key, value] = entry.split("="); 46 | if (!exclude.includes(key)) { 47 | paramObj[key] = decodeURIComponent(value); 48 | } 49 | }); 50 | return paramObj; 51 | } else { 52 | return "" 53 | } 54 | }; 55 | 56 | /** 57 | * object转query 58 | * @param {Object} object参数 59 | * @return {String} query参数 60 | * @example {x:1,y:2}转化为x=1&y=2 61 | */ 62 | export const formatQuery = (obj: string | object) => { 63 | const retArr = Object.entries(obj).map(([key, val]) => { 64 | let queryVal = ""; 65 | if (typeof val === "object") { 66 | queryVal = JSON.stringify(val); 67 | } else { 68 | queryVal = val; 69 | } 70 | const retStr = `${key}=${encodeURIComponent(queryVal)}`; 71 | return retStr; 72 | }); 73 | return retArr.join("&"); 74 | }; 75 | 76 | /** 77 | * 判断是否在iOS中 78 | * @return {Boolean} 79 | */ 80 | export const isInIOS = (() => { 81 | if (isClient()) { 82 | const ua = navigator.userAgent; 83 | const ret = !!ua.match(/iphone|ipad|ipod/i); 84 | return () => ret; 85 | } 86 | })(); 87 | 88 | /** 89 | * 判断是否在微信中(包含微信H5, 微信小程序) 90 | * @type {Boolean} 91 | */ 92 | export const isInWechat = (() => { 93 | if (isClient()) { 94 | const ua = navigator.userAgent; 95 | const ret = !!ua.match(/micromessenger/i); 96 | return () => ret; 97 | } 98 | })(); 99 | 100 | /** 101 | * 判断是否在快应用 102 | */ 103 | export const isInQuickApp = (() => { 104 | const ua = navigator.userAgent; 105 | const ret = ua.includes("mode-quickapp"); 106 | return () => ret; 107 | })(); 108 | 109 | /** 110 | * 判断是否在百度小程序 111 | */ 112 | export const isInBaidu = (() => { 113 | const ua = navigator.userAgent; 114 | const ret = /swan\//.test(ua) || /^webswan-/.test((window as any).name); 115 | return () => ret; 116 | })(); 117 | 118 | /** 119 | * 判断是否在头条小程序 120 | */ 121 | export const isInToutiao = (() => { 122 | const ua = navigator.userAgent; 123 | const ret = !!ua.match(/toutiaomicroapp/i); 124 | return () => ret; 125 | })(); 126 | 127 | /** 128 | * 判断是否在支付宝中 129 | */ 130 | export const isInAlipay = (() => { 131 | const ua = navigator.userAgent; 132 | const ret = !!ua.match(/AlipayClient/i); 133 | return () => ret; 134 | })(); 135 | 136 | /** 137 | * 判断是否在支付宝小程序 138 | */ 139 | export const isInAlipayMp = (() => { 140 | const ua = navigator.userAgent; 141 | const ret = isInAlipay() && !!ua.match(/MiniProgram/i); 142 | return () => ret; 143 | })(); 144 | 145 | /** 146 | * 判断是否在支付宝H5中 147 | */ 148 | export const isInAlipayH5 = (() => { 149 | const ret = isInAlipay() && !isInAlipayMp(); 150 | return () => ret; 151 | })(); 152 | 153 | /** 154 | * 判断是否在QQ小程序 155 | */ 156 | export const isInQQ = (() => { 157 | const ua = navigator.userAgent; 158 | const ret = ua.includes("qq"); 159 | return () => ret; 160 | })(); 161 | 162 | /** 163 | * 判断是否需要适配iphoneX 164 | */ 165 | export const isIPhoneXSeries = (() => { 166 | const ret = (() => { 167 | const ua = navigator.userAgent; 168 | 169 | if (ua.match(/iphone/i)) { 170 | const xSeriesConfig = [ 171 | { 172 | devicePixelRatio: 3, // iPhone X, iPhone Xs 173 | width: 375, 174 | height: 812, 175 | }, 176 | { 177 | devicePixelRatio: 2, // iPhone XR, iPhone 11 178 | width: 414, 179 | height: 896, 180 | }, 181 | { 182 | devicePixelRatio: 3, // iPhone Xs Max, iPhone 11 Pro Max 183 | width: 414, 184 | height: 896, 185 | }, 186 | { 187 | devicePixelRatio: 3, // iPhone 11 Pro 188 | width: 375, 189 | height: 812, 190 | }, 191 | { 192 | devicePixelRatio: 3, // iPhone 12 mini 193 | width: 360, 194 | height: 780, 195 | }, 196 | { 197 | devicePixelRatio: 3, // iPhone 12, iPhone 12 Pro 198 | width: 390, 199 | height: 844, 200 | }, 201 | { 202 | devicePixelRatio: 3, // iPhone 12 Pro Max 203 | width: 428, 204 | height: 926, 205 | }, 206 | ]; 207 | const { 208 | devicePixelRatio, 209 | screen: { width, height }, 210 | } = window; 211 | return xSeriesConfig.some( 212 | (item) => 213 | item.devicePixelRatio === devicePixelRatio && 214 | item.width === width && 215 | item.height === height 216 | ); 217 | } 218 | return false; 219 | })(); 220 | return () => ret; 221 | })(); 222 | 223 | /** 224 | * 获取窗口可视范围的高度 225 | * @returns 226 | */ 227 | export function getClientHeight() { 228 | let clientHeight = 0; 229 | if (document.body.clientHeight && document.documentElement.clientHeight) { 230 | clientHeight = 231 | document.body.clientHeight < document.documentElement.clientHeight 232 | ? document.body.clientHeight 233 | : document.documentElement.clientHeight; 234 | } else { 235 | clientHeight = 236 | document.body.clientHeight > document.documentElement.clientHeight 237 | ? document.body.clientHeight 238 | : document.documentElement.clientHeight; 239 | } 240 | return clientHeight; 241 | } 242 | 243 | /** 244 | * 获取窗口可视范围宽度 245 | * @returns 246 | */ 247 | export function getPageViewWidth() { 248 | let d = document, 249 | a = d.compatMode == "BackCompat" ? d.body : d.documentElement; 250 | return a.clientWidth; 251 | } 252 | 253 | /** 254 | * 获取窗口宽度 255 | * @returns 256 | */ 257 | export function getPageWidth() { 258 | let g = document, 259 | a = g.body, 260 | f = g.documentElement, 261 | d = g.compatMode == "BackCompat" ? a : g.documentElement; 262 | return Math.max(f.scrollWidth, a.scrollWidth, d.clientWidth); 263 | } 264 | 265 | /** 266 | * 获取窗口尺寸 267 | * @returns 268 | */ 269 | export function getViewportOffset() { 270 | if (window.innerWidth) { 271 | return { 272 | w: window.innerWidth, 273 | h: window.innerHeight, 274 | }; 275 | } else { 276 | // ie8及其以下 277 | if (document.compatMode === "BackCompat") { 278 | // 怪异模式 279 | return { 280 | w: document.body.clientWidth, 281 | h: document.body.clientHeight, 282 | }; 283 | } else { 284 | // 标准模式 285 | return { 286 | w: document.documentElement.clientWidth, 287 | h: document.documentElement.clientHeight, 288 | }; 289 | } 290 | } 291 | } 292 | 293 | /** 294 | * 获取滚动条距顶部高度 295 | * @returns 296 | */ 297 | export function getPageScrollTop() { 298 | let a = document; 299 | return a.documentElement.scrollTop || a.body.scrollTop; 300 | } 301 | 302 | /** 303 | * 获取滚动条距左边的高度 304 | * @returns 305 | */ 306 | export function getPageScrollLeft() { 307 | let a = document; 308 | return a.documentElement.scrollLeft || a.body.scrollLeft; 309 | } 310 | 311 | /** 312 | * 开启全屏 313 | * @param {*} element 314 | */ 315 | export function launchFullscreen(element) { 316 | if (element.requestFullscreen) { 317 | element.requestFullscreen(); 318 | } else if (element.mozRequestFullScreen) { 319 | element.mozRequestFullScreen(); 320 | } else if (element.msRequestFullscreen) { 321 | element.msRequestFullscreen(); 322 | } else if (element.webkitRequestFullscreen) { 323 | element.webkitRequestFullScreen(); 324 | } 325 | } 326 | 327 | /** 328 | * 滚动到指定元素区域 329 | */ 330 | export const smoothScroll = (element) => { 331 | document.querySelector(element).scrollIntoView({ 332 | behavior: "smooth", 333 | }); 334 | }; 335 | 336 | /** 337 | * http跳转https 338 | */ 339 | export const scrollToTop = () => { 340 | const c = document.documentElement.scrollTop || document.body.scrollTop; 341 | if (c > 0) { 342 | window.requestAnimationFrame(scrollToTop); 343 | window.scrollTo(0, c - c / 8); 344 | } 345 | }; 346 | 347 | /** 348 | * 检查页面底部是否可见 349 | * @returns 350 | */ 351 | export const bottomVisible = () => { 352 | return ( 353 | document.documentElement.clientHeight + window.scrollY >= 354 | (document.documentElement.scrollHeight || 355 | document.documentElement.clientHeight) 356 | ); 357 | }; 358 | 359 | /** 360 | * 打开一个窗口 361 | * @param { string } url 362 | * @param { string } windowName 363 | * @param { number } width 364 | * @param { number } height 365 | */ 366 | export function openWindow( 367 | url: string, 368 | windowName: string, 369 | width: number, 370 | height: number 371 | ) { 372 | var x = parseInt(`${screen.width / 2.0}`) - width / 2.0; 373 | var y = parseInt(`${screen.height / 2.0}`) - height / 2.0; 374 | var isMSIE = navigator.appName == "Microsoft Internet Explorer"; 375 | if (isMSIE) { 376 | var p = "resizable=1,location=no,scrollbars=no,width="; 377 | p = p + width; 378 | p = p + ",height="; 379 | p = p + height; 380 | p = p + ",left="; 381 | p = p + x; 382 | p = p + ",top="; 383 | p = p + y; 384 | window.open(url, windowName, p); 385 | } else { 386 | var win = window.open( 387 | url, 388 | "ZyiisPopup", 389 | "top=" + 390 | y + 391 | ",left=" + 392 | x + 393 | ",scrollbars=" + 394 | scrollbars + 395 | ",dialog=yes,modal=yes,width=" + 396 | width + 397 | ",height=" + 398 | height + 399 | ",resizable=no" 400 | ); 401 | eval("try { win.resizeTo(width, height); } catch(e) { }"); 402 | window.focus(); 403 | } 404 | } 405 | 406 | /** 407 | * 客户端检测,并返回客户端的详细信息 408 | * @param w 409 | * @param nav 410 | * @returns 411 | */ 412 | export const clientInfo = (w, nav) => { 413 | var _Client = function () { 414 | // 呈现引擎 415 | this.engine = {}; 416 | // 浏览器 417 | this.brower = {}; 418 | // 系统平台 419 | this.system = {}; 420 | // 初始化 421 | this.init(); 422 | }; 423 | _Client.prototype.init = function () { 424 | var ua = nav.userAgent, 425 | p = nav.platform; 426 | // 检测呈现引擎和浏览器 427 | if (w.opera) { 428 | // opera 429 | this.engine.name = this.brower.name = "opera"; 430 | this.engine.ver = this.brower.ver = w.opera.version(); 431 | } else if (/AppleWebKit\/(\S+)/.test(ua)) { 432 | this.engine.name = "webkit"; 433 | this.engine.ver = RegExp["$1"]; 434 | if (/Chrome\/(\S+)/.test(ua)) { 435 | // chrome 436 | this.brower.name = "chrome"; 437 | this.brower.ver = RegExp["$1"]; 438 | } else if (/Version\/(\S)+/.test(ua)) { 439 | // safari 440 | this.brower.name = "safari"; 441 | this.brower.ver = RegExp["$1"]; 442 | } else { 443 | // 近似确定版本号 444 | var safariVersion = 1, 445 | webkitVersion = parseFloat(this.engine.ver); 446 | if (webkitVersion < 100) { 447 | safariVersion = 1; 448 | } else if (webkitVersion < 312) { 449 | safariVersion = 1.2; 450 | } else if (webkitVersion < 412) { 451 | safariVersion = 1.3; 452 | } else { 453 | safariVersion = 2; 454 | } 455 | this.brower.name = "safari"; 456 | this.brower.ver = safariVersion; 457 | } 458 | } else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)) { 459 | // konq 460 | this.engine.name = "khtml"; 461 | this.brower.name = "konq"; 462 | this.engine.ver = this.brower.ver = RegExp["$1"]; 463 | } else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) { 464 | this.engine.name = "gecko"; 465 | this.engine.ver = RegExp["$1"]; 466 | if (/Firefox\/(\S+)/.test(ua)) { 467 | // firefox 468 | this.brower.name = "firefox"; 469 | this.brower.ver = RegExp["$1"]; 470 | } else { 471 | this.brower.name = null; 472 | this.brower.ver = null; 473 | } 474 | } else if (/MSIE ([^;]+)/.test(ua)) { 475 | // IE 476 | this.engine.name = this.brower.name = "ie"; 477 | this.engine.ver = this.brower.ver = RegExp["$1"]; 478 | } else { 479 | this.engine.name = this.brower.name = null; 480 | this.engine.ver = this.brower.ver = null; 481 | } 482 | 483 | // 检测系统平台 484 | // 移动设备 485 | if (ua.indexOf("iPhone") > -1) { 486 | this.system.mobile = {}; 487 | this.system.mobile.name = "iphone"; 488 | this.system.mobile.ver = null; 489 | } else if (ua.indexOf("iPod") > -1) { 490 | this.system.mobile = {}; 491 | this.system.mobile.name = "ipod"; 492 | this.system.mobile.ver = null; 493 | } else if (ua.indexOf("iPad") > -1) { 494 | this.system.mobile = {}; 495 | this.system.mobile.name = "ipad"; 496 | this.system.mobile.ver = null; 497 | } else if (ua.indexOf("NokiaN") > -1) { 498 | this.system.mobile = {}; 499 | this.system.mobile.name = "nokian"; 500 | this.system.mobile.ver = null; 501 | } else if (/Android (\d+\.\d+)/.test(ua)) { 502 | this.system.mobile = {}; 503 | this.system.mobile.name = "android"; 504 | this.system.mobile.ver = RegExp["$1"]; 505 | } 506 | // 游戏设备 507 | if (ua.indexOf("Wii") > -1) { 508 | this.system.game = {}; 509 | this.system.game.name = "wii"; 510 | this.system.game.ver = null; 511 | } else if (/playstation/i.test(ua)) { 512 | this.system.game = {}; 513 | this.system.game.name = "ps"; 514 | this.system.game.ver = null; 515 | } 516 | 517 | if (p.indexOf("Win") == 0) { 518 | // Windows 519 | this.system.name = "win"; 520 | if (/Win(?:dows )?([^do]{2})\s?(\d+\.\d+)?/.test(ua)) { 521 | if (RegExp["$1"] == "NT") { 522 | switch (RegExp["$2"]) { 523 | case "5.0": 524 | this.system.ver = "2000"; 525 | break; 526 | case "5.1": 527 | this.system.ver = "XP"; 528 | break; 529 | case "6.0": 530 | this.system.ver = "Vista"; 531 | break; 532 | case "6.1": 533 | this.system.ver = "7"; 534 | break; 535 | default: 536 | this.system.ver = "NT"; 537 | } 538 | } else if (RegExp["$1"] == "9x") { 539 | this.system.ver = "ME"; 540 | } else { 541 | this.system.ver = RegExp["$1"]; 542 | } 543 | // 检测Windows CE或Windows Phone 544 | if (this.system.ver == "CE") { 545 | this.system.mobile = {}; 546 | this.system.mobile.name = "ce"; 547 | this.system.mobile.ver = "CE"; 548 | } else if (this.system.ver == "Ph") { 549 | if (/Windows Phone OS (\d+.\d+)/.test(ua)) { 550 | this.system.ver = "Phone"; 551 | this.system.mobile = {}; 552 | this.system.mobile.name = "phone"; 553 | this.system.mobile.ver = RegExp["$1"]; 554 | } 555 | } 556 | } 557 | } else if (p.indexOf("Mac") == 0) { 558 | // Mac 559 | this.system.name = "mac"; 560 | this.system.ver = null; 561 | // 检测IOS版本号 562 | if (ua.indexOf("Mobile") > -1) { 563 | if (/CPU (?:iPhone )?OS (\d+_\d+)/.test(ua)) { 564 | this.system.mobile.ver = RegExp.$1.replace("_", "."); 565 | } else { 566 | this.system.mobile.ver = 2; 567 | } 568 | } 569 | } else if (p == "X11" || p.indexOf("Linux") == 0) { 570 | // Unix or Linux 571 | this.system.name = "x11"; 572 | this.system.ver = null; 573 | } 574 | }; 575 | // 对象是否包含此方法 576 | _Client.prototype.isHostMethod = function (object, property) { 577 | // author: Peter Michaux 578 | var t = typeof object[property]; 579 | return ( 580 | t == "function" || !!(t == "object" && object[property]) || t == "object" 581 | ); 582 | }; 583 | // 是否是移动设备 584 | _Client.prototype.isMobile = function () { 585 | var t = typeof this.system.mobile; 586 | return t != "undefined"; 587 | }; 588 | return _Client; 589 | }; 590 | 591 | 592 | /** 593 | * 获取浏览器光标信息 594 | * @param win 595 | * @returns 596 | */ 597 | export const getSelectionCoords = (win) => { 598 | win = win || window; 599 | var doc = win.document; 600 | var sel = doc.selection, range, rects, rect; 601 | var x = 0, y = 0; 602 | if (sel) { 603 | if (sel.type != "Control") { 604 | range = sel.createRange(); 605 | range.collapse(true); 606 | x = range.boundingLeft; 607 | y = range.boundingTop; 608 | } 609 | } else if (win.getSelection) { 610 | sel = win.getSelection(); 611 | if (sel.rangeCount) { 612 | range = sel.getRangeAt(0).cloneRange(); 613 | if (range.getClientRects) { 614 | range.collapse(true); 615 | rects = range.getClientRects(); 616 | if (rects.length > 0) { 617 | rect = rects[0]; 618 | } 619 | if (rect) { 620 | x = rect.left; 621 | y = rect.top; 622 | } 623 | } 624 | if ((x == 0 && y == 0) || rect === undefined) { 625 | var span = doc.createElement("span"); 626 | if (span.getClientRects) { 627 | span.appendChild(doc.createTextNode("\u200b")); 628 | range.insertNode(span); 629 | rect = span.getClientRects()[0]; 630 | x = rect.left; 631 | y = rect.top; 632 | var spanParent = span.parentNode; 633 | spanParent.removeChild(span); 634 | spanParent.normalize(); 635 | } 636 | } 637 | } 638 | } 639 | return { x: x, y: y }; 640 | } 641 | -------------------------------------------------------------------------------- /src/utils/common.ts: -------------------------------------------------------------------------------- 1 | /** 拼接秒数 */ 2 | export const formatNumber = (n) => { 3 | const s = n.toString(); 4 | return s[1] ? s : "0" + s; 5 | }; 6 | 7 | /** 过滤表情 */ 8 | export const filterEmoji = (name = "") => { 9 | if (name) { 10 | const str = name.replace( 11 | /[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/gi, 12 | "" 13 | ); 14 | return str; 15 | } 16 | return ""; 17 | }; 18 | 19 | /** 验证税号 */ 20 | export const isNumber = (num) => 21 | /^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/.test(num); 22 | 23 | /** 验证手机号 */ 24 | export const isPhone = (phone) => /^[1][0-9]{10}$/.test(phone); 25 | 26 | /** 验证邮箱 */ 27 | export const isEmail = (email) => 28 | /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/.test( 29 | email 30 | ); 31 | 32 | /** 对象数组根据对象某一个属性去重 */ 33 | export const unique = (arr, key, hash = {}) => 34 | arr.reduce(function (item, next) { 35 | hash[next[key]] ? "" : (hash[next[key]] = true && item.push(next)); 36 | return item; 37 | }, []); 38 | 39 | // 验证emoji表情 40 | export const hasEmoji = function (value, tips = "") { 41 | let char = 42 | /[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/gi; 43 | if (char.test(value)) { 44 | console.log(`${tips}不能含有特殊字符`); 45 | return true; 46 | } 47 | return false; 48 | }; 49 | 50 | /** 保留N位小数 */ 51 | export const getFloat = function (number, n) { 52 | n = n ? parseInt(n) : 0; 53 | if (n <= 0) { 54 | return Math.round(number); 55 | } 56 | number = Math.round(number * Math.pow(10, n)) / Math.pow(10, n); 57 | number = Number(number).toFixed(n); 58 | return number; 59 | }; 60 | 61 | /** 时间戳转 YY-mm-dd HH:ii:ss */ 62 | export const timeStrtoUtc = function (timeStamp, returnType) { 63 | timeStamp = parseInt(timeStamp); 64 | var date = new Date(); 65 | if (timeStamp < 90000000000) { 66 | date.setTime(timeStamp * 1000); 67 | } else { 68 | date.setTime(timeStamp); 69 | } 70 | var y = date.getFullYear(); 71 | var m: any = date.getMonth() + 1; 72 | m = m < 10 ? "0" + m : m; 73 | var d: any = date.getDate(); 74 | d = d < 10 ? "0" + d : d; 75 | var h: any = date.getHours(); 76 | h = h < 10 ? "0" + h : h; 77 | var minute: any = date.getMinutes(); 78 | var second: any = date.getSeconds(); 79 | minute = minute < 10 ? "0" + minute : minute; 80 | second = second < 10 ? "0" + second : second; 81 | if (returnType == "str") { 82 | return y + "-" + m + "-" + d + " " + h + ":" + minute + ":" + second; 83 | } 84 | return [y, m, d, h, minute, second]; 85 | }; 86 | 87 | /** 查询数组中第N大的数据 */ 88 | export const insertQueryArrIndex = function (arr, index) { 89 | if (arr.length == 0) { 90 | return; 91 | } 92 | if (index > arr.length - 1 || index - 1 < 0) { 93 | return; 94 | } 95 | for (let i = 1; i < arr.length; i++) { 96 | var current = arr[i]; 97 | var preIndex = i - 1; 98 | while (preIndex >= 0 && arr[preIndex] < current) { 99 | arr[preIndex + 1] = arr[preIndex]; 100 | preIndex--; 101 | } 102 | arr[preIndex + 1] = current; 103 | } 104 | return arr[index - 1]; 105 | }; 106 | 107 | /** 验证函数节流 */ 108 | export const throttle = (fn, wait) => { 109 | let canRun = true; 110 | return function () { 111 | if (!canRun) return; 112 | canRun = false; 113 | setTimeout(() => { 114 | fn.apply(this, arguments); 115 | canRun = true; 116 | }, wait); 117 | }; 118 | }; 119 | -------------------------------------------------------------------------------- /src/utils/css.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 检查是否支持CSS动画 3 | * @returns 4 | */ 5 | export const checkCssAnimation = function () { 6 | var styles = document.createElement("div").style; 7 | var animations = [ 8 | "animation", 9 | "webkitAnimation", 10 | "msAnimation", 11 | "MozAnimation", 12 | "-moz-animation", 13 | "-webkit-animation", 14 | ]; 15 | for (var i = 0, len = animations.length; i < len; i++) { 16 | if (animations[i] in styles) { 17 | return true; 18 | } 19 | } 20 | return false; 21 | }; 22 | -------------------------------------------------------------------------------- /src/utils/date.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 返回时分秒中的秒 3 | * @param n 4 | * @returns 5 | */ 6 | export const formatNumber = (n: number) => { 7 | const s = n.toString(); 8 | return s[1] ? s : "0" + s; 9 | }; 10 | 11 | /** 12 | * UTC时间转YYYY-MM-DD HH:MM:SS 13 | * @param date 14 | * @param num 15 | * @param interval 16 | * @returns 17 | */ 18 | export const formatDateTime = ( 19 | date: string, 20 | num: number = 3, 21 | interval: string = "-" 22 | ) => { 23 | const arr = date.split("T"); 24 | const d = arr[0]; 25 | const darr = d.split("-"); 26 | const t = arr[1]; 27 | const tarr = t.split("+"); 28 | const marr = tarr[0].split(":"); 29 | const tzone = Number(tarr[1].substr(0, 2)); 30 | const dd = 31 | parseInt(darr[0]) + 32 | "/" + 33 | parseInt(darr[1]) + 34 | "/" + 35 | parseInt(darr[2]) + 36 | " " + 37 | parseInt(marr[0]) + 38 | ":" + 39 | parseInt(marr[1]) + 40 | ":" + 41 | parseInt(marr[2]); 42 | let time = new Date(Date.parse(dd)); 43 | time.setTime(time.setHours(time.getHours() + (8 - tzone))); 44 | let Y = time.getFullYear() + interval; 45 | const addZero = (num: number) => (num < 10 ? "0" + num : num); 46 | let M = addZero(time.getMonth() + 1) + interval; 47 | let D = addZero(time.getDate()); 48 | let h = " " + addZero(time.getHours()); 49 | let m = ":" + addZero(time.getMinutes()); 50 | let s = ":" + addZero(time.getSeconds()); 51 | let result = Y + M + D; 52 | switch (num) { 53 | case 2: 54 | result = h + m; 55 | break; 56 | case 3: 57 | result = Y + M + D; 58 | break; 59 | case 4: 60 | result = Y + M + D + h; 61 | break; 62 | case 5: 63 | result = Y + M + D + h + m; 64 | break; 65 | case 6: 66 | result = Y + M + D + h + m + s; 67 | break; 68 | } 69 | return result; 70 | }; 71 | 72 | /** 73 | * 时间戳转 YY-mm-dd HH:ii:ss 74 | * @param timeStamp 75 | * @param returnType 76 | * @returns 77 | */ 78 | export const timeStampToDate = function ( 79 | timeStamp: any, 80 | returnType: string | undefined 81 | ) { 82 | timeStamp = parseInt(timeStamp); 83 | var date = new Date(); 84 | if (timeStamp < 90000000000) { 85 | date.setTime(timeStamp * 1000); 86 | } else { 87 | date.setTime(timeStamp); 88 | } 89 | var y = date.getFullYear(); 90 | var m: number | string = date.getMonth() + 1; 91 | m = m < 10 ? "0" + m : m; 92 | var d: number | string = date.getDate(); 93 | d = d < 10 ? "0" + d : d; 94 | var h: number | string = date.getHours(); 95 | h = h < 10 ? "0" + h : h; 96 | var minute: number | string = date.getMinutes(); 97 | var second: number | string = date.getSeconds(); 98 | minute = minute < 10 ? "0" + minute : minute; 99 | second = second < 10 ? "0" + second : second; 100 | if (returnType == "str") { 101 | return y + "-" + m + "-" + d + " " + h + ":" + minute + ":" + second; 102 | } 103 | return [y, m, d, h, minute, second]; 104 | }; 105 | 106 | /** 107 | * 字符串转时间戳 108 | * @param timeStamp 109 | * @returns 110 | */ 111 | export const toTimeStamp = function (timeStamp) { 112 | var reg = 113 | /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/; 114 | var res = timeStamp.match(reg); 115 | if (res == null) { 116 | var reg2 = 117 | /^([0-9]{2})\/([0-9]{2})\/([0-9]{4}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/; 118 | var res2 = timeStamp.match(reg2); 119 | if (res2 == null) { 120 | console.log("时间格式错误 E001"); 121 | return false; 122 | } 123 | var year = parseInt(res2[3]); 124 | var month = parseInt(res2[1]); 125 | var day = parseInt(res2[2]); 126 | var h = parseInt(res2[4]); 127 | var i = parseInt(res2[5]); 128 | var s = parseInt(res2[6]); 129 | } else { 130 | var year = parseInt(res[1]); 131 | var month = parseInt(res[2]); 132 | var day = parseInt(res[3]); 133 | var h = parseInt(res[4]); 134 | var i = parseInt(res[5]); 135 | var s = parseInt(res[6]); 136 | } 137 | if (year < 1000) { 138 | console.log("时间格式错误"); 139 | return false; 140 | } 141 | if (h < 0 || h > 24) { 142 | console.log("时间格式错误"); 143 | return false; 144 | } 145 | if (i < 0 || i > 60) { 146 | console.log("时间格式错误"); 147 | return false; 148 | } 149 | if (s < 0 || s > 60) { 150 | console.log("时间格式错误"); 151 | return false; 152 | } 153 | return Date.parse(new Date(year, month - 1, day, h, i, s) + ""); 154 | }; 155 | 156 | /** 157 | * 根据时间戳计算多少分钟/小时/天之前 158 | * @param time 159 | * @returns 160 | */ 161 | export const fromTime = function (time: number) { 162 | if (time < 90000000000) { 163 | time *= 1000; 164 | } 165 | var timer = new Date().getTime() - time; 166 | timer = parseInt(timer / 1000 + ""); 167 | if (timer < 180) { 168 | return "刚刚"; 169 | } else if (timer >= 180 && timer < 3600) { 170 | return parseInt(timer / 60 + "") + "分钟前"; 171 | } else if (timer >= 3600 && timer < 86400) { 172 | return parseInt(timer / 3600 + "") + "小时前"; 173 | } else if (timer >= 86400 && timer < 2592000) { 174 | return parseInt(timer / 86400 + "") + "天前"; 175 | } else { 176 | return this.toDate(time, "str"); 177 | } 178 | }; 179 | 180 | /** 181 | * 计算当前时间,传入类型 182 | * @param type 183 | * @param addTime 184 | * @returns 185 | */ 186 | export const nowTimeStemp = (type: string, addTime: number) => { 187 | var dateObj = new Date(); 188 | var cTime = dateObj.getTime(); 189 | try { 190 | if (addTime) { 191 | cTime += addTime; 192 | } 193 | if (!type) { 194 | type = "number"; 195 | } 196 | if (type == "number") { 197 | return cTime; 198 | } else if (type == "str") { 199 | return timerToUTC(cTime / 1000, "str"); 200 | } else if (type == "array") { 201 | return timerToUTC(cTime / 1000, "array"); 202 | } 203 | } catch (error) { 204 | return cTime; 205 | } 206 | }; 207 | 208 | /** 209 | * 时间戳 转 UTC格式时间, 入参是时间戳、返回的类型 210 | * @param timeStamp 211 | * @param returnType 212 | * @returns 213 | */ 214 | export const timerToUTC = (timeStamp, returnType) => { 215 | timeStamp = parseInt(timeStamp); 216 | var date = new Date(); 217 | if (timeStamp < 90000000000) { 218 | date.setTime(timeStamp * 1000); 219 | } else { 220 | date.setTime(timeStamp); 221 | } 222 | var y = date.getFullYear(); 223 | var m: String | Number = date.getMonth() + 1; 224 | m = m < 10 ? "0" + m : m; 225 | var d: String | Number = date.getDate(); 226 | d = d < 10 ? "0" + d : d; 227 | var h: String | Number = date.getHours(); 228 | h = h < 10 ? "0" + h : h; 229 | var minute: String | Number = date.getMinutes(); 230 | var second: String | Number = date.getSeconds(); 231 | minute = minute < 10 ? "0" + minute : minute; 232 | second = second < 10 ? "0" + second : second; 233 | if (returnType == "str") { 234 | return y + "-" + m + "-" + d + " " + h + ":" + minute + ":" + second; 235 | } 236 | if (returnType == "assistEndTime") { 237 | return y * 1 - 2000 + "/" + m + "/" + d; 238 | } 239 | return [y, m, d, h, minute, second]; 240 | }; 241 | 242 | /** 243 | * 根据时间戳 检查 时间是否过期 true 表示没过期, false 表示过期 244 | * @param time 245 | * @returns 246 | */ 247 | export const checkTimeStamp = (time: number) => { 248 | if (time < 90000000000) { 249 | time *= 1000; 250 | } 251 | var timer: number = time - new Date().getTime(); 252 | timer = parseInt(`${timer / 1000}`); 253 | if (timer < 0) { 254 | return false; 255 | } else { 256 | return true; 257 | } 258 | }; 259 | 260 | /** 261 | * IOS格式时间 转 国际UTC标准时间 262 | * @param data 263 | * @returns 264 | */ 265 | export const iosDateToUtc = (data: string) => { 266 | return JSON.parse( 267 | JSON.stringify(data).replace( 268 | /\/Date\(\-?(\d+)(?:\-|\+)(?:\d+)\)\//g, 269 | function () { 270 | return new Date(Number(arguments[1]) + 8 * 3600 * 1000) 271 | .toISOString() 272 | .replace(/^(.*)T(.*)\.\d+Z$/, "$1 $2"); 273 | } 274 | ) 275 | ); 276 | }; 277 | 278 | /** 279 | * 返回当前时间 280 | * @param type 281 | * @param addTime 282 | * @returns 283 | */ 284 | export function returnNowTime(type, addTime) { 285 | var dateObj = new Date(); 286 | var cTime: any = dateObj.getTime(); 287 | if (addTime) { 288 | cTime += addTime; 289 | } 290 | if (!type) { 291 | type = "number"; 292 | } 293 | if (type == "number") { 294 | return cTime; 295 | } 296 | if (type == "YYYYMMDD") { 297 | var timeStamp = parseInt(cTime); 298 | var date = new Date(); 299 | if (timeStamp < 90000000000) { 300 | date.setTime(timeStamp * 1000); 301 | } else { 302 | date.setTime(timeStamp); 303 | } 304 | var y = date.getFullYear(); 305 | var m: any = date.getMonth() + 1; 306 | m = m < 10 ? "0" + m : m; 307 | var d: any = date.getDate(); 308 | d = d < 10 ? "0" + d : d; 309 | return y + m + d; 310 | } 311 | return this.toDate(cTime / 1000, "str"); 312 | } 313 | 314 | /** 315 | * 返回2个UTC时间之间的时间差 316 | * @param startTime 317 | * @param endTime 318 | * @returns 319 | */ 320 | export const getDateDiff = (startTime: any, endTime: any) => { 321 | var sTime: any = new Date(startTime).getTime(); 322 | var eTime: any = new Date(endTime).getTime(); 323 | var divNumSecond = 1000; 324 | var divNumMinute = 1000 * 60; 325 | var divNumHour = 1000 * 3600; 326 | var divNumDay = 1000 * 3600 * 24; 327 | 328 | const day: any = parseInt(`${(eTime - sTime) / parseInt(`${divNumDay}`)}`); 329 | const hour: any = parseInt(`${((eTime - sTime) % parseInt(`${divNumDay}`)) / parseInt(`${divNumHour}`)}`); 330 | const minute: any = parseInt(`${parseInt(`${((eTime - sTime) % parseInt(`${divNumDay}`)) % parseInt(`${divNumHour}`)}`) / 331 | parseInt(`${divNumMinute}`)}`); 332 | const second: any = 333 | (parseInt(`${((eTime - sTime) % parseInt(`${divNumDay}`)) % parseInt(`${divNumHour}`)}`) % 334 | parseInt(`${divNumMinute}`)) / 335 | parseInt(`${divNumSecond}`); 336 | const str: any = day + "天" + hour + "小时" + minute + "分" + second + "秒"; 337 | return str; 338 | }; 339 | 340 | /** 341 | * 计算星座 342 | * @param mon 343 | * @param day 344 | * @returns 345 | */ 346 | export const constellation = (mon, day) => { 347 | const days = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; 348 | mon = parseInt(mon); 349 | day = parseInt(day); 350 | 351 | const words = [ 352 | "白羊座", 353 | "金牛座", 354 | "双子座", 355 | "巨蟹座", 356 | "狮子座", 357 | "处女座", 358 | "天秤座", 359 | "天蝎座", 360 | "射手座", 361 | "摩羯座", 362 | "水瓶座", 363 | "双鱼座", 364 | ]; 365 | 366 | if (mon < 1 || mon > 12) { 367 | throw new Error("Invalid month."); 368 | } 369 | 370 | if (day < 1 || day > days[mon - 1]) { 371 | throw new Error("Invalid day."); 372 | } 373 | 374 | var val = mon * 100 + day; 375 | 376 | if (val >= 321 && val <= 419) { 377 | return words[0]; 378 | } else if (val >= 420 && val <= 520) { 379 | return words[1]; 380 | } else if (val >= 521 && val <= 621) { 381 | return words[2]; 382 | } else if (val >= 622 && val <= 722) { 383 | return words[3]; 384 | } else if (val >= 723 && val <= 822) { 385 | return words[4]; 386 | } else if (val >= 823 && val <= 922) { 387 | return words[5]; 388 | } else if (val >= 923 && val <= 1023) { 389 | return words[6]; 390 | } else if (val >= 1024 && val <= 1122) { 391 | return words[7]; 392 | } else if (val >= 1123 && val <= 1221) { 393 | return words[8]; 394 | } else if (val >= 1222 || val <= 119) { 395 | return words[9]; 396 | } else if (val >= 120 && val <= 218) { 397 | return words[10]; 398 | } else if (val >= 219 && val <= 320) { 399 | return words[11]; 400 | } 401 | return ""; 402 | }; 403 | 404 | 405 | /** 406 | * 判断某一年是否是闰年 407 | * @param year 可以是一个date类型,也可以是一个int类型的年份,不传默认当前时间 408 | */ 409 | export const _isLeapYear = (year) => { 410 | if (year === undefined) year = new Date(); 411 | if (year instanceof Date) year = year.getFullYear(); 412 | return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); 413 | } 414 | 415 | /** 416 | * 获取某一年某一月的总天数,没有任何参数时获取当前月份的 417 | * 方式一:dateUtils.getDaysOfMonth(); 418 | * 方式二:dateUtils.getDaysOfMonth(new Date()); 419 | * 方式三:dateUtils.getDaysOfMonth(2013, 12); 420 | */ 421 | export const _getDaysOfMonth = (date, month) => { 422 | var y, m; 423 | if (date == undefined) date = new Date(); 424 | if (date instanceof Date) { 425 | y = date.getFullYear(); 426 | m = date.getMonth(); 427 | } 428 | else if (typeof date == 'number') { 429 | y = date; 430 | m = month - 1; 431 | } else if (typeof date === 'string') { 432 | var strdate = new Date(date); 433 | y = strdate.getFullYear(); 434 | m = strdate.getMonth(); 435 | } 436 | var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; // 非闰年的一年中每个月份的天数 437 | //如果是闰年并且是2月 438 | if (m == 1 && _isLeapYear(y)) return days[m] + 1; 439 | return days[m]; 440 | } 441 | 442 | /** 443 | * 转换时间,带星期 444 | * @param date 445 | * @param format 446 | * @returns 447 | */ 448 | export const formatDate = (date, format = 'yyyy-MM-dd') => { 449 | var v = ""; 450 | var year = date.getFullYear(); 451 | var month = date.getMonth() + 1; 452 | var day = date.getDate(); 453 | var hour = date.getHours(); 454 | var minute = date.getMinutes(); 455 | var second = date.getSeconds(); 456 | var weekDay = date.getDay(); 457 | var ms = date.getMilliseconds(); 458 | var weekDayString = ""; 459 | if (weekDay == 1) { 460 | weekDayString = "星期一"; 461 | } else if (weekDay == 2) { 462 | weekDayString = "星期二"; 463 | } else if (weekDay == 3) { 464 | weekDayString = "星期三"; 465 | } else if (weekDay == 4) { 466 | weekDayString = "星期四"; 467 | } else if (weekDay == 5) { 468 | weekDayString = "星期五"; 469 | } else if (weekDay == 6) { 470 | weekDayString = "星期六"; 471 | } else if (weekDay == 7) { 472 | weekDayString = "星期日"; 473 | } 474 | v = format; 475 | //Year 476 | v = v.replace(/yyyy/g, year); 477 | v = v.replace(/YYYY/g, year); 478 | v = v.replace(/yy/g, (year + "").substring(2, 4)); 479 | v = v.replace(/YY/g, (year + "").substring(2, 4)); 480 | //Month 481 | var monthStr = ("0" + month); 482 | v = v.replace(/MM/g, monthStr.substring(monthStr.length - 2)); 483 | //Day 484 | var dayStr = ("0" + day); 485 | v = v.replace(/dd/g, dayStr.substring(dayStr.length - 2)); 486 | //hour 487 | var hourStr = ("0" + hour); 488 | v = v.replace(/HH/g, hourStr.substring(hourStr.length - 2)); 489 | v = v.replace(/hh/g, hourStr.substring(hourStr.length - 2)); 490 | //minute 491 | var minuteStr = ("0" + minute); 492 | v = v.replace(/mm/g, minuteStr.substring(minuteStr.length - 2)); 493 | //Millisecond 494 | v = v.replace(/sss/g, ms); 495 | v = v.replace(/SSS/g, ms); 496 | //second 497 | var secondStr = ("0" + second); 498 | v = v.replace(/ss/g, secondStr.substring(secondStr.length - 2)); 499 | v = v.replace(/SS/g, secondStr.substring(secondStr.length - 2)); 500 | //weekDay 501 | v = v.replace(/E/g, weekDayString); 502 | return v; 503 | } 504 | -------------------------------------------------------------------------------- /src/utils/event.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 客户端环境下Event事件 3 | */ 4 | export const _Event = function () { }; 5 | // 添加事件处理 6 | _Event.prototype.addHandler = function (element, type, handler) { 7 | if (element.addEventListener) { 8 | element.addEventListener(type, handler, false); 9 | } else if (element.attachEvent) { 10 | element.attachEvent("on" + type, handler); 11 | } else { 12 | element["on" + type] = handler; 13 | } 14 | }; 15 | // 移除事件处理 16 | _Event.prototype.removeHandler = function (element, type, handler) { 17 | if (element.removeEventListener) { 18 | element.removeEventListener(type, handler, false); 19 | } else if (element.detachEvent) { 20 | element.detachEvent("on" + type, handler); 21 | } else { 22 | element["on" + type] = null; 23 | } 24 | }; 25 | // 获取event对象 26 | _Event.prototype.getEvent = function (event) { 27 | return event ? event : w.event; 28 | }; 29 | // 获取目标对象 30 | _Event.prototype.getTarget = function (event) { 31 | return event.target || event.srcElement; 32 | }; 33 | // 获取相关目标对象 34 | _Event.prototype.getRelatedTarget = function (event) { 35 | if (event.relatedTarget) { 36 | return event.relatedTarget; 37 | } else if (event.toElement) { 38 | return event.toElement; 39 | } else if (event.fromElement) { 40 | return fromElement; 41 | } else { 42 | return null; 43 | } 44 | }; 45 | // 阻止默认事件 46 | _Event.prototype.preventDefault = function (event) { 47 | if (event.preventDefault) { 48 | event.preventDefault(); 49 | } else { 50 | event.returnValue = false; 51 | } 52 | }; 53 | // 阻止冒泡事件 54 | _Event.prototype.stopPropagation = function (event) { 55 | if (event.stopPropagation) { 56 | event.stopPropagation(); 57 | } else { 58 | event.cancelBubble = true; 59 | } 60 | }; 61 | return _Event; 62 | } -------------------------------------------------------------------------------- /src/utils/file.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 输入一个数组,然后拍平输出,保证文件名唯一 3 | * @param names 4 | * @returns 5 | */ 6 | export const getFolderNames = (names: string[]): string[] => { 7 | let d: { [_: string]: number } = {}; 8 | return names.map((name) => { 9 | let s = name; 10 | while (s in d) { 11 | s = `${name}(${d[name]})`; 12 | ++d[name]; 13 | } 14 | d[s] = 1; 15 | return s; 16 | }); 17 | }; 18 | 19 | /** 20 | * 计算文件占用体积 21 | * @param size 22 | * @returns 23 | */ 24 | export const handleSize = (size) => { 25 | const KB = size / 1024; 26 | const MB = KB / 1024; 27 | const GB = MB / 1024; 28 | const TB = GB / 1024; 29 | if (MB < 1) { 30 | return `${KB.toFixed(2)} KB`; 31 | } else if (GB < 1) { 32 | return `${MB.toFixed(2)} MB`; 33 | } else if (TB < 1) { 34 | return `${GB.toFixed(2)} GB`; 35 | } else { 36 | return `${TB.toFixed(2)} TB`; 37 | } 38 | }; 39 | 40 | 41 | function charToBinary(text) { 42 | var code = ""; 43 | for (let i of text) { 44 | // 字符编码 45 | let number = i.charCodeAt().toString(2); 46 | // 1 bytes = 8bit,将 number 不足8位的0补上 47 | for (let a = 0; a <= 8 - number.length; a++) { 48 | number = 0 + number; 49 | } 50 | code += number; 51 | } 52 | return code; 53 | } 54 | 55 | /** 56 | * 将二进制数据每 6bit 位替换成一个 base64 字符 57 | * @param code 58 | * @returns 59 | */ 60 | export const binaryTobase64 = (code) => { 61 | let base64Code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 62 | let res = ''; 63 | if (code.length % 24 === 8) { 64 | code += '0000'; 65 | res += '==' 66 | } 67 | if (code.length % 24 === 16) { 68 | code += '00'; 69 | res += '=' 70 | } 71 | 72 | let encode = ''; 73 | for (let i = 0; i < code.length; i += 6) { 74 | let item = code.slice(i, i + 6); 75 | encode += base64Code[parseInt(item, 2)]; 76 | } 77 | return encode + res; 78 | } 79 | -------------------------------------------------------------------------------- /src/utils/filterdata/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {number[]} nums 3 | * @return {number[]} 4 | */ 5 | export const findDuplicates = function (nums) { 6 | const res = []; 7 | 8 | for (const num of nums) { 9 | const absNum = Math.abs(num); 10 | if (nums[absNum - 1] < 0) { 11 | res.push(absNum); 12 | } else { 13 | nums[absNum - 1] *= -1; 14 | } 15 | } 16 | 17 | return res; 18 | }; 19 | 20 | /** 21 | * 数组对象去重 22 | * @param arr 23 | * @param key 24 | * @param hash 25 | * @returns 26 | */ 27 | export const unique = (arr: AnyArray, key: string, hash: any = {}) => 28 | arr.reduce(function (item, next) { 29 | hash[next[key]] ? "" : (hash[next[key]] = true && item.push(next)); 30 | return item; 31 | }, []); 32 | 33 | /** 34 | * 校验输入框是都有表情 35 | * @param substring 36 | * @returns 37 | */ 38 | export const isEmojiCharacter = (substring) => { 39 | for (var i = 0; i < substring.length; i++) { 40 | const hs = substring.charCodeAt(i); 41 | if (hs >= 0xd800 && hs <= 0xdbff) { 42 | if (substring.length > 1) { 43 | const ls = substring.charCodeAt(i + 1); 44 | var uc = (hs - 0xd800) * 0x400 + (ls - 0xdc00) + 0x10000; 45 | if (uc >= 0x1d000 && uc <= 0x1f77f) { 46 | return true; 47 | } 48 | } 49 | } else if (substring.length > 1) { 50 | const ls = substring.charCodeAt(i + 1); 51 | if (ls === 0x20e3) { 52 | return true; 53 | } 54 | } else { 55 | if (hs >= 0x2100 && hs <= 0x27ff) { 56 | return true; 57 | } else if (hs >= 0x2b05 && hs <= 0x2b07) { 58 | return true; 59 | } else if (hs >= 0x2934 && hs <= 0x2935) { 60 | return true; 61 | } else if (hs >= 0x3297 && hs <= 0x3299) { 62 | return true; 63 | } else if ( 64 | hs === 0xa9 || 65 | hs === 0xae || 66 | hs === 0x303d || 67 | hs === 0x3030 || 68 | hs === 0x2b55 || 69 | hs === 0x2b1c || 70 | hs === 0x2b1b || 71 | hs === 0x2b50 72 | ) { 73 | return true; 74 | } 75 | } 76 | } 77 | }; 78 | 79 | /** 80 | * 根据指定条件(如回调对象的某个属性)进行分组 构成对象返回。 81 | * @param target 82 | * @param val 83 | */ 84 | export const groupBy = (target, val) => { 85 | var result = {}; 86 | var iterator = typeof val == "function" ? val : function (obj) { 87 | return obj[val]; 88 | } 89 | target.forEach(function (value, index) { 90 | var key = iterator(value, index); 91 | (result[key] || (result[key] = [])).push(value); 92 | }); 93 | return result; 94 | } -------------------------------------------------------------------------------- /src/utils/idcard.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 校验身份证的合法性 3 | * @param idcard 4 | * @returns 5 | */ 6 | export const checkIdcard = (idcard: number | string) => { 7 | var Errors = new Array( 8 | "ok", 9 | "身份证号码位数错误", 10 | "身份证号码出生日期错误", 11 | "身份证号码校验错误", 12 | "身份证地区错误" 13 | ); 14 | var area = { 15 | 11: "北京", 16 | 12: "天津", 17 | 13: "河北", 18 | 14: "山西", 19 | 15: "内蒙古", 20 | 21: "辽宁", 21 | 22: "吉林", 22 | 23: "黑龙江", 23 | 31: "上海", 24 | 32: "江苏", 25 | 33: "浙江", 26 | 34: "安徽", 27 | 35: "福建", 28 | 36: "江西", 29 | 37: "山东", 30 | 41: "河南", 31 | 42: "湖北", 32 | 43: "湖南", 33 | 44: "广东", 34 | 45: "广西", 35 | 46: "海南", 36 | 50: "重庆", 37 | 51: "四川", 38 | 52: "贵州", 39 | 53: "云南", 40 | 54: "西藏", 41 | 61: "陕西", 42 | 62: "甘肃", 43 | 63: "青海", 44 | 64: "宁夏", 45 | 65: "新疆", 46 | 71: "台湾", 47 | 81: "香港", 48 | 82: "澳门", 49 | 91: "国外", 50 | }; 51 | 52 | var idcard, Y, JYM, ereg; 53 | var S, M; 54 | var idcard_array = new Array(); 55 | idcard_array = idcard.split(""); 56 | //地区检验 57 | if (area[parseInt(idcard.substr(0, 2))] == null) return Errors[4]; 58 | //身份号码位数及格式检验 59 | switch (idcard.length) { 60 | case 15: 61 | if ( 62 | (parseInt(idcard.substr(6, 2)) + 1900) % 4 == 0 || 63 | ((parseInt(idcard.substr(6, 2)) + 1900) % 100 == 0 && 64 | (parseInt(idcard.substr(6, 2)) + 1900) % 4 == 0) 65 | ) { 66 | ereg = 67 | /^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}$/; //测试出生日期的合法性 68 | } else { 69 | ereg = 70 | /^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}$/; //测试出生日期的合法性 71 | } 72 | if (ereg.test(idcard)) return Errors[0]; 73 | else return Errors[2]; 74 | break; 75 | case 18: 76 | //18位身份号码检测 77 | //出生日期的合法性检查 78 | //闰年月日:((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9])) 79 | //平年月日:((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8])) 80 | if ( 81 | parseInt(idcard.substr(6, 4)) % 4 == 0 || 82 | (parseInt(idcard.substr(6, 4)) % 100 == 0 && 83 | parseInt(idcard.substr(6, 4)) % 4 == 0) 84 | ) { 85 | ereg = 86 | /^[1-9][0-9]{5}[1-2]+[0-9]{3}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}[0-9Xx]$/; //闰年出生日期的合法性正则表达式 87 | } else { 88 | ereg = 89 | /^[1-9][0-9]{5}[1-2]+[0-9]{3}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}[0-9Xx]$/; //平年出生日期的合法性正则表达式 90 | } 91 | // 测试出生日期的合法性 92 | if (ereg.test(idcard)) { 93 | //计算校验位 94 | S = 95 | (parseInt(idcard_array[0]) + parseInt(idcard_array[10])) * 7 + 96 | (parseInt(idcard_array[1]) + parseInt(idcard_array[11])) * 9 + 97 | (parseInt(idcard_array[2]) + parseInt(idcard_array[12])) * 10 + 98 | (parseInt(idcard_array[3]) + parseInt(idcard_array[13])) * 5 + 99 | (parseInt(idcard_array[4]) + parseInt(idcard_array[14])) * 8 + 100 | (parseInt(idcard_array[5]) + parseInt(idcard_array[15])) * 4 + 101 | (parseInt(idcard_array[6]) + parseInt(idcard_array[16])) * 2 + 102 | parseInt(idcard_array[7]) * 1 + 103 | parseInt(idcard_array[8]) * 6 + 104 | parseInt(idcard_array[9]) * 3; 105 | Y = S % 11; 106 | M = "F"; 107 | JYM = "10X98765432"; 108 | M = JYM.substr(Y, 1); //判断校验位 109 | if (M == idcard_array[17]) return Errors[0]; //检测ID的校验位 110 | else return Errors[3]; 111 | } else { 112 | return Errors[2]; 113 | } 114 | break; 115 | default: 116 | return Errors[1]; 117 | break; 118 | } 119 | }; 120 | 121 | /** 122 | * 计算最后一位应该是多少 123 | * @param idCard 124 | * @returns 125 | */ 126 | function idCardEndNum(idCard) { 127 | idCard = idCard.toString(); 128 | var factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; 129 | var parity = [1, 0, "X", 9, 8, 7, 6, 5, 4, 3, 2]; 130 | var sum = 0; 131 | var ai = 0; 132 | var wi = 0; 133 | for (var i = 0; i < 17; i++) { 134 | ai = idCard[i]; 135 | wi = factor[i]; 136 | sum += ai * wi; 137 | } 138 | var last = parity[sum % 11]; 139 | return last; 140 | } 141 | 142 | // 解析生日信息 143 | function birthDay(idCard) { 144 | idCard = idCard.toString(); 145 | var birthday, month, day, nong; 146 | let year = idCard.substr(6, 4); 147 | month = idCard.substr(10, 2); 148 | day = idCard.substr(12, 2); 149 | birthday = year + "/" + month + "/" + day; 150 | nong = Nong(birthday); 151 | let nongyear = nong.substr(0, 4); 152 | return { 153 | date: birthday, 154 | nong: nong, 155 | year: year, 156 | month: month, 157 | day: day, 158 | nongCn: NongCn(birthday), 159 | week: dict.week(birthday), // 星期几 160 | zodiac: dict.zodiac(month, day), // 星座 161 | zodiac_zh: dict.zodiac_zh(nongyear), // 生肖 162 | }; 163 | } 164 | 165 | // 验证身份证号是否正确 166 | function checkIdCard(idCard) { 167 | idCard = idCard.toString(); 168 | if (/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(idCard)) { 169 | if (idCard.length >= 18) { 170 | return idCardEndNum(idCard) == idCard[17].toUpperCase(); 171 | } else { 172 | return false; 173 | } 174 | } 175 | return false; 176 | } 177 | 178 | // 补全身份证号 179 | function repairIdCard(idCard) { 180 | idCard = idCard.toString(); 181 | if (/(^\d{17}$)/.test(idCard)) return idCard + idCardEndNum(idCard); 182 | if (/(^\d{18}$)/.test(idCard)) 183 | return idCard.slice(0, 17) + idCardEndNum(idCard); 184 | } 185 | 186 | // 15位转换18位 187 | function num15to18(idCard) { 188 | idCard = idCard.toString(); 189 | if (/(^\d{15}$)/.test(idCard)) 190 | return repairIdCard(idCard.slice(0, 6) + "19" + idCard.slice(6, 15)); 191 | } 192 | 193 | // 性别解析 194 | function sex(idCard) { 195 | idCard = idCard.toString(); 196 | if (idCard[16] % 2) return "男"; 197 | return "女"; 198 | } 199 | 200 | 201 | 202 | export default { 203 | checkIdcard: checkIdcard, 204 | }; 205 | -------------------------------------------------------------------------------- /src/utils/locate.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断当前设备的IP地址 3 | * @param callback 4 | */ 5 | export const getIPs = (callback) => { 6 | var ip_dups = {}; 7 | 8 | //compatibility for firefox and chrome 9 | var RTCPeerConnection = 10 | window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; 11 | var useWebKit = !!window.webkitRTCPeerConnection; 12 | 13 | //bypass naive webrtc blocking using an iframe 14 | if (!RTCPeerConnection) { 15 | var win = iframe.contentWindow; 16 | RTCPeerConnection = 17 | win.RTCPeerConnection || win.mozRTCPeerConnection || win.webkitRTCPeerConnection; 18 | useWebKit = !!win.webkitRTCPeerConnection; 19 | } 20 | 21 | //minimal requirements for data connection 22 | var mediaConstraints = { 23 | optional: [{ RtpDataChannels: true }], 24 | }; 25 | 26 | var servers = { iceServers: [{ urls: 'stun:stun.services.mozilla.com' }] }; 27 | 28 | //construct a new RTCPeerConnection 29 | var pc = new RTCPeerConnection(servers, mediaConstraints); 30 | 31 | function handleCandidate(candidate) { 32 | //match just the IP address 33 | var ip_regex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/; 34 | var ip_addr = ip_regex.exec(candidate)[1]; 35 | 36 | //remove duplicates 37 | if (ip_dups[ip_addr] === undefined) callback(ip_addr); 38 | 39 | ip_dups[ip_addr] = true; 40 | } 41 | 42 | //listen for candidate events 43 | pc.onicecandidate = function (ice) { 44 | //skip non-candidate events 45 | if (ice.candidate) handleCandidate(ice.candidate.candidate); 46 | }; 47 | 48 | //create a bogus data channel 49 | pc.createDataChannel(''); 50 | 51 | //create an offer sdp 52 | pc.createOffer( 53 | function (result) { 54 | //trigger the stun server request 55 | pc.setLocalDescription( 56 | result, 57 | function () {}, 58 | function () {}, 59 | ); 60 | }, 61 | function () {}, 62 | ); 63 | 64 | //wait for a while to let everything done 65 | setTimeout(function () { 66 | //read candidate info from local description 67 | var lines = pc.localDescription.sdp.split('\n'); 68 | 69 | lines.forEach(function (line) { 70 | if (line.indexOf('a=candidate:') === 0) handleCandidate(line); 71 | }); 72 | }, 1000); 73 | }; 74 | 75 | /** 76 | * 计算2个坐标之间的距离 77 | * @param lat1 78 | * @param lng1 79 | * @param lat2 80 | * @param lng2 81 | * @returns 82 | */ 83 | export const getDistance = function (lat1, lng1, lat2, lng2) { 84 | var radLat1 = (lat1 * Math.PI) / 180.0; 85 | var radLat2 = (lat2 * Math.PI) / 180.0; 86 | var a = radLat1 - radLat2; 87 | var b = (lng1 * Math.PI) / 180.0 - (lng2 * Math.PI) / 180.0; 88 | var s = 89 | 2 * 90 | Math.asin( 91 | Math.sqrt( 92 | Math.pow(Math.sin(a / 2), 2) + 93 | Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2), 94 | ), 95 | ); 96 | s = s * 6378.137; 97 | s = Math.round(s * 10000) / 10000; 98 | return s; 99 | }; 100 | 101 | /** 102 | * 导出GPS的工具函数 103 | */ 104 | export const GPSTools = { 105 | delta: function (lat, lon) { 106 | var a = 6378245.0; 107 | var ee = 0.00669342162296594323; 108 | var dLat = this.transformLat(lon - 105.0, lat - 35.0); 109 | var dLon = this.transformLon(lon - 105.0, lat - 35.0); 110 | var radLat = (lat / 180.0) * Math.PI; 111 | var magic = Math.sin(radLat); 112 | magic = 1 - ee * magic * magic; 113 | var sqrtMagic = Math.sqrt(magic); 114 | dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * Math.PI); 115 | dLon = (dLon * 180.0) / ((a / sqrtMagic) * Math.cos(radLat) * Math.PI); 116 | return { lat: dLat, lon: dLon }; 117 | }, 118 | gcj_encrypt: function (wgsLat, wgsLon) { 119 | if (this.outOfChina(wgsLat, wgsLon)) { 120 | return { lat: wgsLat, lng: wgsLon }; 121 | } 122 | var d = this.delta(wgsLat, wgsLon); 123 | return { lat: wgsLat + d.lat, lng: wgsLon + d.lon }; 124 | }, 125 | outOfChina: function (lat, lon) { 126 | if (lon < 72.004 || lon > 137.8347) { 127 | return true; 128 | } 129 | if (lat < 0.8293 || lat > 55.8271) { 130 | return true; 131 | } 132 | return false; 133 | }, 134 | transformLat: function (x, y) { 135 | var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x)); 136 | ret += ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0) / 3.0; 137 | ret += ((20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin((y / 3.0) * Math.PI)) * 2.0) / 3.0; 138 | ret += 139 | ((160.0 * Math.sin((y / 12.0) * Math.PI) + 320 * Math.sin((y * Math.PI) / 30.0)) * 2.0) / 3.0; 140 | return ret; 141 | }, 142 | transformLon: function (x, y) { 143 | var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)); 144 | ret += ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0) / 3.0; 145 | ret += ((20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin((x / 3.0) * Math.PI)) * 2.0) / 3.0; 146 | ret += 147 | ((150.0 * Math.sin((x / 12.0) * Math.PI) + 300.0 * Math.sin((x / 30.0) * Math.PI)) * 2.0) / 148 | 3.0; 149 | return ret; 150 | }, 151 | }; 152 | 153 | /** 154 | * 高德转百度坐标系 155 | * @param lng 156 | * @param lat 157 | * @returns 158 | */ 159 | export const aMapToBMap = function (lng, lat) { 160 | var x_pi = (3.14159265358979324 * 3000.0) / 180.0; 161 | var x = lng, 162 | y = lat; 163 | var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi); 164 | var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi); 165 | return { 166 | lng: z * Math.cos(theta) + 0.0065, 167 | lat: z * Math.sin(theta) + 0.006, 168 | }; 169 | }; 170 | 171 | /** 172 | * 百度坐标系转高德坐标系 173 | * @param lng 174 | * @param lat 175 | * @returns 176 | */ 177 | export const bMapToAMap = function (lng, lat) { 178 | var x_pi = (3.14159265358979324 * 3000.0) / 180.0; 179 | var x = lng - 0.0065, 180 | y = lat - 0.006; 181 | var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi); 182 | var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi); 183 | return { 184 | lng: z * Math.cos(theta), 185 | lat: z * Math.sin(theta), 186 | isBMap: false, 187 | }; 188 | }; 189 | 190 | /** 191 | * 高德坐标系转谷歌坐标系 192 | * @param lon 193 | * @param lat 194 | * @returns 195 | */ 196 | export const aMapToGMap = function (lon, lat) { 197 | var a = 6378245.0; 198 | var ee = 0.00669342162296594323; 199 | var x = lon - 105.0; 200 | var y = lat - 35.0; 201 | var dLon = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)); 202 | dLon += ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0) / 3.0; 203 | dLon += ((20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin((x / 3.0) * Math.PI)) * 2.0) / 3.0; 204 | dLon += 205 | ((150.0 * Math.sin((x / 12.0) * Math.PI) + 300.0 * Math.sin((x / 30.0) * Math.PI)) * 2.0) / 3.0; 206 | var dLat = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x)); 207 | dLat += ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0) / 3.0; 208 | dLat += ((20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin((y / 3.0) * Math.PI)) * 2.0) / 3.0; 209 | dLat += 210 | ((160.0 * Math.sin((y / 12.0) * Math.PI) + 320 * Math.sin((y * Math.PI) / 30.0)) * 2.0) / 3.0; 211 | var radLat = (lat / 180.0) * Math.PI; 212 | var magic = Math.sin(radLat); 213 | magic = 1 - ee * magic * magic; 214 | var sqrtMagic = Math.sqrt(magic); 215 | dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * Math.PI); 216 | dLon = (dLon * 180.0) / ((a / sqrtMagic) * Math.cos(radLat) * Math.PI); 217 | var wgsLon = lon - dLon; 218 | var wgsLat = lat - dLat; 219 | return { 220 | lat: wgsLat, 221 | lng: wgsLon, 222 | }; 223 | }; 224 | 225 | /** 226 | * 谷歌坐标系转高德坐标系 227 | * @param lng 228 | * @param lat 229 | * @returns 230 | */ 231 | export const gMapToAMap = function (lng, lat) { 232 | return GPS.gcj_encrypt(lat, lng); 233 | }; 234 | 235 | /** 236 | * 谷歌坐标系转百度坐标系 237 | * @param lng 238 | * @param lat 239 | * @returns 240 | */ 241 | export const gMapToBMap = function (lng, lat) { 242 | var aPoint = gMapToAMap(lng, lat); 243 | return aMapToBMap(aPoint.lng, aPoint.lat); 244 | }; 245 | 246 | /** 247 | * 百度坐标转化为腾讯坐标 248 | * @param lng 249 | * @param lat 250 | * @returns 251 | */ 252 | export const qqMapToBMap = (lng, lat) => { 253 | if (lng == null || lng == '' || lat == null || lat == '') return [lng, lat]; 254 | var x_pi = 3.14159265358979324; 255 | var x = parseFloat(lng); 256 | var y = parseFloat(lat); 257 | var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi); 258 | var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi); 259 | lng = (z * Math.cos(theta) + 0.0065).toFixed(5); 260 | lat = (z * Math.sin(theta) + 0.006).toFixed(5); 261 | return lat + ',' + lng; 262 | }; 263 | 264 | /** 265 | * 火星坐标(gcj02)转化为百度坐标 266 | * @param mars_point 267 | * @returns 268 | */ 269 | export const transformGCtoBMap = (mars_point) => { 270 | const x_pi = (3.14159265358979324 * 3000.0) / 180.0; 271 | var baidu_point = { lon: 0, lat: 0 }; 272 | var x = mars_point.lon; 273 | var y = mars_point.lat; 274 | var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi); 275 | var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi); 276 | baidu_point.lon = z * Math.cos(theta) + 0.0065; 277 | baidu_point.lat = z * Math.sin(theta) + 0.006; 278 | return baidu_point; 279 | }; 280 | -------------------------------------------------------------------------------- /src/utils/math.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 保留N位小数 3 | * @param number 4 | * @param n 5 | * @returns 6 | */ 7 | export const getFloat = function (number: any, n: any) { 8 | n = n ? parseInt(n) : 0; 9 | if (n <= 0) { 10 | return Math.round(number); 11 | } 12 | number = Math.round(number * Math.pow(10, n)) / Math.pow(10, n); 13 | number = Number(number).toFixed(n); 14 | return number; 15 | }; 16 | 17 | /** 18 | * 从一个给定的数组arr中,随机返回num个不重复项 19 | * @param arr 20 | * @param num 21 | * @returns 22 | */ 23 | export const getArrayItems = function (arr: Array, num: number) { 24 | //新建一个数组,将传入的数组复制过来,用于运算,而不要直接操作传入的数组; 25 | var temp_array = new Array(); 26 | for (var index in arr) { 27 | temp_array.push(arr[index]); 28 | } 29 | //取出的数值项,保存在此数组 30 | var return_array = new Array(); 31 | for (var i = 0; i < num; i++) { 32 | //判断如果数组还有可以取出的元素,以防下标越界 33 | if (temp_array.length > 0) { 34 | //在数组中产生一个随机索引 35 | var arrIndex = Math.floor(Math.random() * temp_array.length); 36 | //将此随机索引的对应的数组元素值复制出来 37 | return_array[i] = temp_array[arrIndex]; 38 | //然后删掉此索引的数组元素,这时候temp_array变为新的数组 39 | temp_array.splice(arrIndex, 1); 40 | } else { 41 | //数组中数据项取完后,退出循环,比如数组本来只有10项,但要求取出20项. 42 | break; 43 | } 44 | } 45 | return return_array; 46 | }; 47 | 48 | /** 49 | * 返回数组种重复数据的列表 50 | * @param nums 51 | * @returns 52 | */ 53 | export const findDuplicates = function (nums: Array) { 54 | const res: Array = []; 55 | 56 | for (const num of nums) { 57 | const absNum = Math.abs(num); 58 | if (nums[absNum - 1] < 0) { 59 | res.push(absNum); 60 | } else { 61 | nums[absNum - 1] *= -1; 62 | } 63 | } 64 | 65 | return res; 66 | }; 67 | 68 | /** 69 | * 在M*N网格内筛选出最短路径,寻路 70 | * @param grid 0 空、1 障碍 71 | * @param k 72 | * @returns 73 | */ 74 | export const shortestPath = function (grid: Array>, k: number) { 75 | const directions = [ 76 | [1, 0], 77 | [0, 1], 78 | [-1, 0], 79 | [0, -1], 80 | ]; 81 | const height = grid.length; 82 | const width = grid[0].length; 83 | const dp = Array(height).fill(null).map((_) => 84 | Array(width).fill(null).map((_) => Array(k + 1).fill(Infinity)) 85 | ); 86 | dp[0][0][0] = 0; 87 | const queue: Array = [[0, 0]]; 88 | while (queue.length > 0) { 89 | const [x, y] = queue.shift(); 90 | const status = dp[y][x]; 91 | 92 | for (const [offsetX, offsetY] of directions) { 93 | const nextXY = [x + offsetX, y + offsetY]; 94 | const [nextX, nextY] = nextXY; 95 | 96 | if (nextX >= 0 && nextY >= 0 && nextX < width && nextY < height) { 97 | const nextStatus = dp[nextY][nextX]; 98 | const isObstacle = grid[nextY][nextX] == 1; 99 | 100 | let available = false; 101 | if (!isObstacle) { 102 | for (let i = 0; i < k + 1; i++) { 103 | const nextStep = status[i] + 1; 104 | if (nextStep < nextStatus[i]) { 105 | nextStatus[i] = nextStep; 106 | available = true; 107 | } 108 | } 109 | } else { 110 | for (let i = 0; i < k; i++) { 111 | const nextStep = status[i] + 1; 112 | if (nextStep < nextStatus[i + 1]) { 113 | nextStatus[i + 1] = nextStep; 114 | available = true; 115 | } 116 | } 117 | } 118 | if (available) queue.push(nextXY); 119 | } 120 | } 121 | } 122 | let totalSteps = Math.min(...dp[height - 1][width - 1]); 123 | return totalSteps == Infinity ? -1 : totalSteps; 124 | }; 125 | 126 | /** 生成指定范围内的随机数 */ 127 | export const randomNum = (min: number, max: number) => { 128 | return Math.floor(Math.random() * (max - min + 1)) + min; 129 | } 130 | 131 | /** 数组乱序 */ 132 | export const arrScrambling = (arr: Array) => { 133 | for (let i = 0; i < arr.length; i++) { 134 | const randomIndex = Math.round(Math.random() * (arr.length - 1 - i)) + i; 135 | [arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]]; 136 | } 137 | return arr; 138 | }; 139 | 140 | /** 数组扁平化 */ 141 | export const flatten = (arr: Array) => { 142 | let result: Array = []; 143 | 144 | for (let i = 0; i < arr.length; i++) { 145 | if (Array.isArray(arr[i])) { 146 | result = result.concat(flatten(arr[i])); 147 | } else { 148 | result.push(arr[i]); 149 | } 150 | } 151 | return result; 152 | }; 153 | 154 | /** 数组中获取随机数 */ 155 | export const sample = (arr) => arr[Math.floor(Math.random() * arr.length)]; 156 | 157 | /** 生成随机字符串 */ 158 | export const randomString = (len) => { 159 | let chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz123456789"; 160 | let strLen = chars.length; 161 | let randomStr = ""; 162 | for (let i = 0; i < len; i++) { 163 | randomStr += chars.charAt(Math.floor(Math.random() * strLen)); 164 | } 165 | return randomStr; 166 | }; 167 | 168 | /** 字符串首字母大写 */ 169 | export const fistLetterUpper = (str) => { 170 | return str.charAt(0).toUpperCase() + str.slice(1); 171 | }; 172 | 173 | /** 手机号中间四位变成* */ 174 | export const telFormat = (tel) => { 175 | tel = String(tel); 176 | return tel.substr(0, 3) + "****" + tel.substr(7); 177 | }; 178 | 179 | /** 驼峰命名转换成短横线命名 */ 180 | export const getKebabCase = (str) => { 181 | return str.replace(/[A-Z]/g, (item) => "-" + item.toLowerCase()); 182 | }; 183 | 184 | /** 短横线命名转换成驼峰命名 */ 185 | export const getCamelCase = (str) => { 186 | return str.replace(/-([a-z])/g, (i, item) => item.toUpperCase()); 187 | }; 188 | 189 | /** 全角转换为半角 */ 190 | export const toCDB = (str) => { 191 | let result = ""; 192 | for (let i = 0; i < str.length; i++) { 193 | var code = str.charCodeAt(i); 194 | if (code >= 65281 && code <= 65374) { 195 | result += String.fromCharCode(str.charCodeAt(i) - 65248); 196 | } else if (code == 12288) { 197 | result += String.fromCharCode(str.charCodeAt(i) - 12288 + 32); 198 | } else { 199 | result += str.charAt(i); 200 | } 201 | } 202 | return result; 203 | }; 204 | 205 | /** 半角转换为全角 */ 206 | export const toDBC = (str) => { 207 | let result = ""; 208 | for (let i = 0; i < str.length; i++) { 209 | var code = str.charCodeAt(i); 210 | if (code >= 33 && code <= 126) { 211 | result += String.fromCharCode(str.charCodeAt(i) + 65248); 212 | } else if (code == 32) { 213 | result += String.fromCharCode(str.charCodeAt(i) + 12288 - 32); 214 | } else { 215 | result += str.charAt(i); 216 | } 217 | } 218 | return result; 219 | }; 220 | 221 | /** 数字转化为大写金额 */ 222 | export const digitUppercase = (n) => { 223 | const fraction = ["角", "分"]; 224 | const digit = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"]; 225 | const unit = [ 226 | ["元", "万", "亿"], 227 | ["", "拾", "佰", "仟"], 228 | ]; 229 | n = Math.abs(n); 230 | let s = ""; 231 | for (let i = 0; i < fraction.length; i++) { 232 | s += ( 233 | digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i] 234 | ).replace(/零./, ""); 235 | } 236 | s = s || "整"; 237 | n = Math.floor(n); 238 | for (let i = 0; i < unit[0].length && n > 0; i++) { 239 | let p = ""; 240 | for (let j = 0; j < unit[1].length && n > 0; j++) { 241 | p = digit[n % 10] + unit[1][j] + p; 242 | n = Math.floor(n / 10); 243 | } 244 | s = p.replace(/(零.)*零$/, "").replace(/^$/, "零") + unit[0][i] + s; 245 | } 246 | return s 247 | .replace(/(零.)*零元/, "元") 248 | .replace(/(零.)+/g, "零") 249 | .replace(/^整$/, "零元整"); 250 | }; 251 | 252 | /** 数字转化为中文数字 */ 253 | export const intToChinese = (value) => { 254 | const str = String(value); 255 | const len = str.length - 1; 256 | const idxs = [ 257 | "", 258 | "十", 259 | "百", 260 | "千", 261 | "万", 262 | "十", 263 | "百", 264 | "千", 265 | "亿", 266 | "十", 267 | "百", 268 | "千", 269 | "万", 270 | "十", 271 | "百", 272 | "千", 273 | "亿", 274 | ]; 275 | const num = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"]; 276 | return str.replace(/([1-9]|0+)/g, ($, $1, idx, full) => { 277 | let pos = 0; 278 | if ($1[0] !== "0") { 279 | pos = len - idx; 280 | if (idx == 0 && $1[0] == 1 && idxs[len - idx] == "十") { 281 | return idxs[len - idx]; 282 | } 283 | return num[$1[0]] + idxs[len - idx]; 284 | } else { 285 | let left = len - idx; 286 | let right = len - idx + $1.length; 287 | if (Math.floor(right / 4) - Math.floor(left / 4) > 0) { 288 | pos = left - (left % 4); 289 | } 290 | if (pos) { 291 | return idxs[pos] + num[$1[0]]; 292 | } else if (idx + $1.length >= len) { 293 | return ""; 294 | } else { 295 | return num[$1[0]]; 296 | } 297 | } 298 | }); 299 | }; 300 | 301 | /** 302 | * 拆分整数和小数 303 | * @param tranvalue 304 | * @returns 305 | */ 306 | export const splits = (tranvalue: string) => { 307 | let value = new Array("", ""); 308 | const temp = tranvalue.split("."); 309 | for (var i = 0; i < temp.length; i++) { 310 | value = temp; 311 | } 312 | return value; 313 | }; 314 | -------------------------------------------------------------------------------- /src/utils/miniprogram.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeluosiyu/toa-tools/99c0cf757eaa076a028324010c0254f780ab3743/src/utils/miniprogram.ts -------------------------------------------------------------------------------- /src/utils/random/number.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 生成随机字符串(可指定长度) 3 | * @param len 4 | * @returns {string} 5 | */ 6 | export const randomString = function (len) { 7 | len = len || 8; 8 | var $chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678"; 9 | var maxPos = $chars.length; 10 | var pwd = ""; 11 | for (var i = 0; i < len; i++) { 12 | pwd += $chars.charAt(Math.floor(Math.random() * maxPos)); 13 | } 14 | return pwd; 15 | }; 16 | 17 | /** 18 | * 数组乱序 19 | * @param arr 20 | * @returns 21 | */ 22 | export const shuffleArray = (arr) => { 23 | let m = arr.length; 24 | while (m > 1) { 25 | let index = Math.floor(Math.random() * m--); 26 | [arr[m], arr[index]] = [arr[index], arr[m]] 27 | } 28 | return arr; 29 | } 30 | 31 | /** 32 | * 切分数组 33 | * @param nums 34 | * @returns 35 | */ 36 | export const splitArray = (nums) => { 37 | return bfs(nums, divide(nums)); 38 | function bfs(nums, { factors, multiples }) { 39 | const n = nums.length; 40 | const steps = Array.from({ length: n + 1 }, () => Infinity); 41 | steps[0] = 0; 42 | let status: any = new Set([0]); 43 | while (status.size) { 44 | const status2 = new Set(); 45 | for (const i of status) { 46 | const step = steps[i] + 1; 47 | for (const [factor, next] of factors[i]) { 48 | const multiple = multiples.get(factor); 49 | for (let j = next; j < multiple.length; j++) { 50 | const index = multiple[j] + 1; 51 | if (step < steps[index]) { 52 | steps[index] = step; 53 | if (index < n) status2.add(index); 54 | } 55 | } 56 | } 57 | } 58 | status = status2; 59 | } 60 | return steps[n]; 61 | } 62 | 63 | function divide(nums) { 64 | const n = nums.length; 65 | const max = Math.max(...nums); 66 | const positions = new Map(); 67 | for (let i = 0; i < n; i++) { 68 | const num = nums[i]; 69 | if (!positions.has(num)) { 70 | positions.set(num, []); 71 | } 72 | positions.get(num).push(i); 73 | } 74 | 75 | const factors = Array.from({ length: n }, () => new Map()); 76 | const multiples = new Map(); 77 | 78 | const isPrimes = Array(max + 1).fill(true); 79 | isPrimes[0] = false; 80 | isPrimes[1] = false; 81 | for (let i = 2; i <= max; i++) { 82 | if (isPrimes[i]) { 83 | if (positions.has(i)) { 84 | for (const index of positions.get(i)) { 85 | addFactor(i, index); 86 | } 87 | } 88 | for (let j = i + i; j <= max; j += i) { 89 | isPrimes[j] = false; 90 | if (positions.has(j)) { 91 | for (const index of positions.get(j)) { 92 | addFactor(i, index); 93 | } 94 | } 95 | } 96 | if (multiples.has(i)) { 97 | multiples.get(i).sort((a, b) => a - b); 98 | } 99 | } 100 | } 101 | return { factors, multiples }; 102 | 103 | function addFactor(factor, i) { 104 | if (!multiples.has(factor)) { 105 | multiples.set(factor, []); 106 | } 107 | factors[i].set(factor, multiples.get(factor).push(i) - 1); 108 | } 109 | } 110 | }; 111 | 112 | -------------------------------------------------------------------------------- /src/utils/string.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 字符串长度计算 3 | * @param str 4 | * @param countSpace 5 | * @returns 6 | */ 7 | export const count = (str, countSpace = true) => { 8 | if (countSpace) { 9 | return str.length; 10 | } 11 | return removeAllSpace(str).length; 12 | }; 13 | 14 | /** 15 | * 去除全部空格 16 | * @param str 17 | * @returns 18 | */ 19 | export const removeAllSpace = (str) => { 20 | return str.replace(/\s+/g, ""); 21 | }; 22 | 23 | /** 24 | * 去除首尾空格 25 | * @param str 26 | * @returns 27 | */ 28 | export const trim = (str) => { 29 | return str.trim(); 30 | }; 31 | 32 | /** 33 | * 去除左侧空格 34 | * @param str 35 | * @returns 36 | */ 37 | export const trimL = (str) => { 38 | return str.replace(/^\s+/g, ""); 39 | }; 40 | 41 | /** 42 | * 去除右侧空格 43 | * @param str 44 | * @returns 45 | */ 46 | export const trimR = (str) => { 47 | return str.replace(/\s+$/g, ""); 48 | }; 49 | 50 | /** 51 | * 字符串搜索 52 | * @param str 53 | * @param kwd 54 | * @param caseSensitive 55 | * @returns 56 | */ 57 | export const search = (str, kwd, caseSensitive = true) => { 58 | if (!caseSensitive) { 59 | kwd = kwd.toLowerCase(); 60 | str = str.toLowerCase(); 61 | } 62 | return str.indexOf(kwd); 63 | }; 64 | 65 | /** 66 | * 获取 扩展名 67 | * @param str 68 | * @returns 69 | */ 70 | export const getExtension = (str) => { 71 | str = str.split("."); 72 | return str.pop(); 73 | }; 74 | 75 | /** 76 | * 全部替换 77 | * @param str 78 | * @param replaceKey 79 | * @param replaceVal 80 | * @returns 81 | */ 82 | export const replaceAll = (str, replaceKey, replaceVal) => { 83 | var reg = new RegExp(replaceKey, 'g'); 84 | return str.replace(reg, replaceVal || ''); 85 | } 86 | 87 | /** 88 | * 均分数组 89 | * @param array 90 | * @param subGroupLength 91 | * @returns 92 | */ 93 | export const averageArrGroup = (array, subGroupLength) => { 94 | let index = 0; 95 | let newArray = []; 96 | while (index < array.length) { 97 | newArray.push(array.slice(index, index += subGroupLength)); 98 | } 99 | return newArray; 100 | } 101 | 102 | /** 103 | * toFixed修正版 104 | * @param number 105 | * @param m 106 | * @returns 107 | */ 108 | export const toFixed = (number, m) => { 109 | if (typeof number !== 'number') { 110 | throw new Error("target is not a number"); 111 | } 112 | let result = Math.round(Math.pow(10, m) * number) / Math.pow(10, m); 113 | result = String(result); 114 | if (result.indexOf(".") == -1) { 115 | if (m != 0) { 116 | result += "."; 117 | result += new Array(m + 1).join('0'); 118 | } 119 | } else { 120 | let arr = result.split('.'); 121 | if (arr[1].length < m) { 122 | arr[1] += new Array(m - arr[1].length + 1).join('0') 123 | } 124 | result = arr.join('.') 125 | } 126 | return result 127 | } 128 | 129 | /** 130 | * 判断字符串是否为json字符串 131 | * @param str 132 | * @returns 133 | */ 134 | export const isJsonString = (str) => { 135 | try { 136 | if (typeof (JSON.parse(str)) === 'object') { 137 | return true 138 | } 139 | } catch (e) { 140 | return false 141 | } 142 | return false 143 | } 144 | 145 | 146 | /** 147 | * "Safer" String.toLowerCase() 148 | */ 149 | function lowerCase(str) { 150 | return str.toLowerCase(); 151 | } 152 | 153 | /** 154 | * "Safer" String.toUpperCase() 155 | */ 156 | function upperCase(str) { 157 | return str.toUpperCase(); 158 | } 159 | 160 | /** 161 | * Convert string to camelCase text. 162 | */ 163 | function camelCase(str) { 164 | str = replaceAccents(str); 165 | str = removeNonWord(str) 166 | .replace(/\-/g, ' ') //convert all hyphens to spaces 167 | .replace(/\s[a-z]/g, upperCase) //convert first char of each word to UPPERCASE 168 | .replace(/\s+/g, '') //remove spaces 169 | .replace(/^[A-Z]/g, lowerCase); //convert first char to lowercase 170 | return str; 171 | } 172 | 173 | /** 174 | * Add space between camelCase text. 175 | */ 176 | function unCamelCase(str) { 177 | str = str.replace(/([a-z\xE0-\xFF])([A-Z\xC0\xDF])/g, '$1 $2'); 178 | str = str.toLowerCase(); //add space between camelCase text 179 | return str; 180 | } 181 | 182 | /** 183 | * UPPERCASE first char of each word. 184 | */ 185 | function properCase(str) { 186 | return lowerCase(str).replace(/^\w|\s\w/g, upperCase); 187 | } 188 | 189 | /** 190 | * camelCase + UPPERCASE first char 191 | */ 192 | function pascalCase(str) { 193 | return camelCase(str).replace(/^[a-z]/, upperCase); 194 | } 195 | 196 | function normalizeLineBreaks(str, lineEnd) { 197 | lineEnd = lineEnd || 'n'; 198 | 199 | return str 200 | .replace(/rn/g, lineEnd) // DOS 201 | .replace(/r/g, lineEnd) // Mac 202 | .replace(/n/g, lineEnd); // Unix 203 | } 204 | 205 | /** 206 | * UPPERCASE first char of each sentence and lowercase other chars. 207 | */ 208 | function sentenceCase(str) { 209 | // Replace first char of each sentence (new line or after '.\s+') to 210 | // UPPERCASE 211 | return lowerCase(str).replace(/(^\w)|\.\s+(\w)/gm, upperCase); 212 | } 213 | 214 | /** 215 | * Convert to lower case, remove accents, remove non-word chars and 216 | * replace spaces with the specified delimeter. 217 | * Does not split camelCase text. 218 | */ 219 | function slugify(str, delimeter) { 220 | if (delimeter == null) { 221 | delimeter = "-"; 222 | } 223 | 224 | str = replaceAccents(str); 225 | str = removeNonWord(str); 226 | str = trim(str) //should come after removeNonWord 227 | .replace(/ +/g, delimeter) //replace spaces with delimeter 228 | .toLowerCase(); 229 | 230 | return str; 231 | } 232 | 233 | /** 234 | * Replaces spaces with hyphens, split camelCase text, remove non-word chars, remove accents and convert to lower case. 235 | */ 236 | function hyphenate(str) { 237 | str = unCamelCase(str); 238 | return slugify(str, "-"); 239 | } 240 | 241 | /** 242 | * Replaces hyphens with spaces. (only hyphens between word chars) 243 | */ 244 | function unhyphenate(str) { 245 | return str.replace(/(\w)(-)(\w)/g, '$1 $3'); 246 | } 247 | 248 | /** 249 | * Replaces spaces with underscores, split camelCase text, remove 250 | * non-word chars, remove accents and convert to lower case. 251 | */ 252 | function underscore(str) { 253 | str = unCamelCase(str); 254 | return slugify(str, "_"); 255 | } 256 | 257 | /** 258 | * Remove non-word chars. 259 | */ 260 | function removeNonWord(str) { 261 | return str.replace(/[^0-9a-zA-Z\xC0-\xFF \-]/g, ''); 262 | } 263 | 264 | /** 265 | * Convert line-breaks from DOS/MAC to a single standard (UNIX by default) 266 | */ 267 | function normalizeLineBreaks(str, lineEnd) { 268 | lineEnd = lineEnd || '\n'; 269 | 270 | return str 271 | .replace(/\r\n/g, lineEnd) // DOS 272 | .replace(/\r/g, lineEnd) // Mac 273 | .replace(/\n/g, lineEnd); // Unix 274 | } 275 | 276 | /** 277 | * Replaces all accented chars with regular ones 278 | */ 279 | function replaceAccents(str) { 280 | // verifies if the String has accents and replace them 281 | if (str.search(/[\xC0-\xFF]/g) > -1) { 282 | str = str 283 | .replace(/[\xC0-\xC5]/g, "A") 284 | .replace(/[\xC6]/g, "AE") 285 | .replace(/[\xC7]/g, "C") 286 | .replace(/[\xC8-\xCB]/g, "E") 287 | .replace(/[\xCC-\xCF]/g, "I") 288 | .replace(/[\xD0]/g, "D") 289 | .replace(/[\xD1]/g, "N") 290 | .replace(/[\xD2-\xD6\xD8]/g, "O") 291 | .replace(/[\xD9-\xDC]/g, "U") 292 | .replace(/[\xDD]/g, "Y") 293 | .replace(/[\xDE]/g, "P") 294 | .replace(/[\xE0-\xE5]/g, "a") 295 | .replace(/[\xE6]/g, "ae") 296 | .replace(/[\xE7]/g, "c") 297 | .replace(/[\xE8-\xEB]/g, "e") 298 | .replace(/[\xEC-\xEF]/g, "i") 299 | .replace(/[\xF1]/g, "n") 300 | .replace(/[\xF2-\xF6\xF8]/g, "o") 301 | .replace(/[\xF9-\xFC]/g, "u") 302 | .replace(/[\xFE]/g, "p") 303 | .replace(/[\xFD\xFF]/g, "y"); 304 | } 305 | 306 | return str; 307 | } 308 | 309 | /** 310 | * Searches for a given substring 311 | */ 312 | function contains(str, substring, fromIndex) { 313 | return str.indexOf(substring, fromIndex) !== -1; 314 | } 315 | 316 | /** 317 | * Truncate string at full words. 318 | */ 319 | function crop(str, maxChars, append) { 320 | return truncate(str, maxChars, append, true); 321 | } 322 | 323 | /** 324 | * Escape RegExp string chars. 325 | */ 326 | function escapeRegExp(str) { 327 | var ESCAPE_CHARS = /[\\.+*?\^$\[\](){}\/'#]/g; 328 | return str.replace(ESCAPE_CHARS, '\\$&'); 329 | } 330 | 331 | /** 332 | * Escapes a string for insertion into HTML. 333 | */ 334 | function escapeHtml(str) { 335 | str = str 336 | .replace(/&/g, '&') 337 | .replace(//g, '>') 339 | .replace(/'/g, ''') 340 | .replace(/"/g, '"'); 341 | 342 | return str; 343 | } 344 | 345 | /** 346 | * Unescapes HTML special chars 347 | */ 348 | function unescapeHtml(str) { 349 | str = str 350 | .replace(/&/g, '&') 351 | .replace(/</g, '<') 352 | .replace(/>/g, '>') 353 | .replace(/'/g, "'") 354 | .replace(/"/g, '"'); 355 | return str; 356 | } 357 | 358 | /** 359 | * Escape string into unicode sequences 360 | */ 361 | function escapeUnicode(str, shouldEscapePrintable) { 362 | return str.replace(/[\s\S]/g, function (ch) { 363 | // skip printable ASCII chars if we should not escape them 364 | if (!shouldEscapePrintable && (/[\x20-\x7E]/).test(ch)) { 365 | return ch; 366 | } 367 | // we use "000" and slice(-4) for brevity, need to pad zeros, 368 | // unicode escape always have 4 chars after "\u" 369 | return '\\u' + ('000' + ch.charCodeAt(0).toString(16)).slice(-4); 370 | }); 371 | } 372 | 373 | /** 374 | * Remove HTML tags from string. 375 | */ 376 | function stripHtmlTags(str) { 377 | return str.replace(/<[^>]*>/g, ''); 378 | } 379 | 380 | /** 381 | * Remove non-printable ASCII chars 382 | */ 383 | function removeNonASCII(str) { 384 | // Matches non-printable ASCII chars - 385 | // http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters 386 | return str.replace(/[^\x20-\x7E]/g, ''); 387 | } 388 | 389 | /** 390 | * String interpolation 391 | */ 392 | function interpolate(template, replacements, syntax) { 393 | var stache = /\{\{(\w+)\}\}/g; //mustache-like 394 | 395 | var replaceFn = function (match, prop) { 396 | return (prop in replacements) ? replacements[prop] : ''; 397 | }; 398 | 399 | return template.replace(syntax || stache, replaceFn); 400 | } 401 | 402 | /** 403 | * Pad string with `char` if its' length is smaller than `minLen` 404 | */ 405 | function rpad(str, minLen, ch) { 406 | ch = ch || ' '; 407 | return (str.length < minLen) ? str + repeat(ch, minLen - str.length) : str; 408 | } 409 | 410 | /** 411 | * Pad string with `char` if its' length is smaller than `minLen` 412 | */ 413 | function lpad(str, minLen, ch) { 414 | ch = ch || ' '; 415 | 416 | return ((str.length < minLen) 417 | ? repeat(ch, minLen - str.length) + str : str); 418 | } 419 | 420 | /** 421 | * Repeat string n times 422 | */ 423 | function repeat(str, n) { 424 | return (new Array(n + 1)).join(str); 425 | } 426 | 427 | /** 428 | * Limit number of chars. 429 | */ 430 | function truncate(str, maxChars, append, onlyFullWords) { 431 | append = append || '...'; 432 | maxChars = onlyFullWords ? maxChars + 1 : maxChars; 433 | 434 | str = trim(str); 435 | if (str.length <= maxChars) { 436 | return str; 437 | } 438 | str = str.substr(0, maxChars - append.length); 439 | //crop at last space or remove trailing whitespace 440 | str = onlyFullWords ? str.substr(0, str.lastIndexOf(' ')) : trim(str); 441 | return str + append; 442 | } 443 | 444 | var WHITE_SPACES = [ 445 | ' ', '\n', '\r', '\t', '\f', '\v', '\u00A0', '\u1680', '\u180E', 446 | '\u2000', '\u2001', '\u2002', '\u2003', '\u2004', '\u2005', '\u2006', 447 | '\u2007', '\u2008', '\u2009', '\u200A', '\u2028', '\u2029', '\u202F', 448 | '\u205F', '\u3000' 449 | ]; 450 | 451 | /** 452 | * Remove chars from beginning of string. 453 | */ 454 | function ltrim(str, chars) { 455 | chars = chars || WHITE_SPACES; 456 | 457 | var start = 0, 458 | len = str.length, 459 | charLen = chars.length, 460 | found = true, 461 | i, c; 462 | 463 | while (found && start < len) { 464 | found = false; 465 | i = -1; 466 | c = str.charAt(start); 467 | 468 | while (++i < charLen) { 469 | if (c === chars[i]) { 470 | found = true; 471 | start++; 472 | break; 473 | } 474 | } 475 | } 476 | 477 | return (start >= len) ? '' : str.substr(start, len); 478 | } 479 | 480 | /** 481 | * Remove chars from end of string. 482 | */ 483 | function rtrim(str, chars) { 484 | chars = chars || WHITE_SPACES; 485 | 486 | var end = str.length - 1, 487 | charLen = chars.length, 488 | found = true, 489 | i, c; 490 | 491 | while (found && end >= 0) { 492 | found = false; 493 | i = -1; 494 | c = str.charAt(end); 495 | 496 | while (++i < charLen) { 497 | if (c === chars[i]) { 498 | found = true; 499 | end--; 500 | break; 501 | } 502 | } 503 | } 504 | 505 | return (end >= 0) ? str.substring(0, end + 1) : ''; 506 | } 507 | 508 | /** 509 | * Remove white-spaces from beginning and end of string. 510 | */ 511 | function trim(str, chars) { 512 | chars = chars || WHITE_SPACES; 513 | return ltrim(rtrim(str, chars), chars); 514 | } 515 | 516 | /** 517 | * Capture all capital letters following a word boundary (in case the 518 | * input is in all caps) 519 | */ 520 | function abbreviate(str) { 521 | return str.match(/\b([A-Z])/g).join(''); 522 | } 523 | -------------------------------------------------------------------------------- /src/utils/tree.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 扁平数据结构转Tree 3 | * @param items 4 | * @param childId 5 | * @param parentId 6 | * @returns 7 | */ 8 | function arrayToTree(items, childId, parentId) { 9 | const result = []; 10 | const itemMap = {}; // 11 | for (const item of items) { 12 | const id = item[childId]; 13 | const pid = item[parentId]; 14 | 15 | if (!itemMap[id]) { 16 | itemMap[id] = { 17 | children: [], 18 | }; 19 | } 20 | 21 | itemMap[id] = { 22 | ...item, 23 | children: itemMap[id]["children"], 24 | }; 25 | 26 | const treeItem = itemMap[id]; 27 | 28 | if (pid === 0) { 29 | result.push(treeItem); 30 | } else { 31 | if (!itemMap[pid]) { 32 | itemMap[pid] = { 33 | children: [], 34 | }; 35 | } 36 | itemMap[pid].children.push(treeItem); 37 | } 38 | } 39 | return result; 40 | } 41 | -------------------------------------------------------------------------------- /src/utils/url.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取所有URL参数 getAllUrlParams 3 | * @description: 把url参数 转换为json对象(但是# 后面的 都不会解析 ),所以在 Vue 中 history模式可以使用,但是 hash模式 他会返回一个空 4 | * @param {type} 路径 || 在页面或者组件里面调用,他会自己取的当前页面路径 5 | * @return {type} Object 注:无参数的时候,返回一个json 6 | */ 7 | export function getAllUrlParams(url) { 8 | // 用JS拿到URL,如果函数接收了URL,那就用函数的参数。如果没传参,就使用当前页面的URL 9 | let queryString = url ? url.split("?")[1] : window.location.search.slice(1); 10 | // 用来存储我们所有的参数 11 | let obj = {}; 12 | // 如果没有传参,返回一个空对象 13 | if (!queryString) { 14 | return obj; 15 | } 16 | // #后面的东西不是查询字符串的一部分,所以去掉它 17 | queryString = queryString.split("#")[0]; 18 | // 将参数分成数组 19 | let arr = queryString.split("&"); 20 | for (let i = 0; i < arr.length; i++) { 21 | // 分离成key:value的形式 22 | let a = arr[i].split("="); 23 | // 将undefined标记为true 24 | let paramName = a[0]; 25 | let paramValue = typeof a[1] === "undefined" ? true : a[1]; 26 | // 如果调用对象时要求大小写区分,可删除这两行代码 27 | paramName = paramName.toLowerCase(); 28 | if (typeof paramValue === "string") paramValue = paramValue.toLowerCase(); 29 | // 如果paramName以方括号结束, e.g. colors[] or colors[2] 30 | if (paramName.match(/[(\d+)?]$/)) { 31 | // 如果paramName不存在,则创建key 32 | let key = paramName.replace(/[(\d+)?]/, ""); 33 | if (!obj[key]) obj[key] = []; 34 | // 如果是索引数组 e.g. colors[2] 35 | if (paramName.match(/[\d+]$/)) { 36 | // 获取索引值并在对应的位置添加值 37 | let index = /[(\d+)]/.exec(paramName)[1]; 38 | obj[key][index] = paramValue; 39 | } else { 40 | // 如果是其它的类型,也放到数组中 41 | obj[key].push(paramValue); 42 | } 43 | } else { 44 | // 处理字符串类型 45 | if (!obj[paramName]) { 46 | // 如果如果paramName不存在,则创建对象的属性 47 | obj[paramName] = paramValue; 48 | } else if (obj[paramName] && typeof obj[paramName] === "string") { 49 | // 如果属性存在,并且是个字符串,那么就转换为数组 50 | obj[paramName] = [obj[paramName]]; 51 | obj[paramName].push(paramValue); 52 | } else { 53 | // 如果是其它的类型,还是往数组里丢 54 | obj[paramName].push(paramValue); 55 | } 56 | } 57 | } 58 | return obj; 59 | } 60 | 61 | /** 62 | * parseQueryString 63 | * @description: 这个所有模式下 都可以使用,他会 取得 ? 号后面的 所有内容 64 | * @param {type} 路径 || 在页面或者组件里面调用,他会自己取的当前页面路径 65 | * @return {type} Object 注:无参数的时候,返回一个空json 66 | */ 67 | export function parseQueryString(url) { 68 | let queryString = url ? url.split("?")[1] : window.location.href; 69 | let obj = {}; 70 | //这个 去查找是否,传了参数过来 71 | if (queryString.indexOf("?") === -1) { 72 | return obj; 73 | } 74 | let keyvalue = []; 75 | let key = "", 76 | value = ""; 77 | let paraString = queryString 78 | .substring(queryString.indexOf("?") + 1, queryString.length) 79 | .split("&"); 80 | for (let i in paraString) { 81 | keyvalue = paraString[i].split("="); 82 | key = keyvalue[0]; 83 | value = keyvalue[1]; 84 | obj[key] = value; 85 | } 86 | return obj; 87 | } 88 | 89 | /** 90 | * 修改url中的参数 91 | * @param { string } paramName 92 | * @param { string } replaceWith 93 | */ 94 | export function replaceParamVal(paramName: string, replaceWith: any) { 95 | var oUrl = location.href.toString(); 96 | var re = eval("/(" + paramName + "=)([^&]*)/gi"); 97 | location.href = oUrl.replace(re, paramName + "=" + replaceWith); 98 | return location.href; 99 | } 100 | 101 | /** 102 | * 删除url中指定的参数 103 | * @param { string } name 104 | */ 105 | export function funcUrlDel(name: string) { 106 | var loca = location; 107 | var baseUrl = loca.origin + loca.pathname + "?"; 108 | var query = loca.search.substr(1); 109 | if (query.indexOf(name) > -1) { 110 | var obj = {}; 111 | var arr: Array = query.split("&"); 112 | for (var i = 0; i < arr.length; i++) { 113 | arr[i] = arr[i].split("="); 114 | obj[arr[i][0]] = arr[i][1]; 115 | } 116 | delete obj[name]; 117 | var url = 118 | baseUrl + 119 | JSON.stringify(obj) 120 | .replace(/[\"\{\}]/g, "") 121 | .replace(/\:/g, "=") 122 | .replace(/\,/g, "&"); 123 | return url; 124 | } 125 | } 126 | 127 | /** 128 | * 解析页面URL参数params(并处理 decodeURIComponent ) 129 | * @param name 130 | * @returns 131 | */ 132 | export const delUrlParam = (name: string) => { 133 | let loca = window.location; 134 | let baseUrl = loca.origin + loca.pathname + "?"; 135 | let query = decodeURIComponent(loca.search.split("?")[1]); 136 | if (!query) return loca; 137 | 138 | if (loca.href.indexOf(name) < 0) return loca.href; 139 | 140 | let obj = {}; 141 | let arr: any = query.indexOf("&") > -1 ? query.split("&") : [query]; 142 | for (let i = 0; i < arr.length; i++) { 143 | arr[i] = arr[i].split("="); 144 | obj[arr[i][0]] = arr[i][1]; 145 | } 146 | delete obj[name]; 147 | 148 | const url = 149 | baseUrl + 150 | JSON.stringify(obj) 151 | .replace(/[\"\{\}]/g, "") 152 | .replace(/\:/g, "=") 153 | .replace(/\,/g, "&"); 154 | return url; 155 | }; 156 | 157 | /** 158 | * URLSearchParams 159 | * @param url String 可选参数, url地址 160 | * @param OneKey String 可选参数, 获取当前网址指定参数 161 | * @param Delete_key Array 可选参数, 过滤指定参数 162 | * @returns 163 | */ 164 | export const getUrlData = function (url, OneKey, Delete_key = []) { 165 | if (!url) url = window.location.search; 166 | var url_l = url.split("?")[1]; 167 | var url_ll = url_l.split("&"), 168 | obj = {}; 169 | url_ll.forEach((item, idx) => { 170 | var key = item.split("=")[0]; 171 | var val = item.split("=")[1]; 172 | if (Delete_key.indexOf(key) == -1) obj[key] = val; 173 | }); 174 | if (OneKey && url_l.indexOf(OneKey)) return obj[OneKey]; 175 | return obj; 176 | }; 177 | 178 | /** 179 | * 获取单个参数 180 | * @param {String} name 181 | * @param {String} url [default:location.href] 182 | * @return {String|Boolean} 183 | */ 184 | export const getParam = (name, url) => { 185 | if (typeof name !== "string") return false; 186 | if (!url) url = window.location.href; 187 | // 当遇到name[xx]时,对方括号做一下转义为 name\[xxx\],因为下面还需要使用name做正则 188 | name = name.replace(/[\[\]]/g, "\\$&"); 189 | var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"); 190 | var results = regex.exec(url); 191 | if (!results) return null; 192 | if (!results[2]) return ""; 193 | return decodeURIComponent(results[2].replace(/\+/g, " ")); 194 | }; 195 | 196 | /** 197 | * 设置单个参数 198 | * @param name 199 | * @param val 200 | * @param url 201 | * @returns 202 | */ 203 | export const setParam = (name, val, url) => { 204 | if (typeof name !== "string") return false; 205 | if (!url) url = window.location.href; 206 | var _name = name.replace(/[\[\]]/g, "\\$&"); 207 | var value = name + "=" + encodeURIComponent(val); 208 | var regex = new RegExp(_name + "=[^&]*"); 209 | var urlArr = url.split("#"); 210 | var result = ""; 211 | 212 | if (regex.exec(url)) { 213 | result = url.replace(regex, value); 214 | } else { 215 | result = urlArr[0] + "&" + value + (urlArr[1] || ""); 216 | } 217 | 218 | return result; 219 | }; 220 | 221 | /** 222 | * 移除单个参数 223 | * @param name 224 | * @param url 225 | * @returns 226 | */ 227 | export const removeParam = (name, url) => { 228 | if (typeof name !== "string") return false; 229 | if (!url) url = window.location.href; 230 | var urlparts = url.split("?"); 231 | var prefix = encodeURIComponent(name + "="); 232 | var pars = urlparts[1].split(/[&;]/g); 233 | var i = 0, 234 | len = pars.length; 235 | 236 | for (; i < len; i++) { 237 | if (encodeURIComponent(pars[i]).lastIndexOf(prefix, 0) !== -1) { 238 | pars.splice(i, 1); 239 | } 240 | } 241 | 242 | url = urlparts[0] + (pars.length > 0 ? "?" + pars.join("&") : ""); 243 | 244 | return url; 245 | }; 246 | 247 | /** 248 | * 获取多个参数 249 | * @param {String} names 250 | * @param {String} url 251 | * @return {[String|Boolean]} 252 | */ 253 | export const getParams = (names, url) => { 254 | if (typeof name !== "string") return false; 255 | var names = names.split(" "); 256 | var result = {}; 257 | var i = 0, 258 | len = names.length; 259 | if (names.length === 0) return false; 260 | for (; i < len; i++) { 261 | result[names[i]] = getParam(names[i], url); 262 | } 263 | return result; 264 | }; 265 | 266 | /** 267 | * 设置多个参数 268 | * @param {Object} obj 269 | * @param {String} url 270 | * @return {[String|Boolean]} 271 | */ 272 | export const setParams = (obj, url) => { 273 | var result = url || ""; 274 | if (Object.prototype.toString.call(obj) !== "[object Object]") return false; 275 | for (var name in obj) { 276 | result = setParam(name, obj[name], result); 277 | } 278 | return result; 279 | }; 280 | 281 | /** 282 | * 移除多个参数 283 | * @param {String} names 284 | * @param {String} url 285 | * @return {[String|Boolean]} 286 | */ 287 | export const removeParams = (names, url) => { 288 | var result = url || ""; 289 | var names = names.split(" "); 290 | var i = 0, 291 | len = names.length; 292 | if (names.length === 0) return false; 293 | 294 | for (; i < len; i++) { 295 | result = removeParam(names[i], result); 296 | } 297 | return result; 298 | }; 299 | -------------------------------------------------------------------------------- /src/utils/verification.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 过滤表情 3 | * @param name 4 | * @returns 5 | */ 6 | export const filterEmoji = (name) => { 7 | const str = name.replace( 8 | /[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/gi, 9 | "" 10 | ); 11 | return str; 12 | }; 13 | 14 | /** 15 | * 过滤税号 16 | * @param num 17 | * @returns 18 | */ 19 | export const isNumber = (num: string) => 20 | /^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/.test(num); 21 | 22 | /** 23 | * 验证手机号 24 | * @param phone 25 | * @returns 26 | */ 27 | export const isPhone = (phone: any) => /^[1][0-9]{10}$/.test(phone); 28 | 29 | /** 30 | * 验证邮箱 31 | * @param email 32 | * @returns 33 | */ 34 | export const isEmail = (email: string) => 35 | /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/.test( 36 | email 37 | ); 38 | 39 | /** 40 | * 验证是否存在特殊符号或者表情 41 | * @param value 42 | * @param tips 43 | * @returns 44 | */ 45 | export const hasEmoji = function (value: string, tips = "") { 46 | let char = 47 | /[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/gi; 48 | if (char.test(value)) { 49 | return true; 50 | } 51 | return false; 52 | }; 53 | 54 | /** 55 | * 获取数组索引值 56 | * @param param0 57 | * @returns 58 | */ 59 | export const getIndexArr = function ({ 60 | id = "", 61 | productTree = [], 62 | idKey = "id", 63 | childrenKey = "child", 64 | }: { 65 | id: string; 66 | productTree?: Array; 67 | idKey?: string; 68 | childrenKey?: string; 69 | }) { 70 | let indexArr: number[] = []; 71 | let fn: (arr: Array) => boolean = (arr) => 72 | arr.some((elem: any, index) => { 73 | if (elem[idKey] == id) { 74 | indexArr.push(index); 75 | return true; 76 | } else if ( 77 | elem[childrenKey] && 78 | elem[childrenKey] instanceof Array && 79 | elem[childrenKey].length 80 | ) { 81 | return fn(elem[childrenKey]) && indexArr.push(index); 82 | } 83 | return false; 84 | }); 85 | fn(productTree); 86 | indexArr.reverse(); 87 | return indexArr; 88 | }; 89 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowUnreachableCode": true, // 不报告执行不到的代码错误。 4 | "allowUnusedLabels": false, // 不报告未使用的标签错误 5 | "alwaysStrict": false, // 以严格模式解析并为每个源文件生成 "use strict"语句 6 | "baseUrl": ".", // 工作根目录 7 | "experimentalDecorators": true, // 启用实验性的ES装饰器 8 | "jsx": "react", // 在 .tsx文件里支持JSX 9 | "sourceMap": true, // 是否生成map文件 10 | "module": "ES2015", // 指定生成哪个模块系统代码 11 | "noImplicitAny": false, // 是否默认禁用 any 12 | "removeComments": true, // 是否移除注释 13 | "target": "ESNext", // 编译的目标是什么版本的 14 | "outDir": "./dist", // 输出目录 15 | "declaration": true, // 是否自动创建类型声明文件 16 | "declarationDir": "./ts/lib", // 类型声明文件的输出目录 17 | "allowJs": true, // 允许编译javascript文件。 18 | "moduleResolution": "node", 19 | "allowSyntheticDefaultImports": true, 20 | "lib": [ // 编译过程中需要引入的库文件的列表 21 | "es5", 22 | "es2015", 23 | "es2016", 24 | "es2017", 25 | "es2018", 26 | "dom" 27 | ] 28 | }, 29 | // 指定一个匹配列表(属于自动指定该路径下的所有ts相关文件) 30 | "include": [ 31 | "src/**/*" 32 | ], 33 | // 指定一个排除列表(include的反向操作) 34 | "exclude": [ 35 | "node_modules", 36 | "dist" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "toa-tools" { 2 | export const loading: { 3 | show: (options?: { 4 | content?: string; 5 | isShowCancel?: boolean; 6 | needMask?: boolean; 7 | }) => void; 8 | hide: () => void; 9 | }; 10 | export const appVersion: () => string; 11 | export const isInApp: () => boolean; 12 | export const isInWechat: () => boolean; 13 | export const isInWechatH5: () => boolean; 14 | export const isInWechatMp: () => boolean; 15 | export const isInQuickApp: () => boolean; 16 | export const isInBaiDu: () => boolean; 17 | export const isInToutiao: () => boolean; 18 | export const isInAlipay: () => boolean; 19 | export const isInAlipayH5: () => boolean; 20 | export const isInAlipayMp: () => boolean; 21 | export const isInQQ: () => boolean; 22 | export const isInIOS: () => boolean; 23 | export const isIPhoneXSeries: () => boolean; 24 | export const isInCtApp: () => boolean; 25 | export const isInCtWeb: () => boolean; 26 | export const isInZhixingApp: () => boolean; 27 | export const getEnvByHost: () => string; 28 | export const jump: (arg0: string | object) => void; 29 | export const back: () => {}; 30 | export const getQuery: (arg0: string, arg1?: boolean) => string; 31 | export const getQueryAll: () => object; 32 | export const url: { 33 | dirname: () => string; 34 | basename: () => string; 35 | filename: () => string; 36 | }; 37 | export const isLogin: () => boolean; 38 | export const forceLogin: (options?: { 39 | env?: string; 40 | from?: string; 41 | backUrl?: string; 42 | dynamic?: boolean; 43 | appCallback?: () => void; 44 | }) => void; 45 | export const login: ( 46 | callback?: () => void, 47 | options?: { 48 | env?: string; 49 | from?: string; 50 | backUrl?: string; 51 | dynamic?: boolean; 52 | appCallback?: () => void; 53 | } 54 | ) => void; 55 | export const setShare: ( 56 | options: { 57 | appType?: any[]; 58 | icon?: string; 59 | title?: string; 60 | desc?: string; 61 | href?: string; 62 | image?: string; 63 | wechatMp?: string | object; 64 | quickApp?: string; 65 | baidu?: string; 66 | toutiao?: string; 67 | alipayMp?: string; 68 | qq?: string; 69 | miniProgramID?: string; 70 | }, 71 | success?: () => void, 72 | cancel?: () => void 73 | ) => any; 74 | export const callOneShare: ( 75 | options: { 76 | type?: string; 77 | icon?: string; 78 | title?: string; 79 | desc?: string; 80 | href?: string; 81 | image?: string; 82 | wechatMp?: string; 83 | }, 84 | success?: () => void, 85 | cancel?: () => void 86 | ) => void; 87 | export const wechatReady: (callback?: (wx: object) => void) => void; 88 | export const quickAppReady: (callback?: (qa: object) => void) => void; 89 | export const baiduReady: (callback?: (swan: object) => void) => void; 90 | export const toutiaoReady: (callback?: (tt: object) => void) => void; 91 | export const alipayReady: (callback?: (my: object) => void) => void; 92 | export const qqReady: (callback?: (qq: object) => void) => void; 93 | export const openWechatMp: (options: { path: string }) => void; 94 | export const setTitle: (title?: string) => void; 95 | export const setNavBar: (options: { hide?: boolean; color?: string }) => void; 96 | export const getWechatUserInfo: ( 97 | callback?: (userInfo: object) => void 98 | ) => void; 99 | export const activateApp: ( 100 | url?: string, 101 | downloadApp?: boolean, 102 | callback?: () => void 103 | ) => void; 104 | export const getUnion: () => object; 105 | export const getRmsToken: (callback?: (rmsToken) => void) => void; 106 | export const pay: (options: object) => void; 107 | export const pay2: (options: object) => void; 108 | export const localStorage: { 109 | setItem: ( 110 | key: string, 111 | value: string | number | object, 112 | timeout: number 113 | ) => void; 114 | getItem: (key: string) => string | object; 115 | removeItem: (key: string) => void; 116 | }; 117 | export const sessionStorage: { 118 | setItem: (key: string, value: string | number | object) => void; 119 | getItem: (key: string) => string | object; 120 | removeItem: (key: string) => void; 121 | }; 122 | export const app: { 123 | on: (key: string, callback: (...args) => void) => void; 124 | off: (key: string) => void; 125 | }; 126 | export const on: (key: string, callback: (...args) => void) => void; 127 | export const emit: (key: string, ...args) => void; 128 | export const off: (key: string) => void; 129 | export const $: () => any; 130 | export const debounce: ( 131 | func: () => void, 132 | wait: number, 133 | immediate?: boolean 134 | ) => any; 135 | export const throttle: ( 136 | func: () => void, 137 | wait: number, 138 | options?: { 139 | leading?: boolean; 140 | trailing?: boolean; 141 | } 142 | ) => any; 143 | 144 | // server methods 145 | export const version: () => string; 146 | export const isString: () => boolean; 147 | export const isNumber: () => boolean; 148 | export const isArray: () => boolean; 149 | export const isObject: () => boolean; 150 | export const isFunction: () => boolean; 151 | export const isUndefined: () => boolean; 152 | export const isServer: () => boolean; 153 | export const isClient: () => boolean; 154 | export const getGateway: (config: { 155 | req?: object; 156 | env?: string; 157 | code?: string; 158 | name?: string; 159 | }) => string; 160 | export const model: ( 161 | code: string | object, 162 | name?: string, 163 | body?: object, 164 | req?: object, 165 | env?: string 166 | ) => Promise; 167 | export const modelSet: (params: object) => void; 168 | export const modelLog: (callback: () => void, keepOne?: boolean) => void; 169 | export const CModel: (config?: object) => any; 170 | export const base64: { 171 | encode: (arg0: string) => void; 172 | decode: (arg0: string) => void; 173 | }; 174 | export const time2Date: (time?: number, type?: string) => string; 175 | export const date2Time: (date?: string) => number; 176 | export const formatDate: (arg0: T) => T; 177 | export const formatQuery: (arg0: object) => string; 178 | export const randomInt: (lower: number, upper: number) => number; 179 | } 180 | --------------------------------------------------------------------------------